From 91b562a98e776d6661535b59872dd2e44cac349d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:53:18 +0200 Subject: [PATCH 001/565] * Imported QML resources. --- data/images/inputfield-border.svg | 145 +++++++++++++ data/images/station-artist.svg | 14 ++ data/images/station-genre.svg | 18 ++ data/images/station-year.svg | 19 ++ data/qml/DeclarativeHeader.qml | 21 ++ data/qml/QmlGridView.qml | 65 ++++++ data/qml/SpinnerTest.qml | 34 +++ data/qml/StationView.qml | 162 ++++++++++++++ data/qml/stations/CreateByArtist.qml | 69 ++++++ data/qml/stations/CreateByGenre.qml | 88 ++++++++ data/qml/stations/StationConfig.qml | 79 +++++++ data/qml/stations/StationCreatorPage1.qml | 64 ++++++ data/qml/stations/StationCreatorPage2.qml | 25 +++ data/qml/tomahawkimports/ArtistView.qml | 71 +++++++ data/qml/tomahawkimports/BusyIndicator.qml | 52 +++++ data/qml/tomahawkimports/Button.qml | 29 +++ data/qml/tomahawkimports/CoverFlip.qml | 137 ++++++++++++ data/qml/tomahawkimports/CoverImage.qml | 197 +++++++++++++++++ data/qml/tomahawkimports/DoubleSlider.qml | 151 +++++++++++++ data/qml/tomahawkimports/FlexibleHeader.qml | 199 ++++++++++++++++++ data/qml/tomahawkimports/HeaderLabel.qml | 7 + data/qml/tomahawkimports/InputField.qml | 90 ++++++++ data/qml/tomahawkimports/PushButton.qml | 34 +++ data/qml/tomahawkimports/RoundedButton.qml | 43 ++++ data/qml/tomahawkimports/ScrollBar.qml | 69 ++++++ data/qml/tomahawkimports/TagCloud.qml | 80 +++++++ .../qml/tomahawkimports/ToggleViewButtons.qml | 34 +++ 27 files changed, 1996 insertions(+) create mode 100644 data/images/inputfield-border.svg create mode 100644 data/images/station-artist.svg create mode 100644 data/images/station-genre.svg create mode 100644 data/images/station-year.svg create mode 100644 data/qml/DeclarativeHeader.qml create mode 100644 data/qml/QmlGridView.qml create mode 100644 data/qml/SpinnerTest.qml create mode 100644 data/qml/StationView.qml create mode 100644 data/qml/stations/CreateByArtist.qml create mode 100644 data/qml/stations/CreateByGenre.qml create mode 100644 data/qml/stations/StationConfig.qml create mode 100644 data/qml/stations/StationCreatorPage1.qml create mode 100644 data/qml/stations/StationCreatorPage2.qml create mode 100644 data/qml/tomahawkimports/ArtistView.qml create mode 100644 data/qml/tomahawkimports/BusyIndicator.qml create mode 100644 data/qml/tomahawkimports/Button.qml create mode 100644 data/qml/tomahawkimports/CoverFlip.qml create mode 100644 data/qml/tomahawkimports/CoverImage.qml create mode 100644 data/qml/tomahawkimports/DoubleSlider.qml create mode 100644 data/qml/tomahawkimports/FlexibleHeader.qml create mode 100644 data/qml/tomahawkimports/HeaderLabel.qml create mode 100644 data/qml/tomahawkimports/InputField.qml create mode 100644 data/qml/tomahawkimports/PushButton.qml create mode 100644 data/qml/tomahawkimports/RoundedButton.qml create mode 100644 data/qml/tomahawkimports/ScrollBar.qml create mode 100644 data/qml/tomahawkimports/TagCloud.qml create mode 100644 data/qml/tomahawkimports/ToggleViewButtons.qml diff --git a/data/images/inputfield-border.svg b/data/images/inputfield-border.svg new file mode 100644 index 0000000000..3f3ff841ca --- /dev/null +++ b/data/images/inputfield-border.svg @@ -0,0 +1,145 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/images/station-artist.svg b/data/images/station-artist.svg new file mode 100644 index 0000000000..43ed51948e --- /dev/null +++ b/data/images/station-artist.svg @@ -0,0 +1,14 @@ + + + station-artist + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + \ No newline at end of file diff --git a/data/images/station-genre.svg b/data/images/station-genre.svg new file mode 100644 index 0000000000..ba5c1157fe --- /dev/null +++ b/data/images/station-genre.svg @@ -0,0 +1,18 @@ + + + station-genre + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/images/station-year.svg b/data/images/station-year.svg new file mode 100644 index 0000000000..a18fb23d33 --- /dev/null +++ b/data/images/station-year.svg @@ -0,0 +1,19 @@ + + + station-year + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/qml/DeclarativeHeader.qml b/data/qml/DeclarativeHeader.qml new file mode 100644 index 0000000000..5863a9d27f --- /dev/null +++ b/data/qml/DeclarativeHeader.qml @@ -0,0 +1,21 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "tomahawkimports" + +// Only to be used together with DeclarativeHeader C++ class +// If you want to use the header in QML, use FlexibleHeader + +Item { + anchors.fill: parent + + FlexibleHeader { + anchors.fill: parent + icon: iconSource + title: caption + subtitle: description + buttonModel: buttonList + + onSearchTextChanged: mainView.setFilterText(searchText) + onCurrentButtonIndexChanged: mainView.viewModeSelected(currentButtonIndex) + } +} diff --git a/data/qml/QmlGridView.qml b/data/qml/QmlGridView.qml new file mode 100644 index 0000000000..1d27a2d5e2 --- /dev/null +++ b/data/qml/QmlGridView.qml @@ -0,0 +1,65 @@ +import QtQuick 1.1 +//import tomahawk 1.0 +import "tomahawkimports" + +Rectangle { + anchors.fill: parent + color: "black" + + Text { + id: fontMetrics + text: "Here's some sample text" + opacity: 0 + } + + GridView { + id: gridView + anchors.fill: parent + //anchors.rightMargin: scrollBar.width + + cellHeight: cellWidth + cellWidth: calculateCoverSize(gridView.width - 3) + + cacheBuffer: cellHeight * 5 + + function calculateCoverSize(rectWidth) { + var itemWidth = fontMetrics.width; + var itemsPerRow = Math.max( 1, Math.floor( rectWidth / itemWidth ) ); + + var remSpace = rectWidth - ( itemsPerRow * itemWidth ); + var extraSpace = remSpace / itemsPerRow; + return itemWidth + extraSpace; + + } + + model: mainModel + + delegate: CoverImage { + height: gridView.cellHeight// * 0.95 + width: gridView.cellWidth// * 0.95 + + showLabels: true + showMirror: false + artistName: model.artistName + trackName: model.trackName + artworkId: model.coverID + showPlayButton: true + currentlyPlaying: isPlaying + smooth: !gridView.moving + + onClicked: { + rootView.onItemClicked(index) + } + onPlayClicked: { + rootView.onItemPlayClicked(index) + } + } + } + + ScrollBar { + id: scrollBar + listView: gridView + orientation: Qt.Vertical + margin: -width + } +} diff --git a/data/qml/SpinnerTest.qml b/data/qml/SpinnerTest.qml new file mode 100644 index 0000000000..217b4dbcf2 --- /dev/null +++ b/data/qml/SpinnerTest.qml @@ -0,0 +1,34 @@ +import QtQuick 1.1 +import "tomahawkimports" + +Rectangle { +width: 1400 +height: 900 +color: "black" + +BusyIndicator { + anchors.centerIn: parent + anchors.horizontalCenterOffset: 200 + height: 200 + width: 200 +} + +Image { + id: svgSpinner + source: "../images/loading-animation.svg" + smooth: true + height: 200 + width: 200 + anchors.centerIn: parent + anchors.horizontalCenterOffset: -200 + + Timer { + running: true + repeat: true + interval: 200 + onTriggered: svgSpinner.rotation += 360 / 12 + } +} + + +} diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml new file mode 100644 index 0000000000..7bb79e1b47 --- /dev/null +++ b/data/qml/StationView.qml @@ -0,0 +1,162 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "tomahawkimports" +import "stations" +Rectangle { + id: scene + color: "black" + anchors.fill: parent + state: "list" + + FlexibleHeader { + id: header + anchors { + left: parent.left + top: parent.top + right: parent.right + } + height: defaultFontHeight * 4 + width: parent.width + icon: "../images/station.svg" + title: mainView.title + subtitle: generator.summary + showSearchField: false + showBackButton: stationListView.currentIndex > 0 + showNextButton: stationListView.currentIndex == 2 + nextButtonText: "Save" + + z: 1 //cover albumcovers that may leave their area + + onBackPressed: stationListView.decrementCurrentIndex() + onNextPressed: stationListView.incrementCurrentIndex() + } + + ListModel { + id: modeModel + ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml" } + ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml" } + ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "year" } + } + + VisualItemModel { + id: stationVisualModel + + StationCreatorPage1 { + height: scene.height - header.height + width: scene.width + model: modeModel + + onItemClicked: { + stationCreator.content = modeModel.get(index).creatorContent + stationListView.incrementCurrentIndex() + } + } + + StationCreatorPage2 { + id: stationCreator + height: stationListView.height + width: stationListView.width + + onNext: stationListView.incrementCurrentIndex() + } + + Item { + id: stationItem + height: stationListView.height + width: stationListView.width + + CoverFlip { + id: coverView + anchors.right: parent.right + anchors.top: parent.top + height: parent.height + width: parent.width + interactive: false + + backgroundColor: scene.color + + model: dynamicModel + currentIndex: currentlyPlayedIndex + + onItemPlayPauseClicked: { + mainView.playItem(index) + } + + onItemClicked: { + mainView.playItem(index) + } + + states: [ + State { + name: "empty"; when: mainView.loading + PropertyChanges { + target: coverView + anchors.rightMargin: -coverView.width + anchors.topMargin: - coverView.height + scale: 0 + } + } + ] + transitions: [ + Transition { + from: "empty" + to: "*" + NumberAnimation { + properties: "anchors.topMargin,anchors.rightMargin,scale" + duration: 1000 + easing.type: Easing.OutQuad + } + } + + ] +// Behavior on anchors.topMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on anchors.rightMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on scale { +// NumberAnimation { duration: 500 } +// } + + } + BusyIndicator { + id: busyIndicator + anchors.centerIn: parent + height: defaultFontHeight * 4 + width: height + + opacity: mainView.loading ? 1 : 0 + running: mainView.loading + } + + } + + } + + + ListView { + id: stationListView + anchors { + left: parent.left + top: header.bottom + right: parent.right + bottom: parent.bottom + } + + contentHeight: height + contentWidth: width + orientation: ListView.Horizontal + model: stationVisualModel + interactive: false + highlightMoveDuration: 300 + + onHeightChanged: { + contentHeight = scene.height + } + onWidthChanged: { + contentWidth = scene.width + } + } + +} diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml new file mode 100644 index 0000000000..f3f2f4ceb3 --- /dev/null +++ b/data/qml/stations/CreateByArtist.qml @@ -0,0 +1,69 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + anchors.fill: parent + + signal done() + + function createStation(artist) { + mainView.startStationFromArtist(artist) + root.done() + } + + Column { + id: upperColumn + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height + width: defaultFontHeight * 30 + anchors.bottomMargin: defaultFontHeight + spacing: defaultFontHeight + + HeaderLabel { + id: headerText + text: "Create station by artist..." + } + + Row { + height: artistInputField.height + width: parent.width + spacing: defaultFontHeight * 0.5 + + InputField { + id: artistInputField + width: parent.width - createFromInputButton.width - parent.spacing + + onAccepted: createStation(text) + } + + PushButton { + id: createFromInputButton + text: "Go!" + enabled: artistInputField.text.length > 2 + onClicked: createStation(artistInputField.text) + } + } + + Item { + height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3 + width: parent.width + ArtistView { + id: artistView + height: parent.height + width: parent.width + model: artistChartsModel + clip: true + delegateHeight: defaultFontHeight * 6 + + onItemClicked: { + createStation(artistChartsModel.itemFromIndex(index).artistName); + } + } + ScrollBar { + listView: artistView + } + } + } +} diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml new file mode 100644 index 0000000000..9d2fb1b9b9 --- /dev/null +++ b/data/qml/stations/CreateByGenre.qml @@ -0,0 +1,88 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + anchors.fill: parent + + signal done() + + function createStation(genre) { + mainView.startStationFromGenre(genre) + root.done() + } + + ListModel { + id: styleModel + ListElement { modelData: "acoustic" } + ListElement { modelData: "alternative" } + ListElement { modelData: "alternative rock" } + ListElement { modelData: "classic" } + ListElement { modelData: "folk" } + ListElement { modelData: "indie" } + ListElement { modelData: "pop" } + ListElement { modelData: "rock" } + ListElement { modelData: "hip-hop" } + ListElement { modelData: "punk" } + ListElement { modelData: "grunge" } + ListElement { modelData: "indie" } + ListElement { modelData: "electronic" } + ListElement { modelData: "country" } + ListElement { modelData: "jazz" } + ListElement { modelData: "psychodelic" } + ListElement { modelData: "soundtrack" } + ListElement { modelData: "reggae" } + ListElement { modelData: "house" } + ListElement { modelData: "drum and base" } + } + + Column { + id: upperColumn + anchors.fill: parent + anchors.bottomMargin: defaultFontHeight + spacing: defaultFontHeight + + HeaderLabel { + id: headerText + anchors.horizontalCenter: parent.horizontalCenter + text: "Create station by genre..." + } + + Row { + width: defaultFontHeight * 30 + height: genreInputField.height + spacing: defaultFontHeight * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + + InputField { + id: genreInputField + width: parent.width - createFromInputButton.width - parent.spacing + + onAccepted: createStation(text); + } + + PushButton { + id: createFromInputButton + text: "Go!" + height: genreInputField.height + enabled: genreInputField.text.length > 2 + onClicked: createStation(genreInputField.text) + } + } + + Item { + height: parent.height - headerText.height - genreInputField.height + width: parent.width + TagCloud { + anchors.fill: parent + anchors.margins: parent.width / 6 + model: styleModel + + onTagClicked: { + root.createStation(tag); + } + } + } + } +} diff --git a/data/qml/stations/StationConfig.qml b/data/qml/stations/StationConfig.qml new file mode 100644 index 0000000000..d6f0ce11df --- /dev/null +++ b/data/qml/stations/StationConfig.qml @@ -0,0 +1,79 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "tomahawkimports" + +Item { + id: fineTuneView + + property color textColor: "white" + + signal done(); + + Grid { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 50 + anchors.horizontalCenter: parent.horizontalCenter + width: scene.width / 2 + spacing: 50 + columns: 2 + + Text { + color: fineTuneView.textColor + text: "Name:" + + } + InputField { + text: echonestStation.name + + onAccepted: { + print("text changed!!!") + echonestStation.name = text; + } + } + + Text { + id: tempoText + text: "Tempo:" + color: "white" + } + DoubleSlider { + width: 500 + height: tempoText.height + min: 0 + max: 500 + lowerSliderPos: echonestStation.minTempo + upperSliderPos: echonestStation.maxTempo + onValueChanged: echonestStation.setTempo( lowerSliderPos, upperSliderPos ) + } + + Text { + id: hotnessText + text: "Hotness:" + color: "white" + } + DoubleSlider { + width: 500 + height: hotnessText.height + min: 0 + max: 100 + minLabel: "Less" + maxLabel: "More" + showFloatingLabel: false + lowerSliderPos: echonestStation.minHotttness * 100 + upperSliderPos: echonestStation.maxHotttness * 100 + onValueChanged: echonestStation.setHotttness( 1.0 * lowerSliderPos / 100, 1.0 * upperSliderPos / 100 ) + } + } + + + Button { + id: configureButton + onClicked: fineTuneView.done(); + text: "configure" + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + } + +} diff --git a/data/qml/stations/StationCreatorPage1.qml b/data/qml/stations/StationCreatorPage1.qml new file mode 100644 index 0000000000..826d69d691 --- /dev/null +++ b/data/qml/stations/StationCreatorPage1.qml @@ -0,0 +1,64 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + + +Item { + id: root + property alias model: gridView.model + property int spacing: 10 + + signal itemClicked(int index) + + GridView { + id: gridView + anchors.centerIn: parent + width: root.width * 9 / 10 + height: cellHeight + + cellWidth: (width - 1) / 3 + cellHeight: cellWidth //* 10 / 16 + + delegate: Image { + width: gridView.cellWidth - root.spacing + height: gridView.cellHeight - root.spacing + source: image + smooth: true + + Rectangle { + id: textBackground + anchors { + left: parent.left + bottom: parent.bottom + right: parent.right + } + height: parent.height / 5 + color: "black" + opacity: .5 + + } + Text { + anchors.centerIn: textBackground + text: label + color: "white" + font.bold: true + } + Rectangle { + id: hoverShade + anchors.fill: parent + color: "white" + opacity: mouseArea.containsMouse ? .2 : 0 + + Behavior on opacity { + NumberAnimation { easing.type: Easing.Linear; duration: 300 } + } + } + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: root.itemClicked(index) + } + } + } +} diff --git a/data/qml/stations/StationCreatorPage2.qml b/data/qml/stations/StationCreatorPage2.qml new file mode 100644 index 0000000000..34f4b65cda --- /dev/null +++ b/data/qml/stations/StationCreatorPage2.qml @@ -0,0 +1,25 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + + property int margins: defaultFontHeight * 2 + property alias content: contentLoader.source + + signal next() + + Loader { + id: contentLoader + anchors.fill: parent + anchors.margins: root.margins + } + + Connections { + target: contentLoader.item + + onDone: root.next() + } + +} diff --git a/data/qml/tomahawkimports/ArtistView.qml b/data/qml/tomahawkimports/ArtistView.qml new file mode 100644 index 0000000000..a35c9b4284 --- /dev/null +++ b/data/qml/tomahawkimports/ArtistView.qml @@ -0,0 +1,71 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +ListView { + id: root + + property int delegateHeight: defaultFontHeight * 3 + + signal itemClicked(int index) + + delegate: Item { + width: parent.width + height: root.delegateHeight + + Rectangle { + id: background + anchors.fill: parent + radius: defaultFontHeight / 2 + opacity: 0.5 + gradient: Gradient { + GradientStop { position: 0.0; color: "#00FFFFFF" } + GradientStop { position: 1.0; color: "#AAFFFFFF" } + } + + states: [ + State { + name: "hovered"; when: mouseArea.containsMouse + PropertyChanges { target: background; opacity: 1 } + } + ] + + transitions: [ + Transition { + from: "*"; to: "hovered" + NumberAnimation { properties: "opacity"; duration: 100 } + }, + Transition { + from: "hovered"; to: "*" + NumberAnimation { properties: "opacity"; duration: 600 } + } + ] + } + + Row { + anchors.fill: parent + spacing: defaultFontHeight + + CoverImage { + id: coverImage + height: parent.height + width: height + showLabels: false + artworkId: model.coverID + } + Text { + text: model.artistName + color: "white" + anchors.verticalCenter: parent.verticalCenter + width: parent.width - coverImage.width - parent.spacing + elide: Text.ElideRight + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.itemClicked(index) + hoverEnabled: true + } + } +} diff --git a/data/qml/tomahawkimports/BusyIndicator.qml b/data/qml/tomahawkimports/BusyIndicator.qml new file mode 100644 index 0000000000..52b6e0fba3 --- /dev/null +++ b/data/qml/tomahawkimports/BusyIndicator.qml @@ -0,0 +1,52 @@ +import QtQuick 1.1 + +Item { + id: busyIndicator + width: 100 + height: width + property int barWidth: width / 10 + property int barHeight: height / 4 + property int count: 12 + property color color: "white" + property int currentHighlight: 0 + property bool running: true + property int interval: 200 + + Behavior on opacity { + NumberAnimation { duration: 500 } + } + + Repeater { + model: busyIndicator.count + + + Item { + height: parent.height + width: busyIndicator.barWidth + anchors.centerIn: parent + Rectangle { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: busyIndicator.barHeight + radius: width / 2 + + color: busyIndicator.color + } + rotation: 360 / busyIndicator.count * index + opacity: 1 - ((index > busyIndicator.currentHighlight ? busyIndicator.currentHighlight + busyIndicator.count : busyIndicator.currentHighlight) - index) / busyIndicator.count + Behavior on opacity { + NumberAnimation { duration: busyIndicator.interval } + } + } + } + + Timer { + interval: busyIndicator.interval + running: busyIndicator.running + repeat: true + onTriggered: parent.currentHighlight = (parent.currentHighlight + 1) % busyIndicator.count + } +} diff --git a/data/qml/tomahawkimports/Button.qml b/data/qml/tomahawkimports/Button.qml new file mode 100644 index 0000000000..1c73d7c8c8 --- /dev/null +++ b/data/qml/tomahawkimports/Button.qml @@ -0,0 +1,29 @@ +import QtQuick 1.1 + +Rectangle { + id: root + color: buttonMouseArea.containsMouse ? "blue" : "gray" + border.width: 2 + border.color: "white" + radius: height/2 + height: buttonText.height * 1.2 + width: buttonText.width * 1.5 + + property alias text: buttonText.text + property color textColor: "white" + + signal clicked() + + Text { + id: buttonText + anchors.centerIn: parent + color: root.textColor + } + + MouseArea { + id: buttonMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: root.clicked(); + } +} diff --git a/data/qml/tomahawkimports/CoverFlip.qml b/data/qml/tomahawkimports/CoverFlip.qml new file mode 100644 index 0000000000..58e85824c4 --- /dev/null +++ b/data/qml/tomahawkimports/CoverFlip.qml @@ -0,0 +1,137 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +PathView { + id: coverView + + // The start coordinates for the covers + // Default is left, centered in height + property int pathStartX: 0 + property int pathStartY: height + + // The size of the covers in the path + property int coverSize: height + + property color backgroundColor: "black" + + // emitted when a cover is clicked + signal itemClicked(int index) + + // emitted when a cover is clicked + signal itemPlayPauseClicked(int index) + + preferredHighlightBegin: 0.2 // scene.width / 11000 + preferredHighlightEnd: preferredHighlightBegin + pathItemCount: 5 + //highlightMoveDuration: 500 + + property bool itemHovered: false + + delegate: Item { + id: delegateItem + height: coverView.coverSize + width: coverView.coverSize + + scale: PathView.itemScale + // itemBrightness: PathView.itemBrightness - ((coverView.itemHovered && !coverDelegate.containsMouse) ? .4 : 0) + property double itemBrightness: PathView.itemBrightness + property double itemOpacity: PathView.itemOpacity + property int _origZ + + z: coverView.width - x + + CoverImage { + id: coverDelegate + height: coverView.coverSize + width: coverView.coverSize + anchors { + top: parent.top + right: parent.right + } + + backgroundColor: coverView.backgroundColor + + showLabels: true + showMirror: true + artistName: model.artistName + trackName: model.trackName + artworkId: model.coverID + showPlayButton: true + currentlyPlaying: isPlaying + smooth: true + + // itemBrightness: PathView.itemBrightness - ((coverView.itemHovered && !coverDelegate.containsMouse) ? .4 : 0) + itemBrightness: coverDelegate.containsMouse ? 1 : parent.itemBrightness * (coverView.itemHovered ? .5 : 1) + opacity: parent.itemOpacity + z: coverView.width - x + + onPlayClicked: { + console.log("***************") + coverView.itemPlayPauseClicked(index) + } + + onClicked: { + coverView.itemClicked(index) + } + + onContainsMouseChanged: { + if (containsMouse) { + delegateItem._origZ = delegateItem.z; + coverView.itemHovered = true + } else { + coverView.itemHovered = false + } + } + + + } + states: [ + State { + name: "hovered"; when: coverDelegate.containsMouse && !coverView.moving && index !== currentIndex + PropertyChanges { + target: delegateItem + width: coverView.coverSize * 2 + z: delegateItem._origZ + } + } + ] + transitions: [ + Transition { + NumberAnimation { + properties: "width" + duration: 300 + easing.type: Easing.InOutSine + } + + } + ] + } + + path: Path { + startX: coverView.pathStartX + startY: coverView.pathStartY + + PathAttribute { name: "itemOpacity"; value: 0 } + PathAttribute { name: "itemBrightness"; value: 0 } + PathAttribute { name: "itemScale"; value: 1.3 } + + PathLine { x: coverView.width / 4; y: coverView.height / 4 * 3} + PathPercent { value: 0.1 } + PathAttribute { name: "itemOpacity"; value: 0 } + PathAttribute { name: "itemBrightness"; value: 1 } + PathAttribute { name: "itemScale"; value: 1.0 } + + PathLine { x: coverView.width / 2; y: coverView.height / 2} + PathPercent { value: 0.2 } + PathAttribute { name: "itemOpacity"; value: 1 } + PathAttribute { name: "itemBrightness"; value: 1 } + PathAttribute { name: "itemScale"; value: 0.5 } + + PathLine { x: coverView.width; y: 0 } + PathPercent { value: 1 } + PathAttribute { name: "itemOpacity"; value: 1 } + PathAttribute { name: "itemBrightness"; value: 0 } + PathAttribute { name: "itemScale"; value: 0.1 } + } + +} diff --git a/data/qml/tomahawkimports/CoverImage.qml b/data/qml/tomahawkimports/CoverImage.qml new file mode 100644 index 0000000000..518137aac8 --- /dev/null +++ b/data/qml/tomahawkimports/CoverImage.qml @@ -0,0 +1,197 @@ +import QtQuick 1.1 + +Item { + id: root + + // Should the artist + track labels be painted + property bool showLabels: true + + // Should the play button be painted on mouse hover? + property bool showPlayButton: false + + // if this is true, the play button will be swapped by a pause button + property bool currentlyPlaying: false + + // Should the mirror be painted? + property bool showMirror: false + + // Labels & Cover + property string artistName + property string trackName + property string artworkId + + // The border color for the cover image + property color borderColor: "black" + // The border width for the cover image + property int borderWidth: 2 + + // needed to adjust the shadow + property color backgroundColor: "black" + + // sets the brightness for the item and its mirror (1: brightest, 0: darkest) + property double itemBrightness: 1 + property double mirrorBrightness: .5 + + // set this to true if you want to smoothly scale the cover (be aware of performance impacts) + property bool smooth: false + + // will be emitted when the on hower play button is clicked + signal playClicked() + // will be emitted when the cover is clicked + signal clicked() + // will be emitted when the cover is hovered by the mouse + property alias containsMouse: mouseArea.containsMouse + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + + onClicked: { + print("Cover clicked"); + root.clicked(); + } + } + + Rectangle { + id: itemShadow + color: backgroundColor + anchors.fill: parent + + //opacity: 1 - itemBrightness + + Behavior on opacity { + NumberAnimation { easing.type: Easing.Linear; duration: 300 } + } + } + + Component { + id: coverImage + + Item { + property bool isMirror: false + + Image { + anchors.fill: parent + source: "image://albumart/" + artworkId + (isMirror ? "-mirror" : "") + (showLabels ? "-labels" : "") + smooth: root.smooth + opacity: itemBrightness + Behavior on opacity { + NumberAnimation { duration: 300 } + } + } + + Rectangle { + id: itemGlow + anchors.fill: parent + anchors.topMargin: isMirror ? parent.height / 2 : 0 + + opacity: (mouseArea.containsMouse ? .2 : 0) + + Gradient { + id: glowGradient + GradientStop { position: 0.0; color: "white" } + GradientStop { position: 0.7; color: "white" } + GradientStop { position: 0.8; color: "#00000000" } + GradientStop { position: 1.0; color: "#00000000" } + } + Gradient { + id: mirrorGlowGradient + GradientStop { position: 0.0; color: "#00000000" } + GradientStop { position: 0.5; color: "#00000000" } + GradientStop { position: 1.0; color: "#44FFFFFF" } + } + + states: [ + State { + name: "mirrored"; when: isMirror + PropertyChanges { + target: itemGlow + gradient: mirrorGlowGradient + } + }, + State { + name: "normal"; when: !isMirror + PropertyChanges { + target: itemGlow + gradient: glowGradient + } + } + ] + + Behavior on opacity { + NumberAnimation { easing.type: Easing.Linear; duration: 300 } + } + } + + Text { + id: trackText + color: "white" + font.bold: true + text: trackName + anchors { left: parent.left; right: parent.right; bottom: artistText.top } + anchors.margins: 2 + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + opacity: showLabels ? itemBrightness * (isMirror ? 0.5 : 1): 0 + font.pixelSize: root.height / 15 + Behavior on opacity { + NumberAnimation { duration: 300 } + } + } + Text { + id: artistText + color: "white" + font.bold: trackText.text.length == 0 + text: artistName + anchors { left: parent.left; right: parent.right; bottom: parent.bottom } + anchors.margins: root.height / 20 + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + opacity: showLabels ? itemBrightness * (isMirror ? 0.5 : 1) : 0 + font.pixelSize: trackText.text.length == 0 ? root.height / 10 : root.height / 15 + Behavior on opacity { + NumberAnimation { duration: 300 } + } + } + } + + } + Loader { + sourceComponent: coverImage + anchors.fill: parent + } + + Loader { + id: mirroredCover + sourceComponent: parent.showMirror ? coverImage : undefined + anchors.fill: parent + onLoaded: { + item.isMirror = true + } + transform : [ + Rotation { + angle: 180; origin.y: root.height + axis.x: 1; axis.y: 0; axis.z: 0 + } + ] + } + + Image { + id: playButton + visible: showPlayButton ? (mouseArea.containsMouse || currentlyPlaying) : false + source: currentlyPlaying ? "../../images/pause-rest.svg" : "../../images/play-rest.svg" + anchors.centerIn: parent + height: mirroredCover.height / 5 + width: height + smooth: root.smooth + MouseArea { + anchors.fill: parent + onClicked: { + print("Play button clicked"); + root.playClicked(); + } + } + } + +} diff --git a/data/qml/tomahawkimports/DoubleSlider.qml b/data/qml/tomahawkimports/DoubleSlider.qml new file mode 100644 index 0000000000..b1dc522cc0 --- /dev/null +++ b/data/qml/tomahawkimports/DoubleSlider.qml @@ -0,0 +1,151 @@ +import QtQuick 1.1 + +Item { + id: root + width: 500 + height: 10 + + property int min: 0 + property int max: 100 + + /** The labels next to the slider + * if empty, min and max values are used + */ + property string minLabel: "" + property string maxLabel: "" + + /** Should the floating label indicating the current position be shown? */ + property bool showFloatingLabel: true + + property int lowerSliderPos: 25 + property int upperSliderPos: 75 + + signal valueChanged() + + Row { + anchors.fill: parent + spacing: 10 + + Text { + id: minText + text: root.minLabel.length > 0 ? root.minLabel : min + color: "white" + } + + Item { + id: sliderRect + height: root.height + width: parent.width - minText.width - maxText.width - parent.spacing * 2 + + function sliderPosToValue( sliderPos ) { + var percent = sliderPos * 100 / (sliderRect.width - lowerSlider.width); + return Math.floor(percent * (root.max - root.min) / 100) + root.min + } + + function valueToSloderPos( value ) { + var percent = (value - root.min) * 100 / (root.max - root.min) + return percent * (sliderRect.width - lowerSlider.width) / 100 + } + + Rectangle { + id: sliderBase + height: root.height / 5 + width: parent.width + color: "white" + radius: height / 2 + anchors.centerIn: parent + + } + Rectangle { + id: lowerSlider + height: root.height + width: height + anchors.top: root.top + radius: height/2 + border.color: "black" + border.width: 2 + x: sliderRect.valueToSloderPos(root.lowerSliderPos) + + Rectangle { + id: lowerFloatingRect + color: "white" + anchors.bottom: lowerSlider.top + anchors.bottomMargin: 10 + visible: root.showFloatingLabel && lowerSliderMouseArea.pressed + width: lowerFloatingText.width * 1.2 + height: lowerFloatingText.height + height * 1.2 + x: -(width - lowerSlider.width) / 2 + radius: height / 4 + + Text { + id: lowerFloatingText + anchors.centerIn: parent + text: sliderRect.sliderPosToValue(lowerSlider.x) + } + } + } + MouseArea { + id: lowerSliderMouseArea + anchors.fill: lowerSlider + drag.target: lowerSlider + drag.axis: "XAxis" + drag.minimumX: 0 + drag.maximumX: upperSlider.x - lowerSlider.width + onReleased: { + root.lowerSliderPos = sliderRect.sliderPosToValue( lowerSlider.x ); + root.valueChanged(); + } + } + + Rectangle { + id: upperSlider + height: root.height + width: height + anchors.top: root.top + radius: height/2 + border.color: "black" + border.width: 2 + x: sliderRect.valueToSloderPos(root.upperSliderPos) + Rectangle { + id: upperFloatingRect + color: "white" + anchors.bottom: upperSlider.top + anchors.bottomMargin: 10 + visible: root.showFloatingLabel && upperSliderMouseArea.pressed + width: upperFloatingText.width * 1.2 + height: upperFloatingText.height + height * 1.2 + radius: height / 4 + x: -(width - upperSlider.width) / 2 + + Text { + id: upperFloatingText + anchors.centerIn: parent + text: sliderRect.sliderPosToValue(upperSlider.x) + } + } + + } + MouseArea { + id: upperSliderMouseArea + anchors.fill: upperSlider + onClicked: print("button pressed") + drag.target: upperSlider + drag.axis: "XAxis" + drag.minimumX: lowerSlider.x + lowerSlider.width + drag.maximumX: parent.width - upperSlider.width + onReleased: { + root.upperSliderPos = sliderRect.sliderPosToValue( upperSlider.x ); + root.valueChanged(); + } + + } + } + + + Text { + id: maxText + text: root.maxLabel.length > 0 ? root.maxLabel : max + color: "white" + } + } +} diff --git a/data/qml/tomahawkimports/FlexibleHeader.qml b/data/qml/tomahawkimports/FlexibleHeader.qml new file mode 100644 index 0000000000..04c5e2566e --- /dev/null +++ b/data/qml/tomahawkimports/FlexibleHeader.qml @@ -0,0 +1,199 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Rectangle { + id: root + + // The icon + property alias icon: iconImage.source + + // The title + property alias title: titleItem.titleText + + // The subtitle/description + property alias subtitle: subtitleText.text + + // The model for the ToggleViewButtons. + // "modelData" role name holds the iconSource + // => You can use a QStringList or StandardListModel here + property alias buttonModel: toggleViewButtons.model + + // The index of the currently selected item + property alias currentButtonIndex: toggleViewButtons.currentIndex + + // Should we show the searchfield? + property bool showSearchField: true + + // The SearchFields text + property alias searchText: searchField.text + + property bool showBackButton: false + property bool showNextButton: false + + property string backButtonText: "Back" + property string nextButtonText: "Next" + + // Layout spacing + property int spacing: defaultFontHeight * 0.5 + + signal backPressed() + signal nextPressed() + signal savePressed() + + gradient: Gradient { + GradientStop { position: 0.0; color: "#615858" } + GradientStop { position: 1.0; color: "#231F1F" } + } + + Row { + id: leftRow + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom + right: rightRow.left + } + + anchors.margins: root.spacing + spacing: root.spacing + + Image { + id: iconImage + height: parent.height * 0.8 + width: height + anchors.verticalCenter: parent.verticalCenter + smooth: true + } + + Column { + height: parent.height + width: parent.width - iconImage.width - parent.spacing + + Item { + id: titleItem + height: captionText1.height + width: parent.width + clip: true + + property string titleText + + onTitleTextChanged: { + if(captionText1.text.length > 0) { + captionText2.text = titleText; + renewTitleAnimation.start(); + } else { + captionText1.text = titleText; + } + } + + ParallelAnimation { + id: renewTitleAnimation + property int duration: 500 + property variant easingType: Easing.OutBounce; + + NumberAnimation { target: captionText2; property: "anchors.topMargin"; to: 0; duration: renewTitleAnimation.duration; easing.type: renewTitleAnimation.easingType } + NumberAnimation { target: captionText1; property: "anchors.topMargin"; to: captionText1.height * 2; duration: renewTitleAnimation.duration; easing.type: renewTitleAnimation.easingType } + + onCompleted: { + captionText1.text = titleItem.titleText + captionText2.anchors.topMargin = -captionText2.height * 2 + captionText1.anchors.topMargin = 0 + } + } + + Text { + id: captionText1 + color: "white" + anchors.left: parent.left + anchors.top: parent.top + + font.pointSize: defaultFontSize * 1.5 + font.bold: true + width: parent.width + elide: Text.ElideRight + } + Text { + id: captionText2 + color: "white" + anchors.left: parent.left + anchors.top: parent.top + anchors.topMargin: -height * 2 + font.pointSize: defaultFontSize * 1.5 + font.bold: true + width: parent.width + elide: Text.ElideRight + } + + } + Text { + id: subtitleText + color: "white" + font.pointSize: defaultFontSize * 1.2 + width: parent.width + elide: Text.ElideRight + } + } + + } + + Row { + id: rightRow + anchors { + top: parent.top + right: parent.right + rightMargin: -backButton.width - root.spacing - nextButton.width + bottom: parent.bottom + margins: root.spacing + } + width: childrenRect.width + spacing: root.spacing + layoutDirection: Qt.RightToLeft + + states: [ + State { + name: "oneVisible"; when: root.showBackButton && !root.showNextButton + PropertyChanges { + target: rightRow + anchors.rightMargin: -nextButton.width + } + }, + State { + name: "bothVisible"; when: root.showBackButton && root.showNextButton + PropertyChanges { + target: rightRow + anchors.rightMargin: root.spacing + } + } + + ] + + Behavior on anchors.rightMargin { + NumberAnimation { duration: 200 } + } + + PushButton { + id: nextButton + anchors.verticalCenter: parent.verticalCenter + text: root.nextButtonText + onClicked: root.nextPressed(); + } + PushButton { + id: backButton + anchors.verticalCenter: parent.verticalCenter + text: root.backButtonText + onClicked: root.backPressed(); + } + InputField { + id: searchField + visible: root.showSearchField + anchors.verticalCenter: parent.verticalCenter + placeholderText: "Search..." + showSearchIcon: true + } + ToggleViewButtons { + id: toggleViewButtons + anchors.verticalCenter: parent.verticalCenter + height: defaultFontHeight * 1.5 + } + } +} diff --git a/data/qml/tomahawkimports/HeaderLabel.qml b/data/qml/tomahawkimports/HeaderLabel.qml new file mode 100644 index 0000000000..ebffedbd48 --- /dev/null +++ b/data/qml/tomahawkimports/HeaderLabel.qml @@ -0,0 +1,7 @@ +import QtQuick 1.1 + +Text { + color: "white" + font.pointSize: defaultFontSize + 5 + font.bold: true +} diff --git a/data/qml/tomahawkimports/InputField.qml b/data/qml/tomahawkimports/InputField.qml new file mode 100644 index 0000000000..9f052124de --- /dev/null +++ b/data/qml/tomahawkimports/InputField.qml @@ -0,0 +1,90 @@ +import QtQuick 1.1 + +Rectangle { + id: root + color: "white" + border.color: "black" + border.width: defaultFontHeight * 0.1 + radius: defaultFontHeight * 0.25 + + height: textInput.height * 1.4 + width: 300 + + property bool showSearchIcon: false + property string text: "" + property string placeholderText: "" + + property int spacing: defaultFontHeight * 0.2 + signal accepted( string text ) + + Image { + id: searchIcon + anchors { + left: parent.left + leftMargin: root.spacing + verticalCenter: parent.verticalCenter + } + height: parent.height * 0.6 + width: root.showSearchIcon ? height : 1 + opacity: root.showSearchIcon ? 1 : 0 + smooth: true + source: "../../images/search-icon.svg" + } + + Item { + id: textItem + anchors.left: searchIcon.right + anchors.leftMargin: root.spacing + anchors.right: clearIcon.right + anchors.rightMargin: root.spacing + height: textInput.height + anchors.verticalCenter: parent.verticalCenter + + TextInput { + id: textInput + width: parent.width + anchors.centerIn: parent + text: root.text + font.pointSize: defaultFontSize + + onAccepted: root.accepted( text ); + onTextChanged: root.text = text; + } + Text { + width: parent.width + anchors.centerIn: parent + text: root.text.length === 0 ? root.placeholderText : "" + color: "lightgray" + font.pointSize: defaultFontSize + } + } + + Image { + id: clearIcon + anchors { + right: parent.right + rightMargin: root.spacing + verticalCenter: parent.verticalCenter + } + height: parent.height * 0.8 + width: (root.showSearchIcon && root.text.length > 0) ? height : 1 + opacity: (root.showSearchIcon && root.text.length > 0) ? 1 : 0 + smooth: true + source: "../../images/search-box-dismiss-x.svg" + + MouseArea { + anchors.fill: parent + onClicked: textInput.text = "" + } + } + + + BorderImage { + source: "../../images/inputfield-border.svg" + anchors.fill: parent + anchors.margins: root.radius * 0.1 + clip: true + border.left: defaultFontHeight/4; border.top: defaultFontHeight/4 + border.right: defaultFontHeight/4; border.bottom: defaultFontHeight/4 + } +} diff --git a/data/qml/tomahawkimports/PushButton.qml b/data/qml/tomahawkimports/PushButton.qml new file mode 100644 index 0000000000..b2c4b25035 --- /dev/null +++ b/data/qml/tomahawkimports/PushButton.qml @@ -0,0 +1,34 @@ +import QtQuick 1.1 +//import tomahawk 1.0 + +Rectangle { + id: root + height: buttonText.height * 1.4 + width: buttonText.width + (spacing * 2) + radius: defaultFontHeight * 0.25 + border.width: defaultFontHeight * 0.05 + border.color: "#a7a7a7" + + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? "#040404" : "#fbfbfb" } + GradientStop { position: 1.0; color: mouseArea.pressed ? "#8e8f8e" : "#787878" } + } + + property int spacing: defaultFontHeight * 0.5 + property alias text: buttonText.text + + signal clicked() + + Text { + id: buttonText + anchors.centerIn: root + font.pointSize: defaultFontSize + color: mouseArea.pressed ? "white" : "black" + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.clicked() + } +} diff --git a/data/qml/tomahawkimports/RoundedButton.qml b/data/qml/tomahawkimports/RoundedButton.qml new file mode 100644 index 0000000000..790afe227a --- /dev/null +++ b/data/qml/tomahawkimports/RoundedButton.qml @@ -0,0 +1,43 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Rectangle { + id: root + border.width: 4 + border.color: enabled ? "white" : "grey" + radius: height / 2 + color: (buttonMouseArea.containsMouse && enabled) ? "#22ffffff" : "black" + opacity: hidden ? 0 : 1 + + height: defaultFontHeight * 2 + width: height + + property string text + property bool enabled: true + property bool hidden: false + + signal clicked() + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + Behavior on color { + ColorAnimation { duration: 200 } + } + + Text { + anchors.centerIn: parent + text: parent.text + color: root.border.color + font.pixelSize: parent.height * .75 + font.bold: true + } + MouseArea { + id: buttonMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: root.enabled + onClicked: parent.clicked() + } +} diff --git a/data/qml/tomahawkimports/ScrollBar.qml b/data/qml/tomahawkimports/ScrollBar.qml new file mode 100644 index 0000000000..351e378030 --- /dev/null +++ b/data/qml/tomahawkimports/ScrollBar.qml @@ -0,0 +1,69 @@ +import QtQuick 1.1 + +Item { + id: scrollBar + width: defaultFontHeight / 2 + + // the ListView where to attach this scrollbar + property variant listView + // the orientation of the scrollbar + property variant orientation : Qt.Vertical + + property int margin: defaultFontHeight * 0.25 + + states: [ + State { + name: "hidden"; when: !listView.moving + PropertyChanges { target: scrollBar; opacity: 0 } + }, + State { + name: "visible"; when: listView.moving + PropertyChanges { target: scrollBar; opacity: 1 } + } + ] + transitions: [ + Transition { + from: "hidden" + to: "visible" + NumberAnimation { properties: "opacity"; duration: 200 } + }, + Transition { + from: "visible" + to: "hidden" + NumberAnimation { properties: "opacity"; duration: 2000 } + } + ] + + anchors { + left: orientation == Qt.Vertical ? listView.right : listView.left + leftMargin: orientation == Qt.Vertical ? scrollBar.margin : 0 + top: orientation == Qt.Vertical ? listView.top : listView.bottom + topMargin: orientation == Qt.Vertical ? 0 : scrollBar.margin + bottom: orientation == Qt.Vertical ? listView.bottom : undefined + right: orientation == Qt.Vertical ? undefined : listView.right + } + + // A light, semi-transparent background + Rectangle { + id: background + anchors.fill: parent + radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1) + color: "white" + opacity: 0.2 + clip: true + // Size the bar to the required size, depending upon the orientation. + Rectangle { + property real position: orientation == Qt.Vertical ? (listView.contentY / listView.contentHeight) : (listView.contentX / listView.contentWidth) + property real pageSize: orientation == Qt.Vertical ? (listView.height / listView.contentHeight) : (listView.width / listView.contentWidth) + + x: orientation == Qt.Vertical ? 1 : (position * (scrollBar.width-2) + 1) + y: orientation == Qt.Vertical ? (position * (scrollBar.height-2) + 1) : 1 + width: orientation == Qt.Vertical ? (parent.width-2) : (pageSize * (scrollBar.width-2)) + height: orientation == Qt.Vertical ? (pageSize * (scrollBar.height-2)) : (parent.height-2) + radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1) + color: "white" + opacity: 1 + } + } + +} diff --git a/data/qml/tomahawkimports/TagCloud.qml b/data/qml/tomahawkimports/TagCloud.qml new file mode 100644 index 0000000000..ba3de03aa2 --- /dev/null +++ b/data/qml/tomahawkimports/TagCloud.qml @@ -0,0 +1,80 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Item { + id: tagCloud + + property variant model: 10 + + signal tagClicked( string tag ) + + function randomNumber(min, max) { + var date = new Date(); + return (max - min) * Math.random(date.getSeconds()) + min + } + + Flow { + anchors.centerIn: parent + width: parent.width + spacing: 3 + + Repeater { + id: cloudRepeater + model: tagCloud.model + + delegate: Item { + id: cloudItem + width: delegateText.width * 1.1 + height: delegateText.height + property double itemScale: Math.random() + .3 + scale: itemScale + Text { + id: delegateText + color: "gray" + //text: controlModel.controlAt( index ).summary + text: modelData + font.pointSize: 16 + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15) + + states: [ + State { + name: "hovered"; when: cloudItemMouseArea.containsMouse + PropertyChanges { + target: delegateText + color: "white" + } + } + ] + transitions: [ + Transition { + from: "*" + to: "hovered" + ColorAnimation { + duration: 200 + } + }, + Transition { + from: "hovered" + to: "*" + ColorAnimation { + duration: 1000 + } + } + ] + + } + MouseArea { + id: cloudItemMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: tagCloud.tagClicked( modelData ) + } + + Behavior on scale { + NumberAnimation { easing: Easing.Linear; duration: 1000 } + } + } + } + } +} diff --git a/data/qml/tomahawkimports/ToggleViewButtons.qml b/data/qml/tomahawkimports/ToggleViewButtons.qml new file mode 100644 index 0000000000..066d5f4c9e --- /dev/null +++ b/data/qml/tomahawkimports/ToggleViewButtons.qml @@ -0,0 +1,34 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Row { + id: root + width: repeater.width + + property alias model: repeater.model + property int currentIndex: 0 + + Repeater { + id: repeater + height: root.height + width: count * height + + + delegate: Image { + height: repeater.height + width: height + + source: "../../images/view-toggle-" + (index === root.currentIndex ? "active-" : "inactive-" ) + (index === 0 ? "left" : ( index === repeater.count - 1 ? "right" : "centre" )) + ".svg" + smooth: true + Image { + anchors.fill: parent + source: "../../images/" + modelData + (index === root.currentIndex ? "-active.svg" : "-inactive.svg") + } + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.currentIndex = index + } + } + } +} From 2704f1261d5b229879afd00bad65148c3d38c37d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:53:56 +0200 Subject: [PATCH 002/565] * Imported QML specific widgets / views / providers. --- .../dynamic/widgets/DynamicQmlWidget.cpp | 266 ++++++++++++++++++ .../dynamic/widgets/DynamicQmlWidget.h | 109 +++++++ .../widgets/DeclarativeCoverArtProvider.cpp | 142 ++++++++++ .../widgets/DeclarativeCoverArtProvider.h | 23 ++ src/libtomahawk/widgets/DeclarativeView.cpp | 58 ++++ src/libtomahawk/widgets/DeclarativeView.h | 61 ++++ 6 files changed, 659 insertions(+) create mode 100644 src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp create mode 100644 src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h create mode 100644 src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp create mode 100644 src/libtomahawk/widgets/DeclarativeCoverArtProvider.h create mode 100644 src/libtomahawk/widgets/DeclarativeView.cpp create mode 100644 src/libtomahawk/widgets/DeclarativeView.h diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp new file mode 100644 index 0000000000..c4b149df1d --- /dev/null +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -0,0 +1,266 @@ +#include "DynamicQmlWidget.h" + +#include "playlist/dynamic/DynamicModel.h" +#include "playlist/PlayableProxyModel.h" +#include "playlist/dynamic/DynamicModel.h" +#include "playlist/dynamic/echonest/EchonestControl.h" +#include "playlist/dynamic/GeneratorInterface.h" +#include "playlist/PlayableItem.h" +#include "Source.h" +#include "SourceList.h" +#include "audio/AudioEngine.h" +#include "database/Database.h" +#include "database/DatabaseCommand_PlaybackCharts.h" +#include "widgets/DeclarativeCoverArtProvider.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +#include +#include +#include +#include + +namespace Tomahawk +{ + +DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent ) + : DeclarativeView( parent ) + , m_playlist( playlist ) + , m_runningOnDemand( false ) + , m_activePlaylist( false ) + , m_playNextResolved( false ) +{ + m_model = new DynamicModel( this ); + + m_proxyModel = new PlayableProxyModel( this ); + m_proxyModel->setSourcePlayableModel( m_model ); + m_proxyModel->setShowOfflineResults( false ); + + m_model->loadPlaylist( m_playlist ); + + m_artistChartsModel = new PlayableModel( this ); + + + qmlRegisterUncreatableType("tomahawk", 1, 0, "Generator", "you cannot create it on your own - should be set in context"); + + rootContext()->setContextProperty( "dynamicModel", m_proxyModel ); + rootContext()->setContextProperty( "artistChartsModel", m_artistChartsModel ); + rootContext()->setContextProperty( "generator", m_playlist->generator().data() ); + rootContext()->setContextProperty( "currentlyPlayedIndex", QVariant::fromValue( 0 ) ); + + setSource( QUrl( "qrc" RESPATH "qml/StationView.qml" ) ); + + connect( m_model, SIGNAL( currentIndexChanged()), SLOT( currentIndexChanged() ) ); + connect( m_model, SIGNAL( loadingStarted() ), SIGNAL(loadingChanged() ) ); + connect( m_model, SIGNAL( loadingFinished() ), SIGNAL(loadingChanged() ) ); + connect( m_model, SIGNAL( changed() ), SIGNAL( titleChanged() ) ); + connect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); + connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( nextTrackGenerated( Tomahawk::query_ptr ) ) ); + connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); + connect( m_playlist->generator().data(), SIGNAL( error( QString, QString )), SLOT( error(QString,QString) ) ); + + connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); + connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) ); + + // m_playlist->generator()->generate( 20 ); + loadArtistCharts(); +} + + +DynamicQmlWidget::~DynamicQmlWidget() +{ +} + + +Tomahawk::playlistinterface_ptr +DynamicQmlWidget::playlistInterface() const +{ + return m_proxyModel->playlistInterface(); +} + + +QString +DynamicQmlWidget::title() const +{ + return m_model->title(); +} + + +QString +DynamicQmlWidget::description() const +{ + return m_model->description(); +} + + +QString +DynamicQmlWidget::iconSource() const +{ + return QLatin1String( RESPATH "images/station.png" ); +} + + +bool +DynamicQmlWidget::jumpToCurrentTrack() +{ + return true; +} + +playlist_ptr DynamicQmlWidget::playlist() const +{ + return m_model->playlist(); +} + +bool DynamicQmlWidget::loading() +{ + // Why does isLoading() not reset to true when cleared and station started again? +// return m_model->isLoading(); + return m_playNextResolved && m_proxyModel->rowCount() == 0; +} + +bool DynamicQmlWidget::configured() +{ + return !m_playlist->generator()->controls().isEmpty(); +} + +void DynamicQmlWidget::playItem(int index) +{ + tDebug() << "playItem called for cover" << index; + AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), m_proxyModel->itemFromIndex( index )->result() ); +} + +void DynamicQmlWidget::pause() +{ + AudioEngine::instance()->pause(); +} + +void DynamicQmlWidget::startStationFromArtist(const QString &artist) +{ + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromArtist(Artist::get(artist)); + emit loadingChanged(); + emit configuredChanged(); +} + +void DynamicQmlWidget::startStationFromGenre(const QString &genre) +{ + tDebug() << "should start startion from genre" << genre; + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromGenre( genre ); + emit loadingChanged(); + emit configuredChanged(); +} + +void DynamicQmlWidget::currentIndexChanged() +{ + tDebug() << "current index is" << m_model->currentItem().row(); + rootContext()->setContextProperty( "currentlyPlayedIndex", m_proxyModel->mapFromSource( m_model->currentItem() ).row() ); +} + +void +DynamicQmlWidget::tracksGenerated( const QList< query_ptr >& queries ) +{ + m_model->tracksGenerated( queries, queries.count() ); + m_playlist->resolve(); +} + +void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track) +{ + m_model->tracksGenerated( QList() << track ); + m_playlist->resolve(); + + connect( track.data(), SIGNAL( resolvingFinished( bool )), SLOT( resolvingFinished( bool ) ) ); + +} + +void DynamicQmlWidget::error(const QString &title, const QString &body) +{ + qDebug() << "got a generator error:" << title << body; + +// m_playlist->generator()->fetchNext(); + +} + +void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision) +{ + m_playlist->resolve(); +} + +void DynamicQmlWidget::resolvingFinished(bool hasResults) +{ + Q_UNUSED(hasResults) + qDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row(); + if( m_proxyModel->rowCount() <= m_proxyModel->currentIndex().row() + 8 ) { + qDebug() << "fetching next one"; + m_playlist->generator()->fetchNext(); + } + + if( m_playNextResolved && m_proxyModel->rowCount() > 0 ) { + playItem( 0 ); + m_playNextResolved = false; + } +} + +void DynamicQmlWidget::trackStarted() +{ + if ( m_activePlaylist && !m_playlist.isNull() && + m_playlist->mode() == OnDemand && !m_runningOnDemand ) + { + + startStation(); + } +} + +void +DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl ) +{ + if ( pl == m_proxyModel->playlistInterface() ) // same playlist + m_activePlaylist = true; + else + { + m_activePlaylist = false; + + // user started playing something somewhere else, so give it a rest + if ( m_runningOnDemand ) + { + stopStation( false ); + } + } +} + +void +DynamicQmlWidget::stopStation( bool stopPlaying ) +{ + m_model->stopOnDemand( stopPlaying ); + m_runningOnDemand = false; + +} + +void +DynamicQmlWidget::startStation() +{ + m_runningOnDemand = true; + m_model->startOnDemand(); +} + + +void +DynamicQmlWidget::loadArtistCharts() +{ + DatabaseCommand_PlaybackCharts* cmd = new DatabaseCommand_PlaybackCharts( SourceList::instance()->getLocal(), this ); + cmd->setLimit( 15 ); + connect( cmd, SIGNAL( artists( QList ) ), SLOT( onArtistCharts( QList< Tomahawk::artist_ptr > ) ), Qt::UniqueConnection ); + Database::instance()->enqueue( QSharedPointer( cmd ) ); +} + + +void +DynamicQmlWidget::onArtistCharts( const QList< Tomahawk::artist_ptr >& artists ) +{ + m_artistChartsModel->clear(); + m_artistChartsModel->appendArtists( artists ); + +} +} diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h new file mode 100644 index 0000000000..382bf314ac --- /dev/null +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -0,0 +1,109 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Michael Zanetti + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DYNAMIC_QML_WIDGET_H +#define DYNAMIC_QML_WIDGET_H + +#include "ViewPage.h" +#include "Typedefs.h" +#include "widgets/DeclarativeView.h" + +#include + +class PlayableModel; +class PlayableProxyModel; + +namespace Tomahawk +{ + +class DynamicModel; + +class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage +{ + Q_OBJECT + + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) + Q_PROPERTY(bool configured READ configured NOTIFY configuredChanged) + +public: + explicit DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent = 0 ); + virtual ~DynamicQmlWidget(); + + virtual QWidget* widget() { return this; } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const; + + virtual QString title() const; + virtual QString description() const; + virtual QString iconSource() const; + + virtual bool showInfoBar() const { return false; } + virtual bool showModes() const { return false; } + virtual bool showFilter() const { return false; } + + virtual bool jumpToCurrentTrack(); + + playlist_ptr playlist() const; + + bool loading(); + bool configured(); + +signals: + void loadingChanged(); + void configuredChanged(); + void titleChanged(); + +public slots: + void playItem(int index); + void pause(); + void startStationFromArtist(const QString &artist); + void startStationFromGenre(const QString &genre); + +private slots: + void currentIndexChanged(); + void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); + void nextTrackGenerated( const Tomahawk::query_ptr& track ); + void error( const QString& title, const QString& body); + + void onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ); + void playlistChanged( Tomahawk::playlistinterface_ptr pl ); + + void resolvingFinished( bool hasResults ); + + void trackStarted(); + void startStation(); + void stopStation( bool stopPlaying ); + + void loadArtistCharts(); + void onArtistCharts( const QList< Tomahawk::artist_ptr >& artists ); + +private: + DynamicModel* m_model; + PlayableProxyModel* m_proxyModel; + dynplaylist_ptr m_playlist; + + PlayableModel* m_artistChartsModel; + + bool m_runningOnDemand; + bool m_activePlaylist; + bool m_playNextResolved; +}; + +} + +#endif // DYNAMIC_QML_WIDGET_H diff --git a/src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp new file mode 100644 index 0000000000..49e743c861 --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp @@ -0,0 +1,142 @@ +#include "DeclarativeCoverArtProvider.h" +#include "playlist/PlayableItem.h" +#include "playlist/PlayableProxyModel.h" +#include "Query.h" +#include "Album.h" +#include "Artist.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +#include +#include +#include +#include + +namespace Tomahawk +{ + +DeclarativeCoverArtProvider::DeclarativeCoverArtProvider( ) + : QDeclarativeImageProvider( QDeclarativeImageProvider::Pixmap ) +{ + +} + +DeclarativeCoverArtProvider::~DeclarativeCoverArtProvider() +{ +} + +QPixmap DeclarativeCoverArtProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +{ + // We always can generate it in the requested size + int width = requestedSize.width() > 0 ? requestedSize.width() : 230; + int height = requestedSize.height() > 0 ? requestedSize.height() : 230; + + if( size ) + *size = QSize( width, height ); + + QPixmap cover; + + tDebug() << "DeclarativeCoverArtprovider: Getting album art by id:" << id << requestedSize; + + bool mirrored = false; + bool labeled = false; + + QString coverId = id; + if(coverId.contains("-mirror")) { + coverId.remove("-mirror"); + mirrored = true; + } + if(coverId.contains("-labels")) { + coverId.remove("-labels"); + labeled = true; + } + + artist_ptr artist = Artist::getByCoverId( coverId ); + if ( !artist.isNull() ) + { + tDebug() << "Returning artist cover:" << artist->cover( *size ).isNull(); + cover = artist->cover( *size ); + if ( cover.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Returning default artist image"; + cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Original, *size ); + } + } + + if ( cover.isNull() ) + { + album_ptr album = Album::getByCoverId( coverId ); + if ( !album.isNull() ) + { + tDebug() << "Returning album cover:" << album->cover( *size ).isNull(); + cover = album->cover( *size ); + if ( cover.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Returning default album image"; + cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::Original, *size ); + } + } + } + + if ( cover.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Returning default track image"; + cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Original, *size ); + } + + QImage image(*size, QImage::Format_ARGB32); + + if(labeled) { + QImage coverImage(*size, QImage::Format_RGB32); + QPainter bgPainter(&coverImage); + bgPainter.drawPixmap(0, 0, size->width(), size->height(), cover); + + QColor c1; + c1.setRgb( 0, 0, 0 ); + c1.setAlphaF( 0.00 ); + QColor c2; + c2.setRgb( 0, 0, 0 ); + c2.setAlphaF( 0.88 ); + + QLinearGradient gradient( QPointF( 0, 0 ), QPointF( 0, 1 ) ); + gradient.setCoordinateMode( QGradient::ObjectBoundingMode ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.6, c2 ); + gradient.setColorAt( 1.0, c2 ); + + bgPainter.setPen( Qt::transparent ); + bgPainter.setBrush(QBrush(gradient)); + bgPainter.drawRect(0, size->height() * 0.7, size->width(), size->height() * 0.3); + cover = QPixmap::fromImage(coverImage); + } + + QPainter painter(&image); + if(!mirrored) { + image.fill(Qt::white); + painter.drawPixmap(0, 0, size->width(), size->height(), cover); + } else { + image.fill(QColor(0, 0, 0, 0)); + + // Lets paint half of the image in a fragment per line + int mirrorHeight = size->height() / 2; + int fragmentCount = mirrorHeight; + int fragmentHeight = mirrorHeight / fragmentCount; + + QPainter::PixmapFragment fragments[fragmentCount]; + + qreal fragmentOpacity = 0; + int fragmentStartY = size->height() - mirrorHeight; + for(int i = 0; i < fragmentCount; ++i) { + QPointF point = QPointF(size->width() / 2, fragmentStartY + (fragmentHeight / 2)); + QRectF sourceRect = QRectF(0, fragmentStartY, size->width(), fragmentHeight); + fragments[i] = QPainter::PixmapFragment::create(point, sourceRect, 1, 1, 0, fragmentOpacity); + fragmentOpacity += 0.5 / fragmentCount; + fragmentStartY += fragmentHeight; + } + painter.drawPixmapFragments(fragments, fragmentCount, cover); + } + + return QPixmap::fromImage(image); +} + +} diff --git a/src/libtomahawk/widgets/DeclarativeCoverArtProvider.h b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.h new file mode 100644 index 0000000000..3614070959 --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.h @@ -0,0 +1,23 @@ +#ifndef DECLARATIVECOVERARTPROVIDER_H +#define DECLARATIVECOVERARTPROVIDER_H + + +#include "playlist/PlayableProxyModel.h" + +#include + + +namespace Tomahawk +{ + +class DeclarativeCoverArtProvider: public QDeclarativeImageProvider +{ +public: + DeclarativeCoverArtProvider(); + ~DeclarativeCoverArtProvider(); + + QPixmap requestPixmap( const QString &id, QSize *size, const QSize &requestedSize ); +}; + +} +#endif // DECLARATIVECOVERARTPROVIDER_H diff --git a/src/libtomahawk/widgets/DeclarativeView.cpp b/src/libtomahawk/widgets/DeclarativeView.cpp new file mode 100644 index 0000000000..9804b70aaf --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeView.cpp @@ -0,0 +1,58 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Michael Zanetti + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "DeclarativeView.h" +#include "playlist/PlayableItem.h" +#include "DeclarativeCoverArtProvider.h" +#include "utils/TomahawkUtilsGui.h" + +#include +#include +#include + +namespace Tomahawk +{ + +DeclarativeView::DeclarativeView( QWidget *parent ): + QDeclarativeView( parent ) +{ + + // Needed to make the QML contents scale with tomahawk + setResizeMode( QDeclarativeView::SizeRootObjectToView ); + + // This types seem to be needed everywhere anyways, lets the register here + qmlRegisterType( "tomahawk", 1, 0, "PlayableItem"); +// qmlRegisterType("tomahawk", 1, 0, "SearchField"); + + // QML image providers will be deleted by the view + engine()->addImageProvider( "albumart", new DeclarativeCoverArtProvider() ); + + // Register the view itself to make it easy to invoke the view's slots from QML + rootContext()->setContextProperty( "mainView", this ); + + rootContext()->setContextProperty( "defaultFontSize", TomahawkUtils::defaultFontSize() ); + rootContext()->setContextProperty( "defaultFontHeight", TomahawkUtils::defaultFontHeight() ); + +} + +DeclarativeView::~DeclarativeView() +{ + +} + +} diff --git a/src/libtomahawk/widgets/DeclarativeView.h b/src/libtomahawk/widgets/DeclarativeView.h new file mode 100644 index 0000000000..06e36e7903 --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeView.h @@ -0,0 +1,61 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Michael Zanetti + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DECLARATIVEVIEW_H +#define DECLARATIVEVIEW_H + +#include + +class QAbstractItemModel; + +/** + * @class This is the main class for Tomahawk's declarative views + * + * DeclarativeView inherits from QDeclarativeView and registers some + * common types, properties and functions used by all of Tomhawk's + * declarative views: + * + * Registered Types: + * - PlayableItem + * + * Set context properties: + * - mainView: This view, so you can invoke this view's slots from QML + * - defaultFontSize: system default font point size + * - defaultFontHeight: system default font pixel height + * + * It also registers an albumart image provider. You can access album art + * in QML with the source url "image://albumart/". + * The cover id can be obtained by the CoverIdRole in PlayableModels + * + * After subclassing this, all you have to do is call setSource() to + * load the QML file and optionally setModel(). + */ + +namespace Tomahawk +{ + +class DeclarativeView: public QDeclarativeView +{ + Q_OBJECT +public: + DeclarativeView(QWidget *parent = 0); + ~DeclarativeView(); +}; + +} +#endif From f42a5f461c9b8b41ad1e7f898b2c9e6f5ef696b6 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:54:42 +0200 Subject: [PATCH 003/565] * Added coverID() related methods to Artist, Album, Track. --- src/libtomahawk/Album.cpp | 29 +++++++++++++++++++++++++++++ src/libtomahawk/Album.h | 4 ++++ src/libtomahawk/Artist.cpp | 29 +++++++++++++++++++++++++++++ src/libtomahawk/Artist.h | 4 ++++ src/libtomahawk/Track.cpp | 17 ++++++++++++++++- src/libtomahawk/Track.h | 1 + 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/Album.cpp b/src/libtomahawk/Album.cpp index c182efb18b..d5a6d5faeb 100644 --- a/src/libtomahawk/Album.cpp +++ b/src/libtomahawk/Album.cpp @@ -37,6 +37,7 @@ using namespace Tomahawk; QHash< QString, album_wptr > Album::s_albumsByName = QHash< QString, album_wptr >(); QHash< unsigned int, album_wptr > Album::s_albumsById = QHash< unsigned int, album_wptr >(); +QHash< QString, album_ptr > Album::s_albumsByCoverId = QHash< QString, album_ptr >(); static QMutex s_nameCacheMutex; static QReadWriteLock s_idMutex; @@ -79,6 +80,7 @@ Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCr album->setWeakRef( album.toWeakRef() ); album->loadId( autoCreate ); s_albumsByName.insert( key, album ); + s_albumsByCoverId.insert( album->coverId(), album ); return album; } @@ -110,6 +112,7 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar album_ptr a = album_ptr( new Album( id, name, artist ), &Album::deleteLater ); a->setWeakRef( a.toWeakRef() ); s_albumsByName.insert( key, a ); + s_albumsByCoverId.insert( a->coverId(), a ); if ( id > 0 ) { @@ -122,6 +125,18 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar } +album_ptr +Album::getByCoverId( const QString& uuid ) +{ + QMutexLocker lock( &s_nameCacheMutex ); + + if ( s_albumsByCoverId.contains( uuid ) ) + return s_albumsByCoverId.value( uuid ); + + return album_ptr(); +} + + Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ) : QObject() , m_waitingForId( false ) @@ -327,6 +342,10 @@ Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData, m_coverBuffer = ba; } + s_albumsByCoverId.remove( coverId() ); + m_coverId = uuid(); + s_albumsByCoverId.insert( m_coverId, m_ownRef.toStrongRef() ); + m_coverLoaded = true; emit coverChanged(); } @@ -383,3 +402,13 @@ Album::infoid() const return m_uuid; } + + +QString +Album::coverId() const +{ + if ( m_coverId.isEmpty() ) + m_coverId = uuid(); + + return m_coverId; +} diff --git a/src/libtomahawk/Album.h b/src/libtomahawk/Album.h index a137f6b9f1..dd164259d5 100644 --- a/src/libtomahawk/Album.h +++ b/src/libtomahawk/Album.h @@ -45,12 +45,14 @@ Q_OBJECT public: static album_ptr get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate = false ); static album_ptr get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ); + static album_ptr getByCoverId( const QString& uuid ); Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ); Album( const QString& name, const Tomahawk::artist_ptr& artist ); virtual ~Album(); unsigned int id() const; + QString coverId() const; QString name() const { return m_name; } QString sortname() const { return m_sortname; } @@ -98,6 +100,7 @@ private slots: mutable bool m_coverLoaded; mutable bool m_coverLoading; mutable QString m_uuid; + mutable QString m_coverId; mutable QByteArray m_coverBuffer; #ifndef ENABLE_HEADLESS @@ -110,6 +113,7 @@ private slots: static QHash< QString, album_wptr > s_albumsByName; static QHash< unsigned int, album_wptr > s_albumsById; + static QHash< QString, album_ptr > s_albumsByCoverId; friend class ::IdThreadWorker; }; diff --git a/src/libtomahawk/Artist.cpp b/src/libtomahawk/Artist.cpp index c2498ccf60..faf5622c8b 100644 --- a/src/libtomahawk/Artist.cpp +++ b/src/libtomahawk/Artist.cpp @@ -39,6 +39,7 @@ using namespace Tomahawk; QHash< QString, artist_wptr > Artist::s_artistsByName = QHash< QString, artist_wptr >(); QHash< unsigned int, artist_wptr > Artist::s_artistsById = QHash< unsigned int, artist_wptr >(); +QHash< QString, artist_ptr > Artist::s_artistsByCoverId = QHash< QString, artist_ptr >(); static QMutex s_nameCacheMutex; static QReadWriteLock s_idMutex; @@ -77,6 +78,7 @@ Artist::get( const QString& name, bool autoCreate ) artist->setWeakRef( artist.toWeakRef() ); artist->loadId( autoCreate ); s_artistsByName.insert( key, artist ); + s_artistsByCoverId.insert( artist->coverId(), artist ); return artist; } @@ -110,6 +112,7 @@ Artist::get( unsigned int id, const QString& name ) artist_ptr a = artist_ptr( new Artist( id, name ), &Artist::deleteLater ); a->setWeakRef( a.toWeakRef() ); s_artistsByName.insert( key, a ); + s_artistsByCoverId.insert( a->coverId(), a ); if ( id > 0 ) { @@ -122,6 +125,18 @@ Artist::get( unsigned int id, const QString& name ) } +artist_ptr +Artist::getByCoverId( const QString& uuid ) +{ + QMutexLocker lock( &s_nameCacheMutex ); + + if ( s_artistsByCoverId.contains( uuid ) ) + return s_artistsByCoverId.value( uuid ); + + return artist_ptr(); +} + + Artist::Artist( unsigned int id, const QString& name ) : QObject() , m_waitingForFuture( false ) @@ -481,6 +496,10 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari m_coverBuffer = ba; } + s_artistsByCoverId.remove( coverId() ); + m_coverId = uuid(); + s_artistsByCoverId.insert( m_coverId, m_ownRef.toStrongRef() ); + m_coverLoaded = true; emit coverChanged(); } @@ -642,3 +661,13 @@ Artist::infoid() const return m_uuid; } + + +QString +Artist::coverId() const +{ + if ( m_coverId.isEmpty() ) + m_coverId = uuid(); + + return m_coverId; +} diff --git a/src/libtomahawk/Artist.h b/src/libtomahawk/Artist.h index c9bd14fb75..1336893d8c 100644 --- a/src/libtomahawk/Artist.h +++ b/src/libtomahawk/Artist.h @@ -43,12 +43,14 @@ Q_OBJECT public: static artist_ptr get( const QString& name, bool autoCreate = false ); static artist_ptr get( unsigned int id, const QString& name ); + static artist_ptr getByCoverId( const QString& uuid ); Artist( unsigned int id, const QString& name ); explicit Artist( const QString& name ); virtual ~Artist(); unsigned int id() const; + QString coverId() const; QString name() const { return m_name; } QString sortname() const { return m_sortname; } @@ -117,6 +119,7 @@ private slots: bool m_biographyLoaded; mutable QString m_uuid; + mutable QString m_coverId; mutable int m_infoJobs; QList m_databaseAlbums; @@ -138,6 +141,7 @@ private slots: static QHash< QString, artist_wptr > s_artistsByName; static QHash< unsigned int, artist_wptr > s_artistsById; + static QHash< QString, artist_ptr > s_artistsByCoverId; friend class ::IdThreadWorker; }; diff --git a/src/libtomahawk/Track.cpp b/src/libtomahawk/Track.cpp index b4068afdd2..96d500020d 100644 --- a/src/libtomahawk/Track.cpp +++ b/src/libtomahawk/Track.cpp @@ -494,10 +494,25 @@ Track::coverLoaded() const return m_artistPtr->coverLoaded(); } - #endif +QString +Track::coverId() const +{ + if ( m_albumPtr && m_albumPtr->coverLoaded() && !m_albumPtr->cover( QSize( 0, 0 ) ).isNull() ) + { + return m_albumPtr->coverId(); + } + else if ( m_artistPtr ) + { + return m_artistPtr->coverId(); + } + + return QString(); +} + + QList Track::similarTracks() const { diff --git a/src/libtomahawk/Track.h b/src/libtomahawk/Track.h index 8d94f011ce..abc63119cf 100644 --- a/src/libtomahawk/Track.h +++ b/src/libtomahawk/Track.h @@ -84,6 +84,7 @@ friend class Pipeline; QPixmap cover( const QSize& size, bool forceLoad = true ) const; #endif bool coverLoaded() const; + QString coverId() const; void setLoved( bool loved, bool postToInfoSystem = true ); bool loved(); From 7a1ef2eba7e71b4221c20623023f2e5b8ced75f7 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:55:09 +0200 Subject: [PATCH 004/565] * Added model specific roles and updated data methods. --- src/libtomahawk/playlist/PlayableModel.cpp | 42 ++++++++++++++++++- src/libtomahawk/playlist/PlayableModel.h | 19 +++++++++ .../playlist/PlayableProxyModel.cpp | 9 ++++ src/libtomahawk/playlist/PlayableProxyModel.h | 3 +- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 4ccb20b7a1..113a8067a5 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -44,6 +44,13 @@ PlayableModel::PlayableModel( QObject* parent, bool loading ) , m_readOnly( true ) , m_loading( loading ) { + QHash roleNames; + roleNames.insert( ArtistRole, "artistName" ); + roleNames.insert( TrackRole, "trackName" ); + roleNames.insert( CoverIDRole, "coverID" ); + roleNames.insert( IsPlayingRole, "isPlaying" ); + setRoleNames( roleNames ); + connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection ); connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); @@ -147,6 +154,12 @@ PlayableModel::parent( const QModelIndex& child ) const QVariant PlayableModel::artistData( const artist_ptr& artist, int role ) const { + if ( role == CoverIDRole ) + { + artist->cover( QSize( 0, 0 ) ); + return artist->coverId(); + } + if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) return QVariant(); @@ -157,6 +170,12 @@ PlayableModel::artistData( const artist_ptr& artist, int role ) const QVariant PlayableModel::albumData( const album_ptr& album, int role ) const { + if ( role == CoverIDRole ) + { + album->cover( QSize( 0, 0 ) ); + return album->coverId(); + } + if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) return QVariant(); @@ -167,6 +186,13 @@ PlayableModel::albumData( const album_ptr& album, int role ) const QVariant PlayableModel::queryData( const query_ptr& query, int column, int role ) const { + if ( role == CoverIDRole ) + { + query->track()->cover( QSize( 0, 0 ) ); + return query->track()->coverId(); + } + + tDebug() << Q_FUNC_INFO << role; if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) return QVariant(); @@ -287,6 +313,14 @@ PlayableModel::data( const QModelIndex& index, int role ) const if ( !entry ) return QVariant(); + int column = index.column(); + if ( role < CoverIDRole && role >= Qt::UserRole ) + { + // map role to column + column = role - Qt::UserRole; + role = Qt::DisplayRole; + } + switch ( role ) { case Qt::TextAlignmentRole: @@ -295,6 +329,12 @@ PlayableModel::data( const QModelIndex& index, int role ) const break; } + case IsPlayingRole: + { + return entry->isPlaying(); + break; + } + case PlayableProxyModel::TypeRole: { if ( entry->result() ) @@ -320,7 +360,7 @@ PlayableModel::data( const QModelIndex& index, int role ) const { if ( !entry->query().isNull() ) { - return queryData( entry->query(), index.column(), role ); + return queryData( entry->query(), column, role ); } else if ( !entry->artist().isNull() ) { diff --git a/src/libtomahawk/playlist/PlayableModel.h b/src/libtomahawk/playlist/PlayableModel.h index 0f228c9169..e5cc14d526 100644 --- a/src/libtomahawk/playlist/PlayableModel.h +++ b/src/libtomahawk/playlist/PlayableModel.h @@ -55,6 +55,25 @@ Q_OBJECT Name = 12 }; + enum PlayableRoles + { + ArtistRole = Qt::UserRole, + TrackRole, + ComposerRole, + AlbumRole, + AlbumPosRole, + DurationRole, + BitrateRole, + AgeRole, + YearRole, + FilesizeRole, + OriginRole, + ScoreRole, + NameRole, + CoverIDRole, + IsPlayingRole + }; + explicit PlayableModel( QObject* parent = 0, bool loading = true ); virtual ~PlayableModel(); diff --git a/src/libtomahawk/playlist/PlayableProxyModel.cpp b/src/libtomahawk/playlist/PlayableProxyModel.cpp index 29d877103b..821ada7e93 100644 --- a/src/libtomahawk/playlist/PlayableProxyModel.cpp +++ b/src/libtomahawk/playlist/PlayableProxyModel.cpp @@ -628,6 +628,15 @@ PlayableProxyModel::setFilter( const QString& pattern ) } +PlayableItem* +PlayableProxyModel::itemFromIndex( int itemIndex ) const +{ + // qDebug() << "returning item" << sourceModel()->itemFromIndex( itemIndex )->name(); + QModelIndex modelIndex = index( itemIndex, 0 ); + return sourceModel()->itemFromIndex( mapToSource( modelIndex ) ); +} + + void PlayableProxyModel::setCurrentIndex( const QModelIndex& index ) { diff --git a/src/libtomahawk/playlist/PlayableProxyModel.h b/src/libtomahawk/playlist/PlayableProxyModel.h index c3a0d7be07..b49cde20cd 100644 --- a/src/libtomahawk/playlist/PlayableProxyModel.h +++ b/src/libtomahawk/playlist/PlayableProxyModel.h @@ -36,7 +36,7 @@ Q_OBJECT { Detailed = 0, Short = 1, ShortWithAvatars = 2, Large = 3, Collection = 4 }; enum PlayableProxyModelRole - { StyleRole = Qt::UserRole + 1, TypeRole }; + { StyleRole = Qt::UserRole + 100, TypeRole }; explicit PlayableProxyModel ( QObject* parent = 0 ); virtual ~PlayableProxyModel() {} @@ -68,6 +68,7 @@ Q_OBJECT virtual int maxVisibleItems() const { return m_maxVisibleItems; } virtual void setMaxVisibleItems( int items ); + Q_INVOKABLE virtual PlayableItem* itemFromIndex( int itemIndex ) const; virtual PlayableItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); } virtual PlayableItem* itemFromQuery( const Tomahawk::query_ptr& query ) const { return sourceModel()->itemFromQuery( query ); } virtual PlayableItem* itemFromResult( const Tomahawk::result_ptr& result ) const { return sourceModel()->itemFromResult( result ); } From 74ec938037e084d6e14efc2425f4181f8497b4ee Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:56:28 +0200 Subject: [PATCH 005/565] * Stations shouldn't depend on widgets / controls. --- src/libtomahawk/GlobalActionManager.cpp | 12 +- ...baseCommand_LoadDynamicPlaylistEntries.cpp | 6 +- ...tabaseCommand_LoadDynamicPlaylistEntries.h | 4 +- ...baseCommand_SetDynamicPlaylistRevision.cpp | 74 +++-------- ...tabaseCommand_SetDynamicPlaylistRevision.h | 9 +- .../playlist/dynamic/DynamicPlaylist.cpp | 78 ++---------- .../playlist/dynamic/DynamicPlaylist.h | 28 ++--- .../dynamic/DynamicPlaylistRevision.cpp | 2 +- .../dynamic/DynamicPlaylistRevision.h | 2 +- .../playlist/dynamic/GeneratorFactory.cpp | 4 +- .../playlist/dynamic/GeneratorFactory.h | 4 +- .../playlist/dynamic/GeneratorInterface.cpp | 42 +------ .../playlist/dynamic/GeneratorInterface.h | 37 +++--- .../dynamic/database/DatabaseGenerator.h | 8 +- .../dynamic/echonest/EchonestGenerator.cpp | 118 +++++++++++++++--- .../dynamic/echonest/EchonestGenerator.h | 10 +- src/sourcetree/items/CategoryItems.cpp | 8 +- 17 files changed, 196 insertions(+), 250 deletions(-) diff --git a/src/libtomahawk/GlobalActionManager.cpp b/src/libtomahawk/GlobalActionManager.cpp index 38f8542a51..fcb074d148 100644 --- a/src/libtomahawk/GlobalActionManager.cpp +++ b/src/libtomahawk/GlobalActionManager.cpp @@ -225,7 +225,10 @@ GlobalActionManager::copyPlaylistToClipboard( const dynplaylist_ptr& playlist ) TomahawkUtils::urlAddQueryItem( link, "type", "echonest" ); TomahawkUtils::urlAddQueryItem( link, "title", playlist->title() ); - QList< dyncontrol_ptr > controls = playlist->generator()->controls(); + Q_ASSERT( false ); + //FIXME +/* + QVariantList controls = playlist->generator()->controls(); foreach ( const dyncontrol_ptr& c, controls ) { if ( c->selectedType() == "Artist" ) @@ -252,7 +255,7 @@ GlobalActionManager::copyPlaylistToClipboard( const dynplaylist_ptr& playlist ) TomahawkUtils::urlAddQueryItem( link, name, c->input() ); } - } + }*/ QClipboard* cb = QApplication::clipboard(); QByteArray data = percentEncode( link ); @@ -886,7 +889,8 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) return Tomahawk::dynplaylist_ptr(); } - if ( parts[ 0 ] == "create" ) + Q_ASSERT( false ); +/* if ( parts[ 0 ] == "create" ) { if ( !urlHasQueryItem( url, "title" ) || !urlHasQueryItem( url, "type" ) ) { @@ -1057,7 +1061,7 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) ViewManager::instance()->show( pl ); return pl; - } + }*/ return Tomahawk::dynplaylist_ptr(); } diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp index c429f48464..5ad0dc4fb3 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp @@ -52,7 +52,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) QString type; GeneratorMode mode; - QList< QVariantMap > controls; + QVariantList controls; QString playlist_guid; // qDebug() << "Loading controls..." << revisionGuid(); // qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " @@ -71,7 +71,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) type = controlsQuery.value( 3 ).toString(); mode = static_cast( controlsQuery.value( 2 ).toInt() ); - QStringList controlIds = v.toStringList(); +/* QStringList controlIds = v.toStringList(); // qDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1); foreach( const QString& controlId, controlIds ) { @@ -91,7 +91,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) c[ "input" ] = controlQuery.value( 2 ).toString(); controls << c; } - } + }*/ } else { diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h index c15adc0b6e..b812cd63c3 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h @@ -47,14 +47,14 @@ class DatabaseCommand_LoadDynamicPlaylistEntries : public DatabaseCommand_LoadPl void done( QString, bool, QString, - QList< QVariantMap>, + QVariantList, bool ); // used when loading a static playlist void done( QString, QList< QString >, QList< QString >, QString, - QList< QVariantMap>, + QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ); diff --git a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp index 659041dc17..3498d2c160 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp @@ -37,7 +37,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe const QList& entries, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ) + const QVariantList& controls ) : DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, orderedguids, addedentries, entries ) , m_type( type ) , m_mode( mode ) @@ -54,7 +54,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe const QString& oldrev, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ) + const QVariantList& controls ) : DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, QStringList(), QList< plentry_ptr >(), QList< plentry_ptr >() ) , m_type( type ) , m_mode( mode ) @@ -68,18 +68,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe QVariantList DatabaseCommand_SetDynamicPlaylistRevision::controlsV() { - if ( m_controls.isEmpty() ) - return m_controlsV; - - if ( !m_controls.isEmpty() && m_controlsV.isEmpty() ) - { - foreach ( const dyncontrol_ptr& control, m_controls ) - { - m_controlsV << QJson::QObjectHelper::qobject2qvariant( control.data() ); - } - } - - return m_controlsV; + return m_controls; } @@ -117,7 +106,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook() return; } - if ( !m_controlsV.isEmpty() && m_controls.isEmpty() ) +/* if ( !m_controlsV.isEmpty() && m_controls.isEmpty() ) { QList controlMap; foreach( const QVariant& v, m_controlsV ) @@ -139,7 +128,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook() m_addedmap, m_applied ); } - else + else*/ { if ( m_mode == OnDemand ) rawPl->setRevision( newrev(), @@ -169,19 +158,9 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib ) DatabaseCommand_SetPlaylistRevision::exec( lib ); QVariantList newcontrols; - if ( m_controlsV.isEmpty() && !m_controls.isEmpty() ) + foreach( const QVariant& v, m_controls ) { - foreach( const dyncontrol_ptr& control, m_controls ) - { - newcontrols << control->id(); - } - } - else if( !m_controlsV.isEmpty() ) - { - foreach( const QVariant& v, m_controlsV ) - { - newcontrols << v.toMap().value( "id" ); - } + newcontrols << v.toMap().value( "id" ); } QJson::Serializer ser; @@ -209,34 +188,18 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib ) TomahawkSqlQuery controlsQuery = lib->newquery(); controlsQuery.prepare( "INSERT INTO dynamic_playlist_controls( id, playlist, selectedType, match, input ) " "VALUES( ?, ?, ?, ?, ? )" ); - if ( m_controlsV.isEmpty() && !m_controls.isEmpty() ) - { - foreach ( const dyncontrol_ptr& control, m_controls ) - { - qDebug() << "inserting dynamic control:" << control->id() << m_playlistguid << control->selectedType() << control->match() << control->input(); - controlsQuery.addBindValue( control->id() ); - controlsQuery.addBindValue( m_playlistguid ); - controlsQuery.addBindValue( control->selectedType() ); - controlsQuery.addBindValue( control->match() ); - controlsQuery.addBindValue( control->input() ); - - controlsQuery.exec(); - } - } - else + + foreach ( const QVariant& v, m_controls ) { - foreach ( const QVariant& v, m_controlsV ) - { - QVariantMap control = v.toMap(); - qDebug() << "inserting dynamic control from JSON:" << control.value( "id" ) << m_playlistguid << control.value( "selectedType" ) << control.value( "match" ) << control.value( "input" ); - controlsQuery.addBindValue( control.value( "id" ) ); - controlsQuery.addBindValue( m_playlistguid ); - controlsQuery.addBindValue( control.value( "selectedType" ) ); - controlsQuery.addBindValue( control.value( "match" ) ); - controlsQuery.addBindValue( control.value( "input" ) ); - - controlsQuery.exec(); - } + QVariantMap control = v.toMap(); + qDebug() << "inserting dynamic control from JSON:" << control.value( "id" ) << m_playlistguid << control.value( "selectedType" ) << control.value( "match" ) << control.value( "input" ); + controlsQuery.addBindValue( control.value( "id" ) ); + controlsQuery.addBindValue( m_playlistguid ); + controlsQuery.addBindValue( control.value( "selectedType" ) ); + controlsQuery.addBindValue( control.value( "match" ) ); + controlsQuery.addBindValue( control.value( "input" ) ); + + controlsQuery.exec(); } if ( m_applied ) @@ -252,6 +215,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib ) } } + void DatabaseCommand_SetDynamicPlaylistRevision::setPlaylist( DynamicPlaylist* pl ) { diff --git a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h index a23f39ddda..6a81d98b6a 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h +++ b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h @@ -45,7 +45,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla const QList& entries, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ); + const QVariantList& controls ); explicit DatabaseCommand_SetDynamicPlaylistRevision( const source_ptr& s, const QString& playlistguid, @@ -53,7 +53,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla const QString& oldrev, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ); + const QVariantList& controls ); QString commandname() const { return "setdynamicplaylistrevision"; } @@ -64,7 +64,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla void setControlsV( const QVariantList& vlist ) { - m_controlsV = vlist; + m_controls = vlist; } QVariantList controlsV(); @@ -80,8 +80,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla private: QString m_type; GeneratorMode m_mode; - QList< dyncontrol_ptr > m_controls; - QList< QVariant > m_controlsV; + QVariantList m_controls; // ARG i hate sharedpointers sometimes DynamicPlaylist* m_playlist; // Only used if setting revision of a non-autoloaded playlist, as those aren't able to be looked up by guid diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 57f61dd6fb..c7269cd1d5 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -185,7 +185,7 @@ void DynamicPlaylist::createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, - const QList< dyncontrol_ptr>& controls, + const QVariantList& controls, const QList< plentry_ptr >& entries ) { Q_ASSERT( m_source->isLocal() || newrev == oldrev ); @@ -233,7 +233,7 @@ void DynamicPlaylist::createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, - const QList< dyncontrol_ptr>& controls ) + const QVariantList& controls ) { Q_ASSERT( m_source->isLocal() || newrev == oldrev ); @@ -275,19 +275,19 @@ DynamicPlaylist::loadRevision( const QString& rev ) connect( cmd, SIGNAL( done( QString, bool, QString, - QList< QVariantMap >, + QVariantList, bool ) ), SLOT( setRevision( QString, bool, QString, - QList< QVariantMap >, + QVariantList, bool) ) ); } else if ( m_generator->mode() == Static ) { connect( cmd, SIGNAL( done( QString, QList< QString >, QList< QString >, QString, - QList< QVariantMap >, + QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ) ), @@ -295,7 +295,7 @@ DynamicPlaylist::loadRevision( const QString& rev ) QList< QString >, QList< QString >, QString, - QList< QVariantMap >, + QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ) ) ); @@ -376,7 +376,7 @@ DynamicPlaylist::setRevision( const QString& rev, const QList< QString >& neworderedguids, const QList< QString >& oldorderedguids, const QString& type, - const QList< dyncontrol_ptr >& controls, + const QVariantList& controls, bool is_newest_rev, const QMap< QString, plentry_ptr >& addedmap, bool applied ) @@ -391,7 +391,7 @@ DynamicPlaylist::setRevision( const QString& rev, Q_ARG( QList , neworderedguids ), Q_ARG( QList , oldorderedguids ), Q_ARG( QString , type ), - QGenericArgument( "QList< Tomahawk::dyncontrol_ptr > " , (const void*)&controls ), + Q_ARG( QVariantList , controls ), Q_ARG( bool, is_newest_rev ), QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ), Q_ARG( bool, applied ) ); @@ -421,42 +421,11 @@ DynamicPlaylist::setRevision( const QString& rev, } -void -DynamicPlaylist::setRevision( const QString& rev, - const QList< QString >& neworderedguids, - const QList< QString >& oldorderedguids, - const QString& type, - const QList< QVariantMap>& controlsV, - bool is_newest_rev, - const QMap< QString, Tomahawk::plentry_ptr >& addedmap, - bool applied ) -{ - if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, - "setRevision", - Qt::BlockingQueuedConnection, - Q_ARG( QString, rev ), - Q_ARG( QList , neworderedguids ), - Q_ARG( QList , oldorderedguids ), - Q_ARG( QString , type ), - QGenericArgument( "QList< QVariantMap > " , (const void*)&controlsV ), - Q_ARG( bool, is_newest_rev ), - QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ), - Q_ARG( bool, applied ) ); - return; - } - - QList controls = variantsToControl( controlsV ); - setRevision( rev, neworderedguids, oldorderedguids, type, controls, is_newest_rev, addedmap, applied ); -} - - void DynamicPlaylist::setRevision( const QString& rev, bool is_newest_rev, const QString& type, - const QList< dyncontrol_ptr >& controls, + const QVariantList& controls, bool applied ) { if ( QThread::currentThread() != thread() ) @@ -498,32 +467,7 @@ DynamicPlaylist::setRevision( const QString& rev, } -void -DynamicPlaylist::setRevision( const QString& rev, - bool is_newest_rev, - const QString& type, - const QList< QVariantMap >& controlsV, - bool applied ) -{ - if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, - "setRevision", - Qt::BlockingQueuedConnection, - Q_ARG( QString, rev ), - Q_ARG( bool, is_newest_rev ), - Q_ARG( QString, type ), - QGenericArgument( "QList< QVariantMap >" , (const void*)&controlsV ), - Q_ARG( bool, applied ) ); - return; - } - - QList controls = variantsToControl( controlsV ); - setRevision( rev, is_newest_rev, type, controls, applied ); -} - - -QList< dyncontrol_ptr > +/*QList< dyncontrol_ptr > DynamicPlaylist::variantsToControl( const QList< QVariantMap >& controlsV ) { QList realControls; @@ -534,7 +478,7 @@ DynamicPlaylist::variantsToControl( const QList< QVariantMap >& controlsV ) realControls << control; } return realControls; -} +}*/ void diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h index 2a86bbb022..d66d139477 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h @@ -49,10 +49,10 @@ class DatabaseCommand_LoadDynamicPlaylist; struct DynQueueItem : RevisionQueueItem { QString type; - QList controls; + QVariantList controls; int mode; - DynQueueItem( const QString& nRev, const QString& oRev, const QString& typ, const QList< dyncontrol_ptr >& ctrls, int m, const QList< plentry_ptr >& e, bool latest ) : + DynQueueItem( const QString& nRev, const QString& oRev, const QString& typ, const QVariantList& ctrls, int m, const QList< plentry_ptr >& e, bool latest ) : RevisionQueueItem( nRev, oRev, e, latest ), type( typ ), controls( ctrls ), mode( m ) {} }; @@ -125,9 +125,9 @@ public slots: // want to update the playlist from the model? // generate a newrev using uuid() and call this: // if this is a static playlist, pass it a new list of entries. implicitly sets mode to static - void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls, const QList< plentry_ptr >& entries ); + void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QVariantList& controls, const QList< plentry_ptr >& entries ); // if it is ondemand, no entries are needed implicitly sets mode to ondemand - void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls ); + void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QVariantList& controls ); void reportCreated( const Tomahawk::dynplaylist_ptr& self ); void reportDeleted( const Tomahawk::dynplaylist_ptr& self ); @@ -142,15 +142,7 @@ public slots: const QList& neworderedguids, const QList& oldorderedguids, const QString& type, - const QList< QVariantMap >& controls, - bool is_newest_rev, - const QMap< QString, Tomahawk::plentry_ptr >& addedmap, - bool applied ); - void setRevision( const QString& rev, - const QList& neworderedguids, - const QList& oldorderedguids, - const QString& type, - const QList< Tomahawk::dyncontrol_ptr >& controls, + const QVariantList& controls, bool is_newest_rev, const QMap< QString, Tomahawk::plentry_ptr >& addedmap, bool applied ); @@ -158,13 +150,9 @@ public slots: void setRevision( const QString& rev, bool is_newest_rev, const QString& type, - const QList< QVariantMap>& controls, - bool applied ); - void setRevision( const QString& rev, - bool is_newest_rev, - const QString& type, - const QList< Tomahawk::dyncontrol_ptr>& controls, + const QVariantList& controls, bool applied ); + private: // called from loadAllPlaylists DB cmd via databasecollection (in GUI thread) explicit DynamicPlaylist( const source_ptr& src, @@ -192,8 +180,6 @@ public slots: void checkRevisionQueue(); - QList< dyncontrol_ptr > variantsToControl( const QList< QVariantMap >& controlsV ); - geninterface_ptr m_generator; bool m_autoLoad; diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp index d14737b07f..ea7048eb30 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp @@ -23,7 +23,7 @@ using namespace Tomahawk; -DynamicPlaylistRevision::DynamicPlaylistRevision(const PlaylistRevision &other) +DynamicPlaylistRevision::DynamicPlaylistRevision( const PlaylistRevision &other ) { revisionguid = other.revisionguid; oldrevisionguid = other.oldrevisionguid; diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h index 1001da59fa..96af9273b9 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h @@ -29,7 +29,7 @@ struct DLLEXPORT DynamicPlaylistRevision : PlaylistRevision { public: - QList< dyncontrol_ptr > controls; + QVariantList controls; Tomahawk::GeneratorMode mode; QString type; diff --git a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp index 75838998e0..d5ed4effcd 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp @@ -40,14 +40,14 @@ GeneratorFactory::create ( const QString& type ) } -dyncontrol_ptr +/*dyncontrol_ptr GeneratorFactory::createControl( const QString& generatorType, const QString& controlType ) { if( generatorType.isEmpty() || !s_factories.contains( generatorType ) ) return dyncontrol_ptr(); return s_factories.value( generatorType )->createControl( controlType ); -} +}*/ void diff --git a/src/libtomahawk/playlist/dynamic/GeneratorFactory.h b/src/libtomahawk/playlist/dynamic/GeneratorFactory.h index 3cbb309688..421c39e7af 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorFactory.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorFactory.h @@ -46,7 +46,7 @@ class DLLEXPORT GeneratorFactoryInterface * Create a control for this generator, not tied to this generator itself. Used when loading dynamic * playlists from a dbcmd. */ - virtual dyncontrol_ptr createControl( const QString& controlType = QString() ) = 0; +// virtual dyncontrol_ptr createControl( const QString& controlType = QString() ) = 0; virtual QStringList typeSelectors() const = 0; }; @@ -59,7 +59,7 @@ class DLLEXPORT GeneratorFactory public: static geninterface_ptr create( const QString& type ); // only used when loading from dbcmd - static dyncontrol_ptr createControl( const QString& generatorType, const QString& controlType = QString() ); +// static dyncontrol_ptr createControl( const QString& generatorType, const QString& controlType = QString() ); static void registerFactory( const QString& type, GeneratorFactoryInterface* interface ); static QStringList types(); diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp index 9cfb12c76f..13805b0c2d 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp @@ -34,17 +34,6 @@ Tomahawk::GeneratorInterface::~GeneratorInterface() } -QList< Tomahawk::dyncontrol_ptr > -Tomahawk::GeneratorInterface::controls() -{ -// if( m_controls.isEmpty() ) { // return a default control (so the user can add more) -// return QList< Tomahawk::dyncontrol_ptr >() << createControl(); -// } - - return m_controls; -} - - QPixmap Tomahawk::GeneratorInterface::logo() { @@ -52,38 +41,15 @@ Tomahawk::GeneratorInterface::logo() } -void -Tomahawk::GeneratorInterface::addControl( const Tomahawk::dyncontrol_ptr& control ) -{ - m_controls << control; -} - - -void -Tomahawk::GeneratorInterface::clearControls() +QVariantList +Tomahawk::GeneratorInterface::controls() const { - m_controls.clear(); + return m_controls; } void -Tomahawk::GeneratorInterface::setControls( const QList< Tomahawk::dyncontrol_ptr >& controls ) +Tomahawk::GeneratorInterface::setControls( const QVariantList& controls ) { m_controls = controls; } - - -void -Tomahawk::GeneratorInterface::removeControl( const Tomahawk::dyncontrol_ptr& control ) -{ - m_controls.removeAll( control ); -} - - -Tomahawk::dyncontrol_ptr -Tomahawk::GeneratorInterface::createControl( const QString& type ) -{ - Q_UNUSED( type ); - Q_ASSERT( false ); - return dyncontrol_ptr(); -} diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h index fd0746a5b9..3a6d7fbdfc 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h @@ -47,6 +47,7 @@ class DLLEXPORT GeneratorInterface : public QObject { Q_OBJECT Q_PROPERTY( QString type READ type ) + Q_PROPERTY( QString summary READ sentenceSummary) /// oh qjson. Q_PROPERTY( int mode READ mode WRITE setMode ) @@ -55,12 +56,6 @@ class DLLEXPORT GeneratorInterface : public QObject explicit GeneratorInterface( QObject* parent = 0 ); virtual ~GeneratorInterface(); - // Can't make it pure otherwise we can't shove it in QVariants :-/ - // empty QString means use default - /// The generator will keep track of all the controls it creates. No need to tell it about controls - /// you ask it to create - virtual dyncontrol_ptr createControl( const QString& type = QString() ); - /// A logo to display for this generator, if it has one virtual QPixmap logo(); @@ -98,17 +93,17 @@ class DLLEXPORT GeneratorInterface : public QObject */ virtual bool onDemandSteerable() const { return false; } + /** - * Returns a widget used to steer the OnDemand dynamic playlist. - * If this generator doesn't support this (and returns false for - * \c onDemandSteerable) this will be null. The generator is responsible - * for reacting to changes in the widget. - * - * Steering widgets may emit a \c steeringChanged() signal, which will cause the model to toss any - * upcoming tracks and re-fetch them. - * + * Returns the controls for this station. */ - virtual QWidget* steeringWidget() { return 0; } + virtual QVariantList controls() const; + + /** + * Sets the controls (for example when loaded from database) + */ + virtual void setControls( const QVariantList& controls ); + /// The type of this generator QString type() const { return m_type; } @@ -116,22 +111,20 @@ class DLLEXPORT GeneratorInterface : public QObject int mode() const { return (int)m_mode; } void setMode( int mode ) { m_mode = (GeneratorMode)mode; } - // control functions - QList< dyncontrol_ptr > controls(); - void addControl( const dyncontrol_ptr& control ); - void clearControls(); - void setControls( const QList< dyncontrol_ptr>& controls ); - void removeControl( const dyncontrol_ptr& control ); + virtual bool startFromTrack( const Tomahawk::query_ptr& query ) = 0; + virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ) = 0; + virtual bool startFromGenre( const QString& genre ) = 0; signals: void error( const QString& title, const QString& body); void generated( const QList< Tomahawk::query_ptr>& queries ); void nextTrackGenerated( const Tomahawk::query_ptr& track ); + void summaryChanged(); protected: QString m_type; GeneratorMode m_mode; - QList< dyncontrol_ptr > m_controls; + QVariantList m_controls; }; typedef QSharedPointer geninterface_ptr; diff --git a/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h b/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h index 7513763dc8..ce991026b6 100644 --- a/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h +++ b/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h @@ -36,10 +36,10 @@ namespace Tomahawk DatabaseFactory() {} virtual GeneratorInterface* create(); - virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); +// virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); // TO create a special SQL resolver that consists of a pre-baked SQL query and a description of it - virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary ); +// virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary ); virtual QStringList typeSelectors() const; }; @@ -55,8 +55,8 @@ namespace Tomahawk explicit DatabaseGenerator( QObject* parent = 0 ); virtual ~DatabaseGenerator(); - virtual dyncontrol_ptr createControl( const QString& type = QString() ); - virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary ); +/* virtual dyncontrol_ptr createControl( const QString& type = QString() ); + virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary );*/ virtual QPixmap logo(); virtual void generate ( int number = -1 ); diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index fdaf4df40f..9edac4ccc8 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -61,11 +61,11 @@ EchonestFactory::create() } -dyncontrol_ptr +/*dyncontrol_ptr EchonestFactory::createControl( const QString& controlType ) { return dyncontrol_ptr( new EchonestControl( controlType, typeSelectors() ) ); -} +}*/ QStringList @@ -160,12 +160,12 @@ EchonestGenerator::setupCatalogs() // qDebug() << "ECHONEST:" << m_logo.size(); } -dyncontrol_ptr +/*dyncontrol_ptr EchonestGenerator::createControl( const QString& type ) { m_controls << dyncontrol_ptr( new EchonestControl( type, GeneratorFactory::typeSelectors( m_type ) ) ); return m_controls.last(); -} +}*/ QPixmap EchonestGenerator::logo() @@ -176,11 +176,11 @@ QPixmap EchonestGenerator::logo() void EchonestGenerator::knownCatalogsChanged() { - // Refresh all contrls +/* // Refresh all contrls foreach( const dyncontrol_ptr& control, m_controls ) { control.staticCast< EchonestControl >()->updateWidgetsFromData(); - } + }*/ } @@ -188,7 +188,7 @@ void EchonestGenerator::generate( int number ) { // convert to an echonest query, and fire it off - qDebug() << Q_FUNC_INFO; +/* qDebug() << Q_FUNC_INFO; qDebug() << "Generating playlist with" << m_controls.size(); foreach( const dyncontrol_ptr& ctrl, m_controls ) qDebug() << ctrl->selectedType() << ctrl->match() << ctrl->input(); @@ -202,7 +202,16 @@ EchonestGenerator::generate( int number ) } catch( std::runtime_error& e ) { qWarning() << "Got invalid controls!" << e.what(); emit error( "Filters are not valid", e.what() ); - } + }*/ + + QList< query_ptr > queries; + queries << Query::get("Colour Haze", "All", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Sun", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Zen", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Outside", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Dirt", QString(), uuid(), true); + + emit generated( queries ); } @@ -226,6 +235,81 @@ EchonestGenerator::startOnDemand() } +bool +EchonestGenerator::startFromTrack( const Tomahawk::query_ptr& query ) +{ + tDebug() << "Generating station content by query:" << query->toString(); + + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::SongId; + data.second = query->track()->artist() + " " + query->track()->track(); + + Echonest::DynamicPlaylist::PlaylistParams params; + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); + params << data; + + // FIXME! + + return true; +} + + +bool +EchonestGenerator::startFromArtist( const Tomahawk::artist_ptr& artist ) +{ + tDebug() << "Generating station content by artist:" << artist->name(); + + if ( !m_dynPlaylist->sessionId().isNull() ) + { + // Running session, delete it + QNetworkReply* deleteReply = m_dynPlaylist->deleteSession(); + connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) ); + } + + connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doStartOnDemand( Echonest::DynamicPlaylist::PlaylistParams ) ) ); + + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::Artist; + data.second = artist->name(); + + Echonest::DynamicPlaylist::PlaylistParams params; + params << data; + // params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); + emit paramsGenerated( params ); + + return true; +} + + +bool +EchonestGenerator::startFromGenre( const QString& genre ) +{ + tDebug() << "Generating station content by genre:" << genre; + + if ( !m_dynPlaylist->sessionId().isNull() ) + { + // Running session, delete it + QNetworkReply* deleteReply = m_dynPlaylist->deleteSession(); + connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) ); + } + + connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doGenerate( Echonest::DynamicPlaylist::PlaylistParams ) ) ); + + setProperty( "number", 20 ); + + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::Description; + data.second = genre; + + Echonest::DynamicPlaylist::PlaylistParams params; + params << data; + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); + emit paramsGenerated( params ); + + return true; +} + + void EchonestGenerator::doGenerate( const Echonest::DynamicPlaylist::PlaylistParams& paramsIn ) { @@ -308,7 +392,7 @@ void EchonestGenerator::getParams() throw( std::runtime_error ) { Echonest::DynamicPlaylist::PlaylistParams params; - foreach( const dyncontrol_ptr& control, m_controls ) { +/* foreach( const dyncontrol_ptr& control, m_controls ) { params.append( control.dynamicCast()->toENParam() ); } @@ -343,7 +427,7 @@ EchonestGenerator::getParams() throw( std::runtime_error ) } else { emit paramsGenerated( params ); - } + }*/ } @@ -440,7 +524,7 @@ EchonestGenerator::userCatalogs() return s_catalogs->catalogs().keys(); } -bool +/*bool EchonestGenerator::onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error ) { bool only = true; @@ -460,7 +544,7 @@ EchonestGenerator::onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum } return false; -} +}*/ Echonest::DynamicPlaylist::ArtistTypeEnum @@ -477,7 +561,7 @@ EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& p /// 3. artist-description: If all the artist entries are Description. If some were but not all, error out. /// 4. artist-radio: If all the artist entries are Similar To. If some were but not all, error out. /// 5. song-radio: If all the artist entries are Similar To. If some were but not all, error out. - bool someCatalog = false; +/* bool someCatalog = false; bool genreType = false; foreach( const dyncontrol_ptr& control, m_controls ) { if ( control->selectedType() == "User Radio" ) @@ -498,7 +582,7 @@ EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& p else if( onlyThisArtistType( Echonest::DynamicPlaylist::SongRadioType ) ) params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); else // no artist or song or description types. default to artist-description - params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );*/ return static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( params.last().second.toInt() ); } @@ -529,7 +613,7 @@ EchonestGenerator::sentenceSummary() * NOTE / TODO: In order for the sentence to be grammatically correct, we must follow the EN API rules. That means we can't have multiple of some types of filters, * and all Artist types must be the same. The filters aren't checked at the moment until Generate / Play is pressed. Consider doing a check on hide as well. */ - QList< dyncontrol_ptr > allcontrols = m_controls; +/* QList< dyncontrol_ptr > allcontrols = m_controls; QString sentence = "Songs "; /// 1. Collect all required filters @@ -612,7 +696,9 @@ EchonestGenerator::sentenceSummary() sentence += "and " + sorting.dynamicCast< EchonestControl >()->summary() + "."; } - return sentence; + return sentence;*/ + + return "This is a station!"; } void diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h index 5f8777abb0..4375a5153b 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h @@ -60,7 +60,7 @@ class DLLEXPORT EchonestFactory : public GeneratorFactoryInterface EchonestFactory(); virtual GeneratorInterface* create(); - virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); +// virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); virtual QStringList typeSelectors() const; }; @@ -71,7 +71,7 @@ class DLLEXPORT EchonestGenerator : public GeneratorInterface explicit EchonestGenerator( QObject* parent = 0 ); virtual ~EchonestGenerator(); - virtual dyncontrol_ptr createControl( const QString& type = QString() ); +// virtual dyncontrol_ptr createControl( const QString& type = QString() ); virtual QPixmap logo(); virtual void generate ( int number = -1 ); virtual void startOnDemand(); @@ -80,6 +80,10 @@ class DLLEXPORT EchonestGenerator : public GeneratorInterface virtual bool onDemandSteerable() const { return false; } virtual QWidget* steeringWidget() { return 0; } + virtual bool startFromTrack( const Tomahawk::query_ptr& query ); + virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ); + virtual bool startFromGenre( const QString& genre ); + static QStringList styles(); static QStringList moods(); static QStringList genres(); @@ -118,7 +122,7 @@ private slots: query_ptr queryFromSong( const Echonest::Song& song ); Echonest::DynamicPlaylist::ArtistTypeEnum appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error ); - bool onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error ); +// bool onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error ); void loadStylesMoodsAndGenres(); diff --git a/src/sourcetree/items/CategoryItems.cpp b/src/sourcetree/items/CategoryItems.cpp index 3696baa263..ce505f107a 100644 --- a/src/sourcetree/items/CategoryItems.cpp +++ b/src/sourcetree/items/CategoryItems.cpp @@ -163,7 +163,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) QString firstArtist; // now we want to add each artist as a filter... - QList< dyncontrol_ptr > contrls; +/* QList< dyncontrol_ptr > contrls; while ( !stream.atEnd() ) { QString artist; @@ -183,7 +183,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) QString name = firstArtist.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstArtist ); newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls ); newpl->setProperty( "newname", name ); - connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) ); + connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );*/ ViewManager::instance()->show( newpl ); return true; @@ -295,7 +295,7 @@ CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks ) newpl->setMode( OnDemand ); // now we want to add each query as a song or similar artist filter... - QList< dyncontrol_ptr > controls; +/* QList< dyncontrol_ptr > controls; foreach ( const Tomahawk::query_ptr& q, tracks ) { dyncontrol_ptr c = newpl->generator()->createControl( "Song" ); @@ -303,7 +303,7 @@ CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks ) controls << c; } - newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), controls ); + newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), controls );*/ ViewManager::instance()->show( newpl ); connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) ); From a52998d76667f13e6c1698a31492103c957a77c2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:56:51 +0200 Subject: [PATCH 006/565] * Use DynamicQmlWidget for stations. --- CMakeLists.txt | 1 + TomahawkUse.cmake | 6 +++--- resources.qrc | 28 ++++++++++++++++++++++++++++ src/TomahawkApp.cpp | 6 +++--- src/libtomahawk/CMakeLists.txt | 17 +++++++++++------ src/libtomahawk/ViewManager.cpp | 11 ++++++----- src/libtomahawk/ViewManager.h | 4 ++-- 7 files changed, 54 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0672c18d1b..c7d7967021 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ if(NOT Qt5Core_DIR) set(QT_USE_QTNETWORK TRUE) set(QT_USE_QTXML TRUE) set(QT_USE_QTWEBKIT TRUE) + set(QT_USE_QTDECLARATIVE TRUE) include( ${QT_USE_FILE} ) endmacro() diff --git a/TomahawkUse.cmake b/TomahawkUse.cmake index 503ba8d7d6..8b465457d3 100644 --- a/TomahawkUse.cmake +++ b/TomahawkUse.cmake @@ -1,11 +1,11 @@ #FIXME: this only handles qt4 and duplicates top level cmakelists: how can we reduce code duplication? -find_package(Qt4 COMPONENTS QtNetwork QtCore QtGui QtSql REQUIRED) +find_package(Qt4 COMPONENTS QtNetwork QtCore QtGui QtSql QtDeclarative REQUIRED) include( ${QT_USE_FILE} ) -set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork") +set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork" "QtDeclarative") if(BUILD_GUI) - list(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" "QtSvg") + list(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" "QtSvg" "QtDeclarative") endif() find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS}) diff --git a/resources.qrc b/resources.qrc index 2b69fa96b0..5c0708890e 100644 --- a/resources.qrc +++ b/resources.qrc @@ -90,6 +90,28 @@ data/images/star-unstarred.svg data/images/apply-check.svg data/stylesheets/topbar-radiobuttons.css + data/qml/tomahawkimports/CoverImage.qml + data/qml/tomahawkimports/ArtistView.qml + data/qml/tomahawkimports/HeaderLabel.qml + data/qml/tomahawkimports/TagCloud.qml + data/qml/tomahawkimports/ScrollBar.qml + data/qml/tomahawkimports/InputField.qml + data/qml/tomahawkimports/Button.qml + data/qml/tomahawkimports/DoubleSlider.qml + data/qml/tomahawkimports/RoundedButton.qml + data/qml/tomahawkimports/PushButton.qml + data/qml/tomahawkimports/CoverFlip.qml + data/qml/tomahawkimports/BusyIndicator.qml + data/qml/StationView.qml + data/qml/stations/StationCreatorPage1.qml + data/qml/stations/StationCreatorPage2.qml + data/qml/stations/CreateByArtist.qml + data/qml/stations/StationConfig.qml + data/qml/QmlGridView.qml + data/qml/stations/CreateByGenre.qml + data/qml/tomahawkimports/FlexibleHeader.qml + data/qml/tomahawkimports/ToggleViewButtons.qml + data/qml/DeclarativeHeader.qml data/icons/tomahawk-icon-16x16.png data/icons/tomahawk-icon-32x32.png data/icons/tomahawk-icon-64x64.png @@ -152,5 +174,11 @@ data/images/refresh.svg data/images/inbox.svg data/images/new-inbox.svg + data/images/inputfield-border.svg + data/images/search-box-dismiss-x.svg + data/images/loading-animation.svg + data/images/station-artist.svg + data/images/station-genre.svg + data/images/station-year.svg diff --git a/src/TomahawkApp.cpp b/src/TomahawkApp.cpp index b623924e32..8bee7cfc79 100644 --- a/src/TomahawkApp.cpp +++ b/src/TomahawkApp.cpp @@ -39,7 +39,7 @@ #include "database/DatabaseResolver.h" #include "playlist/dynamic/GeneratorFactory.h" #include "playlist/dynamic/echonest/EchonestGenerator.h" -#include "playlist/dynamic/database/DatabaseGenerator.h" +//#include "playlist/dynamic/database/DatabaseGenerator.h" #include "playlist/XspfUpdater.h" #include "network/Servent.h" #include "web/Api_v1.h" @@ -219,8 +219,8 @@ TomahawkApp::init() tDebug() << "Init Echonest Factory."; GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); #endif - tDebug() << "Init Database Factory."; - GeneratorFactory::registerFactory( "database", new DatabaseFactory ); +/* tDebug() << "Init Database Factory."; + GeneratorFactory::registerFactory( "database", new DatabaseFactory );*/ // Register shortcut handler for this platform #ifdef Q_WS_MAC diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index e02f9f5039..6eb4f0b111 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -77,19 +77,21 @@ set( libGuiSources playlist/PlayableItem.cpp playlist/SingleTrackPlaylistInterface.cpp + playlist/dynamic/GeneratorInterface.cpp playlist/dynamic/DynamicPlaylist.cpp playlist/dynamic/DynamicView.cpp playlist/dynamic/DynamicModel.cpp playlist/dynamic/echonest/EchonestGenerator.cpp playlist/dynamic/echonest/EchonestControl.cpp - playlist/dynamic/echonest/EchonestSteerer.cpp - playlist/dynamic/widgets/DynamicWidget.cpp +# playlist/dynamic/echonest/EchonestSteerer.cpp +# playlist/dynamic/widgets/DynamicWidget.cpp + playlist/dynamic/widgets/DynamicQmlWidget.cpp playlist/dynamic/widgets/DynamicControlWrapper.cpp - playlist/dynamic/widgets/DynamicControlList.cpp +# playlist/dynamic/widgets/DynamicControlList.cpp playlist/dynamic/widgets/ReadOrWriteWidget.cpp playlist/dynamic/widgets/MiscControlWidgets.cpp - playlist/dynamic/widgets/CollapsibleControls.cpp - playlist/dynamic/widgets/DynamicSetupWidget.cpp +# playlist/dynamic/widgets/CollapsibleControls.cpp +# playlist/dynamic/widgets/DynamicSetupWidget.cpp resolvers/ExternalResolverGui.cpp resolvers/ScriptResolver.cpp @@ -121,6 +123,8 @@ set( libGuiSources utils/ResultUrlChecker.cpp utils/NetworkReply.cpp + widgets/DeclarativeCoverArtProvider.cpp + widgets/DeclarativeView.cpp widgets/AnimatedCounterLabel.cpp widgets/BasicHeader.cpp widgets/FilterHeader.cpp @@ -314,7 +318,7 @@ list(APPEND libSources playlist/dynamic/GeneratorInterface.cpp playlist/dynamic/DynamicPlaylistRevision.cpp playlist/XspfUpdater.cpp - playlist/dynamic/database/DatabaseGenerator.cpp +# playlist/dynamic/database/DatabaseGenerator.cpp playlist/dynamic/database/DatabaseControl.cpp playlist/dynamic/DynamicControl.cpp @@ -497,6 +501,7 @@ TARGET_LINK_LIBRARIES( tomahawklib ${QT_QTXML_LIBRARY} ${QT_QTSVG_LIBRARY} ${QT_QTCORE_LIBRARY} + ${QT_QTDECLARATIVE_LIBRARY} ${OS_SPECIFIC_LINK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${LINK_LIBRARIES} diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index f382c93e93..688b29966e 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -42,7 +42,8 @@ #include "playlist/InboxView.h" #include "playlist/PlaylistLargeItemDelegate.h" #include "playlist/RecentlyPlayedModel.h" -#include "playlist/dynamic/widgets/DynamicWidget.h" +//#include "playlist/dynamic/widgets/DynamicWidget.h" +#include "playlist/dynamic/widgets/DynamicQmlWidget.h" #include "widgets/NewReleasesWidget.h" #include "widgets/WelcomeWidget.h" @@ -193,8 +194,8 @@ ViewManager::playlistForPage( ViewPage* page ) const { p = dynamic_cast< PlaylistView* >( page )->playlistModel()->playlist(); } - else if ( dynamic_cast< DynamicWidget* >( page ) ) - p = dynamic_cast< DynamicWidget* >( page )->playlist(); + else if ( dynamic_cast< DynamicQmlWidget* >( page ) ) + p = dynamic_cast< DynamicQmlWidget* >( page )->playlist(); return p; } @@ -228,7 +229,7 @@ ViewManager::show( const Tomahawk::dynplaylist_ptr& playlist ) { if ( !m_dynamicWidgets.contains( playlist ) || m_dynamicWidgets.value( playlist ).isNull() ) { - m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicWidget( playlist, m_stack ); + m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicQmlWidget( playlist, m_stack ); playlist->resolve(); } @@ -816,7 +817,7 @@ ViewManager::playlistForInterface( Tomahawk::playlistinterface_ptr interface ) c Tomahawk::dynplaylist_ptr ViewManager::dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr interface ) const { - foreach ( QPointer view, m_dynamicWidgets.values() ) + foreach ( QPointer view, m_dynamicWidgets.values() ) { if ( !view.isNull() && view.data()->playlistInterface() == interface ) { diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index fb6f08bc63..7cc46c4439 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -59,7 +59,7 @@ class InboxModel; namespace Tomahawk { - class DynamicWidget; + class DynamicQmlWidget; } class DLLEXPORT ViewManager : public QObject @@ -195,7 +195,7 @@ private slots: QList< Tomahawk::collection_ptr > m_superCollections; - QHash< Tomahawk::dynplaylist_ptr, QPointer > m_dynamicWidgets; + QHash< Tomahawk::dynplaylist_ptr, QPointer > m_dynamicWidgets; QHash< Tomahawk::collection_ptr, QPointer > m_treeWidgets; QHash< Tomahawk::artist_ptr, QPointer > m_artistViews; QHash< Tomahawk::album_ptr, QPointer > m_albumViews; From aaebfe39c5756322323afb423c82cecebc4db7ae Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 23:55:30 +0200 Subject: [PATCH 007/565] * Bring back Radio sidebar item. --- src/libtomahawk/ViewManager.cpp | 17 +++++++++++++++++ src/libtomahawk/ViewManager.h | 2 ++ .../playlist/dynamic/DynamicPlaylist.cpp | 17 +++++++++++------ .../playlist/dynamic/DynamicPlaylist.h | 3 ++- src/sourcetree/SourcesModel.cpp | 16 ++++++++++------ 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 688b29966e..af79f7a1ab 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -83,6 +83,7 @@ ViewManager::ViewManager( QObject* parent ) , m_newReleasesWidget( 0 ) , m_recentPlaysWidget( 0 ) , m_inboxWidget( 0 ) + , m_radioView( 0 ) , m_currentPage( 0 ) , m_loaded( false ) { @@ -383,6 +384,22 @@ ViewManager::showSuperCollection() } +Tomahawk::ViewPage* +ViewManager::showRadioPage() +{ + if ( !m_radioView ) + { + dynplaylist_ptr playlist = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false, QString(), false, false ); + playlist->setMode( OnDemand ); + + m_radioView = new Tomahawk::DynamicQmlWidget( playlist, m_stack ); + } + + setPage( m_radioView ); + return m_radioView; +} + + void ViewManager::playlistInterfaceChanged( Tomahawk::playlistinterface_ptr interface ) { diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 7cc46c4439..dbe3d46ef9 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -133,6 +133,7 @@ Q_OBJECT void historyForwardAvailable( bool avail ); public slots: + Tomahawk::ViewPage* showRadioPage(); Tomahawk::ViewPage* showSuperCollection(); Tomahawk::ViewPage* showWelcomePage(); Tomahawk::ViewPage* showWhatsHotPage(); @@ -191,6 +192,7 @@ private slots: NewReleasesWidget* m_newReleasesWidget; Tomahawk::ViewPage* m_recentPlaysWidget; Tomahawk::ViewPage* m_inboxWidget; + Tomahawk::DynamicQmlWidget* m_radioView; InboxModel* m_inboxModel; QList< Tomahawk::collection_ptr > m_superCollections; diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index c7269cd1d5..486774f4b7 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -151,17 +151,22 @@ DynamicPlaylist::create( const Tomahawk::source_ptr& author, GeneratorMode mode, bool shared, const QString& type, - bool autoLoad + bool autoLoad, + bool temporary ) { dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ), &QObject::deleteLater ); dynplaylist->setWeakSelf( dynplaylist.toWeakRef() ); - DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad ); - connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); - Database::instance()->enqueue( QSharedPointer(cmd) ); - if ( autoLoad ) - dynplaylist->reportCreated( dynplaylist ); + if ( !temporary ) + { + DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad ); + connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + if ( autoLoad ) + dynplaylist->reportCreated( dynplaylist ); + } + return dynplaylist; } diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h index d66d139477..5e576c84b3 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h @@ -85,7 +85,8 @@ class DLLEXPORT DynamicPlaylist : public Tomahawk::Playlist GeneratorMode mode, bool shared, const QString& type = QString(), - bool autoLoad = true + bool autoLoad = true, + bool temporary = false ); static void remove( const dynplaylist_ptr& playlist ); diff --git a/src/sourcetree/SourcesModel.cpp b/src/sourcetree/SourcesModel.cpp index dd20740ce6..c068674ea8 100644 --- a/src/sourcetree/SourcesModel.cpp +++ b/src/sourcetree/SourcesModel.cpp @@ -309,29 +309,33 @@ SourcesModel::appendGroups() sc->setSortValue( 1 ); // browse section + GenericPageItem* radio = new GenericPageItem( this, browse, tr( "Radio" ), ImageRegistry::instance()->icon( RESPATH "images/station.svg" ), + boost::bind( &ViewManager::showRadioPage, ViewManager::instance() ), + boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); + radio->setSortValue( 2 ); + LovedTracksItem* loved = new LovedTracksItem( this, browse ); - loved->setSortValue( 2 ); + loved->setSortValue( 3 ); GenericPageItem* recent = new GenericPageItem( this, browse, tr( "Recently Played" ), ImageRegistry::instance()->icon( RESPATH "images/recently-played.svg" ), boost::bind( &ViewManager::showRecentPlaysPage, ViewManager::instance() ), boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); - recent->setSortValue( 3 ); + recent->setSortValue( 4 ); GenericPageItem* hot = new GenericPageItem( this, browse, tr( "Charts" ), ImageRegistry::instance()->icon( RESPATH "images/charts.svg" ), boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ), boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() ) ); - hot->setSortValue( 4 ); + hot->setSortValue( 5 ); GenericPageItem* newReleases = new GenericPageItem( this, browse, tr( "New Releases" ), ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), boost::bind( &ViewManager::showNewReleasesPage, ViewManager::instance() ), boost::bind( &ViewManager::newReleasesWidget, ViewManager::instance() ) ); - newReleases->setSortValue( 5 ); + newReleases->setSortValue( 6 ); InboxItem* inbox = new InboxItem( this, browse ); - inbox->setSortValue( 6 ); + inbox->setSortValue( 7 ); m_collectionsGroup = new GroupItem( this, m_rootItem, tr( "Friends" ), 4 ); - m_cloudGroup = new GroupItem( this, m_rootItem, tr( "Cloud" ), 5 ); endInsertRows(); From 3490975de51bddfb700a2488fc914246f8cac3ab Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 00:31:15 +0200 Subject: [PATCH 008/565] * Fixed PlayableItem & added properties. --- src/libtomahawk/playlist/PlayableItem.cpp | 8 ++++++++ src/libtomahawk/playlist/PlayableItem.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/libtomahawk/playlist/PlayableItem.cpp b/src/libtomahawk/playlist/PlayableItem.cpp index af7562446e..9ffe017f83 100644 --- a/src/libtomahawk/playlist/PlayableItem.cpp +++ b/src/libtomahawk/playlist/PlayableItem.cpp @@ -205,6 +205,14 @@ PlayableItem::artistName() const { return m_query->track()->artist(); } + else if ( !m_album.isNull() ) + { + return m_album->artist()->name(); + } + else if ( !m_artist.isNull() ) + { + return m_artist->name(); + } return QString(); } diff --git a/src/libtomahawk/playlist/PlayableItem.h b/src/libtomahawk/playlist/PlayableItem.h index 5f1e7913fa..41f769740f 100644 --- a/src/libtomahawk/playlist/PlayableItem.h +++ b/src/libtomahawk/playlist/PlayableItem.h @@ -31,6 +31,10 @@ class DLLEXPORT PlayableItem : public QObject { Q_OBJECT +Q_PROPERTY(QString name READ name NOTIFY dataChanged) +Q_PROPERTY(QString artistName READ artistName NOTIFY dataChanged) +Q_PROPERTY(QString albumName READ albumName NOTIFY dataChanged) +Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY dataChanged) public: ~PlayableItem(); From 6cd680c3887ac49c14469c28683641631e1016fd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 00:31:40 +0200 Subject: [PATCH 009/565] * Added itemFromIndex to PlayableModel. --- src/libtomahawk/playlist/PlayableModel.cpp | 7 +++++++ src/libtomahawk/playlist/PlayableModel.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 113a8067a5..9cf2daa5bf 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -816,6 +816,13 @@ PlayableModel::finishLoading() } +PlayableItem* +PlayableModel::itemFromIndex( int itemIndex ) const +{ + return itemFromIndex( index( itemIndex, 0, QModelIndex() ) ); +} + + PlayableItem* PlayableModel::itemFromIndex( const QModelIndex& index ) const { diff --git a/src/libtomahawk/playlist/PlayableModel.h b/src/libtomahawk/playlist/PlayableModel.h index e5cc14d526..656fd254cb 100644 --- a/src/libtomahawk/playlist/PlayableModel.h +++ b/src/libtomahawk/playlist/PlayableModel.h @@ -120,6 +120,7 @@ Q_OBJECT virtual void ensureResolved(); + Q_INVOKABLE PlayableItem* itemFromIndex( int itemIndex ) const; virtual PlayableItem* itemFromIndex( const QModelIndex& index ) const; virtual PlayableItem* itemFromQuery( const Tomahawk::query_ptr& query ) const; virtual PlayableItem* itemFromResult( const Tomahawk::result_ptr& result ) const; From 7ad06f5ee2cb8dcc40b12d1c92368f2184498c1e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 00:32:31 +0200 Subject: [PATCH 010/565] * Disable 'Create new station' item. --- src/sourcetree/items/SourceItem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sourcetree/items/SourceItem.cpp b/src/sourcetree/items/SourceItem.cpp index 5bbaac4922..f68738e738 100644 --- a/src/sourcetree/items/SourceItem.cpp +++ b/src/sourcetree/items/SourceItem.cpp @@ -108,7 +108,7 @@ SourceItem::SourceItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahaw } if ( !stations.isEmpty() || source->isLocal() ) { - m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source->isLocal() ); + m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, false /* source->isLocal() */ ); onStationsAdded( stations ); } @@ -339,14 +339,14 @@ void SourceItem::playlistsAddedInternal( SourceTreeItem* parent, const QList< dynplaylist_ptr >& playlists ) { QList< SourceTreeItem* > items; - int addOffset = playlists.first()->author()->isLocal() ? 1 : 0; + int addOffset = 0; //playlists.first()->author()->isLocal() ? 1 : 0; int from = parent->children().count() - addOffset; parent->beginRowsAdded( from, from + playlists.count() - 1 ); foreach ( const dynplaylist_ptr& p, playlists ) { DynamicPlaylistItem* plItem = new DynamicPlaylistItem( model(), parent, p, parent->children().count() - addOffset ); -// qDebug() << "Dynamic Playlist added:" << p->title() << p->creator() << p->info(); +// tDebug() << "Dynamic Playlist added:" << p->title() << p->creator() << p->info(); p->loadRevision(); items << plItem; @@ -516,7 +516,7 @@ SourceItem::onStationsAdded( const QList< dynplaylist_ptr >& stations ) // add the category too int cur = children().count(); beginRowsAdded( cur, cur ); - m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source()->isLocal() ); + m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, false /* source()->isLocal() */ ); endRowsAdded(); } From e468ad2687d4946fbd35ed24af8396a04c34dab2 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 00:39:29 +0200 Subject: [PATCH 011/565] some more work on stations merged --- data/qml/SpinnerTest.qml | 1 + data/qml/StationView.qml | 177 ++++++++++-------- data/qml/stations/CreateByArtist.qml | 4 +- data/qml/stations/CreateByGenre.qml | 4 +- data/qml/stations/CreateByYear.qml | 79 ++++++++ data/qml/stations/StationCreatorPage2.qml | 4 +- data/qml/stations/StationItem.qml | 73 ++++++++ data/qml/tomahawkimports/CoverFlip.qml | 2 - data/qml/tomahawkimports/CoverImage.qml | 5 +- data/qml/tomahawkimports/DoubleSlider.qml | 130 ++++++++++--- data/qml/tomahawkimports/InputField.qml | 6 + data/qml/tomahawkimports/TagCloud.qml | 6 +- resources.qrc | 2 + .../dynamic/widgets/DynamicQmlWidget.cpp | 17 +- .../dynamic/widgets/DynamicQmlWidget.h | 3 +- 15 files changed, 393 insertions(+), 120 deletions(-) create mode 100644 data/qml/stations/CreateByYear.qml create mode 100644 data/qml/stations/StationItem.qml diff --git a/data/qml/SpinnerTest.qml b/data/qml/SpinnerTest.qml index 217b4dbcf2..aa11c4031d 100644 --- a/data/qml/SpinnerTest.qml +++ b/data/qml/SpinnerTest.qml @@ -11,6 +11,7 @@ BusyIndicator { anchors.horizontalCenterOffset: 200 height: 200 width: 200 + count: 11 } Image { diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 7bb79e1b47..3448c3adbf 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -19,23 +19,42 @@ Rectangle { width: parent.width icon: "../images/station.svg" title: mainView.title - subtitle: generator.summary + subtitle: ""//generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: stationListView.currentIndex == 2 + showNextButton: !mainView.configured && stationListView.currentIndex == 2 nextButtonText: "Save" + backButtonText: mainView.configured ? "Configure" : "Back" z: 1 //cover albumcovers that may leave their area - onBackPressed: stationListView.decrementCurrentIndex() - onNextPressed: stationListView.incrementCurrentIndex() + onBackPressed: { + if(mainView.configured) { + return; + } + + inputBubble.opacity = 0 + stationListView.decrementCurrentIndex() + if(stationListView.currentIndex == 1) { + subtitle = modeModel.get(stationCreator.modeIndex).headerSubtitle + "..." + } + if(stationListView.currentIndex == 0) { + subtitle = "" + } + } + // In our case the next button is the save button + onNextPressed: { + inputBubble.opacity = 1 + saveNameInput.forceActiveFocus(); + } } + ListModel { id: modeModel - ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml" } - ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml" } - ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "year" } + ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml"; headerSubtitle: "by" } + ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml"; headerSubtitle: "like" } + ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "stations/CreateByYear.qml"; headerSubtitle: "from" } } VisualItemModel { @@ -47,8 +66,9 @@ Rectangle { model: modeModel onItemClicked: { - stationCreator.content = modeModel.get(index).creatorContent + stationCreator.modeIndex = index stationListView.incrementCurrentIndex() + header.subtitle = modeModel.get(index).headerSubtitle + "..." } } @@ -57,84 +77,35 @@ Rectangle { height: stationListView.height width: stationListView.width - onNext: stationListView.incrementCurrentIndex() + property int modeIndex + + content: modeModel.get(modeIndex).creatorContent + + onNext: { + stationListView.incrementCurrentIndex() + header.subtitle = modeModel.get(modeIndex).headerSubtitle + " " + text + } } - Item { + StationItem { id: stationItem height: stationListView.height width: stationListView.width + } + } - CoverFlip { - id: coverView - anchors.right: parent.right - anchors.top: parent.top - height: parent.height - width: parent.width - interactive: false - - backgroundColor: scene.color - - model: dynamicModel - currentIndex: currentlyPlayedIndex - - onItemPlayPauseClicked: { - mainView.playItem(index) - } - - onItemClicked: { - mainView.playItem(index) - } - - states: [ - State { - name: "empty"; when: mainView.loading - PropertyChanges { - target: coverView - anchors.rightMargin: -coverView.width - anchors.topMargin: - coverView.height - scale: 0 - } - } - ] - transitions: [ - Transition { - from: "empty" - to: "*" - NumberAnimation { - properties: "anchors.topMargin,anchors.rightMargin,scale" - duration: 1000 - easing.type: Easing.OutQuad - } - } - - ] -// Behavior on anchors.topMargin { -// NumberAnimation { duration: 500 } -// } -// Behavior on anchors.rightMargin { -// NumberAnimation { duration: 500 } -// } -// Behavior on scale { -// NumberAnimation { duration: 500 } -// } - } - BusyIndicator { - id: busyIndicator - anchors.centerIn: parent - height: defaultFontHeight * 4 - width: height + VisualItemModel { + id: configuredStationVisualModel - opacity: mainView.loading ? 1 : 0 - running: mainView.loading - } + StationItem { + id: cfgstationItem + height: stationListView.height + width: stationListView.width } - } - ListView { id: stationListView anchors { @@ -147,7 +118,7 @@ Rectangle { contentHeight: height contentWidth: width orientation: ListView.Horizontal - model: stationVisualModel + //model: mainView.configured ? configuredStationVisualModel : stationVisualModel interactive: false highlightMoveDuration: 300 @@ -157,6 +128,62 @@ Rectangle { onWidthChanged: { contentWidth = scene.width } + + Component.onCompleted: { + model = mainView.configured ? configuredStationVisualModel : stationVisualModel + } + onModelChanged: print("ccccccccccccc", mainView.configured) } + Rectangle { + id: inputBubble + color: "black" + border.width: 2 + border.color: "white" + height: defaultFontHeight * 3 + width: height * 6 + radius: defaultFontHeight / 2 + anchors.top: header.bottom + anchors.right: parent.right + anchors.rightMargin: defaultFontHeight / 2 + anchors.topMargin: -defaultFontHeight / 2 + z: 2 + opacity: 0 + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + Row { + anchors.centerIn: parent + width: parent.width - defaultFontHeight + spacing: defaultFontHeight / 2 + + function saveStation(name) { + mainView.title = name + inputBubble.opacity = 0 + header.showNextButton = false + header.backButtonText = "Configure" + } + + Text { + id: nameText + color: "white" + text: "Name:" + anchors.verticalCenter: parent.verticalCenter + } + InputField { + id: saveNameInput + width: parent.width - nameText.width - saveOkButton.width - parent.spacing * 2 + placeholderText: "Station" + onAccepted: parent.saveStation(text); + } + PushButton { + id: saveOkButton + text: "OK" + onClicked: parent.saveStation(saveNameInput.text) + } + } + + } + } diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml index f3f2f4ceb3..359b353f78 100644 --- a/data/qml/stations/CreateByArtist.qml +++ b/data/qml/stations/CreateByArtist.qml @@ -6,11 +6,11 @@ Item { id: root anchors.fill: parent - signal done() + signal done(string text) function createStation(artist) { mainView.startStationFromArtist(artist) - root.done() + root.done(artist) } Column { diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml index 9d2fb1b9b9..1d89641833 100644 --- a/data/qml/stations/CreateByGenre.qml +++ b/data/qml/stations/CreateByGenre.qml @@ -6,11 +6,11 @@ Item { id: root anchors.fill: parent - signal done() + signal done(string text) function createStation(genre) { mainView.startStationFromGenre(genre) - root.done() + root.done(genre) } ListModel { diff --git a/data/qml/stations/CreateByYear.qml b/data/qml/stations/CreateByYear.qml new file mode 100644 index 0000000000..5e14616689 --- /dev/null +++ b/data/qml/stations/CreateByYear.qml @@ -0,0 +1,79 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + anchors.fill: parent + + signal done(string text) + + function createStation(artist) { + mainView.startStationFromArtist(artist) + root.done(artist) + } + + Column { + id: upperColumn + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height + width: defaultFontHeight * 30 + anchors.bottomMargin: defaultFontHeight + spacing: defaultFontHeight + + HeaderLabel { + id: headerText + text: "Create station by year..." + } + + Row { + height: artistInputField.height + width: parent.width + spacing: defaultFontHeight * 0.5 + + InputField { + id: artistInputField + width: parent.width - createFromInputButton.width - parent.spacing + + onAccepted: createStation(text) + } + + PushButton { + id: createFromInputButton + text: "Go!" + enabled: artistInputField.text.length > 2 + onClicked: createStation(artistInputField.text) + } + } + +// Item { +// height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3 +// width: parent.width +// ArtistView { +// id: artistView +// height: parent.height +// width: parent.width +// model: artistChartsModel +// clip: true +// delegateHeight: defaultFontHeight * 6 + +// onItemClicked: { +// createStation(artistChartsModel.itemFromIndex(index).artistName); +// } +// } +// ScrollBar { +// listView: artistView +// } +// } + + DoubleSlider { + width: parent.width + height: defaultFontHeight * 2 + min: 1960 + max: new Date().getFullYear() + lowerSliderPos: 1990 + upperSliderPos: 2010 + minMaxLabelsVisible: false + } + } +} diff --git a/data/qml/stations/StationCreatorPage2.qml b/data/qml/stations/StationCreatorPage2.qml index 34f4b65cda..295069aa66 100644 --- a/data/qml/stations/StationCreatorPage2.qml +++ b/data/qml/stations/StationCreatorPage2.qml @@ -8,7 +8,7 @@ Item { property int margins: defaultFontHeight * 2 property alias content: contentLoader.source - signal next() + signal next(string text) Loader { id: contentLoader @@ -19,7 +19,7 @@ Item { Connections { target: contentLoader.item - onDone: root.next() + onDone: root.next(text) } } diff --git a/data/qml/stations/StationItem.qml b/data/qml/stations/StationItem.qml new file mode 100644 index 0000000000..9d7804a0a9 --- /dev/null +++ b/data/qml/stations/StationItem.qml @@ -0,0 +1,73 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: stationItem + + CoverFlip { + id: coverView + anchors.right: parent.right + anchors.top: parent.top + height: parent.height + width: parent.width + interactive: false + + backgroundColor: scene.color + + model: dynamicModel + currentIndex: currentlyPlayedIndex + + onItemPlayPauseClicked: { + mainView.playItem(index) + } + + onItemClicked: { + mainView.playItem(index) + } + + states: [ + State { + name: "empty"; when: mainView.loading + PropertyChanges { + target: coverView + anchors.rightMargin: -coverView.width + anchors.topMargin: - coverView.height + scale: 0 + } + } + ] + transitions: [ + Transition { + from: "empty" + to: "*" + NumberAnimation { + properties: "anchors.topMargin,anchors.rightMargin,scale" + duration: 1000 + easing.type: Easing.OutQuad + } + } + + ] +// Behavior on anchors.topMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on anchors.rightMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on scale { +// NumberAnimation { duration: 500 } +// } + + } + BusyIndicator { + id: busyIndicator + anchors.centerIn: parent + height: defaultFontHeight * 4 + width: height + + opacity: mainView.loading ? 1 : 0 + running: mainView.loading + } + +} diff --git a/data/qml/tomahawkimports/CoverFlip.qml b/data/qml/tomahawkimports/CoverFlip.qml index 58e85824c4..742dcce719 100644 --- a/data/qml/tomahawkimports/CoverFlip.qml +++ b/data/qml/tomahawkimports/CoverFlip.qml @@ -49,8 +49,6 @@ PathView { right: parent.right } - backgroundColor: coverView.backgroundColor - showLabels: true showMirror: true artistName: model.artistName diff --git a/data/qml/tomahawkimports/CoverImage.qml b/data/qml/tomahawkimports/CoverImage.qml index 518137aac8..e3f2c1d306 100644 --- a/data/qml/tomahawkimports/CoverImage.qml +++ b/data/qml/tomahawkimports/CoverImage.qml @@ -25,9 +25,6 @@ Item { // The border width for the cover image property int borderWidth: 2 - // needed to adjust the shadow - property color backgroundColor: "black" - // sets the brightness for the item and its mirror (1: brightest, 0: darkest) property double itemBrightness: 1 property double mirrorBrightness: .5 @@ -55,7 +52,7 @@ Item { Rectangle { id: itemShadow - color: backgroundColor + color: "black" anchors.fill: parent //opacity: 1 - itemBrightness diff --git a/data/qml/tomahawkimports/DoubleSlider.qml b/data/qml/tomahawkimports/DoubleSlider.qml index b1dc522cc0..08061b96e2 100644 --- a/data/qml/tomahawkimports/DoubleSlider.qml +++ b/data/qml/tomahawkimports/DoubleSlider.qml @@ -2,8 +2,6 @@ import QtQuick 1.1 Item { id: root - width: 500 - height: 10 property int min: 0 property int max: 100 @@ -16,71 +14,144 @@ Item { /** Should the floating label indicating the current position be shown? */ property bool showFloatingLabel: true + property bool minMaxLabelsVisible: true property int lowerSliderPos: 25 property int upperSliderPos: 75 + onUpperSliderPosChanged: print("fooooooooo", upperSliderPos) + signal valueChanged() + QtObject { + id: priv + + property int steps: root.max - root.min + 1 + + property int sliderHeight: root.height + property int sliderWidth: root.height / 2 + } + Row { anchors.fill: parent + anchors.topMargin: defaultFontHeight * 1.2 + anchors.bottomMargin: defaultFontHeight * 1.2 spacing: 10 Text { id: minText text: root.minLabel.length > 0 ? root.minLabel : min color: "white" + visible: root.minMaxLabelsVisible } Item { id: sliderRect height: root.height - width: parent.width - minText.width - maxText.width - parent.spacing * 2 + property int maxWidth: parent.width - (minText.visible ? minText.width : 0) - (maxText.visible ? maxText.width : 0) - parent.spacing * 2 + width: maxWidth - (maxWidth % priv.steps) + anchors.horizontalCenter: parent.horizontalCenter function sliderPosToValue( sliderPos ) { - var percent = sliderPos * 100 / (sliderRect.width - lowerSlider.width); - return Math.floor(percent * (root.max - root.min) / 100) + root.min + var percent = sliderPos * 100 / (sliderRect.width - priv.sliderWidth/2); + return Math.floor(percent * (priv.steps-1) / 100) + root.min } function valueToSloderPos( value ) { - var percent = (value - root.min) * 100 / (root.max - root.min) - return percent * (sliderRect.width - lowerSlider.width) / 100 + var percent = (value - root.min) * 100 / (priv.steps-1) + return percent * (sliderRect.width - priv.sliderWidth/2) / 100 } Rectangle { id: sliderBase - height: root.height / 5 - width: parent.width + height: root.height / 1.5 + width: parent.width + defaultFontHeight * 1.5 color: "white" radius: height / 2 anchors.centerIn: parent + gradient: Gradient { + GradientStop { position: 0.0; color: "#ffffffff" } + GradientStop { position: 1.0; color: "#aaffffff" } + } + + Rectangle { + anchors.fill: sliderBase + anchors.leftMargin: lowerSlider.x + priv.sliderWidth + anchors.rightMargin: sliderBase.width - upperSlider.x - priv.sliderWidth + gradient: Gradient { + GradientStop { position: 0.0; color: "#aa962c26" } + GradientStop { position: 1.0; color: "#962c26" } + } + } + + Row { + id: stepRow + anchors.fill: parent + anchors.leftMargin: defaultFontHeight - lineWidth/2 + anchors.rightMargin: defaultFontHeight - lineWidth/2 + property int stepCount: root.max - root.min + 1 + property int lineHeight: height + property int lineWidth: lineHeight / 15 + spacing: (width - (stepCount * lineWidth)) / stepCount + + Repeater { + model: stepRow.stepCount + + Rectangle { + id: marker + height: stepRow.lineHeight * (isHighlight ? 1.2 : 1) + width: stepRow.lineWidth + color: "black" + + property bool isHighlight: index % 10 === 0 + + gradient: Gradient { + GradientStop { position: 0.0; color: marker.isHighlight ? "white" : "black" } + GradientStop { position: 1.0; color: marker.isHighlight ? "#aaffffff" : "black" } + } + + Text { + text: root.min + index + visible: marker.isHighlight + anchors.horizontalCenter: marker.horizontalCenter + anchors.top: marker.bottom + anchors.topMargin: defaultFontHeight / 2 + color: "white" + } + + } + + } + } + } + Rectangle { id: lowerSlider - height: root.height - width: height + height: priv.sliderHeight + width: priv.sliderWidth anchors.top: root.top - radius: height/2 + radius: height/4 border.color: "black" border.width: 2 - x: sliderRect.valueToSloderPos(root.lowerSliderPos) + x: sliderRect.valueToSloderPos(root.lowerSliderPos) - priv.sliderWidth/2 Rectangle { id: lowerFloatingRect color: "white" anchors.bottom: lowerSlider.top anchors.bottomMargin: 10 - visible: root.showFloatingLabel && lowerSliderMouseArea.pressed +// visible: root.showFloatingLabel && lowerSliderMouseArea.pressed width: lowerFloatingText.width * 1.2 height: lowerFloatingText.height + height * 1.2 - x: -(width - lowerSlider.width) / 2 - radius: height / 4 + x: -(width - priv.sliderWidth) / 2 + radius: height / 8 Text { id: lowerFloatingText anchors.centerIn: parent - text: sliderRect.sliderPosToValue(lowerSlider.x) + text: sliderRect.sliderPosToValue(lowerSlider.x + priv.sliderWidth/2) } } } @@ -89,10 +160,10 @@ Item { anchors.fill: lowerSlider drag.target: lowerSlider drag.axis: "XAxis" - drag.minimumX: 0 - drag.maximumX: upperSlider.x - lowerSlider.width + drag.minimumX: -priv.sliderWidth / 2 + drag.maximumX: upperSlider.x - priv.sliderWidth onReleased: { - root.lowerSliderPos = sliderRect.sliderPosToValue( lowerSlider.x ); + root.lowerSliderPos = sliderRect.sliderPosToValue( lowerSlider.x + priv.sliderWidth/2 ); root.valueChanged(); } } @@ -100,9 +171,9 @@ Item { Rectangle { id: upperSlider height: root.height - width: height + width: height / 2 anchors.top: root.top - radius: height/2 + radius: height / 4 border.color: "black" border.width: 2 x: sliderRect.valueToSloderPos(root.upperSliderPos) @@ -111,16 +182,16 @@ Item { color: "white" anchors.bottom: upperSlider.top anchors.bottomMargin: 10 - visible: root.showFloatingLabel && upperSliderMouseArea.pressed +// visible: root.showFloatingLabel && upperSliderMouseArea.pressed width: upperFloatingText.width * 1.2 height: upperFloatingText.height + height * 1.2 radius: height / 4 - x: -(width - upperSlider.width) / 2 + x: -(width - priv.sliderWidth) / 2 Text { id: upperFloatingText anchors.centerIn: parent - text: sliderRect.sliderPosToValue(upperSlider.x) + text: sliderRect.sliderPosToValue(upperSlider.x + priv.sliderWidth/2) } } @@ -131,14 +202,16 @@ Item { onClicked: print("button pressed") drag.target: upperSlider drag.axis: "XAxis" - drag.minimumX: lowerSlider.x + lowerSlider.width - drag.maximumX: parent.width - upperSlider.width + drag.minimumX: lowerSlider.x + priv.sliderWidth + drag.maximumX: parent.width - priv.sliderWidth onReleased: { - root.upperSliderPos = sliderRect.sliderPosToValue( upperSlider.x ); + root.upperSliderPos = sliderRect.sliderPosToValue( upperSlider.x + priv.sliderWidth/2 ); root.valueChanged(); } } + + } @@ -146,6 +219,7 @@ Item { id: maxText text: root.maxLabel.length > 0 ? root.maxLabel : max color: "white" + visible: root.minMaxLabelsVisible } } } diff --git a/data/qml/tomahawkimports/InputField.qml b/data/qml/tomahawkimports/InputField.qml index 9f052124de..c15d030771 100644 --- a/data/qml/tomahawkimports/InputField.qml +++ b/data/qml/tomahawkimports/InputField.qml @@ -17,6 +17,12 @@ Rectangle { property int spacing: defaultFontHeight * 0.2 signal accepted( string text ) + onFocusChanged: { + if(focus) { + textInput.forceActiveFocus(); + } + } + Image { id: searchIcon anchors { diff --git a/data/qml/tomahawkimports/TagCloud.qml b/data/qml/tomahawkimports/TagCloud.qml index ba3de03aa2..ccdc82cd30 100644 --- a/data/qml/tomahawkimports/TagCloud.qml +++ b/data/qml/tomahawkimports/TagCloud.qml @@ -16,7 +16,7 @@ Item { Flow { anchors.centerIn: parent width: parent.width - spacing: 3 + spacing: defaultFontSize Repeater { id: cloudRepeater @@ -26,7 +26,7 @@ Item { id: cloudItem width: delegateText.width * 1.1 height: delegateText.height - property double itemScale: Math.random() + .3 + property double itemScale: tagCloud.randomNumber(0.5, 1.2) scale: itemScale Text { id: delegateText @@ -35,7 +35,7 @@ Item { text: modelData font.pointSize: 16 anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15) + //anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15) states: [ State { diff --git a/resources.qrc b/resources.qrc index 5c0708890e..100c6a0f76 100644 --- a/resources.qrc +++ b/resources.qrc @@ -103,9 +103,11 @@ data/qml/tomahawkimports/CoverFlip.qml data/qml/tomahawkimports/BusyIndicator.qml data/qml/StationView.qml + data/qml/stations/StationItem.qml data/qml/stations/StationCreatorPage1.qml data/qml/stations/StationCreatorPage2.qml data/qml/stations/CreateByArtist.qml + data/qml/stations/CreateByYear.qml data/qml/stations/StationConfig.qml data/qml/QmlGridView.qml data/qml/stations/CreateByGenre.qml diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index c4b149df1d..0b5c1edd4e 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -82,7 +82,22 @@ DynamicQmlWidget::playlistInterface() const QString DynamicQmlWidget::title() const { - return m_model->title(); + if ( !m_playlist->title().isEmpty() ) { + return m_playlist->title(); + } + return "Listen to radio..."; +} + + +void +DynamicQmlWidget::setTitle(const QString &title) +{ + m_model->setTitle( title ); + m_playlist->setTitle( title ); + m_model->playlist()->setTitle( title ); + m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); + m_playlist->reportCreated( m_playlist ); + emit titleChanged(); } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index 382bf314ac..c5a0ceac79 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -37,7 +37,7 @@ class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage { Q_OBJECT - Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) Q_PROPERTY(bool configured READ configured NOTIFY configuredChanged) @@ -49,6 +49,7 @@ class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage virtual Tomahawk::playlistinterface_ptr playlistInterface() const; virtual QString title() const; + virtual void setTitle(const QString &title); virtual QString description() const; virtual QString iconSource() const; From abd2957e0e1f3b2d389fe6432d0f4d15305cd0b1 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 02:36:11 +0200 Subject: [PATCH 012/565] create station wizard: workaround slow fist scrolling of the ListView --- data/qml/StationView.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 3448c3adbf..3ce7639c6d 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -67,6 +67,12 @@ Rectangle { onItemClicked: { stationCreator.modeIndex = index + + // FIXME: This is a workaround for the ListView scrolling too slow on the first time + // Lets reinitialize the current index to something invalid and back to 0 (what it already is) before scrolling over to page 1 + stationListView.currentIndex = -1 + stationListView.currentIndex = 0 + stationListView.incrementCurrentIndex() header.subtitle = modeModel.get(index).headerSubtitle + "..." } From e7259f619e599f801e325ddd2df40b9e791602a9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 02:38:25 +0200 Subject: [PATCH 013/565] * Behave, Qt4. --- data/qml/tomahawkimports/InputField.qml | 6 ++---- data/qml/tomahawkimports/PushButton.qml | 11 ++++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/data/qml/tomahawkimports/InputField.qml b/data/qml/tomahawkimports/InputField.qml index c15d030771..29159ad23b 100644 --- a/data/qml/tomahawkimports/InputField.qml +++ b/data/qml/tomahawkimports/InputField.qml @@ -85,12 +85,10 @@ Rectangle { } - BorderImage { - source: "../../images/inputfield-border.svg" + Image { +// source: "../../images/inputfield-border.svg" anchors.fill: parent anchors.margins: root.radius * 0.1 clip: true - border.left: defaultFontHeight/4; border.top: defaultFontHeight/4 - border.right: defaultFontHeight/4; border.bottom: defaultFontHeight/4 } } diff --git a/data/qml/tomahawkimports/PushButton.qml b/data/qml/tomahawkimports/PushButton.qml index b2c4b25035..25dc864001 100644 --- a/data/qml/tomahawkimports/PushButton.qml +++ b/data/qml/tomahawkimports/PushButton.qml @@ -6,13 +6,14 @@ Rectangle { height: buttonText.height * 1.4 width: buttonText.width + (spacing * 2) radius: defaultFontHeight * 0.25 - border.width: defaultFontHeight * 0.05 - border.color: "#a7a7a7" +// border.width: defaultFontHeight * 0.05 +// border.color: "#a7a7a7" - gradient: Gradient { + color: "white" +/* gradient: Gradient { GradientStop { position: 0.0; color: mouseArea.pressed ? "#040404" : "#fbfbfb" } GradientStop { position: 1.0; color: mouseArea.pressed ? "#8e8f8e" : "#787878" } - } + }*/ property int spacing: defaultFontHeight * 0.5 property alias text: buttonText.text @@ -23,7 +24,7 @@ Rectangle { id: buttonText anchors.centerIn: root font.pointSize: defaultFontSize - color: mouseArea.pressed ? "white" : "black" + color: mouseArea.pressed ? "grey" : "black" } MouseArea { From 3f25010cd9ea3cedda9d501a924ab3c289267310 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 03:43:50 +0200 Subject: [PATCH 014/565] add animation to FlexibleHeader --- data/qml/tomahawkimports/FlexibleHeader.qml | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/data/qml/tomahawkimports/FlexibleHeader.qml b/data/qml/tomahawkimports/FlexibleHeader.qml index 04c5e2566e..4822b76f4c 100644 --- a/data/qml/tomahawkimports/FlexibleHeader.qml +++ b/data/qml/tomahawkimports/FlexibleHeader.qml @@ -66,8 +66,9 @@ Rectangle { } Column { - height: parent.height + height: childrenRect.height width: parent.width - iconImage.width - parent.spacing + anchors.verticalCenter: parent.verticalCenter Item { id: titleItem @@ -131,7 +132,30 @@ Rectangle { font.pointSize: defaultFontSize * 1.2 width: parent.width elide: Text.ElideRight + height: text.length > 0 ? defaultFontHeight : 0 + opacity: text.length > 0 ? 1 : 0 + Behavior on height { + NumberAnimation { duration: 200 } + } + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + onTextChanged: { + if (text.length > 0) { + animationText.text = text + } + } + + Text { + id: animationText + color: parent.color + font: parent.font + elide: parent.elide + anchors.fill: parent + } } + } } From 541ad42b86aa79853f9f59f771e4147744a2f038 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 04:39:57 +0200 Subject: [PATCH 015/565] some more work on start by year --- data/qml/stations/CreateByYear.qml | 76 +++++++++++-------- data/qml/stations/StationCreatorPage1.qml | 1 + data/qml/stations/StationItem.qml | 1 + data/qml/tomahawkimports/DoubleSlider.qml | 16 ++-- .../playlist/dynamic/GeneratorInterface.h | 2 + .../dynamic/echonest/EchonestGenerator.cpp | 12 +++ .../dynamic/echonest/EchonestGenerator.h | 2 + .../dynamic/widgets/DynamicQmlWidget.cpp | 20 +++++ .../dynamic/widgets/DynamicQmlWidget.h | 2 + 9 files changed, 91 insertions(+), 41 deletions(-) diff --git a/data/qml/stations/CreateByYear.qml b/data/qml/stations/CreateByYear.qml index 5e14616689..ae9f15a69b 100644 --- a/data/qml/stations/CreateByYear.qml +++ b/data/qml/stations/CreateByYear.qml @@ -8,9 +8,14 @@ Item { signal done(string text) - function createStation(artist) { - mainView.startStationFromArtist(artist) - root.done(artist) + function createStationFromYear(year) { + mainView.startStationFromYear(year) + root.done(year) + } + + function createStationFromTo(yearFrom, yearTo) { + mainView.startStationFromTo(yearFrom, yearTo) + root.done(yearFrom + " to " + yearTo) } Column { @@ -27,53 +32,58 @@ Item { } Row { - height: artistInputField.height + height: yearInputField.height width: parent.width spacing: defaultFontHeight * 0.5 + Text { + text: "Year:" + color: "white" + anchors.verticalCenter: parent.verticalCenter + } + InputField { - id: artistInputField + id: yearInputField width: parent.width - createFromInputButton.width - parent.spacing onAccepted: createStation(text) } - - PushButton { - id: createFromInputButton - text: "Go!" - enabled: artistInputField.text.length > 2 - onClicked: createStation(artistInputField.text) - } } -// Item { -// height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3 -// width: parent.width -// ArtistView { -// id: artistView -// height: parent.height -// width: parent.width -// model: artistChartsModel -// clip: true -// delegateHeight: defaultFontHeight * 6 - -// onItemClicked: { -// createStation(artistChartsModel.itemFromIndex(index).artistName); -// } -// } -// ScrollBar { -// listView: artistView -// } -// } - DoubleSlider { + id: yearSlider width: parent.width - height: defaultFontHeight * 2 + height: defaultFontHeight * 4 min: 1960 max: new Date().getFullYear() lowerSliderPos: 1990 upperSliderPos: 2010 minMaxLabelsVisible: false + opacity: yearInputField.text.length > 0 ? 0.3 : 1 + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + } + + PushButton { + id: createFromInputButton + text: "Go!" + enabled: yearInputField.text.length == 0 || (yearInputField.text >= yearSlider.min && yearInputField.text <= yearSlider.max) + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + if (yearInputField.text.length > 0) { + createStationFromYear(yearInputField.text) + } else { + createStationFromTo(yearSlider.lowerSliderPos, yearSlider.upperSliderPos) + } + } + + // TODO: move some disabled look/animation to the button itself + opacity: enabled ? 1 : 0.3 + Behavior on opacity { + NumberAnimation { duration: 200 } + } } } } diff --git a/data/qml/stations/StationCreatorPage1.qml b/data/qml/stations/StationCreatorPage1.qml index 826d69d691..ca1aed727e 100644 --- a/data/qml/stations/StationCreatorPage1.qml +++ b/data/qml/stations/StationCreatorPage1.qml @@ -15,6 +15,7 @@ Item { anchors.centerIn: parent width: root.width * 9 / 10 height: cellHeight + interactive: false cellWidth: (width - 1) / 3 cellHeight: cellWidth //* 10 / 16 diff --git a/data/qml/stations/StationItem.qml b/data/qml/stations/StationItem.qml index 9d7804a0a9..77bec8c46d 100644 --- a/data/qml/stations/StationItem.qml +++ b/data/qml/stations/StationItem.qml @@ -65,6 +65,7 @@ Item { anchors.centerIn: parent height: defaultFontHeight * 4 width: height +// count: 12 opacity: mainView.loading ? 1 : 0 running: mainView.loading diff --git a/data/qml/tomahawkimports/DoubleSlider.qml b/data/qml/tomahawkimports/DoubleSlider.qml index 08061b96e2..2ac2c64a8d 100644 --- a/data/qml/tomahawkimports/DoubleSlider.qml +++ b/data/qml/tomahawkimports/DoubleSlider.qml @@ -28,8 +28,8 @@ Item { property int steps: root.max - root.min + 1 - property int sliderHeight: root.height - property int sliderWidth: root.height / 2 + property int sliderHeight: root.height / 3 + property int sliderWidth: sliderHeight / 2 } Row { @@ -47,7 +47,7 @@ Item { Item { id: sliderRect - height: root.height + height: root.height / 4 property int maxWidth: parent.width - (minText.visible ? minText.width : 0) - (maxText.visible ? maxText.width : 0) - parent.spacing * 2 width: maxWidth - (maxWidth % priv.steps) anchors.horizontalCenter: parent.horizontalCenter @@ -64,7 +64,7 @@ Item { Rectangle { id: sliderBase - height: root.height / 1.5 + height: parent.height width: parent.width + defaultFontHeight * 1.5 color: "white" radius: height / 2 @@ -131,7 +131,7 @@ Item { id: lowerSlider height: priv.sliderHeight width: priv.sliderWidth - anchors.top: root.top + anchors.verticalCenter: sliderBase.verticalCenter radius: height/4 border.color: "black" border.width: 2 @@ -170,9 +170,9 @@ Item { Rectangle { id: upperSlider - height: root.height - width: height / 2 - anchors.top: root.top + height: priv.sliderHeight + width: priv.sliderWidth + anchors.verticalCenter: sliderBase.verticalCenter radius: height / 4 border.color: "black" border.width: 2 diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h index 3a6d7fbdfc..4eb217ee45 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h @@ -114,6 +114,8 @@ class DLLEXPORT GeneratorInterface : public QObject virtual bool startFromTrack( const Tomahawk::query_ptr& query ) = 0; virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ) = 0; virtual bool startFromGenre( const QString& genre ) = 0; + virtual bool startFromYear( int year ) = 0; + virtual bool startFromTo( int yearFrom, int yearTo) = 0; signals: void error( const QString& title, const QString& body); diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 9edac4ccc8..056a4b526e 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -309,6 +309,18 @@ EchonestGenerator::startFromGenre( const QString& genre ) return true; } +bool EchonestGenerator::startFromYear(int year) +{ + //TODO: libechonest doesn't support filtering for year yet... + return false; +} + +bool EchonestGenerator::startFromTo(int yearFrom, int yearTo) +{ + //TODO: libechonest doesn't support filtering for year yet... + return false; +} + void EchonestGenerator::doGenerate( const Echonest::DynamicPlaylist::PlaylistParams& paramsIn ) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h index 4375a5153b..7fb77cd432 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h @@ -83,6 +83,8 @@ class DLLEXPORT EchonestGenerator : public GeneratorInterface virtual bool startFromTrack( const Tomahawk::query_ptr& query ); virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ); virtual bool startFromGenre( const QString& genre ); + virtual bool startFromYear( int year ); + virtual bool startFromTo( int yearFrom, int yearTo ); static QStringList styles(); static QStringList moods(); diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 0b5c1edd4e..3c783d3ca7 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -168,6 +168,26 @@ void DynamicQmlWidget::startStationFromGenre(const QString &genre) emit configuredChanged(); } +void DynamicQmlWidget::startStationFromYear(int year) +{ + tDebug() << "should start startion from year" << year; + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromYear( year ); + emit loadingChanged(); + emit configuredChanged(); +} + +void DynamicQmlWidget::startStationFromTo(int yearFrom, int yearTo) +{ + tDebug() << "should start startion from years" << yearFrom << "to" << yearTo; + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromTo( yearFrom, yearTo ); + emit loadingChanged(); + emit configuredChanged(); +} + void DynamicQmlWidget::currentIndexChanged() { tDebug() << "current index is" << m_model->currentItem().row(); diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index c5a0ceac79..bf77e4b78c 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -74,6 +74,8 @@ public slots: void pause(); void startStationFromArtist(const QString &artist); void startStationFromGenre(const QString &genre); + void startStationFromYear(int year); + void startStationFromTo(int yearFrom, int yearTo); private slots: void currentIndexChanged(); From 7b6c183bf734c3719db236c356897ce32bd5c689 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 20 May 2013 20:05:56 +0200 Subject: [PATCH 016/565] * Need to actually load controls. --- .../database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp index 5ad0dc4fb3..5812178768 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp @@ -71,8 +71,8 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) type = controlsQuery.value( 3 ).toString(); mode = static_cast( controlsQuery.value( 2 ).toInt() ); -/* QStringList controlIds = v.toStringList(); -// qDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1); + QStringList controlIds = v.toStringList(); + tDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1); foreach( const QString& controlId, controlIds ) { TomahawkSqlQuery controlQuery = dbi->newquery(); @@ -91,7 +91,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) c[ "input" ] = controlQuery.value( 2 ).toString(); controls << c; } - }*/ + } } else { From 65eec14a48e94a1e4c0bbce406d2232744fe2b8a Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 17:06:54 +0200 Subject: [PATCH 017/565] fix merge --- src/tomahawk/sourcetree/SourcesModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 13c552487d..891fed0033 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -313,7 +313,7 @@ SourcesModel::appendGroups() sc->setSortValue( 1 ); // browse section - GenericPageItem* radio = new GenericPageItem( this, browse, tr( "Radio" ), ImageRegistry::instance()->icon( RESPATH "images/station.svg" ), + GenericPageItem* radio = new GenericPageItem( this, m_browse, tr( "Radio" ), ImageRegistry::instance()->icon( RESPATH "images/station.svg" ), boost::bind( &ViewManager::showRadioPage, ViewManager::instance() ), boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); radio->setSortValue( 2 ); From fd0ee87208fcbddcbe3ad5a30f31bf47464dba3b Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 18:48:18 +0200 Subject: [PATCH 018/565] fix playlist generation and clean up dynamicqmlwidget a bit --- .../dynamic/widgets/DynamicQmlWidget.cpp | 42 ++++++------------- .../dynamic/widgets/DynamicQmlWidget.h | 2 - 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 3c783d3ca7..a645eec91a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -26,8 +26,6 @@ namespace Tomahawk DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent ) : DeclarativeView( parent ) , m_playlist( playlist ) - , m_runningOnDemand( false ) - , m_activePlaylist( false ) , m_playNextResolved( false ) { m_model = new DynamicModel( this ); @@ -62,8 +60,12 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) ); - // m_playlist->generator()->generate( 20 ); - loadArtistCharts(); + if (configured()) { + m_playlist->generator()->generate( 20 ); + } else { + // TODO: only load if needed, i.e. the user clicks on start station by artist + loadArtistCharts(); + } } @@ -212,10 +214,7 @@ void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track) void DynamicQmlWidget::error(const QString &title, const QString &body) { - qDebug() << "got a generator error:" << title << body; - -// m_playlist->generator()->fetchNext(); - + tDebug() << "got a generator error:" << title << body; } void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision) @@ -226,9 +225,9 @@ void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision) void DynamicQmlWidget::resolvingFinished(bool hasResults) { Q_UNUSED(hasResults) - qDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row(); + tDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row(); if( m_proxyModel->rowCount() <= m_proxyModel->currentIndex().row() + 8 ) { - qDebug() << "fetching next one"; + tDebug() << "fetching next one"; m_playlist->generator()->fetchNext(); } @@ -240,28 +239,14 @@ void DynamicQmlWidget::resolvingFinished(bool hasResults) void DynamicQmlWidget::trackStarted() { - if ( m_activePlaylist && !m_playlist.isNull() && - m_playlist->mode() == OnDemand && !m_runningOnDemand ) - { - - startStation(); - } + startStation(); } void DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl ) { - if ( pl == m_proxyModel->playlistInterface() ) // same playlist - m_activePlaylist = true; - else - { - m_activePlaylist = false; - - // user started playing something somewhere else, so give it a rest - if ( m_runningOnDemand ) - { - stopStation( false ); - } + if ( pl != m_proxyModel->playlistInterface() ) { + stopStation( false ); } } @@ -269,14 +254,11 @@ void DynamicQmlWidget::stopStation( bool stopPlaying ) { m_model->stopOnDemand( stopPlaying ); - m_runningOnDemand = false; - } void DynamicQmlWidget::startStation() { - m_runningOnDemand = true; m_model->startOnDemand(); } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index bf77e4b78c..ce2d59f40e 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -102,8 +102,6 @@ private slots: PlayableModel* m_artistChartsModel; - bool m_runningOnDemand; - bool m_activePlaylist; bool m_playNextResolved; }; From ad88242d3a1cdda7853d25709cd57b18b9ffcecf Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 18:55:29 +0200 Subject: [PATCH 019/565] more cleanup --- .../dynamic/widgets/DynamicQmlWidget.cpp | 29 ------------------- .../dynamic/widgets/DynamicQmlWidget.h | 5 ---- 2 files changed, 34 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index a645eec91a..deba5107d7 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -57,9 +57,6 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); connect( m_playlist->generator().data(), SIGNAL( error( QString, QString )), SLOT( error(QString,QString) ) ); - connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); - connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) ); - if (configured()) { m_playlist->generator()->generate( 20 ); } else { @@ -237,32 +234,6 @@ void DynamicQmlWidget::resolvingFinished(bool hasResults) } } -void DynamicQmlWidget::trackStarted() -{ - startStation(); -} - -void -DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl ) -{ - if ( pl != m_proxyModel->playlistInterface() ) { - stopStation( false ); - } -} - -void -DynamicQmlWidget::stopStation( bool stopPlaying ) -{ - m_model->stopOnDemand( stopPlaying ); -} - -void -DynamicQmlWidget::startStation() -{ - m_model->startOnDemand(); -} - - void DynamicQmlWidget::loadArtistCharts() { diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index ce2d59f40e..397ee47430 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -84,14 +84,9 @@ private slots: void error( const QString& title, const QString& body); void onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ); - void playlistChanged( Tomahawk::playlistinterface_ptr pl ); void resolvingFinished( bool hasResults ); - void trackStarted(); - void startStation(); - void stopStation( bool stopPlaying ); - void loadArtistCharts(); void onArtistCharts( const QList< Tomahawk::artist_ptr >& artists ); From bafcb1c07eabf32ae1b26e0ffa660a0f77ad3887 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:53:30 +0200 Subject: [PATCH 020/565] * Style fix. --- src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 486774f4b7..2df06574e2 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -276,7 +276,8 @@ DynamicPlaylist::loadRevision( const QString& rev ) setBusy( true ); DatabaseCommand_LoadDynamicPlaylistEntries* cmd = new DatabaseCommand_LoadDynamicPlaylistEntries( rev.isEmpty() ? currentrevision() : rev ); - if ( m_generator->mode() == OnDemand ) { + if ( m_generator->mode() == OnDemand ) + { connect( cmd, SIGNAL( done( QString, bool, QString, @@ -287,7 +288,9 @@ DynamicPlaylist::loadRevision( const QString& rev ) QString, QVariantList, bool) ) ); - } else if ( m_generator->mode() == Static ) { + } + else if ( m_generator->mode() == Static ) + { connect( cmd, SIGNAL( done( QString, QList< QString >, QList< QString >, @@ -407,6 +410,7 @@ DynamicPlaylist::setRevision( const QString& rev, m_generator = GeneratorFactory::create( type ); } + tDebug() << Q_FUNC_INFO << controls; m_generator->setControls( controls ); m_generator->setMode( Static ); @@ -452,6 +456,7 @@ DynamicPlaylist::setRevision( const QString& rev, m_generator = geninterface_ptr( GeneratorFactory::create( type ) ); } + tDebug() << Q_FUNC_INFO << controls; m_generator->setControls( controls ); m_generator->setMode( OnDemand ); From 656f621868c97ae4005ff188f700c29c0c2755c6 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:56:08 +0200 Subject: [PATCH 021/565] * Create dynamic playlist when needed - for temporary ones. --- .../dynamic/widgets/DynamicQmlWidget.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index deba5107d7..6ad8a743e6 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -10,6 +10,7 @@ #include "SourceList.h" #include "audio/AudioEngine.h" #include "database/Database.h" +#include "database/DatabaseCommand_CreateDynamicPlaylist.h" #include "database/DatabaseCommand_PlaybackCharts.h" #include "widgets/DeclarativeCoverArtProvider.h" #include "utils/TomahawkUtilsGui.h" @@ -89,14 +90,23 @@ DynamicQmlWidget::title() const void -DynamicQmlWidget::setTitle(const QString &title) +DynamicQmlWidget::setTitle( const QString& title ) { m_model->setTitle( title ); m_playlist->setTitle( title ); m_model->playlist()->setTitle( title ); - m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); - m_playlist->reportCreated( m_playlist ); - emit titleChanged(); + + if ( !m_playlist->loaded() ) + { + DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( SourceList::instance()->getLocal(), m_playlist, true ); +// connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + m_playlist->reportCreated( m_playlist ); + + m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); +// m_playlist->reportCreated( m_playlist ); + emit titleChanged(); + } } @@ -134,6 +144,7 @@ bool DynamicQmlWidget::loading() bool DynamicQmlWidget::configured() { +// return true; return !m_playlist->generator()->controls().isEmpty(); } @@ -202,6 +213,7 @@ DynamicQmlWidget::tracksGenerated( const QList< query_ptr >& queries ) void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track) { + tDebug() << Q_FUNC_INFO << track->toString(); m_model->tracksGenerated( QList() << track ); m_playlist->resolve(); From b2a7b23590b27b3b310d3dbbeeb31beb855aa696 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:56:26 +0200 Subject: [PATCH 022/565] * Radio mode needs to initialize a temporary station. --- src/libtomahawk/ViewManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 75c9707f29..4899b9e6f1 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -394,7 +394,7 @@ ViewManager::showRadioPage() { if ( !m_radioView ) { - dynplaylist_ptr playlist = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false, QString(), false, false ); + dynplaylist_ptr playlist = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false, QString(), false, true ); playlist->setMode( OnDemand ); m_radioView = new Tomahawk::DynamicQmlWidget( playlist, m_stack ); From 6b6fd9a6e2bd4e02444c4b739cf90f54e97407c6 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:57:57 +0200 Subject: [PATCH 023/565] * Fixed displaying of back / save button. --- data/qml/StationView.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 3ce7639c6d..33eb10e1a6 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -22,9 +22,9 @@ Rectangle { subtitle: ""//generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: !mainView.configured && stationListView.currentIndex == 2 + showNextButton: !mainView.configured nextButtonText: "Save" - backButtonText: mainView.configured ? "Configure" : "Back" + backButtonText: "Back" z: 1 //cover albumcovers that may leave their area From d025e8f512bb6f0c5e1bf876525d59cf4f94cec8 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:46:16 +0200 Subject: [PATCH 024/565] * Show save button when we have a configured station. --- data/qml/StationView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 33eb10e1a6..bea98e94d7 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -22,7 +22,7 @@ Rectangle { subtitle: ""//generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: !mainView.configured + showNextButton: mainView.configured nextButtonText: "Save" backButtonText: "Back" From 3a822f22e94abb365fa29868ee854d3985fa9a2a Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:46:31 +0200 Subject: [PATCH 025/565] * Fixed invoke. --- src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 2df06574e2..bdac2772fe 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -445,7 +445,7 @@ DynamicPlaylist::setRevision( const QString& rev, Q_ARG( QString, rev ), Q_ARG( bool, is_newest_rev ), Q_ARG( QString, type ), - QGenericArgument( "QList< Tomahawk::dyncontrol_ptr >" , (const void*)&controls ), + Q_ARG( QVariantList, controls ), Q_ARG( bool, applied ) ); return; } From daff8101a09a54e066d8056770e0d92d501deece Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:46:55 +0200 Subject: [PATCH 026/565] * Create those darned controls manually. --- .../dynamic/echonest/EchonestGenerator.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 056a4b526e..ffcbbd9820 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -274,6 +274,22 @@ EchonestGenerator::startFromArtist( const Tomahawk::artist_ptr& artist ) Echonest::DynamicPlaylist::PlaylistParams params; params << data; + +/* Q_PROPERTY( QString type READ type WRITE setType ) // the generator type associated with this control + Q_PROPERTY( QString id READ id WRITE setId ) + Q_PROPERTY( QString selectedType READ selectedType WRITE setSelectedType ) + Q_PROPERTY( QString match READ match WRITE setMatch ) + Q_PROPERTY( QString input READ input WRITE setInput ) + Q_PROPERTY( QString summary READ summary ) // a summary of the control in phrase form*/ + + QVariantMap controlsList; + controlsList[ "id" ] = uuid(); + controlsList[ "selectedType" ] = "echonest"; + controlsList[ "match" ] = QString::number( data.first ); + controlsList[ "input" ] = data.second; + controlsList[ "summary" ] = ""; + setControls( QVariantList() << controlsList ); + // params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); emit paramsGenerated( params ); From 914f8ccdbb88d73a1b5211437e23b6aea205d01a Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:47:12 +0200 Subject: [PATCH 027/565] * Print out controls when creating a station. --- .../playlist/dynamic/widgets/DynamicQmlWidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 6ad8a743e6..20f564610a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -98,13 +98,13 @@ DynamicQmlWidget::setTitle( const QString& title ) if ( !m_playlist->loaded() ) { + tDebug() << "CONTROLS ARE SAVED:" << m_playlist->generator()->controls(); DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( SourceList::instance()->getLocal(), m_playlist, true ); -// connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); Database::instance()->enqueue( QSharedPointer(cmd) ); - m_playlist->reportCreated( m_playlist ); + m_playlist->reportCreated( m_playlist ); m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); -// m_playlist->reportCreated( m_playlist ); + emit titleChanged(); } } From ecab5b41b03e18819032e9baa28c91fb7c20f51e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 21:15:51 +0200 Subject: [PATCH 028/565] * Create EchonestParams out of the variant list. --- .../dynamic/echonest/EchonestGenerator.cpp | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index ffcbbd9820..87527c6afa 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -419,22 +419,40 @@ EchonestGenerator::staticFinished() void EchonestGenerator::getParams() throw( std::runtime_error ) { + /*Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::Artist; + data.second = artist->name(); + Echonest::DynamicPlaylist::PlaylistParams params; -/* foreach( const dyncontrol_ptr& control, m_controls ) { - params.append( control.dynamicCast()->toENParam() ); + params << data; + */ + + Echonest::DynamicPlaylist::PlaylistParams params; + foreach( const QVariant& control, m_controls ) + { + QVariantMap controlMap = control.toMap(); + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = (Echonest::DynamicPlaylist::PlaylistParam)controlMap[ "match" ].toUInt(); + data.second = controlMap[ "input" ].toString(); + + params.append( data ); } - if( appendRadioType( params ) == Echonest::DynamicPlaylist::SongRadioType ) { + if ( appendRadioType( params ) == Echonest::DynamicPlaylist::SongRadioType ) + { // we need to do another pass, converting all song queries to song-ids. m_storedParams = params; qDeleteAll( m_waiting ); m_waiting.clear(); // one query per track - for( int i = 0; i < params.count(); i++ ) { + for( int i = 0; i < params.count(); i++ ) + { const Echonest::DynamicPlaylist::PlaylistParamData param = params.value( i ); - if( param.first == Echonest::DynamicPlaylist::SongId ) { // this is a song type enum + if ( param.first == Echonest::DynamicPlaylist::SongId ) + { + // this is a song type enum QString text = param.second.toString(); Echonest::Song::SearchParams q; @@ -448,14 +466,16 @@ EchonestGenerator::getParams() throw( std::runtime_error ) } } - if( m_waiting.isEmpty() ) { + if ( m_waiting.isEmpty() ) + { m_storedParams.clear(); emit paramsGenerated( params ); } - - } else { + } + else + { emit paramsGenerated( params ); - }*/ + } } From b211263ced47e7a0c0c0f85f90c0b543e5208835 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 21:07:13 +0200 Subject: [PATCH 029/565] generate does the real thing again --- .../dynamic/echonest/EchonestGenerator.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 87527c6afa..087c413761 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -188,10 +188,7 @@ void EchonestGenerator::generate( int number ) { // convert to an echonest query, and fire it off -/* qDebug() << Q_FUNC_INFO; - qDebug() << "Generating playlist with" << m_controls.size(); - foreach( const dyncontrol_ptr& ctrl, m_controls ) - qDebug() << ctrl->selectedType() << ctrl->match() << ctrl->input(); + tDebug() << "Generating playlist with" << m_controls.size(); setProperty( "number", number ); //HACK @@ -202,16 +199,7 @@ EchonestGenerator::generate( int number ) } catch( std::runtime_error& e ) { qWarning() << "Got invalid controls!" << e.what(); emit error( "Filters are not valid", e.what() ); - }*/ - - QList< query_ptr > queries; - queries << Query::get("Colour Haze", "All", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Sun", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Zen", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Outside", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Dirt", QString(), uuid(), true); - - emit generated( queries ); + } } From 23f47f825a72cac690694b845c833d2b3d37d51f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 21:51:10 +0200 Subject: [PATCH 030/565] * Fixed startFromGenre. --- .../dynamic/echonest/EchonestGenerator.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 087c413761..1c0e78ba32 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -307,7 +307,24 @@ EchonestGenerator::startFromGenre( const QString& genre ) Echonest::DynamicPlaylist::PlaylistParams params; params << data; + + QVariantList controlsList; + QVariantMap controlsMap; + + controlsMap[ "id" ] = uuid(); + controlsMap[ "selectedType" ] = "echonest"; + controlsMap[ "match" ] = QString::number( data.first ); + controlsMap[ "input" ] = data.second; + controlsMap[ "summary" ] = ""; + controlsList << controlsMap; + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); + controlsMap[ "id" ] = uuid(); + controlsMap[ "match" ] = QString::number( Echonest::DynamicPlaylist::Type ); + controlsMap[ "input" ] = QString::number( Echonest::DynamicPlaylist::ArtistDescriptionType ); + controlsList << controlsMap; + + setControls( controlsList ); emit paramsGenerated( params ); return true; From fd2d0a53ed6c0a8fab935ed16cbd9d1ad0fd03a0 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 21:42:11 +0200 Subject: [PATCH 031/565] station summary does something again, at least for stations by artist --- data/qml/StationView.qml | 6 +----- .../playlist/dynamic/echonest/EchonestGenerator.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index bea98e94d7..ed032e4ab9 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -19,7 +19,7 @@ Rectangle { width: parent.width icon: "../images/station.svg" title: mainView.title - subtitle: ""//generator.summary + subtitle: generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 showNextButton: mainView.configured @@ -29,10 +29,6 @@ Rectangle { z: 1 //cover albumcovers that may leave their area onBackPressed: { - if(mainView.configured) { - return; - } - inputBubble.opacity = 0 stationListView.decrementCurrentIndex() if(stationListView.currentIndex == 1) { diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 1c0e78ba32..4ee1ade16e 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -275,7 +275,7 @@ EchonestGenerator::startFromArtist( const Tomahawk::artist_ptr& artist ) controlsList[ "selectedType" ] = "echonest"; controlsList[ "match" ] = QString::number( data.first ); controlsList[ "input" ] = data.second; - controlsList[ "summary" ] = ""; + controlsList[ "summary" ] = tr("Songs from %1").arg(data.second.toString()); setControls( QVariantList() << controlsList ); // params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); @@ -666,6 +666,9 @@ EchonestGenerator::sentenceSummary() * NOTE / TODO: In order for the sentence to be grammatically correct, we must follow the EN API rules. That means we can't have multiple of some types of filters, * and all Artist types must be the same. The filters aren't checked at the moment until Generate / Play is pressed. Consider doing a check on hide as well. */ + + // Keeping this for now to make stuff backwards compatible + /* QList< dyncontrol_ptr > allcontrols = m_controls; QString sentence = "Songs "; @@ -751,7 +754,10 @@ EchonestGenerator::sentenceSummary() return sentence;*/ - return "This is a station!"; + if (m_controls.isEmpty()) { + return ""; + } + return m_controls.first().toMap().value("summary").toString(); } void From d09fe0a5d643dcfcb2274c269268409046d3cd78 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 22:00:01 +0200 Subject: [PATCH 032/565] also add a description for byStation --- src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 4ee1ade16e..07965385de 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -315,7 +315,7 @@ EchonestGenerator::startFromGenre( const QString& genre ) controlsMap[ "selectedType" ] = "echonest"; controlsMap[ "match" ] = QString::number( data.first ); controlsMap[ "input" ] = data.second; - controlsMap[ "summary" ] = ""; + controlsMap[ "summary" ] = data.second; controlsList << controlsMap; params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); From 2045dcfaf7d331d90940045502ca0ae4e1560f96 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 22:00:10 +0200 Subject: [PATCH 033/565] fix loading state --- src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 20f564610a..8fede25e52 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -50,8 +50,6 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa setSource( QUrl( "qrc" RESPATH "qml/StationView.qml" ) ); connect( m_model, SIGNAL( currentIndexChanged()), SLOT( currentIndexChanged() ) ); - connect( m_model, SIGNAL( loadingStarted() ), SIGNAL(loadingChanged() ) ); - connect( m_model, SIGNAL( loadingFinished() ), SIGNAL(loadingChanged() ) ); connect( m_model, SIGNAL( changed() ), SIGNAL( titleChanged() ) ); connect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( nextTrackGenerated( Tomahawk::query_ptr ) ) ); @@ -241,6 +239,7 @@ void DynamicQmlWidget::resolvingFinished(bool hasResults) } if( m_playNextResolved && m_proxyModel->rowCount() > 0 ) { + emit loadingChanged(); playItem( 0 ); m_playNextResolved = false; } From 145cb6739087ee70a3b1948c8991dee705371aad Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 22:24:50 +0200 Subject: [PATCH 034/565] adjust station summaries to look the same everywhere --- data/qml/StationView.qml | 6 +++--- data/qml/stations/CreateByArtist.qml | 2 +- data/qml/stations/CreateByGenre.qml | 2 +- data/qml/stations/CreateByYear.qml | 2 +- .../playlist/dynamic/echonest/EchonestGenerator.cpp | 2 +- .../playlist/dynamic/widgets/DynamicQmlWidget.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index ed032e4ab9..009f7b6ac0 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -48,9 +48,9 @@ Rectangle { ListModel { id: modeModel - ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml"; headerSubtitle: "by" } - ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml"; headerSubtitle: "like" } - ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "stations/CreateByYear.qml"; headerSubtitle: "from" } + ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml"; headerSubtitle: "Songs from" } + ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml"; headerSubtitle: "Songs of genre" } + ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "stations/CreateByYear.qml"; headerSubtitle: "Songs from" } } VisualItemModel { diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml index 359b353f78..60bb5b4b6a 100644 --- a/data/qml/stations/CreateByArtist.qml +++ b/data/qml/stations/CreateByArtist.qml @@ -23,7 +23,7 @@ Item { HeaderLabel { id: headerText - text: "Create station by artist..." + text: "Enter or pick an artist" } Row { diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml index 1d89641833..017c0cf020 100644 --- a/data/qml/stations/CreateByGenre.qml +++ b/data/qml/stations/CreateByGenre.qml @@ -46,7 +46,7 @@ Item { HeaderLabel { id: headerText anchors.horizontalCenter: parent.horizontalCenter - text: "Create station by genre..." + text: "Enter or pick a genre" } Row { diff --git a/data/qml/stations/CreateByYear.qml b/data/qml/stations/CreateByYear.qml index ae9f15a69b..ebd0a8c3a3 100644 --- a/data/qml/stations/CreateByYear.qml +++ b/data/qml/stations/CreateByYear.qml @@ -28,7 +28,7 @@ Item { HeaderLabel { id: headerText - text: "Create station by year..." + text: "Enter a year or pick a range" } Row { diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 07965385de..8a703458f9 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -315,7 +315,7 @@ EchonestGenerator::startFromGenre( const QString& genre ) controlsMap[ "selectedType" ] = "echonest"; controlsMap[ "match" ] = QString::number( data.first ); controlsMap[ "input" ] = data.second; - controlsMap[ "summary" ] = data.second; + controlsMap[ "summary" ] = tr("Songs of genre %1").arg(data.second.toString()); controlsList << controlsMap; params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 8fede25e52..fc60eb447b 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -83,7 +83,7 @@ DynamicQmlWidget::title() const if ( !m_playlist->title().isEmpty() ) { return m_playlist->title(); } - return "Listen to radio..."; + return "Listen to radio"; } From 64fd91c0cac78a352477beb30a5bd43d2250a592 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 1 Jul 2013 21:03:33 +0200 Subject: [PATCH 035/565] some more work on saving stations --- data/qml/StationView.qml | 73 +++++----------- data/qml/tomahawkimports/Button.qml | 37 +++++--- data/qml/tomahawkimports/InputBubble.qml | 104 +++++++++++++++++++++++ resources.qrc | 1 + 4 files changed, 153 insertions(+), 62 deletions(-) create mode 100644 data/qml/tomahawkimports/InputBubble.qml diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 009f7b6ac0..01ae10474e 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -2,6 +2,7 @@ import QtQuick 1.1 import tomahawk 1.0 import "tomahawkimports" import "stations" + Rectangle { id: scene color: "black" @@ -22,7 +23,7 @@ Rectangle { subtitle: generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: mainView.configured + showNextButton: mainView.configured && stationListView.currentIndex > 0 nextButtonText: "Save" backButtonText: "Back" @@ -41,7 +42,7 @@ Rectangle { // In our case the next button is the save button onNextPressed: { inputBubble.opacity = 1 - saveNameInput.forceActiveFocus(); + inputBubble.forceActiveFocus(); } } @@ -137,55 +138,25 @@ Rectangle { onModelChanged: print("ccccccccccccc", mainView.configured) } - Rectangle { - id: inputBubble - color: "black" - border.width: 2 - border.color: "white" - height: defaultFontHeight * 3 - width: height * 6 - radius: defaultFontHeight / 2 - anchors.top: header.bottom - anchors.right: parent.right - anchors.rightMargin: defaultFontHeight / 2 - anchors.topMargin: -defaultFontHeight / 2 - z: 2 - opacity: 0 - Behavior on opacity { - NumberAnimation { duration: 200 } - } - - Row { - anchors.centerIn: parent - width: parent.width - defaultFontHeight - spacing: defaultFontHeight / 2 - - function saveStation(name) { - mainView.title = name - inputBubble.opacity = 0 - header.showNextButton = false - header.backButtonText = "Configure" - } - - Text { - id: nameText - color: "white" - text: "Name:" - anchors.verticalCenter: parent.verticalCenter - } - InputField { - id: saveNameInput - width: parent.width - nameText.width - saveOkButton.width - parent.spacing * 2 - placeholderText: "Station" - onAccepted: parent.saveStation(text); - } - PushButton { - id: saveOkButton - text: "OK" - onClicked: parent.saveStation(saveNameInput.text) - } - } - + InputBubble { + id: inputBubble + text: "Station name:" + width: defaultFontHeight * 18 + anchors.top: header.bottom + anchors.right: parent.right + anchors.rightMargin: defaultFontHeight / 2 + anchors.topMargin: -defaultFontHeight / 2 + z: 2 + opacity: 0 + + onAccepted: { + mainView.title = inputBubble.inputText + inputBubble.opacity = 0 + header.showNextButton = false + header.showBackButton = false + inputBubble.opacity = 0 } + onRejected: inputBubble.opacity = 0 + } } diff --git a/data/qml/tomahawkimports/Button.qml b/data/qml/tomahawkimports/Button.qml index 1c73d7c8c8..344b1403d8 100644 --- a/data/qml/tomahawkimports/Button.qml +++ b/data/qml/tomahawkimports/Button.qml @@ -2,28 +2,43 @@ import QtQuick 1.1 Rectangle { id: root - color: buttonMouseArea.containsMouse ? "blue" : "gray" - border.width: 2 - border.color: "white" - radius: height/2 - height: buttonText.height * 1.2 - width: buttonText.width * 1.5 + height: contentRow.height + defaultFontHeight / 2 + width: contentRow.width + defaultFontHeight / 2 property alias text: buttonText.text - property color textColor: "white" + property alias imageSource: image.source + property bool enabled: true + + color: "transparent" + border.width: defaultFontHeight / 16 + border.color: buttonMouseArea.containsMouse ? "lightblue" : "transparent" + radius: defaultFontHeight / 4 signal clicked() - Text { - id: buttonText + Row { + id: contentRow + spacing: defaultFontHeight / 4 + width: childrenRect.width + height: childrenRect.height anchors.centerIn: parent - color: root.textColor + Image { + id: image + height: defaultFontHeight + width: source.length == 0 ? 0 : height + } + + Text { + id: buttonText + color: root.enabled ? "black" : "grey" + } } MouseArea { id: buttonMouseArea anchors.fill: parent - hoverEnabled: true + hoverEnabled: root.enabled + enabled: root.enabled onClicked: root.clicked(); } } diff --git a/data/qml/tomahawkimports/InputBubble.qml b/data/qml/tomahawkimports/InputBubble.qml new file mode 100644 index 0000000000..efaa4d2496 --- /dev/null +++ b/data/qml/tomahawkimports/InputBubble.qml @@ -0,0 +1,104 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +FocusScope { + id: root + + property alias text: messageText.text + property alias inputText: inputField.text + + property real arrowPosition: 1 + + height: contentColumn.height + defaultFontHeight * 2 + + signal accepted() + signal rejected() + + onFocusChanged: { + if (focus) { + inputField.forceActiveFocus() + } + } + + MouseArea { + anchors.fill: parent + anchors.margins: -999999999 + hoverEnabled: root.opacity > 0 + enabled: root.opacity > 0 + } + + Item { + id: backgroundItem + anchors.fill: parent + opacity: 0.9 + Rectangle { + id: arrowRect + height: defaultFontHeight / 1.8 + width: height + rotation: 45 + color: "black" + anchors.top: backgroundItem.top + x: defaultFontHeight - arrowRect.width/2 + (root.width - defaultFontHeight*2) * arrowPosition + } + Rectangle { + id: background + anchors.fill: parent + color: "white" + border.color: "black" + border.width: defaultFontHeight / 8 + radius: defaultFontHeight / 2 + anchors.topMargin: defaultFontHeight / 4 + } + Rectangle { + height: defaultFontHeight / 2 + width: height + rotation: 45 + color: "white" + anchors.centerIn: arrowRect + } + } + + Column { + id: contentColumn + width: parent.width - defaultFontHeight + height: childrenRect.height + anchors.centerIn: parent + anchors.verticalCenterOffset: defaultFontHeight / 4 + spacing: defaultFontHeight / 2 + + Row { + width: parent.width + height: childrenRect.height + spacing: defaultFontHeight / 2 + Text { + id: messageText + wrapMode: Text.WordWrap + anchors.verticalCenter: parent.verticalCenter + } + InputField { + id: inputField + width: parent.width - x + anchors.verticalCenter: parent.verticalCenter + } + } + Row { + height: childrenRect.height + anchors.right: parent.right + spacing: defaultFontHeight + Button { + text: "OK" + imageSource: "qrc:///data/images/ok.svg" + enabled: inputField.text.length > 0 + onClicked: root.accepted() + } + Button { + text: "Cancel" + imageSource: "qrc:///data/images/cancel.svg" + onClicked: { + inputField.text = "" + root.rejected() + } + } + } + } +} diff --git a/resources.qrc b/resources.qrc index 131291a629..5fd4cea3bb 100644 --- a/resources.qrc +++ b/resources.qrc @@ -102,6 +102,7 @@ data/qml/tomahawkimports/PushButton.qml data/qml/tomahawkimports/CoverFlip.qml data/qml/tomahawkimports/BusyIndicator.qml + data/qml/tomahawkimports/InputBubble.qml data/qml/StationView.qml data/qml/stations/StationItem.qml data/qml/stations/StationCreatorPage1.qml From 17eeae9a8f65bf274e8095ce41fcc604085514f4 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:53:18 +0200 Subject: [PATCH 036/565] * Imported QML resources. --- data/images/inputfield-border.svg | 145 +++++++++++++ data/images/station-artist.svg | 14 ++ data/images/station-genre.svg | 18 ++ data/images/station-year.svg | 19 ++ data/qml/DeclarativeHeader.qml | 21 ++ data/qml/QmlGridView.qml | 65 ++++++ data/qml/SpinnerTest.qml | 34 +++ data/qml/StationView.qml | 162 ++++++++++++++ data/qml/stations/CreateByArtist.qml | 69 ++++++ data/qml/stations/CreateByGenre.qml | 88 ++++++++ data/qml/stations/StationConfig.qml | 79 +++++++ data/qml/stations/StationCreatorPage1.qml | 64 ++++++ data/qml/stations/StationCreatorPage2.qml | 25 +++ data/qml/tomahawkimports/ArtistView.qml | 71 +++++++ data/qml/tomahawkimports/BusyIndicator.qml | 52 +++++ data/qml/tomahawkimports/Button.qml | 29 +++ data/qml/tomahawkimports/CoverFlip.qml | 137 ++++++++++++ data/qml/tomahawkimports/CoverImage.qml | 197 +++++++++++++++++ data/qml/tomahawkimports/DoubleSlider.qml | 151 +++++++++++++ data/qml/tomahawkimports/FlexibleHeader.qml | 199 ++++++++++++++++++ data/qml/tomahawkimports/HeaderLabel.qml | 7 + data/qml/tomahawkimports/InputField.qml | 90 ++++++++ data/qml/tomahawkimports/PushButton.qml | 34 +++ data/qml/tomahawkimports/RoundedButton.qml | 43 ++++ data/qml/tomahawkimports/ScrollBar.qml | 69 ++++++ data/qml/tomahawkimports/TagCloud.qml | 80 +++++++ .../qml/tomahawkimports/ToggleViewButtons.qml | 34 +++ 27 files changed, 1996 insertions(+) create mode 100644 data/images/inputfield-border.svg create mode 100644 data/images/station-artist.svg create mode 100644 data/images/station-genre.svg create mode 100644 data/images/station-year.svg create mode 100644 data/qml/DeclarativeHeader.qml create mode 100644 data/qml/QmlGridView.qml create mode 100644 data/qml/SpinnerTest.qml create mode 100644 data/qml/StationView.qml create mode 100644 data/qml/stations/CreateByArtist.qml create mode 100644 data/qml/stations/CreateByGenre.qml create mode 100644 data/qml/stations/StationConfig.qml create mode 100644 data/qml/stations/StationCreatorPage1.qml create mode 100644 data/qml/stations/StationCreatorPage2.qml create mode 100644 data/qml/tomahawkimports/ArtistView.qml create mode 100644 data/qml/tomahawkimports/BusyIndicator.qml create mode 100644 data/qml/tomahawkimports/Button.qml create mode 100644 data/qml/tomahawkimports/CoverFlip.qml create mode 100644 data/qml/tomahawkimports/CoverImage.qml create mode 100644 data/qml/tomahawkimports/DoubleSlider.qml create mode 100644 data/qml/tomahawkimports/FlexibleHeader.qml create mode 100644 data/qml/tomahawkimports/HeaderLabel.qml create mode 100644 data/qml/tomahawkimports/InputField.qml create mode 100644 data/qml/tomahawkimports/PushButton.qml create mode 100644 data/qml/tomahawkimports/RoundedButton.qml create mode 100644 data/qml/tomahawkimports/ScrollBar.qml create mode 100644 data/qml/tomahawkimports/TagCloud.qml create mode 100644 data/qml/tomahawkimports/ToggleViewButtons.qml diff --git a/data/images/inputfield-border.svg b/data/images/inputfield-border.svg new file mode 100644 index 0000000000..3f3ff841ca --- /dev/null +++ b/data/images/inputfield-border.svg @@ -0,0 +1,145 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/images/station-artist.svg b/data/images/station-artist.svg new file mode 100644 index 0000000000..43ed51948e --- /dev/null +++ b/data/images/station-artist.svg @@ -0,0 +1,14 @@ + + + station-artist + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + \ No newline at end of file diff --git a/data/images/station-genre.svg b/data/images/station-genre.svg new file mode 100644 index 0000000000..ba5c1157fe --- /dev/null +++ b/data/images/station-genre.svg @@ -0,0 +1,18 @@ + + + station-genre + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/images/station-year.svg b/data/images/station-year.svg new file mode 100644 index 0000000000..a18fb23d33 --- /dev/null +++ b/data/images/station-year.svg @@ -0,0 +1,19 @@ + + + station-year + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/qml/DeclarativeHeader.qml b/data/qml/DeclarativeHeader.qml new file mode 100644 index 0000000000..5863a9d27f --- /dev/null +++ b/data/qml/DeclarativeHeader.qml @@ -0,0 +1,21 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "tomahawkimports" + +// Only to be used together with DeclarativeHeader C++ class +// If you want to use the header in QML, use FlexibleHeader + +Item { + anchors.fill: parent + + FlexibleHeader { + anchors.fill: parent + icon: iconSource + title: caption + subtitle: description + buttonModel: buttonList + + onSearchTextChanged: mainView.setFilterText(searchText) + onCurrentButtonIndexChanged: mainView.viewModeSelected(currentButtonIndex) + } +} diff --git a/data/qml/QmlGridView.qml b/data/qml/QmlGridView.qml new file mode 100644 index 0000000000..1d27a2d5e2 --- /dev/null +++ b/data/qml/QmlGridView.qml @@ -0,0 +1,65 @@ +import QtQuick 1.1 +//import tomahawk 1.0 +import "tomahawkimports" + +Rectangle { + anchors.fill: parent + color: "black" + + Text { + id: fontMetrics + text: "Here's some sample text" + opacity: 0 + } + + GridView { + id: gridView + anchors.fill: parent + //anchors.rightMargin: scrollBar.width + + cellHeight: cellWidth + cellWidth: calculateCoverSize(gridView.width - 3) + + cacheBuffer: cellHeight * 5 + + function calculateCoverSize(rectWidth) { + var itemWidth = fontMetrics.width; + var itemsPerRow = Math.max( 1, Math.floor( rectWidth / itemWidth ) ); + + var remSpace = rectWidth - ( itemsPerRow * itemWidth ); + var extraSpace = remSpace / itemsPerRow; + return itemWidth + extraSpace; + + } + + model: mainModel + + delegate: CoverImage { + height: gridView.cellHeight// * 0.95 + width: gridView.cellWidth// * 0.95 + + showLabels: true + showMirror: false + artistName: model.artistName + trackName: model.trackName + artworkId: model.coverID + showPlayButton: true + currentlyPlaying: isPlaying + smooth: !gridView.moving + + onClicked: { + rootView.onItemClicked(index) + } + onPlayClicked: { + rootView.onItemPlayClicked(index) + } + } + } + + ScrollBar { + id: scrollBar + listView: gridView + orientation: Qt.Vertical + margin: -width + } +} diff --git a/data/qml/SpinnerTest.qml b/data/qml/SpinnerTest.qml new file mode 100644 index 0000000000..217b4dbcf2 --- /dev/null +++ b/data/qml/SpinnerTest.qml @@ -0,0 +1,34 @@ +import QtQuick 1.1 +import "tomahawkimports" + +Rectangle { +width: 1400 +height: 900 +color: "black" + +BusyIndicator { + anchors.centerIn: parent + anchors.horizontalCenterOffset: 200 + height: 200 + width: 200 +} + +Image { + id: svgSpinner + source: "../images/loading-animation.svg" + smooth: true + height: 200 + width: 200 + anchors.centerIn: parent + anchors.horizontalCenterOffset: -200 + + Timer { + running: true + repeat: true + interval: 200 + onTriggered: svgSpinner.rotation += 360 / 12 + } +} + + +} diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml new file mode 100644 index 0000000000..7bb79e1b47 --- /dev/null +++ b/data/qml/StationView.qml @@ -0,0 +1,162 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "tomahawkimports" +import "stations" +Rectangle { + id: scene + color: "black" + anchors.fill: parent + state: "list" + + FlexibleHeader { + id: header + anchors { + left: parent.left + top: parent.top + right: parent.right + } + height: defaultFontHeight * 4 + width: parent.width + icon: "../images/station.svg" + title: mainView.title + subtitle: generator.summary + showSearchField: false + showBackButton: stationListView.currentIndex > 0 + showNextButton: stationListView.currentIndex == 2 + nextButtonText: "Save" + + z: 1 //cover albumcovers that may leave their area + + onBackPressed: stationListView.decrementCurrentIndex() + onNextPressed: stationListView.incrementCurrentIndex() + } + + ListModel { + id: modeModel + ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml" } + ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml" } + ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "year" } + } + + VisualItemModel { + id: stationVisualModel + + StationCreatorPage1 { + height: scene.height - header.height + width: scene.width + model: modeModel + + onItemClicked: { + stationCreator.content = modeModel.get(index).creatorContent + stationListView.incrementCurrentIndex() + } + } + + StationCreatorPage2 { + id: stationCreator + height: stationListView.height + width: stationListView.width + + onNext: stationListView.incrementCurrentIndex() + } + + Item { + id: stationItem + height: stationListView.height + width: stationListView.width + + CoverFlip { + id: coverView + anchors.right: parent.right + anchors.top: parent.top + height: parent.height + width: parent.width + interactive: false + + backgroundColor: scene.color + + model: dynamicModel + currentIndex: currentlyPlayedIndex + + onItemPlayPauseClicked: { + mainView.playItem(index) + } + + onItemClicked: { + mainView.playItem(index) + } + + states: [ + State { + name: "empty"; when: mainView.loading + PropertyChanges { + target: coverView + anchors.rightMargin: -coverView.width + anchors.topMargin: - coverView.height + scale: 0 + } + } + ] + transitions: [ + Transition { + from: "empty" + to: "*" + NumberAnimation { + properties: "anchors.topMargin,anchors.rightMargin,scale" + duration: 1000 + easing.type: Easing.OutQuad + } + } + + ] +// Behavior on anchors.topMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on anchors.rightMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on scale { +// NumberAnimation { duration: 500 } +// } + + } + BusyIndicator { + id: busyIndicator + anchors.centerIn: parent + height: defaultFontHeight * 4 + width: height + + opacity: mainView.loading ? 1 : 0 + running: mainView.loading + } + + } + + } + + + ListView { + id: stationListView + anchors { + left: parent.left + top: header.bottom + right: parent.right + bottom: parent.bottom + } + + contentHeight: height + contentWidth: width + orientation: ListView.Horizontal + model: stationVisualModel + interactive: false + highlightMoveDuration: 300 + + onHeightChanged: { + contentHeight = scene.height + } + onWidthChanged: { + contentWidth = scene.width + } + } + +} diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml new file mode 100644 index 0000000000..f3f2f4ceb3 --- /dev/null +++ b/data/qml/stations/CreateByArtist.qml @@ -0,0 +1,69 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + anchors.fill: parent + + signal done() + + function createStation(artist) { + mainView.startStationFromArtist(artist) + root.done() + } + + Column { + id: upperColumn + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height + width: defaultFontHeight * 30 + anchors.bottomMargin: defaultFontHeight + spacing: defaultFontHeight + + HeaderLabel { + id: headerText + text: "Create station by artist..." + } + + Row { + height: artistInputField.height + width: parent.width + spacing: defaultFontHeight * 0.5 + + InputField { + id: artistInputField + width: parent.width - createFromInputButton.width - parent.spacing + + onAccepted: createStation(text) + } + + PushButton { + id: createFromInputButton + text: "Go!" + enabled: artistInputField.text.length > 2 + onClicked: createStation(artistInputField.text) + } + } + + Item { + height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3 + width: parent.width + ArtistView { + id: artistView + height: parent.height + width: parent.width + model: artistChartsModel + clip: true + delegateHeight: defaultFontHeight * 6 + + onItemClicked: { + createStation(artistChartsModel.itemFromIndex(index).artistName); + } + } + ScrollBar { + listView: artistView + } + } + } +} diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml new file mode 100644 index 0000000000..9d2fb1b9b9 --- /dev/null +++ b/data/qml/stations/CreateByGenre.qml @@ -0,0 +1,88 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + anchors.fill: parent + + signal done() + + function createStation(genre) { + mainView.startStationFromGenre(genre) + root.done() + } + + ListModel { + id: styleModel + ListElement { modelData: "acoustic" } + ListElement { modelData: "alternative" } + ListElement { modelData: "alternative rock" } + ListElement { modelData: "classic" } + ListElement { modelData: "folk" } + ListElement { modelData: "indie" } + ListElement { modelData: "pop" } + ListElement { modelData: "rock" } + ListElement { modelData: "hip-hop" } + ListElement { modelData: "punk" } + ListElement { modelData: "grunge" } + ListElement { modelData: "indie" } + ListElement { modelData: "electronic" } + ListElement { modelData: "country" } + ListElement { modelData: "jazz" } + ListElement { modelData: "psychodelic" } + ListElement { modelData: "soundtrack" } + ListElement { modelData: "reggae" } + ListElement { modelData: "house" } + ListElement { modelData: "drum and base" } + } + + Column { + id: upperColumn + anchors.fill: parent + anchors.bottomMargin: defaultFontHeight + spacing: defaultFontHeight + + HeaderLabel { + id: headerText + anchors.horizontalCenter: parent.horizontalCenter + text: "Create station by genre..." + } + + Row { + width: defaultFontHeight * 30 + height: genreInputField.height + spacing: defaultFontHeight * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + + InputField { + id: genreInputField + width: parent.width - createFromInputButton.width - parent.spacing + + onAccepted: createStation(text); + } + + PushButton { + id: createFromInputButton + text: "Go!" + height: genreInputField.height + enabled: genreInputField.text.length > 2 + onClicked: createStation(genreInputField.text) + } + } + + Item { + height: parent.height - headerText.height - genreInputField.height + width: parent.width + TagCloud { + anchors.fill: parent + anchors.margins: parent.width / 6 + model: styleModel + + onTagClicked: { + root.createStation(tag); + } + } + } + } +} diff --git a/data/qml/stations/StationConfig.qml b/data/qml/stations/StationConfig.qml new file mode 100644 index 0000000000..d6f0ce11df --- /dev/null +++ b/data/qml/stations/StationConfig.qml @@ -0,0 +1,79 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "tomahawkimports" + +Item { + id: fineTuneView + + property color textColor: "white" + + signal done(); + + Grid { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 50 + anchors.horizontalCenter: parent.horizontalCenter + width: scene.width / 2 + spacing: 50 + columns: 2 + + Text { + color: fineTuneView.textColor + text: "Name:" + + } + InputField { + text: echonestStation.name + + onAccepted: { + print("text changed!!!") + echonestStation.name = text; + } + } + + Text { + id: tempoText + text: "Tempo:" + color: "white" + } + DoubleSlider { + width: 500 + height: tempoText.height + min: 0 + max: 500 + lowerSliderPos: echonestStation.minTempo + upperSliderPos: echonestStation.maxTempo + onValueChanged: echonestStation.setTempo( lowerSliderPos, upperSliderPos ) + } + + Text { + id: hotnessText + text: "Hotness:" + color: "white" + } + DoubleSlider { + width: 500 + height: hotnessText.height + min: 0 + max: 100 + minLabel: "Less" + maxLabel: "More" + showFloatingLabel: false + lowerSliderPos: echonestStation.minHotttness * 100 + upperSliderPos: echonestStation.maxHotttness * 100 + onValueChanged: echonestStation.setHotttness( 1.0 * lowerSliderPos / 100, 1.0 * upperSliderPos / 100 ) + } + } + + + Button { + id: configureButton + onClicked: fineTuneView.done(); + text: "configure" + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + } + +} diff --git a/data/qml/stations/StationCreatorPage1.qml b/data/qml/stations/StationCreatorPage1.qml new file mode 100644 index 0000000000..826d69d691 --- /dev/null +++ b/data/qml/stations/StationCreatorPage1.qml @@ -0,0 +1,64 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + + +Item { + id: root + property alias model: gridView.model + property int spacing: 10 + + signal itemClicked(int index) + + GridView { + id: gridView + anchors.centerIn: parent + width: root.width * 9 / 10 + height: cellHeight + + cellWidth: (width - 1) / 3 + cellHeight: cellWidth //* 10 / 16 + + delegate: Image { + width: gridView.cellWidth - root.spacing + height: gridView.cellHeight - root.spacing + source: image + smooth: true + + Rectangle { + id: textBackground + anchors { + left: parent.left + bottom: parent.bottom + right: parent.right + } + height: parent.height / 5 + color: "black" + opacity: .5 + + } + Text { + anchors.centerIn: textBackground + text: label + color: "white" + font.bold: true + } + Rectangle { + id: hoverShade + anchors.fill: parent + color: "white" + opacity: mouseArea.containsMouse ? .2 : 0 + + Behavior on opacity { + NumberAnimation { easing.type: Easing.Linear; duration: 300 } + } + } + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: root.itemClicked(index) + } + } + } +} diff --git a/data/qml/stations/StationCreatorPage2.qml b/data/qml/stations/StationCreatorPage2.qml new file mode 100644 index 0000000000..34f4b65cda --- /dev/null +++ b/data/qml/stations/StationCreatorPage2.qml @@ -0,0 +1,25 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + + property int margins: defaultFontHeight * 2 + property alias content: contentLoader.source + + signal next() + + Loader { + id: contentLoader + anchors.fill: parent + anchors.margins: root.margins + } + + Connections { + target: contentLoader.item + + onDone: root.next() + } + +} diff --git a/data/qml/tomahawkimports/ArtistView.qml b/data/qml/tomahawkimports/ArtistView.qml new file mode 100644 index 0000000000..a35c9b4284 --- /dev/null +++ b/data/qml/tomahawkimports/ArtistView.qml @@ -0,0 +1,71 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +ListView { + id: root + + property int delegateHeight: defaultFontHeight * 3 + + signal itemClicked(int index) + + delegate: Item { + width: parent.width + height: root.delegateHeight + + Rectangle { + id: background + anchors.fill: parent + radius: defaultFontHeight / 2 + opacity: 0.5 + gradient: Gradient { + GradientStop { position: 0.0; color: "#00FFFFFF" } + GradientStop { position: 1.0; color: "#AAFFFFFF" } + } + + states: [ + State { + name: "hovered"; when: mouseArea.containsMouse + PropertyChanges { target: background; opacity: 1 } + } + ] + + transitions: [ + Transition { + from: "*"; to: "hovered" + NumberAnimation { properties: "opacity"; duration: 100 } + }, + Transition { + from: "hovered"; to: "*" + NumberAnimation { properties: "opacity"; duration: 600 } + } + ] + } + + Row { + anchors.fill: parent + spacing: defaultFontHeight + + CoverImage { + id: coverImage + height: parent.height + width: height + showLabels: false + artworkId: model.coverID + } + Text { + text: model.artistName + color: "white" + anchors.verticalCenter: parent.verticalCenter + width: parent.width - coverImage.width - parent.spacing + elide: Text.ElideRight + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.itemClicked(index) + hoverEnabled: true + } + } +} diff --git a/data/qml/tomahawkimports/BusyIndicator.qml b/data/qml/tomahawkimports/BusyIndicator.qml new file mode 100644 index 0000000000..52b6e0fba3 --- /dev/null +++ b/data/qml/tomahawkimports/BusyIndicator.qml @@ -0,0 +1,52 @@ +import QtQuick 1.1 + +Item { + id: busyIndicator + width: 100 + height: width + property int barWidth: width / 10 + property int barHeight: height / 4 + property int count: 12 + property color color: "white" + property int currentHighlight: 0 + property bool running: true + property int interval: 200 + + Behavior on opacity { + NumberAnimation { duration: 500 } + } + + Repeater { + model: busyIndicator.count + + + Item { + height: parent.height + width: busyIndicator.barWidth + anchors.centerIn: parent + Rectangle { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: busyIndicator.barHeight + radius: width / 2 + + color: busyIndicator.color + } + rotation: 360 / busyIndicator.count * index + opacity: 1 - ((index > busyIndicator.currentHighlight ? busyIndicator.currentHighlight + busyIndicator.count : busyIndicator.currentHighlight) - index) / busyIndicator.count + Behavior on opacity { + NumberAnimation { duration: busyIndicator.interval } + } + } + } + + Timer { + interval: busyIndicator.interval + running: busyIndicator.running + repeat: true + onTriggered: parent.currentHighlight = (parent.currentHighlight + 1) % busyIndicator.count + } +} diff --git a/data/qml/tomahawkimports/Button.qml b/data/qml/tomahawkimports/Button.qml new file mode 100644 index 0000000000..1c73d7c8c8 --- /dev/null +++ b/data/qml/tomahawkimports/Button.qml @@ -0,0 +1,29 @@ +import QtQuick 1.1 + +Rectangle { + id: root + color: buttonMouseArea.containsMouse ? "blue" : "gray" + border.width: 2 + border.color: "white" + radius: height/2 + height: buttonText.height * 1.2 + width: buttonText.width * 1.5 + + property alias text: buttonText.text + property color textColor: "white" + + signal clicked() + + Text { + id: buttonText + anchors.centerIn: parent + color: root.textColor + } + + MouseArea { + id: buttonMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: root.clicked(); + } +} diff --git a/data/qml/tomahawkimports/CoverFlip.qml b/data/qml/tomahawkimports/CoverFlip.qml new file mode 100644 index 0000000000..58e85824c4 --- /dev/null +++ b/data/qml/tomahawkimports/CoverFlip.qml @@ -0,0 +1,137 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +PathView { + id: coverView + + // The start coordinates for the covers + // Default is left, centered in height + property int pathStartX: 0 + property int pathStartY: height + + // The size of the covers in the path + property int coverSize: height + + property color backgroundColor: "black" + + // emitted when a cover is clicked + signal itemClicked(int index) + + // emitted when a cover is clicked + signal itemPlayPauseClicked(int index) + + preferredHighlightBegin: 0.2 // scene.width / 11000 + preferredHighlightEnd: preferredHighlightBegin + pathItemCount: 5 + //highlightMoveDuration: 500 + + property bool itemHovered: false + + delegate: Item { + id: delegateItem + height: coverView.coverSize + width: coverView.coverSize + + scale: PathView.itemScale + // itemBrightness: PathView.itemBrightness - ((coverView.itemHovered && !coverDelegate.containsMouse) ? .4 : 0) + property double itemBrightness: PathView.itemBrightness + property double itemOpacity: PathView.itemOpacity + property int _origZ + + z: coverView.width - x + + CoverImage { + id: coverDelegate + height: coverView.coverSize + width: coverView.coverSize + anchors { + top: parent.top + right: parent.right + } + + backgroundColor: coverView.backgroundColor + + showLabels: true + showMirror: true + artistName: model.artistName + trackName: model.trackName + artworkId: model.coverID + showPlayButton: true + currentlyPlaying: isPlaying + smooth: true + + // itemBrightness: PathView.itemBrightness - ((coverView.itemHovered && !coverDelegate.containsMouse) ? .4 : 0) + itemBrightness: coverDelegate.containsMouse ? 1 : parent.itemBrightness * (coverView.itemHovered ? .5 : 1) + opacity: parent.itemOpacity + z: coverView.width - x + + onPlayClicked: { + console.log("***************") + coverView.itemPlayPauseClicked(index) + } + + onClicked: { + coverView.itemClicked(index) + } + + onContainsMouseChanged: { + if (containsMouse) { + delegateItem._origZ = delegateItem.z; + coverView.itemHovered = true + } else { + coverView.itemHovered = false + } + } + + + } + states: [ + State { + name: "hovered"; when: coverDelegate.containsMouse && !coverView.moving && index !== currentIndex + PropertyChanges { + target: delegateItem + width: coverView.coverSize * 2 + z: delegateItem._origZ + } + } + ] + transitions: [ + Transition { + NumberAnimation { + properties: "width" + duration: 300 + easing.type: Easing.InOutSine + } + + } + ] + } + + path: Path { + startX: coverView.pathStartX + startY: coverView.pathStartY + + PathAttribute { name: "itemOpacity"; value: 0 } + PathAttribute { name: "itemBrightness"; value: 0 } + PathAttribute { name: "itemScale"; value: 1.3 } + + PathLine { x: coverView.width / 4; y: coverView.height / 4 * 3} + PathPercent { value: 0.1 } + PathAttribute { name: "itemOpacity"; value: 0 } + PathAttribute { name: "itemBrightness"; value: 1 } + PathAttribute { name: "itemScale"; value: 1.0 } + + PathLine { x: coverView.width / 2; y: coverView.height / 2} + PathPercent { value: 0.2 } + PathAttribute { name: "itemOpacity"; value: 1 } + PathAttribute { name: "itemBrightness"; value: 1 } + PathAttribute { name: "itemScale"; value: 0.5 } + + PathLine { x: coverView.width; y: 0 } + PathPercent { value: 1 } + PathAttribute { name: "itemOpacity"; value: 1 } + PathAttribute { name: "itemBrightness"; value: 0 } + PathAttribute { name: "itemScale"; value: 0.1 } + } + +} diff --git a/data/qml/tomahawkimports/CoverImage.qml b/data/qml/tomahawkimports/CoverImage.qml new file mode 100644 index 0000000000..518137aac8 --- /dev/null +++ b/data/qml/tomahawkimports/CoverImage.qml @@ -0,0 +1,197 @@ +import QtQuick 1.1 + +Item { + id: root + + // Should the artist + track labels be painted + property bool showLabels: true + + // Should the play button be painted on mouse hover? + property bool showPlayButton: false + + // if this is true, the play button will be swapped by a pause button + property bool currentlyPlaying: false + + // Should the mirror be painted? + property bool showMirror: false + + // Labels & Cover + property string artistName + property string trackName + property string artworkId + + // The border color for the cover image + property color borderColor: "black" + // The border width for the cover image + property int borderWidth: 2 + + // needed to adjust the shadow + property color backgroundColor: "black" + + // sets the brightness for the item and its mirror (1: brightest, 0: darkest) + property double itemBrightness: 1 + property double mirrorBrightness: .5 + + // set this to true if you want to smoothly scale the cover (be aware of performance impacts) + property bool smooth: false + + // will be emitted when the on hower play button is clicked + signal playClicked() + // will be emitted when the cover is clicked + signal clicked() + // will be emitted when the cover is hovered by the mouse + property alias containsMouse: mouseArea.containsMouse + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + + onClicked: { + print("Cover clicked"); + root.clicked(); + } + } + + Rectangle { + id: itemShadow + color: backgroundColor + anchors.fill: parent + + //opacity: 1 - itemBrightness + + Behavior on opacity { + NumberAnimation { easing.type: Easing.Linear; duration: 300 } + } + } + + Component { + id: coverImage + + Item { + property bool isMirror: false + + Image { + anchors.fill: parent + source: "image://albumart/" + artworkId + (isMirror ? "-mirror" : "") + (showLabels ? "-labels" : "") + smooth: root.smooth + opacity: itemBrightness + Behavior on opacity { + NumberAnimation { duration: 300 } + } + } + + Rectangle { + id: itemGlow + anchors.fill: parent + anchors.topMargin: isMirror ? parent.height / 2 : 0 + + opacity: (mouseArea.containsMouse ? .2 : 0) + + Gradient { + id: glowGradient + GradientStop { position: 0.0; color: "white" } + GradientStop { position: 0.7; color: "white" } + GradientStop { position: 0.8; color: "#00000000" } + GradientStop { position: 1.0; color: "#00000000" } + } + Gradient { + id: mirrorGlowGradient + GradientStop { position: 0.0; color: "#00000000" } + GradientStop { position: 0.5; color: "#00000000" } + GradientStop { position: 1.0; color: "#44FFFFFF" } + } + + states: [ + State { + name: "mirrored"; when: isMirror + PropertyChanges { + target: itemGlow + gradient: mirrorGlowGradient + } + }, + State { + name: "normal"; when: !isMirror + PropertyChanges { + target: itemGlow + gradient: glowGradient + } + } + ] + + Behavior on opacity { + NumberAnimation { easing.type: Easing.Linear; duration: 300 } + } + } + + Text { + id: trackText + color: "white" + font.bold: true + text: trackName + anchors { left: parent.left; right: parent.right; bottom: artistText.top } + anchors.margins: 2 + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + opacity: showLabels ? itemBrightness * (isMirror ? 0.5 : 1): 0 + font.pixelSize: root.height / 15 + Behavior on opacity { + NumberAnimation { duration: 300 } + } + } + Text { + id: artistText + color: "white" + font.bold: trackText.text.length == 0 + text: artistName + anchors { left: parent.left; right: parent.right; bottom: parent.bottom } + anchors.margins: root.height / 20 + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + opacity: showLabels ? itemBrightness * (isMirror ? 0.5 : 1) : 0 + font.pixelSize: trackText.text.length == 0 ? root.height / 10 : root.height / 15 + Behavior on opacity { + NumberAnimation { duration: 300 } + } + } + } + + } + Loader { + sourceComponent: coverImage + anchors.fill: parent + } + + Loader { + id: mirroredCover + sourceComponent: parent.showMirror ? coverImage : undefined + anchors.fill: parent + onLoaded: { + item.isMirror = true + } + transform : [ + Rotation { + angle: 180; origin.y: root.height + axis.x: 1; axis.y: 0; axis.z: 0 + } + ] + } + + Image { + id: playButton + visible: showPlayButton ? (mouseArea.containsMouse || currentlyPlaying) : false + source: currentlyPlaying ? "../../images/pause-rest.svg" : "../../images/play-rest.svg" + anchors.centerIn: parent + height: mirroredCover.height / 5 + width: height + smooth: root.smooth + MouseArea { + anchors.fill: parent + onClicked: { + print("Play button clicked"); + root.playClicked(); + } + } + } + +} diff --git a/data/qml/tomahawkimports/DoubleSlider.qml b/data/qml/tomahawkimports/DoubleSlider.qml new file mode 100644 index 0000000000..b1dc522cc0 --- /dev/null +++ b/data/qml/tomahawkimports/DoubleSlider.qml @@ -0,0 +1,151 @@ +import QtQuick 1.1 + +Item { + id: root + width: 500 + height: 10 + + property int min: 0 + property int max: 100 + + /** The labels next to the slider + * if empty, min and max values are used + */ + property string minLabel: "" + property string maxLabel: "" + + /** Should the floating label indicating the current position be shown? */ + property bool showFloatingLabel: true + + property int lowerSliderPos: 25 + property int upperSliderPos: 75 + + signal valueChanged() + + Row { + anchors.fill: parent + spacing: 10 + + Text { + id: minText + text: root.minLabel.length > 0 ? root.minLabel : min + color: "white" + } + + Item { + id: sliderRect + height: root.height + width: parent.width - minText.width - maxText.width - parent.spacing * 2 + + function sliderPosToValue( sliderPos ) { + var percent = sliderPos * 100 / (sliderRect.width - lowerSlider.width); + return Math.floor(percent * (root.max - root.min) / 100) + root.min + } + + function valueToSloderPos( value ) { + var percent = (value - root.min) * 100 / (root.max - root.min) + return percent * (sliderRect.width - lowerSlider.width) / 100 + } + + Rectangle { + id: sliderBase + height: root.height / 5 + width: parent.width + color: "white" + radius: height / 2 + anchors.centerIn: parent + + } + Rectangle { + id: lowerSlider + height: root.height + width: height + anchors.top: root.top + radius: height/2 + border.color: "black" + border.width: 2 + x: sliderRect.valueToSloderPos(root.lowerSliderPos) + + Rectangle { + id: lowerFloatingRect + color: "white" + anchors.bottom: lowerSlider.top + anchors.bottomMargin: 10 + visible: root.showFloatingLabel && lowerSliderMouseArea.pressed + width: lowerFloatingText.width * 1.2 + height: lowerFloatingText.height + height * 1.2 + x: -(width - lowerSlider.width) / 2 + radius: height / 4 + + Text { + id: lowerFloatingText + anchors.centerIn: parent + text: sliderRect.sliderPosToValue(lowerSlider.x) + } + } + } + MouseArea { + id: lowerSliderMouseArea + anchors.fill: lowerSlider + drag.target: lowerSlider + drag.axis: "XAxis" + drag.minimumX: 0 + drag.maximumX: upperSlider.x - lowerSlider.width + onReleased: { + root.lowerSliderPos = sliderRect.sliderPosToValue( lowerSlider.x ); + root.valueChanged(); + } + } + + Rectangle { + id: upperSlider + height: root.height + width: height + anchors.top: root.top + radius: height/2 + border.color: "black" + border.width: 2 + x: sliderRect.valueToSloderPos(root.upperSliderPos) + Rectangle { + id: upperFloatingRect + color: "white" + anchors.bottom: upperSlider.top + anchors.bottomMargin: 10 + visible: root.showFloatingLabel && upperSliderMouseArea.pressed + width: upperFloatingText.width * 1.2 + height: upperFloatingText.height + height * 1.2 + radius: height / 4 + x: -(width - upperSlider.width) / 2 + + Text { + id: upperFloatingText + anchors.centerIn: parent + text: sliderRect.sliderPosToValue(upperSlider.x) + } + } + + } + MouseArea { + id: upperSliderMouseArea + anchors.fill: upperSlider + onClicked: print("button pressed") + drag.target: upperSlider + drag.axis: "XAxis" + drag.minimumX: lowerSlider.x + lowerSlider.width + drag.maximumX: parent.width - upperSlider.width + onReleased: { + root.upperSliderPos = sliderRect.sliderPosToValue( upperSlider.x ); + root.valueChanged(); + } + + } + } + + + Text { + id: maxText + text: root.maxLabel.length > 0 ? root.maxLabel : max + color: "white" + } + } +} diff --git a/data/qml/tomahawkimports/FlexibleHeader.qml b/data/qml/tomahawkimports/FlexibleHeader.qml new file mode 100644 index 0000000000..04c5e2566e --- /dev/null +++ b/data/qml/tomahawkimports/FlexibleHeader.qml @@ -0,0 +1,199 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Rectangle { + id: root + + // The icon + property alias icon: iconImage.source + + // The title + property alias title: titleItem.titleText + + // The subtitle/description + property alias subtitle: subtitleText.text + + // The model for the ToggleViewButtons. + // "modelData" role name holds the iconSource + // => You can use a QStringList or StandardListModel here + property alias buttonModel: toggleViewButtons.model + + // The index of the currently selected item + property alias currentButtonIndex: toggleViewButtons.currentIndex + + // Should we show the searchfield? + property bool showSearchField: true + + // The SearchFields text + property alias searchText: searchField.text + + property bool showBackButton: false + property bool showNextButton: false + + property string backButtonText: "Back" + property string nextButtonText: "Next" + + // Layout spacing + property int spacing: defaultFontHeight * 0.5 + + signal backPressed() + signal nextPressed() + signal savePressed() + + gradient: Gradient { + GradientStop { position: 0.0; color: "#615858" } + GradientStop { position: 1.0; color: "#231F1F" } + } + + Row { + id: leftRow + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom + right: rightRow.left + } + + anchors.margins: root.spacing + spacing: root.spacing + + Image { + id: iconImage + height: parent.height * 0.8 + width: height + anchors.verticalCenter: parent.verticalCenter + smooth: true + } + + Column { + height: parent.height + width: parent.width - iconImage.width - parent.spacing + + Item { + id: titleItem + height: captionText1.height + width: parent.width + clip: true + + property string titleText + + onTitleTextChanged: { + if(captionText1.text.length > 0) { + captionText2.text = titleText; + renewTitleAnimation.start(); + } else { + captionText1.text = titleText; + } + } + + ParallelAnimation { + id: renewTitleAnimation + property int duration: 500 + property variant easingType: Easing.OutBounce; + + NumberAnimation { target: captionText2; property: "anchors.topMargin"; to: 0; duration: renewTitleAnimation.duration; easing.type: renewTitleAnimation.easingType } + NumberAnimation { target: captionText1; property: "anchors.topMargin"; to: captionText1.height * 2; duration: renewTitleAnimation.duration; easing.type: renewTitleAnimation.easingType } + + onCompleted: { + captionText1.text = titleItem.titleText + captionText2.anchors.topMargin = -captionText2.height * 2 + captionText1.anchors.topMargin = 0 + } + } + + Text { + id: captionText1 + color: "white" + anchors.left: parent.left + anchors.top: parent.top + + font.pointSize: defaultFontSize * 1.5 + font.bold: true + width: parent.width + elide: Text.ElideRight + } + Text { + id: captionText2 + color: "white" + anchors.left: parent.left + anchors.top: parent.top + anchors.topMargin: -height * 2 + font.pointSize: defaultFontSize * 1.5 + font.bold: true + width: parent.width + elide: Text.ElideRight + } + + } + Text { + id: subtitleText + color: "white" + font.pointSize: defaultFontSize * 1.2 + width: parent.width + elide: Text.ElideRight + } + } + + } + + Row { + id: rightRow + anchors { + top: parent.top + right: parent.right + rightMargin: -backButton.width - root.spacing - nextButton.width + bottom: parent.bottom + margins: root.spacing + } + width: childrenRect.width + spacing: root.spacing + layoutDirection: Qt.RightToLeft + + states: [ + State { + name: "oneVisible"; when: root.showBackButton && !root.showNextButton + PropertyChanges { + target: rightRow + anchors.rightMargin: -nextButton.width + } + }, + State { + name: "bothVisible"; when: root.showBackButton && root.showNextButton + PropertyChanges { + target: rightRow + anchors.rightMargin: root.spacing + } + } + + ] + + Behavior on anchors.rightMargin { + NumberAnimation { duration: 200 } + } + + PushButton { + id: nextButton + anchors.verticalCenter: parent.verticalCenter + text: root.nextButtonText + onClicked: root.nextPressed(); + } + PushButton { + id: backButton + anchors.verticalCenter: parent.verticalCenter + text: root.backButtonText + onClicked: root.backPressed(); + } + InputField { + id: searchField + visible: root.showSearchField + anchors.verticalCenter: parent.verticalCenter + placeholderText: "Search..." + showSearchIcon: true + } + ToggleViewButtons { + id: toggleViewButtons + anchors.verticalCenter: parent.verticalCenter + height: defaultFontHeight * 1.5 + } + } +} diff --git a/data/qml/tomahawkimports/HeaderLabel.qml b/data/qml/tomahawkimports/HeaderLabel.qml new file mode 100644 index 0000000000..ebffedbd48 --- /dev/null +++ b/data/qml/tomahawkimports/HeaderLabel.qml @@ -0,0 +1,7 @@ +import QtQuick 1.1 + +Text { + color: "white" + font.pointSize: defaultFontSize + 5 + font.bold: true +} diff --git a/data/qml/tomahawkimports/InputField.qml b/data/qml/tomahawkimports/InputField.qml new file mode 100644 index 0000000000..9f052124de --- /dev/null +++ b/data/qml/tomahawkimports/InputField.qml @@ -0,0 +1,90 @@ +import QtQuick 1.1 + +Rectangle { + id: root + color: "white" + border.color: "black" + border.width: defaultFontHeight * 0.1 + radius: defaultFontHeight * 0.25 + + height: textInput.height * 1.4 + width: 300 + + property bool showSearchIcon: false + property string text: "" + property string placeholderText: "" + + property int spacing: defaultFontHeight * 0.2 + signal accepted( string text ) + + Image { + id: searchIcon + anchors { + left: parent.left + leftMargin: root.spacing + verticalCenter: parent.verticalCenter + } + height: parent.height * 0.6 + width: root.showSearchIcon ? height : 1 + opacity: root.showSearchIcon ? 1 : 0 + smooth: true + source: "../../images/search-icon.svg" + } + + Item { + id: textItem + anchors.left: searchIcon.right + anchors.leftMargin: root.spacing + anchors.right: clearIcon.right + anchors.rightMargin: root.spacing + height: textInput.height + anchors.verticalCenter: parent.verticalCenter + + TextInput { + id: textInput + width: parent.width + anchors.centerIn: parent + text: root.text + font.pointSize: defaultFontSize + + onAccepted: root.accepted( text ); + onTextChanged: root.text = text; + } + Text { + width: parent.width + anchors.centerIn: parent + text: root.text.length === 0 ? root.placeholderText : "" + color: "lightgray" + font.pointSize: defaultFontSize + } + } + + Image { + id: clearIcon + anchors { + right: parent.right + rightMargin: root.spacing + verticalCenter: parent.verticalCenter + } + height: parent.height * 0.8 + width: (root.showSearchIcon && root.text.length > 0) ? height : 1 + opacity: (root.showSearchIcon && root.text.length > 0) ? 1 : 0 + smooth: true + source: "../../images/search-box-dismiss-x.svg" + + MouseArea { + anchors.fill: parent + onClicked: textInput.text = "" + } + } + + + BorderImage { + source: "../../images/inputfield-border.svg" + anchors.fill: parent + anchors.margins: root.radius * 0.1 + clip: true + border.left: defaultFontHeight/4; border.top: defaultFontHeight/4 + border.right: defaultFontHeight/4; border.bottom: defaultFontHeight/4 + } +} diff --git a/data/qml/tomahawkimports/PushButton.qml b/data/qml/tomahawkimports/PushButton.qml new file mode 100644 index 0000000000..b2c4b25035 --- /dev/null +++ b/data/qml/tomahawkimports/PushButton.qml @@ -0,0 +1,34 @@ +import QtQuick 1.1 +//import tomahawk 1.0 + +Rectangle { + id: root + height: buttonText.height * 1.4 + width: buttonText.width + (spacing * 2) + radius: defaultFontHeight * 0.25 + border.width: defaultFontHeight * 0.05 + border.color: "#a7a7a7" + + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? "#040404" : "#fbfbfb" } + GradientStop { position: 1.0; color: mouseArea.pressed ? "#8e8f8e" : "#787878" } + } + + property int spacing: defaultFontHeight * 0.5 + property alias text: buttonText.text + + signal clicked() + + Text { + id: buttonText + anchors.centerIn: root + font.pointSize: defaultFontSize + color: mouseArea.pressed ? "white" : "black" + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.clicked() + } +} diff --git a/data/qml/tomahawkimports/RoundedButton.qml b/data/qml/tomahawkimports/RoundedButton.qml new file mode 100644 index 0000000000..790afe227a --- /dev/null +++ b/data/qml/tomahawkimports/RoundedButton.qml @@ -0,0 +1,43 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Rectangle { + id: root + border.width: 4 + border.color: enabled ? "white" : "grey" + radius: height / 2 + color: (buttonMouseArea.containsMouse && enabled) ? "#22ffffff" : "black" + opacity: hidden ? 0 : 1 + + height: defaultFontHeight * 2 + width: height + + property string text + property bool enabled: true + property bool hidden: false + + signal clicked() + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + Behavior on color { + ColorAnimation { duration: 200 } + } + + Text { + anchors.centerIn: parent + text: parent.text + color: root.border.color + font.pixelSize: parent.height * .75 + font.bold: true + } + MouseArea { + id: buttonMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: root.enabled + onClicked: parent.clicked() + } +} diff --git a/data/qml/tomahawkimports/ScrollBar.qml b/data/qml/tomahawkimports/ScrollBar.qml new file mode 100644 index 0000000000..351e378030 --- /dev/null +++ b/data/qml/tomahawkimports/ScrollBar.qml @@ -0,0 +1,69 @@ +import QtQuick 1.1 + +Item { + id: scrollBar + width: defaultFontHeight / 2 + + // the ListView where to attach this scrollbar + property variant listView + // the orientation of the scrollbar + property variant orientation : Qt.Vertical + + property int margin: defaultFontHeight * 0.25 + + states: [ + State { + name: "hidden"; when: !listView.moving + PropertyChanges { target: scrollBar; opacity: 0 } + }, + State { + name: "visible"; when: listView.moving + PropertyChanges { target: scrollBar; opacity: 1 } + } + ] + transitions: [ + Transition { + from: "hidden" + to: "visible" + NumberAnimation { properties: "opacity"; duration: 200 } + }, + Transition { + from: "visible" + to: "hidden" + NumberAnimation { properties: "opacity"; duration: 2000 } + } + ] + + anchors { + left: orientation == Qt.Vertical ? listView.right : listView.left + leftMargin: orientation == Qt.Vertical ? scrollBar.margin : 0 + top: orientation == Qt.Vertical ? listView.top : listView.bottom + topMargin: orientation == Qt.Vertical ? 0 : scrollBar.margin + bottom: orientation == Qt.Vertical ? listView.bottom : undefined + right: orientation == Qt.Vertical ? undefined : listView.right + } + + // A light, semi-transparent background + Rectangle { + id: background + anchors.fill: parent + radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1) + color: "white" + opacity: 0.2 + clip: true + // Size the bar to the required size, depending upon the orientation. + Rectangle { + property real position: orientation == Qt.Vertical ? (listView.contentY / listView.contentHeight) : (listView.contentX / listView.contentWidth) + property real pageSize: orientation == Qt.Vertical ? (listView.height / listView.contentHeight) : (listView.width / listView.contentWidth) + + x: orientation == Qt.Vertical ? 1 : (position * (scrollBar.width-2) + 1) + y: orientation == Qt.Vertical ? (position * (scrollBar.height-2) + 1) : 1 + width: orientation == Qt.Vertical ? (parent.width-2) : (pageSize * (scrollBar.width-2)) + height: orientation == Qt.Vertical ? (pageSize * (scrollBar.height-2)) : (parent.height-2) + radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1) + color: "white" + opacity: 1 + } + } + +} diff --git a/data/qml/tomahawkimports/TagCloud.qml b/data/qml/tomahawkimports/TagCloud.qml new file mode 100644 index 0000000000..ba3de03aa2 --- /dev/null +++ b/data/qml/tomahawkimports/TagCloud.qml @@ -0,0 +1,80 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Item { + id: tagCloud + + property variant model: 10 + + signal tagClicked( string tag ) + + function randomNumber(min, max) { + var date = new Date(); + return (max - min) * Math.random(date.getSeconds()) + min + } + + Flow { + anchors.centerIn: parent + width: parent.width + spacing: 3 + + Repeater { + id: cloudRepeater + model: tagCloud.model + + delegate: Item { + id: cloudItem + width: delegateText.width * 1.1 + height: delegateText.height + property double itemScale: Math.random() + .3 + scale: itemScale + Text { + id: delegateText + color: "gray" + //text: controlModel.controlAt( index ).summary + text: modelData + font.pointSize: 16 + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15) + + states: [ + State { + name: "hovered"; when: cloudItemMouseArea.containsMouse + PropertyChanges { + target: delegateText + color: "white" + } + } + ] + transitions: [ + Transition { + from: "*" + to: "hovered" + ColorAnimation { + duration: 200 + } + }, + Transition { + from: "hovered" + to: "*" + ColorAnimation { + duration: 1000 + } + } + ] + + } + MouseArea { + id: cloudItemMouseArea + hoverEnabled: true + anchors.fill: parent + onClicked: tagCloud.tagClicked( modelData ) + } + + Behavior on scale { + NumberAnimation { easing: Easing.Linear; duration: 1000 } + } + } + } + } +} diff --git a/data/qml/tomahawkimports/ToggleViewButtons.qml b/data/qml/tomahawkimports/ToggleViewButtons.qml new file mode 100644 index 0000000000..066d5f4c9e --- /dev/null +++ b/data/qml/tomahawkimports/ToggleViewButtons.qml @@ -0,0 +1,34 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +Row { + id: root + width: repeater.width + + property alias model: repeater.model + property int currentIndex: 0 + + Repeater { + id: repeater + height: root.height + width: count * height + + + delegate: Image { + height: repeater.height + width: height + + source: "../../images/view-toggle-" + (index === root.currentIndex ? "active-" : "inactive-" ) + (index === 0 ? "left" : ( index === repeater.count - 1 ? "right" : "centre" )) + ".svg" + smooth: true + Image { + anchors.fill: parent + source: "../../images/" + modelData + (index === root.currentIndex ? "-active.svg" : "-inactive.svg") + } + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: root.currentIndex = index + } + } + } +} From d03cb674c977b0f771878b787a867e20c901eefd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:53:56 +0200 Subject: [PATCH 037/565] * Imported QML specific widgets / views / providers. --- .../dynamic/widgets/DynamicQmlWidget.cpp | 266 ++++++++++++++++++ .../dynamic/widgets/DynamicQmlWidget.h | 109 +++++++ .../widgets/DeclarativeCoverArtProvider.cpp | 142 ++++++++++ .../widgets/DeclarativeCoverArtProvider.h | 23 ++ src/libtomahawk/widgets/DeclarativeView.cpp | 58 ++++ src/libtomahawk/widgets/DeclarativeView.h | 61 ++++ 6 files changed, 659 insertions(+) create mode 100644 src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp create mode 100644 src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h create mode 100644 src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp create mode 100644 src/libtomahawk/widgets/DeclarativeCoverArtProvider.h create mode 100644 src/libtomahawk/widgets/DeclarativeView.cpp create mode 100644 src/libtomahawk/widgets/DeclarativeView.h diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp new file mode 100644 index 0000000000..c4b149df1d --- /dev/null +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -0,0 +1,266 @@ +#include "DynamicQmlWidget.h" + +#include "playlist/dynamic/DynamicModel.h" +#include "playlist/PlayableProxyModel.h" +#include "playlist/dynamic/DynamicModel.h" +#include "playlist/dynamic/echonest/EchonestControl.h" +#include "playlist/dynamic/GeneratorInterface.h" +#include "playlist/PlayableItem.h" +#include "Source.h" +#include "SourceList.h" +#include "audio/AudioEngine.h" +#include "database/Database.h" +#include "database/DatabaseCommand_PlaybackCharts.h" +#include "widgets/DeclarativeCoverArtProvider.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +#include +#include +#include +#include + +namespace Tomahawk +{ + +DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent ) + : DeclarativeView( parent ) + , m_playlist( playlist ) + , m_runningOnDemand( false ) + , m_activePlaylist( false ) + , m_playNextResolved( false ) +{ + m_model = new DynamicModel( this ); + + m_proxyModel = new PlayableProxyModel( this ); + m_proxyModel->setSourcePlayableModel( m_model ); + m_proxyModel->setShowOfflineResults( false ); + + m_model->loadPlaylist( m_playlist ); + + m_artistChartsModel = new PlayableModel( this ); + + + qmlRegisterUncreatableType("tomahawk", 1, 0, "Generator", "you cannot create it on your own - should be set in context"); + + rootContext()->setContextProperty( "dynamicModel", m_proxyModel ); + rootContext()->setContextProperty( "artistChartsModel", m_artistChartsModel ); + rootContext()->setContextProperty( "generator", m_playlist->generator().data() ); + rootContext()->setContextProperty( "currentlyPlayedIndex", QVariant::fromValue( 0 ) ); + + setSource( QUrl( "qrc" RESPATH "qml/StationView.qml" ) ); + + connect( m_model, SIGNAL( currentIndexChanged()), SLOT( currentIndexChanged() ) ); + connect( m_model, SIGNAL( loadingStarted() ), SIGNAL(loadingChanged() ) ); + connect( m_model, SIGNAL( loadingFinished() ), SIGNAL(loadingChanged() ) ); + connect( m_model, SIGNAL( changed() ), SIGNAL( titleChanged() ) ); + connect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); + connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( nextTrackGenerated( Tomahawk::query_ptr ) ) ); + connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); + connect( m_playlist->generator().data(), SIGNAL( error( QString, QString )), SLOT( error(QString,QString) ) ); + + connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); + connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) ); + + // m_playlist->generator()->generate( 20 ); + loadArtistCharts(); +} + + +DynamicQmlWidget::~DynamicQmlWidget() +{ +} + + +Tomahawk::playlistinterface_ptr +DynamicQmlWidget::playlistInterface() const +{ + return m_proxyModel->playlistInterface(); +} + + +QString +DynamicQmlWidget::title() const +{ + return m_model->title(); +} + + +QString +DynamicQmlWidget::description() const +{ + return m_model->description(); +} + + +QString +DynamicQmlWidget::iconSource() const +{ + return QLatin1String( RESPATH "images/station.png" ); +} + + +bool +DynamicQmlWidget::jumpToCurrentTrack() +{ + return true; +} + +playlist_ptr DynamicQmlWidget::playlist() const +{ + return m_model->playlist(); +} + +bool DynamicQmlWidget::loading() +{ + // Why does isLoading() not reset to true when cleared and station started again? +// return m_model->isLoading(); + return m_playNextResolved && m_proxyModel->rowCount() == 0; +} + +bool DynamicQmlWidget::configured() +{ + return !m_playlist->generator()->controls().isEmpty(); +} + +void DynamicQmlWidget::playItem(int index) +{ + tDebug() << "playItem called for cover" << index; + AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), m_proxyModel->itemFromIndex( index )->result() ); +} + +void DynamicQmlWidget::pause() +{ + AudioEngine::instance()->pause(); +} + +void DynamicQmlWidget::startStationFromArtist(const QString &artist) +{ + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromArtist(Artist::get(artist)); + emit loadingChanged(); + emit configuredChanged(); +} + +void DynamicQmlWidget::startStationFromGenre(const QString &genre) +{ + tDebug() << "should start startion from genre" << genre; + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromGenre( genre ); + emit loadingChanged(); + emit configuredChanged(); +} + +void DynamicQmlWidget::currentIndexChanged() +{ + tDebug() << "current index is" << m_model->currentItem().row(); + rootContext()->setContextProperty( "currentlyPlayedIndex", m_proxyModel->mapFromSource( m_model->currentItem() ).row() ); +} + +void +DynamicQmlWidget::tracksGenerated( const QList< query_ptr >& queries ) +{ + m_model->tracksGenerated( queries, queries.count() ); + m_playlist->resolve(); +} + +void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track) +{ + m_model->tracksGenerated( QList() << track ); + m_playlist->resolve(); + + connect( track.data(), SIGNAL( resolvingFinished( bool )), SLOT( resolvingFinished( bool ) ) ); + +} + +void DynamicQmlWidget::error(const QString &title, const QString &body) +{ + qDebug() << "got a generator error:" << title << body; + +// m_playlist->generator()->fetchNext(); + +} + +void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision) +{ + m_playlist->resolve(); +} + +void DynamicQmlWidget::resolvingFinished(bool hasResults) +{ + Q_UNUSED(hasResults) + qDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row(); + if( m_proxyModel->rowCount() <= m_proxyModel->currentIndex().row() + 8 ) { + qDebug() << "fetching next one"; + m_playlist->generator()->fetchNext(); + } + + if( m_playNextResolved && m_proxyModel->rowCount() > 0 ) { + playItem( 0 ); + m_playNextResolved = false; + } +} + +void DynamicQmlWidget::trackStarted() +{ + if ( m_activePlaylist && !m_playlist.isNull() && + m_playlist->mode() == OnDemand && !m_runningOnDemand ) + { + + startStation(); + } +} + +void +DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl ) +{ + if ( pl == m_proxyModel->playlistInterface() ) // same playlist + m_activePlaylist = true; + else + { + m_activePlaylist = false; + + // user started playing something somewhere else, so give it a rest + if ( m_runningOnDemand ) + { + stopStation( false ); + } + } +} + +void +DynamicQmlWidget::stopStation( bool stopPlaying ) +{ + m_model->stopOnDemand( stopPlaying ); + m_runningOnDemand = false; + +} + +void +DynamicQmlWidget::startStation() +{ + m_runningOnDemand = true; + m_model->startOnDemand(); +} + + +void +DynamicQmlWidget::loadArtistCharts() +{ + DatabaseCommand_PlaybackCharts* cmd = new DatabaseCommand_PlaybackCharts( SourceList::instance()->getLocal(), this ); + cmd->setLimit( 15 ); + connect( cmd, SIGNAL( artists( QList ) ), SLOT( onArtistCharts( QList< Tomahawk::artist_ptr > ) ), Qt::UniqueConnection ); + Database::instance()->enqueue( QSharedPointer( cmd ) ); +} + + +void +DynamicQmlWidget::onArtistCharts( const QList< Tomahawk::artist_ptr >& artists ) +{ + m_artistChartsModel->clear(); + m_artistChartsModel->appendArtists( artists ); + +} +} diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h new file mode 100644 index 0000000000..382bf314ac --- /dev/null +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -0,0 +1,109 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Michael Zanetti + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DYNAMIC_QML_WIDGET_H +#define DYNAMIC_QML_WIDGET_H + +#include "ViewPage.h" +#include "Typedefs.h" +#include "widgets/DeclarativeView.h" + +#include + +class PlayableModel; +class PlayableProxyModel; + +namespace Tomahawk +{ + +class DynamicModel; + +class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage +{ + Q_OBJECT + + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) + Q_PROPERTY(bool configured READ configured NOTIFY configuredChanged) + +public: + explicit DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent = 0 ); + virtual ~DynamicQmlWidget(); + + virtual QWidget* widget() { return this; } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const; + + virtual QString title() const; + virtual QString description() const; + virtual QString iconSource() const; + + virtual bool showInfoBar() const { return false; } + virtual bool showModes() const { return false; } + virtual bool showFilter() const { return false; } + + virtual bool jumpToCurrentTrack(); + + playlist_ptr playlist() const; + + bool loading(); + bool configured(); + +signals: + void loadingChanged(); + void configuredChanged(); + void titleChanged(); + +public slots: + void playItem(int index); + void pause(); + void startStationFromArtist(const QString &artist); + void startStationFromGenre(const QString &genre); + +private slots: + void currentIndexChanged(); + void tracksGenerated( const QList< Tomahawk::query_ptr>& queries ); + void nextTrackGenerated( const Tomahawk::query_ptr& track ); + void error( const QString& title, const QString& body); + + void onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ); + void playlistChanged( Tomahawk::playlistinterface_ptr pl ); + + void resolvingFinished( bool hasResults ); + + void trackStarted(); + void startStation(); + void stopStation( bool stopPlaying ); + + void loadArtistCharts(); + void onArtistCharts( const QList< Tomahawk::artist_ptr >& artists ); + +private: + DynamicModel* m_model; + PlayableProxyModel* m_proxyModel; + dynplaylist_ptr m_playlist; + + PlayableModel* m_artistChartsModel; + + bool m_runningOnDemand; + bool m_activePlaylist; + bool m_playNextResolved; +}; + +} + +#endif // DYNAMIC_QML_WIDGET_H diff --git a/src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp new file mode 100644 index 0000000000..49e743c861 --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.cpp @@ -0,0 +1,142 @@ +#include "DeclarativeCoverArtProvider.h" +#include "playlist/PlayableItem.h" +#include "playlist/PlayableProxyModel.h" +#include "Query.h" +#include "Album.h" +#include "Artist.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +#include +#include +#include +#include + +namespace Tomahawk +{ + +DeclarativeCoverArtProvider::DeclarativeCoverArtProvider( ) + : QDeclarativeImageProvider( QDeclarativeImageProvider::Pixmap ) +{ + +} + +DeclarativeCoverArtProvider::~DeclarativeCoverArtProvider() +{ +} + +QPixmap DeclarativeCoverArtProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +{ + // We always can generate it in the requested size + int width = requestedSize.width() > 0 ? requestedSize.width() : 230; + int height = requestedSize.height() > 0 ? requestedSize.height() : 230; + + if( size ) + *size = QSize( width, height ); + + QPixmap cover; + + tDebug() << "DeclarativeCoverArtprovider: Getting album art by id:" << id << requestedSize; + + bool mirrored = false; + bool labeled = false; + + QString coverId = id; + if(coverId.contains("-mirror")) { + coverId.remove("-mirror"); + mirrored = true; + } + if(coverId.contains("-labels")) { + coverId.remove("-labels"); + labeled = true; + } + + artist_ptr artist = Artist::getByCoverId( coverId ); + if ( !artist.isNull() ) + { + tDebug() << "Returning artist cover:" << artist->cover( *size ).isNull(); + cover = artist->cover( *size ); + if ( cover.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Returning default artist image"; + cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Original, *size ); + } + } + + if ( cover.isNull() ) + { + album_ptr album = Album::getByCoverId( coverId ); + if ( !album.isNull() ) + { + tDebug() << "Returning album cover:" << album->cover( *size ).isNull(); + cover = album->cover( *size ); + if ( cover.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Returning default album image"; + cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::Original, *size ); + } + } + } + + if ( cover.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Returning default track image"; + cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Original, *size ); + } + + QImage image(*size, QImage::Format_ARGB32); + + if(labeled) { + QImage coverImage(*size, QImage::Format_RGB32); + QPainter bgPainter(&coverImage); + bgPainter.drawPixmap(0, 0, size->width(), size->height(), cover); + + QColor c1; + c1.setRgb( 0, 0, 0 ); + c1.setAlphaF( 0.00 ); + QColor c2; + c2.setRgb( 0, 0, 0 ); + c2.setAlphaF( 0.88 ); + + QLinearGradient gradient( QPointF( 0, 0 ), QPointF( 0, 1 ) ); + gradient.setCoordinateMode( QGradient::ObjectBoundingMode ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.6, c2 ); + gradient.setColorAt( 1.0, c2 ); + + bgPainter.setPen( Qt::transparent ); + bgPainter.setBrush(QBrush(gradient)); + bgPainter.drawRect(0, size->height() * 0.7, size->width(), size->height() * 0.3); + cover = QPixmap::fromImage(coverImage); + } + + QPainter painter(&image); + if(!mirrored) { + image.fill(Qt::white); + painter.drawPixmap(0, 0, size->width(), size->height(), cover); + } else { + image.fill(QColor(0, 0, 0, 0)); + + // Lets paint half of the image in a fragment per line + int mirrorHeight = size->height() / 2; + int fragmentCount = mirrorHeight; + int fragmentHeight = mirrorHeight / fragmentCount; + + QPainter::PixmapFragment fragments[fragmentCount]; + + qreal fragmentOpacity = 0; + int fragmentStartY = size->height() - mirrorHeight; + for(int i = 0; i < fragmentCount; ++i) { + QPointF point = QPointF(size->width() / 2, fragmentStartY + (fragmentHeight / 2)); + QRectF sourceRect = QRectF(0, fragmentStartY, size->width(), fragmentHeight); + fragments[i] = QPainter::PixmapFragment::create(point, sourceRect, 1, 1, 0, fragmentOpacity); + fragmentOpacity += 0.5 / fragmentCount; + fragmentStartY += fragmentHeight; + } + painter.drawPixmapFragments(fragments, fragmentCount, cover); + } + + return QPixmap::fromImage(image); +} + +} diff --git a/src/libtomahawk/widgets/DeclarativeCoverArtProvider.h b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.h new file mode 100644 index 0000000000..3614070959 --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeCoverArtProvider.h @@ -0,0 +1,23 @@ +#ifndef DECLARATIVECOVERARTPROVIDER_H +#define DECLARATIVECOVERARTPROVIDER_H + + +#include "playlist/PlayableProxyModel.h" + +#include + + +namespace Tomahawk +{ + +class DeclarativeCoverArtProvider: public QDeclarativeImageProvider +{ +public: + DeclarativeCoverArtProvider(); + ~DeclarativeCoverArtProvider(); + + QPixmap requestPixmap( const QString &id, QSize *size, const QSize &requestedSize ); +}; + +} +#endif // DECLARATIVECOVERARTPROVIDER_H diff --git a/src/libtomahawk/widgets/DeclarativeView.cpp b/src/libtomahawk/widgets/DeclarativeView.cpp new file mode 100644 index 0000000000..9804b70aaf --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeView.cpp @@ -0,0 +1,58 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Michael Zanetti + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "DeclarativeView.h" +#include "playlist/PlayableItem.h" +#include "DeclarativeCoverArtProvider.h" +#include "utils/TomahawkUtilsGui.h" + +#include +#include +#include + +namespace Tomahawk +{ + +DeclarativeView::DeclarativeView( QWidget *parent ): + QDeclarativeView( parent ) +{ + + // Needed to make the QML contents scale with tomahawk + setResizeMode( QDeclarativeView::SizeRootObjectToView ); + + // This types seem to be needed everywhere anyways, lets the register here + qmlRegisterType( "tomahawk", 1, 0, "PlayableItem"); +// qmlRegisterType("tomahawk", 1, 0, "SearchField"); + + // QML image providers will be deleted by the view + engine()->addImageProvider( "albumart", new DeclarativeCoverArtProvider() ); + + // Register the view itself to make it easy to invoke the view's slots from QML + rootContext()->setContextProperty( "mainView", this ); + + rootContext()->setContextProperty( "defaultFontSize", TomahawkUtils::defaultFontSize() ); + rootContext()->setContextProperty( "defaultFontHeight", TomahawkUtils::defaultFontHeight() ); + +} + +DeclarativeView::~DeclarativeView() +{ + +} + +} diff --git a/src/libtomahawk/widgets/DeclarativeView.h b/src/libtomahawk/widgets/DeclarativeView.h new file mode 100644 index 0000000000..06e36e7903 --- /dev/null +++ b/src/libtomahawk/widgets/DeclarativeView.h @@ -0,0 +1,61 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Michael Zanetti + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DECLARATIVEVIEW_H +#define DECLARATIVEVIEW_H + +#include + +class QAbstractItemModel; + +/** + * @class This is the main class for Tomahawk's declarative views + * + * DeclarativeView inherits from QDeclarativeView and registers some + * common types, properties and functions used by all of Tomhawk's + * declarative views: + * + * Registered Types: + * - PlayableItem + * + * Set context properties: + * - mainView: This view, so you can invoke this view's slots from QML + * - defaultFontSize: system default font point size + * - defaultFontHeight: system default font pixel height + * + * It also registers an albumart image provider. You can access album art + * in QML with the source url "image://albumart/". + * The cover id can be obtained by the CoverIdRole in PlayableModels + * + * After subclassing this, all you have to do is call setSource() to + * load the QML file and optionally setModel(). + */ + +namespace Tomahawk +{ + +class DeclarativeView: public QDeclarativeView +{ + Q_OBJECT +public: + DeclarativeView(QWidget *parent = 0); + ~DeclarativeView(); +}; + +} +#endif From c47d52b144f9f2ab851536e33a505c1fcb9e4dae Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:54:42 +0200 Subject: [PATCH 038/565] * Added coverID() related methods to Artist, Album, Track. --- src/libtomahawk/Album.cpp | 29 +++++++++++++++++++++++++++++ src/libtomahawk/Album.h | 4 ++++ src/libtomahawk/Artist.cpp | 29 +++++++++++++++++++++++++++++ src/libtomahawk/Artist.h | 4 ++++ src/libtomahawk/Track.cpp | 17 ++++++++++++++++- src/libtomahawk/Track.h | 1 + 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/Album.cpp b/src/libtomahawk/Album.cpp index c182efb18b..d5a6d5faeb 100644 --- a/src/libtomahawk/Album.cpp +++ b/src/libtomahawk/Album.cpp @@ -37,6 +37,7 @@ using namespace Tomahawk; QHash< QString, album_wptr > Album::s_albumsByName = QHash< QString, album_wptr >(); QHash< unsigned int, album_wptr > Album::s_albumsById = QHash< unsigned int, album_wptr >(); +QHash< QString, album_ptr > Album::s_albumsByCoverId = QHash< QString, album_ptr >(); static QMutex s_nameCacheMutex; static QReadWriteLock s_idMutex; @@ -79,6 +80,7 @@ Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCr album->setWeakRef( album.toWeakRef() ); album->loadId( autoCreate ); s_albumsByName.insert( key, album ); + s_albumsByCoverId.insert( album->coverId(), album ); return album; } @@ -110,6 +112,7 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar album_ptr a = album_ptr( new Album( id, name, artist ), &Album::deleteLater ); a->setWeakRef( a.toWeakRef() ); s_albumsByName.insert( key, a ); + s_albumsByCoverId.insert( a->coverId(), a ); if ( id > 0 ) { @@ -122,6 +125,18 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar } +album_ptr +Album::getByCoverId( const QString& uuid ) +{ + QMutexLocker lock( &s_nameCacheMutex ); + + if ( s_albumsByCoverId.contains( uuid ) ) + return s_albumsByCoverId.value( uuid ); + + return album_ptr(); +} + + Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ) : QObject() , m_waitingForId( false ) @@ -327,6 +342,10 @@ Album::infoSystemInfo( const Tomahawk::InfoSystem::InfoRequestData& requestData, m_coverBuffer = ba; } + s_albumsByCoverId.remove( coverId() ); + m_coverId = uuid(); + s_albumsByCoverId.insert( m_coverId, m_ownRef.toStrongRef() ); + m_coverLoaded = true; emit coverChanged(); } @@ -383,3 +402,13 @@ Album::infoid() const return m_uuid; } + + +QString +Album::coverId() const +{ + if ( m_coverId.isEmpty() ) + m_coverId = uuid(); + + return m_coverId; +} diff --git a/src/libtomahawk/Album.h b/src/libtomahawk/Album.h index a137f6b9f1..dd164259d5 100644 --- a/src/libtomahawk/Album.h +++ b/src/libtomahawk/Album.h @@ -45,12 +45,14 @@ Q_OBJECT public: static album_ptr get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate = false ); static album_ptr get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ); + static album_ptr getByCoverId( const QString& uuid ); Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ); Album( const QString& name, const Tomahawk::artist_ptr& artist ); virtual ~Album(); unsigned int id() const; + QString coverId() const; QString name() const { return m_name; } QString sortname() const { return m_sortname; } @@ -98,6 +100,7 @@ private slots: mutable bool m_coverLoaded; mutable bool m_coverLoading; mutable QString m_uuid; + mutable QString m_coverId; mutable QByteArray m_coverBuffer; #ifndef ENABLE_HEADLESS @@ -110,6 +113,7 @@ private slots: static QHash< QString, album_wptr > s_albumsByName; static QHash< unsigned int, album_wptr > s_albumsById; + static QHash< QString, album_ptr > s_albumsByCoverId; friend class ::IdThreadWorker; }; diff --git a/src/libtomahawk/Artist.cpp b/src/libtomahawk/Artist.cpp index c2498ccf60..faf5622c8b 100644 --- a/src/libtomahawk/Artist.cpp +++ b/src/libtomahawk/Artist.cpp @@ -39,6 +39,7 @@ using namespace Tomahawk; QHash< QString, artist_wptr > Artist::s_artistsByName = QHash< QString, artist_wptr >(); QHash< unsigned int, artist_wptr > Artist::s_artistsById = QHash< unsigned int, artist_wptr >(); +QHash< QString, artist_ptr > Artist::s_artistsByCoverId = QHash< QString, artist_ptr >(); static QMutex s_nameCacheMutex; static QReadWriteLock s_idMutex; @@ -77,6 +78,7 @@ Artist::get( const QString& name, bool autoCreate ) artist->setWeakRef( artist.toWeakRef() ); artist->loadId( autoCreate ); s_artistsByName.insert( key, artist ); + s_artistsByCoverId.insert( artist->coverId(), artist ); return artist; } @@ -110,6 +112,7 @@ Artist::get( unsigned int id, const QString& name ) artist_ptr a = artist_ptr( new Artist( id, name ), &Artist::deleteLater ); a->setWeakRef( a.toWeakRef() ); s_artistsByName.insert( key, a ); + s_artistsByCoverId.insert( a->coverId(), a ); if ( id > 0 ) { @@ -122,6 +125,18 @@ Artist::get( unsigned int id, const QString& name ) } +artist_ptr +Artist::getByCoverId( const QString& uuid ) +{ + QMutexLocker lock( &s_nameCacheMutex ); + + if ( s_artistsByCoverId.contains( uuid ) ) + return s_artistsByCoverId.value( uuid ); + + return artist_ptr(); +} + + Artist::Artist( unsigned int id, const QString& name ) : QObject() , m_waitingForFuture( false ) @@ -481,6 +496,10 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari m_coverBuffer = ba; } + s_artistsByCoverId.remove( coverId() ); + m_coverId = uuid(); + s_artistsByCoverId.insert( m_coverId, m_ownRef.toStrongRef() ); + m_coverLoaded = true; emit coverChanged(); } @@ -642,3 +661,13 @@ Artist::infoid() const return m_uuid; } + + +QString +Artist::coverId() const +{ + if ( m_coverId.isEmpty() ) + m_coverId = uuid(); + + return m_coverId; +} diff --git a/src/libtomahawk/Artist.h b/src/libtomahawk/Artist.h index c9bd14fb75..1336893d8c 100644 --- a/src/libtomahawk/Artist.h +++ b/src/libtomahawk/Artist.h @@ -43,12 +43,14 @@ Q_OBJECT public: static artist_ptr get( const QString& name, bool autoCreate = false ); static artist_ptr get( unsigned int id, const QString& name ); + static artist_ptr getByCoverId( const QString& uuid ); Artist( unsigned int id, const QString& name ); explicit Artist( const QString& name ); virtual ~Artist(); unsigned int id() const; + QString coverId() const; QString name() const { return m_name; } QString sortname() const { return m_sortname; } @@ -117,6 +119,7 @@ private slots: bool m_biographyLoaded; mutable QString m_uuid; + mutable QString m_coverId; mutable int m_infoJobs; QList m_databaseAlbums; @@ -138,6 +141,7 @@ private slots: static QHash< QString, artist_wptr > s_artistsByName; static QHash< unsigned int, artist_wptr > s_artistsById; + static QHash< QString, artist_ptr > s_artistsByCoverId; friend class ::IdThreadWorker; }; diff --git a/src/libtomahawk/Track.cpp b/src/libtomahawk/Track.cpp index b4068afdd2..96d500020d 100644 --- a/src/libtomahawk/Track.cpp +++ b/src/libtomahawk/Track.cpp @@ -494,10 +494,25 @@ Track::coverLoaded() const return m_artistPtr->coverLoaded(); } - #endif +QString +Track::coverId() const +{ + if ( m_albumPtr && m_albumPtr->coverLoaded() && !m_albumPtr->cover( QSize( 0, 0 ) ).isNull() ) + { + return m_albumPtr->coverId(); + } + else if ( m_artistPtr ) + { + return m_artistPtr->coverId(); + } + + return QString(); +} + + QList Track::similarTracks() const { diff --git a/src/libtomahawk/Track.h b/src/libtomahawk/Track.h index 8d94f011ce..abc63119cf 100644 --- a/src/libtomahawk/Track.h +++ b/src/libtomahawk/Track.h @@ -84,6 +84,7 @@ friend class Pipeline; QPixmap cover( const QSize& size, bool forceLoad = true ) const; #endif bool coverLoaded() const; + QString coverId() const; void setLoved( bool loved, bool postToInfoSystem = true ); bool loved(); From 2841593a852f104cc6dde19ca6f5faec9e030734 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:55:09 +0200 Subject: [PATCH 039/565] * Added model specific roles and updated data methods. --- src/libtomahawk/playlist/PlayableModel.cpp | 42 ++++++++++++++++++- src/libtomahawk/playlist/PlayableModel.h | 19 +++++++++ .../playlist/PlayableProxyModel.cpp | 9 ++++ src/libtomahawk/playlist/PlayableProxyModel.h | 3 +- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 4ccb20b7a1..113a8067a5 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -44,6 +44,13 @@ PlayableModel::PlayableModel( QObject* parent, bool loading ) , m_readOnly( true ) , m_loading( loading ) { + QHash roleNames; + roleNames.insert( ArtistRole, "artistName" ); + roleNames.insert( TrackRole, "trackName" ); + roleNames.insert( CoverIDRole, "coverID" ); + roleNames.insert( IsPlayingRole, "isPlaying" ); + setRoleNames( roleNames ); + connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( onPlaybackStarted( Tomahawk::result_ptr ) ), Qt::DirectConnection ); connect( AudioEngine::instance(), SIGNAL( stopped() ), SLOT( onPlaybackStopped() ), Qt::DirectConnection ); @@ -147,6 +154,12 @@ PlayableModel::parent( const QModelIndex& child ) const QVariant PlayableModel::artistData( const artist_ptr& artist, int role ) const { + if ( role == CoverIDRole ) + { + artist->cover( QSize( 0, 0 ) ); + return artist->coverId(); + } + if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) return QVariant(); @@ -157,6 +170,12 @@ PlayableModel::artistData( const artist_ptr& artist, int role ) const QVariant PlayableModel::albumData( const album_ptr& album, int role ) const { + if ( role == CoverIDRole ) + { + album->cover( QSize( 0, 0 ) ); + return album->coverId(); + } + if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) return QVariant(); @@ -167,6 +186,13 @@ PlayableModel::albumData( const album_ptr& album, int role ) const QVariant PlayableModel::queryData( const query_ptr& query, int column, int role ) const { + if ( role == CoverIDRole ) + { + query->track()->cover( QSize( 0, 0 ) ); + return query->track()->coverId(); + } + + tDebug() << Q_FUNC_INFO << role; if ( role != Qt::DisplayRole ) // && role != Qt::ToolTipRole ) return QVariant(); @@ -287,6 +313,14 @@ PlayableModel::data( const QModelIndex& index, int role ) const if ( !entry ) return QVariant(); + int column = index.column(); + if ( role < CoverIDRole && role >= Qt::UserRole ) + { + // map role to column + column = role - Qt::UserRole; + role = Qt::DisplayRole; + } + switch ( role ) { case Qt::TextAlignmentRole: @@ -295,6 +329,12 @@ PlayableModel::data( const QModelIndex& index, int role ) const break; } + case IsPlayingRole: + { + return entry->isPlaying(); + break; + } + case PlayableProxyModel::TypeRole: { if ( entry->result() ) @@ -320,7 +360,7 @@ PlayableModel::data( const QModelIndex& index, int role ) const { if ( !entry->query().isNull() ) { - return queryData( entry->query(), index.column(), role ); + return queryData( entry->query(), column, role ); } else if ( !entry->artist().isNull() ) { diff --git a/src/libtomahawk/playlist/PlayableModel.h b/src/libtomahawk/playlist/PlayableModel.h index 0f228c9169..e5cc14d526 100644 --- a/src/libtomahawk/playlist/PlayableModel.h +++ b/src/libtomahawk/playlist/PlayableModel.h @@ -55,6 +55,25 @@ Q_OBJECT Name = 12 }; + enum PlayableRoles + { + ArtistRole = Qt::UserRole, + TrackRole, + ComposerRole, + AlbumRole, + AlbumPosRole, + DurationRole, + BitrateRole, + AgeRole, + YearRole, + FilesizeRole, + OriginRole, + ScoreRole, + NameRole, + CoverIDRole, + IsPlayingRole + }; + explicit PlayableModel( QObject* parent = 0, bool loading = true ); virtual ~PlayableModel(); diff --git a/src/libtomahawk/playlist/PlayableProxyModel.cpp b/src/libtomahawk/playlist/PlayableProxyModel.cpp index 29d877103b..821ada7e93 100644 --- a/src/libtomahawk/playlist/PlayableProxyModel.cpp +++ b/src/libtomahawk/playlist/PlayableProxyModel.cpp @@ -628,6 +628,15 @@ PlayableProxyModel::setFilter( const QString& pattern ) } +PlayableItem* +PlayableProxyModel::itemFromIndex( int itemIndex ) const +{ + // qDebug() << "returning item" << sourceModel()->itemFromIndex( itemIndex )->name(); + QModelIndex modelIndex = index( itemIndex, 0 ); + return sourceModel()->itemFromIndex( mapToSource( modelIndex ) ); +} + + void PlayableProxyModel::setCurrentIndex( const QModelIndex& index ) { diff --git a/src/libtomahawk/playlist/PlayableProxyModel.h b/src/libtomahawk/playlist/PlayableProxyModel.h index c3a0d7be07..b49cde20cd 100644 --- a/src/libtomahawk/playlist/PlayableProxyModel.h +++ b/src/libtomahawk/playlist/PlayableProxyModel.h @@ -36,7 +36,7 @@ Q_OBJECT { Detailed = 0, Short = 1, ShortWithAvatars = 2, Large = 3, Collection = 4 }; enum PlayableProxyModelRole - { StyleRole = Qt::UserRole + 1, TypeRole }; + { StyleRole = Qt::UserRole + 100, TypeRole }; explicit PlayableProxyModel ( QObject* parent = 0 ); virtual ~PlayableProxyModel() {} @@ -68,6 +68,7 @@ Q_OBJECT virtual int maxVisibleItems() const { return m_maxVisibleItems; } virtual void setMaxVisibleItems( int items ); + Q_INVOKABLE virtual PlayableItem* itemFromIndex( int itemIndex ) const; virtual PlayableItem* itemFromIndex( const QModelIndex& index ) const { return sourceModel()->itemFromIndex( index ); } virtual PlayableItem* itemFromQuery( const Tomahawk::query_ptr& query ) const { return sourceModel()->itemFromQuery( query ); } virtual PlayableItem* itemFromResult( const Tomahawk::result_ptr& result ) const { return sourceModel()->itemFromResult( result ); } From f3a50bedbd0c852a2ccbec409c7b0a07c009b4ea Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:56:28 +0200 Subject: [PATCH 040/565] * Stations shouldn't depend on widgets / controls. --- src/libtomahawk/GlobalActionManager.cpp | 12 +- ...baseCommand_LoadDynamicPlaylistEntries.cpp | 6 +- ...tabaseCommand_LoadDynamicPlaylistEntries.h | 4 +- ...baseCommand_SetDynamicPlaylistRevision.cpp | 74 +++-------- ...tabaseCommand_SetDynamicPlaylistRevision.h | 9 +- .../playlist/dynamic/DynamicPlaylist.cpp | 78 ++---------- .../playlist/dynamic/DynamicPlaylist.h | 28 ++--- .../dynamic/DynamicPlaylistRevision.cpp | 2 +- .../dynamic/DynamicPlaylistRevision.h | 2 +- .../playlist/dynamic/GeneratorFactory.cpp | 4 +- .../playlist/dynamic/GeneratorFactory.h | 4 +- .../playlist/dynamic/GeneratorInterface.cpp | 42 +------ .../playlist/dynamic/GeneratorInterface.h | 37 +++--- .../dynamic/database/DatabaseGenerator.h | 8 +- .../dynamic/echonest/EchonestGenerator.cpp | 118 +++++++++++++++--- .../dynamic/echonest/EchonestGenerator.h | 10 +- src/sourcetree/items/CategoryItems.cpp | 8 +- 17 files changed, 196 insertions(+), 250 deletions(-) diff --git a/src/libtomahawk/GlobalActionManager.cpp b/src/libtomahawk/GlobalActionManager.cpp index 38f8542a51..fcb074d148 100644 --- a/src/libtomahawk/GlobalActionManager.cpp +++ b/src/libtomahawk/GlobalActionManager.cpp @@ -225,7 +225,10 @@ GlobalActionManager::copyPlaylistToClipboard( const dynplaylist_ptr& playlist ) TomahawkUtils::urlAddQueryItem( link, "type", "echonest" ); TomahawkUtils::urlAddQueryItem( link, "title", playlist->title() ); - QList< dyncontrol_ptr > controls = playlist->generator()->controls(); + Q_ASSERT( false ); + //FIXME +/* + QVariantList controls = playlist->generator()->controls(); foreach ( const dyncontrol_ptr& c, controls ) { if ( c->selectedType() == "Artist" ) @@ -252,7 +255,7 @@ GlobalActionManager::copyPlaylistToClipboard( const dynplaylist_ptr& playlist ) TomahawkUtils::urlAddQueryItem( link, name, c->input() ); } - } + }*/ QClipboard* cb = QApplication::clipboard(); QByteArray data = percentEncode( link ); @@ -886,7 +889,8 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) return Tomahawk::dynplaylist_ptr(); } - if ( parts[ 0 ] == "create" ) + Q_ASSERT( false ); +/* if ( parts[ 0 ] == "create" ) { if ( !urlHasQueryItem( url, "title" ) || !urlHasQueryItem( url, "type" ) ) { @@ -1057,7 +1061,7 @@ GlobalActionManager::loadDynamicPlaylist( const QUrl& url, bool station ) ViewManager::instance()->show( pl ); return pl; - } + }*/ return Tomahawk::dynplaylist_ptr(); } diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp index c429f48464..5ad0dc4fb3 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp @@ -52,7 +52,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) QString type; GeneratorMode mode; - QList< QVariantMap > controls; + QVariantList controls; QString playlist_guid; // qDebug() << "Loading controls..." << revisionGuid(); // qDebug() << "SELECT playlist_revision.playlist, controls, plmode, pltype " @@ -71,7 +71,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) type = controlsQuery.value( 3 ).toString(); mode = static_cast( controlsQuery.value( 2 ).toInt() ); - QStringList controlIds = v.toStringList(); +/* QStringList controlIds = v.toStringList(); // qDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1); foreach( const QString& controlId, controlIds ) { @@ -91,7 +91,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) c[ "input" ] = controlQuery.value( 2 ).toString(); controls << c; } - } + }*/ } else { diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h index c15adc0b6e..b812cd63c3 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.h @@ -47,14 +47,14 @@ class DatabaseCommand_LoadDynamicPlaylistEntries : public DatabaseCommand_LoadPl void done( QString, bool, QString, - QList< QVariantMap>, + QVariantList, bool ); // used when loading a static playlist void done( QString, QList< QString >, QList< QString >, QString, - QList< QVariantMap>, + QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ); diff --git a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp index 659041dc17..3498d2c160 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp @@ -37,7 +37,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe const QList& entries, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ) + const QVariantList& controls ) : DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, orderedguids, addedentries, entries ) , m_type( type ) , m_mode( mode ) @@ -54,7 +54,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe const QString& oldrev, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ) + const QVariantList& controls ) : DatabaseCommand_SetPlaylistRevision( s, playlistguid, newrev, oldrev, QStringList(), QList< plentry_ptr >(), QList< plentry_ptr >() ) , m_type( type ) , m_mode( mode ) @@ -68,18 +68,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRe QVariantList DatabaseCommand_SetDynamicPlaylistRevision::controlsV() { - if ( m_controls.isEmpty() ) - return m_controlsV; - - if ( !m_controls.isEmpty() && m_controlsV.isEmpty() ) - { - foreach ( const dyncontrol_ptr& control, m_controls ) - { - m_controlsV << QJson::QObjectHelper::qobject2qvariant( control.data() ); - } - } - - return m_controlsV; + return m_controls; } @@ -117,7 +106,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook() return; } - if ( !m_controlsV.isEmpty() && m_controls.isEmpty() ) +/* if ( !m_controlsV.isEmpty() && m_controls.isEmpty() ) { QList controlMap; foreach( const QVariant& v, m_controlsV ) @@ -139,7 +128,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook() m_addedmap, m_applied ); } - else + else*/ { if ( m_mode == OnDemand ) rawPl->setRevision( newrev(), @@ -169,19 +158,9 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib ) DatabaseCommand_SetPlaylistRevision::exec( lib ); QVariantList newcontrols; - if ( m_controlsV.isEmpty() && !m_controls.isEmpty() ) + foreach( const QVariant& v, m_controls ) { - foreach( const dyncontrol_ptr& control, m_controls ) - { - newcontrols << control->id(); - } - } - else if( !m_controlsV.isEmpty() ) - { - foreach( const QVariant& v, m_controlsV ) - { - newcontrols << v.toMap().value( "id" ); - } + newcontrols << v.toMap().value( "id" ); } QJson::Serializer ser; @@ -209,34 +188,18 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib ) TomahawkSqlQuery controlsQuery = lib->newquery(); controlsQuery.prepare( "INSERT INTO dynamic_playlist_controls( id, playlist, selectedType, match, input ) " "VALUES( ?, ?, ?, ?, ? )" ); - if ( m_controlsV.isEmpty() && !m_controls.isEmpty() ) - { - foreach ( const dyncontrol_ptr& control, m_controls ) - { - qDebug() << "inserting dynamic control:" << control->id() << m_playlistguid << control->selectedType() << control->match() << control->input(); - controlsQuery.addBindValue( control->id() ); - controlsQuery.addBindValue( m_playlistguid ); - controlsQuery.addBindValue( control->selectedType() ); - controlsQuery.addBindValue( control->match() ); - controlsQuery.addBindValue( control->input() ); - - controlsQuery.exec(); - } - } - else + + foreach ( const QVariant& v, m_controls ) { - foreach ( const QVariant& v, m_controlsV ) - { - QVariantMap control = v.toMap(); - qDebug() << "inserting dynamic control from JSON:" << control.value( "id" ) << m_playlistguid << control.value( "selectedType" ) << control.value( "match" ) << control.value( "input" ); - controlsQuery.addBindValue( control.value( "id" ) ); - controlsQuery.addBindValue( m_playlistguid ); - controlsQuery.addBindValue( control.value( "selectedType" ) ); - controlsQuery.addBindValue( control.value( "match" ) ); - controlsQuery.addBindValue( control.value( "input" ) ); - - controlsQuery.exec(); - } + QVariantMap control = v.toMap(); + qDebug() << "inserting dynamic control from JSON:" << control.value( "id" ) << m_playlistguid << control.value( "selectedType" ) << control.value( "match" ) << control.value( "input" ); + controlsQuery.addBindValue( control.value( "id" ) ); + controlsQuery.addBindValue( m_playlistguid ); + controlsQuery.addBindValue( control.value( "selectedType" ) ); + controlsQuery.addBindValue( control.value( "match" ) ); + controlsQuery.addBindValue( control.value( "input" ) ); + + controlsQuery.exec(); } if ( m_applied ) @@ -252,6 +215,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib ) } } + void DatabaseCommand_SetDynamicPlaylistRevision::setPlaylist( DynamicPlaylist* pl ) { diff --git a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h index a23f39ddda..6a81d98b6a 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h +++ b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.h @@ -45,7 +45,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla const QList& entries, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ); + const QVariantList& controls ); explicit DatabaseCommand_SetDynamicPlaylistRevision( const source_ptr& s, const QString& playlistguid, @@ -53,7 +53,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla const QString& oldrev, const QString& type, GeneratorMode mode, - const QList< dyncontrol_ptr >& controls ); + const QVariantList& controls ); QString commandname() const { return "setdynamicplaylistrevision"; } @@ -64,7 +64,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla void setControlsV( const QVariantList& vlist ) { - m_controlsV = vlist; + m_controls = vlist; } QVariantList controlsV(); @@ -80,8 +80,7 @@ class DatabaseCommand_SetDynamicPlaylistRevision : public DatabaseCommand_SetPla private: QString m_type; GeneratorMode m_mode; - QList< dyncontrol_ptr > m_controls; - QList< QVariant > m_controlsV; + QVariantList m_controls; // ARG i hate sharedpointers sometimes DynamicPlaylist* m_playlist; // Only used if setting revision of a non-autoloaded playlist, as those aren't able to be looked up by guid diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 57f61dd6fb..c7269cd1d5 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -185,7 +185,7 @@ void DynamicPlaylist::createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, - const QList< dyncontrol_ptr>& controls, + const QVariantList& controls, const QList< plentry_ptr >& entries ) { Q_ASSERT( m_source->isLocal() || newrev == oldrev ); @@ -233,7 +233,7 @@ void DynamicPlaylist::createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, - const QList< dyncontrol_ptr>& controls ) + const QVariantList& controls ) { Q_ASSERT( m_source->isLocal() || newrev == oldrev ); @@ -275,19 +275,19 @@ DynamicPlaylist::loadRevision( const QString& rev ) connect( cmd, SIGNAL( done( QString, bool, QString, - QList< QVariantMap >, + QVariantList, bool ) ), SLOT( setRevision( QString, bool, QString, - QList< QVariantMap >, + QVariantList, bool) ) ); } else if ( m_generator->mode() == Static ) { connect( cmd, SIGNAL( done( QString, QList< QString >, QList< QString >, QString, - QList< QVariantMap >, + QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ) ), @@ -295,7 +295,7 @@ DynamicPlaylist::loadRevision( const QString& rev ) QList< QString >, QList< QString >, QString, - QList< QVariantMap >, + QVariantList, bool, QMap< QString, Tomahawk::plentry_ptr >, bool ) ) ); @@ -376,7 +376,7 @@ DynamicPlaylist::setRevision( const QString& rev, const QList< QString >& neworderedguids, const QList< QString >& oldorderedguids, const QString& type, - const QList< dyncontrol_ptr >& controls, + const QVariantList& controls, bool is_newest_rev, const QMap< QString, plentry_ptr >& addedmap, bool applied ) @@ -391,7 +391,7 @@ DynamicPlaylist::setRevision( const QString& rev, Q_ARG( QList , neworderedguids ), Q_ARG( QList , oldorderedguids ), Q_ARG( QString , type ), - QGenericArgument( "QList< Tomahawk::dyncontrol_ptr > " , (const void*)&controls ), + Q_ARG( QVariantList , controls ), Q_ARG( bool, is_newest_rev ), QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ), Q_ARG( bool, applied ) ); @@ -421,42 +421,11 @@ DynamicPlaylist::setRevision( const QString& rev, } -void -DynamicPlaylist::setRevision( const QString& rev, - const QList< QString >& neworderedguids, - const QList< QString >& oldorderedguids, - const QString& type, - const QList< QVariantMap>& controlsV, - bool is_newest_rev, - const QMap< QString, Tomahawk::plentry_ptr >& addedmap, - bool applied ) -{ - if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, - "setRevision", - Qt::BlockingQueuedConnection, - Q_ARG( QString, rev ), - Q_ARG( QList , neworderedguids ), - Q_ARG( QList , oldorderedguids ), - Q_ARG( QString , type ), - QGenericArgument( "QList< QVariantMap > " , (const void*)&controlsV ), - Q_ARG( bool, is_newest_rev ), - QGenericArgument( "QMap< QString,Tomahawk::plentry_ptr > " , (const void*)&addedmap ), - Q_ARG( bool, applied ) ); - return; - } - - QList controls = variantsToControl( controlsV ); - setRevision( rev, neworderedguids, oldorderedguids, type, controls, is_newest_rev, addedmap, applied ); -} - - void DynamicPlaylist::setRevision( const QString& rev, bool is_newest_rev, const QString& type, - const QList< dyncontrol_ptr >& controls, + const QVariantList& controls, bool applied ) { if ( QThread::currentThread() != thread() ) @@ -498,32 +467,7 @@ DynamicPlaylist::setRevision( const QString& rev, } -void -DynamicPlaylist::setRevision( const QString& rev, - bool is_newest_rev, - const QString& type, - const QList< QVariantMap >& controlsV, - bool applied ) -{ - if ( QThread::currentThread() != thread() ) - { - QMetaObject::invokeMethod( this, - "setRevision", - Qt::BlockingQueuedConnection, - Q_ARG( QString, rev ), - Q_ARG( bool, is_newest_rev ), - Q_ARG( QString, type ), - QGenericArgument( "QList< QVariantMap >" , (const void*)&controlsV ), - Q_ARG( bool, applied ) ); - return; - } - - QList controls = variantsToControl( controlsV ); - setRevision( rev, is_newest_rev, type, controls, applied ); -} - - -QList< dyncontrol_ptr > +/*QList< dyncontrol_ptr > DynamicPlaylist::variantsToControl( const QList< QVariantMap >& controlsV ) { QList realControls; @@ -534,7 +478,7 @@ DynamicPlaylist::variantsToControl( const QList< QVariantMap >& controlsV ) realControls << control; } return realControls; -} +}*/ void diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h index 2a86bbb022..d66d139477 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h @@ -49,10 +49,10 @@ class DatabaseCommand_LoadDynamicPlaylist; struct DynQueueItem : RevisionQueueItem { QString type; - QList controls; + QVariantList controls; int mode; - DynQueueItem( const QString& nRev, const QString& oRev, const QString& typ, const QList< dyncontrol_ptr >& ctrls, int m, const QList< plentry_ptr >& e, bool latest ) : + DynQueueItem( const QString& nRev, const QString& oRev, const QString& typ, const QVariantList& ctrls, int m, const QList< plentry_ptr >& e, bool latest ) : RevisionQueueItem( nRev, oRev, e, latest ), type( typ ), controls( ctrls ), mode( m ) {} }; @@ -125,9 +125,9 @@ public slots: // want to update the playlist from the model? // generate a newrev using uuid() and call this: // if this is a static playlist, pass it a new list of entries. implicitly sets mode to static - void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls, const QList< plentry_ptr >& entries ); + void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QVariantList& controls, const QList< plentry_ptr >& entries ); // if it is ondemand, no entries are needed implicitly sets mode to ondemand - void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QList< dyncontrol_ptr>& controls ); + void createNewRevision( const QString& newrev, const QString& oldrev, const QString& type, const QVariantList& controls ); void reportCreated( const Tomahawk::dynplaylist_ptr& self ); void reportDeleted( const Tomahawk::dynplaylist_ptr& self ); @@ -142,15 +142,7 @@ public slots: const QList& neworderedguids, const QList& oldorderedguids, const QString& type, - const QList< QVariantMap >& controls, - bool is_newest_rev, - const QMap< QString, Tomahawk::plentry_ptr >& addedmap, - bool applied ); - void setRevision( const QString& rev, - const QList& neworderedguids, - const QList& oldorderedguids, - const QString& type, - const QList< Tomahawk::dyncontrol_ptr >& controls, + const QVariantList& controls, bool is_newest_rev, const QMap< QString, Tomahawk::plentry_ptr >& addedmap, bool applied ); @@ -158,13 +150,9 @@ public slots: void setRevision( const QString& rev, bool is_newest_rev, const QString& type, - const QList< QVariantMap>& controls, - bool applied ); - void setRevision( const QString& rev, - bool is_newest_rev, - const QString& type, - const QList< Tomahawk::dyncontrol_ptr>& controls, + const QVariantList& controls, bool applied ); + private: // called from loadAllPlaylists DB cmd via databasecollection (in GUI thread) explicit DynamicPlaylist( const source_ptr& src, @@ -192,8 +180,6 @@ public slots: void checkRevisionQueue(); - QList< dyncontrol_ptr > variantsToControl( const QList< QVariantMap >& controlsV ); - geninterface_ptr m_generator; bool m_autoLoad; diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp index d14737b07f..ea7048eb30 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.cpp @@ -23,7 +23,7 @@ using namespace Tomahawk; -DynamicPlaylistRevision::DynamicPlaylistRevision(const PlaylistRevision &other) +DynamicPlaylistRevision::DynamicPlaylistRevision( const PlaylistRevision &other ) { revisionguid = other.revisionguid; oldrevisionguid = other.oldrevisionguid; diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h index 1001da59fa..96af9273b9 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylistRevision.h @@ -29,7 +29,7 @@ struct DLLEXPORT DynamicPlaylistRevision : PlaylistRevision { public: - QList< dyncontrol_ptr > controls; + QVariantList controls; Tomahawk::GeneratorMode mode; QString type; diff --git a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp index 75838998e0..d5ed4effcd 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorFactory.cpp @@ -40,14 +40,14 @@ GeneratorFactory::create ( const QString& type ) } -dyncontrol_ptr +/*dyncontrol_ptr GeneratorFactory::createControl( const QString& generatorType, const QString& controlType ) { if( generatorType.isEmpty() || !s_factories.contains( generatorType ) ) return dyncontrol_ptr(); return s_factories.value( generatorType )->createControl( controlType ); -} +}*/ void diff --git a/src/libtomahawk/playlist/dynamic/GeneratorFactory.h b/src/libtomahawk/playlist/dynamic/GeneratorFactory.h index 3cbb309688..421c39e7af 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorFactory.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorFactory.h @@ -46,7 +46,7 @@ class DLLEXPORT GeneratorFactoryInterface * Create a control for this generator, not tied to this generator itself. Used when loading dynamic * playlists from a dbcmd. */ - virtual dyncontrol_ptr createControl( const QString& controlType = QString() ) = 0; +// virtual dyncontrol_ptr createControl( const QString& controlType = QString() ) = 0; virtual QStringList typeSelectors() const = 0; }; @@ -59,7 +59,7 @@ class DLLEXPORT GeneratorFactory public: static geninterface_ptr create( const QString& type ); // only used when loading from dbcmd - static dyncontrol_ptr createControl( const QString& generatorType, const QString& controlType = QString() ); +// static dyncontrol_ptr createControl( const QString& generatorType, const QString& controlType = QString() ); static void registerFactory( const QString& type, GeneratorFactoryInterface* interface ); static QStringList types(); diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp index 9cfb12c76f..13805b0c2d 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.cpp @@ -34,17 +34,6 @@ Tomahawk::GeneratorInterface::~GeneratorInterface() } -QList< Tomahawk::dyncontrol_ptr > -Tomahawk::GeneratorInterface::controls() -{ -// if( m_controls.isEmpty() ) { // return a default control (so the user can add more) -// return QList< Tomahawk::dyncontrol_ptr >() << createControl(); -// } - - return m_controls; -} - - QPixmap Tomahawk::GeneratorInterface::logo() { @@ -52,38 +41,15 @@ Tomahawk::GeneratorInterface::logo() } -void -Tomahawk::GeneratorInterface::addControl( const Tomahawk::dyncontrol_ptr& control ) -{ - m_controls << control; -} - - -void -Tomahawk::GeneratorInterface::clearControls() +QVariantList +Tomahawk::GeneratorInterface::controls() const { - m_controls.clear(); + return m_controls; } void -Tomahawk::GeneratorInterface::setControls( const QList< Tomahawk::dyncontrol_ptr >& controls ) +Tomahawk::GeneratorInterface::setControls( const QVariantList& controls ) { m_controls = controls; } - - -void -Tomahawk::GeneratorInterface::removeControl( const Tomahawk::dyncontrol_ptr& control ) -{ - m_controls.removeAll( control ); -} - - -Tomahawk::dyncontrol_ptr -Tomahawk::GeneratorInterface::createControl( const QString& type ) -{ - Q_UNUSED( type ); - Q_ASSERT( false ); - return dyncontrol_ptr(); -} diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h index fd0746a5b9..3a6d7fbdfc 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h @@ -47,6 +47,7 @@ class DLLEXPORT GeneratorInterface : public QObject { Q_OBJECT Q_PROPERTY( QString type READ type ) + Q_PROPERTY( QString summary READ sentenceSummary) /// oh qjson. Q_PROPERTY( int mode READ mode WRITE setMode ) @@ -55,12 +56,6 @@ class DLLEXPORT GeneratorInterface : public QObject explicit GeneratorInterface( QObject* parent = 0 ); virtual ~GeneratorInterface(); - // Can't make it pure otherwise we can't shove it in QVariants :-/ - // empty QString means use default - /// The generator will keep track of all the controls it creates. No need to tell it about controls - /// you ask it to create - virtual dyncontrol_ptr createControl( const QString& type = QString() ); - /// A logo to display for this generator, if it has one virtual QPixmap logo(); @@ -98,17 +93,17 @@ class DLLEXPORT GeneratorInterface : public QObject */ virtual bool onDemandSteerable() const { return false; } + /** - * Returns a widget used to steer the OnDemand dynamic playlist. - * If this generator doesn't support this (and returns false for - * \c onDemandSteerable) this will be null. The generator is responsible - * for reacting to changes in the widget. - * - * Steering widgets may emit a \c steeringChanged() signal, which will cause the model to toss any - * upcoming tracks and re-fetch them. - * + * Returns the controls for this station. */ - virtual QWidget* steeringWidget() { return 0; } + virtual QVariantList controls() const; + + /** + * Sets the controls (for example when loaded from database) + */ + virtual void setControls( const QVariantList& controls ); + /// The type of this generator QString type() const { return m_type; } @@ -116,22 +111,20 @@ class DLLEXPORT GeneratorInterface : public QObject int mode() const { return (int)m_mode; } void setMode( int mode ) { m_mode = (GeneratorMode)mode; } - // control functions - QList< dyncontrol_ptr > controls(); - void addControl( const dyncontrol_ptr& control ); - void clearControls(); - void setControls( const QList< dyncontrol_ptr>& controls ); - void removeControl( const dyncontrol_ptr& control ); + virtual bool startFromTrack( const Tomahawk::query_ptr& query ) = 0; + virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ) = 0; + virtual bool startFromGenre( const QString& genre ) = 0; signals: void error( const QString& title, const QString& body); void generated( const QList< Tomahawk::query_ptr>& queries ); void nextTrackGenerated( const Tomahawk::query_ptr& track ); + void summaryChanged(); protected: QString m_type; GeneratorMode m_mode; - QList< dyncontrol_ptr > m_controls; + QVariantList m_controls; }; typedef QSharedPointer geninterface_ptr; diff --git a/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h b/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h index 7513763dc8..ce991026b6 100644 --- a/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h +++ b/src/libtomahawk/playlist/dynamic/database/DatabaseGenerator.h @@ -36,10 +36,10 @@ namespace Tomahawk DatabaseFactory() {} virtual GeneratorInterface* create(); - virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); +// virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); // TO create a special SQL resolver that consists of a pre-baked SQL query and a description of it - virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary ); +// virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary ); virtual QStringList typeSelectors() const; }; @@ -55,8 +55,8 @@ namespace Tomahawk explicit DatabaseGenerator( QObject* parent = 0 ); virtual ~DatabaseGenerator(); - virtual dyncontrol_ptr createControl( const QString& type = QString() ); - virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary ); +/* virtual dyncontrol_ptr createControl( const QString& type = QString() ); + virtual dyncontrol_ptr createControl( const QString& sql, DatabaseCommand_GenericSelect::QueryType type, const QString& summary );*/ virtual QPixmap logo(); virtual void generate ( int number = -1 ); diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index fdaf4df40f..9edac4ccc8 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -61,11 +61,11 @@ EchonestFactory::create() } -dyncontrol_ptr +/*dyncontrol_ptr EchonestFactory::createControl( const QString& controlType ) { return dyncontrol_ptr( new EchonestControl( controlType, typeSelectors() ) ); -} +}*/ QStringList @@ -160,12 +160,12 @@ EchonestGenerator::setupCatalogs() // qDebug() << "ECHONEST:" << m_logo.size(); } -dyncontrol_ptr +/*dyncontrol_ptr EchonestGenerator::createControl( const QString& type ) { m_controls << dyncontrol_ptr( new EchonestControl( type, GeneratorFactory::typeSelectors( m_type ) ) ); return m_controls.last(); -} +}*/ QPixmap EchonestGenerator::logo() @@ -176,11 +176,11 @@ QPixmap EchonestGenerator::logo() void EchonestGenerator::knownCatalogsChanged() { - // Refresh all contrls +/* // Refresh all contrls foreach( const dyncontrol_ptr& control, m_controls ) { control.staticCast< EchonestControl >()->updateWidgetsFromData(); - } + }*/ } @@ -188,7 +188,7 @@ void EchonestGenerator::generate( int number ) { // convert to an echonest query, and fire it off - qDebug() << Q_FUNC_INFO; +/* qDebug() << Q_FUNC_INFO; qDebug() << "Generating playlist with" << m_controls.size(); foreach( const dyncontrol_ptr& ctrl, m_controls ) qDebug() << ctrl->selectedType() << ctrl->match() << ctrl->input(); @@ -202,7 +202,16 @@ EchonestGenerator::generate( int number ) } catch( std::runtime_error& e ) { qWarning() << "Got invalid controls!" << e.what(); emit error( "Filters are not valid", e.what() ); - } + }*/ + + QList< query_ptr > queries; + queries << Query::get("Colour Haze", "All", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Sun", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Zen", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Outside", QString(), uuid(), true); + queries << Query::get("Colour Haze", "Dirt", QString(), uuid(), true); + + emit generated( queries ); } @@ -226,6 +235,81 @@ EchonestGenerator::startOnDemand() } +bool +EchonestGenerator::startFromTrack( const Tomahawk::query_ptr& query ) +{ + tDebug() << "Generating station content by query:" << query->toString(); + + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::SongId; + data.second = query->track()->artist() + " " + query->track()->track(); + + Echonest::DynamicPlaylist::PlaylistParams params; + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); + params << data; + + // FIXME! + + return true; +} + + +bool +EchonestGenerator::startFromArtist( const Tomahawk::artist_ptr& artist ) +{ + tDebug() << "Generating station content by artist:" << artist->name(); + + if ( !m_dynPlaylist->sessionId().isNull() ) + { + // Running session, delete it + QNetworkReply* deleteReply = m_dynPlaylist->deleteSession(); + connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) ); + } + + connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doStartOnDemand( Echonest::DynamicPlaylist::PlaylistParams ) ) ); + + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::Artist; + data.second = artist->name(); + + Echonest::DynamicPlaylist::PlaylistParams params; + params << data; + // params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); + emit paramsGenerated( params ); + + return true; +} + + +bool +EchonestGenerator::startFromGenre( const QString& genre ) +{ + tDebug() << "Generating station content by genre:" << genre; + + if ( !m_dynPlaylist->sessionId().isNull() ) + { + // Running session, delete it + QNetworkReply* deleteReply = m_dynPlaylist->deleteSession(); + connect( deleteReply, SIGNAL( finished() ), deleteReply, SLOT( deleteLater() ) ); + } + + connect( this, SIGNAL( paramsGenerated( Echonest::DynamicPlaylist::PlaylistParams ) ), this, SLOT( doGenerate( Echonest::DynamicPlaylist::PlaylistParams ) ) ); + + setProperty( "number", 20 ); + + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::Description; + data.second = genre; + + Echonest::DynamicPlaylist::PlaylistParams params; + params << data; + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); + emit paramsGenerated( params ); + + return true; +} + + void EchonestGenerator::doGenerate( const Echonest::DynamicPlaylist::PlaylistParams& paramsIn ) { @@ -308,7 +392,7 @@ void EchonestGenerator::getParams() throw( std::runtime_error ) { Echonest::DynamicPlaylist::PlaylistParams params; - foreach( const dyncontrol_ptr& control, m_controls ) { +/* foreach( const dyncontrol_ptr& control, m_controls ) { params.append( control.dynamicCast()->toENParam() ); } @@ -343,7 +427,7 @@ EchonestGenerator::getParams() throw( std::runtime_error ) } else { emit paramsGenerated( params ); - } + }*/ } @@ -440,7 +524,7 @@ EchonestGenerator::userCatalogs() return s_catalogs->catalogs().keys(); } -bool +/*bool EchonestGenerator::onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error ) { bool only = true; @@ -460,7 +544,7 @@ EchonestGenerator::onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum } return false; -} +}*/ Echonest::DynamicPlaylist::ArtistTypeEnum @@ -477,7 +561,7 @@ EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& p /// 3. artist-description: If all the artist entries are Description. If some were but not all, error out. /// 4. artist-radio: If all the artist entries are Similar To. If some were but not all, error out. /// 5. song-radio: If all the artist entries are Similar To. If some were but not all, error out. - bool someCatalog = false; +/* bool someCatalog = false; bool genreType = false; foreach( const dyncontrol_ptr& control, m_controls ) { if ( control->selectedType() == "User Radio" ) @@ -498,7 +582,7 @@ EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& p else if( onlyThisArtistType( Echonest::DynamicPlaylist::SongRadioType ) ) params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); else // no artist or song or description types. default to artist-description - params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) );*/ return static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( params.last().second.toInt() ); } @@ -529,7 +613,7 @@ EchonestGenerator::sentenceSummary() * NOTE / TODO: In order for the sentence to be grammatically correct, we must follow the EN API rules. That means we can't have multiple of some types of filters, * and all Artist types must be the same. The filters aren't checked at the moment until Generate / Play is pressed. Consider doing a check on hide as well. */ - QList< dyncontrol_ptr > allcontrols = m_controls; +/* QList< dyncontrol_ptr > allcontrols = m_controls; QString sentence = "Songs "; /// 1. Collect all required filters @@ -612,7 +696,9 @@ EchonestGenerator::sentenceSummary() sentence += "and " + sorting.dynamicCast< EchonestControl >()->summary() + "."; } - return sentence; + return sentence;*/ + + return "This is a station!"; } void diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h index 5f8777abb0..4375a5153b 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h @@ -60,7 +60,7 @@ class DLLEXPORT EchonestFactory : public GeneratorFactoryInterface EchonestFactory(); virtual GeneratorInterface* create(); - virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); +// virtual dyncontrol_ptr createControl( const QString& controlType = QString() ); virtual QStringList typeSelectors() const; }; @@ -71,7 +71,7 @@ class DLLEXPORT EchonestGenerator : public GeneratorInterface explicit EchonestGenerator( QObject* parent = 0 ); virtual ~EchonestGenerator(); - virtual dyncontrol_ptr createControl( const QString& type = QString() ); +// virtual dyncontrol_ptr createControl( const QString& type = QString() ); virtual QPixmap logo(); virtual void generate ( int number = -1 ); virtual void startOnDemand(); @@ -80,6 +80,10 @@ class DLLEXPORT EchonestGenerator : public GeneratorInterface virtual bool onDemandSteerable() const { return false; } virtual QWidget* steeringWidget() { return 0; } + virtual bool startFromTrack( const Tomahawk::query_ptr& query ); + virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ); + virtual bool startFromGenre( const QString& genre ); + static QStringList styles(); static QStringList moods(); static QStringList genres(); @@ -118,7 +122,7 @@ private slots: query_ptr queryFromSong( const Echonest::Song& song ); Echonest::DynamicPlaylist::ArtistTypeEnum appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error ); - bool onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error ); +// bool onlyThisArtistType( Echonest::DynamicPlaylist::ArtistTypeEnum type ) const throw( std::runtime_error ); void loadStylesMoodsAndGenres(); diff --git a/src/sourcetree/items/CategoryItems.cpp b/src/sourcetree/items/CategoryItems.cpp index 3696baa263..ce505f107a 100644 --- a/src/sourcetree/items/CategoryItems.cpp +++ b/src/sourcetree/items/CategoryItems.cpp @@ -163,7 +163,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) QString firstArtist; // now we want to add each artist as a filter... - QList< dyncontrol_ptr > contrls; +/* QList< dyncontrol_ptr > contrls; while ( !stream.atEnd() ) { QString artist; @@ -183,7 +183,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction ) QString name = firstArtist.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstArtist ); newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls ); newpl->setProperty( "newname", name ); - connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) ); + connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );*/ ViewManager::instance()->show( newpl ); return true; @@ -295,7 +295,7 @@ CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks ) newpl->setMode( OnDemand ); // now we want to add each query as a song or similar artist filter... - QList< dyncontrol_ptr > controls; +/* QList< dyncontrol_ptr > controls; foreach ( const Tomahawk::query_ptr& q, tracks ) { dyncontrol_ptr c = newpl->generator()->createControl( "Song" ); @@ -303,7 +303,7 @@ CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks ) controls << c; } - newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), controls ); + newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), controls );*/ ViewManager::instance()->show( newpl ); connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) ); From 99ef263ba2677c54f47148069cc6e2528929b116 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 22:56:51 +0200 Subject: [PATCH 041/565] * Use DynamicQmlWidget for stations. --- CMakeLists.txt | 1 + TomahawkUse.cmake | 6 +++--- resources.qrc | 28 ++++++++++++++++++++++++++++ src/TomahawkApp.cpp | 6 +++--- src/libtomahawk/CMakeLists.txt | 17 +++++++++++------ src/libtomahawk/ViewManager.cpp | 11 ++++++----- src/libtomahawk/ViewManager.h | 4 ++-- 7 files changed, 54 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0672c18d1b..c7d7967021 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ if(NOT Qt5Core_DIR) set(QT_USE_QTNETWORK TRUE) set(QT_USE_QTXML TRUE) set(QT_USE_QTWEBKIT TRUE) + set(QT_USE_QTDECLARATIVE TRUE) include( ${QT_USE_FILE} ) endmacro() diff --git a/TomahawkUse.cmake b/TomahawkUse.cmake index 503ba8d7d6..8b465457d3 100644 --- a/TomahawkUse.cmake +++ b/TomahawkUse.cmake @@ -1,11 +1,11 @@ #FIXME: this only handles qt4 and duplicates top level cmakelists: how can we reduce code duplication? -find_package(Qt4 COMPONENTS QtNetwork QtCore QtGui QtSql REQUIRED) +find_package(Qt4 COMPONENTS QtNetwork QtCore QtGui QtSql QtDeclarative REQUIRED) include( ${QT_USE_FILE} ) -set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork") +set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork" "QtDeclarative") if(BUILD_GUI) - list(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" "QtSvg") + list(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" "QtSvg" "QtDeclarative") endif() find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS}) diff --git a/resources.qrc b/resources.qrc index 2b69fa96b0..5c0708890e 100644 --- a/resources.qrc +++ b/resources.qrc @@ -90,6 +90,28 @@ data/images/star-unstarred.svg data/images/apply-check.svg data/stylesheets/topbar-radiobuttons.css + data/qml/tomahawkimports/CoverImage.qml + data/qml/tomahawkimports/ArtistView.qml + data/qml/tomahawkimports/HeaderLabel.qml + data/qml/tomahawkimports/TagCloud.qml + data/qml/tomahawkimports/ScrollBar.qml + data/qml/tomahawkimports/InputField.qml + data/qml/tomahawkimports/Button.qml + data/qml/tomahawkimports/DoubleSlider.qml + data/qml/tomahawkimports/RoundedButton.qml + data/qml/tomahawkimports/PushButton.qml + data/qml/tomahawkimports/CoverFlip.qml + data/qml/tomahawkimports/BusyIndicator.qml + data/qml/StationView.qml + data/qml/stations/StationCreatorPage1.qml + data/qml/stations/StationCreatorPage2.qml + data/qml/stations/CreateByArtist.qml + data/qml/stations/StationConfig.qml + data/qml/QmlGridView.qml + data/qml/stations/CreateByGenre.qml + data/qml/tomahawkimports/FlexibleHeader.qml + data/qml/tomahawkimports/ToggleViewButtons.qml + data/qml/DeclarativeHeader.qml data/icons/tomahawk-icon-16x16.png data/icons/tomahawk-icon-32x32.png data/icons/tomahawk-icon-64x64.png @@ -152,5 +174,11 @@ data/images/refresh.svg data/images/inbox.svg data/images/new-inbox.svg + data/images/inputfield-border.svg + data/images/search-box-dismiss-x.svg + data/images/loading-animation.svg + data/images/station-artist.svg + data/images/station-genre.svg + data/images/station-year.svg diff --git a/src/TomahawkApp.cpp b/src/TomahawkApp.cpp index b623924e32..8bee7cfc79 100644 --- a/src/TomahawkApp.cpp +++ b/src/TomahawkApp.cpp @@ -39,7 +39,7 @@ #include "database/DatabaseResolver.h" #include "playlist/dynamic/GeneratorFactory.h" #include "playlist/dynamic/echonest/EchonestGenerator.h" -#include "playlist/dynamic/database/DatabaseGenerator.h" +//#include "playlist/dynamic/database/DatabaseGenerator.h" #include "playlist/XspfUpdater.h" #include "network/Servent.h" #include "web/Api_v1.h" @@ -219,8 +219,8 @@ TomahawkApp::init() tDebug() << "Init Echonest Factory."; GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); #endif - tDebug() << "Init Database Factory."; - GeneratorFactory::registerFactory( "database", new DatabaseFactory ); +/* tDebug() << "Init Database Factory."; + GeneratorFactory::registerFactory( "database", new DatabaseFactory );*/ // Register shortcut handler for this platform #ifdef Q_WS_MAC diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index e02f9f5039..6eb4f0b111 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -77,19 +77,21 @@ set( libGuiSources playlist/PlayableItem.cpp playlist/SingleTrackPlaylistInterface.cpp + playlist/dynamic/GeneratorInterface.cpp playlist/dynamic/DynamicPlaylist.cpp playlist/dynamic/DynamicView.cpp playlist/dynamic/DynamicModel.cpp playlist/dynamic/echonest/EchonestGenerator.cpp playlist/dynamic/echonest/EchonestControl.cpp - playlist/dynamic/echonest/EchonestSteerer.cpp - playlist/dynamic/widgets/DynamicWidget.cpp +# playlist/dynamic/echonest/EchonestSteerer.cpp +# playlist/dynamic/widgets/DynamicWidget.cpp + playlist/dynamic/widgets/DynamicQmlWidget.cpp playlist/dynamic/widgets/DynamicControlWrapper.cpp - playlist/dynamic/widgets/DynamicControlList.cpp +# playlist/dynamic/widgets/DynamicControlList.cpp playlist/dynamic/widgets/ReadOrWriteWidget.cpp playlist/dynamic/widgets/MiscControlWidgets.cpp - playlist/dynamic/widgets/CollapsibleControls.cpp - playlist/dynamic/widgets/DynamicSetupWidget.cpp +# playlist/dynamic/widgets/CollapsibleControls.cpp +# playlist/dynamic/widgets/DynamicSetupWidget.cpp resolvers/ExternalResolverGui.cpp resolvers/ScriptResolver.cpp @@ -121,6 +123,8 @@ set( libGuiSources utils/ResultUrlChecker.cpp utils/NetworkReply.cpp + widgets/DeclarativeCoverArtProvider.cpp + widgets/DeclarativeView.cpp widgets/AnimatedCounterLabel.cpp widgets/BasicHeader.cpp widgets/FilterHeader.cpp @@ -314,7 +318,7 @@ list(APPEND libSources playlist/dynamic/GeneratorInterface.cpp playlist/dynamic/DynamicPlaylistRevision.cpp playlist/XspfUpdater.cpp - playlist/dynamic/database/DatabaseGenerator.cpp +# playlist/dynamic/database/DatabaseGenerator.cpp playlist/dynamic/database/DatabaseControl.cpp playlist/dynamic/DynamicControl.cpp @@ -497,6 +501,7 @@ TARGET_LINK_LIBRARIES( tomahawklib ${QT_QTXML_LIBRARY} ${QT_QTSVG_LIBRARY} ${QT_QTCORE_LIBRARY} + ${QT_QTDECLARATIVE_LIBRARY} ${OS_SPECIFIC_LINK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${LINK_LIBRARIES} diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index f382c93e93..688b29966e 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -42,7 +42,8 @@ #include "playlist/InboxView.h" #include "playlist/PlaylistLargeItemDelegate.h" #include "playlist/RecentlyPlayedModel.h" -#include "playlist/dynamic/widgets/DynamicWidget.h" +//#include "playlist/dynamic/widgets/DynamicWidget.h" +#include "playlist/dynamic/widgets/DynamicQmlWidget.h" #include "widgets/NewReleasesWidget.h" #include "widgets/WelcomeWidget.h" @@ -193,8 +194,8 @@ ViewManager::playlistForPage( ViewPage* page ) const { p = dynamic_cast< PlaylistView* >( page )->playlistModel()->playlist(); } - else if ( dynamic_cast< DynamicWidget* >( page ) ) - p = dynamic_cast< DynamicWidget* >( page )->playlist(); + else if ( dynamic_cast< DynamicQmlWidget* >( page ) ) + p = dynamic_cast< DynamicQmlWidget* >( page )->playlist(); return p; } @@ -228,7 +229,7 @@ ViewManager::show( const Tomahawk::dynplaylist_ptr& playlist ) { if ( !m_dynamicWidgets.contains( playlist ) || m_dynamicWidgets.value( playlist ).isNull() ) { - m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicWidget( playlist, m_stack ); + m_dynamicWidgets[ playlist ] = new Tomahawk::DynamicQmlWidget( playlist, m_stack ); playlist->resolve(); } @@ -816,7 +817,7 @@ ViewManager::playlistForInterface( Tomahawk::playlistinterface_ptr interface ) c Tomahawk::dynplaylist_ptr ViewManager::dynamicPlaylistForInterface( Tomahawk::playlistinterface_ptr interface ) const { - foreach ( QPointer view, m_dynamicWidgets.values() ) + foreach ( QPointer view, m_dynamicWidgets.values() ) { if ( !view.isNull() && view.data()->playlistInterface() == interface ) { diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index fb6f08bc63..7cc46c4439 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -59,7 +59,7 @@ class InboxModel; namespace Tomahawk { - class DynamicWidget; + class DynamicQmlWidget; } class DLLEXPORT ViewManager : public QObject @@ -195,7 +195,7 @@ private slots: QList< Tomahawk::collection_ptr > m_superCollections; - QHash< Tomahawk::dynplaylist_ptr, QPointer > m_dynamicWidgets; + QHash< Tomahawk::dynplaylist_ptr, QPointer > m_dynamicWidgets; QHash< Tomahawk::collection_ptr, QPointer > m_treeWidgets; QHash< Tomahawk::artist_ptr, QPointer > m_artistViews; QHash< Tomahawk::album_ptr, QPointer > m_albumViews; From bbeeac99351bfa8e3ff967670d606b2fb86e8ac8 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 18 May 2013 23:55:30 +0200 Subject: [PATCH 042/565] * Bring back Radio sidebar item. --- src/libtomahawk/ViewManager.cpp | 17 +++++++++++++++++ src/libtomahawk/ViewManager.h | 2 ++ .../playlist/dynamic/DynamicPlaylist.cpp | 17 +++++++++++------ .../playlist/dynamic/DynamicPlaylist.h | 3 ++- src/sourcetree/SourcesModel.cpp | 16 ++++++++++------ 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 688b29966e..af79f7a1ab 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -83,6 +83,7 @@ ViewManager::ViewManager( QObject* parent ) , m_newReleasesWidget( 0 ) , m_recentPlaysWidget( 0 ) , m_inboxWidget( 0 ) + , m_radioView( 0 ) , m_currentPage( 0 ) , m_loaded( false ) { @@ -383,6 +384,22 @@ ViewManager::showSuperCollection() } +Tomahawk::ViewPage* +ViewManager::showRadioPage() +{ + if ( !m_radioView ) + { + dynplaylist_ptr playlist = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false, QString(), false, false ); + playlist->setMode( OnDemand ); + + m_radioView = new Tomahawk::DynamicQmlWidget( playlist, m_stack ); + } + + setPage( m_radioView ); + return m_radioView; +} + + void ViewManager::playlistInterfaceChanged( Tomahawk::playlistinterface_ptr interface ) { diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 7cc46c4439..dbe3d46ef9 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -133,6 +133,7 @@ Q_OBJECT void historyForwardAvailable( bool avail ); public slots: + Tomahawk::ViewPage* showRadioPage(); Tomahawk::ViewPage* showSuperCollection(); Tomahawk::ViewPage* showWelcomePage(); Tomahawk::ViewPage* showWhatsHotPage(); @@ -191,6 +192,7 @@ private slots: NewReleasesWidget* m_newReleasesWidget; Tomahawk::ViewPage* m_recentPlaysWidget; Tomahawk::ViewPage* m_inboxWidget; + Tomahawk::DynamicQmlWidget* m_radioView; InboxModel* m_inboxModel; QList< Tomahawk::collection_ptr > m_superCollections; diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index c7269cd1d5..486774f4b7 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -151,17 +151,22 @@ DynamicPlaylist::create( const Tomahawk::source_ptr& author, GeneratorMode mode, bool shared, const QString& type, - bool autoLoad + bool autoLoad, + bool temporary ) { dynplaylist_ptr dynplaylist = Tomahawk::dynplaylist_ptr( new DynamicPlaylist( author, guid, title, info, creator, type, mode, shared, autoLoad ), &QObject::deleteLater ); dynplaylist->setWeakSelf( dynplaylist.toWeakRef() ); - DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad ); - connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); - Database::instance()->enqueue( QSharedPointer(cmd) ); - if ( autoLoad ) - dynplaylist->reportCreated( dynplaylist ); + if ( !temporary ) + { + DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( author, dynplaylist, autoLoad ); + connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + if ( autoLoad ) + dynplaylist->reportCreated( dynplaylist ); + } + return dynplaylist; } diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h index d66d139477..5e576c84b3 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.h @@ -85,7 +85,8 @@ class DLLEXPORT DynamicPlaylist : public Tomahawk::Playlist GeneratorMode mode, bool shared, const QString& type = QString(), - bool autoLoad = true + bool autoLoad = true, + bool temporary = false ); static void remove( const dynplaylist_ptr& playlist ); diff --git a/src/sourcetree/SourcesModel.cpp b/src/sourcetree/SourcesModel.cpp index dd20740ce6..c068674ea8 100644 --- a/src/sourcetree/SourcesModel.cpp +++ b/src/sourcetree/SourcesModel.cpp @@ -309,29 +309,33 @@ SourcesModel::appendGroups() sc->setSortValue( 1 ); // browse section + GenericPageItem* radio = new GenericPageItem( this, browse, tr( "Radio" ), ImageRegistry::instance()->icon( RESPATH "images/station.svg" ), + boost::bind( &ViewManager::showRadioPage, ViewManager::instance() ), + boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); + radio->setSortValue( 2 ); + LovedTracksItem* loved = new LovedTracksItem( this, browse ); - loved->setSortValue( 2 ); + loved->setSortValue( 3 ); GenericPageItem* recent = new GenericPageItem( this, browse, tr( "Recently Played" ), ImageRegistry::instance()->icon( RESPATH "images/recently-played.svg" ), boost::bind( &ViewManager::showRecentPlaysPage, ViewManager::instance() ), boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); - recent->setSortValue( 3 ); + recent->setSortValue( 4 ); GenericPageItem* hot = new GenericPageItem( this, browse, tr( "Charts" ), ImageRegistry::instance()->icon( RESPATH "images/charts.svg" ), boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ), boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() ) ); - hot->setSortValue( 4 ); + hot->setSortValue( 5 ); GenericPageItem* newReleases = new GenericPageItem( this, browse, tr( "New Releases" ), ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), boost::bind( &ViewManager::showNewReleasesPage, ViewManager::instance() ), boost::bind( &ViewManager::newReleasesWidget, ViewManager::instance() ) ); - newReleases->setSortValue( 5 ); + newReleases->setSortValue( 6 ); InboxItem* inbox = new InboxItem( this, browse ); - inbox->setSortValue( 6 ); + inbox->setSortValue( 7 ); m_collectionsGroup = new GroupItem( this, m_rootItem, tr( "Friends" ), 4 ); - m_cloudGroup = new GroupItem( this, m_rootItem, tr( "Cloud" ), 5 ); endInsertRows(); From 266de41624377da1df5e79c7389de32b8a6e7585 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 00:31:15 +0200 Subject: [PATCH 043/565] * Fixed PlayableItem & added properties. --- src/libtomahawk/playlist/PlayableItem.cpp | 8 ++++++++ src/libtomahawk/playlist/PlayableItem.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/libtomahawk/playlist/PlayableItem.cpp b/src/libtomahawk/playlist/PlayableItem.cpp index af7562446e..9ffe017f83 100644 --- a/src/libtomahawk/playlist/PlayableItem.cpp +++ b/src/libtomahawk/playlist/PlayableItem.cpp @@ -205,6 +205,14 @@ PlayableItem::artistName() const { return m_query->track()->artist(); } + else if ( !m_album.isNull() ) + { + return m_album->artist()->name(); + } + else if ( !m_artist.isNull() ) + { + return m_artist->name(); + } return QString(); } diff --git a/src/libtomahawk/playlist/PlayableItem.h b/src/libtomahawk/playlist/PlayableItem.h index 5f1e7913fa..41f769740f 100644 --- a/src/libtomahawk/playlist/PlayableItem.h +++ b/src/libtomahawk/playlist/PlayableItem.h @@ -31,6 +31,10 @@ class DLLEXPORT PlayableItem : public QObject { Q_OBJECT +Q_PROPERTY(QString name READ name NOTIFY dataChanged) +Q_PROPERTY(QString artistName READ artistName NOTIFY dataChanged) +Q_PROPERTY(QString albumName READ albumName NOTIFY dataChanged) +Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY dataChanged) public: ~PlayableItem(); From 21564667623d34b9f3cf8e1c9aa3ec95aabe5fe1 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 00:31:40 +0200 Subject: [PATCH 044/565] * Added itemFromIndex to PlayableModel. --- src/libtomahawk/playlist/PlayableModel.cpp | 7 +++++++ src/libtomahawk/playlist/PlayableModel.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 113a8067a5..9cf2daa5bf 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -816,6 +816,13 @@ PlayableModel::finishLoading() } +PlayableItem* +PlayableModel::itemFromIndex( int itemIndex ) const +{ + return itemFromIndex( index( itemIndex, 0, QModelIndex() ) ); +} + + PlayableItem* PlayableModel::itemFromIndex( const QModelIndex& index ) const { diff --git a/src/libtomahawk/playlist/PlayableModel.h b/src/libtomahawk/playlist/PlayableModel.h index e5cc14d526..656fd254cb 100644 --- a/src/libtomahawk/playlist/PlayableModel.h +++ b/src/libtomahawk/playlist/PlayableModel.h @@ -120,6 +120,7 @@ Q_OBJECT virtual void ensureResolved(); + Q_INVOKABLE PlayableItem* itemFromIndex( int itemIndex ) const; virtual PlayableItem* itemFromIndex( const QModelIndex& index ) const; virtual PlayableItem* itemFromQuery( const Tomahawk::query_ptr& query ) const; virtual PlayableItem* itemFromResult( const Tomahawk::result_ptr& result ) const; From 2e3785f92b0b9e461803a30ea87f88ca4c71ce04 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 00:32:31 +0200 Subject: [PATCH 045/565] * Disable 'Create new station' item. --- src/sourcetree/items/SourceItem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sourcetree/items/SourceItem.cpp b/src/sourcetree/items/SourceItem.cpp index 5bbaac4922..f68738e738 100644 --- a/src/sourcetree/items/SourceItem.cpp +++ b/src/sourcetree/items/SourceItem.cpp @@ -108,7 +108,7 @@ SourceItem::SourceItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahaw } if ( !stations.isEmpty() || source->isLocal() ) { - m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source->isLocal() ); + m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, false /* source->isLocal() */ ); onStationsAdded( stations ); } @@ -339,14 +339,14 @@ void SourceItem::playlistsAddedInternal( SourceTreeItem* parent, const QList< dynplaylist_ptr >& playlists ) { QList< SourceTreeItem* > items; - int addOffset = playlists.first()->author()->isLocal() ? 1 : 0; + int addOffset = 0; //playlists.first()->author()->isLocal() ? 1 : 0; int from = parent->children().count() - addOffset; parent->beginRowsAdded( from, from + playlists.count() - 1 ); foreach ( const dynplaylist_ptr& p, playlists ) { DynamicPlaylistItem* plItem = new DynamicPlaylistItem( model(), parent, p, parent->children().count() - addOffset ); -// qDebug() << "Dynamic Playlist added:" << p->title() << p->creator() << p->info(); +// tDebug() << "Dynamic Playlist added:" << p->title() << p->creator() << p->info(); p->loadRevision(); items << plItem; @@ -516,7 +516,7 @@ SourceItem::onStationsAdded( const QList< dynplaylist_ptr >& stations ) // add the category too int cur = children().count(); beginRowsAdded( cur, cur ); - m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source()->isLocal() ); + m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, false /* source()->isLocal() */ ); endRowsAdded(); } From fcc6f5c9662323bf47d1fd56d39996cd44cf9f8e Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 00:39:29 +0200 Subject: [PATCH 046/565] some more work on stations merged --- data/qml/SpinnerTest.qml | 1 + data/qml/StationView.qml | 177 ++++++++++-------- data/qml/stations/CreateByArtist.qml | 4 +- data/qml/stations/CreateByGenre.qml | 4 +- data/qml/stations/CreateByYear.qml | 79 ++++++++ data/qml/stations/StationCreatorPage2.qml | 4 +- data/qml/stations/StationItem.qml | 73 ++++++++ data/qml/tomahawkimports/CoverFlip.qml | 2 - data/qml/tomahawkimports/CoverImage.qml | 5 +- data/qml/tomahawkimports/DoubleSlider.qml | 130 ++++++++++--- data/qml/tomahawkimports/InputField.qml | 6 + data/qml/tomahawkimports/TagCloud.qml | 6 +- resources.qrc | 2 + .../dynamic/widgets/DynamicQmlWidget.cpp | 17 +- .../dynamic/widgets/DynamicQmlWidget.h | 3 +- 15 files changed, 393 insertions(+), 120 deletions(-) create mode 100644 data/qml/stations/CreateByYear.qml create mode 100644 data/qml/stations/StationItem.qml diff --git a/data/qml/SpinnerTest.qml b/data/qml/SpinnerTest.qml index 217b4dbcf2..aa11c4031d 100644 --- a/data/qml/SpinnerTest.qml +++ b/data/qml/SpinnerTest.qml @@ -11,6 +11,7 @@ BusyIndicator { anchors.horizontalCenterOffset: 200 height: 200 width: 200 + count: 11 } Image { diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 7bb79e1b47..3448c3adbf 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -19,23 +19,42 @@ Rectangle { width: parent.width icon: "../images/station.svg" title: mainView.title - subtitle: generator.summary + subtitle: ""//generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: stationListView.currentIndex == 2 + showNextButton: !mainView.configured && stationListView.currentIndex == 2 nextButtonText: "Save" + backButtonText: mainView.configured ? "Configure" : "Back" z: 1 //cover albumcovers that may leave their area - onBackPressed: stationListView.decrementCurrentIndex() - onNextPressed: stationListView.incrementCurrentIndex() + onBackPressed: { + if(mainView.configured) { + return; + } + + inputBubble.opacity = 0 + stationListView.decrementCurrentIndex() + if(stationListView.currentIndex == 1) { + subtitle = modeModel.get(stationCreator.modeIndex).headerSubtitle + "..." + } + if(stationListView.currentIndex == 0) { + subtitle = "" + } + } + // In our case the next button is the save button + onNextPressed: { + inputBubble.opacity = 1 + saveNameInput.forceActiveFocus(); + } } + ListModel { id: modeModel - ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml" } - ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml" } - ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "year" } + ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml"; headerSubtitle: "by" } + ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml"; headerSubtitle: "like" } + ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "stations/CreateByYear.qml"; headerSubtitle: "from" } } VisualItemModel { @@ -47,8 +66,9 @@ Rectangle { model: modeModel onItemClicked: { - stationCreator.content = modeModel.get(index).creatorContent + stationCreator.modeIndex = index stationListView.incrementCurrentIndex() + header.subtitle = modeModel.get(index).headerSubtitle + "..." } } @@ -57,84 +77,35 @@ Rectangle { height: stationListView.height width: stationListView.width - onNext: stationListView.incrementCurrentIndex() + property int modeIndex + + content: modeModel.get(modeIndex).creatorContent + + onNext: { + stationListView.incrementCurrentIndex() + header.subtitle = modeModel.get(modeIndex).headerSubtitle + " " + text + } } - Item { + StationItem { id: stationItem height: stationListView.height width: stationListView.width + } + } - CoverFlip { - id: coverView - anchors.right: parent.right - anchors.top: parent.top - height: parent.height - width: parent.width - interactive: false - - backgroundColor: scene.color - - model: dynamicModel - currentIndex: currentlyPlayedIndex - - onItemPlayPauseClicked: { - mainView.playItem(index) - } - - onItemClicked: { - mainView.playItem(index) - } - - states: [ - State { - name: "empty"; when: mainView.loading - PropertyChanges { - target: coverView - anchors.rightMargin: -coverView.width - anchors.topMargin: - coverView.height - scale: 0 - } - } - ] - transitions: [ - Transition { - from: "empty" - to: "*" - NumberAnimation { - properties: "anchors.topMargin,anchors.rightMargin,scale" - duration: 1000 - easing.type: Easing.OutQuad - } - } - - ] -// Behavior on anchors.topMargin { -// NumberAnimation { duration: 500 } -// } -// Behavior on anchors.rightMargin { -// NumberAnimation { duration: 500 } -// } -// Behavior on scale { -// NumberAnimation { duration: 500 } -// } - } - BusyIndicator { - id: busyIndicator - anchors.centerIn: parent - height: defaultFontHeight * 4 - width: height + VisualItemModel { + id: configuredStationVisualModel - opacity: mainView.loading ? 1 : 0 - running: mainView.loading - } + StationItem { + id: cfgstationItem + height: stationListView.height + width: stationListView.width } - } - ListView { id: stationListView anchors { @@ -147,7 +118,7 @@ Rectangle { contentHeight: height contentWidth: width orientation: ListView.Horizontal - model: stationVisualModel + //model: mainView.configured ? configuredStationVisualModel : stationVisualModel interactive: false highlightMoveDuration: 300 @@ -157,6 +128,62 @@ Rectangle { onWidthChanged: { contentWidth = scene.width } + + Component.onCompleted: { + model = mainView.configured ? configuredStationVisualModel : stationVisualModel + } + onModelChanged: print("ccccccccccccc", mainView.configured) } + Rectangle { + id: inputBubble + color: "black" + border.width: 2 + border.color: "white" + height: defaultFontHeight * 3 + width: height * 6 + radius: defaultFontHeight / 2 + anchors.top: header.bottom + anchors.right: parent.right + anchors.rightMargin: defaultFontHeight / 2 + anchors.topMargin: -defaultFontHeight / 2 + z: 2 + opacity: 0 + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + Row { + anchors.centerIn: parent + width: parent.width - defaultFontHeight + spacing: defaultFontHeight / 2 + + function saveStation(name) { + mainView.title = name + inputBubble.opacity = 0 + header.showNextButton = false + header.backButtonText = "Configure" + } + + Text { + id: nameText + color: "white" + text: "Name:" + anchors.verticalCenter: parent.verticalCenter + } + InputField { + id: saveNameInput + width: parent.width - nameText.width - saveOkButton.width - parent.spacing * 2 + placeholderText: "Station" + onAccepted: parent.saveStation(text); + } + PushButton { + id: saveOkButton + text: "OK" + onClicked: parent.saveStation(saveNameInput.text) + } + } + + } + } diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml index f3f2f4ceb3..359b353f78 100644 --- a/data/qml/stations/CreateByArtist.qml +++ b/data/qml/stations/CreateByArtist.qml @@ -6,11 +6,11 @@ Item { id: root anchors.fill: parent - signal done() + signal done(string text) function createStation(artist) { mainView.startStationFromArtist(artist) - root.done() + root.done(artist) } Column { diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml index 9d2fb1b9b9..1d89641833 100644 --- a/data/qml/stations/CreateByGenre.qml +++ b/data/qml/stations/CreateByGenre.qml @@ -6,11 +6,11 @@ Item { id: root anchors.fill: parent - signal done() + signal done(string text) function createStation(genre) { mainView.startStationFromGenre(genre) - root.done() + root.done(genre) } ListModel { diff --git a/data/qml/stations/CreateByYear.qml b/data/qml/stations/CreateByYear.qml new file mode 100644 index 0000000000..5e14616689 --- /dev/null +++ b/data/qml/stations/CreateByYear.qml @@ -0,0 +1,79 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: root + anchors.fill: parent + + signal done(string text) + + function createStation(artist) { + mainView.startStationFromArtist(artist) + root.done(artist) + } + + Column { + id: upperColumn + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height + width: defaultFontHeight * 30 + anchors.bottomMargin: defaultFontHeight + spacing: defaultFontHeight + + HeaderLabel { + id: headerText + text: "Create station by year..." + } + + Row { + height: artistInputField.height + width: parent.width + spacing: defaultFontHeight * 0.5 + + InputField { + id: artistInputField + width: parent.width - createFromInputButton.width - parent.spacing + + onAccepted: createStation(text) + } + + PushButton { + id: createFromInputButton + text: "Go!" + enabled: artistInputField.text.length > 2 + onClicked: createStation(artistInputField.text) + } + } + +// Item { +// height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3 +// width: parent.width +// ArtistView { +// id: artistView +// height: parent.height +// width: parent.width +// model: artistChartsModel +// clip: true +// delegateHeight: defaultFontHeight * 6 + +// onItemClicked: { +// createStation(artistChartsModel.itemFromIndex(index).artistName); +// } +// } +// ScrollBar { +// listView: artistView +// } +// } + + DoubleSlider { + width: parent.width + height: defaultFontHeight * 2 + min: 1960 + max: new Date().getFullYear() + lowerSliderPos: 1990 + upperSliderPos: 2010 + minMaxLabelsVisible: false + } + } +} diff --git a/data/qml/stations/StationCreatorPage2.qml b/data/qml/stations/StationCreatorPage2.qml index 34f4b65cda..295069aa66 100644 --- a/data/qml/stations/StationCreatorPage2.qml +++ b/data/qml/stations/StationCreatorPage2.qml @@ -8,7 +8,7 @@ Item { property int margins: defaultFontHeight * 2 property alias content: contentLoader.source - signal next() + signal next(string text) Loader { id: contentLoader @@ -19,7 +19,7 @@ Item { Connections { target: contentLoader.item - onDone: root.next() + onDone: root.next(text) } } diff --git a/data/qml/stations/StationItem.qml b/data/qml/stations/StationItem.qml new file mode 100644 index 0000000000..9d7804a0a9 --- /dev/null +++ b/data/qml/stations/StationItem.qml @@ -0,0 +1,73 @@ +import QtQuick 1.1 +import tomahawk 1.0 +import "../tomahawkimports" + +Item { + id: stationItem + + CoverFlip { + id: coverView + anchors.right: parent.right + anchors.top: parent.top + height: parent.height + width: parent.width + interactive: false + + backgroundColor: scene.color + + model: dynamicModel + currentIndex: currentlyPlayedIndex + + onItemPlayPauseClicked: { + mainView.playItem(index) + } + + onItemClicked: { + mainView.playItem(index) + } + + states: [ + State { + name: "empty"; when: mainView.loading + PropertyChanges { + target: coverView + anchors.rightMargin: -coverView.width + anchors.topMargin: - coverView.height + scale: 0 + } + } + ] + transitions: [ + Transition { + from: "empty" + to: "*" + NumberAnimation { + properties: "anchors.topMargin,anchors.rightMargin,scale" + duration: 1000 + easing.type: Easing.OutQuad + } + } + + ] +// Behavior on anchors.topMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on anchors.rightMargin { +// NumberAnimation { duration: 500 } +// } +// Behavior on scale { +// NumberAnimation { duration: 500 } +// } + + } + BusyIndicator { + id: busyIndicator + anchors.centerIn: parent + height: defaultFontHeight * 4 + width: height + + opacity: mainView.loading ? 1 : 0 + running: mainView.loading + } + +} diff --git a/data/qml/tomahawkimports/CoverFlip.qml b/data/qml/tomahawkimports/CoverFlip.qml index 58e85824c4..742dcce719 100644 --- a/data/qml/tomahawkimports/CoverFlip.qml +++ b/data/qml/tomahawkimports/CoverFlip.qml @@ -49,8 +49,6 @@ PathView { right: parent.right } - backgroundColor: coverView.backgroundColor - showLabels: true showMirror: true artistName: model.artistName diff --git a/data/qml/tomahawkimports/CoverImage.qml b/data/qml/tomahawkimports/CoverImage.qml index 518137aac8..e3f2c1d306 100644 --- a/data/qml/tomahawkimports/CoverImage.qml +++ b/data/qml/tomahawkimports/CoverImage.qml @@ -25,9 +25,6 @@ Item { // The border width for the cover image property int borderWidth: 2 - // needed to adjust the shadow - property color backgroundColor: "black" - // sets the brightness for the item and its mirror (1: brightest, 0: darkest) property double itemBrightness: 1 property double mirrorBrightness: .5 @@ -55,7 +52,7 @@ Item { Rectangle { id: itemShadow - color: backgroundColor + color: "black" anchors.fill: parent //opacity: 1 - itemBrightness diff --git a/data/qml/tomahawkimports/DoubleSlider.qml b/data/qml/tomahawkimports/DoubleSlider.qml index b1dc522cc0..08061b96e2 100644 --- a/data/qml/tomahawkimports/DoubleSlider.qml +++ b/data/qml/tomahawkimports/DoubleSlider.qml @@ -2,8 +2,6 @@ import QtQuick 1.1 Item { id: root - width: 500 - height: 10 property int min: 0 property int max: 100 @@ -16,71 +14,144 @@ Item { /** Should the floating label indicating the current position be shown? */ property bool showFloatingLabel: true + property bool minMaxLabelsVisible: true property int lowerSliderPos: 25 property int upperSliderPos: 75 + onUpperSliderPosChanged: print("fooooooooo", upperSliderPos) + signal valueChanged() + QtObject { + id: priv + + property int steps: root.max - root.min + 1 + + property int sliderHeight: root.height + property int sliderWidth: root.height / 2 + } + Row { anchors.fill: parent + anchors.topMargin: defaultFontHeight * 1.2 + anchors.bottomMargin: defaultFontHeight * 1.2 spacing: 10 Text { id: minText text: root.minLabel.length > 0 ? root.minLabel : min color: "white" + visible: root.minMaxLabelsVisible } Item { id: sliderRect height: root.height - width: parent.width - minText.width - maxText.width - parent.spacing * 2 + property int maxWidth: parent.width - (minText.visible ? minText.width : 0) - (maxText.visible ? maxText.width : 0) - parent.spacing * 2 + width: maxWidth - (maxWidth % priv.steps) + anchors.horizontalCenter: parent.horizontalCenter function sliderPosToValue( sliderPos ) { - var percent = sliderPos * 100 / (sliderRect.width - lowerSlider.width); - return Math.floor(percent * (root.max - root.min) / 100) + root.min + var percent = sliderPos * 100 / (sliderRect.width - priv.sliderWidth/2); + return Math.floor(percent * (priv.steps-1) / 100) + root.min } function valueToSloderPos( value ) { - var percent = (value - root.min) * 100 / (root.max - root.min) - return percent * (sliderRect.width - lowerSlider.width) / 100 + var percent = (value - root.min) * 100 / (priv.steps-1) + return percent * (sliderRect.width - priv.sliderWidth/2) / 100 } Rectangle { id: sliderBase - height: root.height / 5 - width: parent.width + height: root.height / 1.5 + width: parent.width + defaultFontHeight * 1.5 color: "white" radius: height / 2 anchors.centerIn: parent + gradient: Gradient { + GradientStop { position: 0.0; color: "#ffffffff" } + GradientStop { position: 1.0; color: "#aaffffff" } + } + + Rectangle { + anchors.fill: sliderBase + anchors.leftMargin: lowerSlider.x + priv.sliderWidth + anchors.rightMargin: sliderBase.width - upperSlider.x - priv.sliderWidth + gradient: Gradient { + GradientStop { position: 0.0; color: "#aa962c26" } + GradientStop { position: 1.0; color: "#962c26" } + } + } + + Row { + id: stepRow + anchors.fill: parent + anchors.leftMargin: defaultFontHeight - lineWidth/2 + anchors.rightMargin: defaultFontHeight - lineWidth/2 + property int stepCount: root.max - root.min + 1 + property int lineHeight: height + property int lineWidth: lineHeight / 15 + spacing: (width - (stepCount * lineWidth)) / stepCount + + Repeater { + model: stepRow.stepCount + + Rectangle { + id: marker + height: stepRow.lineHeight * (isHighlight ? 1.2 : 1) + width: stepRow.lineWidth + color: "black" + + property bool isHighlight: index % 10 === 0 + + gradient: Gradient { + GradientStop { position: 0.0; color: marker.isHighlight ? "white" : "black" } + GradientStop { position: 1.0; color: marker.isHighlight ? "#aaffffff" : "black" } + } + + Text { + text: root.min + index + visible: marker.isHighlight + anchors.horizontalCenter: marker.horizontalCenter + anchors.top: marker.bottom + anchors.topMargin: defaultFontHeight / 2 + color: "white" + } + + } + + } + } + } + Rectangle { id: lowerSlider - height: root.height - width: height + height: priv.sliderHeight + width: priv.sliderWidth anchors.top: root.top - radius: height/2 + radius: height/4 border.color: "black" border.width: 2 - x: sliderRect.valueToSloderPos(root.lowerSliderPos) + x: sliderRect.valueToSloderPos(root.lowerSliderPos) - priv.sliderWidth/2 Rectangle { id: lowerFloatingRect color: "white" anchors.bottom: lowerSlider.top anchors.bottomMargin: 10 - visible: root.showFloatingLabel && lowerSliderMouseArea.pressed +// visible: root.showFloatingLabel && lowerSliderMouseArea.pressed width: lowerFloatingText.width * 1.2 height: lowerFloatingText.height + height * 1.2 - x: -(width - lowerSlider.width) / 2 - radius: height / 4 + x: -(width - priv.sliderWidth) / 2 + radius: height / 8 Text { id: lowerFloatingText anchors.centerIn: parent - text: sliderRect.sliderPosToValue(lowerSlider.x) + text: sliderRect.sliderPosToValue(lowerSlider.x + priv.sliderWidth/2) } } } @@ -89,10 +160,10 @@ Item { anchors.fill: lowerSlider drag.target: lowerSlider drag.axis: "XAxis" - drag.minimumX: 0 - drag.maximumX: upperSlider.x - lowerSlider.width + drag.minimumX: -priv.sliderWidth / 2 + drag.maximumX: upperSlider.x - priv.sliderWidth onReleased: { - root.lowerSliderPos = sliderRect.sliderPosToValue( lowerSlider.x ); + root.lowerSliderPos = sliderRect.sliderPosToValue( lowerSlider.x + priv.sliderWidth/2 ); root.valueChanged(); } } @@ -100,9 +171,9 @@ Item { Rectangle { id: upperSlider height: root.height - width: height + width: height / 2 anchors.top: root.top - radius: height/2 + radius: height / 4 border.color: "black" border.width: 2 x: sliderRect.valueToSloderPos(root.upperSliderPos) @@ -111,16 +182,16 @@ Item { color: "white" anchors.bottom: upperSlider.top anchors.bottomMargin: 10 - visible: root.showFloatingLabel && upperSliderMouseArea.pressed +// visible: root.showFloatingLabel && upperSliderMouseArea.pressed width: upperFloatingText.width * 1.2 height: upperFloatingText.height + height * 1.2 radius: height / 4 - x: -(width - upperSlider.width) / 2 + x: -(width - priv.sliderWidth) / 2 Text { id: upperFloatingText anchors.centerIn: parent - text: sliderRect.sliderPosToValue(upperSlider.x) + text: sliderRect.sliderPosToValue(upperSlider.x + priv.sliderWidth/2) } } @@ -131,14 +202,16 @@ Item { onClicked: print("button pressed") drag.target: upperSlider drag.axis: "XAxis" - drag.minimumX: lowerSlider.x + lowerSlider.width - drag.maximumX: parent.width - upperSlider.width + drag.minimumX: lowerSlider.x + priv.sliderWidth + drag.maximumX: parent.width - priv.sliderWidth onReleased: { - root.upperSliderPos = sliderRect.sliderPosToValue( upperSlider.x ); + root.upperSliderPos = sliderRect.sliderPosToValue( upperSlider.x + priv.sliderWidth/2 ); root.valueChanged(); } } + + } @@ -146,6 +219,7 @@ Item { id: maxText text: root.maxLabel.length > 0 ? root.maxLabel : max color: "white" + visible: root.minMaxLabelsVisible } } } diff --git a/data/qml/tomahawkimports/InputField.qml b/data/qml/tomahawkimports/InputField.qml index 9f052124de..c15d030771 100644 --- a/data/qml/tomahawkimports/InputField.qml +++ b/data/qml/tomahawkimports/InputField.qml @@ -17,6 +17,12 @@ Rectangle { property int spacing: defaultFontHeight * 0.2 signal accepted( string text ) + onFocusChanged: { + if(focus) { + textInput.forceActiveFocus(); + } + } + Image { id: searchIcon anchors { diff --git a/data/qml/tomahawkimports/TagCloud.qml b/data/qml/tomahawkimports/TagCloud.qml index ba3de03aa2..ccdc82cd30 100644 --- a/data/qml/tomahawkimports/TagCloud.qml +++ b/data/qml/tomahawkimports/TagCloud.qml @@ -16,7 +16,7 @@ Item { Flow { anchors.centerIn: parent width: parent.width - spacing: 3 + spacing: defaultFontSize Repeater { id: cloudRepeater @@ -26,7 +26,7 @@ Item { id: cloudItem width: delegateText.width * 1.1 height: delegateText.height - property double itemScale: Math.random() + .3 + property double itemScale: tagCloud.randomNumber(0.5, 1.2) scale: itemScale Text { id: delegateText @@ -35,7 +35,7 @@ Item { text: modelData font.pointSize: 16 anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15) + //anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15) states: [ State { diff --git a/resources.qrc b/resources.qrc index 5c0708890e..100c6a0f76 100644 --- a/resources.qrc +++ b/resources.qrc @@ -103,9 +103,11 @@ data/qml/tomahawkimports/CoverFlip.qml data/qml/tomahawkimports/BusyIndicator.qml data/qml/StationView.qml + data/qml/stations/StationItem.qml data/qml/stations/StationCreatorPage1.qml data/qml/stations/StationCreatorPage2.qml data/qml/stations/CreateByArtist.qml + data/qml/stations/CreateByYear.qml data/qml/stations/StationConfig.qml data/qml/QmlGridView.qml data/qml/stations/CreateByGenre.qml diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index c4b149df1d..0b5c1edd4e 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -82,7 +82,22 @@ DynamicQmlWidget::playlistInterface() const QString DynamicQmlWidget::title() const { - return m_model->title(); + if ( !m_playlist->title().isEmpty() ) { + return m_playlist->title(); + } + return "Listen to radio..."; +} + + +void +DynamicQmlWidget::setTitle(const QString &title) +{ + m_model->setTitle( title ); + m_playlist->setTitle( title ); + m_model->playlist()->setTitle( title ); + m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); + m_playlist->reportCreated( m_playlist ); + emit titleChanged(); } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index 382bf314ac..c5a0ceac79 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -37,7 +37,7 @@ class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage { Q_OBJECT - Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) Q_PROPERTY(bool configured READ configured NOTIFY configuredChanged) @@ -49,6 +49,7 @@ class DynamicQmlWidget : public DeclarativeView, public Tomahawk::ViewPage virtual Tomahawk::playlistinterface_ptr playlistInterface() const; virtual QString title() const; + virtual void setTitle(const QString &title); virtual QString description() const; virtual QString iconSource() const; From c53800f0d3b15f27e8a9dbc3770d9d19691baa8a Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 02:36:11 +0200 Subject: [PATCH 047/565] create station wizard: workaround slow fist scrolling of the ListView --- data/qml/StationView.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 3448c3adbf..3ce7639c6d 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -67,6 +67,12 @@ Rectangle { onItemClicked: { stationCreator.modeIndex = index + + // FIXME: This is a workaround for the ListView scrolling too slow on the first time + // Lets reinitialize the current index to something invalid and back to 0 (what it already is) before scrolling over to page 1 + stationListView.currentIndex = -1 + stationListView.currentIndex = 0 + stationListView.incrementCurrentIndex() header.subtitle = modeModel.get(index).headerSubtitle + "..." } From 050503e8b57dbfb58987f19ab8c827ee4470bd90 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 19 May 2013 02:38:25 +0200 Subject: [PATCH 048/565] * Behave, Qt4. --- data/qml/tomahawkimports/InputField.qml | 6 ++---- data/qml/tomahawkimports/PushButton.qml | 11 ++++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/data/qml/tomahawkimports/InputField.qml b/data/qml/tomahawkimports/InputField.qml index c15d030771..29159ad23b 100644 --- a/data/qml/tomahawkimports/InputField.qml +++ b/data/qml/tomahawkimports/InputField.qml @@ -85,12 +85,10 @@ Rectangle { } - BorderImage { - source: "../../images/inputfield-border.svg" + Image { +// source: "../../images/inputfield-border.svg" anchors.fill: parent anchors.margins: root.radius * 0.1 clip: true - border.left: defaultFontHeight/4; border.top: defaultFontHeight/4 - border.right: defaultFontHeight/4; border.bottom: defaultFontHeight/4 } } diff --git a/data/qml/tomahawkimports/PushButton.qml b/data/qml/tomahawkimports/PushButton.qml index b2c4b25035..25dc864001 100644 --- a/data/qml/tomahawkimports/PushButton.qml +++ b/data/qml/tomahawkimports/PushButton.qml @@ -6,13 +6,14 @@ Rectangle { height: buttonText.height * 1.4 width: buttonText.width + (spacing * 2) radius: defaultFontHeight * 0.25 - border.width: defaultFontHeight * 0.05 - border.color: "#a7a7a7" +// border.width: defaultFontHeight * 0.05 +// border.color: "#a7a7a7" - gradient: Gradient { + color: "white" +/* gradient: Gradient { GradientStop { position: 0.0; color: mouseArea.pressed ? "#040404" : "#fbfbfb" } GradientStop { position: 1.0; color: mouseArea.pressed ? "#8e8f8e" : "#787878" } - } + }*/ property int spacing: defaultFontHeight * 0.5 property alias text: buttonText.text @@ -23,7 +24,7 @@ Rectangle { id: buttonText anchors.centerIn: root font.pointSize: defaultFontSize - color: mouseArea.pressed ? "white" : "black" + color: mouseArea.pressed ? "grey" : "black" } MouseArea { From 97c8b93d3cb531d57199ac5e601bd6351d81c644 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 03:43:50 +0200 Subject: [PATCH 049/565] add animation to FlexibleHeader --- data/qml/tomahawkimports/FlexibleHeader.qml | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/data/qml/tomahawkimports/FlexibleHeader.qml b/data/qml/tomahawkimports/FlexibleHeader.qml index 04c5e2566e..4822b76f4c 100644 --- a/data/qml/tomahawkimports/FlexibleHeader.qml +++ b/data/qml/tomahawkimports/FlexibleHeader.qml @@ -66,8 +66,9 @@ Rectangle { } Column { - height: parent.height + height: childrenRect.height width: parent.width - iconImage.width - parent.spacing + anchors.verticalCenter: parent.verticalCenter Item { id: titleItem @@ -131,7 +132,30 @@ Rectangle { font.pointSize: defaultFontSize * 1.2 width: parent.width elide: Text.ElideRight + height: text.length > 0 ? defaultFontHeight : 0 + opacity: text.length > 0 ? 1 : 0 + Behavior on height { + NumberAnimation { duration: 200 } + } + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + onTextChanged: { + if (text.length > 0) { + animationText.text = text + } + } + + Text { + id: animationText + color: parent.color + font: parent.font + elide: parent.elide + anchors.fill: parent + } } + } } From e99439eccad143fe2073d4dc2eee4182d1c60900 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 19 May 2013 04:39:57 +0200 Subject: [PATCH 050/565] some more work on start by year --- data/qml/stations/CreateByYear.qml | 76 +++++++++++-------- data/qml/stations/StationCreatorPage1.qml | 1 + data/qml/stations/StationItem.qml | 1 + data/qml/tomahawkimports/DoubleSlider.qml | 16 ++-- .../playlist/dynamic/GeneratorInterface.h | 2 + .../dynamic/echonest/EchonestGenerator.cpp | 12 +++ .../dynamic/echonest/EchonestGenerator.h | 2 + .../dynamic/widgets/DynamicQmlWidget.cpp | 20 +++++ .../dynamic/widgets/DynamicQmlWidget.h | 2 + 9 files changed, 91 insertions(+), 41 deletions(-) diff --git a/data/qml/stations/CreateByYear.qml b/data/qml/stations/CreateByYear.qml index 5e14616689..ae9f15a69b 100644 --- a/data/qml/stations/CreateByYear.qml +++ b/data/qml/stations/CreateByYear.qml @@ -8,9 +8,14 @@ Item { signal done(string text) - function createStation(artist) { - mainView.startStationFromArtist(artist) - root.done(artist) + function createStationFromYear(year) { + mainView.startStationFromYear(year) + root.done(year) + } + + function createStationFromTo(yearFrom, yearTo) { + mainView.startStationFromTo(yearFrom, yearTo) + root.done(yearFrom + " to " + yearTo) } Column { @@ -27,53 +32,58 @@ Item { } Row { - height: artistInputField.height + height: yearInputField.height width: parent.width spacing: defaultFontHeight * 0.5 + Text { + text: "Year:" + color: "white" + anchors.verticalCenter: parent.verticalCenter + } + InputField { - id: artistInputField + id: yearInputField width: parent.width - createFromInputButton.width - parent.spacing onAccepted: createStation(text) } - - PushButton { - id: createFromInputButton - text: "Go!" - enabled: artistInputField.text.length > 2 - onClicked: createStation(artistInputField.text) - } } -// Item { -// height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3 -// width: parent.width -// ArtistView { -// id: artistView -// height: parent.height -// width: parent.width -// model: artistChartsModel -// clip: true -// delegateHeight: defaultFontHeight * 6 - -// onItemClicked: { -// createStation(artistChartsModel.itemFromIndex(index).artistName); -// } -// } -// ScrollBar { -// listView: artistView -// } -// } - DoubleSlider { + id: yearSlider width: parent.width - height: defaultFontHeight * 2 + height: defaultFontHeight * 4 min: 1960 max: new Date().getFullYear() lowerSliderPos: 1990 upperSliderPos: 2010 minMaxLabelsVisible: false + opacity: yearInputField.text.length > 0 ? 0.3 : 1 + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + } + + PushButton { + id: createFromInputButton + text: "Go!" + enabled: yearInputField.text.length == 0 || (yearInputField.text >= yearSlider.min && yearInputField.text <= yearSlider.max) + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + if (yearInputField.text.length > 0) { + createStationFromYear(yearInputField.text) + } else { + createStationFromTo(yearSlider.lowerSliderPos, yearSlider.upperSliderPos) + } + } + + // TODO: move some disabled look/animation to the button itself + opacity: enabled ? 1 : 0.3 + Behavior on opacity { + NumberAnimation { duration: 200 } + } } } } diff --git a/data/qml/stations/StationCreatorPage1.qml b/data/qml/stations/StationCreatorPage1.qml index 826d69d691..ca1aed727e 100644 --- a/data/qml/stations/StationCreatorPage1.qml +++ b/data/qml/stations/StationCreatorPage1.qml @@ -15,6 +15,7 @@ Item { anchors.centerIn: parent width: root.width * 9 / 10 height: cellHeight + interactive: false cellWidth: (width - 1) / 3 cellHeight: cellWidth //* 10 / 16 diff --git a/data/qml/stations/StationItem.qml b/data/qml/stations/StationItem.qml index 9d7804a0a9..77bec8c46d 100644 --- a/data/qml/stations/StationItem.qml +++ b/data/qml/stations/StationItem.qml @@ -65,6 +65,7 @@ Item { anchors.centerIn: parent height: defaultFontHeight * 4 width: height +// count: 12 opacity: mainView.loading ? 1 : 0 running: mainView.loading diff --git a/data/qml/tomahawkimports/DoubleSlider.qml b/data/qml/tomahawkimports/DoubleSlider.qml index 08061b96e2..2ac2c64a8d 100644 --- a/data/qml/tomahawkimports/DoubleSlider.qml +++ b/data/qml/tomahawkimports/DoubleSlider.qml @@ -28,8 +28,8 @@ Item { property int steps: root.max - root.min + 1 - property int sliderHeight: root.height - property int sliderWidth: root.height / 2 + property int sliderHeight: root.height / 3 + property int sliderWidth: sliderHeight / 2 } Row { @@ -47,7 +47,7 @@ Item { Item { id: sliderRect - height: root.height + height: root.height / 4 property int maxWidth: parent.width - (minText.visible ? minText.width : 0) - (maxText.visible ? maxText.width : 0) - parent.spacing * 2 width: maxWidth - (maxWidth % priv.steps) anchors.horizontalCenter: parent.horizontalCenter @@ -64,7 +64,7 @@ Item { Rectangle { id: sliderBase - height: root.height / 1.5 + height: parent.height width: parent.width + defaultFontHeight * 1.5 color: "white" radius: height / 2 @@ -131,7 +131,7 @@ Item { id: lowerSlider height: priv.sliderHeight width: priv.sliderWidth - anchors.top: root.top + anchors.verticalCenter: sliderBase.verticalCenter radius: height/4 border.color: "black" border.width: 2 @@ -170,9 +170,9 @@ Item { Rectangle { id: upperSlider - height: root.height - width: height / 2 - anchors.top: root.top + height: priv.sliderHeight + width: priv.sliderWidth + anchors.verticalCenter: sliderBase.verticalCenter radius: height / 4 border.color: "black" border.width: 2 diff --git a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h index 3a6d7fbdfc..4eb217ee45 100644 --- a/src/libtomahawk/playlist/dynamic/GeneratorInterface.h +++ b/src/libtomahawk/playlist/dynamic/GeneratorInterface.h @@ -114,6 +114,8 @@ class DLLEXPORT GeneratorInterface : public QObject virtual bool startFromTrack( const Tomahawk::query_ptr& query ) = 0; virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ) = 0; virtual bool startFromGenre( const QString& genre ) = 0; + virtual bool startFromYear( int year ) = 0; + virtual bool startFromTo( int yearFrom, int yearTo) = 0; signals: void error( const QString& title, const QString& body); diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 9edac4ccc8..056a4b526e 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -309,6 +309,18 @@ EchonestGenerator::startFromGenre( const QString& genre ) return true; } +bool EchonestGenerator::startFromYear(int year) +{ + //TODO: libechonest doesn't support filtering for year yet... + return false; +} + +bool EchonestGenerator::startFromTo(int yearFrom, int yearTo) +{ + //TODO: libechonest doesn't support filtering for year yet... + return false; +} + void EchonestGenerator::doGenerate( const Echonest::DynamicPlaylist::PlaylistParams& paramsIn ) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h index 4375a5153b..7fb77cd432 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.h @@ -83,6 +83,8 @@ class DLLEXPORT EchonestGenerator : public GeneratorInterface virtual bool startFromTrack( const Tomahawk::query_ptr& query ); virtual bool startFromArtist( const Tomahawk::artist_ptr& artist ); virtual bool startFromGenre( const QString& genre ); + virtual bool startFromYear( int year ); + virtual bool startFromTo( int yearFrom, int yearTo ); static QStringList styles(); static QStringList moods(); diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 0b5c1edd4e..3c783d3ca7 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -168,6 +168,26 @@ void DynamicQmlWidget::startStationFromGenre(const QString &genre) emit configuredChanged(); } +void DynamicQmlWidget::startStationFromYear(int year) +{ + tDebug() << "should start startion from year" << year; + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromYear( year ); + emit loadingChanged(); + emit configuredChanged(); +} + +void DynamicQmlWidget::startStationFromTo(int yearFrom, int yearTo) +{ + tDebug() << "should start startion from years" << yearFrom << "to" << yearTo; + m_model->clear(); + m_playNextResolved = true; + m_playlist->generator()->startFromTo( yearFrom, yearTo ); + emit loadingChanged(); + emit configuredChanged(); +} + void DynamicQmlWidget::currentIndexChanged() { tDebug() << "current index is" << m_model->currentItem().row(); diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index c5a0ceac79..bf77e4b78c 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -74,6 +74,8 @@ public slots: void pause(); void startStationFromArtist(const QString &artist); void startStationFromGenre(const QString &genre); + void startStationFromYear(int year); + void startStationFromTo(int yearFrom, int yearTo); private slots: void currentIndexChanged(); From c2f3cff83ad8b56d81c7dfca668f3beb18bca3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Sun, 19 May 2013 12:05:23 +0200 Subject: [PATCH 051/565] Bump charts and newreleases version --- src/infoplugins/generic/charts/ChartsPlugin.cpp | 2 +- src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infoplugins/generic/charts/ChartsPlugin.cpp b/src/infoplugins/generic/charts/ChartsPlugin.cpp index 0a77d1695e..dd76dc1f7f 100644 --- a/src/infoplugins/generic/charts/ChartsPlugin.cpp +++ b/src/infoplugins/generic/charts/ChartsPlugin.cpp @@ -55,7 +55,7 @@ ChartsPlugin::ChartsPlugin() { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QThread::currentThread(); - m_chartVersion = "2.6.5"; + m_chartVersion = "2.6.6"; m_supportedGetTypes << InfoChart << InfoChartCapabilities; m_cacheIdentifier = TomahawkUtils::md5( QString("ChartsPlugin" + m_chartVersion ).toUtf8() ); } diff --git a/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp index 75992ba6a8..f6296268f0 100644 --- a/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp +++ b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp @@ -64,7 +64,7 @@ NewReleasesPlugin::NewReleasesPlugin() : InfoPlugin() , m_nrFetchJobs( 0 ) { - m_nrVersion = "0.5"; + m_nrVersion = "0.5.1"; m_supportedGetTypes << InfoNewReleaseCapabilities << InfoNewRelease; } From c3995a0cc8459da3d86fec2962dc7dd211652e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Sun, 19 May 2013 12:35:10 +0200 Subject: [PATCH 052/565] Sort by rank if possible --- .../generic/newreleases/NewReleasesPlugin.cpp | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp index f6296268f0..2ce6f532e2 100644 --- a/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp +++ b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp @@ -48,15 +48,21 @@ using namespace Tomahawk::InfoSystem; bool newReleaseSort( const InfoStringHash& left, const InfoStringHash& right ) { - if ( !left.contains( "date" ) || !right.contains( "date" ) ) + if ( left.contains( "rank" ) && right.contains( "rank" ) ) { - return true; + const int lRank = left[ "rank" ].toInt(); + const int rRank = right[ "rank" ].toInt(); + return lRank < rRank; } - const QDate lDate = QDate::fromString( left[ "date" ], "yyyy-MM-dd" ); - const QDate rDate = QDate::fromString( right[ "date" ], "yyyy-MM-dd" ); + if ( left.contains( "date" ) && right.contains( "date" ) ) + { + const QDate lDate = QDate::fromString( left[ "date" ], "yyyy-MM-dd" ); + const QDate rDate = QDate::fromString( right[ "date" ], "yyyy-MM-dd" ); + return lDate > rDate; + } - return lDate > rDate; + return true; } @@ -64,7 +70,7 @@ NewReleasesPlugin::NewReleasesPlugin() : InfoPlugin() , m_nrFetchJobs( 0 ) { - m_nrVersion = "0.5.1"; + m_nrVersion = "0.5.2"; m_supportedGetTypes << InfoNewReleaseCapabilities << InfoNewRelease; } @@ -685,11 +691,12 @@ NewReleasesPlugin::nrReturned() const QString album = albumMap.value( "album" ).toString(); const QString artist = albumMap.value( "artist" ).toString(); const QString date = albumMap.value( "date" ).toString(); - + const QString rank = albumMap.value( "rank" ).toString(); Tomahawk::InfoSystem::InfoStringHash pair; pair[ "artist" ] = artist; pair[ "album" ] = album; pair[ "date" ] = date; + pair[ "rank" ] = rank; newreleases.append( pair ); } } From 8c5ec2bc23aadc85f7f923925083d898979002d7 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Mon, 20 May 2013 02:16:48 +0200 Subject: [PATCH 053/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 2 +- lang/tomahawk_bg.ts | 2 +- lang/tomahawk_bn_IN.ts | 2 +- lang/tomahawk_ca.ts | 2 +- lang/tomahawk_ca@valencia.ts | 2 +- lang/tomahawk_cs.ts | 2 +- lang/tomahawk_da.ts | 2 +- lang/tomahawk_de.ts | 2 +- lang/tomahawk_el.ts | 2 +- lang/tomahawk_en.ts | 2 +- lang/tomahawk_es.ts | 2 +- lang/tomahawk_fi.ts | 2 +- lang/tomahawk_fr.ts | 2 +- lang/tomahawk_gl.ts | 2 +- lang/tomahawk_hi_IN.ts | 2 +- lang/tomahawk_hu.ts | 2 +- lang/tomahawk_id.ts | 2 +- lang/tomahawk_it.ts | 2 +- lang/tomahawk_ja.ts | 2 +- lang/tomahawk_lt.ts | 2 +- lang/tomahawk_pl.ts | 2 +- lang/tomahawk_pt_BR.ts | 2 +- lang/tomahawk_ru.ts | 2 +- lang/tomahawk_sv.ts | 2 +- lang/tomahawk_tr.ts | 2 +- lang/tomahawk_zh_CN.ts | 2 +- lang/tomahawk_zh_TW.ts | 2 +- 27 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 60878f0420..622a57162c 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -3260,7 +3260,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums ألبومات diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 5f12acfd3c..a45e0ca80c 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -3273,7 +3273,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Албуми diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index ee67f2413f..8318447cb0 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 1ca18fee4d..2e9795e5bd 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -3261,7 +3261,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Àlbums diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 0bdbf62a3b..82efcb2cb7 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -3261,7 +3261,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Àlbums diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index b78c38dcff..5c0bab587b 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -3259,7 +3259,7 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Alba diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index b5cc27fc92..1592c05f5c 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -3249,7 +3249,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index e3d9cfe411..76298cb73b 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -3254,7 +3254,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Alben diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index b44729007e..388dc413d1 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -3260,7 +3260,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Άλμπουμ diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 4291f4b6f0..4a7eef25fc 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -3262,7 +3262,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albums diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index e53245f2b6..e2ee43c8f6 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -3262,7 +3262,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Álbumes diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 1840dbb022..da6e387263 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -3265,7 +3265,7 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumit diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 8148f319dc..8a65d8c5bd 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -3259,7 +3259,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albums diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 2b3f70a01f..741aadcf1b 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -3261,7 +3261,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Álbums diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index a71ead5100..6c90361ea9 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 6bfaed5c8d..e7bf261834 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumok diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 9cabf4cb22..12b2e7c839 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index f9c87ef09c..f9a505b15d 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Album diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index e4340b18c0..b2c27dde2d 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -3259,7 +3259,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums アルバム diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 5be6b9f0a8..d9fb70fb8f 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumai diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 5e83638649..d646ecfac5 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -3256,7 +3256,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumy diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 7fb19ea8b8..feb1fe9ebd 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -3256,7 +3256,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Álbuns diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 255b590dd5..6fb44974c3 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -3263,7 +3263,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Альбом diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 8e20123aeb..af2d2517b8 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -3260,7 +3260,7 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Album diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 1b6350ee58..3f068e8231 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index c496660335..89cd6a49c9 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -3257,7 +3257,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums 专辑 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 2237eb43be..13d17df7d8 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -3247,7 +3247,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums 專輯 From 02dee0ec0673490b26ed6e61b26fc2c55581e247 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 20 May 2013 13:42:18 +0200 Subject: [PATCH 054/565] * Don't retry on empty SQL error. --- src/libtomahawk/database/TomahawkSqlQuery.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/database/TomahawkSqlQuery.cpp b/src/libtomahawk/database/TomahawkSqlQuery.cpp index 2591b18b1e..540e5dc76e 100644 --- a/src/libtomahawk/database/TomahawkSqlQuery.cpp +++ b/src/libtomahawk/database/TomahawkSqlQuery.cpp @@ -127,7 +127,7 @@ TomahawkSqlQuery::commitTransaction() log = true; #endif if ( log ) - tLog( LOGSQL ) << "TomahawkSqlQuery::commitTransaction running in thread " << QThread::currentThread(); + tLog( LOGSQL ) << "TomahawkSqlQuery::commitTransaction running in thread" << QThread::currentThread(); unsigned int retries = 0; while ( !m_db.commit() && ++retries < 10 ) @@ -149,7 +149,8 @@ TomahawkSqlQuery::showError() tLog() << endl << "*** DATABASE ERROR ***" << endl << lastQuery() << endl << "boundValues:" << boundValues() << endl - << lastError().text() << endl; + << lastError().text() << endl + << lastError().databaseText() << endl; Q_ASSERT( false ); } @@ -160,5 +161,5 @@ TomahawkSqlQuery::isBusyError( const QSqlError& error ) const { const QString text = error.text().trimmed().toLower(); - return ( text.contains( "locked" ) || text.contains( "busy" ) || text.isEmpty() ); + return ( text.contains( "locked" ) || text.contains( "busy" ) ); } From 045e4df526cd7a8d455b56df100ef29acc551593 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 20 May 2013 13:43:04 +0200 Subject: [PATCH 055/565] * DbCmd_SetDynamicPlaylistRevision should check if its parent's exec() succeeded. --- ...baseCommand_SetDynamicPlaylistRevision.cpp | 21 ++++++++++++------- .../DatabaseCommand_SetPlaylistRevision.cpp | 5 ++++- .../DatabaseCommand_SetPlaylistRevision.h | 1 + 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp index 3498d2c160..5071871dfc 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp @@ -75,6 +75,7 @@ DatabaseCommand_SetDynamicPlaylistRevision::controlsV() void DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook() { + tDebug() << Q_FUNC_INFO; if ( source().isNull() || source()->dbCollection().isNull() ) { tDebug() << "Source has gone offline, not emitting to GUI."; @@ -90,19 +91,23 @@ DatabaseCommand_SetDynamicPlaylistRevision::postCommitHook() tLog() << "Postcommitting this playlist:" << playlistguid() << source().isNull(); // private, but we are a friend. will recall itself in its own thread: + DynamicPlaylist* rawPl = 0; dynplaylist_ptr playlist = source()->dbCollection()->autoPlaylist( playlistguid() ); - if ( playlist.isNull() ) + if ( !playlist ) playlist = source()->dbCollection()->station( playlistguid() ); - // UGH we don't have a sharedptr from DynamicPlaylist+ - DynamicPlaylist* rawPl = playlist.data(); - if( playlist.isNull() ) // if it's neither an auto or station, it must not be auto-loaded, so we MUST have been told about it directly + if ( playlist ) + rawPl = playlist.data(); + else + { + // if it's neither an auto or station, it must not be auto-loaded, so we MUST have been told about it directly rawPl = m_playlist; + } - if ( rawPl == 0 ) + if ( !rawPl ) { - tLog() <<"Got null playlist with guid:" << playlistguid() << "from source and collection:" << source()->friendlyName() << source()->dbCollection()->name() << "and mode is static?:" << (m_mode == Static); - Q_ASSERT( false ); + tLog() << "Got null playlist with guid:" << playlistguid() << "from source and collection:" << source()->friendlyName() << source()->dbCollection()->name() << "and mode is static?:" << (m_mode == Static); +// Q_ASSERT( false ); return; } @@ -156,6 +161,8 @@ void DatabaseCommand_SetDynamicPlaylistRevision::exec( DatabaseImpl* lib ) { DatabaseCommand_SetPlaylistRevision::exec( lib ); + if ( m_failed ) + return; QVariantList newcontrols; foreach( const QVariant& v, m_controls ) diff --git a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp index 128ec5f305..add79ce73e 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp @@ -38,6 +38,7 @@ DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision( const QList& addedentries, const QList& entries ) : DatabaseCommandLoggable( s ) + , m_failed( false ) , m_applied( false ) , m_newrev( newrev ) , m_oldrev( oldrev ) @@ -66,6 +67,7 @@ DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision( const QStringList& orderedguids, const QList& entriesToUpdate ) : DatabaseCommandLoggable( s ) + , m_failed( false ) , m_applied( false ) , m_newrev( newrev ) , m_oldrev( oldrev ) @@ -87,7 +89,7 @@ DatabaseCommand_SetPlaylistRevision::DatabaseCommand_SetPlaylistRevision( void DatabaseCommand_SetPlaylistRevision::postCommitHook() { - qDebug() << Q_FUNC_INFO; + tDebug() << Q_FUNC_INFO; if ( m_localOnly ) return; @@ -136,6 +138,7 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib ) { tDebug() << "ERROR: No such playlist:" << m_playlistguid << currentRevision << source()->friendlyName() << source()->id(); // Q_ASSERT_X( false, "DatabaseCommand_SetPlaylistRevision::exec", "No such playlist, WTF?" ); + m_failed = true; return; } diff --git a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.h b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.h index b1c2fd8415..23a5db6ae0 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.h +++ b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.h @@ -113,6 +113,7 @@ Q_PROPERTY( bool metadataUpdate READ metadataUpdate WRITE setMetadataUpdat QVariantList orderedguids() const { return m_orderedguids; } protected: + bool m_failed; bool m_applied; QStringList m_previous_rev_orderedguids; QString m_playlistguid; From 7ca0786f02b37e2607b607a813609b7969f6fe9b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 20 May 2013 20:05:56 +0200 Subject: [PATCH 056/565] * Need to actually load controls. --- .../database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp index 5ad0dc4fb3..5812178768 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadDynamicPlaylistEntries.cpp @@ -71,8 +71,8 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) type = controlsQuery.value( 3 ).toString(); mode = static_cast( controlsQuery.value( 2 ).toInt() ); -/* QStringList controlIds = v.toStringList(); -// qDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1); + QStringList controlIds = v.toStringList(); + tDebug() << "Got controls in dynamic playlist, loading:" << controlIds << controlsQuery.value(1); foreach( const QString& controlId, controlIds ) { TomahawkSqlQuery controlQuery = dbi->newquery(); @@ -91,7 +91,7 @@ DatabaseCommand_LoadDynamicPlaylistEntries::exec( DatabaseImpl* dbi ) c[ "input" ] = controlQuery.value( 2 ).toString(); controls << c; } - }*/ + } } else { From e35b2bcf43b515937ab89e8fc7ad3e9454c6433d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Tue, 21 May 2013 14:56:29 +0200 Subject: [PATCH 057/565] * Show QSqlError's number() when a query fails. --- src/libtomahawk/database/TomahawkSqlQuery.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/database/TomahawkSqlQuery.cpp b/src/libtomahawk/database/TomahawkSqlQuery.cpp index 540e5dc76e..97295b3aef 100644 --- a/src/libtomahawk/database/TomahawkSqlQuery.cpp +++ b/src/libtomahawk/database/TomahawkSqlQuery.cpp @@ -149,6 +149,7 @@ TomahawkSqlQuery::showError() tLog() << endl << "*** DATABASE ERROR ***" << endl << lastQuery() << endl << "boundValues:" << boundValues() << endl + << lastError().number() << endl << lastError().text() << endl << lastError().databaseText() << endl; From 7c963e4c3780c225a9bc4be43f993b35416b9fc0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Tue, 21 May 2013 15:10:44 +0200 Subject: [PATCH 058/565] * Try to work-around translated error messages. Loathing. --- src/libtomahawk/database/TomahawkSqlQuery.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/database/TomahawkSqlQuery.cpp b/src/libtomahawk/database/TomahawkSqlQuery.cpp index 97295b3aef..f7ca2ed90e 100644 --- a/src/libtomahawk/database/TomahawkSqlQuery.cpp +++ b/src/libtomahawk/database/TomahawkSqlQuery.cpp @@ -23,6 +23,7 @@ #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +#include #include #include #include @@ -85,8 +86,8 @@ TomahawkSqlQuery::exec() unsigned int retries = 0; while ( !QSqlQuery::exec() && ++retries < 10 ) { - if ( lastError().text().toLower().contains( "no query" ) || - lastError().text().toLower().contains( "parameter count mismatch" ) ) + if ( lastError().text() == QCoreApplication::translate( "QSQLiteResult", "No query" ) || + lastError().text() == QCoreApplication::translate( "QSQLiteResult", "Parameter count mismatch" ) ) { tDebug() << Q_FUNC_INFO << "Re-preparing query!"; @@ -162,5 +163,5 @@ TomahawkSqlQuery::isBusyError( const QSqlError& error ) const { const QString text = error.text().trimmed().toLower(); - return ( text.contains( "locked" ) || text.contains( "busy" ) ); + return ( error.number() == 5 || error.number() == 6 || text.contains( "locked" ) || text.contains( "busy" ) ); } From bf49d2bec5e7c5757314da61b9b2218e08f51860 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Wed, 22 May 2013 02:16:40 +0200 Subject: [PATCH 059/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 13 +++++++++++++ lang/tomahawk_bg.ts | 13 +++++++++++++ lang/tomahawk_bn_IN.ts | 13 +++++++++++++ lang/tomahawk_ca.ts | 13 +++++++++++++ lang/tomahawk_ca@valencia.ts | 13 +++++++++++++ lang/tomahawk_cs.ts | 13 +++++++++++++ lang/tomahawk_da.ts | 13 +++++++++++++ lang/tomahawk_de.ts | 13 +++++++++++++ lang/tomahawk_el.ts | 13 +++++++++++++ lang/tomahawk_en.ts | 13 +++++++++++++ lang/tomahawk_es.ts | 13 +++++++++++++ lang/tomahawk_fi.ts | 13 +++++++++++++ lang/tomahawk_fr.ts | 13 +++++++++++++ lang/tomahawk_gl.ts | 13 +++++++++++++ lang/tomahawk_hi_IN.ts | 13 +++++++++++++ lang/tomahawk_hu.ts | 13 +++++++++++++ lang/tomahawk_id.ts | 13 +++++++++++++ lang/tomahawk_it.ts | 13 +++++++++++++ lang/tomahawk_ja.ts | 13 +++++++++++++ lang/tomahawk_lt.ts | 13 +++++++++++++ lang/tomahawk_pl.ts | 13 +++++++++++++ lang/tomahawk_pt_BR.ts | 13 +++++++++++++ lang/tomahawk_ru.ts | 13 +++++++++++++ lang/tomahawk_sv.ts | 13 +++++++++++++ lang/tomahawk_tr.ts | 13 +++++++++++++ lang/tomahawk_zh_CN.ts | 13 +++++++++++++ lang/tomahawk_zh_TW.ts | 13 +++++++++++++ 27 files changed, 351 insertions(+) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 622a57162c..97ab0e988d 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1385,6 +1385,19 @@ connect and stream from you? تكوين %1 + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index a45e0ca80c..f844545ec5 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1393,6 +1393,19 @@ Tomahawk създаде доклад относно това и изпращай %1 Настройки + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 8318447cb0..5909078db5 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1382,6 +1382,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 2e9795e5bd..092cd8b92a 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1383,6 +1383,19 @@ connect and stream from you? %1 configuració + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 82efcb2cb7..30f4ff73ca 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1383,6 +1383,19 @@ connect and stream from you? %1 configuració + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 5c0bab587b..4ae88f6c74 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1384,6 +1384,19 @@ se s vámi spojil? %1 nastavení + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 1592c05f5c..8cde56d5d5 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1383,6 +1383,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 76298cb73b..0eb162f42f 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1384,6 +1384,19 @@ erlauben sich mit dir zu verbinden? %1 Konfiguration + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 388dc413d1..d7703d935d 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1383,6 +1383,19 @@ connect and stream from you? %1 Ρυθμίσεις + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 4a7eef25fc..4a3ac37f88 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1384,6 +1384,19 @@ connect and stream from you? %1 Configuration + + QSQLiteResult + + + No query + No query + + + + Parameter count mismatch + Parameter count mismatch + + QtScriptResolver diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index e2ee43c8f6..9d07ba9335 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1384,6 +1384,19 @@ conectarse a usted y transmitir música? Configuración de %1 + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index da6e387263..ac80bdcd15 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1384,6 +1384,19 @@ yhdistää ja toistaa sinulta virtaa? %1-asetukset + + QSQLiteResult + + + No query + Ei kyselyä + + + + Parameter count mismatch + Parametrien määrä ei täsmää + + QtScriptResolver diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 8a65d8c5bd..6ab7611dbe 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1384,6 +1384,19 @@ de se connecter et streamer de vous? %1 Configuration + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 741aadcf1b..ba925c5e58 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1383,6 +1383,19 @@ connect and stream from you? %1 Configuración + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 6c90361ea9..1a69cfaf44 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1382,6 +1382,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index e7bf261834..1944926030 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1382,6 +1382,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 12b2e7c839..bb46f01147 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1382,6 +1382,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index f9a505b15d..be825d197e 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1382,6 +1382,19 @@ connect and stream from you? %1 Configurazione + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index b2c27dde2d..6d750f86b5 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1384,6 +1384,19 @@ connect and stream from you? %1設定 + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index d9fb70fb8f..abead2c826 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1382,6 +1382,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index d646ecfac5..036bbfb8a3 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1384,6 +1384,19 @@ połączyć się i strumieniować od ciebie? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index feb1fe9ebd..7c32be70f3 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1384,6 +1384,19 @@ se conecte e faça o stream de você? Configuração de %1 + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 6fb44974c3..522fdd0f0c 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1387,6 +1387,19 @@ connect and stream from you? %1 Настройка + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index af2d2517b8..fe5ed49e89 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1384,6 +1384,19 @@ ansluta och strömma från dig? %1 konfiguration + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 3f068e8231..ae7769c816 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1382,6 +1382,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 89cd6a49c9..23ab6ad4c5 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1383,6 +1383,19 @@ connect and stream from you? 配置 %1 + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 13d17df7d8..c29a20bacb 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1382,6 +1382,19 @@ connect and stream from you? + + QSQLiteResult + + + No query + + + + + Parameter count mismatch + + + QtScriptResolver From 3ded897d557378e52399c763aa5ac6e1ba05f10a Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 22 May 2013 16:56:26 +0200 Subject: [PATCH 060/565] Remove TomahawkVersion.h and QtCrypto includes from public QtScriptResolver header --- src/libtomahawk/resolvers/QtScriptResolver.cpp | 6 ++++++ src/libtomahawk/resolvers/QtScriptResolver.h | 8 -------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/resolvers/QtScriptResolver.cpp b/src/libtomahawk/resolvers/QtScriptResolver.cpp index fdac7206e6..3c0990ef16 100644 --- a/src/libtomahawk/resolvers/QtScriptResolver.cpp +++ b/src/libtomahawk/resolvers/QtScriptResolver.cpp @@ -27,6 +27,7 @@ #include "ScriptCollection.h" #include "SourceList.h" #include "TomahawkSettings.h" +#include "TomahawkVersion.h" #include "accounts/AccountConfigWidget.h" @@ -48,6 +49,11 @@ #include #include +#ifdef QCA2_FOUND +#include +#endif + + #include // FIXME: bloody hack, remove this for 0.3 diff --git a/src/libtomahawk/resolvers/QtScriptResolver.h b/src/libtomahawk/resolvers/QtScriptResolver.h index 1e7d5ebfd0..226b10b5d5 100644 --- a/src/libtomahawk/resolvers/QtScriptResolver.h +++ b/src/libtomahawk/resolvers/QtScriptResolver.h @@ -25,7 +25,6 @@ #include "Query.h" #include "utils/TomahawkUtils.h" #include "config.h" -#include "TomahawkVersion.h" #include "utils/Logger.h" #include @@ -34,10 +33,6 @@ #include #include -#ifdef QCA2_FOUND -#include -#endif - #include "DllMacro.h" class QtScriptResolver; @@ -89,9 +84,6 @@ public slots: bool m_urlCallbackIsAsync; QVariantMap m_resolverConfig; QtScriptResolver* m_resolver; -#ifdef QCA2_FOUND - QCA::Initializer m_qcaInit; -#endif }; class DLLEXPORT ScriptEngine : public QWebPage From 4beee033aca54ea1a5570c7d62ba3e7643b2c326 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 22 May 2013 16:57:59 +0200 Subject: [PATCH 061/565] hack until Qt5: Make external modules/projects automatically act as gui modules/projects --- TomahawkUse.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TomahawkUse.cmake b/TomahawkUse.cmake index 8b465457d3..7bbc1b2e54 100644 --- a/TomahawkUse.cmake +++ b/TomahawkUse.cmake @@ -3,8 +3,8 @@ find_package(Qt4 COMPONENTS QtNetwork QtCore QtGui QtSql QtDeclarative REQUIRED) include( ${QT_USE_FILE} ) -set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork" "QtDeclarative") -if(BUILD_GUI) +set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork") +if(BUILD_GUI OR NOT DEFINED BUILD_GUI) list(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" "QtUiTools" "QtSvg" "QtDeclarative") endif() From 1bd7c18715a9d19471e9fed757118aa32b2138e5 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Sun, 12 May 2013 12:36:47 +0200 Subject: [PATCH 062/565] Build with QtKeychain. --- CMakeLists.txt | 10 ++++++---- src/libtomahawk/CMakeLists.txt | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7d7967021..000c973e65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -230,14 +230,18 @@ macro_log_feature(QTWEETLIB_FOUND "QTweetLib" "Qt Twitter Library" "https://gith macro_optional_find_package(LibLastFm 1.0.0) macro_log_feature(LIBLASTFM_FOUND "liblastfm" "Qt library for the Last.fm webservices" "https://github.com/lastfm/liblastfm" TRUE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork") +macro_optional_find_package(QtKeychain 0.1.0) +macro_log_feature(QTKEYCHAIN_FOUND "QtKeychain" "Provides support for secure credentials storage" "https://github.com/frankosterfeld/qtkeychain" TRUE "" "") + # we need pthreads too macro_optional_find_package(Threads) macro_log_feature(THREADS_FOUND "Threads" "Threading Library" "" TRUE "" "Platform specific library for threading") -IF( WIN32 ) +### QtSparkle +if( WIN32 ) macro_optional_find_package(QtSparkle) macro_log_feature(QTSPARKLE_FOUND "qtsparkle" "Library for creating auto updaters written in Qt" "https://github.com/davidsansome/qtsparkle" FALSE "" "") -ENDIF( WIN32 ) +endif( WIN32 ) #TODO: support external qxt set(QXTWEB_FOUND TRUE) @@ -264,8 +268,6 @@ add_subdirectory(${THIRDPARTY_DIR}/libportfwd) #### submodules end - - macro_optional_find_package(KDE4) macro_optional_find_package(KDE4Installed) diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 6eb4f0b111..c7ab0ce12f 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -391,6 +391,7 @@ include_directories( ${LIBPORTFWD_INCLUDE_DIR} ${QuaZip_INCLUDE_DIR} + ${QTKEYCHAIN_INCLUDE_DIRS} ) IF(QCA2_FOUND) @@ -492,6 +493,7 @@ TARGET_LINK_LIBRARIES( tomahawklib ${TAGLIB_LIBRARIES} ${CLUCENE_LIBRARIES} ${ECHONEST_LIBRARIES} + ${QTKEYCHAIN_LIBRARIES} ${QT_QTSQL_LIBRARY} ${QT_QTUITOOLS_LIBRARY} ${QT_QTGUI_LIBRARY} From 17f0606139deea3c31ef9e6b389691f8a818b8b9 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Tue, 21 May 2013 19:08:40 +0200 Subject: [PATCH 063/565] Upgrade config. --- src/libtomahawk/TomahawkSettings.cpp | 38 ++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 1e5b79b09c..5895acb388 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -20,8 +20,6 @@ #include "TomahawkSettings.h" -#include - #include "Source.h" #include "PlaylistInterface.h" @@ -33,6 +31,9 @@ #include "playlist/PlaylistUpdaterInterface.h" #include "infosystem/InfoSystemCache.h" +#include +#include + using namespace Tomahawk; TomahawkSettings* TomahawkSettings::s_instance = 0; @@ -612,6 +613,39 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) const bool removed = dataFile.remove(); tDebug() << "Tried to remove echonest_stylesandmoods.dat, succeeded?" << removed; } + else if ( oldVersion == 14 ) + { + const QStringList accounts = value( "accounts/allaccounts" ).toStringList(); + tDebug() << "About to move these accounts to QtKeychain:" << accounts; + //Move storage of Credentials from QSettings to QtKeychain + foreach ( const QString& account, accounts ) + { + tDebug() << "beginGroup" << QString( "accounts/%1" ).arg( account ); + beginGroup( QString( "accounts/%1" ).arg( account ) ); + const QVariantHash creds = value( "credentials" ).toHash(); + + tDebug() << "creds:" << creds; + if ( !creds.isEmpty() ) + { + QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this ); + j->setKey( account ); + j->setAutoDelete( false ); + + QByteArray data; + QDataStream ds( &data, QIODevice::WriteOnly ); + ds << creds; + + j->setBinaryData( data ); + j->start(); + + qDebug() << "Migrating account credentials for account:" << account; + } + + remove( "credentials" ); + + endGroup(); + } + } } From c72316088366e90c9121ed8c45d59bce0e76acdf Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 22 May 2013 16:15:25 +0200 Subject: [PATCH 064/565] Added CredentialsManager as a QtKeychain credentials cache. --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/accounts/AccountManager.cpp | 24 +++- src/libtomahawk/accounts/AccountManager.h | 5 + .../accounts/CredentialsManager.cpp | 110 ++++++++++++++++++ src/libtomahawk/accounts/CredentialsManager.h | 74 ++++++++++++ 5 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 src/libtomahawk/accounts/CredentialsManager.cpp create mode 100644 src/libtomahawk/accounts/CredentialsManager.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index c7ab0ce12f..c14af0d48d 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -208,6 +208,7 @@ list(APPEND libSources accounts/Account.cpp accounts/AccountModel.cpp accounts/AccountModelFilterProxy.cpp + accounts/CredentialsManager.cpp accounts/ResolverAccount.cpp accounts/AccountDelegate.cpp accounts/DelegateConfigWrapper.cpp diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index 8b20bd7c5c..a267ec411e 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -18,6 +18,8 @@ */ #include "AccountManager.h" + +#include "CredentialsManager.h" #include "config.h" #include "SourceList.h" #include "TomahawkSettings.h" @@ -33,6 +35,7 @@ #include #include + namespace Tomahawk { @@ -86,7 +89,7 @@ AccountManager::init() m_accountFactories[ f->factoryId() ] = f; registerAccountFactoryForFilesystem( f ); - emit ready(); + emit ready(); //Notifies TomahawkApp to load the remaining AccountFactories, then Accounts from config } @@ -276,7 +279,22 @@ void AccountManager::loadFromConfig() { QStringList accountIds = TomahawkSettings::instance()->accounts(); + + qDebug() << "LOADING ALL CREDENTIALS" << accountIds; + + m_creds = new CredentialsManager( this ); + connect( m_creds, SIGNAL( ready() ), + this, SLOT( finishLoadingFromConfig() ) ); + m_creds->loadCredentials( accountIds ); +} + + +void +AccountManager::finishLoadingFromConfig() +{ + QStringList accountIds = m_creds->keys(); qDebug() << "LOADING ALL ACCOUNTS" << accountIds; + foreach ( const QString& accountId, accountIds ) { QString pluginFactory = factoryFromId( accountId ); @@ -498,6 +516,6 @@ AccountManager::onStateChanged( Account::ConnectionState state ) } -}; +} -}; +} diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 8f080676e5..7b3076dfb0 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -34,6 +34,8 @@ namespace Tomahawk namespace Accounts { +class CredentialsManager; + class DLLEXPORT AccountManager : public QObject { Q_OBJECT @@ -105,6 +107,7 @@ private slots: void init(); void onStateChanged( Tomahawk::Accounts::Account::ConnectionState state ); void onError( int code, const QString& msg ); + void finishLoadingFromConfig(); void onSettingsChanged(); @@ -117,6 +120,8 @@ private slots: Account* loadPlugin( const QString &accountId ); void hookupAccount( Account* ) const; + CredentialsManager* m_creds; + QList< Account* > m_accounts; QList< Account* > m_enabledAccounts; QList< Account* > m_connectedAccounts; diff --git a/src/libtomahawk/accounts/CredentialsManager.cpp b/src/libtomahawk/accounts/CredentialsManager.cpp new file mode 100644 index 0000000000..7217f5fbc1 --- /dev/null +++ b/src/libtomahawk/accounts/CredentialsManager.cpp @@ -0,0 +1,110 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "CredentialsManager.h" + +#include "utils/Logger.h" + +#include + +#include + +namespace Tomahawk +{ + +namespace Accounts +{ + + +CredentialsManager::CredentialsManager( QObject* parent ) + : QObject( parent ) +{ + +} + + +void +CredentialsManager::loadCredentials( QStringList keys ) +{ + foreach ( QString key, keys ) + { + QKeychain::ReadPasswordJob* j = new QKeychain::ReadPasswordJob( QLatin1String( "tomahawkaccounts" ), this ); + j->setKey( key ); + j->setAutoDelete( false ); +#if defined( Q_OS_UNIX ) && !defined( Q_OS_MAC ) + j->setInsecureFallback( true ); +#endif + connect( j, SIGNAL( finished( QKeychain::Job* ) ), + SLOT( keychainJobFinished( QKeychain::Job* ) ) ); + m_readJobs << j; + j->start(); + } + +} + + +QStringList +CredentialsManager::keys() const +{ + QStringList keys = m_credentials.keys(); + return keys; +} + + +void +CredentialsManager::keychainJobFinished( QKeychain::Job* j ) +{ + if ( j->error() == QKeychain::NoError ) + { + if ( QKeychain::ReadPasswordJob* readJob = qobject_cast< QKeychain::ReadPasswordJob* >( j ) ) + { + tDebug() << "QtKeychain readJob for" << readJob->key() << "finished without errors"; + + QVariantHash creds; + QDataStream dataStream( readJob->binaryData() ); + dataStream >> creds; + + m_credentials.insert( readJob->key(), creds ); + + m_readJobs.removeAll( readJob ); + + if ( m_readJobs.isEmpty() ) + { + emit ready(); + } + } + } + else if ( QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ) ) + { + tLog() << Q_FUNC_INFO << "QtKeychain writeJob finished"; + } + else if ( QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ) ) + { + tLog() << Q_FUNC_INFO << "QtKeychain deleteJob finished"; + } + else + { + tDebug() << "QtKeychain job finished with error:" << j->error() << j->errorString(); + } + j->deleteLater(); +} + + +} //namespace Accounts + +} //namespace Tomahawk diff --git a/src/libtomahawk/accounts/CredentialsManager.h b/src/libtomahawk/accounts/CredentialsManager.h new file mode 100644 index 0000000000..c0b19e5337 --- /dev/null +++ b/src/libtomahawk/accounts/CredentialsManager.h @@ -0,0 +1,74 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef CREDENTIALSMANAGER_H +#define CREDENTIALSMANAGER_H + +#include +#include + + +namespace QKeychain +{ +class Job; +class ReadPasswordJob; +class WritePasswordJob; +} + + +namespace Tomahawk +{ + +namespace Accounts +{ + +/** + * @brief The CredentialsManager class holds an in-memory cache of whatever credentials are stored + * in the system's QtKeychain-accessible credentials storage. + * After instantiating the class, loadCredentials should be called, and this is the only time a read + * operation from QtKeychain is performed. When CredentialsManager emits ready(), it can be used for + * all other operations. The only QtKeychain operations performed at any time after startup are + * write and delete. + * This ensures an illusion of synchronous operations for Tomahawk's Account classes, even though all + * QtKeychain jobs are async. + */ +class CredentialsManager : public QObject +{ + Q_OBJECT +public: + explicit CredentialsManager( QObject* parent = 0 ); + + void loadCredentials( QStringList keys ); + + QStringList keys() const; + +signals: + void ready(); + +private slots: + void keychainJobFinished( QKeychain::Job* ); + +private: + QHash< QString, QVariantHash > m_credentials; + QList< QKeychain::ReadPasswordJob* > m_readJobs; +}; + +} //namespace Accounts + +} //namespace Tomahawk +#endif // CREDENTIALSMANAGER_H From 481b16be89fc50ba1f2e79f87956c54a76e4f1fb Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 22 May 2013 17:42:10 +0200 Subject: [PATCH 065/565] Wait for both Servent and AccountManager to be ready for initSIP. --- src/TomahawkApp.cpp | 43 ++++++++++++--------- src/TomahawkApp.h | 2 +- src/libtomahawk/accounts/AccountManager.cpp | 7 +++- src/libtomahawk/accounts/AccountManager.h | 11 ++++-- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/TomahawkApp.cpp b/src/TomahawkApp.cpp index 8bee7cfc79..35a8f1f589 100644 --- a/src/TomahawkApp.cpp +++ b/src/TomahawkApp.cpp @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Leo Franchi * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -249,7 +250,8 @@ TomahawkApp::init() tDebug() << "Init AccountManager."; m_accountManager = QPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); - connect( m_accountManager.data(), SIGNAL( ready() ), SLOT( accountManagerReady() ) ); + connect( m_accountManager.data(), SIGNAL( readyForFactories() ), SLOT( initFactoriesForAccountManager() ) ); + connect( m_accountManager.data(), SIGNAL( readyForSip() ), SLOT( initSIP() ) ); Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); #ifndef ENABLE_HEADLESS @@ -616,13 +618,32 @@ TomahawkApp::initServent() } -// Called after Servent emits ready() +void +TomahawkApp::initFactoriesForAccountManager() +{ +#ifdef LIBLASTFM_FOUND + Tomahawk::Accounts::LastFmAccountFactory* lastfmFactory = new Tomahawk::Accounts::LastFmAccountFactory(); + m_accountManager.data()->addAccountFactory( lastfmFactory ); +#endif + + Tomahawk::Accounts::SpotifyAccountFactory* spotifyFactory = new Tomahawk::Accounts::SpotifyAccountFactory; + m_accountManager.data()->addAccountFactory( spotifyFactory ); + m_accountManager.data()->registerAccountFactoryForFilesystem( spotifyFactory ); + + Tomahawk::Accounts::AccountManager::instance()->loadFromConfig(); +} + + +// This method will be called twice during Tomahawk startup. +// We don't know which is going to be ready first, AccountManager or Servent, but this goes through +// only when both are. void TomahawkApp::initSIP() { tDebug() << Q_FUNC_INFO; //FIXME: jabber autoconnect is really more, now that there is sip -- should be renamed and/or split out of jabber-specific settings - if ( !arguments().contains( "--nosip" ) ) + if ( !arguments().contains( "--nosip" ) && + Servent::instance()->isReady() && Accounts::AccountManager::instance()->isReadyForSip() ) { tDebug( LOGINFO ) << "Connecting SIP classes"; Accounts::AccountManager::instance()->initSIP(); @@ -645,22 +666,6 @@ TomahawkApp::spotifyApiCheckFinished() } -void -TomahawkApp::accountManagerReady() -{ -#ifdef LIBLASTFM_FOUND - Tomahawk::Accounts::LastFmAccountFactory* lastfmFactory = new Tomahawk::Accounts::LastFmAccountFactory(); - m_accountManager.data()->addAccountFactory( lastfmFactory ); -#endif - - Tomahawk::Accounts::SpotifyAccountFactory* spotifyFactory = new Tomahawk::Accounts::SpotifyAccountFactory; - m_accountManager.data()->addAccountFactory( spotifyFactory ); - m_accountManager.data()->registerAccountFactoryForFilesystem( spotifyFactory ); - - Tomahawk::Accounts::AccountManager::instance()->loadFromConfig(); -} - - void TomahawkApp::activate() { diff --git a/src/TomahawkApp.h b/src/TomahawkApp.h index ed2e3f6326..761e63fdb9 100644 --- a/src/TomahawkApp.h +++ b/src/TomahawkApp.h @@ -111,9 +111,9 @@ private slots: void initServent(); void initSIP(); void initHTTP(); + void initFactoriesForAccountManager(); void spotifyApiCheckFinished(); - void accountManagerReady(); private: void registerMetaTypes(); diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index a267ec411e..8a855fa198 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,6 +56,7 @@ AccountManager::instance() AccountManager::AccountManager( QObject *parent ) : QObject( parent ) + , m_readyForSip( false ) { s_instance = this; @@ -89,7 +91,7 @@ AccountManager::init() m_accountFactories[ f->factoryId() ] = f; registerAccountFactoryForFilesystem( f ); - emit ready(); //Notifies TomahawkApp to load the remaining AccountFactories, then Accounts from config + emit readyForFactories(); //Notifies TomahawkApp to load the remaining AccountFactories, then Accounts from config } @@ -304,6 +306,9 @@ AccountManager::finishLoadingFromConfig() addAccount( account ); } } + + m_readyForSip = true; + emit readyForSip(); } diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 7b3076dfb0..598591f06f 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,7 +48,7 @@ class DLLEXPORT AccountManager : public QObject virtual ~AccountManager(); void loadFromConfig(); - void initSIP(); + void initSIP(); //only call this after isReadyForSip returns true void enableAccount( Account* account ); void disableAccount( Account* account ); @@ -84,7 +85,9 @@ class DLLEXPORT AccountManager : public QObject Account* zeroconfAccount() const; - bool isConnected() { return m_connected; } + bool isConnected() const { return m_connected; } + + bool isReadyForSip() const { return m_readyForSip; } public slots: void connectAll(); @@ -92,7 +95,8 @@ public slots: void toggleAccountsConnected(); signals: - void ready(); + void readyForFactories(); //this happens first, right before loading accounts from config + void readyForSip(); //then this, so TomahawkApp can call initSIP if Servent is ready void added( Tomahawk::Accounts::Account* ); void removed( Tomahawk::Accounts::Account* ); @@ -126,6 +130,7 @@ private slots: QList< Account* > m_enabledAccounts; QList< Account* > m_connectedAccounts; bool m_connected; + bool m_readyForSip; QHash< AccountType, QList< Account* > > m_accountsByAccountType; QHash< QString, AccountFactory* > m_accountFactories; From 812f8cb2507ccf7c5138cfde8932885aa781e49a Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 22 May 2013 18:49:14 +0200 Subject: [PATCH 066/565] Load Account credentials from CredentialsManager. --- src/libtomahawk/accounts/Account.cpp | 14 ++++++++--- src/libtomahawk/accounts/AccountManager.h | 3 ++- .../accounts/CredentialsManager.cpp | 25 +++++++++++++++++++ src/libtomahawk/accounts/CredentialsManager.h | 3 +++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/accounts/Account.cpp b/src/libtomahawk/accounts/Account.cpp index 9cbd308702..9c168c85cd 100644 --- a/src/libtomahawk/accounts/Account.cpp +++ b/src/libtomahawk/accounts/Account.cpp @@ -20,6 +20,8 @@ #include "Account.h" #include "TomahawkSettings.h" +#include "AccountManager.h" +#include "CredentialsManager.h" namespace Tomahawk { @@ -134,12 +136,14 @@ Account::syncConfig() s->beginGroup( "accounts/" + m_accountId ); s->setValue( "accountfriendlyname", m_accountFriendlyName ); s->setValue( "enabled", m_enabled ); - s->setValue( "credentials", m_credentials ); s->setValue( "configuration", m_configuration ); s->setValue( "acl", m_acl ); s->setValue( "types", m_types ); s->endGroup(); s->sync(); + + CredentialsManager* c = AccountManager::instance()->credentialsManager(); + c->setCredentials( m_accountId, m_credentials ); } @@ -151,11 +155,13 @@ Account::loadFromConfig( const QString& accountId ) s->beginGroup( "accounts/" + m_accountId ); m_accountFriendlyName = s->value( "accountfriendlyname", QString() ).toString(); m_enabled = s->value( "enabled", false ).toBool(); - m_credentials = s->value( "credentials", QVariantHash() ).toHash(); m_configuration = s->value( "configuration", QVariantHash() ).toHash(); m_acl = s->value( "acl", QVariantMap() ).toMap(); m_types = s->value( "types", QStringList() ).toStringList(); s->endGroup(); + + CredentialsManager* c = AccountManager::instance()->credentialsManager(); + m_credentials = c->credentials( m_accountId ); } @@ -166,12 +172,14 @@ Account::removeFromConfig() s->beginGroup( "accounts/" + m_accountId ); s->remove( "accountfriendlyname" ); s->remove( "enabled" ); - s->remove( "credentials" ); s->remove( "configuration" ); s->remove( "acl" ); s->remove( "types" ); s->endGroup(); s->remove( "accounts/" + m_accountId ); + + CredentialsManager* c = AccountManager::instance()->credentialsManager(); + c->setCredentials( m_accountId, QVariantHash() ); } diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 598591f06f..42a87eded8 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -86,9 +86,10 @@ class DLLEXPORT AccountManager : public QObject Account* zeroconfAccount() const; bool isConnected() const { return m_connected; } - bool isReadyForSip() const { return m_readyForSip; } + CredentialsManager* credentialsManager() const { return m_creds; } + public slots: void connectAll(); void disconnectAll(); diff --git a/src/libtomahawk/accounts/CredentialsManager.cpp b/src/libtomahawk/accounts/CredentialsManager.cpp index 7217f5fbc1..3952b63e31 100644 --- a/src/libtomahawk/accounts/CredentialsManager.cpp +++ b/src/libtomahawk/accounts/CredentialsManager.cpp @@ -66,6 +66,31 @@ CredentialsManager::keys() const } +QVariantHash +CredentialsManager::credentials( const QString& key ) const +{ + return m_credentials.value( key ); +} + + +void +CredentialsManager::setCredentials( const QString& key, const QVariantHash& value ) +{ + if ( value.isEmpty() ) + { + m_credentials.remove( key ); + + //TODO: delete job + } + else + { + m_credentials.insert( key, value ); + + //TODO: write job + } +} + + void CredentialsManager::keychainJobFinished( QKeychain::Job* j ) { diff --git a/src/libtomahawk/accounts/CredentialsManager.h b/src/libtomahawk/accounts/CredentialsManager.h index c0b19e5337..bf653a070d 100644 --- a/src/libtomahawk/accounts/CredentialsManager.h +++ b/src/libtomahawk/accounts/CredentialsManager.h @@ -57,6 +57,9 @@ class CredentialsManager : public QObject QStringList keys() const; + QVariantHash credentials( const QString& key ) const; + void setCredentials( const QString& key, const QVariantHash& value ); + signals: void ready(); From e790fab9ae5a8ede828288059fc360fca767528f Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 22 May 2013 19:36:34 +0200 Subject: [PATCH 067/565] Also write credentials to QtKeychain through CredentialsManager. --- .../accounts/CredentialsManager.cpp | 52 +++++++++++++++---- src/libtomahawk/accounts/CredentialsManager.h | 3 +- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/accounts/CredentialsManager.cpp b/src/libtomahawk/accounts/CredentialsManager.cpp index 3952b63e31..8f6dc9b4d2 100644 --- a/src/libtomahawk/accounts/CredentialsManager.cpp +++ b/src/libtomahawk/accounts/CredentialsManager.cpp @@ -24,6 +24,8 @@ #include +#define TOMAHAWK_KEYCHAINSVC QLatin1String("tomahawkaccounts") + namespace Tomahawk { @@ -43,7 +45,7 @@ CredentialsManager::loadCredentials( QStringList keys ) { foreach ( QString key, keys ) { - QKeychain::ReadPasswordJob* j = new QKeychain::ReadPasswordJob( QLatin1String( "tomahawkaccounts" ), this ); + QKeychain::ReadPasswordJob* j = new QKeychain::ReadPasswordJob( TOMAHAWK_KEYCHAINSVC, this ); j->setKey( key ); j->setAutoDelete( false ); #if defined( Q_OS_UNIX ) && !defined( Q_OS_MAC ) @@ -76,18 +78,46 @@ CredentialsManager::credentials( const QString& key ) const void CredentialsManager::setCredentials( const QString& key, const QVariantHash& value ) { + QMutexLocker locker( &m_mutex ); + + QKeychain::Job* j; if ( value.isEmpty() ) { + if ( !m_credentials.contains( key ) ) //if we don't have any credentials for this key, we bail + return; + m_credentials.remove( key ); - //TODO: delete job + QKeychain::DeletePasswordJob* dj = new QKeychain::DeletePasswordJob( TOMAHAWK_KEYCHAINSVC, this ); + dj->setKey( key ); + j = dj; } else { + if ( value == m_credentials.value( key ) ) //if the credentials haven't actually changed, we bail + return; + m_credentials.insert( key, value ); - //TODO: write job + QByteArray data; + { + QDataStream ds( &data, QIODevice::WriteOnly ); + ds << value; + } + + QKeychain::WritePasswordJob* wj = new QKeychain::WritePasswordJob( TOMAHAWK_KEYCHAINSVC, this ); + wj->setKey( key ); + wj->setBinaryData( data ); + j = wj; } + + j->setAutoDelete( false ); +#if defined( Q_OS_UNIX ) && !defined( Q_OS_MAC ) + j->setInsecureFallback( true ); +#endif + connect( j, SIGNAL( finished( QKeychain::Job* ) ), + SLOT( keychainJobFinished( QKeychain::Job* ) ) ); + j->start(); } @@ -113,14 +143,14 @@ CredentialsManager::keychainJobFinished( QKeychain::Job* j ) emit ready(); } } - } - else if ( QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ) ) - { - tLog() << Q_FUNC_INFO << "QtKeychain writeJob finished"; - } - else if ( QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ) ) - { - tLog() << Q_FUNC_INFO << "QtKeychain deleteJob finished"; + else if ( QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ) ) + { + tLog() << Q_FUNC_INFO << "QtKeychain writeJob for" << writeJob->key() << "finished"; + } + else if ( QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ) ) + { + tLog() << Q_FUNC_INFO << "QtKeychain deleteJob for" << deleteJob->key() << "finished"; + } } else { diff --git a/src/libtomahawk/accounts/CredentialsManager.h b/src/libtomahawk/accounts/CredentialsManager.h index bf653a070d..ec751fa6a1 100644 --- a/src/libtomahawk/accounts/CredentialsManager.h +++ b/src/libtomahawk/accounts/CredentialsManager.h @@ -21,13 +21,13 @@ #include #include +#include namespace QKeychain { class Job; class ReadPasswordJob; -class WritePasswordJob; } @@ -69,6 +69,7 @@ private slots: private: QHash< QString, QVariantHash > m_credentials; QList< QKeychain::ReadPasswordJob* > m_readJobs; + QMutex m_mutex; }; } //namespace Accounts From a129a54d1bef437a01d27ff4b18f04a3918003e2 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 22 May 2013 19:53:48 +0200 Subject: [PATCH 068/565] Enable config upgrade path. --- src/libtomahawk/TomahawkSettings.cpp | 2 +- src/libtomahawk/TomahawkSettings.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 5895acb388..51eff109eb 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -629,7 +629,7 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) { QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this ); j->setKey( account ); - j->setAutoDelete( false ); + j->setAutoDelete( true ); QByteArray data; QDataStream ds( &data, QIODevice::WriteOnly ); diff --git a/src/libtomahawk/TomahawkSettings.h b/src/libtomahawk/TomahawkSettings.h index 30662255b3..82251fcd9c 100644 --- a/src/libtomahawk/TomahawkSettings.h +++ b/src/libtomahawk/TomahawkSettings.h @@ -28,7 +28,7 @@ #include #include -#define TOMAHAWK_SETTINGS_VERSION 14 +#define TOMAHAWK_SETTINGS_VERSION 15 /** * Convenience wrapper around QSettings for tomahawk-specific config From 8e16445b4928a8b092c0cd334fd4f432526fb95e Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 22 May 2013 20:24:19 +0200 Subject: [PATCH 069/565] Fix config file for build tree usage --- TomahawkBuildTreeSettings.cmake.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TomahawkBuildTreeSettings.cmake.in b/TomahawkBuildTreeSettings.cmake.in index 7edf1df1e8..2c70c03378 100644 --- a/TomahawkBuildTreeSettings.cmake.in +++ b/TomahawkBuildTreeSettings.cmake.in @@ -1,3 +1,4 @@ set(TOMAHAWK_INCLUDE_DIRS "@PROJECT_SOURCE_DIR@/src/libtomahawk" - "@PROJECT_BINARY_DIR@") \ No newline at end of file + "@PROJECT_BINARY_DIR@/src/libtomahawk" +) From 29c8b0bea9f77df1830021af19a903934da71a18 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 22 May 2013 20:37:48 +0200 Subject: [PATCH 070/565] Add libqtkeychain.dll to windows installer --- CMakeModules/NSIS.template.in | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeModules/NSIS.template.in b/CMakeModules/NSIS.template.in index 934d7c3baa..b50e0c38f9 100644 --- a/CMakeModules/NSIS.template.in +++ b/CMakeModules/NSIS.template.in @@ -350,6 +350,7 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${MING_BIN}\liblastfm.dll" File "${MING_BIN}\libQTweetLib.dll" File "${MING_BIN}\libquazip.dll" + File "${MING_BIN}\libqtkeychain.dll" ; Jabber File "${MING_BIN}\libjreen.dll" From 748c3106ead9af320508b4565b45349b44eb03f0 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Thu, 23 May 2013 02:16:47 +0200 Subject: [PATCH 071/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 24 ++++++++++++------------ lang/tomahawk_bg.ts | 24 ++++++++++++------------ lang/tomahawk_bn_IN.ts | 24 ++++++++++++------------ lang/tomahawk_ca.ts | 24 ++++++++++++------------ lang/tomahawk_ca@valencia.ts | 24 ++++++++++++------------ lang/tomahawk_cs.ts | 24 ++++++++++++------------ lang/tomahawk_da.ts | 24 ++++++++++++------------ lang/tomahawk_de.ts | 24 ++++++++++++------------ lang/tomahawk_el.ts | 24 ++++++++++++------------ lang/tomahawk_en.ts | 24 ++++++++++++------------ lang/tomahawk_es.ts | 24 ++++++++++++------------ lang/tomahawk_fi.ts | 24 ++++++++++++------------ lang/tomahawk_fr.ts | 24 ++++++++++++------------ lang/tomahawk_gl.ts | 24 ++++++++++++------------ lang/tomahawk_hi_IN.ts | 24 ++++++++++++------------ lang/tomahawk_hu.ts | 24 ++++++++++++------------ lang/tomahawk_id.ts | 24 ++++++++++++------------ lang/tomahawk_it.ts | 24 ++++++++++++------------ lang/tomahawk_ja.ts | 24 ++++++++++++------------ lang/tomahawk_lt.ts | 24 ++++++++++++------------ lang/tomahawk_pl.ts | 24 ++++++++++++------------ lang/tomahawk_pt_BR.ts | 24 ++++++++++++------------ lang/tomahawk_ru.ts | 24 ++++++++++++------------ lang/tomahawk_sv.ts | 24 ++++++++++++------------ lang/tomahawk_tr.ts | 24 ++++++++++++------------ lang/tomahawk_zh_CN.ts | 24 ++++++++++++------------ lang/tomahawk_zh_TW.ts | 24 ++++++++++++------------ 27 files changed, 324 insertions(+), 324 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 97ab0e988d..e5d6b8571a 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1358,17 +1358,17 @@ connect and stream from you? الآن - + Friend Finders محرك البحث عن الأصدقاء - + Music Finders مكتشفي الاغاني - + Status Updaters أنظمة تحديث حالتك @@ -1401,9 +1401,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. تحذير محلل النصي: أعاد ال API البيانات %1 بشكل متزامن. @@ -1467,22 +1467,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 خطأ محلل النصي: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3489,7 +3489,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة @@ -3512,7 +3512,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network الشبكة المحلية diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index f844545ec5..774ae46912 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1366,17 +1366,17 @@ Tomahawk създаде доклад относно това и изпращай току-що - + Friend Finders Търсене на приятели - + Music Finders Търсене на музика - + Status Updaters Обновяване на статуси @@ -1409,9 +1409,9 @@ Tomahawk създаде доклад относно това и изпращай QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1475,22 +1475,22 @@ Tomahawk създаде доклад относно това и изпращай ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Грешка на скриптът за извличане на данни: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3503,7 +3503,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -3525,7 +3525,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Локална мрежа diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 5909078db5..df261b211c 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1355,17 +1355,17 @@ connect and stream from you? - + Friend Finders - + Music Finders - + Status Updaters @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 092cd8b92a..35ab90e178 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1356,17 +1356,17 @@ connect and stream from you? ara mateix - + Friend Finders Cercadors d'Amics - + Music Finders Cercadors de Música - + Status Updaters Actualitzadors d'Estat @@ -1399,9 +1399,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1465,22 +1465,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3490,7 +3490,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció @@ -3513,7 +3513,7 @@ introduïu el PIN aquí: TomahawkSettings - + Local Network Xarxa local diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 30f4ff73ca..4e4d514f4f 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1356,17 +1356,17 @@ connect and stream from you? ara mateix - + Friend Finders Cercadors d'Amics - + Music Finders Cercadors de Música - + Status Updaters Actualitzadors d'Estat @@ -1399,9 +1399,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1465,22 +1465,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3490,7 +3490,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció @@ -3513,7 +3513,7 @@ introduïu el PIN ací: TomahawkSettings - + Local Network Xarxa local diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 4ae88f6c74..fb6b0209bc 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1357,17 +1357,17 @@ se s vámi spojil? právě teď - + Friend Finders Hledač přátel - + Music Finders Hledač hudby - + Status Updaters Obnova stavu @@ -1400,9 +1400,9 @@ se s vámi spojil? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Varování řešitele skriptu: Volání API %1 vrátilo data synchronně. @@ -1466,22 +1466,22 @@ se s vámi spojil? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Chyba řešitele skriptu: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3488,7 +3488,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka @@ -3511,7 +3511,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TomahawkSettings - + Local Network Místní síť diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 8cde56d5d5..af1b81abd9 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1356,17 +1356,17 @@ connect and stream from you? lige nu - + Friend Finders Venne findere - + Music Finders Musik Findere - + Status Updaters Status Opdaterer @@ -1399,9 +1399,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1465,22 +1465,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3478,7 +3478,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling @@ -3500,7 +3500,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 0eb162f42f..a65e8053a0 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1357,17 +1357,17 @@ erlauben sich mit dir zu verbinden? gerade eben - + Friend Finders Freundefinder - + Music Finders Musikfinder - + Status Updaters Status-Updater @@ -1400,9 +1400,9 @@ erlauben sich mit dir zu verbinden? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warnung: API aufruf %1 zurückgegebener Daten synchron. @@ -1466,22 +1466,22 @@ erlauben sich mit dir zu verbinden? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Fehler: %1 %2 %3 %4 - + SSL Error SSL Fehler - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Sie wurden gebeten, Tomahawk sicher zu <b>%1</b> verbinden, aber wir können nicht bestätigen, dass die Verbindung sicher ist:<br><br><b>%2</b><br><br> Möchten Sie dieser Verbindung vertrauen? - + Trust certificate Zertifikat vertrauen @@ -3483,7 +3483,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -3506,7 +3506,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TomahawkSettings - + Local Network Lokales Netzwerk diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index d7703d935d..270c035b7e 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1356,17 +1356,17 @@ connect and stream from you? μόλις τώρα - + Friend Finders Αναζητητής Φίλων - + Music Finders Αναζητητής Μουσικής - + Status Updaters Ανανεωτές Κατάστασης @@ -1399,9 +1399,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Προειδοποίηση Script Resolver: η κλήση API %1 επέστρεψε δεδομένα συγχρόνως. @@ -1465,22 +1465,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Σφάλμα Script Resolver: %1 %2 %3 %4 - + SSL Error SSL Σφάλμα - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Έχετε ζητήσει το Tomahawk να συνδεθεί με ασφάλεια στο <b>%1</b>, αλλά δεν μπορούμε να επιβεβαιώσουμε ότι η σύνδεσή σας είναι ασφαλής:<br><br><b>%2</b><br><br>Θέλετε να εμπιστεύθειτε αυτή τη σύνδεση; - + Trust certificate Εμπιστοσύνη πιστοποιητικόυ @@ -3489,7 +3489,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου @@ -3511,7 +3511,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Τοπικό Δίκτυο diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 4a3ac37f88..c621006857 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1357,17 +1357,17 @@ connect and stream from you? just now - + Friend Finders Friend Finders - + Music Finders Music Finders - + Status Updaters Status Updaters @@ -1400,9 +1400,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warning: API call %1 returned data synchronously. @@ -1466,22 +1466,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Error: %1 %2 %3 %4 - + SSL Error SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate Trust certificate @@ -3491,7 +3491,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -3514,7 +3514,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Local Network diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 9d07ba9335..648e2ee10c 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1357,17 +1357,17 @@ conectarse a usted y transmitir música? justo ahora - + Friend Finders Buscadores de amigos - + Music Finders Buscadores de música - + Status Updaters Actualizadores de estado @@ -1400,9 +1400,9 @@ conectarse a usted y transmitir música? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1466,22 +1466,22 @@ conectarse a usted y transmitir música? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error del resolutor de script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3491,7 +3491,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección @@ -3514,7 +3514,7 @@ introduzca su número PIN aquí: TomahawkSettings - + Local Network Red local diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index ac80bdcd15..0db82ef0d7 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1357,17 +1357,17 @@ yhdistää ja toistaa sinulta virtaa? juuri nyt - + Friend Finders Kaverietsimet - + Music Finders Musiikkietsimet - + Status Updaters Tilanpäivittimet @@ -1400,9 +1400,9 @@ yhdistää ja toistaa sinulta virtaa? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Skriptiselvittimen varoitus: API-kutsu %1 palautti dataa synkronisesti. @@ -1466,22 +1466,22 @@ yhdistää ja toistaa sinulta virtaa? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptiselvittimen virhe: %1 %2 %3 %4 - + SSL Error SSL-virhe - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Olet pyytänyt Tomahawkia yhdistämään turvallisesti palvelimeen <b>%1</b>, mutta yhteyden turvallisuutta ei voida varmistaa:<br><br><b>%2</b><br><br>Haluatko luottaa tähän yhteyteen? - + Trust certificate Luota varmenteeseen @@ -3494,7 +3494,7 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< TomahawkApp - + My Collection Oma kokoelma @@ -3517,7 +3517,7 @@ anna siellä näytetty PIN-koodi tähän: TomahawkSettings - + Local Network Lähiverkko diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 6ab7611dbe..d7e4f86d38 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1357,17 +1357,17 @@ de se connecter et streamer de vous? à l'instant - + Friend Finders Rechercher vos amis - + Music Finders Trouver de la musique - + Status Updaters Mettre à jour votre statut @@ -1400,9 +1400,9 @@ de se connecter et streamer de vous? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1466,22 +1466,22 @@ de se connecter et streamer de vous? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erreur du script de résolution : %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3488,7 +3488,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection @@ -3511,7 +3511,7 @@ saisissez le numéro PIN ici : TomahawkSettings - + Local Network Réseau local diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index ba925c5e58..7f2ac561ae 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1356,17 +1356,17 @@ connect and stream from you? só agora - + Friend Finders Buscador de amizades - + Music Finders Buscador de música - + Status Updaters Actualizador de estado @@ -1399,9 +1399,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1465,22 +1465,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erro do solucionador de erros: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3490,7 +3490,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección @@ -3512,7 +3512,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Rede local diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 1a69cfaf44..7aa75cd013 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1355,17 +1355,17 @@ connect and stream from you? अभी - + Friend Finders - + Music Finders - + Status Updaters @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 1944926030..fdafbb1c95 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1355,17 +1355,17 @@ connect and stream from you? éppen most - + Friend Finders Barát kereső - + Music Finders Zene kereső - + Status Updaters Állapot frissítő @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index bb46f01147..9a99d5abd8 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1355,17 +1355,17 @@ connect and stream from you? - + Friend Finders - + Music Finders - + Status Updaters @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index be825d197e..654fc37764 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1355,17 +1355,17 @@ connect and stream from you? proprio ora - + Friend Finders Cerca amici - + Music Finders Cerca musica - + Status Updaters Status updaters @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Avvertimento Script Resolver: chiamata API 1% ha restituito dati sincroni. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Errore script resolver: %1 %2 %3 %4 - + SSL Error Errore SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Hai chiesto a Tomahawk di connettersi in modo sicuro a <b>%1</b>, ma non possiamo garantire che la conessione sia sicura: <br><br><b>%2</b><br><br> Vuoi fidarti di questa connessione? - + Trust certificate Certificato di trust @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Network locale diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 6d750f86b5..36d9de9b4e 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1357,17 +1357,17 @@ connect and stream from you? たった今 - + Friend Finders 友達を探す - + Music Finders 音楽を探す - + Status Updaters ステータスを更新 @@ -1400,9 +1400,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1466,22 +1466,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3488,7 +3488,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション @@ -3511,7 +3511,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network ローカルネットワーク diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index abead2c826..0ba6c06149 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1355,17 +1355,17 @@ connect and stream from you? ką tik - + Friend Finders Draugų ieškyklės - + Music Finders Muzikos ieškyklės - + Status Updaters Būsenos atnaujintojai @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 036bbfb8a3..0b256d1f5a 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1357,17 +1357,17 @@ połączyć się i strumieniować od ciebie? przed chwilą - + Friend Finders Wyszukiwacze Znajomych - + Music Finders Wyszukiwacze Muzyki - + Status Updaters Aktualizatory Statusu @@ -1400,9 +1400,9 @@ połączyć się i strumieniować od ciebie? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1466,22 +1466,22 @@ połączyć się i strumieniować od ciebie? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3485,7 +3485,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja @@ -3508,7 +3508,7 @@ wprowadź pokazany numer PIN tutaj: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 7c32be70f3..d09e4b3c02 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1357,17 +1357,17 @@ se conecte e faça o stream de você? agora - + Friend Finders Descobridor de amigos - + Music Finders Descobridor de músicas - + Status Updaters Atualizadores de Status @@ -1400,9 +1400,9 @@ se conecte e faça o stream de você? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1466,22 +1466,22 @@ se conecte e faça o stream de você? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3485,7 +3485,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção @@ -3508,7 +3508,7 @@ colocar o número PIN mostrado aqui: TomahawkSettings - + Local Network Rede local diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 522fdd0f0c..f32c1ccae5 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1360,17 +1360,17 @@ connect and stream from you? только что - + Friend Finders Среди Друзей - + Music Finders Среди Музыкальных Сервисов - + Status Updaters Статус Обновления @@ -1403,9 +1403,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3492,7 +3492,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция @@ -3514,7 +3514,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Домашняя сеть diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index fe5ed49e89..7eba22613d 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1357,17 +1357,17 @@ ansluta och strömma från dig? precis nyss - + Friend Finders Hitta vänner - + Music Finders Hitta musik - + Status Updaters Status uppdaterare @@ -1400,9 +1400,9 @@ ansluta och strömma från dig? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Skript Resolver-varning: API-anrop %1 returnerade data synkront @@ -1466,22 +1466,22 @@ ansluta och strömma från dig? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptfel i resolvern: %1 %2 %3 %4 - + SSL Error SSL-Fel - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Du har bett Tomahawk att göra en säker uppkoppling mot <b>%1</b>, men vi kan inte bekräfta att din uppkoppling är säker: <br><br><b>%2</b><br><br>Litar du på denna uppkoppling? - + Trust certificate Tillförlitligt certifikat @@ -3489,7 +3489,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling @@ -3512,7 +3512,7 @@ anger du PIN-koden här: TomahawkSettings - + Local Network Lokalt nätverk diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index ae7769c816..a4091fe74b 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1355,17 +1355,17 @@ connect and stream from you? - + Friend Finders - + Music Finders - + Status Updaters @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 23ab6ad4c5..7ab7b3897c 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1356,17 +1356,17 @@ connect and stream from you? 刚刚 - + Friend Finders 查找朋友 - + Music Finders 查找音乐 - + Status Updaters 更新状态 @@ -1399,9 +1399,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1465,22 +1465,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3486,7 +3486,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3509,7 +3509,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network 本地网络 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index c29a20bacb..e3a5acef12 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1355,17 +1355,17 @@ connect and stream from you? 剛才 - + Friend Finders - + Music Finders - + Status Updaters @@ -1398,9 +1398,9 @@ connect and stream from you? QtScriptResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1464,22 +1464,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3476,7 +3476,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3498,7 +3498,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network From 8b972ca62ed97dc5e2d413250f5d6b1dcc6ff9dd Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 07:16:20 +0200 Subject: [PATCH 072/565] *** Restore v14 config file before running to avoid losing accounts *** Change QtKeychain's user-visible service name to "Tomahawk". --- src/libtomahawk/TomahawkSettings.cpp | 2 +- src/libtomahawk/accounts/CredentialsManager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 51eff109eb..546a395bfb 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -627,7 +627,7 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) tDebug() << "creds:" << creds; if ( !creds.isEmpty() ) { - QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "tomahawkaccounts" ), this ); + QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "Tomahawk" ), this ); j->setKey( account ); j->setAutoDelete( true ); diff --git a/src/libtomahawk/accounts/CredentialsManager.cpp b/src/libtomahawk/accounts/CredentialsManager.cpp index 8f6dc9b4d2..4fd22aaa56 100644 --- a/src/libtomahawk/accounts/CredentialsManager.cpp +++ b/src/libtomahawk/accounts/CredentialsManager.cpp @@ -24,7 +24,7 @@ #include -#define TOMAHAWK_KEYCHAINSVC QLatin1String("tomahawkaccounts") +#define TOMAHAWK_KEYCHAINSVC QLatin1String("Tomahawk") namespace Tomahawk { From 641d28c93266c8670ea30295913b32cbe715bf97 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 07:17:29 +0200 Subject: [PATCH 073/565] Fix issue with not all accountIds being carried over to loading phase. --- src/libtomahawk/accounts/AccountManager.cpp | 7 +++---- src/libtomahawk/accounts/AccountManager.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index 8a855fa198..1bc1e49ecb 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -285,16 +285,15 @@ AccountManager::loadFromConfig() qDebug() << "LOADING ALL CREDENTIALS" << accountIds; m_creds = new CredentialsManager( this ); - connect( m_creds, SIGNAL( ready() ), - this, SLOT( finishLoadingFromConfig() ) ); + NewClosure( m_creds, SIGNAL( ready() ), + this, SLOT( finishLoadingFromConfig( QStringList ) ), accountIds ); m_creds->loadCredentials( accountIds ); } void -AccountManager::finishLoadingFromConfig() +AccountManager::finishLoadingFromConfig( const QStringList& accountIds ) { - QStringList accountIds = m_creds->keys(); qDebug() << "LOADING ALL ACCOUNTS" << accountIds; foreach ( const QString& accountId, accountIds ) diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 42a87eded8..675017625d 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -112,7 +112,7 @@ private slots: void init(); void onStateChanged( Tomahawk::Accounts::Account::ConnectionState state ); void onError( int code, const QString& msg ); - void finishLoadingFromConfig(); + void finishLoadingFromConfig( const QStringList& accountIds ); void onSettingsChanged(); From 24ad7ac701b799604882cb8c6cd6576eea528fb9 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 07:33:58 +0200 Subject: [PATCH 074/565] Enable AccountModel debug spam. --- src/libtomahawk/accounts/AccountModel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index 5c81caac47..bbb169d5b3 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -35,7 +35,7 @@ using namespace Tomahawk; using namespace Accounts; -#define ACCOUNTMODEL_DEBUG 0 +#define ACCOUNTMODEL_DEBUG 1 AccountModel::AccountModel( QObject* parent ) : QAbstractListModel( parent ) @@ -74,6 +74,7 @@ AccountModel::loadData() QList< AccountFactory* > factories = AccountManager::instance()->factories(); QList< Account* > allAccounts = AccountManager::instance()->accounts(); #if ACCOUNTMODEL_DEBUG + qDebug() << Q_FUNC_INFO; qDebug() << "All accounts:"; foreach ( Account* acct, allAccounts ) qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId(); From c35b63da71d5aa0d5259107651a90e6ba03b0bdf Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 10:23:23 +0200 Subject: [PATCH 075/565] Rename QtScriptResolver to JSResolver because JS != QtScript. --- src/TomahawkApp.cpp | 4 +- src/libtomahawk/CMakeLists.txt | 2 +- src/libtomahawk/Pipeline.cpp | 2 +- .../accounts/lastfm/LastFmAccount.cpp | 2 +- src/libtomahawk/network/Servent.cpp | 2 +- .../{QtScriptResolver.cpp => JSResolver.cpp} | 114 +++++++++--------- .../{QtScriptResolver.h => JSResolver.h} | 28 ++--- 7 files changed, 77 insertions(+), 77 deletions(-) rename src/libtomahawk/resolvers/{QtScriptResolver.cpp => JSResolver.cpp} (90%) rename src/libtomahawk/resolvers/{QtScriptResolver.h => JSResolver.h} (89%) diff --git a/src/TomahawkApp.cpp b/src/TomahawkApp.cpp index 35a8f1f589..c20fe28d3f 100644 --- a/src/TomahawkApp.cpp +++ b/src/TomahawkApp.cpp @@ -66,7 +66,7 @@ #include "utils/TomahawkCache.h" #ifndef ENABLE_HEADLESS - #include "resolvers/QtScriptResolver.h" + #include "resolvers/JSResolver.h" #include "resolvers/ScriptResolver.h" #include "utils/SpotifyParser.h" #include "AtticaManager.h" @@ -203,7 +203,7 @@ TomahawkApp::init() m_scanManager = QPointer( new ScanManager( this ) ); #ifndef ENABLE_HEADLESS - Pipeline::instance()->addExternalResolverFactory( boost::bind( &QtScriptResolver::factory, _1, _2 ) ); + Pipeline::instance()->addExternalResolverFactory( boost::bind( &JSResolver::factory, _1, _2 ) ); Pipeline::instance()->addExternalResolverFactory( boost::bind( &ScriptResolver::factory, _1, _2 ) ); new ActionCollection( this ); diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index c14af0d48d..937c0bf88e 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -95,7 +95,7 @@ set( libGuiSources resolvers/ExternalResolverGui.cpp resolvers/ScriptResolver.cpp - resolvers/QtScriptResolver.cpp + resolvers/JSResolver.cpp utils/ImageRegistry.cpp utils/WidgetDragFilter.cpp diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index deddb9c67d..081e15d872 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -24,7 +24,7 @@ #include "database/Database.h" #include "resolvers/ExternalResolver.h" #include "resolvers/ScriptResolver.h" -#include "resolvers/QtScriptResolver.h" +#include "resolvers/JSResolver.h" #include "Source.h" #include "SourceList.h" #include "utils/ResultUrlChecker.h" diff --git a/src/libtomahawk/accounts/lastfm/LastFmAccount.cpp b/src/libtomahawk/accounts/lastfm/LastFmAccount.cpp index e7094d55ae..a966bdd6f8 100644 --- a/src/libtomahawk/accounts/lastfm/LastFmAccount.cpp +++ b/src/libtomahawk/accounts/lastfm/LastFmAccount.cpp @@ -21,8 +21,8 @@ #include "infosystem/InfoSystem.h" #include "LastFmInfoPlugin.h" +#include "resolvers/ExternalResolverGui.h" #include "utils/TomahawkUtilsGui.h" -#include "resolvers/QtScriptResolver.h" #include "AtticaManager.h" #include "Pipeline.h" #include "accounts/AccountManager.h" diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 457fe84aa1..e222639768 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -1123,7 +1123,7 @@ Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result, return; } - //QtScriptResolverHelper::customIODeviceFactory is async! + //JSResolverHelper::customIODeviceFactory is async! m_iofactories.value( proto )( result, callback ); } diff --git a/src/libtomahawk/resolvers/QtScriptResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp similarity index 90% rename from src/libtomahawk/resolvers/QtScriptResolver.cpp rename to src/libtomahawk/resolvers/JSResolver.cpp index 3c0990ef16..d82d8e4c05 100644 --- a/src/libtomahawk/resolvers/QtScriptResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -18,7 +18,7 @@ * along with Tomahawk. If not, see . */ -#include "QtScriptResolver.h" +#include "JSResolver.h" #include "Artist.h" #include "Album.h" @@ -63,7 +63,7 @@ #define RESOLVER_LEGACY_CODE2 "var resolver = Tomahawk.resolver.instance ? Tomahawk.resolver.instance : window;" -QtScriptResolverHelper::QtScriptResolverHelper( const QString& scriptPath, QtScriptResolver* parent ) +JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* parent ) : QObject( parent ) , m_urlCallbackIsAsync( false ) { @@ -73,7 +73,7 @@ QtScriptResolverHelper::QtScriptResolverHelper( const QString& scriptPath, QtScr QByteArray -QtScriptResolverHelper::readRaw( const QString& fileName ) +JSResolverHelper::readRaw( const QString& fileName ) { QString path = QFileInfo( m_scriptPath ).absolutePath(); // remove directories @@ -93,7 +93,7 @@ QtScriptResolverHelper::readRaw( const QString& fileName ) QString -QtScriptResolverHelper::compress( const QString& data ) +JSResolverHelper::compress( const QString& data ) { QByteArray comp = qCompress( data.toLatin1(), 9 ); return comp.toBase64(); @@ -101,21 +101,21 @@ QtScriptResolverHelper::compress( const QString& data ) QString -QtScriptResolverHelper::readCompressed( const QString& fileName ) +JSResolverHelper::readCompressed( const QString& fileName ) { return compress( readRaw( fileName ) ); } QString -QtScriptResolverHelper::readBase64( const QString& fileName ) +JSResolverHelper::readBase64( const QString& fileName ) { return readRaw( fileName ).toBase64(); } QVariantMap -QtScriptResolverHelper::resolverData() +JSResolverHelper::resolverData() { QVariantMap resolver; resolver["config"] = m_resolverConfig; @@ -125,14 +125,14 @@ QtScriptResolverHelper::resolverData() void -QtScriptResolverHelper::log( const QString& message ) +JSResolverHelper::log( const QString& message ) { tLog() << m_scriptPath << ":" << message; } void -QtScriptResolverHelper::addTrackResults( const QVariantMap& results ) +JSResolverHelper::addTrackResults( const QVariantMap& results ) { qDebug() << "Resolver reporting results:" << results; QList< Tomahawk::result_ptr > tracks = m_resolver->parseResultVariantList( results.value("results").toList() ); @@ -144,7 +144,7 @@ QtScriptResolverHelper::addTrackResults( const QVariantMap& results ) void -QtScriptResolverHelper::addArtistResults( const QVariantMap& results ) +JSResolverHelper::addArtistResults( const QVariantMap& results ) { qDebug() << "Resolver reporting artists:" << results; QList< Tomahawk::artist_ptr > artists = m_resolver->parseArtistVariantList( results.value( "artists" ).toList() ); @@ -171,7 +171,7 @@ QtScriptResolverHelper::addArtistResults( const QVariantMap& results ) void -QtScriptResolverHelper::addAlbumResults( const QVariantMap& results ) +JSResolverHelper::addAlbumResults( const QVariantMap& results ) { qDebug() << "Resolver reporting albums:" << results; QString artistName = results.value( "artist" ).toString(); @@ -202,7 +202,7 @@ QtScriptResolverHelper::addAlbumResults( const QVariantMap& results ) void -QtScriptResolverHelper::addAlbumTrackResults( const QVariantMap& results ) +JSResolverHelper::addAlbumTrackResults( const QVariantMap& results ) { qDebug() << "Resolver reporting album tracks:" << results; QString artistName = results.value( "artist" ).toString(); @@ -244,7 +244,7 @@ QtScriptResolverHelper::addAlbumTrackResults( const QVariantMap& results ) void -QtScriptResolverHelper::reportCapabilities( const QVariant& v ) +JSResolverHelper::reportCapabilities( const QVariant& v ) { bool ok = 0; int intCap = v.toInt( &ok ); @@ -259,14 +259,14 @@ QtScriptResolverHelper::reportCapabilities( const QVariant& v ) void -QtScriptResolverHelper::setResolverConfig( const QVariantMap& config ) +JSResolverHelper::setResolverConfig( const QVariantMap& config ) { m_resolverConfig = config; } QString -QtScriptResolverHelper::hmac( const QByteArray& key, const QByteArray &input ) +JSResolverHelper::hmac( const QByteArray& key, const QByteArray &input ) { #ifdef QCA2_FOUND if ( !QCA::isSupported( "hmac(md5)" ) ) @@ -292,7 +292,7 @@ QtScriptResolverHelper::hmac( const QByteArray& key, const QByteArray &input ) QString -QtScriptResolverHelper::md5( const QByteArray& input ) +JSResolverHelper::md5( const QByteArray& input ) { QByteArray const digest = QCryptographicHash::hash( input, QCryptographicHash::Md5 ); return QString::fromLatin1( digest.toHex() ); @@ -300,7 +300,7 @@ QtScriptResolverHelper::md5( const QByteArray& input ) void -QtScriptResolverHelper::addCustomUrlHandler( const QString& protocol, +JSResolverHelper::addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName, const QString& isAsynchronous ) { @@ -308,7 +308,7 @@ QtScriptResolverHelper::addCustomUrlHandler( const QString& protocol, boost::function< void( const Tomahawk::result_ptr&, boost::function< void( QSharedPointer< QIODevice >& ) > )> fac = - boost::bind( &QtScriptResolverHelper::customIODeviceFactory, this, _1, _2 ); + boost::bind( &JSResolverHelper::customIODeviceFactory, this, _1, _2 ); Servent::instance()->registerIODeviceFactory( protocol, fac ); m_urlCallback = callbackFuncName; @@ -316,21 +316,21 @@ QtScriptResolverHelper::addCustomUrlHandler( const QString& protocol, QByteArray -QtScriptResolverHelper::base64Encode( const QByteArray& input ) +JSResolverHelper::base64Encode( const QByteArray& input ) { return input.toBase64(); } QByteArray -QtScriptResolverHelper::base64Decode( const QByteArray& input ) +JSResolverHelper::base64Decode( const QByteArray& input ) { return QByteArray::fromBase64( input ); } void -QtScriptResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result, +JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void( QSharedPointer< QIODevice >& ) > callback ) { //can be sync or async @@ -359,7 +359,7 @@ QtScriptResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& resul void -QtScriptResolverHelper::reportStreamUrl( const QString& qid, +JSResolverHelper::reportStreamUrl( const QString& qid, const QString& streamUrl ) { if ( !m_streamCallbacks.contains( qid ) ) @@ -372,7 +372,7 @@ QtScriptResolverHelper::reportStreamUrl( const QString& qid, void -QtScriptResolverHelper::returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ) +JSResolverHelper::returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ) { QSharedPointer< QIODevice > sp; if ( streamUrl.isEmpty() ) @@ -392,12 +392,12 @@ QtScriptResolverHelper::returnStreamUrl( const QString& streamUrl, boost::functi } -QtScriptResolver::QtScriptResolver( const QString& scriptPath, const QStringList& additionalScriptPaths ) +JSResolver::JSResolver( const QString& scriptPath, const QStringList& additionalScriptPaths ) : Tomahawk::ExternalResolverGui( scriptPath ) , m_ready( false ) , m_stopped( true ) , m_error( Tomahawk::ExternalResolver::NoError ) - , m_resolverHelper( new QtScriptResolverHelper( scriptPath, this ) ) + , m_resolverHelper( new JSResolverHelper( scriptPath, this ) ) , m_requiredScriptPaths( additionalScriptPaths ) { tLog() << Q_FUNC_INFO << "Loading JS resolver:" << scriptPath; @@ -420,7 +420,7 @@ QtScriptResolver::QtScriptResolver( const QString& scriptPath, const QStringList } -QtScriptResolver::~QtScriptResolver() +JSResolver::~JSResolver() { if ( !m_stopped ) stop(); @@ -429,14 +429,14 @@ QtScriptResolver::~QtScriptResolver() } -Tomahawk::ExternalResolver* QtScriptResolver::factory( const QString& scriptPath, const QStringList& additionalScriptPaths ) +Tomahawk::ExternalResolver* JSResolver::factory( const QString& scriptPath, const QStringList& additionalScriptPaths ) { ExternalResolver* res = 0; const QFileInfo fi( scriptPath ); if ( fi.suffix() == "js" || fi.suffix() == "script" ) { - res = new QtScriptResolver( scriptPath, additionalScriptPaths ); + res = new JSResolver( scriptPath, additionalScriptPaths ); tLog() << Q_FUNC_INFO << scriptPath << "Loaded."; } @@ -445,14 +445,14 @@ Tomahawk::ExternalResolver* QtScriptResolver::factory( const QString& scriptPath bool -QtScriptResolver::running() const +JSResolver::running() const { return m_ready && !m_stopped; } void -QtScriptResolver::reload() +JSResolver::reload() { if ( QFile::exists( filePath() ) ) { @@ -466,7 +466,7 @@ QtScriptResolver::reload() void -QtScriptResolver::init() +JSResolver::init() { QFile scriptFile( filePath() ); if( !scriptFile.open( QIODevice::ReadOnly ) ) @@ -550,7 +550,7 @@ QtScriptResolver::init() void -QtScriptResolver::start() +JSResolver::start() { m_stopped = false; if ( m_ready ) @@ -561,7 +561,7 @@ QtScriptResolver::start() void -QtScriptResolver::artists( const Tomahawk::collection_ptr& collection ) +JSResolver::artists( const Tomahawk::collection_ptr& collection ) { if ( QThread::currentThread() != thread() ) { @@ -593,7 +593,7 @@ QtScriptResolver::artists( const Tomahawk::collection_ptr& collection ) void -QtScriptResolver::albums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist ) +JSResolver::albums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist ) { if ( QThread::currentThread() != thread() ) { @@ -628,7 +628,7 @@ QtScriptResolver::albums( const Tomahawk::collection_ptr& collection, const Toma void -QtScriptResolver::tracks( const Tomahawk::collection_ptr& collection, const Tomahawk::album_ptr& album ) +JSResolver::tracks( const Tomahawk::collection_ptr& collection, const Tomahawk::album_ptr& album ) { if ( QThread::currentThread() != thread() ) { @@ -664,14 +664,14 @@ QtScriptResolver::tracks( const Tomahawk::collection_ptr& collection, const Toma Tomahawk::ExternalResolver::ErrorState -QtScriptResolver::error() const +JSResolver::error() const { return m_error; } void -QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) +JSResolver::resolve( const Tomahawk::query_ptr& query ) { if ( QThread::currentThread() != thread() ) { @@ -719,7 +719,7 @@ QtScriptResolver::resolve( const Tomahawk::query_ptr& query ) QList< Tomahawk::result_ptr > -QtScriptResolver::parseResultVariantList( const QVariantList& reslist ) +JSResolver::parseResultVariantList( const QVariantList& reslist ) { QList< Tomahawk::result_ptr > results; @@ -785,7 +785,7 @@ QtScriptResolver::parseResultVariantList( const QVariantList& reslist ) QList< Tomahawk::artist_ptr > -QtScriptResolver::parseArtistVariantList( const QVariantList& reslist ) +JSResolver::parseArtistVariantList( const QVariantList& reslist ) { QList< Tomahawk::artist_ptr > results; @@ -804,7 +804,7 @@ QtScriptResolver::parseArtistVariantList( const QVariantList& reslist ) QList< Tomahawk::album_ptr > -QtScriptResolver::parseAlbumVariantList( const Tomahawk::artist_ptr& artist, const QVariantList& reslist ) +JSResolver::parseAlbumVariantList( const Tomahawk::artist_ptr& artist, const QVariantList& reslist ) { QList< Tomahawk::album_ptr > results; @@ -823,7 +823,7 @@ QtScriptResolver::parseAlbumVariantList( const Tomahawk::artist_ptr& artist, con void -QtScriptResolver::stop() +JSResolver::stop() { m_stopped = true; @@ -838,7 +838,7 @@ QtScriptResolver::stop() void -QtScriptResolver::loadUi() +JSResolver::loadUi() { QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getConfigUi();" ).toMap(); m_dataWidgets = m["fields"].toList(); @@ -871,7 +871,7 @@ QtScriptResolver::loadUi() AccountConfigWidget* -QtScriptResolver::configUI() const +JSResolver::configUI() const { if( m_configWidget.isNull() ) return 0; @@ -881,7 +881,7 @@ QtScriptResolver::configUI() const void -QtScriptResolver::saveConfig() +JSResolver::saveConfig() { QVariant saveData = loadDataFromWidgets(); // qDebug() << Q_FUNC_INFO << saveData; @@ -892,7 +892,7 @@ QtScriptResolver::saveConfig() QVariant -QtScriptResolver::widgetData(QWidget* widget, const QString& property) +JSResolver::widgetData(QWidget* widget, const QString& property) { for( int i = 0; i < widget->metaObject()->propertyCount(); i++ ) { @@ -907,7 +907,7 @@ QtScriptResolver::widgetData(QWidget* widget, const QString& property) void -QtScriptResolver::setWidgetData(const QVariant& value, QWidget* widget, const QString& property) +JSResolver::setWidgetData(const QVariant& value, QWidget* widget, const QString& property) { for( int i = 0; i < widget->metaObject()->propertyCount(); i++ ) { @@ -921,7 +921,7 @@ QtScriptResolver::setWidgetData(const QVariant& value, QWidget* widget, const QS QVariantMap -QtScriptResolver::loadDataFromWidgets() +JSResolver::loadDataFromWidgets() { QVariantMap saveData; foreach( const QVariant& dataWidget, m_dataWidgets ) @@ -941,7 +941,7 @@ QtScriptResolver::loadDataFromWidgets() void -QtScriptResolver::fillDataInWidgets( const QVariantMap& data ) +JSResolver::fillDataInWidgets( const QVariantMap& data ) { foreach(const QVariant& dataWidget, m_dataWidgets) { @@ -963,7 +963,7 @@ QtScriptResolver::fillDataInWidgets( const QVariantMap& data ) void -QtScriptResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capabilities ) +JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capabilities ) { m_capabilities = capabilities; loadCollections(); @@ -971,7 +971,7 @@ QtScriptResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilitie void -QtScriptResolver::loadCollections() +JSResolver::loadCollections() { if ( m_capabilities.testFlag( Browsable ) ) { @@ -1045,7 +1045,7 @@ QtScriptResolver::loadCollections() void -QtScriptResolver::onCollectionIconFetched() +JSResolver::onCollectionIconFetched() { QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); if ( reply != 0 ) @@ -1069,28 +1069,28 @@ QtScriptResolver::onCollectionIconFetched() QVariantMap -QtScriptResolver::resolverSettings() +JSResolver::resolverSettings() { return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "if(resolver.settings) resolver.settings; else getSettings(); " ).toMap(); } QVariantMap -QtScriptResolver::resolverUserConfig() +JSResolver::resolverUserConfig() { return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getUserConfig();" ).toMap(); } QVariantMap -QtScriptResolver::resolverInit() +JSResolver::resolverInit() { return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.init();" ).toMap(); } QVariantMap -QtScriptResolver::resolverCollections() +JSResolver::resolverCollections() { return QVariantMap(); //TODO: add a way to distinguish collections // the resolver should provide a unique ID string for each collection, and then be queriable @@ -1100,7 +1100,7 @@ QtScriptResolver::resolverCollections() } -ScriptEngine::ScriptEngine( QtScriptResolver* parent ) +ScriptEngine::ScriptEngine( JSResolver* parent ) : QWebPage( (QObject*) parent ) , m_parent( parent ) { @@ -1117,7 +1117,7 @@ ScriptEngine::ScriptEngine( QtScriptResolver* parent ) .arg( TOMAHAWK_APPLICATION_NAME ) .arg( TOMAHAWK_VERSION ) ,""); - tLog( LOGVERBOSE ) << "QtScriptResolver Using header" << m_header; + tLog( LOGVERBOSE ) << "JSResolver Using header" << m_header; connect( networkAccessManager(), SIGNAL( sslErrors( QNetworkReply*, QList ) ), SLOT( sslErrorHandler( QNetworkReply*, QList ) ) ); diff --git a/src/libtomahawk/resolvers/QtScriptResolver.h b/src/libtomahawk/resolvers/JSResolver.h similarity index 89% rename from src/libtomahawk/resolvers/QtScriptResolver.h rename to src/libtomahawk/resolvers/JSResolver.h index 226b10b5d5..461f1649f9 100644 --- a/src/libtomahawk/resolvers/QtScriptResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -18,8 +18,8 @@ * along with Tomahawk. If not, see . */ -#ifndef QTSCRIPTRESOLVER_H -#define QTSCRIPTRESOLVER_H +#ifndef JSRESOLVER_H +#define JSRESOLVER_H #include "ExternalResolverGui.h" #include "Query.h" @@ -35,14 +35,14 @@ #include "DllMacro.h" -class QtScriptResolver; +class JSResolver; -class DLLEXPORT QtScriptResolverHelper : public QObject +class DLLEXPORT JSResolverHelper : public QObject { Q_OBJECT public: - QtScriptResolverHelper( const QString& scriptPath, QtScriptResolver* parent ); + JSResolverHelper( const QString& scriptPath, JSResolver* parent ); void setResolverConfig( const QVariantMap& config ); // Return a HMAC (md5) signature of the input text with the desired key @@ -83,7 +83,7 @@ public slots: QHash< QString, boost::function< void( QSharedPointer< QIODevice >& ) > > m_streamCallbacks; bool m_urlCallbackIsAsync; QVariantMap m_resolverConfig; - QtScriptResolver* m_resolver; + JSResolver* m_resolver; }; class DLLEXPORT ScriptEngine : public QWebPage @@ -91,7 +91,7 @@ class DLLEXPORT ScriptEngine : public QWebPage Q_OBJECT public: - explicit ScriptEngine( QtScriptResolver* parent ); + explicit ScriptEngine( JSResolver* parent ); QString userAgentForUrl( const QUrl& url ) const; void setScriptPath( const QString& scriptPath ); @@ -106,21 +106,21 @@ private slots: void sslErrorHandler( QNetworkReply* qnr, const QList& errlist ); private: - QtScriptResolver* m_parent; + JSResolver* m_parent; QString m_scriptPath; QString m_header; }; -class DLLEXPORT QtScriptResolver : public Tomahawk::ExternalResolverGui +class DLLEXPORT JSResolver : public Tomahawk::ExternalResolverGui { Q_OBJECT -friend class ::QtScriptResolverHelper; +friend class ::JSResolverHelper; public: - explicit QtScriptResolver( const QString& scriptPath, const QStringList& additionalScriptPaths = QStringList() ); - virtual ~QtScriptResolver(); + explicit JSResolver( const QString& scriptPath, const QStringList& additionalScriptPaths = QStringList() ); + virtual ~JSResolver(); static ExternalResolver* factory( const QString& scriptPath, const QStringList& additionalScriptPaths = QStringList() ); virtual Capabilities capabilities() const { return m_capabilities; } @@ -187,10 +187,10 @@ private slots: bool m_ready, m_stopped; ExternalResolver::ErrorState m_error; - QtScriptResolverHelper* m_resolverHelper; + JSResolverHelper* m_resolverHelper; QPointer< AccountConfigWidget > m_configWidget; QList< QVariant > m_dataWidgets; QStringList m_requiredScriptPaths; }; -#endif // QTSCRIPTRESOLVER_H +#endif // JSRESOLVER_H From b12aba360c713211f77f541dff57472a25c41e09 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 23 May 2013 12:58:58 +0200 Subject: [PATCH 076/565] Link privately against qtkeychain --- src/libtomahawk/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 937c0bf88e..0575e4db34 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -486,6 +486,7 @@ TARGET_LINK_LIBRARIES( tomahawklib LINK_PRIVATE # Thirdparty shipped with tomahawk ${LIBPORTFWD_LIBRARIES} + ${QTKEYCHAIN_LIBRARIES} LINK_PUBLIC # External deps @@ -494,7 +495,6 @@ TARGET_LINK_LIBRARIES( tomahawklib ${TAGLIB_LIBRARIES} ${CLUCENE_LIBRARIES} ${ECHONEST_LIBRARIES} - ${QTKEYCHAIN_LIBRARIES} ${QT_QTSQL_LIBRARY} ${QT_QTUITOOLS_LIBRARY} ${QT_QTGUI_LIBRARY} From a88816168bc2a69e268b47abb968495579e716eb Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Thu, 23 May 2013 07:14:24 -0400 Subject: [PATCH 077/565] Add outbox SVG --- data/images/outbox.svg | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 data/images/outbox.svg diff --git a/data/images/outbox.svg b/data/images/outbox.svg new file mode 100644 index 0000000000..add557d3c2 --- /dev/null +++ b/data/images/outbox.svg @@ -0,0 +1,18 @@ + + + outbox + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + + + + + \ No newline at end of file From 7c417f1076c08ef0e212d707b09e43627cf9adc8 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 15:02:01 +0200 Subject: [PATCH 078/565] Notify sender of track that an inbox entry has been sent. --- .../database/DatabaseCommand_ShareTrack.cpp | 16 ++++++++++--- src/libtomahawk/jobview/InboxJobItem.cpp | 24 ++++++++++++++----- src/libtomahawk/jobview/InboxJobItem.h | 14 +++++++++-- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp b/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp index 5059538f32..1475f59b37 100644 --- a/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp @@ -70,11 +70,19 @@ DatabaseCommand_ShareTrack::postCommitHook() if ( source()->isLocal() ) Servent::instance()->triggerDBSync(); + QString myDbid = SourceList::instance()->getLocal()->nodeId(); + QString sourceDbid = source()->nodeId(); + + if ( source()->isLocal() || sourceDbid != m_recipient ) //if I just sent a track + { + JobStatusView::instance()->model()->addJob( new InboxJobItem( InboxJobItem::Sending, + SourceList::instance()->get( m_recipient )->friendlyName(), + m_track ) ); + } + if ( m_track ) return; - QString myDbid = SourceList::instance()->getLocal()->nodeId(); - QString sourceDbid = source()->nodeId(); if ( myDbid != m_recipient || sourceDbid == m_recipient ) return; @@ -101,7 +109,9 @@ DatabaseCommand_ShareTrack::postCommitHook() QString friendlyName = source()->friendlyName(); if ( ViewManager::instance()->currentPage() != ViewManager::instance()->inboxWidget() ) - JobStatusView::instance()->model()->addJob( new InboxJobItem( friendlyName, m_track ) ); + JobStatusView::instance()->model()->addJob( new InboxJobItem( InboxJobItem::Receiving, + friendlyName, + m_track ) ); } diff --git a/src/libtomahawk/jobview/InboxJobItem.cpp b/src/libtomahawk/jobview/InboxJobItem.cpp index 9b5160448c..bc2c2f3b0c 100644 --- a/src/libtomahawk/jobview/InboxJobItem.cpp +++ b/src/libtomahawk/jobview/InboxJobItem.cpp @@ -28,12 +28,14 @@ #include -InboxJobItem::InboxJobItem( const QString& sender, +InboxJobItem::InboxJobItem( Side side, + const QString& prettyName, const Tomahawk::trackdata_ptr& track, QObject* parent ) : JobStatusItem() , m_track( track ) - , m_sender( sender ) + , m_prettyName( prettyName ) + , m_side( side ) { m_timer = new QTimer( this ); m_timer->setInterval( 8000 ); @@ -51,10 +53,20 @@ InboxJobItem::~InboxJobItem() QString InboxJobItem::mainText() const { - return tr( "%1 sent you %2 by %3." ) - .arg( m_sender ) - .arg( m_track->track() ) - .arg( m_track->artist() ); + switch ( m_side ) + { + case Sending: + return tr( "Sent %1 by %2 to %3." ) + .arg( m_track->track() ) + .arg( m_track->artist() ) + .arg( m_prettyName ); + case Receiving: + return tr( "%1 sent you %2 by %3." ) + .arg( m_prettyName ) + .arg( m_track->track() ) + .arg( m_track->artist() ); + } + return QString(); } diff --git a/src/libtomahawk/jobview/InboxJobItem.h b/src/libtomahawk/jobview/InboxJobItem.h index b8f65311f2..91dd243b66 100644 --- a/src/libtomahawk/jobview/InboxJobItem.h +++ b/src/libtomahawk/jobview/InboxJobItem.h @@ -30,7 +30,16 @@ class DLLEXPORT InboxJobItem : public JobStatusItem { Q_OBJECT public: - explicit InboxJobItem( const QString& sender, const Tomahawk::trackdata_ptr& track, QObject* parent = 0 ); + enum Side + { + Sending = 0, + Receiving + }; + + explicit InboxJobItem( Side side, + const QString& prettyName, + const Tomahawk::trackdata_ptr& track, + QObject* parent = 0 ); virtual ~InboxJobItem(); virtual QString rightColumnText() const { return QString(); } @@ -42,10 +51,11 @@ class DLLEXPORT InboxJobItem : public JobStatusItem private: Tomahawk::trackdata_ptr m_track; - QString m_sender; + QString m_prettyName; QTimer* m_timer; static QPixmap* s_pixmap; + Side m_side; }; #endif // INBOXJOBITEM_H From 2d8ca57fee90fd254c0f82fa402877eaefcbb3fa Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 15:15:34 +0200 Subject: [PATCH 079/565] Use outbox icon when sending tracks. --- resources.qrc | 1 + src/libtomahawk/jobview/InboxJobItem.cpp | 9 ++++++++- src/libtomahawk/utils/TomahawkUtils.h | 3 ++- src/libtomahawk/utils/TomahawkUtilsGui.cpp | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/resources.qrc b/resources.qrc index 100c6a0f76..17cd5b71ac 100644 --- a/resources.qrc +++ b/resources.qrc @@ -182,5 +182,6 @@ data/images/station-artist.svg data/images/station-genre.svg data/images/station-year.svg + data/images/outbox.svg diff --git a/src/libtomahawk/jobview/InboxJobItem.cpp b/src/libtomahawk/jobview/InboxJobItem.cpp index bc2c2f3b0c..ddd0c54809 100644 --- a/src/libtomahawk/jobview/InboxJobItem.cpp +++ b/src/libtomahawk/jobview/InboxJobItem.cpp @@ -73,5 +73,12 @@ InboxJobItem::mainText() const QPixmap InboxJobItem::icon() const { - return TomahawkUtils::defaultPixmap( TomahawkUtils::Inbox, TomahawkUtils::Original, QSize( 64, 64 ) ); + switch ( m_side ) + { + case Sending: + return TomahawkUtils::defaultPixmap( TomahawkUtils::Outbox, TomahawkUtils::Original, QSize( 64, 64 ) ); + case Receiving: + return TomahawkUtils::defaultPixmap( TomahawkUtils::Inbox, TomahawkUtils::Original, QSize( 64, 64 ) ); + } + return QPixmap(); } diff --git a/src/libtomahawk/utils/TomahawkUtils.h b/src/libtomahawk/utils/TomahawkUtils.h index 86e94ea178..7d86515fec 100644 --- a/src/libtomahawk/utils/TomahawkUtils.h +++ b/src/libtomahawk/utils/TomahawkUtils.h @@ -123,7 +123,8 @@ namespace TomahawkUtils ResolverBundle, Inbox, Invalid, - InboxNewItem + InboxNewItem, + Outbox }; enum ImageMode diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index 198e902e8d..3e270df881 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -718,6 +718,9 @@ defaultPixmap( ImageType type, ImageMode mode, const QSize& size ) case InboxNewItem: pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/new-inbox.svg", size ); break; + case Outbox: + pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/outbox.svg", size ); + break; default: break; From 3f7c08fdb85f03323c727cd0d819bc9a0ad2d820 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 15:22:14 +0200 Subject: [PATCH 080/565] Debug spam++ --- src/libtomahawk/accounts/AccountModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index bbb169d5b3..82eed2c96a 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -661,7 +661,7 @@ AccountModel::accountAdded( Account* account ) // Ok, just a plain resolver. add it at the end if ( ResolverAccount* resolver = qobject_cast< ResolverAccount* >( account ) ) { - qDebug() << "Plain old manual resolver added, appending at end"; + qDebug() << "Plain old manual resolver added, appending at end" << resolver->accountId() << resolver->accountServiceName() << resolver->accountFriendlyName(); if ( !m_waitingForAtticaLoaded ) Q_ASSERT( qobject_cast< AtticaResolverAccount* >( account ) == 0 ); // should NOT get attica accounts here, should be caught above const int count = m_accounts.size(); From b006c56876ab1a9abc642fc54f0bdc5f35db7661 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 17:49:53 +0200 Subject: [PATCH 081/565] Make AccountModel loading asynchronous. AccountManager starts up in 3 steps, first waiting for credentials and then for Servent to be ready. During this time an AccountModel could be instantiated in the GUI thread, which needs AccountManager, and if the latter is not ready at that time, the former cannot be populated with sane data. This commit splits the AccountModel ctor so that it waits for AccountManager to be ready before hooking up to its signals and performing the initial model reset. --- src/libtomahawk/accounts/AccountManager.cpp | 6 +++++- src/libtomahawk/accounts/AccountManager.h | 7 +++++-- src/libtomahawk/accounts/AccountModel.cpp | 13 +++++++++++++ src/libtomahawk/accounts/AccountModel.h | 2 ++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index 1bc1e49ecb..fdc26c2166 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -57,6 +57,7 @@ AccountManager::instance() AccountManager::AccountManager( QObject *parent ) : QObject( parent ) , m_readyForSip( false ) + , m_completelyReady( false ) { s_instance = this; @@ -307,7 +308,7 @@ AccountManager::finishLoadingFromConfig( const QStringList& accountIds ) } m_readyForSip = true; - emit readyForSip(); + emit readyForSip(); //we have to yield to TomahawkApp because we don't know if Servent is ready } @@ -319,6 +320,9 @@ AccountManager::initSIP() { hookupAndEnable( account, true ); } + + m_completelyReady = true; + emit ready(); } diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 675017625d..99963dd454 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -85,8 +85,9 @@ class DLLEXPORT AccountManager : public QObject Account* zeroconfAccount() const; - bool isConnected() const { return m_connected; } - bool isReadyForSip() const { return m_readyForSip; } + bool isConnected() const { return m_connected; } //for use by TomahawkApp during startup + bool isReadyForSip() const { return m_readyForSip; } //for use by TomahawkApp during startup + bool isReady() const { return m_completelyReady; } CredentialsManager* credentialsManager() const { return m_creds; } @@ -98,6 +99,7 @@ public slots: signals: void readyForFactories(); //this happens first, right before loading accounts from config void readyForSip(); //then this, so TomahawkApp can call initSIP if Servent is ready + void ready(); //finally, when everything is done void added( Tomahawk::Accounts::Account* ); void removed( Tomahawk::Accounts::Account* ); @@ -132,6 +134,7 @@ private slots: QList< Account* > m_connectedAccounts; bool m_connected; bool m_readyForSip; + bool m_completelyReady; QHash< AccountType, QList< Account* > > m_accountsByAccountType; QHash< QString, AccountFactory* > m_accountFactories; diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp index 82eed2c96a..ff36e0b6b1 100644 --- a/src/libtomahawk/accounts/AccountModel.cpp +++ b/src/libtomahawk/accounts/AccountModel.cpp @@ -40,6 +40,19 @@ using namespace Accounts; AccountModel::AccountModel( QObject* parent ) : QAbstractListModel( parent ) , m_waitingForAtticaLoaded( true ) +{ + tDebug() << "Creating AccountModel"; + if ( !AccountManager::instance()->isReady() ) + { + connect( AccountManager::instance(), SIGNAL( ready() ), SLOT( init() ) ); + } + else + init(); +} + + +void +AccountModel::init() { connect( AtticaManager::instance(), SIGNAL( resolversLoaded( Attica::Content::List ) ), this, SLOT( atticaLoaded() ) ); connect( AtticaManager::instance(), SIGNAL( startedInstalling( QString ) ), this, SLOT( onStartedInstalling( QString ) ) ); diff --git a/src/libtomahawk/accounts/AccountModel.h b/src/libtomahawk/accounts/AccountModel.h index d8c69d6c31..02e38959f8 100644 --- a/src/libtomahawk/accounts/AccountModel.h +++ b/src/libtomahawk/accounts/AccountModel.h @@ -100,6 +100,8 @@ class DLLEXPORT AccountModel : public QAbstractListModel void errorInstalling( const QPersistentModelIndex& idx ); private slots: + void init(); + void atticaLoaded(); void loadData(); From 30a4019c652cc61835e86eac8a79dee5e3b93e0f Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 23 May 2013 22:23:19 +0200 Subject: [PATCH 082/565] Try to fix startup issue. --- .../accounts/CredentialsManager.cpp | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/libtomahawk/accounts/CredentialsManager.cpp b/src/libtomahawk/accounts/CredentialsManager.cpp index 4fd22aaa56..e021afe567 100644 --- a/src/libtomahawk/accounts/CredentialsManager.cpp +++ b/src/libtomahawk/accounts/CredentialsManager.cpp @@ -36,13 +36,14 @@ namespace Accounts CredentialsManager::CredentialsManager( QObject* parent ) : QObject( parent ) { - + tDebug() << Q_FUNC_INFO; } void CredentialsManager::loadCredentials( QStringList keys ) { + tDebug() << Q_FUNC_INFO << "keys:" << keys; foreach ( QString key, keys ) { QKeychain::ReadPasswordJob* j = new QKeychain::ReadPasswordJob( TOMAHAWK_KEYCHAINSVC, this ); @@ -55,6 +56,7 @@ CredentialsManager::loadCredentials( QStringList keys ) SLOT( keychainJobFinished( QKeychain::Job* ) ) ); m_readJobs << j; j->start(); + tDebug() << "Launching QtKeychain readJob for" << key; } } @@ -124,9 +126,10 @@ CredentialsManager::setCredentials( const QString& key, const QVariantHash& valu void CredentialsManager::keychainJobFinished( QKeychain::Job* j ) { - if ( j->error() == QKeychain::NoError ) + tDebug() << Q_FUNC_INFO; + if ( QKeychain::ReadPasswordJob* readJob = qobject_cast< QKeychain::ReadPasswordJob* >( j ) ) { - if ( QKeychain::ReadPasswordJob* readJob = qobject_cast< QKeychain::ReadPasswordJob* >( j ) ) + if ( readJob->error() == QKeychain::NoError ) { tDebug() << "QtKeychain readJob for" << readJob->key() << "finished without errors"; @@ -135,26 +138,28 @@ CredentialsManager::keychainJobFinished( QKeychain::Job* j ) dataStream >> creds; m_credentials.insert( readJob->key(), creds ); - - m_readJobs.removeAll( readJob ); - - if ( m_readJobs.isEmpty() ) - { - emit ready(); - } } - else if ( QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ) ) + else { - tLog() << Q_FUNC_INFO << "QtKeychain writeJob for" << writeJob->key() << "finished"; + tDebug() << "QtKeychain readJob finished with error:" << j->error() << j->errorString(); } - else if ( QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ) ) + + m_readJobs.removeAll( readJob ); + + if ( m_readJobs.isEmpty() ) { - tLog() << Q_FUNC_INFO << "QtKeychain deleteJob for" << deleteJob->key() << "finished"; + emit ready(); } } - else + else if ( QKeychain::WritePasswordJob* writeJob = qobject_cast< QKeychain::WritePasswordJob* >( j ) ) + { + tLog() << Q_FUNC_INFO << "QtKeychain writeJob for" << writeJob->key() << "finished" + << ( ( j->error() == QKeychain::NoError ) ? "without error" : j->errorString() ); + } + else if ( QKeychain::DeletePasswordJob* deleteJob = qobject_cast< QKeychain::DeletePasswordJob* >( j ) ) { - tDebug() << "QtKeychain job finished with error:" << j->error() << j->errorString(); + tLog() << Q_FUNC_INFO << "QtKeychain deleteJob for" << deleteJob->key() << "finished" + << ( ( j->error() == QKeychain::NoError ) ? "without error" : j->errorString() ); } j->deleteLater(); } From 6da9b2937c8c8c00edf7f5307a15849368dad3fa Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Fri, 24 May 2013 02:16:53 +0200 Subject: [PATCH 083/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 39 +++++++++++++++------------- lang/tomahawk_bg.ts | 39 +++++++++++++++------------- lang/tomahawk_bn_IN.ts | 39 +++++++++++++++------------- lang/tomahawk_ca.ts | 39 +++++++++++++++------------- lang/tomahawk_ca@valencia.ts | 39 +++++++++++++++------------- lang/tomahawk_cs.ts | 49 ++++++++++++++++++++---------------- lang/tomahawk_da.ts | 39 +++++++++++++++------------- lang/tomahawk_de.ts | 39 +++++++++++++++------------- lang/tomahawk_el.ts | 39 +++++++++++++++------------- lang/tomahawk_en.ts | 39 +++++++++++++++------------- lang/tomahawk_es.ts | 39 +++++++++++++++------------- lang/tomahawk_fi.ts | 39 +++++++++++++++------------- lang/tomahawk_fr.ts | 39 +++++++++++++++------------- lang/tomahawk_gl.ts | 39 +++++++++++++++------------- lang/tomahawk_hi_IN.ts | 39 +++++++++++++++------------- lang/tomahawk_hu.ts | 39 +++++++++++++++------------- lang/tomahawk_id.ts | 47 ++++++++++++++++++---------------- lang/tomahawk_it.ts | 39 +++++++++++++++------------- lang/tomahawk_ja.ts | 39 +++++++++++++++------------- lang/tomahawk_lt.ts | 39 +++++++++++++++------------- lang/tomahawk_pl.ts | 39 +++++++++++++++------------- lang/tomahawk_pt_BR.ts | 39 +++++++++++++++------------- lang/tomahawk_ru.ts | 39 +++++++++++++++------------- lang/tomahawk_sv.ts | 39 +++++++++++++++------------- lang/tomahawk_tr.ts | 39 +++++++++++++++------------- lang/tomahawk_zh_CN.ts | 39 +++++++++++++++------------- lang/tomahawk_zh_TW.ts | 39 +++++++++++++++------------- 27 files changed, 603 insertions(+), 468 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index e5d6b8571a..dbc2a4b684 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -687,7 +687,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. %1 أرسل لك %2 للفنان %3. @@ -708,6 +713,16 @@ connect and stream from you? مرشح... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1398,16 +1413,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - تحذير محلل النصي: أعاد ال API البيانات %1 بشكل متزامن. - - QueueView @@ -1467,22 +1472,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 خطأ محلل النصي: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2166,12 +2171,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required مطلوب تثبيت يدوي - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 لسوء الحظ، التثبيت التلقائي لهذا المحلل لم يتوفر بعد على منصتك. <br /><br /> الرجاء إستخدام " التثبيت من الملف" أعلاه، بواسطة جلبه من توزيعتك أو تجميعه بنفسك. ويمكن الاطلاع على مزيد من التعليمات هنا: <br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 774ae46912..d8d946bb32 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -693,7 +693,12 @@ Tomahawk създаде доклад относно това и изпращай InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -714,6 +719,16 @@ Tomahawk създаде доклад относно това и изпращай Филтър... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1406,16 +1421,6 @@ Tomahawk създаде доклад относно това и изпращай - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1475,22 +1480,22 @@ Tomahawk създаде доклад относно това и изпращай ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Грешка на скриптът за извличане на данни: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2178,12 +2183,12 @@ Tomahawk създаде доклад относно това и изпращай Tomahawk::Accounts::AccountModel - + Manual Install Required Изисква се ръчно инсталиране - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 За съжаление, автоматичното инсталиране на този компонент е деактивирано за твоята платформа. <br /><br /> diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index df261b211c..58c2598631 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 35ab90e178..f1ee3e54ea 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? Filtra... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1396,16 +1411,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1465,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2167,12 +2172,12 @@ i emissores basades en els vostres gusts musicals. Tomahawk::Accounts::AccountModel - + Manual Install Required Es requereix una instal·lació manual - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 La instal·lació automàtica d'aquest resoludor no està disponible o està desactivada per a la vostra plataforma.<br /><br />Feu servir «Instal·la des d'un fitxer», obtenint-lo des de la vostra distribució o compilant-lo. Trobareu més instruccions aquí:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 4e4d514f4f..55f4f12cf6 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? Filtra... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1396,16 +1411,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1465,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2167,12 +2172,12 @@ i emissores basades en els vostres gusts musicals. Tomahawk::Accounts::AccountModel - + Manual Install Required Es requereix una instal·lació manual - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 La instal·lació automàtica d'este resoludor no està disponible o està desactivada per a la vostra plataforma.<br /><br />Feu servir «Instal·la des d'un fitxer», obtenint-lo des de la vostra distribució o compilant-lo. Trobareu més instruccions ací:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index fb6b0209bc..711c29fec1 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -687,7 +687,12 @@ se s vámi spojil? InboxJobItem - + + Sent %1 by %2 to %3. + Posláno %1 od %2 %3. + + + %1 sent you %2 by %3. %1 vám poslán %2 od %3. @@ -708,6 +713,16 @@ se s vámi spojil? Filtr... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + Varování řešitele skriptu: Volání API %1 vrátilo data synchronně. + + LastFmConfig @@ -1389,22 +1404,12 @@ se s vámi spojil? No query - + Žádný dotaz Parameter count mismatch - - - - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - Varování řešitele skriptu: Volání API %1 vrátilo data synchronně. + Počet parametrů neodpovídá @@ -1466,24 +1471,24 @@ se s vámi spojil? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Chyba řešitele skriptu: %1 %2 %3 %4 - + SSL Error - + Chyba SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Požádal jste Tomahawk o to, aby se bezpečně připojil k <b>%1</b>, ale nelze potvrdit, že je vaše připojení bezpečné:<br><br><b>%2</b><br><br>Chcete tomuto připojení důvěřovat? - + Trust certificate - + Důvěřovat certifikátu @@ -2165,12 +2170,12 @@ se s vámi spojil? Tomahawk::Accounts::AccountModel - + Manual Install Required Je potřeba ruční instalace - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Bohužel není instalace tohoto řešitele ve vašem systému možná.<br/><br/>Použijte, prosím, "Instalovat ze souboru" a nainstalujte jej ručněl. Další informace naleznete zde:<br/><br/>http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index af1b81abd9..fae0247c74 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? Filter... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1396,16 +1411,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1465,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2164,12 +2169,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index a65e8053a0..eb49881b1c 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -687,7 +687,12 @@ erlauben sich mit dir zu verbinden? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. %1 gesendet an dir %2 von %3. @@ -708,6 +713,16 @@ erlauben sich mit dir zu verbinden? Filter... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1397,16 +1412,6 @@ erlauben sich mit dir zu verbinden? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - Script Resolver Warnung: API aufruf %1 zurückgegebener Daten synchron. - - QueueView @@ -1466,22 +1471,22 @@ erlauben sich mit dir zu verbinden? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Fehler: %1 %2 %3 %4 - + SSL Error SSL Fehler - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Sie wurden gebeten, Tomahawk sicher zu <b>%1</b> verbinden, aber wir können nicht bestätigen, dass die Verbindung sicher ist:<br><br><b>%2</b><br><br> Möchten Sie dieser Verbindung vertrauen? - + Trust certificate Zertifikat vertrauen @@ -2165,12 +2170,12 @@ erlauben sich mit dir zu verbinden? Tomahawk::Accounts::AccountModel - + Manual Install Required Manuelle Installation benötigt - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Leider ist die automatische Installation dieses Resolvers nicht auf deinem System möglich.<br/><br/>Bitte benutze "Installiere Datei" und installiere ihn manuell. Weitere Informationen findest du hier:<br/><br/>http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 270c035b7e..ef34319970 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. %1 αποστελθηκε σε εσενα %2 απο %3. @@ -707,6 +712,16 @@ connect and stream from you? Φιλτράρισμα... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1396,16 +1411,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - Προειδοποίηση Script Resolver: η κλήση API %1 επέστρεψε δεδομένα συγχρόνως. - - QueueView @@ -1465,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Σφάλμα Script Resolver: %1 %2 %3 %4 - + SSL Error SSL Σφάλμα - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Έχετε ζητήσει το Tomahawk να συνδεθεί με ασφάλεια στο <b>%1</b>, αλλά δεν μπορούμε να επιβεβαιώσουμε ότι η σύνδεσή σας είναι ασφαλής:<br><br><b>%2</b><br><br>Θέλετε να εμπιστεύθειτε αυτή τη σύνδεση; - + Trust certificate Εμπιστοσύνη πιστοποιητικόυ @@ -2165,12 +2170,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required Απαιτείται Χειροκίνητη Εγκατάσταση - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Δυστυχώς, η αυτόματη εγκατάσταση του αναλυτή δεν είναι διαθέσιμη ή απενεργοποιηθηκε. <br /><br />Παρακαλούμε χρησιμοποιήστε "Εγκατάσταση από αρχείο" προσκομίζοντας το από τη διανομή σας. Περαιτέρω οδηγίες μπορείτε να βρείτε εδώ : http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index c621006857..7fddb85371 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -687,7 +687,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + Sent %1 by %2 to %3. + + + %1 sent you %2 by %3. %1 sent you %2 by %3. @@ -708,6 +713,16 @@ connect and stream from you? Filter... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + Script Resolver Warning: API call %1 returned data synchronously. + + LastFmConfig @@ -1397,16 +1412,6 @@ connect and stream from you? Parameter count mismatch - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - Script Resolver Warning: API call %1 returned data synchronously. - - QueueView @@ -1466,22 +1471,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Error: %1 %2 %3 %4 - + SSL Error SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate Trust certificate @@ -2168,12 +2173,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 648e2ee10c..6312273991 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -687,7 +687,12 @@ conectarse a usted y transmitir música? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -708,6 +713,16 @@ conectarse a usted y transmitir música? Filtrar… + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1397,16 +1412,6 @@ conectarse a usted y transmitir música? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1466,22 +1471,22 @@ conectarse a usted y transmitir música? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error del resolutor de script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2168,12 +2173,12 @@ y estaciones basadas en sus gustos personales. Tomahawk::Accounts::AccountModel - + Manual Install Required Instalación manual necesaria - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Desafortunadamente, la instalación automática de este servicio no está disponible o está desactivada para su plataforma.<br /><br />Por favor use "Instalar desde archivo", obteniéndolo desde su distribución o compilándolo. Más instrucciones aquí:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 0db82ef0d7..08734ca582 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -687,7 +687,12 @@ yhdistää ja toistaa sinulta virtaa? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. %1 lähetti sinulle artistin %3 kappaleen %2. @@ -708,6 +713,16 @@ yhdistää ja toistaa sinulta virtaa? Suodata... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + Skriptiselvittimen varoitus: API-kutsu %1 palautti dataa synkronisesti. + + LastFmConfig @@ -1397,16 +1412,6 @@ yhdistää ja toistaa sinulta virtaa? Parametrien määrä ei täsmää - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - Skriptiselvittimen varoitus: API-kutsu %1 palautti dataa synkronisesti. - - QueueView @@ -1466,22 +1471,22 @@ yhdistää ja toistaa sinulta virtaa? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptiselvittimen virhe: %1 %2 %3 %4 - + SSL Error SSL-virhe - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Olet pyytänyt Tomahawkia yhdistämään turvallisesti palvelimeen <b>%1</b>, mutta yhteyden turvallisuutta ei voida varmistaa:<br><br><b>%2</b><br><br>Haluatko luottaa tähän yhteyteen? - + Trust certificate Luota varmenteeseen @@ -2171,12 +2176,12 @@ napsauttamalla hiiren oikealla. Tomahawk::Accounts::AccountModel - + Manual Install Required Manuaalinen asennus tarvitaan - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Valitettavasti tämän selvittimen automaattinen asennus ei ole saatavilla tai on poissa käytöstä alustallasi.<br /><br />Hae selvitin jakelusi kautta tai kääntämällä se itse, ja käytä sitten Asenna tiedostosta -painiketta. Lisäohjeita on osoitteessa:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index d7e4f86d38..aed5bb8632 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -687,7 +687,12 @@ de se connecter et streamer de vous? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. %1 vous a envoyé %2 par %3. @@ -708,6 +713,16 @@ de se connecter et streamer de vous? Filtre... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1397,16 +1412,6 @@ de se connecter et streamer de vous? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1466,22 +1471,22 @@ de se connecter et streamer de vous? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erreur du script de résolution : %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2165,12 +2170,12 @@ de se connecter et streamer de vous? Tomahawk::Accounts::AccountModel - + Manual Install Required Installation manuelle requise - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Malheureusement, l'installation automatique de ce script de résolution n'est pas disponible ou a été désactivé sur votre plateforme.<br /><br />Utiliser "Installer depuis un fichier" ci-dessus et téléchargez le fichier pour votre distribution, ou compilez-le. D'autres instructions sont disponibles ici :<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 7f2ac561ae..7c33fcd68f 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? Filtro... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1396,16 +1411,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1465,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erro do solucionador de erros: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2166,12 +2171,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required Precísase facer unha instalación manual - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Lamentablemente a instalación automática deste resolvedor non está dispoñíbel ou está desactivado para a túa plataforma.<br/><br/>Usa o «instalar dende ficheiro» de arriba, buscándoo para a túa distribución ou compilándoo. Podes atopar máis instrucións aquí:<br/><br/>http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 7aa75cd013..31809ef9a3 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index fdafbb1c95..b1b248e0c4 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 9a99d5abd8..c0c562fa5b 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -31,7 +31,7 @@ connect and stream from you? Add Account - + Tambah Akun @@ -39,17 +39,17 @@ connect and stream from you? Online - + Daring Connecting... - + menyambungkan... Offline - + Luring @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 654fc37764..b92ab26dfd 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. %1 ti ha spedito %2 di %3. @@ -707,6 +712,16 @@ connect and stream from you? Filtra... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - Avvertimento Script Resolver: chiamata API 1% ha restituito dati sincroni. - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Errore script resolver: %1 %2 %3 %4 - + SSL Error Errore SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Hai chiesto a Tomahawk di connettersi in modo sicuro a <b>%1</b>, ma non possiamo garantire che la conessione sia sicura: <br><br><b>%2</b><br><br> Vuoi fidarti di questa connessione? - + Trust certificate Certificato di trust @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required Richiesta installazione manuale - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Sfortunatamente l'installazione automatica di questo resolver non è disponibile o disabilitata per la tua piattaforma.<br /><br />Per favore usa l'opzione "installa da file", scaricandola per la tua distribuzione o compilandola tu stesso. Puoi trovare ulteriori info qui: <br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 36d9de9b4e..80d0aabb60 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -687,7 +687,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -708,6 +713,16 @@ connect and stream from you? フィルター... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1397,16 +1412,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1466,22 +1471,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2168,12 +2173,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required 手動インストールが要求 - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 残念ながら、このプラットフォームでは、リゾルバの自動インストールは設けてないか、無効にされました。<br /><br />以上の「ファイルからインストール」を使用して、又はディストリビューションから取得するか、自分でコンパイルして下さい。詳しくはこちらを参照:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 0ba6c06149..23d81f5bb1 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? Filtruoti... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required Reikalingas rankinis įdiegimas - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 0b256d1f5a..e4ca0d8721 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -687,7 +687,12 @@ połączyć się i strumieniować od ciebie? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -708,6 +713,16 @@ połączyć się i strumieniować od ciebie? Filtruj... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1397,16 +1412,6 @@ połączyć się i strumieniować od ciebie? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1466,22 +1471,22 @@ połączyć się i strumieniować od ciebie? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2165,12 +2170,12 @@ połączyć się i strumieniować od ciebie? Tomahawk::Accounts::AccountModel - + Manual Install Required Ręczna instalacja wymagana - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Niestety, automatyczna instalacja tej usługi jest niedostępna lub wyłączona na twojej platformie.<br /><br />Użyj instalacji z pliku, ściągając usługę lub kompilując ją samodzielnie. Więcej informacji można znaleźć na:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index d09e4b3c02..263d1b5e3c 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -687,7 +687,12 @@ se conecte e faça o stream de você? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -708,6 +713,16 @@ se conecte e faça o stream de você? Filtro... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1397,16 +1412,6 @@ se conecte e faça o stream de você? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1466,22 +1471,22 @@ se conecte e faça o stream de você? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2165,12 +2170,12 @@ se conecte e faça o stream de você? Tomahawk::Accounts::AccountModel - + Manual Install Required Instalação Manual Requerida - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Infelizmente, a instalação automática deste resolvedor não esta disponível ou esta desabilitada para sua plataforma.<br /><br />Por favor, utilize a opção "Instalar via arquivo" acima e instale do arquivo baixado ou compilado. Instruções adicionais podem ser encontradas aqui:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index f32c1ccae5..f1d2e29fbb 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -690,7 +690,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -711,6 +716,16 @@ connect and stream from you? Фильтр... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1400,16 +1415,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1469,22 +1474,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2171,12 +2176,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required Ручная установка обязательно - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 К сожалению, автоматическая установка этого расширения не доступна или недоступна для вашей платформы. <br /> <br /> Пожалуйста, используйте "Установить из файла", по получении его из дистрибутива или скомпилируйте его самостоятельно. Дальнейшие инструкции можно найти здесь: <br /> <br /> http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 7eba22613d..f1dc6fff09 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -687,7 +687,12 @@ ansluta och strömma från dig? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. %1 skickade %2 av %3 till dig @@ -708,6 +713,16 @@ ansluta och strömma från dig? Filter... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1397,16 +1412,6 @@ ansluta och strömma från dig? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - Skript Resolver-varning: API-anrop %1 returnerade data synkront - - QueueView @@ -1466,22 +1471,22 @@ ansluta och strömma från dig? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptfel i resolvern: %1 %2 %3 %4 - + SSL Error SSL-Fel - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Du har bett Tomahawk att göra en säker uppkoppling mot <b>%1</b>, men vi kan inte bekräfta att din uppkoppling är säker: <br><br><b>%2</b><br><br>Litar du på denna uppkoppling? - + Trust certificate Tillförlitligt certifikat @@ -2167,12 +2172,12 @@ och radiostationer baserat på din personliga profil Tomahawk::Accounts::AccountModel - + Manual Install Required Manuell installation krävs - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 Tyvärr är inte den automatiska installationen av denna resolvern tillgänglig eller så är den inte tillgänglig för din plattform<br /><br />Var god och använd "Installera från fil" ovan genom att hämta den från din distribution, eller genom att kompilera den själv. Fler instruktioner går att finnas här:<br /> <br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index a4091fe74b..75dd2b0f69 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? Filtre... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 7ab7b3897c..152888ab36 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? 过滤... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1396,16 +1411,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1465,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2166,12 +2171,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required 手动安装需求组件 - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 抱歉,这个解析器的自动安装功能在当前平台上不可用或已被禁用。<br /><br />请使用上面的 "从文件安装" 选项,然后手动选择文件安装。更详细的指导可以参见这里:<br /><br />http://www.tomahawk-player.org/resolvers/%1 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index e3a5acef12..c6121e96dc 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -686,7 +686,12 @@ connect and stream from you? InboxJobItem - + + Sent %1 by %2 to %3. + + + + %1 sent you %2 by %3. @@ -707,6 +712,16 @@ connect and stream from you? 過濾器... + + JSResolver + + + + + Script Resolver Warning: API call %1 returned data synchronously. + + + LastFmConfig @@ -1395,16 +1410,6 @@ connect and stream from you? - - QtScriptResolver - - - - - Script Resolver Warning: API call %1 returned data synchronously. - - - QueueView @@ -1464,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2163,12 +2168,12 @@ connect and stream from you? Tomahawk::Accounts::AccountModel - + Manual Install Required - + Unfortunately, automatic installation of this resolver is not available or disabled for your platform.<br /><br />Please use "Install from file" above, by fetching it from your distribution or compiling it yourself. Further instructions can be found here:<br /><br />http://www.tomahawk-player.org/resolvers/%1 From e4ab92b81ffbb0baa7095de44101e347978f0024 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 24 May 2013 11:21:23 +0200 Subject: [PATCH 084/565] Move application target from src/ to src/tomahawk/ --- src/{ => tomahawk}/AclRegistryImpl.cpp | 0 src/{ => tomahawk}/AclRegistryImpl.h | 0 src/{ => tomahawk}/AudioControls.cpp | 0 src/{ => tomahawk}/AudioControls.h | 0 src/{ => tomahawk}/AudioControls.ui | 0 src/{ => tomahawk}/CMakeLists.linux.cmake | 0 src/{ => tomahawk}/CMakeLists.osx.cmake | 0 src/{ => tomahawk}/CMakeLists.txt | 0 src/{ => tomahawk}/CMakeLists.unix.cmake | 0 src/{ => tomahawk}/CMakeLists.win32.cmake | 0 src/{ => tomahawk}/Config.h.in | 0 src/{ => tomahawk}/ConfigDelegateBase.cpp | 0 src/{ => tomahawk}/ConfigDelegateBase.h | 0 src/{ => tomahawk}/DiagnosticsDialog.cpp | 0 src/{ => tomahawk}/DiagnosticsDialog.h | 0 src/{ => tomahawk}/DiagnosticsDialog.ui | 0 src/{ => tomahawk}/LoadXSPFDialog.cpp | 0 src/{ => tomahawk}/LoadXSPFDialog.h | 0 src/{ => tomahawk}/LoadXSPFDialog.ui | 0 src/{ => tomahawk}/ProxyDialog.ui | 0 src/{ => tomahawk}/ResolverConfigDelegate.cpp | 0 src/{ => tomahawk}/ResolverConfigDelegate.h | 0 src/{ => tomahawk}/Scrobbler.cpp | 0 src/{ => tomahawk}/Scrobbler.h | 0 src/{ => tomahawk}/SearchBox.ui | 0 src/{ => tomahawk}/SettingsDialog.cpp | 0 src/{ => tomahawk}/SettingsDialog.h | 0 src/{ => tomahawk}/Settings_Accounts.ui | 0 src/{ => tomahawk}/Settings_Advanced.ui | 0 src/{ => tomahawk}/Settings_Collection.ui | 0 src/{ => tomahawk}/ShortcutHandler.cpp | 0 src/{ => tomahawk}/ShortcutHandler.h | 0 src/{ => tomahawk}/SocialWidget.cpp | 0 src/{ => tomahawk}/SocialWidget.h | 0 src/{ => tomahawk}/SocialWidget.ui | 0 src/{ => tomahawk}/TomahawkApp.cpp | 0 src/{ => tomahawk}/TomahawkApp.h | 0 src/{ => tomahawk}/TomahawkTrayIcon.cpp | 0 src/{ => tomahawk}/TomahawkTrayIcon.h | 0 src/{ => tomahawk}/TomahawkVersion.h.in | 0 src/{ => tomahawk}/TomahawkWindow.cpp | 0 src/{ => tomahawk}/TomahawkWindow.h | 0 src/{ => tomahawk}/TomahawkWindow.ui | 0 src/{ => tomahawk}/UbuntuUnityHack.cpp | 0 src/{ => tomahawk}/UbuntuUnityHack.h | 0 src/{ => tomahawk}/breakpad/BreakPad.cpp | 0 src/{ => tomahawk}/breakpad/BreakPad.h | 0 src/{ => tomahawk}/mac/MacDelegate.h | 0 src/{ => tomahawk}/mac/MacShortcutHandler.cpp | 0 src/{ => tomahawk}/mac/MacShortcutHandler.h | 0 src/{ => tomahawk}/mac/TomahawkApp_Mac.h | 0 src/{ => tomahawk}/mac/TomahawkApp_Mac.mm | 0 src/{ => tomahawk}/main.cpp | 0 src/{ => tomahawk}/sourcetree/AnimationHelper.cpp | 0 src/{ => tomahawk}/sourcetree/AnimationHelper.h | 0 src/{ => tomahawk}/sourcetree/SourceDelegate.cpp | 0 src/{ => tomahawk}/sourcetree/SourceDelegate.h | 0 src/{ => tomahawk}/sourcetree/SourceTreeView.cpp | 0 src/{ => tomahawk}/sourcetree/SourceTreeView.h | 0 src/{ => tomahawk}/sourcetree/SourcesModel.cpp | 0 src/{ => tomahawk}/sourcetree/SourcesModel.h | 0 src/{ => tomahawk}/sourcetree/SourcesProxyModel.cpp | 0 src/{ => tomahawk}/sourcetree/SourcesProxyModel.h | 0 src/{ => tomahawk}/sourcetree/items/CategoryItems.cpp | 0 src/{ => tomahawk}/sourcetree/items/CategoryItems.h | 0 src/{ => tomahawk}/sourcetree/items/GenericPageItems.cpp | 0 src/{ => tomahawk}/sourcetree/items/GenericPageItems.h | 0 src/{ => tomahawk}/sourcetree/items/GroupItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/GroupItem.h | 0 src/{ => tomahawk}/sourcetree/items/HistoryItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/HistoryItem.h | 0 src/{ => tomahawk}/sourcetree/items/InboxItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/InboxItem.h | 0 src/{ => tomahawk}/sourcetree/items/LovedTracksItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/LovedTracksItem.h | 0 src/{ => tomahawk}/sourcetree/items/PlaylistItems.cpp | 0 src/{ => tomahawk}/sourcetree/items/PlaylistItems.h | 0 src/{ => tomahawk}/sourcetree/items/ScriptCollectionItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/ScriptCollectionItem.h | 0 src/{ => tomahawk}/sourcetree/items/SourceItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/SourceItem.h | 0 src/{ => tomahawk}/sourcetree/items/SourceTreeItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/SourceTreeItem.h | 0 src/{ => tomahawk}/sourcetree/items/TemporaryPageItem.cpp | 0 src/{ => tomahawk}/sourcetree/items/TemporaryPageItem.h | 0 src/{ => tomahawk}/web/Api_v1.cpp | 0 src/{ => tomahawk}/web/Api_v1.h | 0 src/{ => tomahawk}/widgets/AccountListView.cpp | 0 src/{ => tomahawk}/widgets/AccountListView.h | 0 src/{ => tomahawk}/widgets/AccountListWidget.cpp | 0 src/{ => tomahawk}/widgets/AccountListWidget.h | 0 src/{ => tomahawk}/widgets/AccountModelFactoryProxy.cpp | 0 src/{ => tomahawk}/widgets/AccountModelFactoryProxy.h | 0 src/{ => tomahawk}/widgets/AccountWidget.cpp | 0 src/{ => tomahawk}/widgets/AccountWidget.h | 0 src/{ => tomahawk}/widgets/AccountsPopupWidget.cpp | 0 src/{ => tomahawk}/widgets/AccountsPopupWidget.h | 0 src/{ => tomahawk}/widgets/AccountsToolButton.cpp | 0 src/{ => tomahawk}/widgets/AccountsToolButton.h | 0 src/{ => tomahawk}/widgets/ContainedMenuButton.cpp | 0 src/{ => tomahawk}/widgets/ContainedMenuButton.h | 0 src/{ => tomahawk}/widgets/SlideSwitchButton.cpp | 0 src/{ => tomahawk}/widgets/SlideSwitchButton.h | 0 src/{ => tomahawk}/widgets/UnstyledFrame.cpp | 0 src/{ => tomahawk}/widgets/UnstyledFrame.h | 0 src/{ => tomahawk}/xmppbot/XmppBot.cpp | 0 src/{ => tomahawk}/xmppbot/XmppBot.h | 0 107 files changed, 0 insertions(+), 0 deletions(-) rename src/{ => tomahawk}/AclRegistryImpl.cpp (100%) rename src/{ => tomahawk}/AclRegistryImpl.h (100%) rename src/{ => tomahawk}/AudioControls.cpp (100%) rename src/{ => tomahawk}/AudioControls.h (100%) rename src/{ => tomahawk}/AudioControls.ui (100%) rename src/{ => tomahawk}/CMakeLists.linux.cmake (100%) rename src/{ => tomahawk}/CMakeLists.osx.cmake (100%) rename src/{ => tomahawk}/CMakeLists.txt (100%) rename src/{ => tomahawk}/CMakeLists.unix.cmake (100%) rename src/{ => tomahawk}/CMakeLists.win32.cmake (100%) rename src/{ => tomahawk}/Config.h.in (100%) rename src/{ => tomahawk}/ConfigDelegateBase.cpp (100%) rename src/{ => tomahawk}/ConfigDelegateBase.h (100%) rename src/{ => tomahawk}/DiagnosticsDialog.cpp (100%) rename src/{ => tomahawk}/DiagnosticsDialog.h (100%) rename src/{ => tomahawk}/DiagnosticsDialog.ui (100%) rename src/{ => tomahawk}/LoadXSPFDialog.cpp (100%) rename src/{ => tomahawk}/LoadXSPFDialog.h (100%) rename src/{ => tomahawk}/LoadXSPFDialog.ui (100%) rename src/{ => tomahawk}/ProxyDialog.ui (100%) rename src/{ => tomahawk}/ResolverConfigDelegate.cpp (100%) rename src/{ => tomahawk}/ResolverConfigDelegate.h (100%) rename src/{ => tomahawk}/Scrobbler.cpp (100%) rename src/{ => tomahawk}/Scrobbler.h (100%) rename src/{ => tomahawk}/SearchBox.ui (100%) rename src/{ => tomahawk}/SettingsDialog.cpp (100%) rename src/{ => tomahawk}/SettingsDialog.h (100%) rename src/{ => tomahawk}/Settings_Accounts.ui (100%) rename src/{ => tomahawk}/Settings_Advanced.ui (100%) rename src/{ => tomahawk}/Settings_Collection.ui (100%) rename src/{ => tomahawk}/ShortcutHandler.cpp (100%) rename src/{ => tomahawk}/ShortcutHandler.h (100%) rename src/{ => tomahawk}/SocialWidget.cpp (100%) rename src/{ => tomahawk}/SocialWidget.h (100%) rename src/{ => tomahawk}/SocialWidget.ui (100%) rename src/{ => tomahawk}/TomahawkApp.cpp (100%) rename src/{ => tomahawk}/TomahawkApp.h (100%) rename src/{ => tomahawk}/TomahawkTrayIcon.cpp (100%) rename src/{ => tomahawk}/TomahawkTrayIcon.h (100%) rename src/{ => tomahawk}/TomahawkVersion.h.in (100%) rename src/{ => tomahawk}/TomahawkWindow.cpp (100%) rename src/{ => tomahawk}/TomahawkWindow.h (100%) rename src/{ => tomahawk}/TomahawkWindow.ui (100%) rename src/{ => tomahawk}/UbuntuUnityHack.cpp (100%) rename src/{ => tomahawk}/UbuntuUnityHack.h (100%) rename src/{ => tomahawk}/breakpad/BreakPad.cpp (100%) rename src/{ => tomahawk}/breakpad/BreakPad.h (100%) rename src/{ => tomahawk}/mac/MacDelegate.h (100%) rename src/{ => tomahawk}/mac/MacShortcutHandler.cpp (100%) rename src/{ => tomahawk}/mac/MacShortcutHandler.h (100%) rename src/{ => tomahawk}/mac/TomahawkApp_Mac.h (100%) rename src/{ => tomahawk}/mac/TomahawkApp_Mac.mm (100%) rename src/{ => tomahawk}/main.cpp (100%) rename src/{ => tomahawk}/sourcetree/AnimationHelper.cpp (100%) rename src/{ => tomahawk}/sourcetree/AnimationHelper.h (100%) rename src/{ => tomahawk}/sourcetree/SourceDelegate.cpp (100%) rename src/{ => tomahawk}/sourcetree/SourceDelegate.h (100%) rename src/{ => tomahawk}/sourcetree/SourceTreeView.cpp (100%) rename src/{ => tomahawk}/sourcetree/SourceTreeView.h (100%) rename src/{ => tomahawk}/sourcetree/SourcesModel.cpp (100%) rename src/{ => tomahawk}/sourcetree/SourcesModel.h (100%) rename src/{ => tomahawk}/sourcetree/SourcesProxyModel.cpp (100%) rename src/{ => tomahawk}/sourcetree/SourcesProxyModel.h (100%) rename src/{ => tomahawk}/sourcetree/items/CategoryItems.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/CategoryItems.h (100%) rename src/{ => tomahawk}/sourcetree/items/GenericPageItems.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/GenericPageItems.h (100%) rename src/{ => tomahawk}/sourcetree/items/GroupItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/GroupItem.h (100%) rename src/{ => tomahawk}/sourcetree/items/HistoryItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/HistoryItem.h (100%) rename src/{ => tomahawk}/sourcetree/items/InboxItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/InboxItem.h (100%) rename src/{ => tomahawk}/sourcetree/items/LovedTracksItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/LovedTracksItem.h (100%) rename src/{ => tomahawk}/sourcetree/items/PlaylistItems.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/PlaylistItems.h (100%) rename src/{ => tomahawk}/sourcetree/items/ScriptCollectionItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/ScriptCollectionItem.h (100%) rename src/{ => tomahawk}/sourcetree/items/SourceItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/SourceItem.h (100%) rename src/{ => tomahawk}/sourcetree/items/SourceTreeItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/SourceTreeItem.h (100%) rename src/{ => tomahawk}/sourcetree/items/TemporaryPageItem.cpp (100%) rename src/{ => tomahawk}/sourcetree/items/TemporaryPageItem.h (100%) rename src/{ => tomahawk}/web/Api_v1.cpp (100%) rename src/{ => tomahawk}/web/Api_v1.h (100%) rename src/{ => tomahawk}/widgets/AccountListView.cpp (100%) rename src/{ => tomahawk}/widgets/AccountListView.h (100%) rename src/{ => tomahawk}/widgets/AccountListWidget.cpp (100%) rename src/{ => tomahawk}/widgets/AccountListWidget.h (100%) rename src/{ => tomahawk}/widgets/AccountModelFactoryProxy.cpp (100%) rename src/{ => tomahawk}/widgets/AccountModelFactoryProxy.h (100%) rename src/{ => tomahawk}/widgets/AccountWidget.cpp (100%) rename src/{ => tomahawk}/widgets/AccountWidget.h (100%) rename src/{ => tomahawk}/widgets/AccountsPopupWidget.cpp (100%) rename src/{ => tomahawk}/widgets/AccountsPopupWidget.h (100%) rename src/{ => tomahawk}/widgets/AccountsToolButton.cpp (100%) rename src/{ => tomahawk}/widgets/AccountsToolButton.h (100%) rename src/{ => tomahawk}/widgets/ContainedMenuButton.cpp (100%) rename src/{ => tomahawk}/widgets/ContainedMenuButton.h (100%) rename src/{ => tomahawk}/widgets/SlideSwitchButton.cpp (100%) rename src/{ => tomahawk}/widgets/SlideSwitchButton.h (100%) rename src/{ => tomahawk}/widgets/UnstyledFrame.cpp (100%) rename src/{ => tomahawk}/widgets/UnstyledFrame.h (100%) rename src/{ => tomahawk}/xmppbot/XmppBot.cpp (100%) rename src/{ => tomahawk}/xmppbot/XmppBot.h (100%) diff --git a/src/AclRegistryImpl.cpp b/src/tomahawk/AclRegistryImpl.cpp similarity index 100% rename from src/AclRegistryImpl.cpp rename to src/tomahawk/AclRegistryImpl.cpp diff --git a/src/AclRegistryImpl.h b/src/tomahawk/AclRegistryImpl.h similarity index 100% rename from src/AclRegistryImpl.h rename to src/tomahawk/AclRegistryImpl.h diff --git a/src/AudioControls.cpp b/src/tomahawk/AudioControls.cpp similarity index 100% rename from src/AudioControls.cpp rename to src/tomahawk/AudioControls.cpp diff --git a/src/AudioControls.h b/src/tomahawk/AudioControls.h similarity index 100% rename from src/AudioControls.h rename to src/tomahawk/AudioControls.h diff --git a/src/AudioControls.ui b/src/tomahawk/AudioControls.ui similarity index 100% rename from src/AudioControls.ui rename to src/tomahawk/AudioControls.ui diff --git a/src/CMakeLists.linux.cmake b/src/tomahawk/CMakeLists.linux.cmake similarity index 100% rename from src/CMakeLists.linux.cmake rename to src/tomahawk/CMakeLists.linux.cmake diff --git a/src/CMakeLists.osx.cmake b/src/tomahawk/CMakeLists.osx.cmake similarity index 100% rename from src/CMakeLists.osx.cmake rename to src/tomahawk/CMakeLists.osx.cmake diff --git a/src/CMakeLists.txt b/src/tomahawk/CMakeLists.txt similarity index 100% rename from src/CMakeLists.txt rename to src/tomahawk/CMakeLists.txt diff --git a/src/CMakeLists.unix.cmake b/src/tomahawk/CMakeLists.unix.cmake similarity index 100% rename from src/CMakeLists.unix.cmake rename to src/tomahawk/CMakeLists.unix.cmake diff --git a/src/CMakeLists.win32.cmake b/src/tomahawk/CMakeLists.win32.cmake similarity index 100% rename from src/CMakeLists.win32.cmake rename to src/tomahawk/CMakeLists.win32.cmake diff --git a/src/Config.h.in b/src/tomahawk/Config.h.in similarity index 100% rename from src/Config.h.in rename to src/tomahawk/Config.h.in diff --git a/src/ConfigDelegateBase.cpp b/src/tomahawk/ConfigDelegateBase.cpp similarity index 100% rename from src/ConfigDelegateBase.cpp rename to src/tomahawk/ConfigDelegateBase.cpp diff --git a/src/ConfigDelegateBase.h b/src/tomahawk/ConfigDelegateBase.h similarity index 100% rename from src/ConfigDelegateBase.h rename to src/tomahawk/ConfigDelegateBase.h diff --git a/src/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp similarity index 100% rename from src/DiagnosticsDialog.cpp rename to src/tomahawk/DiagnosticsDialog.cpp diff --git a/src/DiagnosticsDialog.h b/src/tomahawk/DiagnosticsDialog.h similarity index 100% rename from src/DiagnosticsDialog.h rename to src/tomahawk/DiagnosticsDialog.h diff --git a/src/DiagnosticsDialog.ui b/src/tomahawk/DiagnosticsDialog.ui similarity index 100% rename from src/DiagnosticsDialog.ui rename to src/tomahawk/DiagnosticsDialog.ui diff --git a/src/LoadXSPFDialog.cpp b/src/tomahawk/LoadXSPFDialog.cpp similarity index 100% rename from src/LoadXSPFDialog.cpp rename to src/tomahawk/LoadXSPFDialog.cpp diff --git a/src/LoadXSPFDialog.h b/src/tomahawk/LoadXSPFDialog.h similarity index 100% rename from src/LoadXSPFDialog.h rename to src/tomahawk/LoadXSPFDialog.h diff --git a/src/LoadXSPFDialog.ui b/src/tomahawk/LoadXSPFDialog.ui similarity index 100% rename from src/LoadXSPFDialog.ui rename to src/tomahawk/LoadXSPFDialog.ui diff --git a/src/ProxyDialog.ui b/src/tomahawk/ProxyDialog.ui similarity index 100% rename from src/ProxyDialog.ui rename to src/tomahawk/ProxyDialog.ui diff --git a/src/ResolverConfigDelegate.cpp b/src/tomahawk/ResolverConfigDelegate.cpp similarity index 100% rename from src/ResolverConfigDelegate.cpp rename to src/tomahawk/ResolverConfigDelegate.cpp diff --git a/src/ResolverConfigDelegate.h b/src/tomahawk/ResolverConfigDelegate.h similarity index 100% rename from src/ResolverConfigDelegate.h rename to src/tomahawk/ResolverConfigDelegate.h diff --git a/src/Scrobbler.cpp b/src/tomahawk/Scrobbler.cpp similarity index 100% rename from src/Scrobbler.cpp rename to src/tomahawk/Scrobbler.cpp diff --git a/src/Scrobbler.h b/src/tomahawk/Scrobbler.h similarity index 100% rename from src/Scrobbler.h rename to src/tomahawk/Scrobbler.h diff --git a/src/SearchBox.ui b/src/tomahawk/SearchBox.ui similarity index 100% rename from src/SearchBox.ui rename to src/tomahawk/SearchBox.ui diff --git a/src/SettingsDialog.cpp b/src/tomahawk/SettingsDialog.cpp similarity index 100% rename from src/SettingsDialog.cpp rename to src/tomahawk/SettingsDialog.cpp diff --git a/src/SettingsDialog.h b/src/tomahawk/SettingsDialog.h similarity index 100% rename from src/SettingsDialog.h rename to src/tomahawk/SettingsDialog.h diff --git a/src/Settings_Accounts.ui b/src/tomahawk/Settings_Accounts.ui similarity index 100% rename from src/Settings_Accounts.ui rename to src/tomahawk/Settings_Accounts.ui diff --git a/src/Settings_Advanced.ui b/src/tomahawk/Settings_Advanced.ui similarity index 100% rename from src/Settings_Advanced.ui rename to src/tomahawk/Settings_Advanced.ui diff --git a/src/Settings_Collection.ui b/src/tomahawk/Settings_Collection.ui similarity index 100% rename from src/Settings_Collection.ui rename to src/tomahawk/Settings_Collection.ui diff --git a/src/ShortcutHandler.cpp b/src/tomahawk/ShortcutHandler.cpp similarity index 100% rename from src/ShortcutHandler.cpp rename to src/tomahawk/ShortcutHandler.cpp diff --git a/src/ShortcutHandler.h b/src/tomahawk/ShortcutHandler.h similarity index 100% rename from src/ShortcutHandler.h rename to src/tomahawk/ShortcutHandler.h diff --git a/src/SocialWidget.cpp b/src/tomahawk/SocialWidget.cpp similarity index 100% rename from src/SocialWidget.cpp rename to src/tomahawk/SocialWidget.cpp diff --git a/src/SocialWidget.h b/src/tomahawk/SocialWidget.h similarity index 100% rename from src/SocialWidget.h rename to src/tomahawk/SocialWidget.h diff --git a/src/SocialWidget.ui b/src/tomahawk/SocialWidget.ui similarity index 100% rename from src/SocialWidget.ui rename to src/tomahawk/SocialWidget.ui diff --git a/src/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp similarity index 100% rename from src/TomahawkApp.cpp rename to src/tomahawk/TomahawkApp.cpp diff --git a/src/TomahawkApp.h b/src/tomahawk/TomahawkApp.h similarity index 100% rename from src/TomahawkApp.h rename to src/tomahawk/TomahawkApp.h diff --git a/src/TomahawkTrayIcon.cpp b/src/tomahawk/TomahawkTrayIcon.cpp similarity index 100% rename from src/TomahawkTrayIcon.cpp rename to src/tomahawk/TomahawkTrayIcon.cpp diff --git a/src/TomahawkTrayIcon.h b/src/tomahawk/TomahawkTrayIcon.h similarity index 100% rename from src/TomahawkTrayIcon.h rename to src/tomahawk/TomahawkTrayIcon.h diff --git a/src/TomahawkVersion.h.in b/src/tomahawk/TomahawkVersion.h.in similarity index 100% rename from src/TomahawkVersion.h.in rename to src/tomahawk/TomahawkVersion.h.in diff --git a/src/TomahawkWindow.cpp b/src/tomahawk/TomahawkWindow.cpp similarity index 100% rename from src/TomahawkWindow.cpp rename to src/tomahawk/TomahawkWindow.cpp diff --git a/src/TomahawkWindow.h b/src/tomahawk/TomahawkWindow.h similarity index 100% rename from src/TomahawkWindow.h rename to src/tomahawk/TomahawkWindow.h diff --git a/src/TomahawkWindow.ui b/src/tomahawk/TomahawkWindow.ui similarity index 100% rename from src/TomahawkWindow.ui rename to src/tomahawk/TomahawkWindow.ui diff --git a/src/UbuntuUnityHack.cpp b/src/tomahawk/UbuntuUnityHack.cpp similarity index 100% rename from src/UbuntuUnityHack.cpp rename to src/tomahawk/UbuntuUnityHack.cpp diff --git a/src/UbuntuUnityHack.h b/src/tomahawk/UbuntuUnityHack.h similarity index 100% rename from src/UbuntuUnityHack.h rename to src/tomahawk/UbuntuUnityHack.h diff --git a/src/breakpad/BreakPad.cpp b/src/tomahawk/breakpad/BreakPad.cpp similarity index 100% rename from src/breakpad/BreakPad.cpp rename to src/tomahawk/breakpad/BreakPad.cpp diff --git a/src/breakpad/BreakPad.h b/src/tomahawk/breakpad/BreakPad.h similarity index 100% rename from src/breakpad/BreakPad.h rename to src/tomahawk/breakpad/BreakPad.h diff --git a/src/mac/MacDelegate.h b/src/tomahawk/mac/MacDelegate.h similarity index 100% rename from src/mac/MacDelegate.h rename to src/tomahawk/mac/MacDelegate.h diff --git a/src/mac/MacShortcutHandler.cpp b/src/tomahawk/mac/MacShortcutHandler.cpp similarity index 100% rename from src/mac/MacShortcutHandler.cpp rename to src/tomahawk/mac/MacShortcutHandler.cpp diff --git a/src/mac/MacShortcutHandler.h b/src/tomahawk/mac/MacShortcutHandler.h similarity index 100% rename from src/mac/MacShortcutHandler.h rename to src/tomahawk/mac/MacShortcutHandler.h diff --git a/src/mac/TomahawkApp_Mac.h b/src/tomahawk/mac/TomahawkApp_Mac.h similarity index 100% rename from src/mac/TomahawkApp_Mac.h rename to src/tomahawk/mac/TomahawkApp_Mac.h diff --git a/src/mac/TomahawkApp_Mac.mm b/src/tomahawk/mac/TomahawkApp_Mac.mm similarity index 100% rename from src/mac/TomahawkApp_Mac.mm rename to src/tomahawk/mac/TomahawkApp_Mac.mm diff --git a/src/main.cpp b/src/tomahawk/main.cpp similarity index 100% rename from src/main.cpp rename to src/tomahawk/main.cpp diff --git a/src/sourcetree/AnimationHelper.cpp b/src/tomahawk/sourcetree/AnimationHelper.cpp similarity index 100% rename from src/sourcetree/AnimationHelper.cpp rename to src/tomahawk/sourcetree/AnimationHelper.cpp diff --git a/src/sourcetree/AnimationHelper.h b/src/tomahawk/sourcetree/AnimationHelper.h similarity index 100% rename from src/sourcetree/AnimationHelper.h rename to src/tomahawk/sourcetree/AnimationHelper.h diff --git a/src/sourcetree/SourceDelegate.cpp b/src/tomahawk/sourcetree/SourceDelegate.cpp similarity index 100% rename from src/sourcetree/SourceDelegate.cpp rename to src/tomahawk/sourcetree/SourceDelegate.cpp diff --git a/src/sourcetree/SourceDelegate.h b/src/tomahawk/sourcetree/SourceDelegate.h similarity index 100% rename from src/sourcetree/SourceDelegate.h rename to src/tomahawk/sourcetree/SourceDelegate.h diff --git a/src/sourcetree/SourceTreeView.cpp b/src/tomahawk/sourcetree/SourceTreeView.cpp similarity index 100% rename from src/sourcetree/SourceTreeView.cpp rename to src/tomahawk/sourcetree/SourceTreeView.cpp diff --git a/src/sourcetree/SourceTreeView.h b/src/tomahawk/sourcetree/SourceTreeView.h similarity index 100% rename from src/sourcetree/SourceTreeView.h rename to src/tomahawk/sourcetree/SourceTreeView.h diff --git a/src/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp similarity index 100% rename from src/sourcetree/SourcesModel.cpp rename to src/tomahawk/sourcetree/SourcesModel.cpp diff --git a/src/sourcetree/SourcesModel.h b/src/tomahawk/sourcetree/SourcesModel.h similarity index 100% rename from src/sourcetree/SourcesModel.h rename to src/tomahawk/sourcetree/SourcesModel.h diff --git a/src/sourcetree/SourcesProxyModel.cpp b/src/tomahawk/sourcetree/SourcesProxyModel.cpp similarity index 100% rename from src/sourcetree/SourcesProxyModel.cpp rename to src/tomahawk/sourcetree/SourcesProxyModel.cpp diff --git a/src/sourcetree/SourcesProxyModel.h b/src/tomahawk/sourcetree/SourcesProxyModel.h similarity index 100% rename from src/sourcetree/SourcesProxyModel.h rename to src/tomahawk/sourcetree/SourcesProxyModel.h diff --git a/src/sourcetree/items/CategoryItems.cpp b/src/tomahawk/sourcetree/items/CategoryItems.cpp similarity index 100% rename from src/sourcetree/items/CategoryItems.cpp rename to src/tomahawk/sourcetree/items/CategoryItems.cpp diff --git a/src/sourcetree/items/CategoryItems.h b/src/tomahawk/sourcetree/items/CategoryItems.h similarity index 100% rename from src/sourcetree/items/CategoryItems.h rename to src/tomahawk/sourcetree/items/CategoryItems.h diff --git a/src/sourcetree/items/GenericPageItems.cpp b/src/tomahawk/sourcetree/items/GenericPageItems.cpp similarity index 100% rename from src/sourcetree/items/GenericPageItems.cpp rename to src/tomahawk/sourcetree/items/GenericPageItems.cpp diff --git a/src/sourcetree/items/GenericPageItems.h b/src/tomahawk/sourcetree/items/GenericPageItems.h similarity index 100% rename from src/sourcetree/items/GenericPageItems.h rename to src/tomahawk/sourcetree/items/GenericPageItems.h diff --git a/src/sourcetree/items/GroupItem.cpp b/src/tomahawk/sourcetree/items/GroupItem.cpp similarity index 100% rename from src/sourcetree/items/GroupItem.cpp rename to src/tomahawk/sourcetree/items/GroupItem.cpp diff --git a/src/sourcetree/items/GroupItem.h b/src/tomahawk/sourcetree/items/GroupItem.h similarity index 100% rename from src/sourcetree/items/GroupItem.h rename to src/tomahawk/sourcetree/items/GroupItem.h diff --git a/src/sourcetree/items/HistoryItem.cpp b/src/tomahawk/sourcetree/items/HistoryItem.cpp similarity index 100% rename from src/sourcetree/items/HistoryItem.cpp rename to src/tomahawk/sourcetree/items/HistoryItem.cpp diff --git a/src/sourcetree/items/HistoryItem.h b/src/tomahawk/sourcetree/items/HistoryItem.h similarity index 100% rename from src/sourcetree/items/HistoryItem.h rename to src/tomahawk/sourcetree/items/HistoryItem.h diff --git a/src/sourcetree/items/InboxItem.cpp b/src/tomahawk/sourcetree/items/InboxItem.cpp similarity index 100% rename from src/sourcetree/items/InboxItem.cpp rename to src/tomahawk/sourcetree/items/InboxItem.cpp diff --git a/src/sourcetree/items/InboxItem.h b/src/tomahawk/sourcetree/items/InboxItem.h similarity index 100% rename from src/sourcetree/items/InboxItem.h rename to src/tomahawk/sourcetree/items/InboxItem.h diff --git a/src/sourcetree/items/LovedTracksItem.cpp b/src/tomahawk/sourcetree/items/LovedTracksItem.cpp similarity index 100% rename from src/sourcetree/items/LovedTracksItem.cpp rename to src/tomahawk/sourcetree/items/LovedTracksItem.cpp diff --git a/src/sourcetree/items/LovedTracksItem.h b/src/tomahawk/sourcetree/items/LovedTracksItem.h similarity index 100% rename from src/sourcetree/items/LovedTracksItem.h rename to src/tomahawk/sourcetree/items/LovedTracksItem.h diff --git a/src/sourcetree/items/PlaylistItems.cpp b/src/tomahawk/sourcetree/items/PlaylistItems.cpp similarity index 100% rename from src/sourcetree/items/PlaylistItems.cpp rename to src/tomahawk/sourcetree/items/PlaylistItems.cpp diff --git a/src/sourcetree/items/PlaylistItems.h b/src/tomahawk/sourcetree/items/PlaylistItems.h similarity index 100% rename from src/sourcetree/items/PlaylistItems.h rename to src/tomahawk/sourcetree/items/PlaylistItems.h diff --git a/src/sourcetree/items/ScriptCollectionItem.cpp b/src/tomahawk/sourcetree/items/ScriptCollectionItem.cpp similarity index 100% rename from src/sourcetree/items/ScriptCollectionItem.cpp rename to src/tomahawk/sourcetree/items/ScriptCollectionItem.cpp diff --git a/src/sourcetree/items/ScriptCollectionItem.h b/src/tomahawk/sourcetree/items/ScriptCollectionItem.h similarity index 100% rename from src/sourcetree/items/ScriptCollectionItem.h rename to src/tomahawk/sourcetree/items/ScriptCollectionItem.h diff --git a/src/sourcetree/items/SourceItem.cpp b/src/tomahawk/sourcetree/items/SourceItem.cpp similarity index 100% rename from src/sourcetree/items/SourceItem.cpp rename to src/tomahawk/sourcetree/items/SourceItem.cpp diff --git a/src/sourcetree/items/SourceItem.h b/src/tomahawk/sourcetree/items/SourceItem.h similarity index 100% rename from src/sourcetree/items/SourceItem.h rename to src/tomahawk/sourcetree/items/SourceItem.h diff --git a/src/sourcetree/items/SourceTreeItem.cpp b/src/tomahawk/sourcetree/items/SourceTreeItem.cpp similarity index 100% rename from src/sourcetree/items/SourceTreeItem.cpp rename to src/tomahawk/sourcetree/items/SourceTreeItem.cpp diff --git a/src/sourcetree/items/SourceTreeItem.h b/src/tomahawk/sourcetree/items/SourceTreeItem.h similarity index 100% rename from src/sourcetree/items/SourceTreeItem.h rename to src/tomahawk/sourcetree/items/SourceTreeItem.h diff --git a/src/sourcetree/items/TemporaryPageItem.cpp b/src/tomahawk/sourcetree/items/TemporaryPageItem.cpp similarity index 100% rename from src/sourcetree/items/TemporaryPageItem.cpp rename to src/tomahawk/sourcetree/items/TemporaryPageItem.cpp diff --git a/src/sourcetree/items/TemporaryPageItem.h b/src/tomahawk/sourcetree/items/TemporaryPageItem.h similarity index 100% rename from src/sourcetree/items/TemporaryPageItem.h rename to src/tomahawk/sourcetree/items/TemporaryPageItem.h diff --git a/src/web/Api_v1.cpp b/src/tomahawk/web/Api_v1.cpp similarity index 100% rename from src/web/Api_v1.cpp rename to src/tomahawk/web/Api_v1.cpp diff --git a/src/web/Api_v1.h b/src/tomahawk/web/Api_v1.h similarity index 100% rename from src/web/Api_v1.h rename to src/tomahawk/web/Api_v1.h diff --git a/src/widgets/AccountListView.cpp b/src/tomahawk/widgets/AccountListView.cpp similarity index 100% rename from src/widgets/AccountListView.cpp rename to src/tomahawk/widgets/AccountListView.cpp diff --git a/src/widgets/AccountListView.h b/src/tomahawk/widgets/AccountListView.h similarity index 100% rename from src/widgets/AccountListView.h rename to src/tomahawk/widgets/AccountListView.h diff --git a/src/widgets/AccountListWidget.cpp b/src/tomahawk/widgets/AccountListWidget.cpp similarity index 100% rename from src/widgets/AccountListWidget.cpp rename to src/tomahawk/widgets/AccountListWidget.cpp diff --git a/src/widgets/AccountListWidget.h b/src/tomahawk/widgets/AccountListWidget.h similarity index 100% rename from src/widgets/AccountListWidget.h rename to src/tomahawk/widgets/AccountListWidget.h diff --git a/src/widgets/AccountModelFactoryProxy.cpp b/src/tomahawk/widgets/AccountModelFactoryProxy.cpp similarity index 100% rename from src/widgets/AccountModelFactoryProxy.cpp rename to src/tomahawk/widgets/AccountModelFactoryProxy.cpp diff --git a/src/widgets/AccountModelFactoryProxy.h b/src/tomahawk/widgets/AccountModelFactoryProxy.h similarity index 100% rename from src/widgets/AccountModelFactoryProxy.h rename to src/tomahawk/widgets/AccountModelFactoryProxy.h diff --git a/src/widgets/AccountWidget.cpp b/src/tomahawk/widgets/AccountWidget.cpp similarity index 100% rename from src/widgets/AccountWidget.cpp rename to src/tomahawk/widgets/AccountWidget.cpp diff --git a/src/widgets/AccountWidget.h b/src/tomahawk/widgets/AccountWidget.h similarity index 100% rename from src/widgets/AccountWidget.h rename to src/tomahawk/widgets/AccountWidget.h diff --git a/src/widgets/AccountsPopupWidget.cpp b/src/tomahawk/widgets/AccountsPopupWidget.cpp similarity index 100% rename from src/widgets/AccountsPopupWidget.cpp rename to src/tomahawk/widgets/AccountsPopupWidget.cpp diff --git a/src/widgets/AccountsPopupWidget.h b/src/tomahawk/widgets/AccountsPopupWidget.h similarity index 100% rename from src/widgets/AccountsPopupWidget.h rename to src/tomahawk/widgets/AccountsPopupWidget.h diff --git a/src/widgets/AccountsToolButton.cpp b/src/tomahawk/widgets/AccountsToolButton.cpp similarity index 100% rename from src/widgets/AccountsToolButton.cpp rename to src/tomahawk/widgets/AccountsToolButton.cpp diff --git a/src/widgets/AccountsToolButton.h b/src/tomahawk/widgets/AccountsToolButton.h similarity index 100% rename from src/widgets/AccountsToolButton.h rename to src/tomahawk/widgets/AccountsToolButton.h diff --git a/src/widgets/ContainedMenuButton.cpp b/src/tomahawk/widgets/ContainedMenuButton.cpp similarity index 100% rename from src/widgets/ContainedMenuButton.cpp rename to src/tomahawk/widgets/ContainedMenuButton.cpp diff --git a/src/widgets/ContainedMenuButton.h b/src/tomahawk/widgets/ContainedMenuButton.h similarity index 100% rename from src/widgets/ContainedMenuButton.h rename to src/tomahawk/widgets/ContainedMenuButton.h diff --git a/src/widgets/SlideSwitchButton.cpp b/src/tomahawk/widgets/SlideSwitchButton.cpp similarity index 100% rename from src/widgets/SlideSwitchButton.cpp rename to src/tomahawk/widgets/SlideSwitchButton.cpp diff --git a/src/widgets/SlideSwitchButton.h b/src/tomahawk/widgets/SlideSwitchButton.h similarity index 100% rename from src/widgets/SlideSwitchButton.h rename to src/tomahawk/widgets/SlideSwitchButton.h diff --git a/src/widgets/UnstyledFrame.cpp b/src/tomahawk/widgets/UnstyledFrame.cpp similarity index 100% rename from src/widgets/UnstyledFrame.cpp rename to src/tomahawk/widgets/UnstyledFrame.cpp diff --git a/src/widgets/UnstyledFrame.h b/src/tomahawk/widgets/UnstyledFrame.h similarity index 100% rename from src/widgets/UnstyledFrame.h rename to src/tomahawk/widgets/UnstyledFrame.h diff --git a/src/xmppbot/XmppBot.cpp b/src/tomahawk/xmppbot/XmppBot.cpp similarity index 100% rename from src/xmppbot/XmppBot.cpp rename to src/tomahawk/xmppbot/XmppBot.cpp diff --git a/src/xmppbot/XmppBot.h b/src/tomahawk/xmppbot/XmppBot.h similarity index 100% rename from src/xmppbot/XmppBot.h rename to src/tomahawk/xmppbot/XmppBot.h From af2336b32f7885678197b73e97562a1f996b6c1e Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 24 May 2013 11:23:22 +0200 Subject: [PATCH 085/565] Adjust cmake to build again and fix include errors These are partly hacks - I split the commit for referencing the hacks afterwards --- CMakeLists.txt | 1 - src/CMakeLists.txt | 13 +++++++++++++ src/accounts/twitter/TwitterAccount.cpp | 4 ++-- src/accounts/twitter/TwitterInfoPlugin.cpp | 2 +- src/accounts/twitter/TwitterInfoPlugin.h | 2 +- src/infoplugins/generic/charts/ChartsPlugin.h | 3 ++- src/infoplugins/generic/discogs/DiscogsPlugin.h | 3 ++- .../generic/echonest/EchonestPlugin.h | 3 ++- src/infoplugins/generic/hypem/HypemPlugin.h | 3 ++- .../generic/musicbrainz/MusicBrainzPlugin.h | 3 ++- .../generic/musixmatch/MusixMatchPlugin.h | 4 +++- .../generic/newreleases/NewReleasesPlugin.h | 4 +++- src/infoplugins/generic/rovi/RoviPlugin.h | 4 +++- src/infoplugins/generic/spotify/SpotifyPlugin.h | 4 +++- .../linux/fdonotify/FdoNotifyPlugin.h | 3 ++- src/infoplugins/linux/mpris/MprisPlugin.h | 4 +++- src/infoplugins/mac/adium/AdiumPlugin.h | 3 ++- src/libtomahawk/CMakeLists.txt | 6 ++---- src/tomahawk/CMakeLists.txt | 16 +++------------- src/tomahawk/TomahawkApp.cpp | 2 +- 20 files changed, 52 insertions(+), 35 deletions(-) create mode 100644 src/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 000c973e65..f74590f5a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,7 +311,6 @@ SET( TOMAHAWK_LIBRARIES tomahawklib ) ADD_SUBDIRECTORY( thirdparty ) ADD_SUBDIRECTORY( src ) -ADD_SUBDIRECTORY( src/libtomahawk ) ADD_SUBDIRECTORY( admin ) if( BUILD_TESTS ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000000..c5f4321649 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,13 @@ +setup_qt() + +add_subdirectory( libtomahawk ) + +include_directories( ${CMAKE_CURRENT_BINARY_DIR}/libtomahawk ) +include_directories( ${CMAKE_CURRENT_LIST_DIR}/libtomahawk ) + +add_subdirectory( tomahawk ) + +add_subdirectory( breakpad/CrashReporter/ ) + +add_subdirectory( accounts ) +add_subdirectory( infoplugins ) \ No newline at end of file diff --git a/src/accounts/twitter/TwitterAccount.cpp b/src/accounts/twitter/TwitterAccount.cpp index e28191cc3a..4dd95d6016 100644 --- a/src/accounts/twitter/TwitterAccount.cpp +++ b/src/accounts/twitter/TwitterAccount.cpp @@ -20,8 +20,8 @@ #include "TwitterAccount.h" #include "TwitterConfigWidget.h" -#include "accounts/twitter/TomahawkOAuthTwitter.h" -#include "libtomahawk/infosystem/InfoSystem.h" +#include "TomahawkOAuthTwitter.h" +#include "infosystem/InfoSystem.h" #include "utils/Logger.h" #include "sip/SipPlugin.h" diff --git a/src/accounts/twitter/TwitterInfoPlugin.cpp b/src/accounts/twitter/TwitterInfoPlugin.cpp index c78cc630b8..f6342dc3aa 100644 --- a/src/accounts/twitter/TwitterInfoPlugin.cpp +++ b/src/accounts/twitter/TwitterInfoPlugin.cpp @@ -20,7 +20,7 @@ #include "TwitterInfoPlugin.h" -#include "accounts/twitter/TwitterAccount.h" +#include "TwitterAccount.h" #include "GlobalActionManager.h" #include "utils/Logger.h" diff --git a/src/accounts/twitter/TwitterInfoPlugin.h b/src/accounts/twitter/TwitterInfoPlugin.h index 893249a840..187df4c6c2 100644 --- a/src/accounts/twitter/TwitterInfoPlugin.h +++ b/src/accounts/twitter/TwitterInfoPlugin.h @@ -21,7 +21,7 @@ #define TWITTERINFOPLUGIN_H #include "infosystem/InfoSystem.h" -#include "accounts/twitter/TomahawkOAuthTwitter.h" +#include "TomahawkOAuthTwitter.h" #include #include diff --git a/src/infoplugins/generic/charts/ChartsPlugin.h b/src/infoplugins/generic/charts/ChartsPlugin.h index f7380c90be..17722373a4 100644 --- a/src/infoplugins/generic/charts/ChartsPlugin.h +++ b/src/infoplugins/generic/charts/ChartsPlugin.h @@ -20,7 +20,8 @@ #ifndef ChartsPlugin_H #define ChartsPlugin_H -#include "infoplugins/InfoPluginDllMacro.h" +#include "../../InfoPluginDllMacro.h" + #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" #include diff --git a/src/infoplugins/generic/discogs/DiscogsPlugin.h b/src/infoplugins/generic/discogs/DiscogsPlugin.h index ecf3ac8342..8a3c498e2f 100644 --- a/src/infoplugins/generic/discogs/DiscogsPlugin.h +++ b/src/infoplugins/generic/discogs/DiscogsPlugin.h @@ -22,7 +22,8 @@ #include "Typedefs.h" #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" -#include "infoplugins/InfoPluginDllMacro.h" + +#include "../../InfoPluginDllMacro.h" class QNetworkReply; diff --git a/src/infoplugins/generic/echonest/EchonestPlugin.h b/src/infoplugins/generic/echonest/EchonestPlugin.h index e461060b14..62b70db3fc 100644 --- a/src/infoplugins/generic/echonest/EchonestPlugin.h +++ b/src/infoplugins/generic/echonest/EchonestPlugin.h @@ -20,7 +20,8 @@ #ifndef ECHONESTPLUGIN_H #define ECHONESTPLUGIN_H -#include "infoplugins/InfoPluginDllMacro.h" +#include "../../InfoPluginDllMacro.h" + #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" #include "echonest/Artist.h" diff --git a/src/infoplugins/generic/hypem/HypemPlugin.h b/src/infoplugins/generic/hypem/HypemPlugin.h index d0c9502e02..e3e66bc79d 100644 --- a/src/infoplugins/generic/hypem/HypemPlugin.h +++ b/src/infoplugins/generic/hypem/HypemPlugin.h @@ -20,7 +20,8 @@ #ifndef HYPEMPLUGIN_H #define HYPEMPLUGIN_H -#include "infoplugins/InfoPluginDllMacro.h" +#include "../../InfoPluginDllMacro.h" + #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" #include diff --git a/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h index ac278bd111..f48aa04767 100644 --- a/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h +++ b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h @@ -22,7 +22,8 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" -#include "infoplugins/InfoPluginDllMacro.h" + +#include "../../InfoPluginDllMacro.h" class QNetworkReply; diff --git a/src/infoplugins/generic/musixmatch/MusixMatchPlugin.h b/src/infoplugins/generic/musixmatch/MusixMatchPlugin.h index 96c5dbad28..17495d42e0 100644 --- a/src/infoplugins/generic/musixmatch/MusixMatchPlugin.h +++ b/src/infoplugins/generic/musixmatch/MusixMatchPlugin.h @@ -22,7 +22,9 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" -#include "infoplugins/InfoPluginDllMacro.h" + +#include "../../InfoPluginDllMacro.h" + class QNetworkReply; diff --git a/src/infoplugins/generic/newreleases/NewReleasesPlugin.h b/src/infoplugins/generic/newreleases/NewReleasesPlugin.h index 212b058bd2..7af94a5ad7 100644 --- a/src/infoplugins/generic/newreleases/NewReleasesPlugin.h +++ b/src/infoplugins/generic/newreleases/NewReleasesPlugin.h @@ -22,7 +22,9 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" -#include "infoplugins/InfoPluginDllMacro.h" + +#include "../../InfoPluginDllMacro.h" + #include #include #include diff --git a/src/infoplugins/generic/rovi/RoviPlugin.h b/src/infoplugins/generic/rovi/RoviPlugin.h index 87fc3bc08d..715c608bb7 100644 --- a/src/infoplugins/generic/rovi/RoviPlugin.h +++ b/src/infoplugins/generic/rovi/RoviPlugin.h @@ -21,7 +21,9 @@ #define ROVIPLUGIN_H #include "infosystem/InfoSystem.h" -#include "infoplugins/InfoPluginDllMacro.h" + +#include "../../InfoPluginDllMacro.h" + #include diff --git a/src/infoplugins/generic/spotify/SpotifyPlugin.h b/src/infoplugins/generic/spotify/SpotifyPlugin.h index b8de3967d1..69625dcbab 100644 --- a/src/infoplugins/generic/spotify/SpotifyPlugin.h +++ b/src/infoplugins/generic/spotify/SpotifyPlugin.h @@ -22,7 +22,9 @@ #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" -#include "infoplugins/InfoPluginDllMacro.h" + +#include "../../InfoPluginDllMacro.h" + #include #include diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h index c9f3e78e00..ff4743cdd6 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h @@ -20,7 +20,8 @@ #ifndef FDONOTIFYPLUGIN_H #define FDONOTIFYPLUGIN_H -#include "infoplugins/InfoPluginDllMacro.h" +#include "../../InfoPluginDllMacro.h" + #include "infosystem/InfoSystem.h" #include diff --git a/src/infoplugins/linux/mpris/MprisPlugin.h b/src/infoplugins/linux/mpris/MprisPlugin.h index 576979be1d..a5997cf55c 100644 --- a/src/infoplugins/linux/mpris/MprisPlugin.h +++ b/src/infoplugins/linux/mpris/MprisPlugin.h @@ -24,7 +24,9 @@ #include "Source.h" #include "audio/AudioEngine.h" #include "infosystem/InfoSystem.h" -#include "infoplugins/InfoPluginDllMacro.h" + +#include "../../InfoPluginDllMacro.h" + #include #include diff --git a/src/infoplugins/mac/adium/AdiumPlugin.h b/src/infoplugins/mac/adium/AdiumPlugin.h index 02980eff74..b0fe123d4d 100644 --- a/src/infoplugins/mac/adium/AdiumPlugin.h +++ b/src/infoplugins/mac/adium/AdiumPlugin.h @@ -20,7 +20,8 @@ #ifndef ADIUMPLUGIN_H #define ADIUMPLUGIN_H -#include "infoplugins/InfoPluginDllMacro.h" +#include "../../InfoPluginDllMacro.h" + #include "infosystem/InfoSystem.h" #include diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 0575e4db34..48b53970f1 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -1,16 +1,14 @@ project( tomahawklib ) -setup_qt() - add_definitions( ${QT_DEFINITIONS} ) add_definitions( -DQT_SHARED ) add_definitions( -DDLLEXPORT_PRO ) add_definitions( -DQT_SHAREDPOINTER_TRACK_POINTERS ) add_definitions( -DPORTFWDDLLEXPORT_STATIC ) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../Config.h.in +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../tomahawk/Config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../TomahawkVersion.h.in +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../tomahawk/TomahawkVersion.h.in ${CMAKE_CURRENT_BINARY_DIR}/TomahawkVersion.h) set( libGuiSources diff --git a/src/tomahawk/CMakeLists.txt b/src/tomahawk/CMakeLists.txt index 5ae0c86558..636ff2c3c6 100644 --- a/src/tomahawk/CMakeLists.txt +++ b/src/tomahawk/CMakeLists.txt @@ -1,9 +1,6 @@ PROJECT( tomahawk ) CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) - -setup_qt() - include( AddAppIconMacro ) # SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" ) @@ -93,10 +90,10 @@ SET( tomahawkUI ${tomahawkUI} INCLUDE_DIRECTORIES( . ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/libtomahawk + ${CMAKE_CURRENT_BINARY_DIR}/../libtomahawk sourcetree - libtomahawk + ../libtomahawk mac ${THIRDPARTY_DIR}/breakpad @@ -132,9 +129,6 @@ IF( APPLE ) SET( tomahawkSources ${tomahawkSources} mac/TomahawkApp_Mac.mm mac/MacShortcutHandler.cpp ) ENDIF( APPLE ) -ADD_SUBDIRECTORY( accounts ) -ADD_SUBDIRECTORY( infoplugins ) - IF(QCA2_FOUND) INCLUDE_DIRECTORIES( ${QCA2_INCLUDE_DIR} ) ENDIF(QCA2_FOUND) @@ -156,14 +150,10 @@ SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${trans_outfile}) IF( BUILD_GUI ) LIST(APPEND tomahawkSources ${tomahawkSourcesGui}) qt_wrap_ui( tomahawkUI_H ${tomahawkUI} ) - - IF( WITH_CRASHREPORTER ) - ADD_SUBDIRECTORY( breakpad/CrashReporter ) - ENDIF() ENDIF() kde4_add_app_icon( tomahawkSources "${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon-*.png" ) -qt_add_resources( RC_SRCS "../resources.qrc" ) +qt_add_resources( RC_SRCS "../../resources.qrc" ) SET( final_src ${final_src} ${tomahawkUI_H} ${tomahawkMoc} ${tomahawkSources} ${RC_SRCS} ) diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index c20fe28d3f..c77ca8e21c 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -46,7 +46,7 @@ #include "web/Api_v1.h" #include "SourceList.h" #include "ShortcutHandler.h" -#include "libtomahawk/filemetadata/ScanManager.h" +#include "filemetadata/ScanManager.h" #include "TomahawkSettings.h" #include "GlobalActionManager.h" #include "database/LocalCollection.h" From cb10c59215f3fb246c8c1fdff5a19f060638d2d2 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 24 May 2013 11:48:46 +0200 Subject: [PATCH 086/565] Build all subdirs of src/ automatically --- src/CMakeLists.txt | 14 +++++++------- src/infoplugins/CMakeLists.txt | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c5f4321649..7ba4d839f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,13 +1,13 @@ setup_qt() -add_subdirectory( libtomahawk ) - include_directories( ${CMAKE_CURRENT_BINARY_DIR}/libtomahawk ) include_directories( ${CMAKE_CURRENT_LIST_DIR}/libtomahawk ) -add_subdirectory( tomahawk ) - -add_subdirectory( breakpad/CrashReporter/ ) +file(GLOB SUBDIRECTORIES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*") -add_subdirectory( accounts ) -add_subdirectory( infoplugins ) \ No newline at end of file +foreach(SUBDIRECTORY ${SUBDIRECTORIES}) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}" AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}/CMakeLists.txt") + message(STATUS "build: " ${SUBDIRECTORY}) + add_subdirectory( ${SUBDIRECTORY} ) + endif() +endforeach() diff --git a/src/infoplugins/CMakeLists.txt b/src/infoplugins/CMakeLists.txt index eadafe5a19..6906edeb2a 100644 --- a/src/infoplugins/CMakeLists.txt +++ b/src/infoplugins/CMakeLists.txt @@ -1,3 +1,5 @@ +include( ${CMAKE_CURRENT_LIST_DIR}/../../TomahawkAddPlugin.cmake ) + add_subdirectory( generic ) if(UNIX AND NOT APPLE) From 25be3d4ad745f1d71887fcd088ecb1beca246a4a Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Fri, 24 May 2013 11:49:39 +0200 Subject: [PATCH 087/565] Move src/breakdpad/CrashReporter to src/crashreporter --- src/{breakpad/CrashReporter => crashreporter}/CMakeLists.txt | 2 +- src/{breakpad/CrashReporter => crashreporter}/CrashReporter.cpp | 0 src/{breakpad/CrashReporter => crashreporter}/CrashReporter.h | 0 src/{breakpad/CrashReporter => crashreporter}/CrashReporter.ui | 0 src/{breakpad/CrashReporter => crashreporter}/main.cpp | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename src/{breakpad/CrashReporter => crashreporter}/CMakeLists.txt (94%) rename src/{breakpad/CrashReporter => crashreporter}/CrashReporter.cpp (100%) rename src/{breakpad/CrashReporter => crashreporter}/CrashReporter.h (100%) rename src/{breakpad/CrashReporter => crashreporter}/CrashReporter.ui (100%) rename src/{breakpad/CrashReporter => crashreporter}/main.cpp (100%) diff --git a/src/breakpad/CrashReporter/CMakeLists.txt b/src/crashreporter/CMakeLists.txt similarity index 94% rename from src/breakpad/CrashReporter/CMakeLists.txt rename to src/crashreporter/CMakeLists.txt index 622d4843ba..ee1628cb19 100644 --- a/src/breakpad/CrashReporter/CMakeLists.txt +++ b/src/crashreporter/CMakeLists.txt @@ -5,7 +5,7 @@ setup_qt() set(crashreporter_SOURCES main.cpp CrashReporter.cpp) set(crashreporter_UI CrashReporter.ui) -set(crashreporter_RC ../../../resources.qrc) +set(crashreporter_RC ../../resources.qrc) qt_wrap_ui( crashreporter_UI_HEADERS ${crashreporter_UI} ) qt_add_resources( crashreporter_RC_RCC ${crashreporter_RC} ) diff --git a/src/breakpad/CrashReporter/CrashReporter.cpp b/src/crashreporter/CrashReporter.cpp similarity index 100% rename from src/breakpad/CrashReporter/CrashReporter.cpp rename to src/crashreporter/CrashReporter.cpp diff --git a/src/breakpad/CrashReporter/CrashReporter.h b/src/crashreporter/CrashReporter.h similarity index 100% rename from src/breakpad/CrashReporter/CrashReporter.h rename to src/crashreporter/CrashReporter.h diff --git a/src/breakpad/CrashReporter/CrashReporter.ui b/src/crashreporter/CrashReporter.ui similarity index 100% rename from src/breakpad/CrashReporter/CrashReporter.ui rename to src/crashreporter/CrashReporter.ui diff --git a/src/breakpad/CrashReporter/main.cpp b/src/crashreporter/main.cpp similarity index 100% rename from src/breakpad/CrashReporter/main.cpp rename to src/crashreporter/main.cpp From 2dee4ccca37813b38e270bfbbe2ce1d0f33b14e8 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 24 May 2013 15:35:57 +0200 Subject: [PATCH 088/565] Use QKeySequence::Find to focus the search widget --- src/tomahawk/TomahawkWindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tomahawk/TomahawkWindow.cpp b/src/tomahawk/TomahawkWindow.cpp index 2e81f08e97..631715f6fc 100644 --- a/src/tomahawk/TomahawkWindow.cpp +++ b/src/tomahawk/TomahawkWindow.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -253,7 +254,6 @@ TomahawkWindow::applyPlatformTweaks() #endif } - void TomahawkWindow::setupToolBar() { @@ -292,6 +292,9 @@ TomahawkWindow::setupToolBar() m_searchWidget->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); m_searchWidget->setFixedWidth( 340 ); connect( m_searchWidget, SIGNAL( returnPressed() ), this, SLOT( onFilterEdited() ) ); + // Use Ctrl+F to focus the searchWidget + QShortcut *shortcut = new QShortcut( QKeySequence( QKeySequence::Find ), this ); + QObject::connect( shortcut, SIGNAL( activated() ), m_searchWidget, SLOT( setFocus() ) ); m_toolbar->addWidget( m_searchWidget )->setProperty( "kind", QString( "search" ) ); From 2185b3ae5a34ff0839c5f9f21b3539780d1c19cf Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Fri, 24 May 2013 16:26:09 +0200 Subject: [PATCH 089/565] Fix loading of playlist updaters. --- src/libtomahawk/collection/Collection.cpp | 16 +++++++++++++++- src/libtomahawk/collection/Collection.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/collection/Collection.cpp b/src/libtomahawk/collection/Collection.cpp index 693b6049f7..520970f3cc 100644 --- a/src/libtomahawk/collection/Collection.cpp +++ b/src/libtomahawk/collection/Collection.cpp @@ -24,6 +24,7 @@ #include "utils/Logger.h" #include "playlist/PlaylistUpdaterInterface.h" #include "utils/ImageRegistry.h" +#include "accounts/AccountManager.h" #include #include @@ -231,12 +232,25 @@ Collection::setPlaylists( const QList& plists ) // qDebug() << "Batch inserting playlist:" << p->guid(); m_playlists.insert( p->guid(), p ); if ( !m_source.isNull() && m_source->isLocal() ) - PlaylistUpdaterInterface::loadForPlaylist( p ); + { + if ( Tomahawk::Accounts::AccountManager::instance()->isReady() ) + doLoadPlaylistUpdater( p ); + else + NewClosure( Tomahawk::Accounts::AccountManager::instance(), SIGNAL( ready() ), + this, SLOT( doLoadPlaylistUpdater( playlist_ptr ) ), p ); + } } emit playlistsAdded( plists ); } +void +Collection::doLoadPlaylistUpdater( const playlist_ptr& p ) +{ + PlaylistUpdaterInterface::loadForPlaylist( p ); +} + + void Collection::setAutoPlaylists( const QList< Tomahawk::dynplaylist_ptr >& plists ) { diff --git a/src/libtomahawk/collection/Collection.h b/src/libtomahawk/collection/Collection.h index 574dce637e..0fa2b52bcd 100644 --- a/src/libtomahawk/collection/Collection.h +++ b/src/libtomahawk/collection/Collection.h @@ -129,6 +129,7 @@ public slots: private slots: void onSynced(); + void doLoadPlaylistUpdater( const playlist_ptr& p ); private: bool m_changed; From 9d42209e96f5dc12c1f73399b7f3f44c3cfcd979 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 23 May 2013 21:15:59 -0400 Subject: [PATCH 090/565] Use removeOne, not removeAll, as there won't be more than one --- src/libtomahawk/accounts/CredentialsManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/accounts/CredentialsManager.cpp b/src/libtomahawk/accounts/CredentialsManager.cpp index e021afe567..a7745cd4d8 100644 --- a/src/libtomahawk/accounts/CredentialsManager.cpp +++ b/src/libtomahawk/accounts/CredentialsManager.cpp @@ -144,7 +144,7 @@ CredentialsManager::keychainJobFinished( QKeychain::Job* j ) tDebug() << "QtKeychain readJob finished with error:" << j->error() << j->errorString(); } - m_readJobs.removeAll( readJob ); + m_readJobs.removeOne( readJob ); if ( m_readJobs.isEmpty() ) { From 4497849d0778f3c61e160feba8b57743d13cc57d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 23 May 2013 21:05:59 -0400 Subject: [PATCH 091/565] Fix spelling --- src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.cpp b/src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.cpp index e2e1b6723a..10934a06eb 100644 --- a/src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.cpp +++ b/src/libtomahawk/accounts/spotify/SpotifyPlaylistUpdater.cpp @@ -52,7 +52,7 @@ SpotifyUpdaterFactory::create( const Tomahawk::playlist_ptr& pl, const QVariantH if ( m_account.isNull() ) { - qWarning() << "Found a spotify updater with no spotify account... ignoreing for now!!"; + qWarning() << "Found a spotify updater with no spotify account... ignoring for now!!"; return 0; } From 8798f2103eafd0a0e9872d824ded292e6c8fb1d4 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Fri, 24 May 2013 18:30:01 +0200 Subject: [PATCH 092/565] Avoid crash when propagating dbcmd_ShareTrack. --- .../database/DatabaseCommand_ShareTrack.cpp | 25 +++++++++++++------ src/libtomahawk/playlist/InboxModel.cpp | 23 +++++++++++++++++ src/libtomahawk/playlist/InboxModel.h | 9 +++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp b/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp index 1475f59b37..a97bcbab06 100644 --- a/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ShareTrack.cpp @@ -73,11 +73,16 @@ DatabaseCommand_ShareTrack::postCommitHook() QString myDbid = SourceList::instance()->getLocal()->nodeId(); QString sourceDbid = source()->nodeId(); - if ( source()->isLocal() || sourceDbid != m_recipient ) //if I just sent a track + qRegisterMetaType< InboxJobItem::Side >("InboxJobItem::Side"); + qRegisterMetaType< Tomahawk::trackdata_ptr >("Tomahawk::trackdata_ptr"); + if ( source()->isLocal() && sourceDbid != m_recipient ) //if I just sent a track { - JobStatusView::instance()->model()->addJob( new InboxJobItem( InboxJobItem::Sending, - SourceList::instance()->get( m_recipient )->friendlyName(), - m_track ) ); + QMetaObject::invokeMethod( ViewManager::instance()->inboxModel(), + "showNotification", + Qt::QueuedConnection, + Q_ARG( InboxJobItem::Side, InboxJobItem::Sending ), + Q_ARG( const QString&, m_recipient ), + Q_ARG( const Tomahawk::trackdata_ptr&, m_track ) ); } if ( m_track ) @@ -107,11 +112,15 @@ DatabaseCommand_ShareTrack::postCommitHook() Q_ARG( const Tomahawk::query_ptr&, m_track->toQuery() ), Q_ARG( int, 0 ) /*row*/ ); - QString friendlyName = source()->friendlyName(); if ( ViewManager::instance()->currentPage() != ViewManager::instance()->inboxWidget() ) - JobStatusView::instance()->model()->addJob( new InboxJobItem( InboxJobItem::Receiving, - friendlyName, - m_track ) ); + { + QMetaObject::invokeMethod( ViewManager::instance()->inboxModel(), + "showNotification", + Qt::QueuedConnection, + Q_ARG( InboxJobItem::Side, InboxJobItem::Receiving ), + Q_ARG( const Tomahawk::source_ptr&, source() ), + Q_ARG( const Tomahawk::trackdata_ptr&, m_track ) ); + } } diff --git a/src/libtomahawk/playlist/InboxModel.cpp b/src/libtomahawk/playlist/InboxModel.cpp index 29e28c0250..dd16e30d72 100644 --- a/src/libtomahawk/playlist/InboxModel.cpp +++ b/src/libtomahawk/playlist/InboxModel.cpp @@ -25,6 +25,7 @@ #include "SourceList.h" #include "utils/Logger.h" #include "utils/Closure.h" +#include "jobview/JobStatusModel.h" InboxModel::InboxModel( QObject* parent ) @@ -129,6 +130,28 @@ InboxModel::clear() } +void +InboxModel::showNotification( InboxJobItem::Side side, + const Tomahawk::source_ptr& src, + const Tomahawk::trackdata_ptr& track ) +{ + JobStatusView::instance()->model()->addJob( new InboxJobItem( side, + src->friendlyName(), + track ) ); +} + + +void +InboxModel::showNotification( InboxJobItem::Side side, + const QString& dbid, + const Tomahawk::trackdata_ptr& track ) +{ + Tomahawk::source_ptr src = SourceList::instance()->get( dbid ); + if ( !src.isNull() ) + showNotification( side, src, track ); +} + + void InboxModel::loadTracks() { diff --git a/src/libtomahawk/playlist/InboxModel.h b/src/libtomahawk/playlist/InboxModel.h index 0b4f1f57fd..f593cd8105 100644 --- a/src/libtomahawk/playlist/InboxModel.h +++ b/src/libtomahawk/playlist/InboxModel.h @@ -22,6 +22,7 @@ #include "PlaylistModel.h" #include "Typedefs.h" #include "DllMacro.h" +#include "jobview/InboxJobItem.h" class DLLEXPORT InboxModel : public PlaylistModel @@ -45,6 +46,14 @@ public slots: virtual void clear(); + virtual void showNotification( InboxJobItem::Side side, + const Tomahawk::source_ptr& src, + const Tomahawk::trackdata_ptr& track ); //for lack of a better place to put this + virtual void showNotification( InboxJobItem::Side side, + const QString& dbid, + const Tomahawk::trackdata_ptr& track ); + + private slots: void loadTracks(); From 4c85b1d92d163a4840a08889c2ae23db7f21e8a0 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sat, 25 May 2013 02:16:58 +0200 Subject: [PATCH 093/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 420 +++++++++++++++++------------------ lang/tomahawk_bg.ts | 420 +++++++++++++++++------------------ lang/tomahawk_bn_IN.ts | 420 +++++++++++++++++------------------ lang/tomahawk_ca.ts | 420 +++++++++++++++++------------------ lang/tomahawk_ca@valencia.ts | 420 +++++++++++++++++------------------ lang/tomahawk_cs.ts | 420 +++++++++++++++++------------------ lang/tomahawk_da.ts | 420 +++++++++++++++++------------------ lang/tomahawk_de.ts | 420 +++++++++++++++++------------------ lang/tomahawk_el.ts | 420 +++++++++++++++++------------------ lang/tomahawk_en.ts | 420 +++++++++++++++++------------------ lang/tomahawk_es.ts | 420 +++++++++++++++++------------------ lang/tomahawk_fi.ts | 420 +++++++++++++++++------------------ lang/tomahawk_fr.ts | 420 +++++++++++++++++------------------ lang/tomahawk_gl.ts | 420 +++++++++++++++++------------------ lang/tomahawk_hi_IN.ts | 420 +++++++++++++++++------------------ lang/tomahawk_hu.ts | 420 +++++++++++++++++------------------ lang/tomahawk_id.ts | 420 +++++++++++++++++------------------ lang/tomahawk_it.ts | 420 +++++++++++++++++------------------ lang/tomahawk_ja.ts | 420 +++++++++++++++++------------------ lang/tomahawk_lt.ts | 420 +++++++++++++++++------------------ lang/tomahawk_pl.ts | 420 +++++++++++++++++------------------ lang/tomahawk_pt_BR.ts | 420 +++++++++++++++++------------------ lang/tomahawk_ru.ts | 420 +++++++++++++++++------------------ lang/tomahawk_sv.ts | 420 +++++++++++++++++------------------ lang/tomahawk_tr.ts | 420 +++++++++++++++++------------------ lang/tomahawk_zh_CN.ts | 420 +++++++++++++++++------------------ lang/tomahawk_zh_TW.ts | 420 +++++++++++++++++------------------ 27 files changed, 5670 insertions(+), 5670 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index dbc2a4b684..5759f61702 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -56,18 +56,18 @@ connect and stream from you? AccountListWidget - + Connections روابط - - + + Connect &All ربط &الجيمع - + Disconnect &All فصل &الجيمع @@ -75,7 +75,7 @@ connect and stream from you? AccountWidget - + Invite إدعو @@ -83,7 +83,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts تكوين الحسابات @@ -341,37 +341,37 @@ connect and stream from you? AudioControls - + Shuffle خلط - + Repeat إعادة - + Time Elapsed الوقت المنقضي - + Time Remaining الوقت المتبقي - + Playing from %1 يتم الاستماع من %1 - + Share شارك - + Love أحب @@ -397,24 +397,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist إنشاء قائمة أغاني جديدة - + Create new Station إنشاء إذاعة جديدة - - + + New Station إنشاء إذاعة جديدة - - + + %1 Station إذاعة %1 @@ -422,12 +422,12 @@ connect and stream from you? CategoryItem - + Playlists قائمات الأغاني - + Stations الإذاعات @@ -457,53 +457,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter مراسل توماهوك للانهيار - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">نتأسف!</span>&nbsp;توماهوك تحطم. يرجى أن تخبرنا عن ذلك! لقد أنشئ توماهوك تقرير عن الخطأ نيابة عنك لمساعدتنا في تحسين استقرار البرنامج في المستقبل. يمكنك إرسال هذا التقرير الأن مباشرة لمطوري توماهوك.</p></body></html> - + Send this report أرسل هذا التقرير - + Don't send لا ترسل - + Abort الغاء - + You can disable sending crash reports in the configuration dialog. يمكنك إلغاء إرسال تقارير الانهيار في خيارات الضبط. - + Uploaded %L1 of %L2 KB. تحميل %L1 من أصل %L2 كيلو بايت. - - + + Close اغلاق - + Sent! <b>Many thanks</b>. تم الإرسال! <b>شكرا</b>. - + Failed to send crash info. فشل في إرسال معلومات الانهيار. @@ -542,17 +542,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics تشخيص توماهوك - + &Copy to Clipboard &نسخ إلى الحافظة - + Open &Log-file فتح &ملف السجل @@ -679,7 +679,7 @@ connect and stream from you? InboxItem - + Inbox البريد الوارد @@ -775,27 +775,27 @@ connect and stream from you? LoadXSPF - + Load XSPF تحميل XSPF - + Playlist URL رابط قائمة الأغاني - + Enter URL... أدخل الرابط... - + ... ... - + Automatically update التحديث التلقائي @@ -803,12 +803,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File تحميل ملف XSPF - + XSPF Files (*.xspf) XSPF (*.xspf) ملف @@ -839,32 +839,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks أفضل الأغاني المحبوبة - + Sorry, we could not find any loved tracks! نعتذر، لم نستطيع إيجاد اي من الأغاني المحبوبة! - + The most loved tracks from all your friends أفضل الأغاني المحبوبة من جميع أصدقائك - + All of your loved tracks جميع أغانيك المحبوبة - + All of %1's loved tracks جميع أغاني "%1" المحبوبة - + Loved Tracks الأغاني المحبوبة @@ -1247,60 +1247,60 @@ connect and stream from you? ProxyDialog - + Proxy Settings إعدادات الوكيل - + Hostname of proxy server إسم المضيف لخادم الوسيط - + Host مضيف - + Port بوابة - + Proxy login تسجيل دخول الوسيط - + User المستخدم - + Password كلمة السر - + Proxy password كلمة السر للوكيل - + No Proxy Hosts: (Overrides system proxy) لا مضيف للوكيل: (تجاوز وكيل النظام) - + localhost *.example.com (space separated) localhost *.example.com (مفصولة بفراغ) - + Use proxy for DNS lookups? استخدام ألوكيل لعمليات البحث عن ألدي أن أس (DNS ) ؟ @@ -1451,12 +1451,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 لم يوجد: %1 - + Failed to load: %1 فشل في تحميل: %1 @@ -1516,77 +1516,77 @@ connect and stream from you? SettingsDialog - + Collection مجموعة - + Advanced متقدمة - + All الكل - + Some changed settings will not take effect until Tomahawk is restarted بعض الإعدادات التي تم تغييرها لن تصبح نافذة المفعول حتى يتم إعادة تشغيل توماهوك - + Services خدمات - + Install from file تثبيت من الملف - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. تكوين الحسابات والخدمات التي يستخدمها توماهوك للبحث واسترجاع الموسيقى، والعثور على أصدقائك وتحديث حالتك. - + Manage how Tomahawk finds music on your computer. تنظيم كيف يجد توماهوك الموسيقى على نظامك. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. تكوين إعدادات توماهوك المتطورة، بما في ذلك إعدادات شبكة الاتصال، وتفاعل المتصفح وما إلى ذلك. - + Install resolver from file تثبيت محلل من ملف - + Tomahawk Resolvers (*.axe *.js);;All files (*) محللي توماهوك (*.axe *.js);;جميع الملفات (*) - + Resolver installation from file %1 failed. تثبيت المحلل من الملف %1 فشل. - + Delete all Access Control entries? حذف كافة بيانات التحكم بالوصول؟ - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. هل فعلا تريد حذف جميع بيانات التحكم بالوصول؟ سوف يطلب منك اتخاذ القرار مجددا لكل ند على اتصال به. - + Information معلومات @@ -1594,7 +1594,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: التصفية حسب القدرة: @@ -1602,77 +1602,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method طريقة الاتصال بالأقران عن بعد - + None (outgoing connections only) لا شيء (الاتصالات الصادرة فقط) - + Use UPnP to establish port forward (recommended) استخدام UPNP لإنشاء توجيه المنافذ (مستحسن) - + Use static external IP address/host name and port استخدام عنوان IP خارجي ثابت او اسم المضيف والمنفذ - + Set this to your external IP address or host name. Make sure to forward the port to this host! تعيين هذا إلى عنوان IP الخارجي الخاص بك أو اسم المضيف. تأكد من توجيه المنفذ إلى هذا المضيف! - + Static Host Name: إسم المضيف الثابت: - + Static Port: المنفذ الثابت: - + SOCKS Proxy الوكيل سوكس (SOCKS Proxy) - + Use SOCKS Proxy إستخدام الوكيل سوكس (SOCKS Proxy) - + Proxy Settings... إعدادات الوكيل... - + Other Settings إعدادات أخرى - + Allow web browsers to interact with Tomahawk (recommended) السماح للمتصفح بالتفاعل مع توماهوك (مستحسن) - + Send reports after Tomahawk crashed إرسال تقارير بعض تحطم توماهوك - + Show notification when a new song starts to play عرض إعلام عند بدء اغنية جديدة - + Clear All Access Control Entries مسح كافة إدخالات التحكم بالوصول @@ -1680,12 +1680,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: المسار للبحث عن ملفات الموسيقى: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1693,17 +1693,17 @@ connect and stream from you? The Echo Nest يدعم تتبع البيانات الوصفية للكتالوجات الخاصة بك واستخدامها لصياغة محطاتك الشخصية. تمكين هذا الخيار يسمح لك (ولأصدقائك) بإنشاء قوائم تشغيل ومحطة أوتوماتيكيا بناء على التعريف الخاص بك والذوق الشخصي. - + Upload collection list to The Echo Nest to enable user radio تحميل قائمة المجموعة إلى "The Echo Nest" لتمكين محطات المستخدم - + Watch for changes مراقبة التغييرات - + Time between scans, in seconds: الوقت بين المسح بالثواني: @@ -1711,12 +1711,12 @@ connect and stream from you? SlideSwitchButton - + On بدء - + Off إيقاف @@ -1742,32 +1742,32 @@ connect and stream from you? SocialWidget - + Facebook فيسبوك (Facebook) - + Twitter تويتر (Twitter) - + Tweet تويت (Tweet) - + Listening to "%1" by %2. %3 إنني أستمع إلى "%1" من قبل %2. %3 - + Listening to "%1" by %2 on "%3". %4 إنني أستمع إلى "%1" من قبل %2 على "%3". %4 - + %1 characters left تبقى %1 حروف @@ -1775,44 +1775,44 @@ connect and stream from you? SourceDelegate - + Track اغنية - + Album البوم - + Artist فنان - + Local محلية - + Top 10 توب ١٠ - + All available tracks جميع الأغاني المتاحة - - + + Show أظهر - - + + Hide إخفي @@ -1853,53 +1853,53 @@ connect and stream from you? SourceItem - - + + Latest Additions أحدث الإضافات - + Recently Played تم الاستماع لها مؤخرا - + SuperCollection سوبر كولكشن - + Latest additions to your collection آخر إضافات على مجموعتك - + Latest additions to %1's collection آخر إضافات على مجموعة %1 - + Sorry, we could not find any recent additions! نعتذر، لم نستطيع إيجاد إضافة جديدة! - + Recently Played Tracks الأغاني التي إستمعت إليها مؤخرا - + Your recently played tracks الأغاني التي إستمعت إليها مؤخرا - + %1's recently played tracks الأغاني التي سمعها مؤخرا %1 - + Sorry, we could not find any recent plays! نعتذر، لم نستطيع إيجاد أغاني مسموعة مؤخرا! @@ -1907,68 +1907,68 @@ connect and stream from you? SourceTreeView - + &Copy Link &نسخ الرابط - + &Delete %1 &أحذف %1 - + Add to my Playlists أضف إلى لوائحي للأغاني - + Add to my Automatic Playlists أضف إلى لوائحي الأوتوماتيكية للأغاني - + Add to my Stations أضف إلى إذاعاتي - + &Export Playlist &تصدير قائمة الأغاني - + playlist قائمة الأغاني - + automatic playlist قائمة أغاني أوتوماتيكية - + station إذاعة - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? هل ترغب في حذف %1 <b>"%2"</b>؟ - + Delete احذف - + Save XSPF حفظ XSPF - + Playlists (*.xspf) قوائم أغاني (*.xspf) @@ -1976,77 +1976,77 @@ connect and stream from you? SourcesModel - + Group فئة - + Collection مجموعة - + Playlist قائمة الأغاني - + Automatic Playlist قائمة أغاني أوتوماتيكية - + Station إذاعة - + Browse تصفح - + Search History تاريخ البحث - + My Music موسيقتي الخاصة - + SuperCollection سوبر كولكشن - + Cloud سحابة - + Dashboard لوحة القيادة - + Recently Played تم الاستماع لها مؤخرا - + Charts الرسوم البيانية - + New Releases جديد الاصدارات - + Friends الأصدقاء @@ -2120,17 +2120,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link نسخ رابط الفنان - + Copy Album Link نسخ رابط الالبوم - + Copy Track Link نسخ رابط الأغنية @@ -2606,13 +2606,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection مجموعة - + This collection is empty. هذه المجموعة فارغة. @@ -3494,7 +3494,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة @@ -3525,48 +3525,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track &أوقف الاستماع بعد الأغنية الحالية - - + + Hide Tomahawk Window إخفي نافذة توماهوك - + Show Tomahawk Window أظهر نافذة توماهوك - + Currently not playing. لا يتم الاستماع حاليا. - + Play إستمع - + Pause تعليق - + &Love &أحب - + Un-&Love لا &أحب - + &Continue Playback after current Track &أكمل الاستماع بعد الأغنية الحالية @@ -3574,161 +3574,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk توماهوك - + Back إلى الوراء - + Go back one page العودة صفحة واحدة إلى الوراء - + Forward تقدم - + Go forward one page تقدم صفحة واحدة - - + + Hide Menu Bar إخفي شريط القائمة - - + + Show Menu Bar أظهر شريط القائمة - + Search for any artist, album or song... ابحث عن أي ألبوم، فنان أو أغنية... - + &Main Menu ال&قائمة الرئيسية - + Exit Full Screen الخروج من وضع ملء الشاشة - + Enter Full Screen الدخول إلى وضع ملء الشاشة - + XSPF Error خطأ XSPF - + This is not a valid XSPF playlist. قائمة الأغاني XSPF هذه ليست صالحة. - + Failed to save tracks فشل في حفظ الأغاني - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. بعض الأغاني في قائمة الأغاني لا تحتوي على إسم الفنان أو إسم الأغنية. هذه الأغاني سوف تتجاهل. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. تأكد أن لديك خلفية فونون المناسبة والإضافات المطلوبة مثبتة. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. - + Station إذاعة - + Create New Station إنشاء قائمة أغاني جديدة - + Name: الاسم: - + Playlist قائمة الأغاني - + Automatic Playlist قائمة أغاني أوتوماتيكية - + Pause تعليق - + &Play &إستمع - + %1 by %2 track, artist name %1 من قبل %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 حق النشر ٢٠١٠ - ٢٠١٣ - + Thanks to: شكر لكل من: - + About Tomahawk عن توماهوك @@ -4008,19 +4008,19 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: شروط %1: - + No terms found, sorry. لا شروط موجودة، نتأسف. - + Hotttness for %1: %2 @@ -4029,7 +4029,7 @@ Hotttness for %1: %2 - + Familiarity for %1: %2 @@ -4038,7 +4038,7 @@ Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index d8d946bb32..8ad774283b 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections Връзки - - + + Connect &All Свържи &Всички - + Disconnect &All Прекъсни &Всички @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite Покани @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Настройка на регистрации @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Разбъркано - + Repeat Повтори - + Time Elapsed Изминало време - + Time Remaining Оставащо време - + Playing from %1 Изпълнявам от %1 - + Share Сподели - + Love Харесай @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Създай нов списък - + Create new Station Създай нова станция - - + + New Station Нова станция - - + + %1 Station %1 Станция @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Списъци - + Stations Станции @@ -456,12 +456,12 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Автоматично докладване на грешки за Tomahawk - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;"> Извинявай!</span> @@ -471,44 +471,44 @@ Tomahawk създаде доклад относно това и изпращай Изпрати директно до разработчиците на Tomahawk</p></body></html> - + Send this report Изпрати доклада - + Don't send Не изпращай - + Abort Откажи - + You can disable sending crash reports in the configuration dialog. Можете да спрете изпращането на информация, относно проблеми в панела с настройки. - + Uploaded %L1 of %L2 KB. Качени %L1 от %В2 КБ. - - + + Close Затвори - + Sent! <b>Many thanks</b>. Изпращането приключи. <b>Благодарим ви за отзивчивостта!</b>. - + Failed to send crash info. Изпращането на краш-данни е неуспешно. @@ -547,17 +547,17 @@ Tomahawk създаде доклад относно това и изпращай DiagnosticsDialog - + Tomahawk Diagnostics Диагностична информация относно Tomahawk - + &Copy to Clipboard Копирай в системния буфер - + Open &Log-file Отвори файла с данни за работа на програмата @@ -685,7 +685,7 @@ Tomahawk създаде доклад относно това и изпращай InboxItem - + Inbox @@ -781,27 +781,27 @@ Tomahawk създаде доклад относно това и изпращай LoadXSPF - + Load XSPF Зареди XSPF - + Playlist URL Адрес на списък - + Enter URL... Въведи адрес... - + ... ... - + Automatically update Автоматично обновяване @@ -809,12 +809,12 @@ Tomahawk създаде доклад относно това и изпращай LoadXSPFDialog - + Load XSPF File Зареди XSPF файл - + XSPF Files (*.xspf) XSPF файлове (*.xspf) @@ -845,32 +845,32 @@ Tomahawk създаде доклад относно това и изпращай LovedTracksItem - + Top Loved Tracks Най-харесвани изпълнения - + Sorry, we could not find any loved tracks! Съжалявам, не откривам песни, които са маркирани като любими. - + The most loved tracks from all your friends Най-харесваните изпълнения от всичките ти приятели - + All of your loved tracks Всички песни, които харесваш - + All of %1's loved tracks Всички песни, които %1 харесва - + Loved Tracks Любими изпълнения @@ -1257,58 +1257,58 @@ Tomahawk създаде доклад относно това и изпращай ProxyDialog - + Proxy Settings Proxy настройки - + Hostname of proxy server Адрес на proxy сървър - + Host Адрес - + Port Порт - + Proxy login Настройка на влизане - + User Потребител - + Password Парола - + Proxy password Парола за proxy - + No Proxy Hosts: (Overrides system proxy) Без адреси за Proxy (Отменя системните настройки) - + localhost *.example.com (space separated) localhost *.example.com (отделени с интервал) - + Use proxy for DNS lookups? Използване на Proxy за DNS заявки? @@ -1459,12 +1459,12 @@ Tomahawk създаде доклад относно това и изпращай ResolverConfigDelegate - + Not found: %1 Не откривам : %1 - + Failed to load: %1 Не мога да заредя %1 @@ -1524,78 +1524,78 @@ Tomahawk създаде доклад относно това и изпращай SettingsDialog - + Collection Колекция - + Advanced Разширени - + All Всички - + Some changed settings will not take effect until Tomahawk is restarted Някои промени няма да имат ефект, докато програмата не бъде рестартирана. - + Services Услуги - + Install from file Инсталирай от файл - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Настройки на регистрации и услуги, ползвани от Tomahawk да търси музика, да сте свързва с твоите приятели и да обновява статусът ти. - + Manage how Tomahawk finds music on your computer. Указва начина по който Tomahawk открива музиката на твоя компютър - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Разширени настройки на Tomahawk, включващи настройки на мрежова свързаност, взаимодействие с браузъри и други подобни. - + Install resolver from file Инсталирай услуги за търсене от файл - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Изтриване на всички данни за достъп? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Наистина ли желаеш да изтриеш всички данни за достъп? Ще бъдеш питан отново за даване на достъп за всяка връзка. - + Information Информация @@ -1603,7 +1603,7 @@ Tomahawk създаде доклад относно това и изпращай Settings_Accounts - + Filter by capability: Филтрирай по възможности @@ -1611,79 +1611,79 @@ Tomahawk създаде доклад относно това и изпращай Settings_Advanced - + Remote Peer Connection Method Начин за свързване с другите потребители - + None (outgoing connections only) (Само изходящи връзки) - + Use UPnP to establish port forward (recommended) Ползвай UPnP за контролиране на пренасочването на портове (препоръчително) - + Use static external IP address/host name and port Настрой статичен адрес и порт - + Set this to your external IP address or host name. Make sure to forward the port to this host! Настрой тук твоят реален адрес и порт. Бъди сигурен, че ще настроиш прехвърлянето на портове към този локален адрес. - + Static Host Name: Статичен адрес - + Static Port: Статичен порт - + SOCKS Proxy SOCKS Proxy - + Use SOCKS Proxy Ползвай SOCKS Proxy - + Proxy Settings... Настройки на Proxy - + Other Settings Други настройки - + Allow web browsers to interact with Tomahawk (recommended) Позволи на браузърите да работят съвместно с Tomahawk (препоръчително) - + Send reports after Tomahawk crashed Изпращай данни за анализ, след забиване - + Show notification when a new song starts to play Покажи уведомление, когато почне да се изпълнява нова песен - + Clear All Access Control Entries Изтрий всички данни за достъп @@ -1691,12 +1691,12 @@ Tomahawk създаде доклад относно това и изпращай Settings_Collection - + Path to scan for music files: Път за сканиране на медийни файлове - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1704,17 +1704,17 @@ Tomahawk създаде доклад относно това и изпращай The Echo Nest поддържа създаването на профил съобразно данните извлечени от колекцията ти. Активирането на тази опция ще позволи на теб (и приятелите ти) да създаватe автоматично генерирани списъци базирани на твоят музикален вкус. - + Upload collection list to The Echo Nest to enable user radio Качи списък на колекцията в The Echo Nest, за създаване на потребителско радио - + Watch for changes Наблюдавай за промени - + Time between scans, in seconds: Период между две сканирания, в секунди @@ -1722,12 +1722,12 @@ Tomahawk създаде доклад относно това и изпращай SlideSwitchButton - + On Включен - + Off Изключен @@ -1753,32 +1753,32 @@ Tomahawk създаде доклад относно това и изпращай SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Чурулик - + Listening to "%1" by %2. %3 "%1" от %2, %3 - + Listening to "%1" by %2 on "%3". %4 "%1" от %2, от %3, %4 - + %1 characters left Остават още %1 символа @@ -1786,44 +1786,44 @@ Tomahawk създаде доклад относно това и изпращай SourceDelegate - + Track Песен - + Album Албум - + Artist Изпълнител - + Local Локално - + Top 10 Първите 10 - + All available tracks Всички налични изпълнения - - + + Show Покажи - - + + Hide Скрий @@ -1864,54 +1864,54 @@ Tomahawk създаде доклад относно това и изпращай SourceItem - - + + Latest Additions Последно добавени - + Recently Played Наскоро изпълнени песни - + SuperCollection Обща колекция /Сборен изглед от локалните и наличните в колекциите на приятелите ти изпълнения/ - + Latest additions to your collection Последно добавени към колекцията - + Latest additions to %1's collection Последно добавени в колекцията на %1 - + Sorry, we could not find any recent additions! Съжаляваме, но не откриваме наскоро-добавени позиции! - + Recently Played Tracks Наскоро изпълнени песни - + Your recently played tracks Наскоро изпълнени песни от теб - + %1's recently played tracks Наскоро изпълнените песни от %1 - + Sorry, we could not find any recent plays! Съжалявам, но не откривам нито една наскоро изпълнена песен! @@ -1919,68 +1919,68 @@ Tomahawk създаде доклад относно това и изпращай SourceTreeView - + &Copy Link &Копирай адреса - + &Delete %1 &Изтрий %1 - + Add to my Playlists Добави към моите списъци - + Add to my Automatic Playlists Добави към моите автоматично-генерирани списъци - + Add to my Stations Добави към станциите ми - + &Export Playlist &Изнеси списък - + playlist списък - + automatic playlist автоматично-генериран списък - + station станция - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Наистина ли желаеш да изтриеш %1 <b>''%2" </b> - + Delete Изтрий - + Save XSPF Запази XSPF - + Playlists (*.xspf) Списъци (*.xspf) @@ -1988,77 +1988,77 @@ Tomahawk създаде доклад относно това и изпращай SourcesModel - + Group Групирай - + Collection Колекция - + Playlist Списък за изпълнение - + Automatic Playlist Автоматичен списък - + Station Станция - + Browse Разгледай - + Search History Търси в историята - + My Music Моята музика - + SuperCollection Обща колекция - + Cloud - + Dashboard Табло - + Recently Played Наскоро изпълнени песни - + Charts Класации - + New Releases Нови албуми - + Friends Приятели @@ -2132,17 +2132,17 @@ Tomahawk създаде доклад относно това и изпращай TemporaryPageItem - + Copy Artist Link Копирай адреса на Изпълнителя - + Copy Album Link Копирай адреса на Албума - + Copy Track Link Копирай адреса на изпълнението @@ -2620,13 +2620,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Колекция - + This collection is empty. Тук няма нищо @@ -3508,7 +3508,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -3538,48 +3538,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track &Спри възпроизвеждането след текущата песен - - + + Hide Tomahawk Window Скрий главният прозорец - + Show Tomahawk Window Покажи главният прозорец - + Currently not playing. В момента не се изпълнява нищо - + Play Изпълни - + Pause Пауза - + &Love &Харесай - + Un-&Love Не-&Харесай - + &Continue Playback after current Track &Продължи възпроизвеждането след текущата песен @@ -3587,164 +3587,164 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back Назад - + Go back one page Една страница назад - + Forward Напред - + Go forward one page Една страница напред - - + + Hide Menu Bar Скрий лентата с менюто - - + + Show Menu Bar Покажи лентата с менюто - + Search for any artist, album or song... Търси всеки изпълнител, албум или песен... - + &Main Menu &Основно меню - + Exit Full Screen Излез от режим на цял екран - + Enter Full Screen Превключи в режим на цял екран - + XSPF Error XSPF Грешка - + This is not a valid XSPF playlist. Това не е валиден XSPF списък - + Failed to save tracks Не успях да запазя избраните песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Някои от песните в този списък нямат изпълнител и заглавие. Те ще бъдат игнорирани. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Съжалявам. Има проблем с достъпа до твоето аудио-устройство или до избраната песен - тя ще бъде прескочена. Моля, увери се, че са инсталирани подходящ Phonon и приставки. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Съжалявам. Има проблем с достъпа до твоето аудио устройство или избраната песен. Тя ще бъде пропусната. - + Station Станция - + Create New Station Създай нова станция - + Name: Име: - + Playlist Списък - + Automatic Playlist Автоматично-генериран списък - + Pause Пауза - + &Play &Възпроизвеждане - + %1 by %2 track, artist name %1 от %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Всички права - запазени 2010 - 2013 - + Thanks to: Благодарности на: - + About Tomahawk Относно Tomahawk @@ -4025,33 +4025,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: Условия за %1: - + No terms found, sorry. Няма открити условия, съжалявам. - + Hotttness for %1: %2 Популарност на %1:%2 - + Familiarity for %1: %2 Еднаквост за %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 58c2598631..53978487b7 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle - + Repeat - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist - + Create new Station - - + + New Station - - + + %1 Station @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists - + Stations @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report - + Don't send - + Abort - + You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. - - + + Close - + Sent! <b>Many thanks</b>. - + Failed to send crash info. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics - + &Copy to Clipboard - + Open &Log-file @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF - + Playlist URL - + Enter URL... - + ... - + Automatically update @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File - + XSPF Files (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings - + Hostname of proxy server - + Host - + Port - + Proxy login - + User - + Password - + Proxy password - + No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) - + Use proxy for DNS lookups? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 - + Failed to load: %1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection - + Advanced - + All - + Some changed settings will not take effect until Tomahawk is restarted - + Services - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook - + Twitter - + Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 - + %1 characters left @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track - + Album - + Artist - + Local - + Top 10 - + All available tracks - - + + Show - - + + Hide @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - + Recently Played Tracks - + Your recently played tracks - + %1's recently played tracks - + Sorry, we could not find any recent plays! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group - + Collection - + Playlist - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link - + Copy Album Link - + Copy Track Link @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. - + Play - + Pause - + &Love - + Un-&Love - + &Continue Playback after current Track @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index f1ee3e54ea..825c93b8f2 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections Connexions - - + + Connect &All Connect&a-ho tot - + Disconnect &All Desconnect&a-ho tot @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite Convida @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Configura els comptes @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Aleatori - + Repeat Repeteix - + Time Elapsed Temps transcorregut - + Time Remaining Temps restant - + Playing from %1 Reproduït des de %1 - + Share Comparteix - + Love M'encanta @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Crea una llista de reproducció nova - + Create new Station Crea una emissora nova - - + + New Station Emissora nova - - + + %1 Station Emissora %1 @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Llistes de reproducció - + Stations Emissores @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Enviament d'errors del Tomahawk - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Vaja!</span> Ha fallat el Tomahawk. Informeu-nos d'això! El Tomahawk ha creat un informe d'error que pot ajudar a millorar l'estabilit en futures versions. Podeu enviar aquest informe directament als desenvolupadors del Tomahawk.</p></body></html> - + Send this report Envia aquest informe - + Don't send No l'enviïs - + Abort Interromp - + You can disable sending crash reports in the configuration dialog. Podeu deshabilitar l'enviament d'informació sobre els errors des del diàleg de configuració. - + Uploaded %L1 of %L2 KB. %L1 de %L2 KB carregats. - - + + Close Tanca - + Sent! <b>Many thanks</b>. S'ha enviat! <b>Moltes gràcies</b>. - + Failed to send crash info. S'ha produït un error en enviar la informació sobre l'error. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Diagnòstics del Tomahawk - + &Copy to Clipboard &Copia al porta-retalls - + Open &Log-file Obre e&l fitxer de registre @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Carrega una XSPF - + Playlist URL URL de la llista de reproduccó - + Enter URL... Introduïu l'URL... - + ... ... - + Automatically update Actualitza automàticament @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Carrega un fitxer XSPF - + XSPF Files (*.xspf) Fitxers XSPF (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks Les cançons més estimades - + Sorry, we could not find any loved tracks! No s'ha trobat cap cançó estimada - + The most loved tracks from all your friends Les cançons més estimades per tots els vostres amics - + All of your loved tracks Totes les vostres cançons estimades - + All of %1's loved tracks Totes les cançons més estimades per %1 - + Loved Tracks Cançons preferides @@ -1246,59 +1246,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings Paràmetres del proxy - + Hostname of proxy server Nom del proxy - + Host Ordinador central - + Port Port - + Proxy login Dades d'inici de sessió del proxy - + User Usuari - + Password Contrasenya - + Proxy password Contrasenya del proxy - + No Proxy Hosts: (Overrides system proxy) Cap proxy: (Sobreescriu el proxy del sistema) - + localhost *.example.com (space separated) localhost *.exemple.com (separats per espais) - + Use proxy for DNS lookups? Voleu emprar un proxy per cercar les DNS? @@ -1449,12 +1449,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 No trobades: %1 - + Failed to load: %1 Fallades a carregar: %1 @@ -1514,77 +1514,77 @@ connect and stream from you? SettingsDialog - + Collection Col·lecció - + Advanced Avançat - + All Tot - + Some changed settings will not take effect until Tomahawk is restarted Alguns paràmetres no tindran efecte fins que no reinicieu Tomahawk - + Services Serveis - + Install from file Instal·la des d'un fitxer - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configura els comptes i serveis que fa servir el Tomahawk per a cercar i obtenir música, trobar els vostres amics i actualitzr el vostre estat. - + Manage how Tomahawk finds music on your computer. Gestiona com el Tomahawk troba musica a l'ordinador. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configura els paràmetres avançats del Tomahawk, incloent els paràmetres de connexió de xarxa, interacció del navegador i més. - + Install resolver from file Instal·la un Resolver des d'un fitxer - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Voleu eliminar totes les entrades d'Access Control? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Realment voleu eliminar totes les entrades de l'Access Control? Se us demanarà per una decisió una altra vegada per cada recurs al que us connecteu. - + Information Informació @@ -1592,7 +1592,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: Filtra per capacitat: @@ -1600,77 +1600,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method Mètode de connexió al parell remot - + None (outgoing connections only) Cap (només connexions de sortida) - + Use UPnP to establish port forward (recommended) Fes servir UPnP per establir el redireccionament de port (recomanat) - + Use static external IP address/host name and port Fes servir una adreça IP o nom d'amfitrió i port - + Set this to your external IP address or host name. Make sure to forward the port to this host! Estableix això com l'adreça IP externa o nom d'amfitrió. Assegureu-vos de redireccionar el port a aquest amfitrió. - + Static Host Name: Nom estàtic de l'amfitrió: - + Static Port: Port estàtic: - + SOCKS Proxy Servidor intermediari de Socks - + Use SOCKS Proxy Fes servir el servidor intermediari de Socks - + Proxy Settings... Paràmetres del servidor intermediari... - + Other Settings Altres paràmetres - + Allow web browsers to interact with Tomahawk (recommended) Permet als navegadors web interaccionar amb el Tomahawk (recomanat) - + Send reports after Tomahawk crashed Envia informes quan el Tomahawk falla - + Show notification when a new song starts to play Mostra una notificació quan una cançó nova comença a reproduir-se - + Clear All Access Control Entries Neteja totes les entrades de l'Access Control @@ -1678,12 +1678,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: Camí on es cercaran els fitxers de música: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1694,17 +1694,17 @@ habilitar aquesta opció, es crearan llistes de reproducció automàtiques i emissores basades en els vostres gusts musicals. - + Upload collection list to The Echo Nest to enable user radio Carrega la col·lecció al The Echo Nest i habilita la ràdio d'usuari - + Watch for changes Vigila els canvis - + Time between scans, in seconds: Temps entre escanejaments, en segons: @@ -1712,12 +1712,12 @@ i emissores basades en els vostres gusts musicals. SlideSwitchButton - + On Activat - + Off Desactivat @@ -1743,32 +1743,32 @@ i emissores basades en els vostres gusts musicals. SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Piula - + Listening to "%1" by %2. %3 S'està escoltant «%1» de %2. %3 - + Listening to "%1" by %2 on "%3". %4 S'està escoltant a «%1» de %2 el «%3». %4 - + %1 characters left %1 caràcters restants @@ -1776,44 +1776,44 @@ i emissores basades en els vostres gusts musicals. SourceDelegate - + Track Cançó - + Album Àlbum - + Artist Artista - + Local Local - + Top 10 Top 10 - + All available tracks Totes les cançons disponibles - - + + Show Mostra - - + + Hide Amaga @@ -1854,53 +1854,53 @@ i emissores basades en els vostres gusts musicals. SourceItem - - + + Latest Additions Darreres novetats - + Recently Played Reproduïdes recentment - + SuperCollection Supercol·lecció - + Latest additions to your collection Darreres novetats a la col·lecció - + Latest additions to %1's collection Darreres novetats a la col·lecció %1 - + Sorry, we could not find any recent additions! No s'ha trobat cap novetat recent - + Recently Played Tracks Cançons reproduïdes recentment - + Your recently played tracks Les cançons que heu reproduït recentment - + %1's recently played tracks Cançons reproduïdes recentment per %1 - + Sorry, we could not find any recent plays! No s'ha trobat cap cançó reproduïda recentment @@ -1908,68 +1908,68 @@ i emissores basades en els vostres gusts musicals. SourceTreeView - + &Copy Link &Copia l'Enllaç - + &Delete %1 &Esborra %1 - + Add to my Playlists Afegeix a les meves Llistes - + Add to my Automatic Playlists Afegeix a les meves Llistes Automàtiques - + Add to my Stations Afegeix a les meves Emissores - + &Export Playlist E&xporta la Llista de Reproducció - + playlist llista - + automatic playlist llista automàtica - + station emissora - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Voleu esborrar %1 <b>"%2"</b>? - + Delete Suprimeix - + Save XSPF Desa com XSPF - + Playlists (*.xspf) Llistes de reproducció (*.xspf) @@ -1977,77 +1977,77 @@ i emissores basades en els vostres gusts musicals. SourcesModel - + Group Grup - + Collection Col·lecció - + Playlist Llista de Reproducció - + Automatic Playlist Llista de Reproducció Automàtica - + Station Emissora - + Browse Cerca - + Search History Historial de Cerca - + My Music La Meva Música - + SuperCollection SuperCol·lecció - + Cloud - + Dashboard Presentació - + Recently Played Escoltades Recentment - + Charts Llistes - + New Releases Nous Llançaments - + Friends Amics @@ -2121,17 +2121,17 @@ i emissores basades en els vostres gusts musicals. TemporaryPageItem - + Copy Artist Link Copia l'enllaç de l'artista - + Copy Album Link Copia l'enllaç de l'àlbum - + Copy Track Link Copia l'enllaç de la cançó @@ -2607,13 +2607,13 @@ nomdusuari@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3495,7 +3495,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció @@ -3526,48 +3526,48 @@ introduïu el PIN aquí: TomahawkTrayIcon - + &Stop Playback after current Track &Atura la Reproducció després d'aquesta Cançó - - + + Hide Tomahawk Window Amaga la finestra de Tomahawk - + Show Tomahawk Window Mostra la finestra de Tomahawk - + Currently not playing. No s'està reproduint res. - + Play Reprodueix - + Pause Pausa - + &Love &M'encanta - + Un-&Love Ja no m'en&canta - + &Continue Playback after current Track &Continua la reproducció després d'aquesta Cançó @@ -3575,161 +3575,161 @@ introduïu el PIN aquí: TomahawkWindow - + Tomahawk Tomahawk - + Back Enrere - + Go back one page Retrocedeix una pàgina - + Forward Endavant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Surt de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en desar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4009,7 +4009,7 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant XMPPBot - + Terms for %1: @@ -4017,12 +4017,12 @@ Terms for %1: Termes per %1: - + No terms found, sorry. No s'han trobat termes. - + Hotttness for %1: %2 @@ -4030,14 +4030,14 @@ Hotttness for %1: %2 Rellevància per %1: %2 - + Familiarity for %1: %2 Semblança per %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 55f4f12cf6..3f39bcc910 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections Connexions - - + + Connect &All Connect&a-ho tot - + Disconnect &All Desconnect&a-ho tot @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite Convida @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Configura els comptes @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Aleatori - + Repeat Repeteix - + Time Elapsed Temps transcorregut - + Time Remaining Temps restant - + Playing from %1 Reproduït des de %1 - + Share Comparteix - + Love M'encanta @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Crea una llista de reproducció nova - + Create new Station Crea una emissora nova - - + + New Station Emissora nova - - + + %1 Station Emissora %1 @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Llistes de reproducció - + Stations Emissores @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Enviament d'errors del Tomahawk - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Vaja!</span> Ha fallat el Tomahawk. Informeu-nos d'això! El Tomahawk ha creat un informe d'error que pot ajudar a millorar l'estabilit en futures versions. Podeu enviar este informe directament als desenvolupadors del Tomahawk.</p></body></html> - + Send this report Envia este informe - + Don't send No l'envies - + Abort Interromp - + You can disable sending crash reports in the configuration dialog. Podeu deshabilitar l'enviament d'informació sobre els errors des del diàleg de configuració. - + Uploaded %L1 of %L2 KB. %L1 de %L2 KB carregats. - - + + Close Tanca - + Sent! <b>Many thanks</b>. S'ha enviat! <b>Moltes gràcies</b>. - + Failed to send crash info. S'ha produït un error en enviar la informació sobre l'error. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Diagnòstics del Tomahawk - + &Copy to Clipboard &Copia al porta-retalls - + Open &Log-file Obri e&l fitxer de registre @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Carrega una XSPF - + Playlist URL URL de la llista de reproduccó - + Enter URL... Introduïu l'URL... - + ... ... - + Automatically update Actualitza automàticament @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Carrega un fitxer XSPF - + XSPF Files (*.xspf) Fitxers XSPF (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks Les cançons més estimades - + Sorry, we could not find any loved tracks! No s'ha trobat cap cançó estimada - + The most loved tracks from all your friends Les cançons més estimades per tots els vostres amics - + All of your loved tracks Totes les vostres cançons estimades - + All of %1's loved tracks Totes les cançons més estimades per %1 - + Loved Tracks Cançons preferides @@ -1246,59 +1246,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings Paràmetres del proxy - + Hostname of proxy server Nom del proxy - + Host Ordinador central - + Port Port - + Proxy login Dades d'inici de sessió del proxy - + User Usuari - + Password Contrasenya - + Proxy password Contrasenya del proxy - + No Proxy Hosts: (Overrides system proxy) Cap proxy: (Sobreescriu el proxy del sistema) - + localhost *.example.com (space separated) localhost *.exemple.com (separats per espais) - + Use proxy for DNS lookups? Voleu emprar un proxy per cercar les DNS? @@ -1449,12 +1449,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 No trobades: %1 - + Failed to load: %1 Fallades a carregar: %1 @@ -1514,77 +1514,77 @@ connect and stream from you? SettingsDialog - + Collection Col·lecció - + Advanced Avançat - + All Tot - + Some changed settings will not take effect until Tomahawk is restarted Alguns paràmetres no tindran efecte fins que no reinicieu Tomahawk - + Services Serveis - + Install from file Instal·la des d'un fitxer - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configura els comptes i serveis que fa servir el Tomahawk per a cercar i obtindre música, trobar els vostres amics i actualitzr el vostre estat. - + Manage how Tomahawk finds music on your computer. Gestiona com el Tomahawk troba musica a l'ordinador. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configura els paràmetres avançats del Tomahawk, incloent els paràmetres de connexió de xarxa, interacció del navegador i més. - + Install resolver from file Instal·la un Resolver des d'un fitxer - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Voleu eliminar totes les entrades d'Access Control? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Realment voleu eliminar totes les entrades de l'Access Control? Se vos demanarà per una decisió una altra vegada per cada recurs al que vos connecteu. - + Information Informació @@ -1592,7 +1592,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: Filtra per capacitat: @@ -1600,77 +1600,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method Mètode de connexió al parell remot - + None (outgoing connections only) Cap (només connexions d'eixida) - + Use UPnP to establish port forward (recommended) Fes servir UPnP per establir el redireccionament de port (recomanat) - + Use static external IP address/host name and port Fes servir una adreça IP o nom d'amfitrió i port - + Set this to your external IP address or host name. Make sure to forward the port to this host! Estableix això com l'adreça IP externa o nom d'amfitrió. Assegureu-vos de redireccionar el port a este amfitrió. - + Static Host Name: Nom estàtic de l'amfitrió: - + Static Port: Port estàtic: - + SOCKS Proxy Servidor intermediari de Socks - + Use SOCKS Proxy Fes servir el servidor intermediari de Socks - + Proxy Settings... Paràmetres del servidor intermediari... - + Other Settings Altres paràmetres - + Allow web browsers to interact with Tomahawk (recommended) Permet als navegadors web interaccionar amb el Tomahawk (recomanat) - + Send reports after Tomahawk crashed Envia informes quan el Tomahawk falla - + Show notification when a new song starts to play Mostra una notificació quan una cançó nova comença a reproduir-se - + Clear All Access Control Entries Neteja totes les entrades de l'Access Control @@ -1678,12 +1678,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: Camí on es cercaran els fitxers de música: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1694,17 +1694,17 @@ habilitar esta opció, es crearan llistes de reproducció automàtiques i emissores basades en els vostres gusts musicals. - + Upload collection list to The Echo Nest to enable user radio Carrega la col·lecció al The Echo Nest i habilita la ràdio d'usuari - + Watch for changes Vigila els canvis - + Time between scans, in seconds: Temps entre escanejaments, en segons: @@ -1712,12 +1712,12 @@ i emissores basades en els vostres gusts musicals. SlideSwitchButton - + On Activat - + Off Desactivat @@ -1743,32 +1743,32 @@ i emissores basades en els vostres gusts musicals. SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Piula - + Listening to "%1" by %2. %3 S'està escoltant «%1» de %2. %3 - + Listening to "%1" by %2 on "%3". %4 S'està escoltant a «%1» de %2 el «%3». %4 - + %1 characters left %1 caràcters restants @@ -1776,44 +1776,44 @@ i emissores basades en els vostres gusts musicals. SourceDelegate - + Track Cançó - + Album Àlbum - + Artist Artista - + Local Local - + Top 10 Top 10 - + All available tracks Totes les cançons disponibles - - + + Show Mostra - - + + Hide Amaga @@ -1854,53 +1854,53 @@ i emissores basades en els vostres gusts musicals. SourceItem - - + + Latest Additions Darreres novetats - + Recently Played Reproduïdes recentment - + SuperCollection Supercol·lecció - + Latest additions to your collection Darreres novetats a la col·lecció - + Latest additions to %1's collection Darreres novetats a la col·lecció %1 - + Sorry, we could not find any recent additions! No s'ha trobat cap novetat recent - + Recently Played Tracks Cançons reproduïdes recentment - + Your recently played tracks Les cançons que heu reproduït recentment - + %1's recently played tracks Cançons reproduïdes recentment per %1 - + Sorry, we could not find any recent plays! No s'ha trobat cap cançó reproduïda recentment @@ -1908,68 +1908,68 @@ i emissores basades en els vostres gusts musicals. SourceTreeView - + &Copy Link &Copia l'Enllaç - + &Delete %1 &Esborra %1 - + Add to my Playlists Afig a les meues Llistes - + Add to my Automatic Playlists Afig a les meues Llistes Automàtiques - + Add to my Stations Afig a les meues Emissores - + &Export Playlist E&xporta la Llista de Reproducció - + playlist llista - + automatic playlist llista automàtica - + station emissora - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Voleu esborrar %1 <b>"%2"</b>? - + Delete Suprimeix - + Save XSPF Alça com XSPF - + Playlists (*.xspf) Llistes de reproducció (*.xspf) @@ -1977,77 +1977,77 @@ i emissores basades en els vostres gusts musicals. SourcesModel - + Group Grup - + Collection Col·lecció - + Playlist Llista de Reproducció - + Automatic Playlist Llista de Reproducció Automàtica - + Station Emissora - + Browse Cerca - + Search History Historial de Cerca - + My Music La Meua Música - + SuperCollection SuperCol·lecció - + Cloud - + Dashboard Presentació - + Recently Played Escoltades Recentment - + Charts Llistes - + New Releases Nous Llançaments - + Friends Amics @@ -2121,17 +2121,17 @@ i emissores basades en els vostres gusts musicals. TemporaryPageItem - + Copy Artist Link Copia l'enllaç de l'artista - + Copy Album Link Copia l'enllaç de l'àlbum - + Copy Track Link Copia l'enllaç de la cançó @@ -2607,13 +2607,13 @@ nomdusuari@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3495,7 +3495,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció @@ -3526,48 +3526,48 @@ introduïu el PIN ací: TomahawkTrayIcon - + &Stop Playback after current Track &Atura la Reproducció després d'esta Cançó - - + + Hide Tomahawk Window Amaga la finestra de Tomahawk - + Show Tomahawk Window Mostra la finestra de Tomahawk - + Currently not playing. No s'està reproduint res. - + Play Reprodueix - + Pause Pausa - + &Love &M'encanta - + Un-&Love Ja no m'en&canta - + &Continue Playback after current Track &Continua la reproducció després d'esta Cançó @@ -3575,161 +3575,161 @@ introduïu el PIN ací: TomahawkWindow - + Tomahawk Tomahawk - + Back Arrere - + Go back one page Retrocedeix una pàgina - + Forward Avant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Ix de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en alçar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4009,7 +4009,7 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant XMPPBot - + Terms for %1: @@ -4017,12 +4017,12 @@ Terms for %1: Termes per %1: - + No terms found, sorry. No s'han trobat termes. - + Hotttness for %1: %2 @@ -4030,14 +4030,14 @@ Hotttness for %1: %2 Rellevància per %1: %2 - + Familiarity for %1: %2 Semblança per %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 711c29fec1..0517166461 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -56,18 +56,18 @@ se s vámi spojil? AccountListWidget - + Connections Spojení - - + + Connect &All Spojit &vše - + Disconnect &All &Odpojit vše @@ -75,7 +75,7 @@ se s vámi spojil? AccountWidget - + Invite Pozvat @@ -83,7 +83,7 @@ se s vámi spojil? AccountsToolButton - + Configure Accounts Nastavit účty @@ -341,37 +341,37 @@ se s vámi spojil? AudioControls - + Shuffle Zamíchat - + Repeat Opakovat - + Time Elapsed Uběhlý čas - + Time Remaining Zbývající čas - + Playing from %1 Přehrávání od %1 - + Share Sdílet - + Love Mít rád @@ -397,24 +397,24 @@ se s vámi spojil? CategoryAddItem - + Create new Playlist Vytvořit nový seznam skladeb - + Create new Station Vytvořit novou stanici - - + + New Station Nová stanice - - + + %1 Station %1 stanice @@ -422,12 +422,12 @@ se s vámi spojil? CategoryItem - + Playlists Seznamy skladeb - + Stations Stanice @@ -457,53 +457,53 @@ se s vámi spojil? CrashReporter - + Tomahawk Crash Reporter Hlásič chyb Tomahawku - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Promiňte!</span> Tomahawk spadl. Řekněte nám o tom, prosím! Tomahawk pro vás vytvořil zprávu o chybě, která nám může pomoci zlepšit stabilitu budoucích verzí. Tuto zprávu nyní můžete poslat přímo vývojářům Tomahawku.</p></body></html> - + Send this report Odeslat zprávu - + Don't send Neodesílat - + Abort Zrušit - + You can disable sending crash reports in the configuration dialog. Předávání zpráv o chybách můžete vypnout v nastavení. - + Uploaded %L1 of %L2 KB. Nahráno %L1 z %L2 KB. - - + + Close Zavřít - + Sent! <b>Many thanks</b>. Odesláno! <b>Děkujeme</b>. - + Failed to send crash info. Odeslání zprávy o chybě se nezdařilo. @@ -542,17 +542,17 @@ se s vámi spojil? DiagnosticsDialog - + Tomahawk Diagnostics Diagnostický nástroj Tomahawku - + &Copy to Clipboard &Kopírovat do schránky - + Open &Log-file &Otevřít soubor se zápisem @@ -679,7 +679,7 @@ se s vámi spojil? InboxItem - + Inbox Doručené @@ -775,27 +775,27 @@ se s vámi spojil? LoadXSPF - + Load XSPF Nahrát XSPF - + Playlist URL Adresa (URL) seznamu skladeb - + Enter URL... Zadat adresu (URL)... - + ... ... - + Automatically update Aktualizovat automaticky @@ -803,12 +803,12 @@ se s vámi spojil? LoadXSPFDialog - + Load XSPF File Nahrát soubor XSPF - + XSPF Files (*.xspf) Soubory XSPF (*.xspf) @@ -839,32 +839,32 @@ se s vámi spojil? LovedTracksItem - + Top Loved Tracks Nejoblíbenější skladby - + Sorry, we could not find any loved tracks! Promiňte, nepodařilo se najít žádné oblíbené skladby! - + The most loved tracks from all your friends Nejoblíbenější skladby všech vašich přátel - + All of your loved tracks Vaše nejmilejší skladby - + All of %1's loved tracks %1's nejmilejších skladeb - + Loved Tracks Nejmilejší skladby @@ -1247,59 +1247,59 @@ se s vámi spojil? ProxyDialog - + Proxy Settings Nastavení Proxy - + Hostname of proxy server Název počítače serveru Proxy - + Host Hostitelský počítač - + Port Přípojka (port) - + Proxy login Přihlášení Proxy - + User Uživatel - + Password Heslo - + Proxy password Heslo pro Proxy - + No Proxy Hosts: (Overrides system proxy) Výjimky Proxy: (má přednost před systémovou Proxy) - + localhost *.example.com (space separated) localhost *.example.com (odděleno mezerami) - + Use proxy for DNS lookups? Použít Proxy pro dotazy DNS? @@ -1450,12 +1450,12 @@ se s vámi spojil? ResolverConfigDelegate - + Not found: %1 Nenalezeno: %1 - + Failed to load: %1 Nepodařilo se nahrát: %1 @@ -1515,77 +1515,77 @@ se s vámi spojil? SettingsDialog - + Collection Sbírka - + Advanced Pokročilé - + All Vše - + Some changed settings will not take effect until Tomahawk is restarted Některá změněná nastavení se neprojeví, dokud Tomahawk nebude spuštěn znovu - + Services Služby - + Install from file Instalovat ze souboru - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Nastavit účty a služby, jež Tomahawk používa pro hledání a získávání hudby, ke spojování se s vašimi přáteli a pro aktualizaci vašeho stavu. - + Manage how Tomahawk finds music on your computer. Spravovat, jak Tomahawk ve vašem systému hledá hudbu. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Nastavit pokročilá nastavení Tomahawku, jako jsou volby pro síť, interakci prohlížeče a další. - + Install resolver from file Instalovat řešitele ze souboru - + Tomahawk Resolvers (*.axe *.js);;All files (*) Řešitelé Tomahawk (*.axe *.js);;Všechny soubory (*) - + Resolver installation from file %1 failed. Nepodařilo se nainstalovat řešitele ze souboru %1. - + Delete all Access Control entries? Smazat všechna udělená přístupová oprávnění? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Opravdu chcete smazat všechna nastavení přístupových práv? Budete znovu dotazován na nastavení přístupových oprávnění pro každé spojení. - + Information Informace @@ -1593,7 +1593,7 @@ se s vámi spojil? Settings_Accounts - + Filter by capability: Filtrovat podle způsobilosti: @@ -1601,77 +1601,77 @@ se s vámi spojil? Settings_Advanced - + Remote Peer Connection Method Spojení s jinými Tomahawky - + None (outgoing connections only) Žádné (možná jen odchozí spojení) - + Use UPnP to establish port forward (recommended) Použít UPnP pro předání přípojky dál (doporučeno) - + Use static external IP address/host name and port Použít statickou vnější adresu/název hostitele a přípojku - + Set this to your external IP address or host name. Make sure to forward the port to this host! Zde nastavte svou vnější adresu IP nebo název hostitele. Přípojku musíte tomuto počítači předat dál sami! - + Static Host Name: Statický název počítače: - + Static Port: Statická přípojka: - + SOCKS Proxy SOCKS Proxy - + Use SOCKS Proxy Použít SOCKS Proxy - + Proxy Settings... Nastavení Proxy… - + Other Settings Další nastavení - + Allow web browsers to interact with Tomahawk (recommended) Povolit internetovým prohlížečům komunikaci s Tomahawkem (doporučeno) - + Send reports after Tomahawk crashed Odeslat zprávu o chybě, když Tomahawk spadl - + Show notification when a new song starts to play Ukázat oznámení, když začne hrát nová píseň - + Clear All Access Control Entries Smazat všechna udělená přístupová oprávnění @@ -1679,12 +1679,12 @@ se s vámi spojil? Settings_Collection - + Path to scan for music files: Cesta k hudebním souborům: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1692,17 +1692,17 @@ se s vámi spojil? Echo Nest může analyzovat popisná data vaší sbírky, aby vám nabídl radiostanice podle vašich potřeb. Když je tato volba zapnuta, můžete vy (a všichni vaši přátelé) vytvářet automatické seznamy skladeb a stanice, jež jsou založeny na vaší osobní hudební chuti. - + Upload collection list to The Echo Nest to enable user radio Předat seznam nynější sbírky Echo Nest pro zapnutí uživatelského rádia (User Radio) - + Watch for changes Sledovat změny a aktualizovat automaticky - + Time between scans, in seconds: Doba mezi dvěma prohlédnutími (v sekundách): @@ -1710,12 +1710,12 @@ se s vámi spojil? SlideSwitchButton - + On Zapnuto - + Off Vypnuto @@ -1741,32 +1741,32 @@ se s vámi spojil? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Zpráva - + Listening to "%1" by %2. %3 Poslouchá "%1" od %2, %3 - + Listening to "%1" by %2 on "%3". %4 Poslouchá "%1" od %2 na "%3", %4 - + %1 characters left Zbývá %1 znaků @@ -1774,44 +1774,44 @@ se s vámi spojil? SourceDelegate - + Track Skladba - + Album Album - + Artist Umělec - + Local Místní - + Top 10 Nejlepších deset - + All available tracks Všechny dostupné skladby - - + + Show Ukázat - - + + Hide Skrýt @@ -1852,53 +1852,53 @@ se s vámi spojil? SourceItem - - + + Latest Additions Nedávné přídavky - + Recently Played Nedávno poslouchané - + SuperCollection Supersbírka - + Latest additions to your collection Nejnovější písně ve vaší sbírce - + Latest additions to %1's collection Nejnovější písně ve sbírce %1's - + Sorry, we could not find any recent additions! Promiňte, ale nepodařilo se najít žádné nedávné přídavky! - + Recently Played Tracks Nedávno poslouchané skladby - + Your recently played tracks Vaše nedávno poslouchané skladby - + %1's recently played tracks %1's nedávno poslouchaných skladeb - + Sorry, we could not find any recent plays! Promiňte, ale nepodařilo se najít žádné nedávno přehrávané skladby! @@ -1906,68 +1906,68 @@ se s vámi spojil? SourceTreeView - + &Copy Link &Kopírovat odkaz - + &Delete %1 &Smazat %1 - + Add to my Playlists Přidat do mých seznamů skladeb - + Add to my Automatic Playlists Přidat do mých automatických seznamů skladeb - + Add to my Stations Přidat do mých stanic - + &Export Playlist &Vyvést seznam skladeb - + playlist Seznam skladeb - + automatic playlist Automatický seznam skladeb - + station Stanice - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Chcete smazat %1 <b>"%2"</b>? - + Delete Smazat - + Save XSPF Uložit XSPF - + Playlists (*.xspf) Seznamy skladeb (*.xspf) @@ -1975,77 +1975,77 @@ se s vámi spojil? SourcesModel - + Group Skupina - + Collection Sbírka - + Playlist Seznam skladeb - + Automatic Playlist Automatický seznam skladeb - + Station Stanice - + Browse Procházet - + Search History Historie hledání - + My Music Moje hudba - + SuperCollection Supersbírka - + Cloud Mračno - + Dashboard Přístrojová deska - + Recently Played Nedávno poslouchané - + Charts Žebříčky - + New Releases Novinky - + Friends Přátelé @@ -2119,17 +2119,17 @@ se s vámi spojil? TemporaryPageItem - + Copy Artist Link Kopírovat odkaz pro tohoto umělce - + Copy Album Link Kopírovat odkaz pro toto album - + Copy Track Link Kopírovat odkaz pro tuto skladbu @@ -2605,13 +2605,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Sbírka - + This collection is empty. Tato sbírka je prázdná. @@ -3493,7 +3493,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka @@ -3524,48 +3524,48 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TomahawkTrayIcon - + &Stop Playback after current Track Po této skladbě &zastavit přehrávání - - + + Hide Tomahawk Window Skrýt okno s Tomahawkem - + Show Tomahawk Window Ukázat okno s Tomahawkem - + Currently not playing. Nyní se nic nepřehrává. - + Play Přehrát - + Pause Pozastavit - + &Love &Označit za oblíbenou píseň - + Un-&Love Zrušit zařazení mezi o&blíbené písně - + &Continue Playback after current Track Po této skladbě &pokračovat v přehrávání @@ -3573,161 +3573,161 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TomahawkWindow - + Tomahawk Tomahawk - + Back Zpět - + Go back one page Jít o jednu stranu zpět - + Forward Vpřed - + Go forward one page Jít o jednu stranu vpřed - - + + Hide Menu Bar Skrýt pruh s hlavní nabídkou - - + + Show Menu Bar Ukázat pruh s hlavní nabídkou - + Search for any artist, album or song... Hledat umělce, album nebo píseň... - + &Main Menu Hlavní &nabídka - + Exit Full Screen Ukončit režim na celou obrazovku - + Enter Full Screen Vstoupit do režimu na celou obrazovku - + XSPF Error Chyba XSPF - + This is not a valid XSPF playlist. Toto není platný seznam skladeb XSPF. - + Failed to save tracks Nepodařilo se uložit skladby - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Některé skladby v seznamu skladeb neobsahují ani umělce ani název. Tyto budou přehlíženy. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. Ujistěte se, že máte nainstalováno vhodné jádro Phonona potřebné přídavné moduly. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. - + Station Stanice - + Create New Station Vytvořit novou stanici - + Name: Název: - + Playlist Seznam skladeb - + Automatic Playlist Automatický seznam skladeb - + Pause Pozastavit - + &Play &Přehrát - + %1 by %2 track, artist name %1 od %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorské právo 2010 - 2013 - + Thanks to: Poděkování: - + About Tomahawk O Tomahawku @@ -4006,7 +4006,7 @@ Kdykoli můžete odeslat novou seřizovací zprávu. XMPPBot - + Terms for %1: @@ -4015,12 +4015,12 @@ Pojmy pro %1: - + No terms found, sorry. Nenalezen žádný pojem. Promiňte. - + Hotttness for %1: %2 @@ -4028,7 +4028,7 @@ Hotttness for %1: %2 Oblíbenost %1: %2 - + Familiarity for %1: %2 @@ -4036,7 +4036,7 @@ Familiarity for %1: %2 Stupeň známosti %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index fae0247c74..5c3896355f 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Bland - + Repeat Gentag - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist - + Create new Station - - + + New Station Ny Station - - + + %1 Station %1 Station @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Spilleliste - + Stations Stationer @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk Crash Reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report - + Don't send - + Abort Afbryd - + You can disable sending crash reports in the configuration dialog. Du kan deaktivere afsending af crash reporter i konfigurationsdialogen - + Uploaded %L1 of %L2 KB. Uploadet %L1 af %L2 KB. - - + + Close Luk - + Sent! <b>Many thanks</b>. Sendt! <b>Mange Tak</b> - + Failed to send crash info. Mislykkede at sende crash info @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk Diagnostikker - + &Copy to Clipboard - + Open &Log-file @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Indlæs XSPF - + Playlist URL Spilleliste URL - + Enter URL... Indtast URL - + ... ... - + Automatically update Automatisk Opdatering @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Indlæs XSPF Fil - + XSPF Files (*.xspf) XSPF Filer (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -1246,59 +1246,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings Proxy indstillinger - + Hostname of proxy server Værtsnavn for proxy server - + Host Vært - + Port Port - + Proxy login Proxy login - + User Bruger - + Password Kodeord - + Proxy password Proxy kodeord - + No Proxy Hosts: (Overrides system proxy) Ingen Proxy Vært: (Overskrider system proxy) - + localhost *.example.com (space separated) lokalvært *.eksempel.com (space separated) - + Use proxy for DNS lookups? Benyt proxy for DNS opslag? @@ -1449,12 +1449,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 Ikke fundet: %1 - + Failed to load: %1 Kunne ikke indlæse: %1 @@ -1514,77 +1514,77 @@ connect and stream from you? SettingsDialog - + Collection Samling - + Advanced Advanceret - + All Alle - + Some changed settings will not take effect until Tomahawk is restarted - + Services Services - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file Installer resolver fra fil - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Information @@ -1592,7 +1592,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1600,77 +1600,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1678,12 +1678,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1691,17 +1691,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1709,12 +1709,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1740,32 +1740,32 @@ connect and stream from you? SocialWidget - + Facebook - + Twitter - + Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 - + %1 characters left @@ -1773,44 +1773,44 @@ connect and stream from you? SourceDelegate - + Track Musiknummer - + Album Album - + Artist Kunstner - + Local Lokal - + Top 10 Top 10 - + All available tracks Alle tilgængelige numre - - + + Show Vis - - + + Hide Skjul @@ -1851,53 +1851,53 @@ connect and stream from you? SourceItem - - + + Latest Additions Seneste Tilføjelser - + Recently Played Senest Afspillet - + SuperCollection SuperSamling - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - + Recently Played Tracks - + Your recently played tracks - + %1's recently played tracks - + Sorry, we could not find any recent plays! @@ -1905,68 +1905,68 @@ connect and stream from you? SourceTreeView - + &Copy Link &Kopier Link - + &Delete %1 &Slet %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist &Eksporter Spilleliste - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF Gem XSPF - + Playlists (*.xspf) Spilleliste (*.xspf) @@ -1974,77 +1974,77 @@ connect and stream from you? SourcesModel - + Group Gruppe - + Collection Samling - + Playlist Spilleliste - + Automatic Playlist Automatisk Spilleliste - + Station Station - + Browse Browse - + Search History Søge historie - + My Music Min Musik - + SuperCollection SuperSamling - + Cloud - + Dashboard Instrumentbræt - + Recently Played Nyligt Afspillede - + Charts Lister - + New Releases - + Friends Venner @@ -2118,17 +2118,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link - + Copy Album Link - + Copy Track Link @@ -2599,13 +2599,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3483,7 +3483,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling @@ -3513,48 +3513,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window Skjul Tomahawk Vindue - + Show Tomahawk Window Vis Tomahawk Vindue - + Currently not playing. - + Play Afspil - + Pause Pause - + &Love - + Un-&Love - + &Continue Playback after current Track @@ -3562,161 +3562,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF Fejl - + This is not a valid XSPF playlist. Dette er ikke en gyldig XSPF spilleliste - + Failed to save tracks Fejlede i at gemme numrene - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station Lav Ny Station - + Name: Navn: - + Playlist - + Automatic Playlist - + Pause Pause - + &Play - + %1 by %2 track, artist name %1 af %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -3991,7 +3991,7 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: @@ -4000,12 +4000,12 @@ Betingelser for %1: - + No terms found, sorry. Beklager, ingen betingelser fundet - + Hotttness for %1: %2 @@ -4014,14 +4014,14 @@ Hottness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index eb49881b1c..e3aaf252c2 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -56,18 +56,18 @@ erlauben sich mit dir zu verbinden? AccountListWidget - + Connections Verbindungen - - + + Connect &All &Alle verbinden - + Disconnect &All &Alle trennen @@ -75,7 +75,7 @@ erlauben sich mit dir zu verbinden? AccountWidget - + Invite Einladen @@ -83,7 +83,7 @@ erlauben sich mit dir zu verbinden? AccountsToolButton - + Configure Accounts Konten konfigurieren @@ -341,37 +341,37 @@ erlauben sich mit dir zu verbinden? AudioControls - + Shuffle Zufall - + Repeat Wiederholen - + Time Elapsed Abgelaufene Zeit - + Time Remaining Verbleibende Zeit - + Playing from %1 Wiedergabe von %1 - + Share Teilen - + Love Lieben @@ -397,24 +397,24 @@ erlauben sich mit dir zu verbinden? CategoryAddItem - + Create new Playlist Neue Playlist erstellen - + Create new Station Neue Station erstellen - - + + New Station Neue Station - - + + %1 Station %1 Station @@ -422,12 +422,12 @@ erlauben sich mit dir zu verbinden? CategoryItem - + Playlists Playlisten - + Stations Stationen @@ -457,53 +457,53 @@ erlauben sich mit dir zu verbinden? CrashReporter - + Tomahawk Crash Reporter Tomahawk Fehlermelder - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk ist abgestürzt. Bitte informiere uns darüber! Tomahawk hat einen Fehlerbericht für dich erstellt, der uns dabei helfen kann die Stabilität zukünftiger Versionen zu verbessern. Du kannst diesen Bericht nun direkt an die Tomahawk Entwickler abschicken.</p></body></html> - + Send this report Report abschicken - + Don't send Nicht abschicken - + Abort Abbrechen - + You can disable sending crash reports in the configuration dialog. Du kannst das Übermitteln der Fehlerberichte in den Einstellungen abschalten. - + Uploaded %L1 of %L2 KB. %L1 von %L2 hochgeladen. - - + + Close Schließen - + Sent! <b>Many thanks</b>. Gesendet! <b>Vielen Dank!</b> - + Failed to send crash info. Übertragung des Fehlerberichts fehlgeschlagen. @@ -542,17 +542,17 @@ erlauben sich mit dir zu verbinden? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk Diagnose Tool - + &Copy to Clipboard In die Zwischenablage &kopieren - + Open &Log-file &Log-Datei öffnen @@ -679,7 +679,7 @@ erlauben sich mit dir zu verbinden? InboxItem - + Inbox Posteingang @@ -775,27 +775,27 @@ erlauben sich mit dir zu verbinden? LoadXSPF - + Load XSPF Lade XSPF - + Playlist URL Url der Playlist - + Enter URL... Url eingeben... - + ... - + Automatically update Automatisch aktualisieren @@ -803,12 +803,12 @@ erlauben sich mit dir zu verbinden? LoadXSPFDialog - + Load XSPF File Lade XSPF Datei - + XSPF Files (*.xspf) XSPF Dateien (*.xspf) @@ -839,32 +839,32 @@ erlauben sich mit dir zu verbinden? LovedTracksItem - + Top Loved Tracks Meist-geliebte Songs - + Sorry, we could not find any loved tracks! Sorry, wir konnten keine Lieblingslieder finden! - + The most loved tracks from all your friends Die meist-geliebten Songs all deiner Freunde - + All of your loved tracks Deine Lieblingslieder - + All of %1's loved tracks %1's Lieblingslieder - + Loved Tracks Lieblingslieder @@ -1247,59 +1247,59 @@ erlauben sich mit dir zu verbinden? ProxyDialog - + Proxy Settings Proxy-Einstellungen - + Hostname of proxy server Rechnername des Proxy Servers - + Host Rechnername - + Port Port - + Proxy login Proxy Nutzername - + User Benutzer - + Password Passwort - + Proxy password Proxy Passwort - + No Proxy Hosts: (Overrides system proxy) Proxy Ausnahmen: (Hat Vorrang vor System Proxy) - + localhost *.example.com (space separated) localhost *.beispiel.de (Getrennt durch Leerzeichen) - + Use proxy for DNS lookups? Proxy für DNS Anfragen verwenden? @@ -1450,12 +1450,12 @@ erlauben sich mit dir zu verbinden? ResolverConfigDelegate - + Not found: %1 Nicht gefunden: %1 - + Failed to load: %1 Fehler beim Laden: %1 @@ -1515,77 +1515,77 @@ erlauben sich mit dir zu verbinden? SettingsDialog - + Collection Sammlung - + Advanced Erweitert - + All Alle - + Some changed settings will not take effect until Tomahawk is restarted Einige geänderte Einstellungen haben keinen Effekt bis zum nächsten Neustart - + Services Dienste - + Install from file Von Datei installieren - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Konfiguriere Konten und Dienste die Tomahawk verwendet um Musik zu finden, sich mit Freunden zu verbinden oder deinen Status zu aktualisieren. - + Manage how Tomahawk finds music on your computer. Steuere wie Tomahawk Musik auf deinem System findet. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Konfiguriere Tomahawk's erweiterte Einstellungen, wie Netzwerk Optionen, Browser Interaktion und mehr. - + Install resolver from file Installiere Resolver Datei - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;Alle Dateien (*) - + Resolver installation from file %1 failed. Die Resolver Installation von der Datei %1 ist fehlgeschlagen - + Delete all Access Control entries? Alle erteilten Zugriffsrechte löschen? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Willst du wirklich alle Zugriffseinstellungen zurücksetzen? Du wirst für alle Verbindungen erneut nach Zugriffseinstellungen gefragt werden. - + Information Information @@ -1593,7 +1593,7 @@ erlauben sich mit dir zu verbinden? Settings_Accounts - + Filter by capability: Nach Fähigkeit filtern: @@ -1601,77 +1601,77 @@ erlauben sich mit dir zu verbinden? Settings_Advanced - + Remote Peer Connection Method Verbindung zu anderen Tomahawks - + None (outgoing connections only) Keine (nur ausgehende Verbindungen möglich) - + Use UPnP to establish port forward (recommended) UPnP für Port-Weiterleitung verwenden (empfohlen) - + Use static external IP address/host name and port Statische externe IP Adresse / Hostnamen und Port verwenden - + Set this to your external IP address or host name. Make sure to forward the port to this host! Stelle hier deine externe IP Adresse oder Hostnamen ein. Du musst den Port selbst an diesen Rechner weiterleiten! - + Static Host Name: Statischer Rechnername: - + Static Port: Statischer Port: - + SOCKS Proxy SOCKS Proxy - + Use SOCKS Proxy SOCKS Proxy verwenden - + Proxy Settings... Proxy-Einstellungen… - + Other Settings Andere Einstellungen - + Allow web browsers to interact with Tomahawk (recommended) Erlaube Web Browser mit Tomahawk zu interagieren (empfohlen) - + Send reports after Tomahawk crashed Übermittlung von Fehlerberichten nach Tomahawk Absturz - + Show notification when a new song starts to play Ankündigung einblenden wenn ein neues Lied startet - + Clear All Access Control Entries Alle erteilten Zugriffsrechte löschen @@ -1679,12 +1679,12 @@ erlauben sich mit dir zu verbinden? Settings_Collection - + Path to scan for music files: Pfad zu den Musikdateien: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1692,17 +1692,17 @@ erlauben sich mit dir zu verbinden? The Echo Nest kann die Metadaten deiner Sammlung analysieren um dir personalisierte Radio Stationen anzubieten. Wenn diese Option aktiviert ist, kannst du (und all deine Freunde) Automatische Playlisten und Stationen erstellen die auf deinen persönlichen Musikgeschmack zugeschnitten wurden. - + Upload collection list to The Echo Nest to enable user radio Liste der aktuellen Sammlung an Echo Nest übermitteln, um User Radio zu aktivieren - + Watch for changes Automatisch aktualisieren - + Time between scans, in seconds: Zeitinterval (in Sekunden) zwischen zwei Scans: @@ -1710,12 +1710,12 @@ erlauben sich mit dir zu verbinden? SlideSwitchButton - + On Ein - + Off Aus @@ -1741,32 +1741,32 @@ erlauben sich mit dir zu verbinden? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 Hört "%1" von %2, %3 - + Listening to "%1" by %2 on "%3". %4 Hört "%1" von %2 auf "%3", %4 - + %1 characters left %1 Zeichen übrig @@ -1774,44 +1774,44 @@ erlauben sich mit dir zu verbinden? SourceDelegate - + Track Lied - + Album Album - + Artist Künstler - + Local Lokal - + Top 10 Top 10 - + All available tracks Alle verfügbaren Stücke - - + + Show Einblenden - - + + Hide Verstecken @@ -1852,53 +1852,53 @@ erlauben sich mit dir zu verbinden? SourceItem - - + + Latest Additions Kürzlich hinzugekommen - + Recently Played Kürzlich gehörte Lieder - + SuperCollection Supersammlung - + Latest additions to your collection Neueste Lieder in deiner Sammlung - + Latest additions to %1's collection Neueste Lieder in %1's Sammlung - + Sorry, we could not find any recent additions! Sorry, wir konnten keine Lieder finden die kürzlich hinzugefügt wurden! - + Recently Played Tracks Zuletzt gehörte Lieder - + Your recently played tracks Deine zuletzt gehörten Lieder - + %1's recently played tracks %1's zuletzt gehörte Lieder - + Sorry, we could not find any recent plays! Sorry, wir konnten keine kürzlich gespielten Lieder finden! @@ -1906,68 +1906,68 @@ erlauben sich mit dir zu verbinden? SourceTreeView - + &Copy Link &Kopiere Link - + &Delete %1 &Lösche %1 - + Add to my Playlists Zu meinen Playlisten hinzufügen - + Add to my Automatic Playlists Zu meinen Automatischen Playlisten hinzufügen - + Add to my Stations Zu meinen Stationen hinzufügen - + &Export Playlist Playlist &exportieren - + playlist Playlist - + automatic playlist Automatische Playlist - + station Station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Möchtest du die %1 <b>"%2"</b> löschen? - + Delete Löschen - + Save XSPF XSPF speichern - + Playlists (*.xspf) Playlisten (*.xspf) @@ -1975,77 +1975,77 @@ erlauben sich mit dir zu verbinden? SourcesModel - + Group Gruppe - + Collection Sammlung - + Playlist Playlist - + Automatic Playlist Automatische Playlist - + Station Station - + Browse Stöbern - + Search History Suchverlauf - + My Music Meine Musik - + SuperCollection Supersammlung - + Cloud Cloud - + Dashboard Dashboard - + Recently Played Kürzlich gehörte Lieder - + Charts Charts - + New Releases Neuerscheinungen - + Friends Freunde @@ -2119,17 +2119,17 @@ erlauben sich mit dir zu verbinden? TemporaryPageItem - + Copy Artist Link Kopiere Link für diesen Künstler - + Copy Album Link Kopiere Link für dieses Album - + Copy Track Link Kopiere Link für diesen Lied @@ -2600,13 +2600,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Sammlung - + This collection is empty. Diese Sammlung ist leer. @@ -3488,7 +3488,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -3519,48 +3519,48 @@ Tomahawk auf Twitter's Website authentifiziert hast: TomahawkTrayIcon - + &Stop Playback after current Track Wiedergabe nach diesem Lied &stoppen - - + + Hide Tomahawk Window Tomahawk verbergen - + Show Tomahawk Window Tomahawk anzeigen - + Currently not playing. Derzeit wird nichts gespielt. - + Play Abspielen - + Pause Pause - + &Love Als &Lieblingslied merken - + Un-&Love Als &Lieblingslied entfernen - + &Continue Playback after current Track Wiedergabe nach diesem Lied &fortsetzen @@ -3568,161 +3568,161 @@ Tomahawk auf Twitter's Website authentifiziert hast: TomahawkWindow - + Tomahawk Tomahawk - + Back Zurück - + Go back one page Gehe eine Seite zurück - + Forward Vorwärts - + Go forward one page Gehe eine Seite vorwärts - - + + Hide Menu Bar Menüleiste ausblenden - - + + Show Menu Bar Menüleiste einblenden - + Search for any artist, album or song... Suche nach Künstler, Album oder Lied... - + &Main Menu Haupt&menü - + Exit Full Screen Vollbildmodus deaktivieren - + Enter Full Screen Vollbildmodus aktivieren - + XSPF Error XSPF-Fehler - + This is not a valid XSPF playlist. Dies ist keine gültige XSPF-Playlist. - + Failed to save tracks Konnte Stücke nicht abspeichern - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Einige Stücke in der Playlist enthalten weder Künstler noch Titel. Diese werden ignoriert. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. Vergewisser dich, dass ein geignetes Phonon-Backend mitsamt benötigten Plugins installiert ist. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. - + Station Station - + Create New Station Neue Station erstellen - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatische Playlist - + Pause Pause - + &Play Abs&pielen - + %1 by %2 track, artist name %1 von %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Danke an: - + About Tomahawk Über Tomahawk @@ -4001,7 +4001,7 @@ Du kannst jederzeit eine neue Sync-Nachricht abschicken. XMPPBot - + Terms for %1: @@ -4010,26 +4010,26 @@ Begriffe für %1: - + No terms found, sorry. Kein Begriff gefunden, Sorry. - + Hotttness for %1: %2 Hotttness für %1: %2 - + Familiarity for %1: %2 Bekanntheitsgrad für %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index ef34319970..9de421a88a 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections Συνδέσεις - - + + Connect &All Συνδεση ολων - + Disconnect &All Αποσυνδεση ολων @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite Προσκληση @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Ρυθμισεις λογαριασμων @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Τυχαία σειρά - + Repeat Επανάληψη - + Time Elapsed Χρόνος που πέρασε - + Time Remaining Υπολοιπος χρονος - + Playing from %1 Αναπαραγωγη απο %1 - + Share Διαμοιρασμος - + Love Αγάπησε @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Δημιουργία νέας Λίστας Αναπαραγωγής - + Create new Station Δημιουργία νέου Σταθμού - - + + New Station Νέος Σταθμός - - + + %1 Station %1 Σταθμού @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Λιστες Αναπαραγωγης - + Stations Σταθμοι @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Αναφορά Καταρρεύσεων Tomahawk - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Συγγνωμη!</span> Tο Tomahawk συνετρίβη. Πειτε μας για αυτο! Το Tomahawk δημιουργησε μια αναφορα σφαλματων που μπορει να μας βοηθησει να το διορθωσουμε. Μπορειτε να την στειλετε τωρα στους προγραμματιστες του Tomahawk.</p></body></html> - + Send this report Αποστολή αυτής της αναφοράς - + Don't send Μην στείλεις - + Abort Ματαίωση - + You can disable sending crash reports in the configuration dialog. Μπορείτε να απενεργοποιήσετε την αποστολή αναφορών κατάρρευσης στον διάλογο ρυθμίσεων. - + Uploaded %L1 of %L2 KB. Επιφορτώθηκαν %L1 από %L2 KB. - - + + Close Κλείσιμο - + Sent! <b>Many thanks</b>. Στάλθηκε! <b>Πολλά ευχαριστώ<b>. - + Failed to send crash info. Αποτυχία αποστολής πληροφοριών κατάρρευσης. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Διαγνωστικά Tomahawk - + &Copy to Clipboard &Αντιγραφή στο Πρόχειρο - + Open &Log-file Άνοιγμα &Αρχείου καταγραφής @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox Εισερχομενα @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Φόρτωση Αρχείου XSPF - + Playlist URL URL λίστας αναπαραγωγής - + Enter URL... Εισαγωγή URL... - + ... ... - + Automatically update Αυτόματη ενημέρωση @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Φόρτωση Αρχείου XSPF - + XSPF Files (*.xspf) Αρχεία XSPF (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks Κορυφαία Αγαπημένα Κομμάτια - + Sorry, we could not find any loved tracks! Συγνωμη, δεν βρεθηκε κανενα αγαπημενο τραγουδι! - + The most loved tracks from all your friends Τα πιο αγαπημένα κομμάτια από όλους τους φίλους σας - + All of your loved tracks Όλα τα αγαπημένα κομμάτια σας - + All of %1's loved tracks Όλα τα αγαπημένα κομμάτια του/της %1 - + Loved Tracks Αγαπημένα Κομμάτια @@ -1246,59 +1246,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings Ρυθμίσεις Διαμεσολαβητή - + Hostname of proxy server Όνομα διαμεσολαβητή - + Host Υπολογιστής - + Port Θύρα - + Proxy login Όνομα χρήστη στον διαμεσολαβητή - + User Χρήστης - + Password Συνθηματικό - + Proxy password Συνθηματικό διαμεσολαβητή - + No Proxy Hosts: (Overrides system proxy) Απουσία Διαμεσολαβητών (Παρακάμπτει τον διαμεσολαβητή συστήματος) - + localhost *.example.com (space separated) localhost *.example.com (χωρισμένο με κενά) - + Use proxy for DNS lookups? Χρήση διαμεσολαβητή για αναζήτηση DNS; @@ -1449,12 +1449,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 Δεν βρέθηκε : %1 - + Failed to load: %1 Απέτυχε να φορτώσει: %1 @@ -1514,77 +1514,77 @@ connect and stream from you? SettingsDialog - + Collection Συλλογή - + Advanced Προχωρημένα - + All Όλα - + Some changed settings will not take effect until Tomahawk is restarted Μερικές αλλαγμένες ρυθμίσεις δεν θα εφαρμοστούν μέχρι το Tomahawk να επανεκκινηθεί. - + Services Υπηρεσίες - + Install from file Εγκατάσταση από αρχείο - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Διαμορφώστε τους λογαριασμούς και τις υπηρεσίες που χρησιμοποιούνται από το Tomahawk για να αναζητήσετε και να ανακτήσετε μουσική, να βρείτε τους φίλους σας και να τους ενημερώσετε για την κατάστασή σας. - + Manage how Tomahawk finds music on your computer. Διαχειριση του τροπου που το Tomahawk βρισκει μουσικη στον υπολογιστη σας. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Ρυθμισει των προσαρμοσμενων ρυθμισεων, συμπεριλαμβανομενων και των ρυθμισεων δικτου, περιηγητη και αλλων. - + Install resolver from file Εγκατάσταση επιλυτή από αρχείο - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;Όλα τα αρχεία (*) - + Resolver installation from file %1 failed. Εγκατάσταση Resolver από αρχείο %1 απέτυχε. - + Delete all Access Control entries? Διαγραφή όλων των καταχωρήσεων Ελέγχου Πρόσβασης? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Θέλετε πραγματικά να διαγράψετε όλες τις καταχωρήσεις ελέγχου πρόσβασης? Θα ερωτηθειτε ξανα για κάθε κόμβο που θα συνδεθείτε. - + Information Πληροφορίες @@ -1592,7 +1592,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: Φιλτράρισμα βάση δυνατότητας: @@ -1600,77 +1600,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method Μεθοδος παρακολουθησης της συνδεσης - + None (outgoing connections only) Καμμια (μονο εξαρχομενες συνδεσεις) - + Use UPnP to establish port forward (recommended) Χρησιμοποιηση του UPnP για προωθηση υποδοχων (συνισταται) - + Use static external IP address/host name and port Χρησιμοποιηση στατικης εξωτερικης IP διευθυνσης/ονομα κομβου και υποδοχης - + Set this to your external IP address or host name. Make sure to forward the port to this host! Εφαρμογη αυτης της εξωτερκης IP διευθυνσης η ονοματος κομβου. - + Static Host Name: Στατικο ονομα κομβου: - + Static Port: Στατικη υποδοχη: - + SOCKS Proxy SOCKS Μεσολαβητης - + Use SOCKS Proxy Χρησιμοποιηση του SOCKS Μεσολαβητη - + Proxy Settings... Ρυθμίσεις Μεσολαβητή... - + Other Settings Άλλες Ρυθμίσεις - + Allow web browsers to interact with Tomahawk (recommended) Αλληλεπιδραση προγραμματων περιηγησης με το Tomahawk (συνιστάται) - + Send reports after Tomahawk crashed Αποστολη αναφορων μετα την συντριβη του Tomahawk - + Show notification when a new song starts to play Ειδοποιηση οταν το νεο τραγουδι αρχιζει να αναπαραγετε - + Clear All Access Control Entries Εκκαθαριση όλων των καταχωρήσεων Ελέγχου Πρόσβασης @@ -1678,12 +1678,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: Διαδρομή για σάρωση αρχείων μουσικής: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1691,17 +1691,17 @@ connect and stream from you? Το Echo Nest υποστηρίζει την παρακολούθηση των μεταδεδομένων κατάλογων σας και την χρησιμοποιεί για τα σκάφη εξατομικευμένα ραδιόφωνα. Η ενεργοποίηση αυτής της ⏎ επιλογήα θα σας επιτρέψει (και όλους τους φίλους σας) για να δημιουργήσετε αυτόματες λίστες αναπαραγωγής και ⏎ σταθμούς με βάση τις προσωπικές σας προτιμήσεις. - + Upload collection list to The Echo Nest to enable user radio Ανεβάστε λίστα συλλογων στο Echo για ενεργοποιηση της χρησης ραδιοφωνου - + Watch for changes Παρακολούθηση για αλλαγές - + Time between scans, in seconds: Χρόνος μεταξύ των σαρώσεων, σε δευτερόλεπτα: @@ -1710,12 +1710,12 @@ connect and stream from you? SlideSwitchButton - + On Ενεργοποιηση - + Off Απενεργοποιηση @@ -1741,32 +1741,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 Ακουστε το "%1" απο %2. %3 - + Listening to "%1" by %2 on "%3". %4 Ακουστε το "%1" απο %2 σε "%3". %4 - + %1 characters left %1 χαρακτηρες @@ -1774,44 +1774,44 @@ connect and stream from you? SourceDelegate - + Track Τραγούδι - + Album Άλμπουμ - + Artist Καλλιτέχνες - + Local Τοπικό - + Top 10 Κορυφαία 10 - + All available tracks Όλα τα διαθέσιμα τραγούδια - - + + Show Εμφάνιση - - + + Hide Απόκρυψη @@ -1852,53 +1852,53 @@ connect and stream from you? SourceItem - - + + Latest Additions Τελευταίες Προσθήκες - + Recently Played Τελευταίες Αναπαραγωγές - + SuperCollection ΥπερΣυλλογή - + Latest additions to your collection Τελευταίες προσθήκες στην βιβλιοθήκη σας - + Latest additions to %1's collection Τελευταίες προσθήκες στην βιβλιοθήκη του %1 - + Sorry, we could not find any recent additions! Συγγνωμη, δεν βρεθηκαν προσφατες προσθηκες! - + Recently Played Tracks Τελευταίες Αναπαραγωγές Κομματιών - + Your recently played tracks Οι τελευταίες σας αναπαραγωγές - + %1's recently played tracks %1's Τελευταίες αναπαραγωγές τραγουδιων - + Sorry, we could not find any recent plays! Συγγνωμη, δεν βρεθηκαν προσφατες αναπαραγωγες! @@ -1906,68 +1906,68 @@ connect and stream from you? SourceTreeView - + &Copy Link &Αντιγραφή Υπερσυνδέσμου - + &Delete %1 &Διαγραφή %1 - + Add to my Playlists Προσθηκη στις λιστες αναπαραγωγης - + Add to my Automatic Playlists Προσθηκη στις αυτοματες λιστες αναπαραγωγης σας - + Add to my Stations Προσθηκη στους σταθμους μου - + &Export Playlist &Εξαγωγή Λίστας Αναπαραγωγής - + playlist λίστας αναπαραγωγής - + automatic playlist αυτόματη λίστα αναπαραγωγής - + station σταθμός - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Θέλετε να διαγράψετε την %1 <b>"%2"</b>; - + Delete Διαγραφή - + Save XSPF Αποθήκευση αρχείου XSPF - + Playlists (*.xspf) Λίστες Αναπαραγωγής (*.xspf) @@ -1975,77 +1975,77 @@ connect and stream from you? SourcesModel - + Group Ομάδα - + Collection Συλλογή - + Playlist Λίστα Αναπαραγωγής - + Automatic Playlist Αυτόματη Λίστα Αναπαραγωγής - + Station Σταθμός - + Browse Εύρεση - + Search History Ιστορικο Αναζητησης - + My Music Η Μουσικη Μου - + SuperCollection ΥπερΣυλλογή - + Cloud Σύννεφο - + Dashboard Πίνακας Ελέγχου - + Recently Played Τελευταίες Αναπαραγωγές - + Charts Γραφήματα - + New Releases Νέες Κυκλοφορίες - + Friends Φίλοι @@ -2119,17 +2119,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link Αντιγραφή Συνδέσμου Καλλιτέχνη - + Copy Album Link Αντιγραφή Συνδέσμου Άλμπουμ - + Copy Track Link Αντιγραφή Συνδέσμου Κομματιού @@ -2606,13 +2606,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Συλλογή - + This collection is empty. Αυτή η συλλογή ειναι άδεια. @@ -3494,7 +3494,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου @@ -3524,48 +3524,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track &Διακοπή Αναπαραγωγής μετά το τρέχον Κομμάτι - - + + Hide Tomahawk Window Απόκρυψη του Tomahawk παραθύρου - + Show Tomahawk Window Εμφάνιση του Tomahawk παραθύρου - + Currently not playing. Επί του παρόντος δεν παίζει. - + Play Αναπαραγωγή - + Pause Παύση - + &Love &Αγάπησε - + Un-&Love Ξε-&Αγάπησε - + &Continue Playback after current Track &Συνέχιση Αναπαραγωγής μετά το τρέχον Κομμάτι @@ -3573,161 +3573,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back Πίσω - + Go back one page Πήγαινε πίσω μία σελίδα - + Forward Μπροστά - + Go forward one page Πήγαινε μπροστά μία σελίδα - - + + Hide Menu Bar Απόκρυψη Γραμμής Μενού - - + + Show Menu Bar Εμφανιση του κεντρικου μενου - + Search for any artist, album or song... Αναζήτηση για οποιονδήποτε καλλιτέχνη, άλμπουμ ή τραγούδι... - + &Main Menu &Κεντρικο Μενου - + Exit Full Screen Έξοδος από Πλήρη Οθόνη - + Enter Full Screen Εισαγωγή σε Πλήρη Οθόνη - + XSPF Error Σφάλμα XSPF - + This is not a valid XSPF playlist. Αυτή δεν είναι μια έγκυρη λίστα αναπαραγωγής XSPF. - + Failed to save tracks Αποτυχία αποθήκευσης κομματιών. - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Μερικά κομμάτια στην λίστα αναπαραγωγής δεν περιέχουν έναν καλλιτέχνη ή έναν τίτλο. Θα αγνοηθούν. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. Σιγουρευτείτε ότι έχετε εγκαταστήσει ένα κατάλληλο Phonon backend και τα απαιτούμενα πρόσθετα. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. - + Station Σταθμός - + Create New Station Δημιουργία Νέου Σταθμού - + Name: Όνομα: - + Playlist Λίστας Αναπαραγωγής - + Automatic Playlist Αυτόματη Λίστα Αναπαραγωγής - + Pause Παύση - + &Play &Αναπαραγωγή - + %1 by %2 track, artist name %1 από %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Χάρη στους: - + About Tomahawk Σχετικά με το Tomahawk @@ -4002,19 +4002,19 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: ⏎ Terms for %1:⏎ - + No terms found, sorry. Δεν βρέθηκαν όροι, συγνώμη. - + Hotttness for %1: %2 @@ -4023,7 +4023,7 @@ Hotttness for %1: %2 - + Familiarity for %1: %2 @@ -4032,7 +4032,7 @@ Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 7fddb85371..0dc3fac856 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -56,18 +56,18 @@ connect and stream from you? AccountListWidget - + Connections Connections - - + + Connect &All Connect &All - + Disconnect &All Disconnect &All @@ -75,7 +75,7 @@ connect and stream from you? AccountWidget - + Invite Invite @@ -83,7 +83,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Configure Accounts @@ -341,37 +341,37 @@ connect and stream from you? AudioControls - + Shuffle Shuffle - + Repeat Repeat - + Time Elapsed Time Elapsed - + Time Remaining Time Remaining - + Playing from %1 Playing from %1 - + Share Share - + Love Love @@ -397,24 +397,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Create new Playlist - + Create new Station Create new Station - - + + New Station New Station - - + + %1 Station %1 Station @@ -422,12 +422,12 @@ connect and stream from you? CategoryItem - + Playlists Playlists - + Stations Stations @@ -457,53 +457,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk Crash Reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report Send this report - + Don't send Don't send - + Abort Abort - + You can disable sending crash reports in the configuration dialog. You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. Uploaded %L1 of %L2 KB. - - + + Close Close - + Sent! <b>Many thanks</b>. Sent! <b>Many thanks</b>. - + Failed to send crash info. Failed to send crash info. @@ -542,17 +542,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk Diagnostics - + &Copy to Clipboard &Copy to Clipboard - + Open &Log-file Open &Log-file @@ -679,7 +679,7 @@ connect and stream from you? InboxItem - + Inbox Inbox @@ -775,27 +775,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Load XSPF - + Playlist URL Playlist URL - + Enter URL... Enter URL... - + ... ... - + Automatically update Automatically update @@ -803,12 +803,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Load XSPF File - + XSPF Files (*.xspf) XSPF Files (*.xspf) @@ -839,32 +839,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks Top Loved Tracks - + Sorry, we could not find any loved tracks! Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends The most loved tracks from all your friends - + All of your loved tracks All of your loved tracks - + All of %1's loved tracks All of %1's loved tracks - + Loved Tracks Loved Tracks @@ -1247,59 +1247,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings Proxy Settings - + Hostname of proxy server Hostname of proxy server - + Host Host - + Port Port - + Proxy login Proxy login - + User User - + Password Password - + Proxy password Proxy password - + No Proxy Hosts: (Overrides system proxy) No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) localhost *.example.com (space separated) - + Use proxy for DNS lookups? Use proxy for DNS lookups? @@ -1450,12 +1450,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 Not found: %1 - + Failed to load: %1 Failed to load: %1 @@ -1515,77 +1515,77 @@ connect and stream from you? SettingsDialog - + Collection Collection - + Advanced Advanced - + All All - + Some changed settings will not take effect until Tomahawk is restarted Some changed settings will not take effect until Tomahawk is restarted - + Services Services - + Install from file Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. Resolver installation from file %1 failed. - + Delete all Access Control entries? Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Information @@ -1593,7 +1593,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: Filter by capability: @@ -1601,77 +1601,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method Remote Peer Connection Method - + None (outgoing connections only) None (outgoing connections only) - + Use UPnP to establish port forward (recommended) Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: Static Host Name: - + Static Port: Static Port: - + SOCKS Proxy SOCKS Proxy - + Use SOCKS Proxy Use SOCKS Proxy - + Proxy Settings... Proxy Settings... - + Other Settings Other Settings - + Allow web browsers to interact with Tomahawk (recommended) Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed Send reports after Tomahawk crashed - + Show notification when a new song starts to play Show notification when a new song starts to play - + Clear All Access Control Entries Clear All Access Control Entries @@ -1679,12 +1679,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1695,17 +1695,17 @@ connect and stream from you? and stations based on your personal taste profile. - + Upload collection list to The Echo Nest to enable user radio Upload collection list to The Echo Nest to enable user radio - + Watch for changes Watch for changes - + Time between scans, in seconds: Time between scans, in seconds: @@ -1713,12 +1713,12 @@ connect and stream from you? SlideSwitchButton - + On On - + Off Off @@ -1744,32 +1744,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 Listening to "%1" by %2 on "%3". %4 - + %1 characters left %1 characters left @@ -1777,44 +1777,44 @@ connect and stream from you? SourceDelegate - + Track Track - + Album Album - + Artist Artist - + Local Local - + Top 10 Top 10 - + All available tracks All available tracks - - + + Show Show - - + + Hide Hide @@ -1855,53 +1855,53 @@ connect and stream from you? SourceItem - - + + Latest Additions Latest Additions - + Recently Played Recently Played - + SuperCollection SuperCollection - + Latest additions to your collection Latest additions to your collection - + Latest additions to %1's collection Latest additions to %1's collection - + Sorry, we could not find any recent additions! Sorry, we could not find any recent additions! - + Recently Played Tracks Recently Played Tracks - + Your recently played tracks Your recently played tracks - + %1's recently played tracks %1's recently played tracks - + Sorry, we could not find any recent plays! Sorry, we could not find any recent plays! @@ -1909,68 +1909,68 @@ connect and stream from you? SourceTreeView - + &Copy Link &Copy Link - + &Delete %1 &Delete %1 - + Add to my Playlists Add to my Playlists - + Add to my Automatic Playlists Add to my Automatic Playlists - + Add to my Stations Add to my Stations - + &Export Playlist &Export Playlist - + playlist playlist - + automatic playlist automatic playlist - + station station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Would you like to delete the %1 <b>"%2"</b>? - + Delete Delete - + Save XSPF Save XSPF - + Playlists (*.xspf) Playlists (*.xspf) @@ -1978,77 +1978,77 @@ connect and stream from you? SourcesModel - + Group Group - + Collection Collection - + Playlist Playlist - + Automatic Playlist Automatic Playlist - + Station Station - + Browse Browse - + Search History Search History - + My Music My Music - + SuperCollection SuperCollection - + Cloud Cloud - + Dashboard Dashboard - + Recently Played Recently Played - + Charts Charts - + New Releases New Releases - + Friends Friends @@ -2122,17 +2122,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link Copy Artist Link - + Copy Album Link Copy Album Link - + Copy Track Link Copy Track Link @@ -2608,13 +2608,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Collection - + This collection is empty. This collection is empty. @@ -3496,7 +3496,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -3527,48 +3527,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track &Stop Playback after current Track - - + + Hide Tomahawk Window Hide Tomahawk Window - + Show Tomahawk Window Show Tomahawk Window - + Currently not playing. Currently not playing. - + Play Play - + Pause Pause - + &Love &Love - + Un-&Love Un-&Love - + &Continue Playback after current Track &Continue Playback after current Track @@ -3576,161 +3576,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back Back - + Go back one page Go back one page - + Forward Forward - + Go forward one page Go forward one page - - + + Hide Menu Bar Hide Menu Bar - - + + Show Menu Bar Show Menu Bar - + Search for any artist, album or song... Search for any artist, album or song... - + &Main Menu &Main Menu - + Exit Full Screen Exit Full Screen - + Enter Full Screen Enter Full Screen - + XSPF Error XSPF Error - + This is not a valid XSPF playlist. This is not a valid XSPF playlist. - + Failed to save tracks Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Station - + Create New Station Create New Station - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatic Playlist - + Pause Pause - + &Play &Play - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk About Tomahawk @@ -4010,7 +4010,7 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: @@ -4019,12 +4019,12 @@ Terms for %1: - + No terms found, sorry. No terms found, sorry. - + Hotttness for %1: %2 @@ -4033,7 +4033,7 @@ Hotttness for %1: %2 - + Familiarity for %1: %2 @@ -4042,7 +4042,7 @@ Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 6312273991..ba9c99f342 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -56,18 +56,18 @@ conectarse a usted y transmitir música? AccountListWidget - + Connections Conexiones - - + + Connect &All Conect&ar todo - + Disconnect &All Desconect&ar todo @@ -75,7 +75,7 @@ conectarse a usted y transmitir música? AccountWidget - + Invite Invitar @@ -83,7 +83,7 @@ conectarse a usted y transmitir música? AccountsToolButton - + Configure Accounts Configurar cuentas @@ -341,37 +341,37 @@ conectarse a usted y transmitir música? AudioControls - + Shuffle Aleatorio - + Repeat Repetir - + Time Elapsed Tiempo transcurrido - + Time Remaining Tiempo restante - + Playing from %1 Reproduciendo de %1 - + Share Compartir - + Love Favorito @@ -397,24 +397,24 @@ conectarse a usted y transmitir música? CategoryAddItem - + Create new Playlist Nueva lista de reproducción - + Create new Station Crear estación nueva - - + + New Station Estación nueva - - + + %1 Station %1 estación @@ -422,12 +422,12 @@ conectarse a usted y transmitir música? CategoryItem - + Playlists Listas de reproducción - + Stations Estaciones @@ -457,53 +457,53 @@ conectarse a usted y transmitir música? CrashReporter - + Tomahawk Crash Reporter Informador de fallos de Tomahawk - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">¡Lo siento!</span>Tomahawk ha fallado. ¡Por favor infórmenos! Tomahawk ha creado un informe de error que puede mejorar la estabilidad del programa en el futuro. Usted puede mandar este informe directamente a los desarrolladores de Tomahawk.</p></body></html> - + Send this report Enviar informe de fallo - + Don't send No enviar - + Abort Cancelar - + You can disable sending crash reports in the configuration dialog. Puede desactivar el envío de informes de error en el diálogo de configuración. - + Uploaded %L1 of %L2 KB. Se subieron %L1 de %L2 KB. - - + + Close Cerrar - + Sent! <b>Many thanks</b>. ¡Enviado! <b>Muchas gracias</b>. - + Failed to send crash info. Error al enviar el informe del fallo. @@ -542,17 +542,17 @@ conectarse a usted y transmitir música? DiagnosticsDialog - + Tomahawk Diagnostics Diagnósticos de Tomahawk - + &Copy to Clipboard &Copiar al portapapeles - + Open &Log-file Abrir archivo de &registro @@ -679,7 +679,7 @@ conectarse a usted y transmitir música? InboxItem - + Inbox @@ -775,27 +775,27 @@ conectarse a usted y transmitir música? LoadXSPF - + Load XSPF Cargar XSPF - + Playlist URL URL de lista de reproducción - + Enter URL... Introducir URL… - + ... - + Automatically update Actualizar automáticamente @@ -803,12 +803,12 @@ conectarse a usted y transmitir música? LoadXSPFDialog - + Load XSPF File Cargar archivo XSPF - + XSPF Files (*.xspf) Archivos XSPF (*.xspf) @@ -839,32 +839,32 @@ conectarse a usted y transmitir música? LovedTracksItem - + Top Loved Tracks Pistas favoritas - + Sorry, we could not find any loved tracks! No se encontraron pistas favoritas - + The most loved tracks from all your friends Las pistas favoritas de sus amigos - + All of your loved tracks Mis pistas favoritas - + All of %1's loved tracks Pistas favoritas de %1 - + Loved Tracks Pistas favoritas @@ -1247,59 +1247,59 @@ conectarse a usted y transmitir música? ProxyDialog - + Proxy Settings Configuración del proxy - + Hostname of proxy server Hostname del servidor proxy - + Host Host - + Port Puerto - + Proxy login Inicio de sesión de proxy - + User Usuario - + Password Contraseña - + Proxy password Contraseña del proxy - + No Proxy Hosts: (Overrides system proxy) Sin proxy: (Sobreescribe el proxy del sistema) - + localhost *.example.com (space separated) localhost *.ejemplo.com (separados por espacios) - + Use proxy for DNS lookups? ¿Usar proxy para búsquedas DNS? @@ -1450,12 +1450,12 @@ conectarse a usted y transmitir música? ResolverConfigDelegate - + Not found: %1 No encontrado: %1 - + Failed to load: %1 Fallo al cargar: %1 @@ -1515,77 +1515,77 @@ conectarse a usted y transmitir música? SettingsDialog - + Collection Colección - + Advanced Avanzado - + All Todo - + Some changed settings will not take effect until Tomahawk is restarted Algunos cambios no surtirán efecto hasta reiniciar Tomahawk - + Services Servicios - + Install from file Instalar desde archivo - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configurar las cuentas y los servicios usados por Tomahawk para buscar y recolectar música, encontrar a sus amigos y actualizar su estado. - + Manage how Tomahawk finds music on your computer. Configurar cómo Tomahawk encuentra música en su ordenador - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configurar las preferencias avanzadas de Tomahawk, incluyendo la conectividad de red, del navegador y más. - + Install resolver from file Instalar un servicio desde un fichero - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? ¿Eliminar todas las fuentes del Control de acceso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. ¿De verdad quiere borrar todo el Control de acceso de las fuentes? Será preguntado por cada fuente a la que se conecte. - + Information Información @@ -1593,7 +1593,7 @@ conectarse a usted y transmitir música? Settings_Accounts - + Filter by capability: Filtrar por capacidad: @@ -1601,77 +1601,77 @@ conectarse a usted y transmitir música? Settings_Advanced - + Remote Peer Connection Method Método de conexión al peer remoto - + None (outgoing connections only) Ninguna (sólo conexiones salientes) - + Use UPnP to establish port forward (recommended) Utilizar UPnP para establecer el enrutamiento de puertos (recomendado) - + Use static external IP address/host name and port Utilizar una IP estática externa, dirección/nombre del host y puerto - + Set this to your external IP address or host name. Make sure to forward the port to this host! Fijar esta como su dirección IP externa o nombre del host. Asegúrese de enrutar el puerto hacia este host - + Static Host Name: Nombre del host estático - + Static Port: Puerto estático - + SOCKS Proxy Proxy SOCKS - + Use SOCKS Proxy Utilizar proxy SOCKS - + Proxy Settings... Configuración del proxy - + Other Settings Otras preferencias - + Allow web browsers to interact with Tomahawk (recommended) Autorizar a los navegadores web interactuar con Tomahawk (recomendado) - + Send reports after Tomahawk crashed Mandar informes después de un fallo de Tomahawk - + Show notification when a new song starts to play Mostrar una notificación al comenzar la reproducción de una nueva canción - + Clear All Access Control Entries Limpiar todas las fuentes del Control de acceso @@ -1679,12 +1679,12 @@ conectarse a usted y transmitir música? Settings_Collection - + Path to scan for music files: Ruta para escanear archivos de música: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1695,17 +1695,17 @@ usted y sus amigos podrán crear listas de reproducción automáticas y estaciones basadas en sus gustos personales. - + Upload collection list to The Echo Nest to enable user radio Enviar información de la colección a The Echo Nest para activar la radio - + Watch for changes Vigilar cambios en las carpetas - + Time between scans, in seconds: Tiempo entre escaneo, en segundos: @@ -1713,12 +1713,12 @@ y estaciones basadas en sus gustos personales. SlideSwitchButton - + On On - + Off Off @@ -1744,32 +1744,32 @@ y estaciones basadas en sus gustos personales. SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 Escuchando "%1" de %2.%3 - + Listening to "%1" by %2 on "%3". %4 Escuchando "%1" de %2 en "%3". %4 - + %1 characters left %1 caracteres restantes @@ -1777,44 +1777,44 @@ y estaciones basadas en sus gustos personales. SourceDelegate - + Track Pista - + Album Álbum - + Artist Artista - + Local Local - + Top 10 10 mejores - + All available tracks Todas las pistas disponibles - - + + Show Mostrar - - + + Hide Ocultar @@ -1855,53 +1855,53 @@ y estaciones basadas en sus gustos personales. SourceItem - - + + Latest Additions Añadidos recientemente - + Recently Played Reproducido recientemente - + SuperCollection Supercolección - + Latest additions to your collection Pistas añadidas recientemente - + Latest additions to %1's collection Pistas añadidas recientemente en la colección de %1 - + Sorry, we could not find any recent additions! No se encontraron pistas nuevas recientes - + Recently Played Tracks Pistas reproducidas recientemente - + Your recently played tracks Mis pistas escuchadas recientemente - + %1's recently played tracks Canciones escuchadas recientemente por %1 - + Sorry, we could not find any recent plays! No hay reproducciones recientes @@ -1909,68 +1909,68 @@ y estaciones basadas en sus gustos personales. SourceTreeView - + &Copy Link &Copiar enlace - + &Delete %1 &Eliminar %1 - + Add to my Playlists Añadir a mis listas de reproducción - + Add to my Automatic Playlists Añadir a mis listas de reproducción automáticas - + Add to my Stations Añadir a mis estaciones - + &Export Playlist &Exportar lista de reproducción - + playlist lista de reproducción - + automatic playlist lista de reproducción automática - + station estación - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? ¿Borrar lista de reproducción %1 <b>"%2"</b>? - + Delete Eliminar - + Save XSPF Guardar XSPF - + Playlists (*.xspf) Listas de reproducción (*.xspf) @@ -1978,77 +1978,77 @@ y estaciones basadas en sus gustos personales. SourcesModel - + Group Grupo - + Collection Colección - + Playlist Lista de reproducción - + Automatic Playlist Lista de reproducción automática - + Station Estación - + Browse Navegar - + Search History Historial de búsqueda - + My Music Mi música - + SuperCollection Supercolección - + Cloud Nube - + Dashboard Panel de inicio - + Recently Played Reproducido recientemente - + Charts Listas - + New Releases Últimas novedades - + Friends Amigos @@ -2122,17 +2122,17 @@ y estaciones basadas en sus gustos personales. TemporaryPageItem - + Copy Artist Link Copiar enlace del artista - + Copy Album Link Copiar enlace del álbum - + Copy Track Link Copiar enlace de la pista @@ -2608,13 +2608,13 @@ usuario@jabber.org Tomahawk::Collection - - + + Collection Colección - + This collection is empty. Esta colección está vacía. @@ -3496,7 +3496,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección @@ -3527,48 +3527,48 @@ introduzca su número PIN aquí: TomahawkTrayIcon - + &Stop Playback after current Track Detener la reproducción despué&s de la pista actual - - + + Hide Tomahawk Window Ocultar ventana de Tomahawk - + Show Tomahawk Window Mostrar ventana de Tomahawk - + Currently not playing. Ninguna pista en reproducción. - + Play Reproducir - + Pause Pausar - + &Love &Favorito - + Un-&Love Quitar de &favoritos - + &Continue Playback after current Track &Continuar la reproducción después de la pista actual @@ -3576,161 +3576,161 @@ introduzca su número PIN aquí: TomahawkWindow - + Tomahawk Tomahawk - + Back Atrás - + Go back one page Ir una página hacia atrás - + Forward Adelante - + Go forward one page Ir una página hacia adelante - - + + Hide Menu Bar Ocultar barra de menús - - + + Show Menu Bar Mostrar barra de menús - + Search for any artist, album or song... Buscar un artista, álbum o pista… - + &Main Menu &Menú principal - + Exit Full Screen Salir de pantalla completa - + Enter Full Screen Modo a pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. Esta no es una lista de reproducción XSPF válida. - + Failed to save tracks Fallo al guardar pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunas pistas en la lista de reproducción no contienen artista ni título. Serán ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Se ha producido un error al acceder al dispostivo de audio o a la pista deseada. Asegúrese de que ha instalado un backend de Phonon adecuado y los plugins necesarios. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Se ha producido un error al acceder al dispostivo de audio o a la pista deseado y se va saltar. - + Station Estación - + Create New Station Crear estación nueva - + Name: Nombre: - + Playlist Lista de reproducción - + Automatic Playlist Lista de reproducción automática - + Pause Pausar - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gracias a: - + About Tomahawk Acerca de Tomahawk @@ -4010,7 +4010,7 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en XMPPBot - + Terms for %1: @@ -4018,12 +4018,12 @@ Terms for %1: Términos de %1: - + No terms found, sorry. No se encuentran términos, lo siento. - + Hotttness for %1: %2 @@ -4032,7 +4032,7 @@ Actualidad de %1: %2 - + Familiarity for %1: %2 @@ -4041,7 +4041,7 @@ Familiaridad de %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 08734ca582..4d24be4224 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -56,18 +56,18 @@ yhdistää ja toistaa sinulta virtaa? AccountListWidget - + Connections Yhteydet - - + + Connect &All Yhdistä &kaikki - + Disconnect &All Katkaise &kaikki @@ -75,7 +75,7 @@ yhdistää ja toistaa sinulta virtaa? AccountWidget - + Invite Kutsu @@ -83,7 +83,7 @@ yhdistää ja toistaa sinulta virtaa? AccountsToolButton - + Configure Accounts Tilien asetukset @@ -341,37 +341,37 @@ yhdistää ja toistaa sinulta virtaa? AudioControls - + Shuffle Sekoita - + Repeat Toista - + Time Elapsed Kulunut aika - + Time Remaining Jäljellä oleva aika - + Playing from %1 Soitetaan lähteestä %1 - + Share Jaa - + Love Tykkää @@ -397,24 +397,24 @@ yhdistää ja toistaa sinulta virtaa? CategoryAddItem - + Create new Playlist Luo uusi soittolista - + Create new Station Luo uusi asema - - + + New Station Uusi asema - - + + %1 Station %1 – asema @@ -422,12 +422,12 @@ yhdistää ja toistaa sinulta virtaa? CategoryItem - + Playlists Soittolistat - + Stations Asemat @@ -457,53 +457,53 @@ yhdistää ja toistaa sinulta virtaa? CrashReporter - + Tomahawk Crash Reporter Tomahawkin kaatumisen ilmoitus - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Pahoittelut!</span> Tomahawk kaatui. Kerro siitä meille! Tomahawk on luonut puolestasi virheilmoituksen, joka auttaa vakauden parantamisessa jatkossa. Voit nyt lähettää tämän ilmoituksen suoraan Tomahawkin kehittäjille.</p></body></html> - + Send this report Lähetä tämä ilmoitus - + Don't send Älä lähetä - + Abort Keskeytä - + You can disable sending crash reports in the configuration dialog. Voit poistaa kaatumisilmoitusten lähettämisen käytöstä asetusikkunasta. - + Uploaded %L1 of %L2 KB. Lähetetty %L1/%L2 kB. - - + + Close Sulje - + Sent! <b>Many thanks</b>. Lähetetty! <b>Paljon kiitoksia</b>. - + Failed to send crash info. Kaatumistietojen lähettäminen epäonnistui. @@ -542,17 +542,17 @@ yhdistää ja toistaa sinulta virtaa? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk-diagnostiikka - + &Copy to Clipboard &Kopioi leikepöydälle - + Open &Log-file &Avaa lokitiedosto @@ -679,7 +679,7 @@ yhdistää ja toistaa sinulta virtaa? InboxItem - + Inbox Saapuneet @@ -775,27 +775,27 @@ yhdistää ja toistaa sinulta virtaa? LoadXSPF - + Load XSPF Lataa XSPF - + Playlist URL Soittolistan osoite - + Enter URL... Anna osoite... - + ... ... - + Automatically update Päivitä automaattisesti @@ -803,12 +803,12 @@ yhdistää ja toistaa sinulta virtaa? LoadXSPFDialog - + Load XSPF File Lataa XSPF-tiedosto - + XSPF Files (*.xspf) XSPF-tiedostot (*.xspf) @@ -839,32 +839,32 @@ yhdistää ja toistaa sinulta virtaa? LovedTracksItem - + Top Loved Tracks Tykätyimmät kappaleet - + Sorry, we could not find any loved tracks! Valitettavasti emme löytäneet yhtään tykättyä kappaletta! - + The most loved tracks from all your friends Kaikkien kaveriesi tykätyimmät kappaleet - + All of your loved tracks Kaikki tykkäämäsi kappaleet - + All of %1's loved tracks Kaikki käyttäjän %1 tykkäämät kappaleet - + Loved Tracks Tykätyt kappaleet @@ -1247,59 +1247,59 @@ yhdistää ja toistaa sinulta virtaa? ProxyDialog - + Proxy Settings Välityspalvelinasetukset - + Hostname of proxy server Välityspalvelimen konenimi - + Host Kone - + Port Portti - + Proxy login Välityspalvelimen tunnus - + User Käyttäjä - + Password Salasana - + Proxy password Välityspalvelimen salasana - + No Proxy Hosts: (Overrides system proxy) Välityspalvelittomat osoitteet: (ohittaa järjestelmän välityspalvelimen) - + localhost *.example.com (space separated) localhost *.esimerkki.fi (välilyönnein eroteltuna) - + Use proxy for DNS lookups? Käytetäänkö välityspalvelinta DNS-hauissa? @@ -1450,12 +1450,12 @@ yhdistää ja toistaa sinulta virtaa? ResolverConfigDelegate - + Not found: %1 Ei löydy: %1 - + Failed to load: %1 Lataaminen epäonnistui: %1 @@ -1515,77 +1515,77 @@ yhdistää ja toistaa sinulta virtaa? SettingsDialog - + Collection Kokoelma - + Advanced Lisäasetukset - + All Kaikki - + Some changed settings will not take effect until Tomahawk is restarted Jotkin asetusmuutokset tulevat voimaan vasta, kun Tomahawk käynnistetään uudelleen. - + Services Palvelut - + Install from file Asenna tiedostosta - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Aseta tilit ja palvelut, joita Tomahawk käyttää musiikin etsimiseen ja noutamiseen, kaveriesi etsimiseen sekä tilasi päivittämiseen. - + Manage how Tomahawk finds music on your computer. Hallitse Tomahawkin tapaa etsiä musiikkia tietokoneeltasi. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Aseta Tomahawkin lisäasetuksia, joihin kuuluu verkkoyhteysasetukset, selainyhteys ja muuta. - + Install resolver from file Asenna selvitin tiedostosta - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk-selvittimet (*.axe *.js);;Kaikki tiedostot (*) - + Resolver installation from file %1 failed. Selvittimen asennus tiedostosta %1 epäonnistui. - + Delete all Access Control entries? Poistetaanko kaikki pääsynvalvontatietueet? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Haluatko varmasti poistaa kaikki pääsynvalvontatietueet? Sinulta tullaan kysymään päätös uudelleen jokaisen sellaisen vertaisen kohdalla, johon yhdistät. - + Information Tiedoksi @@ -1593,7 +1593,7 @@ yhdistää ja toistaa sinulta virtaa? Settings_Accounts - + Filter by capability: Suodata kyvyn perusteella: @@ -1601,77 +1601,77 @@ yhdistää ja toistaa sinulta virtaa? Settings_Advanced - + Remote Peer Connection Method Etävertaisten yhteystapa - + None (outgoing connections only) Ei yhteyttä (vain lähtevät yhteydet) - + Use UPnP to establish port forward (recommended) Käytä UPnP:tä porttien uudelleenohjaukseen (suositellaan) - + Use static external IP address/host name and port Käytä kiinteää julkista IP-osoitetta/konenimeä ja porttia - + Set this to your external IP address or host name. Make sure to forward the port to this host! Aseta julkinen IP-osoitteesi tai konenimesi tähän. Varmista, että ohjaat portin tähän koneeseen! - + Static Host Name: Kiinteä konenimi: - + Static Port: Kiinteä portti: - + SOCKS Proxy SOCKS-välityspalvelin - + Use SOCKS Proxy Käytä SOCKS-välityspalvelinta - + Proxy Settings... Välityspalvelinasetukset... - + Other Settings Muut asetukset - + Allow web browsers to interact with Tomahawk (recommended) Salli webselainten olla yhteydessä Tomahawkiin (suositellaan) - + Send reports after Tomahawk crashed Lähetä ilmoituksia Tomahawkin kaatumisen jälkeen - + Show notification when a new song starts to play Näytä ilmoitus, kun uusi kappale alkaa - + Clear All Access Control Entries Tyhjennä kaikki pääsynvalvontatietueet @@ -1679,12 +1679,12 @@ yhdistää ja toistaa sinulta virtaa? Settings_Collection - + Path to scan for music files: Polku, josta musiikkitiedostoja etsitään: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1696,18 +1696,18 @@ Tämän valinnan käyttöönottaminen mahdollistaa sinun asemia sinun henkilökohtaisen musiikkimakuprofiilisi perusteella. - + Upload collection list to The Echo Nest to enable user radio Lähetä kokoelmalista The Echo Nestille käyttäjäradion käyttöönottamiseksi - + Watch for changes Tarkkaile muutoksia - + Time between scans, in seconds: Etsimisten aikaväli (sekunteina): @@ -1715,12 +1715,12 @@ käyttäjäradion käyttöönottamiseksi SlideSwitchButton - + On Päällä - + Off Pois @@ -1746,32 +1746,32 @@ käyttäjäradion käyttöönottamiseksi SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tviittaa - + Listening to "%1" by %2. %3 Kuuntelen ”%1” artistilta %2. %3 - + Listening to "%1" by %2 on "%3". %4 Kuuntelen ”%1” artistilta %2, albumilta ”%3”. %4 - + %1 characters left %1 merkkiä jäljellä @@ -1779,44 +1779,44 @@ käyttäjäradion käyttöönottamiseksi SourceDelegate - + Track Kappale - + Album Albumi - + Artist Artisti - + Local Paikallinen - + Top 10 Kymmenen kärki - + All available tracks Kaikki saatavilla olevat kappaleet - - + + Show Näytä - - + + Hide Piilota @@ -1857,53 +1857,53 @@ käyttäjäradion käyttöönottamiseksi SourceItem - - + + Latest Additions Viimeisimmät lisäykset - + Recently Played Viime aikoina kuunnellut - + SuperCollection Superkokoelma - + Latest additions to your collection Viimeisimmät lisäykset kokoelmaasi - + Latest additions to %1's collection Viimeisimmät lisäykset käyttäjän %1 kokoelmaan - + Sorry, we could not find any recent additions! Valitettavasti emme löytäneet yhtään viimeaikaisia lisäyksiä! - + Recently Played Tracks Viime aikoina kuunnellut kappaleet - + Your recently played tracks Viime aikoina kuuntelemasi kappaleet - + %1's recently played tracks Käyttäjän %1 viime aikoina kuuntelemat kappaleet - + Sorry, we could not find any recent plays! Valitettavasti emme löytäneet yhtään viimeaikaisia soittoja! @@ -1911,68 +1911,68 @@ käyttäjäradion käyttöönottamiseksi SourceTreeView - + &Copy Link &Kopioi linkki - + &Delete %1 &Poista %1 - + Add to my Playlists Lisää omiin soittolistoihin - + Add to my Automatic Playlists Lisää omiin automaattisiin soittolistoihin - + Add to my Stations Lisää omiin asemiin - + &Export Playlist &Vie soittolista - + playlist soittolistan - + automatic playlist automaattisen soittolistan - + station aseman - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Haluatko poistaa %1 <b>”%2”</b>? - + Delete Poista - + Save XSPF Tallenna XSPF - + Playlists (*.xspf) Soittolistat (*.xspf) @@ -1980,77 +1980,77 @@ käyttäjäradion käyttöönottamiseksi SourcesModel - + Group Ryhmä - + Collection Kokoelma - + Playlist soittolista - + Automatic Playlist automaattinen soittolista - + Station asema - + Browse Selaa - + Search History Hakuhistoria - + My Music Oma musiikki - + SuperCollection Superkokoelma - + Cloud Pilvi - + Dashboard Kojelauta - + Recently Played Viime aikoina kuunnellut - + Charts Listat - + New Releases Uudet julkaisut - + Friends Kaverit @@ -2125,17 +2125,17 @@ napsauttamalla hiiren oikealla. TemporaryPageItem - + Copy Artist Link Kopioi artistin linkki - + Copy Album Link Kopioi albumin linkki - + Copy Track Link Kopioi kappaleen linkki @@ -2611,13 +2611,13 @@ käyttäjätunnus@jabber.org Tomahawk::Collection - - + + Collection Kokoelma - + This collection is empty. Tämä kokoelma on tyhjä. @@ -3499,7 +3499,7 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< TomahawkApp - + My Collection Oma kokoelma @@ -3530,48 +3530,48 @@ anna siellä näytetty PIN-koodi tähän: TomahawkTrayIcon - + &Stop Playback after current Track Pysäytä s&oitto nykyisen kappaleen jälkeen - - + + Hide Tomahawk Window Piilota Tomahawk-ikkuna - + Show Tomahawk Window Näytä Tomahawk-ikkuna - + Currently not playing. Mikään ei soi. - + Play Soita - + Pause Tauko - + &Love &Tykkää - + Un-&Love Lakkaa &tykkäämästä - + &Continue Playback after current Track Jatka s&oittoa nykyisen kappaleen jälkeen @@ -3579,161 +3579,161 @@ anna siellä näytetty PIN-koodi tähän: TomahawkWindow - + Tomahawk Tomahawk - + Back Takaisin - + Go back one page Mene yksi sivu takaisin - + Forward Eteenpäin - + Go forward one page Mene yksi sivu eteenpäin - - + + Hide Menu Bar Piilota valikkorivi - - + + Show Menu Bar Näytä valikkorivi - + Search for any artist, album or song... Hae artistia, albumia tai kappaletta... - + &Main Menu &Päävalikko - + Exit Full Screen Poistu koko näytöstä - + Enter Full Screen Siirry koko näyttöön - + XSPF Error XSPF-virhe - + This is not a valid XSPF playlist. Tämä ei ole kelvollinen XSPF-soittolista. - + Failed to save tracks Kappaleiden tallentaminen epäonnistui - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Joillakin soittolistan kappaleilla ei ole artistia ja nimeä. Ne jätetään huomiotta. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. Varmista, että sopiva Phononin taustaosa ja vaaditut liitännäiset on asennettu. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. - + Station Asema - + Create New Station Luo uusi asema - + Name: Nimi: - + Playlist Soittolista - + Automatic Playlist Automaattinen soittolista - + Pause Tauko - + &Play &Soita - + %1 by %2 track, artist name %1 artistilta %2 - + %1 - %2 current track, some window title %1 – %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010–2013 - + Thanks to: Kiitokset: - + About Tomahawk Tietoa Tomahawkista @@ -4013,7 +4013,7 @@ Voit lähettää synkronointiviestin uudelleen millä hetkellä hyvänsä lähet XMPPBot - + Terms for %1: @@ -4021,12 +4021,12 @@ Terms for %1: Artistin %1 nimet: - + No terms found, sorry. Nimiä ei löydy. - + Hotttness for %1: %2 @@ -4034,7 +4034,7 @@ Hotttness for %1: %2 Artistin %1 suosittuus: %2 - + Familiarity for %1: %2 @@ -4042,7 +4042,7 @@ Familiarity for %1: %2 Artistin %1 tuttuus: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index aed5bb8632..66fcc353d5 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -56,18 +56,18 @@ de se connecter et streamer de vous? AccountListWidget - + Connections Connexions - - + + Connect &All &Connecter tout - + Disconnect &All &Déconnecter tout @@ -75,7 +75,7 @@ de se connecter et streamer de vous? AccountWidget - + Invite Inviter @@ -83,7 +83,7 @@ de se connecter et streamer de vous? AccountsToolButton - + Configure Accounts Configurer les comptes @@ -341,37 +341,37 @@ de se connecter et streamer de vous? AudioControls - + Shuffle Lecture Aléatoire - + Repeat Répéter - + Time Elapsed Durée Ecoulé - + Time Remaining Durée Restante - + Playing from %1 Lecture depuis %1 - + Share Partager - + Love Favori @@ -397,24 +397,24 @@ de se connecter et streamer de vous? CategoryAddItem - + Create new Playlist Créer une nouvelle liste de lecture - + Create new Station Créer une nouvelle station - - + + New Station Nouvelle Station - - + + %1 Station Station %1 @@ -422,12 +422,12 @@ de se connecter et streamer de vous? CategoryItem - + Playlists Listes de lecture - + Stations Stations @@ -457,53 +457,53 @@ de se connecter et streamer de vous? CrashReporter - + Tomahawk Crash Reporter Tomahawk Crash Reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Désolé !</span>&nbsp;Tomahawk a planté. Dites-nous ce qui ne va pas ! Tomahawk a créé un rapport d'erreur qui sera utile pour améliorer la stabilité des prochaines versions. Vous pouvez dès maintenant envoyer ce rapport aux développeurs de Tomahawk.</p></body></html> - + Send this report Envoyer le rapport - + Don't send Ne pas envoyer - + Abort Abandonner - + You can disable sending crash reports in the configuration dialog. Vous pouvez désactiver l'envoi des rapports de plantage dans la boite de dialogue de configuration. - + Uploaded %L1 of %L2 KB. Chargement %L1 de %L2 ko. - - + + Close Fermer - + Sent! <b>Many thanks</b>. Envoyé ! <b>Merci beaucoup</b>. - + Failed to send crash info. Échec de l'envoi des informations de plantage. @@ -542,17 +542,17 @@ de se connecter et streamer de vous? DiagnosticsDialog - + Tomahawk Diagnostics Diagnostics de Tomahawk - + &Copy to Clipboard &Copier dans le presse papier - + Open &Log-file Ouvrir le &fichier journal @@ -679,7 +679,7 @@ de se connecter et streamer de vous? InboxItem - + Inbox Boîte de réception @@ -775,27 +775,27 @@ de se connecter et streamer de vous? LoadXSPF - + Load XSPF Charger XSPF - + Playlist URL URL de la liste de lecture - + Enter URL... Saisir une URL... - + ... ... - + Automatically update Mise à jour automatique @@ -803,12 +803,12 @@ de se connecter et streamer de vous? LoadXSPFDialog - + Load XSPF File Charger un fichier XSPF - + XSPF Files (*.xspf) Fichiers XSPF (*.xspf) @@ -839,32 +839,32 @@ de se connecter et streamer de vous? LovedTracksItem - + Top Loved Tracks Top pistes favoris - + Sorry, we could not find any loved tracks! Désolé, nous n'avons pu trouver aucune piste favorite ! - + The most loved tracks from all your friends Les titres favoris de vos amis - + All of your loved tracks Tous vos titres favoris - + All of %1's loved tracks Tous les titres favoris de %1 - + Loved Tracks Titres favoris @@ -1247,59 +1247,59 @@ de se connecter et streamer de vous? ProxyDialog - + Proxy Settings Paramètres de proxy - + Hostname of proxy server Nom d'hôte du serveur proxy - + Host Hôte - + Port Port - + Proxy login Login du proxy - + User Utilisateur - + Password Mot de passe - + Proxy password Mot de passe du proxy - + No Proxy Hosts: (Overrides system proxy) Pas de proxy pour : (remplace les paramètres système) - + localhost *.example.com (space separated) localhost *.example.com (séparé par espace) - + Use proxy for DNS lookups? Utiliser le proxy pour les requêtes DNS ? @@ -1450,12 +1450,12 @@ de se connecter et streamer de vous? ResolverConfigDelegate - + Not found: %1 Non trouvé : %1 - + Failed to load: %1 Echec du chargement : %1 @@ -1515,77 +1515,77 @@ de se connecter et streamer de vous? SettingsDialog - + Collection Collection - + Advanced Avancés - + All Tous - + Some changed settings will not take effect until Tomahawk is restarted Certaines modifications ne prendront effet qu'au prochain démarrage de Tomahawk - + Services Services - + Install from file Installer depuis un fichier - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configurer les comptes et les services que Tomahawk utilisera pour chercher et diffuser de la musique, retrouver vos amis ou mettre à jour votre statut. - + Manage how Tomahawk finds music on your computer. Gérer la manière dont Tomahawk trouve de la musique sur votre ordinateur. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configurer les paramètres avancés de Tomahawk dont les paramètres de connectivité réseau, les interactions avec le navigateur et autres. - + Install resolver from file Installer un script de résolution depuis un fichier - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Supprimer toutes les entrées de Contrôle d'accès? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Voulez-vous vraiment supprimer toutes les entrées de contrôle d'accès ? On vous demandera de nouveau votre autorisation pour toutes les nouvelles connexions. - + Information Information @@ -1593,7 +1593,7 @@ de se connecter et streamer de vous? Settings_Accounts - + Filter by capability: Filtrer par fonctionnalité : @@ -1601,77 +1601,77 @@ de se connecter et streamer de vous? Settings_Advanced - + Remote Peer Connection Method Méthode de connexion distante à un pair - + None (outgoing connections only) Aucune (Connexions sortantes uniquement) - + Use UPnP to establish port forward (recommended) Utiliser UPnP pour établir la redirection de port (recommandé) - + Use static external IP address/host name and port Utiliser une adresse IP externe statique/nom d'hôte et un port - + Set this to your external IP address or host name. Make sure to forward the port to this host! Indiquez votre adresse IP externe ou nom d'hôte. Vérifiez que le port est bien redirigé vers cet hôte ! - + Static Host Name: Nom d'hôte statique : - + Static Port: Port statique : - + SOCKS Proxy Proxy SOCKS - + Use SOCKS Proxy Utiliser le proxy SOCKS - + Proxy Settings... Paramètres Proxy... - + Other Settings Autres paramètres - + Allow web browsers to interact with Tomahawk (recommended) Autoriser les navigateurs web à interagir avec Tomahawk (recommandé) - + Send reports after Tomahawk crashed Envoyer un rapport d'erreur si Tomahawk plante - + Show notification when a new song starts to play Afficher une notification quand une nouvelle piste commence - + Clear All Access Control Entries Supprimer toutes les entrées de contrôle d'accès @@ -1679,12 +1679,12 @@ de se connecter et streamer de vous? Settings_Collection - + Path to scan for music files: Chemin vers votre bibliothèque musicale: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1692,17 +1692,17 @@ de se connecter et streamer de vous? The Echo Nest peut garder les métadonnées de votre catalogue et les utiliser pour créer des radios personnalisées. En activant cette option vous (et vos amis) pourrez créer des listes de lecture automatiquement et des stations basées sur vos goûts. - + Upload collection list to The Echo Nest to enable user radio Envoyer la collection à The Echo Nest pour activer la radio utilisateur - + Watch for changes Surveiller les changements - + Time between scans, in seconds: Intervalle entre les scans, en secondes : @@ -1710,12 +1710,12 @@ de se connecter et streamer de vous? SlideSwitchButton - + On On - + Off Off @@ -1741,32 +1741,32 @@ de se connecter et streamer de vous? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 J'écoute "%1" par %2. %3 - + Listening to "%1" by %2 on "%3". %4 J'écoute "%1" par %2 sur "%3". %4 - + %1 characters left %1 caractères restants @@ -1774,44 +1774,44 @@ de se connecter et streamer de vous? SourceDelegate - + Track Piste - + Album Album - + Artist Artiste - + Local Local - + Top 10 Top 10 - + All available tracks Tous les titres disponibles - - + + Show Afficher - - + + Hide Masquer @@ -1852,53 +1852,53 @@ de se connecter et streamer de vous? SourceItem - - + + Latest Additions Derniers ajouts - + Recently Played Joués récemment - + SuperCollection SuperCollection - + Latest additions to your collection Derniers ajouts à votre collection - + Latest additions to %1's collection Derniers ajouts à la collection de %1 - + Sorry, we could not find any recent additions! Désolé, on a pas pu trouver des dernier ajouts! - + Recently Played Tracks Derniers titres joués - + Your recently played tracks Les derniers titres que vous avez joués - + %1's recently played tracks Derniers titres joués par %1 - + Sorry, we could not find any recent plays! Désolé, aucune piste récemment jouée n'a pu être trouvée ! @@ -1906,68 +1906,68 @@ de se connecter et streamer de vous? SourceTreeView - + &Copy Link &Copier le lien - + &Delete %1 &Supprimer %1 - + Add to my Playlists Ajoute a mes listes de lecture - + Add to my Automatic Playlists Ajoute a mes listes de lecture automatique - + Add to my Stations Ajouter à mes stations - + &Export Playlist &Exporter la liste de lecture - + playlist liste de lecture - + automatic playlist liste de lecture automatique - + station station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Voulez-vous supprimer la %1 <b>"%2"</b>? - + Delete Supprimer - + Save XSPF Enregistrer XSPF - + Playlists (*.xspf) Listes de lecture (*.xspf) @@ -1975,77 +1975,77 @@ de se connecter et streamer de vous? SourcesModel - + Group Groupe - + Collection Collection - + Playlist Liste de lecture - + Automatic Playlist Liste de lecture automatique - + Station Station - + Browse Parcourir - + Search History Chercher dans l'historique - + My Music Ma Musique - + SuperCollection SuperCollection - + Cloud Cloud - + Dashboard Tableau de bord - + Recently Played Joués récemment - + Charts Charts - + New Releases Nouveautés - + Friends Amis @@ -2119,17 +2119,17 @@ de se connecter et streamer de vous? TemporaryPageItem - + Copy Artist Link Copier le lien de l'artiste - + Copy Album Link Copier le lien de l'album - + Copy Track Link Copier le lien de la piste @@ -2605,13 +2605,13 @@ utilisateur@jabber.org Tomahawk::Collection - - + + Collection Collection - + This collection is empty. Cette collection est vide. @@ -3493,7 +3493,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection @@ -3524,48 +3524,48 @@ saisissez le numéro PIN ici : TomahawkTrayIcon - + &Stop Playback after current Track &Stopper la lecture après cette piste - - + + Hide Tomahawk Window Masquer la fenêtre Tomahawk - + Show Tomahawk Window Afficher la fenêtre Tomahawk - + Currently not playing. Pas de lecture en cours. - + Play Lecture - + Pause Pause - + &Love &Favori - + Un-&Love &Supprimer des Favoris - + &Continue Playback after current Track &Continuer la lecture après cette piste @@ -3573,161 +3573,161 @@ saisissez le numéro PIN ici : TomahawkWindow - + Tomahawk Tomahawk - + Back Retour - + Go back one page Reculer d'une page - + Forward Avancer - + Go forward one page Avancer d'une page - - + + Hide Menu Bar Masquer la barre de menu - - + + Show Menu Bar Afficher la barre de menu - + Search for any artist, album or song... Chercher un artiste, un album, ou un morceau... - + &Main Menu &Menu Principal - + Exit Full Screen Quitter le mode plein écran - + Enter Full Screen Activer le mode plein écran - + XSPF Error Erreur XSPF - + This is not a valid XSPF playlist. Ceci n'est pas une liste de lecture XSPF valide. - + Failed to save tracks Échec de la sauvegarde des pistes - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Certaines pistes dans la liste de lecture ne contiennent pas d'artiste ou de titre. Elles seront ignorées. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sautée. Vérifiez que vous avez un backend Phonon et les plugins requis installés. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours , celle-ci va être sautée. - + Station Station - + Create New Station Créer une nouvelle station - + Name: Nom : - + Playlist Liste de lecture - + Automatic Playlist Liste de lecture automatique - + Pause Pause - + &Play &Lire - + %1 by %2 track, artist name %1 par %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Droit d'auteur 2010 - 2013 - + Thanks to: Merci a: - + About Tomahawk A propos de Tomahawk @@ -4007,7 +4007,7 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env XMPPBot - + Terms for %1: @@ -4016,12 +4016,12 @@ Résultats pour %1 : - + No terms found, sorry. Aucun terme trouvé, désolé. - + Hotttness for %1: %2 @@ -4030,7 +4030,7 @@ Hotttness pour %1 : %2 - + Familiarity for %1: %2 @@ -4039,7 +4039,7 @@ Familiarité pour %1 : %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 7c33fcd68f..118bb6b05a 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections Conexións - - + + Connect &All Conectar &todo - + Disconnect &All Desconectar de &todos @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite Convidar @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Configurar as contas @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Ao chou - + Repeat Repetir - + Time Elapsed Tempo transcorrido - + Time Remaining Tempo que falta - + Playing from %1 Reproducindo de %1 - + Share Compartir - + Love gusta @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Crear unha lista de temas nova - + Create new Station Crear unha nova emisora - - + + New Station Nova emisora - - + + %1 Station Emisora %1 @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Listas de reprodución - + Stations Emisoras @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Servizo de informes de fallos de Tomahawk - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Descoida,</span> Tomahawk fallou. Infórmanos de como foi. Tomahawk creou un informe de erro para que nos poidas axudar a mellorar a estabilidade do aplicativo no futuro. Podes enviar este informe directamente aos desenvolvedores de Tomahawk.</p></body></html> - + Send this report Enviar este informe - + Don't send Non enviar - + Abort Abortar - + You can disable sending crash reports in the configuration dialog. Podes desactivar o envío de informes de fallos no diálogo de configuración. - + Uploaded %L1 of %L2 KB. Cargáronse %L1 de %L2 kB. - - + + Close Pechar - + Sent! <b>Many thanks</b>. Enviado! <b>Moitas grazas</b>. - + Failed to send crash info. Fallou o envío do informe do erro. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Diagnóstico de Tomahawk - + &Copy to Clipboard &Copiar ao portarretallos - + Open &Log-file Abrir o ficheiro de &rexistro @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Cargar XSPF - + Playlist URL URL da lista de reprodución - + Enter URL... Introduza o URL... - + ... ... - + Automatically update Actualizar automaticamente @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Cargar o ficheiro XSPF - + XSPF Files (*.xspf) Ficheiros XSPF (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks As pistas que máis me gustan - + Sorry, we could not find any loved tracks! Non se atopan pistas que che gustaran! - + The most loved tracks from all your friends As pistas que máis lle gustaron aos teus amigos - + All of your loved tracks Todas as pistas que che gustaron - + All of %1's loved tracks Todas as pistas de %1 que che gustaron - + Loved Tracks Pistas favoritas @@ -1246,59 +1246,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings Configuración do proxy - + Hostname of proxy server Nome do host do servidor proxy - + Host Servidor - + Port Porto - + Proxy login Iniciar sesión no proxy - + User Usuario - + Password Contrasinal - + Proxy password Contrasinal proxy - + No Proxy Hosts: (Overrides system proxy) Sen host de proxy: (Sobrescribe o sistema de proxy) - + localhost *.example.com (space separated) localhost *.exemplo.net (separado por espazos) - + Use proxy for DNS lookups? Usar o proxy para a resolución de DNS? @@ -1449,12 +1449,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 Non se atopou: %1 - + Failed to load: %1 Fallou a carga de: %1 @@ -1514,77 +1514,77 @@ connect and stream from you? SettingsDialog - + Collection Colección - + Advanced Avanzado - + All Todo - + Some changed settings will not take effect until Tomahawk is restarted Algunhas das configuracións que se cambiaron non van a ter efecto ata que se reinicie Tomahawk - + Services Servizos - + Install from file Instalar dende ficheiro - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configurar as contas e servizos que emprega Tomahawk para buscar e obter música, atopar ás túas amizades e actualizar o teu estado. - + Manage how Tomahawk finds music on your computer. Xestionar como Tomahawk busca música no teu computador. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configurar as propiedades avanzadas de Tomahawk, incluíndo a conectividade a rede, a interacción co navegador e outras. - + Install resolver from file Instalar un resolvedor dende un ficheiro - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Queres borrar tódalas entradas de control de acceso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Seguro que queres borrar tódalas entradas de control de acceso? Preguntaráseche de novo para que o confirmes cada vez que te conectes a un parceiro. - + Information Información @@ -1592,7 +1592,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: Filtrar por capacidade: @@ -1600,77 +1600,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method Método de conexión remota por parceiros - + None (outgoing connections only) Ningún (só conexións saíntes) - + Use UPnP to establish port forward (recommended) Empregar UPnP para definir o porto de saída (recomendado) - + Use static external IP address/host name and port Empregar a dirección IP externa estática ou o nome de servidor e porto - + Set this to your external IP address or host name. Make sure to forward the port to this host! Define isto para a túa dirección IP externa ou nome de servidor. Asegúrate de que remitir o porto a este servidor! - + Static Host Name: Nome de host estático: - + Static Port: Porto estático: - + SOCKS Proxy Proxy de SOCKS - + Use SOCKS Proxy Usar SOCKs de proxy - + Proxy Settings... Configuracións do proxy... - + Other Settings Outras configuracións - + Allow web browsers to interact with Tomahawk (recommended) Permitirlle aos navegadores we interactuar con Tomahawk (recoméndase) - + Send reports after Tomahawk crashed Enviar informes cando falle Tomahawk - + Show notification when a new song starts to play Mostrar notificacións cando se comece a reproducir unha nova canción - + Clear All Access Control Entries Limpar tódalas entradas de control de acceso @@ -1678,12 +1678,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: Ruta para buscar ficheiros de música: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1691,17 +1691,17 @@ connect and stream from you? O Echo Nest soporta estar ao tanto do teu catálogo de metadatos e empregalo para artellar radios personalizadas. Activando esta opción podes (e tódolos teus amigos) crear listas de reprodución automática e emisoras baseándose nos perfís de gustos persoais. - + Upload collection list to The Echo Nest to enable user radio Subir a lista da colección ao Echo Nesta para activar a radio de usuarios - + Watch for changes Ollar os cambios - + Time between scans, in seconds: Tempo entre as buscas, en segundos: @@ -1709,12 +1709,12 @@ connect and stream from you? SlideSwitchButton - + On Activado - + Off Apagado @@ -1740,32 +1740,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Chío - + Listening to "%1" by %2. %3 Escoitando a «%1» por %2. %3 - + Listening to "%1" by %2 on "%3". %4 Escoitando a «%1» por %2 en «%3». %4 - + %1 characters left quedan %1 caracteres @@ -1773,44 +1773,44 @@ connect and stream from you? SourceDelegate - + Track Pista - + Album Álbum - + Artist Artista - + Local Local - + Top 10 Os 10 primeiros - + All available tracks Tódalas pistas dispoñíbeis - - + + Show Mostrar - - + + Hide Agochar @@ -1851,53 +1851,53 @@ connect and stream from you? SourceItem - - + + Latest Additions Últimos engadidos - + Recently Played Escoitados recentemente - + SuperCollection Supercolección - + Latest additions to your collection Os últimos engadidos á túa colección - + Latest additions to %1's collection Os últimos engadidos a colección %1 - + Sorry, we could not find any recent additions! Non se atoparon novas pistas engadidas recentemente! - + Recently Played Tracks Pistas recentemente reproducidas - + Your recently played tracks As pistas que soaron recentemente - + %1's recently played tracks As pistas de %1 que soaron recentemente - + Sorry, we could not find any recent plays! Non se escoitaron pistas recentemente! @@ -1905,69 +1905,69 @@ connect and stream from you? SourceTreeView - + &Copy Link Copiar a &ligazón - + &Delete %1 &Borrar %1 - + Add to my Playlists Engadir ás miñas listas de reprodución - + Add to my Automatic Playlists Engadir ás miñas listas reprodución automática - + Add to my Stations Engadir á miña emisora - + &Export Playlist &Exportar a lista de reprodución - + playlist Lista de reprodución - + automatic playlist lista de reprodución automática - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Queres eliminar a %1 que se chama <b>«%2»</b>? - + Delete Borrar - + Save XSPF Gardar XSPF - + Playlists (*.xspf) Listas de reprodución (*.xspf) @@ -1975,78 +1975,78 @@ connect and stream from you? SourcesModel - + Group Grupo - + Collection Colección - + Playlist Lista de reprodución - + Automatic Playlist Listas de reprodución automáticas - + Station - + Browse Examinar - + Search History Buscar no historial - + My Music A miña música - + SuperCollection Supercolección - + Cloud Nube - + Dashboard Taboleiro - + Recently Played Escoitadas recentemente - + Charts Gráficos - + New Releases Novos lanzamentos - + Friends Amizades @@ -2120,17 +2120,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link Copiar a ligazón do artista - + Copy Album Link Copiar a ligazón do álbum - + Copy Track Link Copiar a ligazón da pista @@ -2607,13 +2607,13 @@ nomedeusuario@jabber.org Tomahawk::Collection - - + + Collection Colección - + This collection is empty. Esta colección está baleira. @@ -3495,7 +3495,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección @@ -3525,48 +3525,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track &Parar a reprodución despois da pista - - + + Hide Tomahawk Window Agochar a xanela de Tomahawk - + Show Tomahawk Window Mostrar a xanela de Tomahawk - + Currently not playing. Agora mesmo non se está a escoitar nada. - + Play Reproducir - + Pause Pausa - + &Love &Gústame - + Un-&Love Xa non me &gusta - + &Continue Playback after current Track &Continuar a reprodución despois da pista @@ -3574,162 +3574,162 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back Atrás - + Go back one page Ir unha páxina atrás - + Forward Adiante - + Go forward one page Ir unha páxina adiante - - + + Hide Menu Bar Agochar a barra de menú - - + + Show Menu Bar Mostrar a barra de menú - + Search for any artist, album or song... Buscar a calquera artista, álbum ou canción... - + &Main Menu Menú &principal - + Exit Full Screen Saír da pantalla ao completo - + Enter Full Screen Entrar na pantalla ao completo - + XSPF Error Erro XSPF - + This is not a valid XSPF playlist. Esta non é unha lista de XSPF válida. - + Failed to save tracks Fallou o gardado de pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunhas pistas na lista de reprodución non indican nin artista nin o título. Ignoraranse. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. Asegúrate de ter o motor Phonon e os engadidos necesarios instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. - + Station - + Create New Station Crear unha nova emisión - + Name: Nome: - + Playlist Lista de reprodución - + Automatic Playlist Lista de reprodución automática - + Pause Pausa - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1.- %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecementos: - + About Tomahawk Acerca de Tomahawk @@ -4009,7 +4009,7 @@ Podes reenviar e sincronizar as mensaxes en calquera momento simplemente enviand XMPPBot - + Terms for %1: @@ -4017,12 +4017,12 @@ Terms for %1: Termos para %1: - + No terms found, sorry. Non se atoparon termos. - + Hotttness for %1: %2 @@ -4030,14 +4030,14 @@ Hotttness for %1: %2 Popularidade de %1: %2 - + Familiarity for %1: %2 Familiaridade con %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 31809ef9a3..b2ed6971f9 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle - + Repeat - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist - + Create new Station - - + + New Station - - + + %1 Station @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists - + Stations @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report - + Don't send - + Abort - + You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. - - + + Close बंद करो - + Sent! <b>Many thanks</b>. - + Failed to send crash info. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics - + &Copy to Clipboard - + Open &Log-file @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF - + Playlist URL - + Enter URL... - + ... ... - + Automatically update @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File - + XSPF Files (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings - + Hostname of proxy server - + Host - + Port - + Proxy login - + User उपभोक्ता - + Password - + Proxy password - + No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) - + Use proxy for DNS lookups? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 - + Failed to load: %1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection संग्रह - + Advanced - + All सभी - + Some changed settings will not take effect until Tomahawk is restarted - + Services सेवाएं - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information सूचना @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook - + Twitter - + Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 - + %1 characters left @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track - + Album - + Artist कलाकार - + Local - + Top 10 - + All available tracks - - + + Show दिखाओ - - + + Hide छुपाओ @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - + Recently Played Tracks - + Your recently played tracks - + %1's recently played tracks - + Sorry, we could not find any recent plays! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group - + Collection - + Playlist गीतसूची - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends मित्रगण @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link - + Copy Album Link - + Copy Track Link @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. - + Play - + Pause - + &Love - + Un-&Love - + &Continue Playback after current Track @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index b1b248e0c4..1942fd8807 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections Kapcsolatok - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle - + Repeat Ismétlés - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Új lejátszólista létrehozása - + Create new Station Új rádióállomás létrehozása - - + + New Station Új rádióállomás - - + + %1 Station @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Lejátszólisták - + Stations Rádióállomások @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk Crash Reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report - + Don't send - + Abort - + You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. - - + + Close Bezárás - + Sent! <b>Many thanks</b>. - + Failed to send crash info. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk diagnosztizálás - + &Copy to Clipboard Másolás a vágólapra - + Open &Log-file @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF XSPF betöltése - + Playlist URL Lejátszólista URL - + Enter URL... URL beírása... - + ... ... - + Automatically update Automatikus frissítés @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File XSPF fájl betöltése - + XSPF Files (*.xspf) XSPF fájlok (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings Proxy beállítások - + Hostname of proxy server Proxy szerver hosztneve - + Host Hosztnév - + Port Port - + Proxy login Proxy bejelentkezés - + User Felhasználó - + Password Jelszó - + Proxy password Proxy jelszó - + No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) localhost *.example.com (szóközzel elválasztva) - + Use proxy for DNS lookups? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 - + Failed to load: %1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection - + Advanced - + All Mind - + Some changed settings will not take effect until Tomahawk is restarted - + Services Szolgáltatások - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Információ @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: Statikus hosztnév: - + Static Port: Statikus port: - + SOCKS Proxy SOCKS Proxy - + Use SOCKS Proxy SOCKS proxy használata - + Proxy Settings... Proxy beállítások... - + Other Settings Egyéb beállítások - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play Értesítések mutatása ha egy új zene elindul - + Clear All Access Control Entries @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes Változások figyelése - + Time between scans, in seconds: Szkennelések közötti idő, másodpercben: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook - + Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 - + %1 characters left @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track Zeneszám - + Album Album - + Artist Előadó - + Local Helyi - + Top 10 Top 10 - + All available tracks Összes elérhető zeneszám - - + + Show Mutatás - - + + Hide Elrejtés @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions - + Recently Played Mostanában játszott - + SuperCollection Szuper kollekció - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - + Recently Played Tracks Monstanában játszott zeneszámok - + Your recently played tracks - + %1's recently played tracks - + Sorry, we could not find any recent plays! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist lejátszólista - + automatic playlist automatikus lejátszólista - + station rádióállomás - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete Törlés - + Save XSPF XSPF mentése - + Playlists (*.xspf) Lejátszólisták (*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group Csoport - + Collection Kollekció - + Playlist Lejátszólista - + Automatic Playlist Automatikus lejátszólista - + Station Rádióállomás - + Browse Böngészés - + Search History Keresési előzmények - + My Music Zenéim - + SuperCollection Szuper kollekció - + Cloud - + Dashboard - + Recently Played Monstanában játszott - + Charts - + New Releases Új kiadások - + Friends Barátok @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link Előadó linkjének másolása - + Copy Album Link Album linkjének másolása - + Copy Track Link Zeneszám linkjének másolása @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window Tomahawk ablak elrejtése - + Show Tomahawk Window Tomahawk ablak megjelenítése - + Currently not playing. - + Play Lejátszás - + Pause Megállítás - + &Love - + Un-&Love - + &Continue Playback after current Track @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF hiba - + This is not a valid XSPF playlist. Nem érvényes XSPF lejátszólista. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Rádióállomás - + Create New Station - + Name: - + Playlist Lejátszólista - + Automatic Playlist Automatikus lejátszólista - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk Tomahawkról @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index c0c562fa5b..adbba263ff 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle - + Repeat - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist - + Create new Station - - + + New Station - - + + %1 Station @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists - + Stations @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report - + Don't send - + Abort - + You can disable sending crash reports in the configuration dialog. - + Uploaded %L1 of %L2 KB. - - + + Close - + Sent! <b>Many thanks</b>. - + Failed to send crash info. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics - + &Copy to Clipboard - + Open &Log-file @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF - + Playlist URL - + Enter URL... - + ... - + Automatically update @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File - + XSPF Files (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings - + Hostname of proxy server - + Host - + Port - + Proxy login - + User - + Password - + Proxy password - + No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) - + Use proxy for DNS lookups? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 - + Failed to load: %1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection - + Advanced - + All - + Some changed settings will not take effect until Tomahawk is restarted - + Services - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook - + Twitter - + Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 - + %1 characters left @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track - + Album - + Artist - + Local - + Top 10 - + All available tracks - - + + Show - - + + Hide @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - + Recently Played Tracks - + Your recently played tracks - + %1's recently played tracks - + Sorry, we could not find any recent plays! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group - + Collection - + Playlist - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link - + Copy Album Link - + Copy Track Link @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. - + Play - + Pause - + &Love - + Un-&Love - + &Continue Playback after current Track @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index b92ab26dfd..039f634ed8 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections Connessioni - - + + Connect &All Connetti &tutto - + Disconnect &All Disconnetti &tutto @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite Invita @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Configura gli account @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Riproduzione casuale - + Repeat Ripeti - + Time Elapsed Tempo trascorso - + Time Remaining Tempo rimanente - + Playing from %1 Riproducendo da %1 - + Share Condividi - + Love Preferito @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Crea una playlist nuova - + Create new Station Crea una nuova stazione - - + + New Station Nuova stazione - - + + %1 Station %1 stazione @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Playlist - + Stations Stazioni @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk crash reporter - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Spiacenti!</span> Tomahawk si è bloccato. Per favore segnalaci come! Tomahawk ha creato un rapporto che può aiutarci a migliorare la stabilità del programma in futuro. Puoi spedire ora questo rapporto agli sviluppatori di Tomahawk.</p></body></html> - + Send this report Invia la segnalazione - + Don't send Non inviare - + Abort Annulla - + You can disable sending crash reports in the configuration dialog. Puoi disabilitare l'invio di crash report nella configurazione - + Uploaded %L1 of %L2 KB. Caricato %L1 di %L2 KB. - - + + Close Chiudi - + Sent! <b>Many thanks</b>. Spedito! <b> Grazie! </b>. - + Failed to send crash info. Crash report non inviato. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Diagnostica Tomahawk - + &Copy to Clipboard &copia nella clipboard - + Open &Log-file Apri il &file-di-log @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Carica XSPF - + Playlist URL Url della playlist - + Enter URL... Inserisci URL... - + ... ... - + Automatically update Aggiorna automaticamente @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Carica file XSPF - + XSPF Files (*.xspf) XSPF Files (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks Top tracce preferite - + Sorry, we could not find any loved tracks! Spiacente, non siamo riusciti a trovare nessuna traccia amata - + The most loved tracks from all your friends Le migliori tracce preferite dai tuoi amici - + All of your loved tracks Tutte le tue tracce preferite - + All of %1's loved tracks Tutte le tracce preferite di %1 - + Loved Tracks Tracce preferite @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings Impostazioni proxy - + Hostname of proxy server Nome del proxy server - + Host Host - + Port Porta - + Proxy login Login proxy - + User Utente - + Password Password - + Proxy password Password proxy - + No Proxy Hosts: (Overrides system proxy) No collegamento proxy:⏎ (annulla impostazione di sistema) - + localhost *.example.com (space separated) localhost *.esempio.it (separata da uno spazio) - + Use proxy for DNS lookups? Utilizza il proxy come DNS? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 Non trovati: %1 - + Failed to load: %1 Impossibili da caricare: %1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection Collezione - + Advanced Avanzate - + All Tutte - + Some changed settings will not take effect until Tomahawk is restarted Alcune modifiche non avranno effetto fino al riavvio di Tomahawk - + Services Servizi - + Install from file Installa da file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configura gli account e servizi utilizzati da Tomahawk per cercare e riprodurre musica, trovare i tuoi amici e aggiornare il tuo stato. - + Manage how Tomahawk finds music on your computer. Configura come Tomahawk trova la musica sul tuo computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configura le impostazioni avanzate di Tomahawk, compresa la connessione ad internet, interazione col browser ed altro ancora. - + Install resolver from file Installa i resolver da file - + Tomahawk Resolvers (*.axe *.js);;All files (*) Risolutore Tomahawk (*.axe *.js);;Tutti i file (*) - + Resolver installation from file %1 failed. L'installazione del risolutore dal file %1 è fallita. - + Delete all Access Control entries? Cancellare tutte le voci di controllo di accesso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Vuoi proprio cancellare tutte le voci di controllo accesso? Ti sarà richiesto di decidere ancora tutte le volte che un peer si connetterà con te. - + Information Informazione @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: Filtra per capacità: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method Metodo per connessione remota ai peer - + None (outgoing connections only) Nessuno (connessioni solo in uscita) - + Use UPnP to establish port forward (recommended) Utilizza UPnp per stabilire un port foward (consigliato) - + Use static external IP address/host name and port Utilizza un indirizzo IP esterno statico - + Set this to your external IP address or host name. Make sure to forward the port to this host! Imposta questo sul tuo indirizzo IP o nome host esterno. Controlla che la porta sia aperta per questo host! - + Static Host Name: Nome host statico: - + Static Port: Porta statica: - + SOCKS Proxy SOCKS Proxy - + Use SOCKS Proxy Utilizza un Proxy SOCKS - + Proxy Settings... Impostazioni Proxy... - + Other Settings Altre impostazioni - + Allow web browsers to interact with Tomahawk (recommended) Permetti ai browser di interagire con Tomahawk (raccomandato) - + Send reports after Tomahawk crashed Manda una segnalazione quando Tomahawk va in crash - + Show notification when a new song starts to play Mostra notifica quando una nuova traccia viene riprodotta - + Clear All Access Control Entries Cancella tutte le voci di controllo accesso @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: Percorso da scansionare per file musicali: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? Echo Nest tiene conto dei metadata del tuo catalogo digitale⏎ utilizzandoli per creare radio personalizzate. Abilitando questa opzione⏎ permetterà te (e tutti i tuoi amici) di creare playlist automatiche⏎ e stazioni basate sui vostri gusti musicali. - + Upload collection list to The Echo Nest to enable user radio Carica la lista della tua collezione su Echo Nest per abilitare la radio personalizzata. - + Watch for changes Controlla per cambiamenti - + Time between scans, in seconds: Tempo tra scansioni, in secondi: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On Acceso - + Off Spento @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweetta - + Listening to "%1" by %2. %3 Ascoltando "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 Ascoltando "%1" di %2. %3".%4 - + %1 characters left %1 caratteri rimanenti @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track Traccia - + Album Album - + Artist Artista - + Local Locale - + Top 10 Top 10 - + All available tracks Tutte le tracce disponibili - - + + Show Mostra - - + + Hide Nascondi @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions Ultime aggiunte - + Recently Played Ascoltate di recente - + SuperCollection Supercollezione - + Latest additions to your collection Ultime aggiunte alla tua collezione - + Latest additions to %1's collection Ultime aggiunte alla collezione di %1 - + Sorry, we could not find any recent additions! Spiacente, non abbiamo trovato nessuna aggiunta recente! - + Recently Played Tracks Tracce ascoltate di recente - + Your recently played tracks Tracce da te ascoltate di recente - + %1's recently played tracks Tracce ascoltate di recente da %1 - + Sorry, we could not find any recent plays! Spiacente, non è stato possibile trovare ascolti recenti! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link &Copia link - + &Delete %1 &Cancella %1 - + Add to my Playlists Aggiungi alle mie playlist - + Add to my Automatic Playlists Aggiungi alle mie playlist automatiche - + Add to my Stations Aggiungi alle mie stazioni - + &Export Playlist &Esporta playlist - + playlist playlist - + automatic playlist playlist automatica - + station stazione - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Vuoi cancellare %1 <b>"%2"</b>? - + Delete Cancella - + Save XSPF Salva XSPF - + Playlists (*.xspf) Playlist (*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group Gruppo - + Collection Collezione - + Playlist Playlist - + Automatic Playlist Playlist automatica - + Station Stazione - + Browse Esplora - + Search History Cronologia di ricerca - + My Music La mia musica - + SuperCollection Supercollezione - + Cloud Cloud - + Dashboard Cruscotto - + Recently Played Ascoltate recentemente - + Charts Classifiche - + New Releases Nuove uscite - + Friends Amici @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link Copia link artista - + Copy Album Link Copia link album - + Copy Track Link Copia link traccia @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Collezione - + This collection is empty. La collezione è vuota. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track &Ferma riproduzione dopo questa traccia - - + + Hide Tomahawk Window Nascondi Tomahawk - + Show Tomahawk Window Mostra Tomahawk - + Currently not playing. Al momento non in riproduzione. - + Play Play - + Pause Pausa - + &Love &Love - + Un-&Love Un-&Love - + &Continue Playback after current Track &Continua riproduzione dopo questa traccia @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back Indietro - + Go back one page Vai indietro di una pagina - + Forward Avanti - + Go forward one page Vai avanti di una pagina - - + + Hide Menu Bar Nascondi barra menu - - + + Show Menu Bar Mostra barra menu - + Search for any artist, album or song... Cerca qualunque artista, album o canzone... - + &Main Menu &Menu principale - + Exit Full Screen Esci da schermo intero - + Enter Full Screen Modalità schermo intero - + XSPF Error Errore XSPF - + This is not a valid XSPF playlist. Questa non è una valida playlist XSPF. - + Failed to save tracks Errore nel salvare le tracce - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Alcune tracce nella playlist non contengono l'artista e il titolo. Verrano ignorate. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. Assicurati di avere le giuste librerie Phonon e i plugin necessari installati. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. - + Station Stazione - + Create New Station Crea una nuova stazione - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist automatica - + Pause Pausa - + &Play Ri&produci - + %1 by %2 track, artist name %1 di %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Grazie a: - + About Tomahawk Info su Tomahawk @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: ⏎ Condizioni per %1:⏎ - + No terms found, sorry. Nessuna condizione trovata, spiacente. - + Hotttness for %1: %2 ⏎ Popolarità per %1: %2⏎ - + Familiarity for %1: %2 ⏎ Familiarità per %1: %2⏎ - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 80d0aabb60..6bfe2543a1 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections コネクション - - + + Connect &All 全部を接続する - + Disconnect &All 全ての接続を切る @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite 招待 @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts アカウントを設定 @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle シャッフル - + Repeat リピート - + Time Elapsed 再生時間 - + Time Remaining 残り時間 - + Playing from %1 %1から再生中 - + Share シェアー - + Love Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist 新規プレイリストを作成 - + Create new Station 新規ステーションを作成 - - + + New Station 新規ステーション - - + + %1 Station %1 ステーション @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists プレイリスト - + Stations ステーション @@ -456,54 +456,54 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawkの問題のレポート - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p>Tomahawkがクラッシュしました。その情報をTomahawkに送信してください。安定した機能に改善するためにエラーレポートを用意しています。このレポートはTomahawkの開発者に直接送られます。</p></body></html> - + Send this report レポートを送信 - + Don't send 送信しない - + Abort 中止する - + You can disable sending crash reports in the configuration dialog. 設定ダイアログでクラッシュレポートの送信を無効にすることができます。 - + Uploaded %L1 of %L2 KB. %L1/%L2 KB アップロード完了 - - + + Close 閉じる - + Sent! <b>Many thanks</b>. 送信完了! <b>ありがとうございます!</b> - + Failed to send crash info. クラッシュ情報の送信に失敗しました。 @@ -542,17 +542,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawkの診断 - + &Copy to Clipboard クリップボードにコピー - + Open &Log-file ログファイルを開く @@ -679,7 +679,7 @@ connect and stream from you? InboxItem - + Inbox @@ -775,27 +775,27 @@ connect and stream from you? LoadXSPF - + Load XSPF XSPFを読み込み - + Playlist URL プレイリストURL - + Enter URL... URLを入力... - + ... ... - + Automatically update 自動更新する @@ -803,12 +803,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File XSPFファイルを読み込む - + XSPF Files (*.xspf) XSPFファイル (*.xspf) @@ -839,32 +839,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks 最もLove トラック - + Sorry, we could not find any loved tracks! Love トラックが見つかりませんでした。 - + The most loved tracks from all your friends 友達の最もLove トラック - + All of your loved tracks 自分のLove トラック - + All of %1's loved tracks %1さんのLove トラック - + Loved Tracks Love トラック @@ -1247,59 +1247,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings プロクシ設定 - + Hostname of proxy server プロキシサーバのホスト - + Host ホスト - + Port ポート - + Proxy login プロキシのログインネーム - + User ユーザー - + Password パスワード - + Proxy password プロキシのパスワード - + No Proxy Hosts: (Overrides system proxy) 優先プロキシ: (システムプロキシより優先されます) - + localhost *.example.com (space separated) localhost *.example.com (スペースで区切られます) - + Use proxy for DNS lookups? 正引きDNSにプロキシを使用しますか? @@ -1450,12 +1450,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 見つかりません: %1 - + Failed to load: %1 ロードに失敗しました: %1 @@ -1515,77 +1515,77 @@ connect and stream from you? SettingsDialog - + Collection コレクション - + Advanced 詳細 - + All すべて - + Some changed settings will not take effect until Tomahawk is restarted Tomahawkを再起動すると設定変更が反映されます - + Services サービス - + Install from file ファイルからインストールする - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. 友達を探すこと、又はステータスの更新や音楽の検索と取得の為に利用するアカウントとサービスを設定。 - + Manage how Tomahawk finds music on your computer. 音楽ファイルの検索方法を設定 - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. ネットワーク接続設定、又はブラウザー設定等の詳細設定を調整する。 - + Install resolver from file ファイルからリゾルバをインストールする - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? 全てのアクセス制御のエントリーを削除しますか? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. 本当に全てのアクセス制御のエントリーを削除しますか?ピア接続に対して、改めて同意を求めます。 - + Information 情報 @@ -1593,7 +1593,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: 機能別に分類 @@ -1601,77 +1601,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method ピア接続方法 - + None (outgoing connections only) 無(出接続のみ) - + Use UPnP to establish port forward (recommended) ポートフォワーディングにはUPnPを使用する(お勧め) - + Use static external IP address/host name and port スタティックIPアドレス、ホスト名とポートを使用する - + Set this to your external IP address or host name. Make sure to forward the port to this host! グローバルIPアドレス、又はホスト名を入力して下さい。なお、ポートフォワーディングの設定を確認して下さい。 - + Static Host Name: スタティックホスト名: - + Static Port: スタティックポート: - + SOCKS Proxy SOCKSプロクシ - + Use SOCKS Proxy SOCKSプロクシを使用する - + Proxy Settings... プロクシ設定... - + Other Settings 他の設定 - + Allow web browsers to interact with Tomahawk (recommended) ブラウザとの連携を許可する(お勧め) - + Send reports after Tomahawk crashed Tomahawkがクラッシュしたら、レポートを送信する - + Show notification when a new song starts to play 曲が再生したら、通知を表示する - + Clear All Access Control Entries 全てのアクセス制御のエントリーを削除する @@ -1679,12 +1679,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: 音楽ファイルのパス: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1695,17 +1695,17 @@ connect and stream from you? 基づいた自動プレイリストやステーションを作れます。 - + Upload collection list to The Echo Nest to enable user radio コレクションのリストをThe Echo Nestにアップロードして、ユーザーラジオを有効にする - + Watch for changes 更新を監視する - + Time between scans, in seconds: スキャンの時間間隔(秒): @@ -1713,12 +1713,12 @@ connect and stream from you? SlideSwitchButton - + On オン - + Off オフ @@ -1744,32 +1744,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet ツイート - + Listening to "%1" by %2. %3 %2の"%1"を聴いています。%3 - + Listening to "%1" by %2 on "%3". %4 %2の"%3"の"%1"を聴いています。%4 - + %1 characters left 残り%1文字 @@ -1777,44 +1777,44 @@ connect and stream from you? SourceDelegate - + Track トラック - + Album アルバム - + Artist アーティスト - + Local ローカル - + Top 10 トップ10 - + All available tracks 利用可能トラック - - + + Show 表示 - - + + Hide 隠す @@ -1855,53 +1855,53 @@ connect and stream from you? SourceItem - - + + Latest Additions 最新追加した項目 - + Recently Played 最近聴いたトラック - + SuperCollection スーパーコレクション - + Latest additions to your collection コレクションの最新追加した項目 - + Latest additions to %1's collection %1のコレクションの最新追加した項目 - + Sorry, we could not find any recent additions! 最近追加した項目が見つかりませんでした。 - + Recently Played Tracks 最近再生したトラック - + Your recently played tracks あなたの最近再生したトラック - + %1's recently played tracks %1の最近再生したトラック - + Sorry, we could not find any recent plays! 最近の再生した項目が見つかりませんでした。 @@ -1909,68 +1909,68 @@ connect and stream from you? SourceTreeView - + &Copy Link リンクをコピー - + &Delete %1 %1を削除 - + Add to my Playlists プレイリストに追加する - + Add to my Automatic Playlists 自動プレイリストに追加する - + Add to my Stations ステーションに追加する - + &Export Playlist プレイリストを書き出し - + playlist プレイリスト - + automatic playlist 自動プレイリスト - + station ステーション - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? <b>"%2"</b>と言う%1を削除しますか? - + Delete 削除 - + Save XSPF XSPFを保存する - + Playlists (*.xspf) プレイリスト (*.xspf) @@ -1978,77 +1978,77 @@ connect and stream from you? SourcesModel - + Group グループ - + Collection コレクション - + Playlist プレイリスト - + Automatic Playlist 自動プレイリスト - + Station ステーション - + Browse 閲覧 - + Search History 履歴を検索 - + My Music マイミュージック - + SuperCollection スーパーコレクション - + Cloud - + Dashboard ダッシュボード - + Recently Played 最近聴いたトラック - + Charts チャート - + New Releases ニューリリース - + Friends 友達 @@ -2122,17 +2122,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link アーティストリンクをコピー - + Copy Album Link アルバムリンクをコピー - + Copy Track Link トラックリンクをコピー @@ -2605,13 +2605,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3493,7 +3493,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション @@ -3524,48 +3524,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track 再生中トラックの再生後、再生を停止する - - + + Hide Tomahawk Window Tomahawkのウインドウを隠す - + Show Tomahawk Window Tomahawkのウインドウを表示 - + Currently not playing. 一時停止 - + Play 再生 - + Pause 一時停止 - + &Love &Love - + Un-&Love Un-&Love - + &Continue Playback after current Track 再生中トラックの再生後、再生を続く @@ -3573,161 +3573,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back プレイリスト - + Go back one page 前のページ - + Forward 次へ - + Go forward one page 次のページ - - + + Hide Menu Bar メニューバーを隠す - - + + Show Menu Bar メニューバーを表示 - + Search for any artist, album or song... アーティスト、又はアルバムや曲で検索して下さい - + &Main Menu メインメニュー - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPFエラー - + This is not a valid XSPF playlist. このプレイリストは有利なXSPFプレイリストではありません。 - + Failed to save tracks トラックの保存に失敗しました。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. プレイリストにアーティストもタイトルの無いトラックが見つかりました。この項目は無視されます。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。適しているPhononのバックエンドを確認の上、必須プラグインのインストールを確認して下さい。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。 - + Station ステーション - + Create New Station 新規ステーションを作成 - + Name: 名前: - + Playlist プレイリスト - + Automatic Playlist 自動プレイリスト - + Pause 一時停止 - + &Play 再生 - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk Tomahawkについて @@ -4007,7 +4007,7 @@ Twitterを使っている友達にTomahawkを接続したいなら、ツイー XMPPBot - + Terms for %1: @@ -4016,12 +4016,12 @@ Terms for %1: - + No terms found, sorry. 語句と一致する結果は見つかりませんでした。 - + Hotttness for %1: %2 @@ -4030,7 +4030,7 @@ Hotttness for %1: %2 - + Familiarity for %1: %2 @@ -4039,7 +4039,7 @@ Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 23d81f5bb1..453dc5dabd 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Maišyti - + Repeat Kartoti - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Sukurti naują grojaraštį - + Create new Station Sukurti naują stotį - - + + New Station Nauja stotis - - + + %1 Station @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Grojaraščiai - + Stations Stotys @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk strigties pranešyklė - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report Siųsti šią ataskaitą - + Don't send Nesiųsti - + Abort Nutraukti - + You can disable sending crash reports in the configuration dialog. Galite išjungti strigčių ataskaitų siuntimą konfigūracijos dialogo lange. - + Uploaded %L1 of %L2 KB. - - + + Close Užverti - + Sent! <b>Many thanks</b>. Išsiųsta! <b>Labai ačiū</b>. - + Failed to send crash info. Nepavyko nusiųsti strigties informacijos. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk diagnostika - + &Copy to Clipboard &Kopijuoti į iškarpinę - + Open &Log-file Atverti žurna&lą @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Įkelti XSPF - + Playlist URL Grojaraščio URL - + Enter URL... Įveskite URL... - + ... ... - + Automatically update Atnaujinti automatiškai @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Įkelti XSPF failą - + XSPF Files (*.xspf) XSPF failai (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks Populiariausios mylimos dainos - + Sorry, we could not find any loved tracks! Deja, neradome jokių mylimų dainų! - + The most loved tracks from all your friends Visų Jūsų draugų labiausiai mylimos dainos - + All of your loved tracks Visos Jūsų mylimos dainos - + All of %1's loved tracks Visos %1 mylimos dainos - + Loved Tracks Mylimos dainos @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings Proxy nuostatos - + Hostname of proxy server Proxy serverio vardas - + Host Serveris - + Port Prievadas - + Proxy login Proxy prisijungimas - + User Vartotojas - + Password Slaptažodis - + Proxy password Proxy slaptažodis - + No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) - + Use proxy for DNS lookups? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 Nerasta: %1 - + Failed to load: %1 Nepavyko įkelti: %1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection Kolekcija - + Advanced Išplėstinės - + All Visi - + Some changed settings will not take effect until Tomahawk is restarted - + Services Paslaugos - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Informacija @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet - + Listening to "%1" by %2. %3 Klausosi "%1", atliekamą %2. %3 - + Listening to "%1" by %2 on "%3". %4 Klausosi "%1", atliekamą %2 iš albumo "%3". %4 - + %1 characters left Liko simbolių: %1 @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track Takelis - + Album Albumas - + Artist Atlikėjas - + Local - + Top 10 Top 10 - + All available tracks Visi prieinami takeliai - - + + Show Rodyti - - + + Hide Slėpti @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions Neseniai pridėta - + Recently Played Neseniai grota - + SuperCollection Super Kolekcija - + Latest additions to your collection Neseniai pridėta prie Jūsų kolekcijos - + Latest additions to %1's collection Neseniai pridėta prie %1 kolekcijos - + Sorry, we could not find any recent additions! Atsiprašome, neradome nieko, kas pridėta neseniai! - + Recently Played Tracks Neseniai groti takeliai - + Your recently played tracks Jūsų neseniai groti takeliai - + %1's recently played tracks %1 neseniai groti takeliai - + Sorry, we could not find any recent plays! Atsiprašome, neradome jokių neseniai grotų takelių! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link &Kopijuoti nuorodą - + &Delete %1 Paša&linti %1 - + Add to my Playlists Pridėti prie mano grojaraščių - + Add to my Automatic Playlists Pridėti prie mano automatinių grojaraščių - + Add to my Stations Pridėti prie mano stočių - + &Export Playlist - + playlist grojaraštis - + automatic playlist automatinis grojaraštis - + station stotis - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Ar norite pašalinti %1 <b>"%2"</b>? - + Delete Pašalinti - + Save XSPF Išsaugoti XSPF - + Playlists (*.xspf) Grojaraščiai (*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group - + Collection Kolekcija - + Playlist Grojaraštis - + Automatic Playlist Automatinis grojaraštis - + Station Stotis - + Browse Naršyti - + Search History Paieškos istorija - + My Music Mano muzika - + SuperCollection Super Kolekcija - + Cloud - + Dashboard Skydelis - + Recently Played Neseniai klausyta - + Charts - + New Releases Nauji leidimai - + Friends Draugai @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link Kopijuoti atlikėjo nuorodą - + Copy Album Link Kopijuoti albumo nuorodą - + Copy Track Link Kopijuoti takelio nuorodą @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track Sta&bdyti grojimą po dabartinio takelio - - + + Hide Tomahawk Window Slėpti Tomahawk langą - + Show Tomahawk Window Rodyti Tomahawk langą - + Currently not playing. Šiuo metu negrojama. - + Play Groti - + Pause Pristabdyti - + &Love &Myliu - + Un-&Love &Nemyliu - + &Continue Playback after current Track &Tęsti grojimą po dabartinio takelio @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back Atgal - + Go back one page Grįžti vienu puslapiu atgal - + Forward Pirmyn - + Go forward one page Eiti vienu puslapiu pirmyn - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF klaida - + This is not a valid XSPF playlist. - + Failed to save tracks Nepavyko išsaugoti takelių - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Stotis - + Create New Station Sukurti naują stotį - + Name: Pavadinimas: - + Playlist Grojaraštis - + Automatic Playlist Automatinis grojaraštis - + Pause Pristabdyti - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorinės teisės 2010 - 2013 - + Thanks to: Dėkojame: - + About Tomahawk Apie Tomahawk @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index e4ca0d8721..49eb85359b 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -56,18 +56,18 @@ połączyć się i strumieniować od ciebie? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -75,7 +75,7 @@ połączyć się i strumieniować od ciebie? AccountWidget - + Invite @@ -83,7 +83,7 @@ połączyć się i strumieniować od ciebie? AccountsToolButton - + Configure Accounts @@ -341,37 +341,37 @@ połączyć się i strumieniować od ciebie? AudioControls - + Shuffle Losowo - + Repeat Powtarzaj - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love Lubię @@ -397,24 +397,24 @@ połączyć się i strumieniować od ciebie? CategoryAddItem - + Create new Playlist - + Create new Station - - + + New Station Nowa stacja - - + + %1 Station Stacja %1 @@ -422,12 +422,12 @@ połączyć się i strumieniować od ciebie? CategoryItem - + Playlists Listy - + Stations Stacje @@ -457,53 +457,53 @@ połączyć się i strumieniować od ciebie? CrashReporter - + Tomahawk Crash Reporter Agent zgłaszania awarii Tomahawka - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style="font-weigth:600;">Przepraszamy!</span>Tomahawk przestał odpowiadać. Poinformuj nas o tym! Tomahawk utworzył raport o błędzie, który może poprawić stabilność w przyszłości. Możesz teraz wysłać ten raport bezpośrednio do programistów Tomahawka.</p></body></html> - + Send this report Wyślij ten raport - + Don't send Nie wysyłaj - + Abort Przerwij - + You can disable sending crash reports in the configuration dialog. Możesz wyłączyć zgłaszanie awarii w ustawieniach. - + Uploaded %L1 of %L2 KB. Wysłano %L1 z %L2 KB. - - + + Close Zamknij - + Sent! <b>Many thanks</b>. Wysłano! <b>Wielkie dzięki</b>. - + Failed to send crash info. Nie udało sie wysłać informacji o awarii. @@ -542,17 +542,17 @@ połączyć się i strumieniować od ciebie? DiagnosticsDialog - + Tomahawk Diagnostics Diagnostyka Tomahawk - + &Copy to Clipboard - + Open &Log-file @@ -679,7 +679,7 @@ połączyć się i strumieniować od ciebie? InboxItem - + Inbox @@ -775,27 +775,27 @@ połączyć się i strumieniować od ciebie? LoadXSPF - + Load XSPF Załaduj XSPF - + Playlist URL URL Listy odtwarzania - + Enter URL... Wprowadź URL... - + ... ... - + Automatically update Aktualizuj automatycznie @@ -803,12 +803,12 @@ połączyć się i strumieniować od ciebie? LoadXSPFDialog - + Load XSPF File Załaduj plik XSPF - + XSPF Files (*.xspf) Pliki XSPF (*.xspf) @@ -839,32 +839,32 @@ połączyć się i strumieniować od ciebie? LovedTracksItem - + Top Loved Tracks Najczęściej lubiane utwory - + Sorry, we could not find any loved tracks! Przepraszamy, nie mogliśmy znaleźć żadnych lubianych utworów! - + The most loved tracks from all your friends Najczęściej lubiane utwory wszystkich twoich znajomych - + All of your loved tracks Wszystkie twoje lubiane utwory - + All of %1's loved tracks Wszystkie utwory lubiane przez %1 - + Loved Tracks Lubiane @@ -1247,59 +1247,59 @@ połączyć się i strumieniować od ciebie? ProxyDialog - + Proxy Settings Ustawienia Proxy - + Hostname of proxy server Host serwera proxy - + Host Host - + Port Port - + Proxy login Login Proxy - + User Użytkownik - + Password Hasło - + Proxy password Hasło Proxy - + No Proxy Hosts: (Overrides system proxy) Hosty bez proxy: (zastępuje proxy systemowe) - + localhost *.example.com (space separated) localhost *.example.com (oddzielone spacją) - + Use proxy for DNS lookups? Używać proxy dla zapytań DNS? @@ -1450,12 +1450,12 @@ połączyć się i strumieniować od ciebie? ResolverConfigDelegate - + Not found: %1 Nie znaleziono: %1 - + Failed to load: %1 Nie udało sie załadować: %1 @@ -1515,77 +1515,77 @@ połączyć się i strumieniować od ciebie? SettingsDialog - + Collection Kolekcje - + Advanced Zaawansowane - + All Wszystkie - + Some changed settings will not take effect until Tomahawk is restarted Niektóre zmiany zaczną działać po restarcie Tomahawka - + Services Usługi - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file Zainstaluj usługę z pliku - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Informacja @@ -1593,7 +1593,7 @@ połączyć się i strumieniować od ciebie? Settings_Accounts - + Filter by capability: @@ -1601,77 +1601,77 @@ połączyć się i strumieniować od ciebie? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1679,12 +1679,12 @@ połączyć się i strumieniować od ciebie? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1692,17 +1692,17 @@ połączyć się i strumieniować od ciebie? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1710,12 +1710,12 @@ połączyć się i strumieniować od ciebie? SlideSwitchButton - + On - + Off @@ -1741,32 +1741,32 @@ połączyć się i strumieniować od ciebie? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 Słucham "%1" wykonawcy %2 z "%3". %4 - + %1 characters left pozostało znaków: %1 @@ -1774,44 +1774,44 @@ połączyć się i strumieniować od ciebie? SourceDelegate - + Track Utwór - + Album Album - + Artist Artysta - + Local Lokalny - + Top 10 Top 10 - + All available tracks Wszystkie dostępne utwory - - + + Show Pokaż - - + + Hide Ukryj @@ -1852,53 +1852,53 @@ połączyć się i strumieniować od ciebie? SourceItem - - + + Latest Additions Ostatnio Dodane - + Recently Played Ostatnio Odtworzone - + SuperCollection Superkolekcja - + Latest additions to your collection Ostatnio dodane do twojej kolekcji - + Latest additions to %1's collection Ostatnio dodane do kolekcji %1 - + Sorry, we could not find any recent additions! - + Recently Played Tracks Ostatnio odtwarzane utwory - + Your recently played tracks Twoje ostatnio odtwarzane utwory - + %1's recently played tracks Utwory ostatnio odtwarzane przez %1 - + Sorry, we could not find any recent plays! @@ -1906,68 +1906,68 @@ połączyć się i strumieniować od ciebie? SourceTreeView - + &Copy Link &Kopiuj Link - + &Delete %1 &Usuń %1 - + Add to my Playlists Dodaj do moich List odtwarzania - + Add to my Automatic Playlists Dodaj do moich Automatycznych list odtwarzania - + Add to my Stations Dodaj do moich Stacji - + &Export Playlist &Eksportuj Listę - + playlist lista odtwarzania - + automatic playlist automatyczna lista odtwarzania - + station stacja - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF Zapisz XSPF - + Playlists (*.xspf) Listy (*.xspf) @@ -1975,77 +1975,77 @@ połączyć się i strumieniować od ciebie? SourcesModel - + Group Grupa - + Collection kolekcja - + Playlist Lista - + Automatic Playlist Automatyczna Lista - + Station Stacja - + Browse Przeglądaj - + Search History Historia wyszukiwania - + My Music Moja Muzyka - + SuperCollection Superkolekcja - + Cloud - + Dashboard Tablica kontrolna - + Recently Played Ostatnio Odtworzone - + Charts Listy Przebojów - + New Releases Nowe Wydania - + Friends Znajomi @@ -2119,17 +2119,17 @@ połączyć się i strumieniować od ciebie? TemporaryPageItem - + Copy Artist Link Kopiuj link do wykonawcy - + Copy Album Link Kopiuj link do albumu - + Copy Track Link Kopiuj link do utworu @@ -2602,13 +2602,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3490,7 +3490,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja @@ -3521,48 +3521,48 @@ wprowadź pokazany numer PIN tutaj: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window Ukryj Okno Tomahawka - + Show Tomahawk Window Pokaż Okno Tomahawka - + Currently not playing. Aktualnie nie odtwarza. - + Play Odtwarzaj - + Pause Pauza - + &Love &Lubię - + Un-&Love Przestań &lubić - + &Continue Playback after current Track @@ -3570,161 +3570,161 @@ wprowadź pokazany numer PIN tutaj: TomahawkWindow - + Tomahawk Tomahawk - + Back Wstecz - + Go back one page Cofnij o jedną stronę - + Forward Naprzód - + Go forward one page Przejdź naprzód o jedną stronę - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error Błąd XSPF - + This is not a valid XSPF playlist. To nie jest poprawna lista XSPF. - + Failed to save tracks Nie udało się zapisać utworów - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Niektóre utwory na liście nie zawierają artysty i tytułu. Zostaną one zignorowane. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Przepraszamy, wystąpił problem z połączeniem z twoim urządzeniem audio lub z żądanym utworem, zostanie on pominięty. - + Station - + Create New Station Utwórz Nową Stację - + Name: Nazwa: - + Playlist - + Automatic Playlist - + Pause Pauza - + &Play - + %1 by %2 track, artist name %1 wykonawcy %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Podziękowania dla: - + About Tomahawk O Tomahawku @@ -4004,26 +4004,26 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 @@ -4032,7 +4032,7 @@ Znajomość dla %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 263d1b5e3c..cb3733059a 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -56,18 +56,18 @@ se conecte e faça o stream de você? AccountListWidget - + Connections Conexões - - + + Connect &All Conect&ar todos - + Disconnect &All Desconect&ar todos @@ -75,7 +75,7 @@ se conecte e faça o stream de você? AccountWidget - + Invite Convidar @@ -83,7 +83,7 @@ se conecte e faça o stream de você? AccountsToolButton - + Configure Accounts Configurar contas @@ -341,37 +341,37 @@ se conecte e faça o stream de você? AudioControls - + Shuffle Shuffle - + Repeat Repetir - + Time Elapsed Tempo decorrido - + Time Remaining Tempo restante - + Playing from %1 Reproduzindo de %1 - + Share Compartilhar - + Love Gostar @@ -397,24 +397,24 @@ se conecte e faça o stream de você? CategoryAddItem - + Create new Playlist Criar nova Playlist - + Create new Station Criar nova Estação - - + + New Station Nova estação - - + + %1 Station %1 Estação @@ -422,12 +422,12 @@ se conecte e faça o stream de você? CategoryItem - + Playlists Playlists - + Stations Estações @@ -457,53 +457,53 @@ se conecte e faça o stream de você? CrashReporter - + Tomahawk Crash Reporter Relatório de falha do Tomahawk - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Desculpe!</span> O Tomahawk travou. Por favor, informe-nos sobre isso! Criamos um relatório de erro que pode ajudar a melhorar a estabilidade no futuro. Você pode enviar este relatório diretamente para os desenvolvedores do Tomahawk.</p></body></html> - + Send this report Enviar este relatório - + Don't send Não enviar - + Abort Cancelar - + You can disable sending crash reports in the configuration dialog. Você pode desabilitar o envio de relatórios de erros na aba de configurações. - + Uploaded %L1 of %L2 KB. Enviado %L1 de %L2 KB. - - + + Close Fechar - + Sent! <b>Many thanks</b>. Enviado! <b>Muito obrigado</b>. - + Failed to send crash info. Falha ao enviar as informações de erro. @@ -542,17 +542,17 @@ se conecte e faça o stream de você? DiagnosticsDialog - + Tomahawk Diagnostics Diagnósticos do Tomahawk - + &Copy to Clipboard &Copiar para a área de transferência - + Open &Log-file Abrir arquivo &log @@ -679,7 +679,7 @@ se conecte e faça o stream de você? InboxItem - + Inbox @@ -775,27 +775,27 @@ se conecte e faça o stream de você? LoadXSPF - + Load XSPF Carregar XSPF - + Playlist URL URL da Playlist - + Enter URL... Colocar URL... - + ... ... - + Automatically update Atualizar automaticamente @@ -803,12 +803,12 @@ se conecte e faça o stream de você? LoadXSPFDialog - + Load XSPF File Carregar arquivo XSPF - + XSPF Files (*.xspf) Arquivos XSPF (*.xspf) @@ -839,32 +839,32 @@ se conecte e faça o stream de você? LovedTracksItem - + Top Loved Tracks Faixas mais favoritas - + Sorry, we could not find any loved tracks! Desculpe, não encontramos nenhuma faixa favorita! - + The most loved tracks from all your friends As faixas mais favoritas de todos os seus amigos - + All of your loved tracks Todas as suas faixas favoritas - + All of %1's loved tracks Todas as faixas favoritas do %1 - + Loved Tracks Faixas favoritas @@ -1247,59 +1247,59 @@ se conecte e faça o stream de você? ProxyDialog - + Proxy Settings Configurações de proxy - + Hostname of proxy server Endereço do servidor proxy - + Host Servidor - + Port Porta - + Proxy login Usuário do servidor proxy - + User Usuário - + Password Senha - + Proxy password Senha do servidor proxy - + No Proxy Hosts: (Overrides system proxy) Sem Servidores de Proxy:⏎ (Substitui o proxy do sistema) - + localhost *.example.com (space separated) localhost *.exemplo.com (separado por espaço) - + Use proxy for DNS lookups? Usar proxy para resolução DNS? @@ -1450,12 +1450,12 @@ se conecte e faça o stream de você? ResolverConfigDelegate - + Not found: %1 Não encontrado: %1 - + Failed to load: %1 Falha ao carregar: %1 @@ -1515,77 +1515,77 @@ se conecte e faça o stream de você? SettingsDialog - + Collection Coleção - + Advanced Avançado - + All Todos - + Some changed settings will not take effect until Tomahawk is restarted Algumas configurações não terão efeito até que o Tomahawk seja reiniciado - + Services Serviços - + Install from file Instalar do arquivo - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Configure as contas e serviços usados pelo Tomahawk para pesquisar e obter músicas, encontrar seus amigos e atualizar seu status. - + Manage how Tomahawk finds music on your computer. Gerenciar como o Tomahawk encontra música no seu computador - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Configure as configurações avançadas do Tomahawk, incluindo as configurações de conectividade de rede, interação com o navegador e mais. - + Install resolver from file Instalar resolvedor via arquivo - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Excluir todas as entradas de controle de acesso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Você realmente quer apagar todas as entradas de controle de acesso? Você será solicitado a tomar uma decisão para cada ponto a que você se conectar. - + Information Informação @@ -1593,7 +1593,7 @@ se conecte e faça o stream de você? Settings_Accounts - + Filter by capability: Filtrar por capacidade: @@ -1601,77 +1601,77 @@ se conecte e faça o stream de você? Settings_Advanced - + Remote Peer Connection Method Método de conexão com o par remoto - + None (outgoing connections only) Nenhum (somente conexões de saída) - + Use UPnP to establish port forward (recommended) Usar UPnP para estabelecer redirecionamento de porta (recomendado) - + Use static external IP address/host name and port Usar nome de máquina/endereço IP estático externo e porta - + Set this to your external IP address or host name. Make sure to forward the port to this host! Defina isso para seu nome de máquina ou endereço IP externo. Certifique-se de redirecionar a porta para esta máquina! - + Static Host Name: Nome de máquina estático: - + Static Port: Porta estática: - + SOCKS Proxy Proxy SOCKS - + Use SOCKS Proxy Usar proxy SOCKS - + Proxy Settings... Configurações de proxy... - + Other Settings Outras configurações - + Allow web browsers to interact with Tomahawk (recommended) Permitir que navegadores web interajam com o Tomahawk (recomendável) - + Send reports after Tomahawk crashed Enviar relatórios após Tomahawk ter travado - + Show notification when a new song starts to play Mostrar notificação quando uma nova música começar a tocar - + Clear All Access Control Entries Limpar todo os registros de controle de acesso @@ -1679,12 +1679,12 @@ se conecte e faça o stream de você? Settings_Collection - + Path to scan for music files: Caminho para varrer por arquivos de músicas: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1692,17 +1692,17 @@ se conecte e faça o stream de você? The Echo Nest suporta manter rastro dosmetadados do seu catálogo e usá-los para criar rádios personalizadas. Ao habilitar essa opção você (e todos os seus amigos) poderão criar listas de reprodução e estações automaticamente baseado nas preferências pessoais do perfil. - + Upload collection list to The Echo Nest to enable user radio Enviar lista de coleções para The Echo Nest para habilitar rádio de usuário - + Watch for changes Vigiar por alterações - + Time between scans, in seconds: Intervalo entre varreduras, em segundos: @@ -1710,12 +1710,12 @@ se conecte e faça o stream de você? SlideSwitchButton - + On On - + Off Off @@ -1741,32 +1741,32 @@ se conecte e faça o stream de você? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 Ouvindo "%1" de %2. %3 - + Listening to "%1" by %2 on "%3". %4 Ouvindo "%1" de %2 em "%3". %4 - + %1 characters left %1 caracteres restantes @@ -1774,44 +1774,44 @@ se conecte e faça o stream de você? SourceDelegate - + Track Faixa - + Album Álbum - + Artist Artista - + Local Local - + Top 10 10 Mais - + All available tracks Todas as faixas disponíveis - - + + Show Mostrar - - + + Hide Ocultar @@ -1852,53 +1852,53 @@ se conecte e faça o stream de você? SourceItem - - + + Latest Additions Últimas Adições - + Recently Played Ouvidas Recentemente - + SuperCollection SuperColeção - + Latest additions to your collection Últimas adições à sua coleção - + Latest additions to %1's collection Últimas adições à coleção de %1 - + Sorry, we could not find any recent additions! Desculpe, não foi possível encontrar adições recentes! - + Recently Played Tracks Faixas Reproduzidas Recentemente - + Your recently played tracks Suas faixas reproduzidas recentemente - + %1's recently played tracks Faixas reproduzidas recentemente por %1 - + Sorry, we could not find any recent plays! Desculpe, não foi possível encontrar playlists recentes! @@ -1906,68 +1906,68 @@ se conecte e faça o stream de você? SourceTreeView - + &Copy Link &Copiar link - + &Delete %1 &Excluir %1 - + Add to my Playlists Adicionar às minhas Playlists - + Add to my Automatic Playlists Adicionar às minhas Playlists Automáticas - + Add to my Stations Adicionar às minhas Estações - + &Export Playlist &Exportar Playlist - + playlist playlist - + automatic playlist playlist automática - + station estação - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Gostaria de deletar a %1 <b>"%2"</b>? - + Delete Excluir - + Save XSPF Salvar XSPF - + Playlists (*.xspf) Playlists (*.xspf) @@ -1975,77 +1975,77 @@ se conecte e faça o stream de você? SourcesModel - + Group Grupo - + Collection Coleção - + Playlist Playlist - + Automatic Playlist Playlist Automática - + Station Estação - + Browse Navegar - + Search History Histórico de Busca - + My Music Minhas Músicas - + SuperCollection SuperColeção - + Cloud - + Dashboard Painel - + Recently Played Ouvidas Recentemente - + Charts Charts - + New Releases Lançamentos - + Friends Amigos @@ -2119,17 +2119,17 @@ se conecte e faça o stream de você? TemporaryPageItem - + Copy Artist Link Copiar Link do Artista - + Copy Album Link Copiar Link do Álbum - + Copy Track Link Copiar Link da Faixa @@ -2602,13 +2602,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3490,7 +3490,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção @@ -3521,48 +3521,48 @@ colocar o número PIN mostrado aqui: TomahawkTrayIcon - + &Stop Playback after current Track &Parar a reprodução após a faixa atual - - + + Hide Tomahawk Window Esconder janela do Tomahawk - + Show Tomahawk Window Mostrar janela do Tomahawk - + Currently not playing. Não reproduzindo nada. - + Play Reporduzir - + Pause Pausar - + &Love &Favorito - + Un-&Love Remover dos &Favoritos - + &Continue Playback after current Track &Continuar a reprodução após a faixa atual @@ -3570,161 +3570,161 @@ colocar o número PIN mostrado aqui: TomahawkWindow - + Tomahawk Tomahawk - + Back Voltar - + Go back one page Voltar uma página - + Forward Avançar - + Go forward one page Avançar uma página - - + + Hide Menu Bar Esconder barra de menu - - + + Show Menu Bar Mostrar barra de menu - + Search for any artist, album or song... Pesquisar por qualquer artista, álbum ou música... - + &Main Menu &Menu principal - + Exit Full Screen - + Enter Full Screen - + XSPF Error Erro de XSPF - + This is not a valid XSPF playlist. Esta não é uma lista de reprodução XSPF válida. - + Failed to save tracks Falha ao salvar faixas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algumas faixas da lista de reprodução não contem artista e título. Estas serão ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. Certifique-se de ter um backend do Phonon adequado e os plugins necessários instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. - + Station Estação - + Create New Station Criar uma nova estação - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist Automática - + Pause PIN do Twitter - + &Play Re&produzir - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecimentos: - + About Tomahawk Sobre o Tomahawk @@ -4004,7 +4004,7 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment XMPPBot - + Terms for %1: @@ -4013,12 +4013,12 @@ Termos para %1: - + No terms found, sorry. Nenhum termo encontrado, descupe. - + Hotttness for %1: %2 @@ -4027,14 +4027,14 @@ Hotttness para %1: %2 - + Familiarity for %1: %2 Familiar para %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index f1d2e29fbb..357ce35480 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -56,18 +56,18 @@ connect and stream from you? AccountListWidget - + Connections Соединения - - + + Connect &All Включить Все - + Disconnect &All Выключить Всё @@ -75,7 +75,7 @@ connect and stream from you? AccountWidget - + Invite Пригласить @@ -84,7 +84,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts Настройки аккаунта @@ -344,37 +344,37 @@ connect and stream from you? AudioControls - + Shuffle Случаная - + Repeat Повторять - + Time Elapsed Прошедшее время - + Time Remaining Оставшееся время - + Playing from %1 Воспроизводит из %1 - + Share Поделиться - + Love Любимая @@ -400,24 +400,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist Создать Новый Плейлист - + Create new Station Создать Новую Станцию - - + + New Station Новая станция - - + + %1 Station %1 Станция @@ -425,12 +425,12 @@ connect and stream from you? CategoryItem - + Playlists Плейлисты - + Stations Станции @@ -460,53 +460,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Отчет о ошибках - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Простите!</span> Tomahawk сломался :). Пожалуйста, сообщите нам об этом! Tomahawk создал отчет об ошибке, который поможет улучшить стабильность в будущем. Теперь Вы можете отправить отчет непосредственно разработчикам Tomahawk. Спасибо за помощь! </p></body></html> - + Send this report Отправить отчет - + Don't send Не отправлять - + Abort Отменить - + You can disable sending crash reports in the configuration dialog. Вы можете отключить отправку отчетов об ошибках в диалоге настройки. - + Uploaded %L1 of %L2 KB. Загружено %L1 из %L2 KB. - - + + Close Закрыть - + Sent! <b>Many thanks</b>. Отправлен! <b>Большое спасибо!!!</b>. - + Failed to send crash info. Невозможно отправить отчет о ошибке. @@ -545,17 +545,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Диагностика - + &Copy to Clipboard &Скопировать в буфер обмена - + Open &Log-file Открыть Log файл @@ -682,7 +682,7 @@ connect and stream from you? InboxItem - + Inbox @@ -778,27 +778,27 @@ connect and stream from you? LoadXSPF - + Load XSPF Загрузить XSPF - + Playlist URL Плейлист URL - + Enter URL... Введите ссылку на плейлист (URI)... - + ... ... - + Automatically update Обновлять автоматически @@ -806,12 +806,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File Загрузить файл XSPF - + XSPF Files (*.xspf) Файлы XSPF (*.xspf) @@ -842,32 +842,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks Топ Любимых Песен - + Sorry, we could not find any loved tracks! К сожалению, мы не смогли найти никаких любимых песен! - + The most loved tracks from all your friends Любимые Песни Вас и Ваших Друзей - + All of your loved tracks Ваши любимые песни - + All of %1's loved tracks Любимые Песни %1 - + Loved Tracks Любимые Песни @@ -1250,59 +1250,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings Настройки прокси - + Hostname of proxy server Имя хоста прокси сервера - + Host Хост - + Port Порт - + Proxy login Введите логин - + User Логин - + Password Пароль - + Proxy password Введите пароль - + No Proxy Hosts: (Overrides system proxy) Не использовать прокси: (Переопределение прокси) - + localhost *.example.com (space separated) localhost *.example.com (Разделять пробелами) - + Use proxy for DNS lookups? Использовать прокси для поиска DNS? @@ -1453,12 +1453,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 Не найдено: %1 - + Failed to load: %1 Ошибка при загрузке: %1 @@ -1518,77 +1518,77 @@ connect and stream from you? SettingsDialog - + Collection Коллекция - + Advanced Дополнительны - + All Все - + Some changed settings will not take effect until Tomahawk is restarted Некоторые измененные настройки не вступят в силу до перезапуска Tomahawk - + Services Сервисы - + Install from file Установить из файла - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Настройка аккаунтов и сервисов используемых Tomahawk для поиска и извлечения музыки, найти друзей и обновить статус. - + Manage how Tomahawk finds music on your computer. Определить где Tomahawk искать музыку на вашем компьютере. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Настройка дополнительных возможносте Tomahawk в том числе подключения к сети, интеграцию с браузером, другие. - + Install resolver from file Установить resolver из файла - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Удаление всех записей контроля доступа? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Вы действительно хотите удалить все записи Access Control? Вас заново спросят о каждом соединении к которым вы были подключены. - + Information Инофрмация @@ -1596,7 +1596,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: Фильтр по возможности: @@ -1604,77 +1604,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method Метод подключения к удаленным пирам - + None (outgoing connections only) Нет (только исходящие соединения) - + Use UPnP to establish port forward (recommended) Использование UPnP для проброса портов (рекомендуется) - + Use static external IP address/host name and port Использовать статический внешний ip адрес/имя хоста и порт - + Set this to your external IP address or host name. Make sure to forward the port to this host! Установите это на ваш внешний ip адресс или имени хоста. Убедитесь в том что проброс портов осуществляется к этому хосту! - + Static Host Name: Статическое имя хоста: - + Static Port: Статический порт: - + SOCKS Proxy SOCKS Прокси - + Use SOCKS Proxy Использовать SOCKS Прокси - + Proxy Settings... Настройки Прокси... - + Other Settings Другие Настройки - + Allow web browsers to interact with Tomahawk (recommended) Разрешить взаимодействие браузеров с Tomahawk (Рекомендуется) - + Send reports after Tomahawk crashed Отправлять отчеты об ошибках Tomahawk - + Show notification when a new song starts to play Показать уведомление, когда новая песня начинает играть - + Clear All Access Control Entries Очистить все элементы управления доступом @@ -1682,12 +1682,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: Путь для поиска музыкальных файлов: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1698,17 +1698,17 @@ connect and stream from you? и станций на основе вашего личного вкуса из вашего профиля. - + Upload collection list to The Echo Nest to enable user radio Загрузить список коллекции на The Echo Nest, чтобы включить пользовательское радио - + Watch for changes Следите за изменениями - + Time between scans, in seconds: Время между проверками в секундах: @@ -1716,12 +1716,12 @@ connect and stream from you? SlideSwitchButton - + On Вкл - + Off Выкл @@ -1747,32 +1747,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Твитнуть - + Listening to "%1" by %2. %3 Прослушивание "%1" %2. %3 - + Listening to "%1" by %2 on "%3". %4 Слушает Песню "%1" %2 альбома "%3". %4 - + %1 characters left Осталось %1 символов @@ -1780,44 +1780,44 @@ connect and stream from you? SourceDelegate - + Track Трек - + Album Альбом - + Artist Исполнитель - + Local Локальная - + Top 10 Топ 10 - + All available tracks Доступные песни - - + + Show Показать - - + + Hide Спрятать @@ -1858,53 +1858,53 @@ connect and stream from you? SourceItem - - + + Latest Additions Последние Добавленные - + Recently Played Последние Воспроизводимые - + SuperCollection Общая Коллекция - + Latest additions to your collection Новые Поступления в Коллекцию - + Latest additions to %1's collection Новые поступления в коллекции %1 - + Sorry, we could not find any recent additions! К сожалению, мы не смогли найти никаких последних добавлений! - + Recently Played Tracks Недавно Воспроизводимые - + Your recently played tracks Ваши Недавно Воспроизводимые - + %1's recently played tracks %1 последние проиграные треки - + Sorry, we could not find any recent plays! К сожалению, мы не смогли найти никаких воспроизвидений треков! @@ -1912,68 +1912,68 @@ connect and stream from you? SourceTreeView - + &Copy Link &Скопировать Cсылку - + &Delete %1 &Удалить %1 - + Add to my Playlists Добавить к Моим Плейлистам - + Add to my Automatic Playlists Добавить к Моим Автоматическим Плейлистам - + Add to my Stations Добавить к Моим Станциям - + &Export Playlist &Экспорт Плейлиста - + playlist плейлист - + automatic playlist автоматический плейлист - + station станция - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Вы хотите удалить %1 <b>"%2"</b>? - + Delete Удалить - + Save XSPF Сохранить XSPF - + Playlists (*.xspf) Плейлисты (*.xspf) @@ -1981,77 +1981,77 @@ connect and stream from you? SourcesModel - + Group Группа - + Collection Коллекция - + Playlist Плейлист - + Automatic Playlist Автоматический плейлист - + Station Станция - + Browse Просмотреть - + Search History История поиска - + My Music Моя Музыка - + SuperCollection Общая Коллекция - + Cloud Облако - + Dashboard Главная Панель - + Recently Played Последние Воспроизводимые - + Charts Чарты - + New Releases Новые Релизы - + Friends Друзья @@ -2125,17 +2125,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link Скопировать Ссылку на Исполнителя - + Copy Album Link Скопировать Ссылку на Альбом - + Copy Track Link Скопировать Ссылку на Песню @@ -2611,13 +2611,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Коллекция - + This collection is empty. Коллекция пуста. @@ -3497,7 +3497,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция @@ -3527,48 +3527,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track &Остановить после текущего трека - - + + Hide Tomahawk Window Спрятать Окно Tomahawk - + Show Tomahawk Window Показать окно Tomahawk - + Currently not playing. Не воспроизводится. - + Play Играть - + Pause Пауза - + &Love &Любиая - + Un-&Love &НеЛюбимая - + &Continue Playback after current Track &Продолжить воспроизведение после текущего трека @@ -3576,161 +3576,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back Назад - + Go back one page Перейти на предыдущую страницу - + Forward Вперед - + Go forward one page Перейдите на следующую страницу - - + + Hide Menu Bar Спрятать Строку Меню - - + + Show Menu Bar Показать Строку Меню - + Search for any artist, album or song... Поиск любого исполнителя, альбома или песни ... - + &Main Menu &Главное меню - + Exit Full Screen Выход из полноэкранного режима - + Enter Full Screen Переход в полноэкранный режим - + XSPF Error Ошибка XSPF - + This is not a valid XSPF playlist. Это не является допустимым XSPF плейлистом. - + Failed to save tracks Не удалось сохранить песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Некоторые песни в плейлисте не содержат исполнителя и название. Они будут проигнорированы. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. Убедитесь, что у вас есть подходящий Phonon backend и необходимые плагины установлены. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. - + Station Станция - + Create New Station Создать Новую Станцию - + Name: Имя: - + Playlist Плейлист - + Automatic Playlist Автоматический Плейлист - + Pause Пауза - + &Play &Играть - + %1 by %2 track, artist name %1 %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Авторское право 2010 - 2013 - + Thanks to: Благодарность - + About Tomahawk О Tomahawk @@ -4010,7 +4010,7 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: @@ -4018,12 +4018,12 @@ Terms for %1: Условия для %1: - + No terms found, sorry. Извините не найдено терминов. - + Hotttness for %1: %2 @@ -4032,7 +4032,7 @@ Hotttness for %1: %2 - + Familiarity for %1: %2 @@ -4041,7 +4041,7 @@ Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index f1dc6fff09..6965643f3e 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -56,18 +56,18 @@ ansluta och strömma från dig? AccountListWidget - + Connections Anslutningar - - + + Connect &All Anslut &alla - + Disconnect &All Koppla från &alla @@ -75,7 +75,7 @@ ansluta och strömma från dig? AccountWidget - + Invite Bjud in @@ -83,7 +83,7 @@ ansluta och strömma från dig? AccountsToolButton - + Configure Accounts Konfigurera konton @@ -341,37 +341,37 @@ ansluta och strömma från dig? AudioControls - + Shuffle Blanda - + Repeat Upprepa - + Time Elapsed Förfluten tid - + Time Remaining Tid kvar - + Playing from %1 Spelar från %1 - + Share Dela - + Love Älska @@ -397,24 +397,24 @@ ansluta och strömma från dig? CategoryAddItem - + Create new Playlist Skapa ny spellista - + Create new Station Skapa ny station - - + + New Station Ny station - - + + %1 Station %1 station @@ -422,12 +422,12 @@ ansluta och strömma från dig? CategoryItem - + Playlists Spellistor - + Stations Stationer @@ -457,53 +457,53 @@ ansluta och strömma från dig? CrashReporter - + Tomahawk Crash Reporter Tomahawk kraschrapport - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk kraschade Låt oss veta vad som hänt! Tomahawk har skapat en felrapport åt dig som kan hjälpa till att förbättra stabiliteten i framtiden. Du kan skicka den direkt till Tomahawks utvecklare.</p></body></html> - + Send this report Skicka rapporten - + Don't send Skicka inte - + Abort Avbryt - + You can disable sending crash reports in the configuration dialog. Du kan välja att inte skicka kraschrapporter i konfigurationsdialogen. - + Uploaded %L1 of %L2 KB. Laddade upp %L1 av %L2 KB. - - + + Close Stäng - + Sent! <b>Many thanks</b>. Skickat! <b>Tack så mycket</b>. - + Failed to send crash info. Misslyckades skicka kraschrapport. @@ -542,17 +542,17 @@ ansluta och strömma från dig? DiagnosticsDialog - + Tomahawk Diagnostics Diagnostik för Tomahawk - + &Copy to Clipboard &Kopiera - + Open &Log-file Öppna &logfil @@ -679,7 +679,7 @@ ansluta och strömma från dig? InboxItem - + Inbox Inkorg @@ -775,27 +775,27 @@ ansluta och strömma från dig? LoadXSPF - + Load XSPF Läs in XSPF - + Playlist URL URL för spellista - + Enter URL... Ange URL... - + ... - + Automatically update Uppdatera automatiskt @@ -803,12 +803,12 @@ ansluta och strömma från dig? LoadXSPFDialog - + Load XSPF File Läs in XSPF-fil - + XSPF Files (*.xspf) XSPF Filer (*.xspf) @@ -839,32 +839,32 @@ ansluta och strömma från dig? LovedTracksItem - + Top Loved Tracks Mest älskade spår - + Sorry, we could not find any loved tracks! Tyvärr! Vi kunde inte hitta några älskade spår! - + The most loved tracks from all your friends Dina vänners mest älskade spår - + All of your loved tracks Alla dina älskade spår - + All of %1's loved tracks Alla %1's älskade spår - + Loved Tracks Älskade spår @@ -1247,59 +1247,59 @@ ansluta och strömma från dig? ProxyDialog - + Proxy Settings Proxyinställningar - + Hostname of proxy server Värdnamn för proxyservern - + Host Värdnamn - + Port Port - + Proxy login Proxy login - + User Användare - + Password Lösenord - + Proxy password Proxy lösenord - + No Proxy Hosts: (Overrides system proxy) Ingen Proxy host (Skriver över systemproxyn) - + localhost *.example.com (space separated) localhost *.example.com (separeras med mellanslag) - + Use proxy for DNS lookups? Använd proxy för DNS kontroll? @@ -1450,12 +1450,12 @@ ansluta och strömma från dig? ResolverConfigDelegate - + Not found: %1 Hittades inte: %1 - + Failed to load: %1 Gick inte att läsa in: %1 @@ -1515,77 +1515,77 @@ ansluta och strömma från dig? SettingsDialog - + Collection Samling - + Advanced Avancerat - + All Alla - + Some changed settings will not take effect until Tomahawk is restarted Några inställningar kommer inte träda i kraft förrän Tomahawk har starts om - + Services Tjänster - + Install from file Installera från fil - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. Konfigurera alla konton och services som Tomahawk använder för att söka och hitta musik, hitta dina vänner och uppdaterar din status. - + Manage how Tomahawk finds music on your computer. Hantera hur Tomahawk hittar musik på din dator. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. Konfigurera Tomahawks avancerade inställningar, inklusive nätverkets anslutningsinställningar, webbläsarinteraktion m.m - + Install resolver from file Installera resolver från fil - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;Alla filer (*) - + Resolver installation from file %1 failed. Resolver-installationen från filen %1 misslyckades - + Delete all Access Control entries? Ta bort alla åtkomstkontrollsposter - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Vill du verkligen ta bort alla åtkomstkontrollsposter? Du kommer att bli förfrågad igen för varje nod du försöker ansluta till. - + Information Information @@ -1593,7 +1593,7 @@ ansluta och strömma från dig? Settings_Accounts - + Filter by capability: Filtrera på kapacitet: @@ -1601,77 +1601,77 @@ ansluta och strömma från dig? Settings_Advanced - + Remote Peer Connection Method Fjärrnodens anslutningsmetod - + None (outgoing connections only) Inga (Enbart utgående anslutningar) - + Use UPnP to establish port forward (recommended) Använd UPnP för att etablera portforward (Rekommenderas) - + Use static external IP address/host name and port Använd statiskt externt IP-nummer/värdnamn och port - + Set this to your external IP address or host name. Make sure to forward the port to this host! Ange detta till ditt externa IPnummer eller värdnamn. Se till att vidarebefordra porten till värden! - + Static Host Name: Statiskt värdnamn: - + Static Port: Statisk Port: - + SOCKS Proxy SOCKS Proxy - + Use SOCKS Proxy Använd SOCKS Proxy - + Proxy Settings... Proxyinställningar... - + Other Settings Andra inställningar - + Allow web browsers to interact with Tomahawk (recommended) Tillåt webbläsare att interagera med Tomahawk (Rekommenderas) - + Send reports after Tomahawk crashed Skicka rapport efter att Tomahawk kraschat - + Show notification when a new song starts to play Visa notifiering när en låt börjar spela - + Clear All Access Control Entries Rensa alla åtkomstkontrollsposter @@ -1679,12 +1679,12 @@ ansluta och strömma från dig? Settings_Collection - + Path to scan for music files: Sökväg att skanna efter musikfiler: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1694,17 +1694,17 @@ och använder för att skapa personliga radiostationer. Genom att aktivera detta och radiostationer baserat på din personliga profil - + Upload collection list to The Echo Nest to enable user radio Ladda upp samlingslistan till Echo Nest för att aktivera användar-radio - + Watch for changes Håll koll på ändringar - + Time between scans, in seconds: Tid mellan skanningar, i sekunder: @@ -1712,12 +1712,12 @@ och radiostationer baserat på din personliga profil SlideSwitchButton - + On - + Off Av @@ -1743,32 +1743,32 @@ och radiostationer baserat på din personliga profil SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 Lyssnar på "%1" av %2. %3 - + Listening to "%1" by %2 on "%3". %4 Lyssnar på "%1" av %2 på "%3". %4 - + %1 characters left %1 bokstäver kvar @@ -1776,44 +1776,44 @@ och radiostationer baserat på din personliga profil SourceDelegate - + Track Spår - + Album Album - + Artist Artist - + Local Lokalt - + Top 10 Topp 10 - + All available tracks Alla tillgängliga spår - - + + Show Visa - - + + Hide Göm @@ -1854,53 +1854,53 @@ och radiostationer baserat på din personliga profil SourceItem - - + + Latest Additions Senast tillagda - + Recently Played Senast spelade spår - + SuperCollection Supersamling - + Latest additions to your collection Senaste tillägget till ditt samling. - + Latest additions to %1's collection Senaste tillägget till %1's samling. - + Sorry, we could not find any recent additions! Tyvärr! Det gick inte hitta några nya tillägg! - + Recently Played Tracks Senast spelade spår - + Your recently played tracks Dina senast spelade spår - + %1's recently played tracks %1's senast spelade spår - + Sorry, we could not find any recent plays! Tyvärr! Det gick inte hitta några nyligen spelade spår @@ -1908,68 +1908,68 @@ och radiostationer baserat på din personliga profil SourceTreeView - + &Copy Link &Kopiera länk - + &Delete %1 &Ta bort %1 - + Add to my Playlists Lägg till i mina spellistor - + Add to my Automatic Playlists Lägg till i mina automatiska spellistor - + Add to my Stations Lägg till i mina stationer - + &Export Playlist &Exportera spellista - + playlist spellista - + automatic playlist automatisk spellista - + station station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Vill du ta bort %1 <b>"%2"</b>? - + Delete Ta bort - + Save XSPF Spara XSPF - + Playlists (*.xspf) Spellistor (*.xspf) @@ -1977,77 +1977,77 @@ och radiostationer baserat på din personliga profil SourcesModel - + Group Grupp - + Collection Samling - + Playlist Spellista - + Automatic Playlist Automatisk spellista - + Station Station - + Browse Bläddra - + Search History Sökhistorik - + My Music Min Musik - + SuperCollection Supersamling - + Cloud Moln - + Dashboard Dashboard - + Recently Played Nyligen Spelade - + Charts Topplistor - + New Releases Nya släpp - + Friends Vänner @@ -2121,17 +2121,17 @@ och radiostationer baserat på din personliga profil TemporaryPageItem - + Copy Artist Link Kopiera artistlänk - + Copy Album Link Kopiera albumlänk - + Copy Track Link Kopiera spårlänk @@ -2606,13 +2606,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Samling - + This collection is empty. Denna samlingen är tom @@ -3494,7 +3494,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling @@ -3525,48 +3525,48 @@ anger du PIN-koden här: TomahawkTrayIcon - + &Stop Playback after current Track &Stoppa uppspelningen efter nuvarande spår - - + + Hide Tomahawk Window Dölj Tomahawk-fönstret - + Show Tomahawk Window Visa Tomahawk-fönstret - + Currently not playing. Spelar ingenting för närvarande. - + Play Spela upp - + Pause Paus - + &Love &älska - + Un-&Love av-&älska - + &Continue Playback after current Track &Fortsätt uppspelning efter nuvarande spår @@ -3574,161 +3574,161 @@ anger du PIN-koden här: TomahawkWindow - + Tomahawk Tomahawk - + Back Tillbaka - + Go back one page Gå tillbaks en sida - + Forward Framåt - + Go forward one page Gå framåt en sida - - + + Hide Menu Bar Göm Menyrad - - + + Show Menu Bar Visa Menyrad - + Search for any artist, album or song... Sök efter valfri artist, album eller låt... - + &Main Menu &Huvudmeny - + Exit Full Screen Gå ur fullskärmsläge - + Enter Full Screen Fullskärmsläge - + XSPF Error XSPF-fel - + This is not a valid XSPF playlist. Detta är inte en giltig XSPF-spellista. - + Failed to save tracks Misslyckades med att spara spår - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Några spår i spellistan innehåller inte någon artist och titel. De kommer att ignoreras. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Tyvärr! Det uppstod ett problem med kontakten till ditt ljudkort eller det önskade spåret. Nuvarande spår kommer att hoppas över. Kontrollera att du har en lämplig Phonon-backend och alla nödvändiga plugins installerade - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Tyvärr blev det problem att hitta din ljudenhet eller den valda låten! Nuvarande låt kommer att hoppas över - + Station Station - + Create New Station Skapa ny station - + Name: Namn: - + Playlist Spellista - + Automatic Playlist Automatisk spellista - + Pause Paus - + &Play &Spela - + %1 by %2 track, artist name %1 av %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Tack till: - + About Tomahawk Om Tomahawk @@ -4008,7 +4008,7 @@ Du kan skicka om ett synkat meddelande när som helst genom att skicka ett tweet XMPPBot - + Terms for %1: @@ -4017,12 +4017,12 @@ Villkor för %1: - + No terms found, sorry. Inga villkor funna, tyvärr. - + Hotttness for %1: %2 @@ -4030,7 +4030,7 @@ Hotttness for %1: %2 - + Familiarity for %1: %2 @@ -4039,7 +4039,7 @@ Kännedom för %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 75dd2b0f69..b7156bfe1b 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle Karışık - + Repeat Yenile - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist - + Create new Station - - + + New Station Yeni İstasyon - - + + %1 Station %1 İstasyon @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists Şarkı Listeleri - + Stations İstasyonlar @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk Çökme Raporcusu - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report - + Don't send - + Abort Vazgeç - + You can disable sending crash reports in the configuration dialog. Çökme raporu göndermeyi yapılandırma iletişim kutusundan devre dışı bırakabilirsiz. - + Uploaded %L1 of %L2 KB. %L2 içinden %L1 KB karşıya yüklendin. - - + + Close Kapat. - + Sent! <b>Many thanks</b>. Gönderildi! <b>Çok teşekkürler.</b>. - + Failed to send crash info. Çökme bilgisi gönderimi başarısız. @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk Tanılama - + &Copy to Clipboard - + Open &Log-file @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF XSPF Yükle - + Playlist URL Şarkı Listesi URL'si - + Enter URL... URL Girin... - + ... ... - + Automatically update Otomatik güncelle @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File XSPF Dosyası Yükle - + XSPF Files (*.xspf) XSPF Dosyaları (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings - + Hostname of proxy server - + Host - + Port - + Proxy login - + User - + Password - + Proxy password - + No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) - + Use proxy for DNS lookups? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 - + Failed to load: %1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection - + Advanced - + All - + Some changed settings will not take effect until Tomahawk is restarted - + Services - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook - + Twitter - + Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 - + %1 characters left @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track - + Album - + Artist - + Local - + Top 10 - + All available tracks - - + + Show - - + + Hide @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - + Recently Played Tracks - + Your recently played tracks - + %1's recently played tracks - + Sorry, we could not find any recent plays! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group - + Collection - + Playlist - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link - + Copy Album Link - + Copy Track Link @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. - + Play - + Pause - + &Love - + Un-&Love - + &Continue Playback after current Track @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 152888ab36..9f0b774530 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections 连接 - - + + Connect &All 连接所有 - + Disconnect &All 断开所有 @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite 邀请 @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts 配置账户 @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle 无序播放 - + Repeat 重复播放 - + Time Elapsed 已播放时间 - + Time Remaining 剩余时间 - + Playing from %1 歌曲来自 %1 - + Share 分享 - + Love 喜欢 @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist 创建新的播放列表 - + Create new Station 创建新的电台 - - + + New Station 新电台 - - + + %1 Station %1 电台 @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists 播放列表 - + Stations 电台 @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk 崩溃报告 - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">抱歉!</span> Tomahawk 崩溃了。请告诉我们具体情况!Tomahawk 已经建立了一个错误报告来帮助我们提高程序的稳定性。你可以直接将它发送给 Tomahawk 开发者。</p></body></html> - + Send this report 发送此报告 - + Don't send 不要发送 - + Abort 中止 - + You can disable sending crash reports in the configuration dialog. 你可以在设置中禁用发送崩溃报告 - + Uploaded %L1 of %L2 KB. 已发送 %L2 KB 中的 %L1 - - + + Close 关闭 - + Sent! <b>Many thanks</b>. 已发送!<b>非常感谢</b>。 - + Failed to send crash info. 发送崩溃信息失败。 @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk 诊断信息 - + &Copy to Clipboard 拷贝剪贴板 - + Open &Log-file 打开日志文件 @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF 导入 XSPF - + Playlist URL 播放列表 URL - + Enter URL... 输入 URL... - + ... ... - + Automatically update 自动更新 @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File 载入 XSPF 文件 - + XSPF Files (*.xspf) XSPF 文件 (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks 最多喜爱的歌曲 - + Sorry, we could not find any loved tracks! 抱歉!未找到任何喜爱歌曲。 - + The most loved tracks from all your friends 从所有朋友中获取的最多喜爱歌曲 - + All of your loved tracks 我所有的喜爱歌曲 - + All of %1's loved tracks %1 所有被喜欢的歌曲 - + Loved Tracks 喜爱歌曲 @@ -1246,59 +1246,59 @@ connect and stream from you? ProxyDialog - + Proxy Settings 代理设置 - + Hostname of proxy server 代理服务器地址 - + Host 服务器 - + Port 端口 - + Proxy login 代理登录 - + User 用户名 - + Password 密码 - + Proxy password 代理服务器密码 - + No Proxy Hosts: (Overrides system proxy) 不用代理的区域: (将覆盖系统代理) - + localhost *.example.com (space separated) localhost *.example.com (使用空格作为分割符) - + Use proxy for DNS lookups? 使用代理查找 DNS 吗? @@ -1449,12 +1449,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 未找到: %1 - + Failed to load: %1 载入未成功: %1 @@ -1514,77 +1514,77 @@ connect and stream from you? SettingsDialog - + Collection 收藏 - + Advanced 高级 - + All 所有 - + Some changed settings will not take effect until Tomahawk is restarted 一些设置改动将在 Tomahawk 下次启动时生效。 - + Services 服务 - + Install from file 从文件安装 - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. 配置 Tomahawk 使用的账户和服务以获取音乐,寻找朋友,更新状态 - + Manage how Tomahawk finds music on your computer. 设置以便让 Tomahawk 发现你计算机上的音乐。 - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. 配置 Tomahawk 的高级设置,包括网络链接设置,浏览交互设置等等。 - + Install resolver from file 从文件安装解析器 - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? 删除所有的访问控制项? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. 你真的想删除所有的访问控制项吗?将在对每个连接的客户端操作后再次询问。 - + Information 信息 @@ -1592,7 +1592,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: 过滤: @@ -1600,77 +1600,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method 远程连接方法 - + None (outgoing connections only) 停用(只启用对外连接) - + Use UPnP to establish port forward (recommended) 使用 UPnP 建立端口转发(建议启用) - + Use static external IP address/host name and port 使用固定的 IP 地址/主机和端口 - + Set this to your external IP address or host name. Make sure to forward the port to this host! 将这里设置为指定的 IP 地址或主机名。请确认您将转发到这台主机的指定端口! - + Static Host Name: 静态主机名: - + Static Port: 端口: - + SOCKS Proxy SOCKS 代理 - + Use SOCKS Proxy 使用 SOCKS 代理 - + Proxy Settings... 代理设置 ... - + Other Settings 其他设置 - + Allow web browsers to interact with Tomahawk (recommended) 允许浏览器与 Tomahawk 交互(推荐启用) - + Send reports after Tomahawk crashed 在 Tomahawk 崩溃后发送报告 - + Show notification when a new song starts to play - + Clear All Access Control Entries 清除所有访问控制条目 @@ -1678,12 +1678,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: 扫描音乐文件的路径: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1693,17 +1693,17 @@ connect and stream from you? 朋友)基于你的个人口味创建自动播放列表和电台。 - + Upload collection list to The Echo Nest to enable user radio 向 Echo Nest 上传专辑列表以启用电台 - + Watch for changes 监视改动 - + Time between scans, in seconds: 扫描间隔时间,使用秒作为单位: @@ -1711,12 +1711,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1742,32 +1742,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet Tweet - + Listening to "%1" by %2. %3 正在听 %2. %3 的 "%1"。 - + Listening to "%1" by %2 on "%3". %4 正在听 "%1",来自 %2 的专辑 "%3"。 %4 - + %1 characters left 剩余 %1 字 @@ -1775,44 +1775,44 @@ connect and stream from you? SourceDelegate - + Track 歌曲 - + Album 专辑 - + Artist 艺术家 - + Local 本地 - + Top 10 Top 10 - + All available tracks 所有可用的歌曲 - - + + Show 显示 - - + + Hide 隐藏 @@ -1853,53 +1853,53 @@ connect and stream from you? SourceItem - - + + Latest Additions 最近添加 - + Recently Played 最近播放 - + SuperCollection 超级收藏 - + Latest additions to your collection 最近加入收藏的歌曲 - + Latest additions to %1's collection 最新加入 %1 收藏的项目 - + Sorry, we could not find any recent additions! 抱歉,未找到任何最近添加的音乐! - + Recently Played Tracks 最近播放歌曲 - + Your recently played tracks 你最近播放的歌曲 - + %1's recently played tracks %1最近播放的歌曲 - + Sorry, we could not find any recent plays! 抱歉,未找到任何最近播放的音乐! @@ -1907,68 +1907,68 @@ connect and stream from you? SourceTreeView - + &Copy Link 复制链接 - + &Delete %1 删除%1 - + Add to my Playlists 添加到我的播放列表 - + Add to my Automatic Playlists 添加到我的自动播放列表 - + Add to my Stations 添加到我的电台 - + &Export Playlist 导出播放列表 - + playlist 播放列表 - + automatic playlist 自动播放列表 - + station 电台 - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? 要删除 %1 <b>"%2"</b> 吗? - + Delete 删除 - + Save XSPF 保存 XSPF - + Playlists (*.xspf) 播放列表 (*.xspf) @@ -1976,77 +1976,77 @@ connect and stream from you? SourcesModel - + Group 群组 - + Collection 收藏 - + Playlist 播放列表 - + Automatic Playlist 自动播放列表 - + Station 电台 - + Browse 随便看看 - + Search History 搜索历史 - + My Music 我的音乐 - + SuperCollection 超级收藏 - + Cloud - + Dashboard 概览 - + Recently Played 最近播放 - + Charts 排行榜 - + New Releases 新专辑 - + Friends 朋友们 @@ -2120,17 +2120,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link 复制艺术家链接 - + Copy Album Link 复制专辑链接 - + Copy Track Link 复制歌曲链接 @@ -2603,13 +2603,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3491,7 +3491,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3522,48 +3522,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track 播放当前歌曲后停止播放 - - + + Hide Tomahawk Window 隐藏 Tomahawk 窗口 - + Show Tomahawk Window 显示 Tomahawk 窗口 - + Currently not playing. 当前没有播放歌曲 - + Play 播放 - + Pause 暂停 - + &Love 喜欢 - + Un-&Love 不喜欢 - + &Continue Playback after current Track 播放当前歌曲后继续播放 @@ -3571,161 +3571,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back 后退 - + Go back one page 转向上一页 - + Forward 下一个 - + Go forward one page 转向下一页 - - + + Hide Menu Bar 隐藏菜单栏 - - + + Show Menu Bar 显示菜单栏 - + Search for any artist, album or song... 搜索任意艺人,专辑,或歌曲... - + &Main Menu 主菜单 - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 错误 - + This is not a valid XSPF playlist. 这不是一个合法的 XSPF 播放列表。 - + Failed to save tracks 保存歌曲失败。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. 播放列表中的一些歌曲缺失艺术家和标题,它们将被忽略。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. 抱歉,访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。请确认你正在使用合适的 Phonon 后端并安装了必要的插件。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. 抱歉,在访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。 - + Station 电台 - + Create New Station 创建新电台 - + Name: 名字: - + Playlist 播放列表 - + Automatic Playlist 自动播放列表 - + Pause 暂停 - + &Play 播放 - + %1 by %2 track, artist name %2 的 %1 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 版权所有 2010 - 2013 - + Thanks to: 感谢: - + About Tomahawk 关于 Tomahawk @@ -4005,33 +4005,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. 没有找到相关项目,抱歉。 - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index c6121e96dc..09951121d6 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -55,18 +55,18 @@ connect and stream from you? AccountListWidget - + Connections - - + + Connect &All - + Disconnect &All @@ -74,7 +74,7 @@ connect and stream from you? AccountWidget - + Invite @@ -82,7 +82,7 @@ connect and stream from you? AccountsToolButton - + Configure Accounts @@ -340,37 +340,37 @@ connect and stream from you? AudioControls - + Shuffle 隨機 - + Repeat 重複 - + Time Elapsed - + Time Remaining - + Playing from %1 - + Share - + Love @@ -396,24 +396,24 @@ connect and stream from you? CategoryAddItem - + Create new Playlist - + Create new Station - - + + New Station - - + + %1 Station @@ -421,12 +421,12 @@ connect and stream from you? CategoryItem - + Playlists 播放清單 - + Stations @@ -456,53 +456,53 @@ connect and stream from you? CrashReporter - + Tomahawk Crash Reporter Tomahawk 崩潰報告 - + <html><head/><body><p><span style=" font-weight:600;">Sorry!</span> Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.</p></body></html> - + Send this report - + Don't send - + Abort 中止 - + You can disable sending crash reports in the configuration dialog. 在配置對話框中,您可以禁用發送崩潰報告。 - + Uploaded %L1 of %L2 KB. - - + + Close 關閉 - + Sent! <b>Many thanks</b>. - + Failed to send crash info. 無法發送故障信息。 @@ -541,17 +541,17 @@ connect and stream from you? DiagnosticsDialog - + Tomahawk Diagnostics Tomahawk 診斷 - + &Copy to Clipboard - + Open &Log-file @@ -678,7 +678,7 @@ connect and stream from you? InboxItem - + Inbox @@ -774,27 +774,27 @@ connect and stream from you? LoadXSPF - + Load XSPF 載入 XSPF - + Playlist URL 播放清單 URL - + Enter URL... 輸入網址... - + ... ... - + Automatically update 自動更新 @@ -802,12 +802,12 @@ connect and stream from you? LoadXSPFDialog - + Load XSPF File 載入 XSPF 檔 - + XSPF Files (*.xspf) XSPF 檔 (*.xspf) @@ -838,32 +838,32 @@ connect and stream from you? LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -1246,58 +1246,58 @@ connect and stream from you? ProxyDialog - + Proxy Settings 代理服務器設定 - + Hostname of proxy server 代理服務器的主機名稱 - + Host 主機 - + Port - + Proxy login Proxy 登錄 - + User 用戶 - + Password 密碼 - + Proxy password Proxy 密碼 - + No Proxy Hosts: (Overrides system proxy) - + localhost *.example.com (space separated) - + Use proxy for DNS lookups? @@ -1448,12 +1448,12 @@ connect and stream from you? ResolverConfigDelegate - + Not found: %1 未找到:%1 - + Failed to load: %1 無法載入:%1 @@ -1513,77 +1513,77 @@ connect and stream from you? SettingsDialog - + Collection 收藏 - + Advanced 進階 - + All 所有 - + Some changed settings will not take effect until Tomahawk is restarted - + Services 服務 - + Install from file - + Configure the accounts and services used by Tomahawk to search and retrieve music, find your friends and update your status. - + Manage how Tomahawk finds music on your computer. - + Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file 從檔案安裝解析器 - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information 資訊 @@ -1591,7 +1591,7 @@ connect and stream from you? Settings_Accounts - + Filter by capability: @@ -1599,77 +1599,77 @@ connect and stream from you? Settings_Advanced - + Remote Peer Connection Method - + None (outgoing connections only) - + Use UPnP to establish port forward (recommended) - + Use static external IP address/host name and port - + Set this to your external IP address or host name. Make sure to forward the port to this host! - + Static Host Name: - + Static Port: - + SOCKS Proxy - + Use SOCKS Proxy - + Proxy Settings... - + Other Settings - + Allow web browsers to interact with Tomahawk (recommended) - + Send reports after Tomahawk crashed - + Show notification when a new song starts to play - + Clear All Access Control Entries @@ -1677,12 +1677,12 @@ connect and stream from you? Settings_Collection - + Path to scan for music files: - + The Echo Nest supports keeping track of your catalog metadata and using it to craft personalized radios. Enabling this option will allow you (and all your friends) to create automatic playlists @@ -1690,17 +1690,17 @@ connect and stream from you? - + Upload collection list to The Echo Nest to enable user radio - + Watch for changes - + Time between scans, in seconds: @@ -1708,12 +1708,12 @@ connect and stream from you? SlideSwitchButton - + On - + Off @@ -1739,32 +1739,32 @@ connect and stream from you? SocialWidget - + Facebook Facebook - + Twitter Twitter - + Tweet - + Listening to "%1" by %2. %3 - + Listening to "%1" by %2 on "%3". %4 - + %1 characters left @@ -1772,44 +1772,44 @@ connect and stream from you? SourceDelegate - + Track 曲目 - + Album 專輯 - + Artist 演出者 - + Local 本地 - + Top 10 前10名 - + All available tracks - - + + Show 顯示 - - + + Hide 隱藏 @@ -1850,53 +1850,53 @@ connect and stream from you? SourceItem - - + + Latest Additions 最新加入 - + Recently Played 最近播放的 - + SuperCollection 超級收藏 - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - + Recently Played Tracks - + Your recently played tracks - + %1's recently played tracks - + Sorry, we could not find any recent plays! @@ -1904,68 +1904,68 @@ connect and stream from you? SourceTreeView - + &Copy Link 複製鏈接 - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist 匯出播放清單 - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF 儲存 XSPF - + Playlists (*.xspf) 播放清單(*.xspf) @@ -1973,77 +1973,77 @@ connect and stream from you? SourcesModel - + Group - + Collection 收藏 - + Playlist 播放清單 - + Automatic Playlist 自動播放清單 - + Station - + Browse 瀏覽 - + Search History 搜尋記錄 - + My Music 我的音樂 - + SuperCollection 超級收藏 - + Cloud - + Dashboard 儀表板 - + Recently Played 最近播放的 - + Charts - + New Releases 新版本 - + Friends 朋友 @@ -2117,17 +2117,17 @@ connect and stream from you? TemporaryPageItem - + Copy Artist Link - + Copy Album Link - + Copy Track Link @@ -2597,13 +2597,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3481,7 +3481,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3511,48 +3511,48 @@ enter the displayed PIN number here: TomahawkTrayIcon - + &Stop Playback after current Track - - + + Hide Tomahawk Window - + Show Tomahawk Window - + Currently not playing. 目前沒有播放。 - + Play 播放 - + Pause 暫停 - + &Love - + Un-&Love - + &Continue Playback after current Track @@ -3560,161 +3560,161 @@ enter the displayed PIN number here: TomahawkWindow - + Tomahawk Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 錯誤 - + This is not a valid XSPF playlist. - + Failed to save tracks 無法儲存曲目 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: 名稱: - + Playlist - + Automatic Playlist - + Pause 暫停 - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -3989,33 +3989,33 @@ You can re-send a sync message at any time simply by sending another tweet using XMPPBot - + Terms for %1: - + No terms found, sorry. - + Hotttness for %1: %2 - + Familiarity for %1: %2 - + Lyrics for "%1" by %2: From aa5e9ea4f2e232659a0019333c15092d0031d712 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Sat, 25 May 2013 13:46:24 +0200 Subject: [PATCH 094/565] Useless entry is useless. --- src/libtomahawk/TomahawkSettings.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 546a395bfb..4416360bd3 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -190,7 +190,6 @@ TomahawkSettings::createSpotifyAccount() beginGroup( "accounts/" + accountKey ); setValue( "enabled", false ); setValue( "types", QStringList() << "ResolverType" ); - setValue( "credentials", QVariantHash() ); setValue( "configuration", QVariantHash() ); setValue( "accountfriendlyname", "Spotify" ); endGroup(); From ec4ac5210b15d29762c1756198c21ea0fe94a866 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sat, 25 May 2013 15:00:18 +0200 Subject: [PATCH 095/565] Pimple AudioEngine --- src/libtomahawk/audio/AudioEngine.cpp | 737 +++++++++++++++----------- src/libtomahawk/audio/AudioEngine.h | 72 +-- 2 files changed, 455 insertions(+), 354 deletions(-) diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index b83857c514..193cb5fbd8 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -19,17 +19,15 @@ */ #include "AudioEngine.h" +#include "AudioEngine_p.h" #include "config.h" -#include "PlaylistInterface.h" -#include "SourcePlaylistInterface.h" #include "TomahawkSettings.h" -#include "database/Database.h" #include "network/Servent.h" #include "utils/Qnr_IoDeviceStream.h" #include "utils/Closure.h" -#include "infosystem/InfoSystem.h" +#include "Artist.h" #include "Album.h" #include "Pipeline.h" #include "jobview/JobStatusView.h" @@ -39,11 +37,9 @@ #include "utils/Logger.h" #include "playlist/SingleTrackPlaylistInterface.h" -#include - -#include #include -#include + +#include using namespace Tomahawk; @@ -53,47 +49,158 @@ static const uint_fast8_t UNDERRUNTHRESHOLD = 2; static QString s_aeInfoIdentifier = QString( "AUDIOENGINE" ); -AudioEngine* AudioEngine::s_instance = 0; + +void +AudioEnginePrivate::onStateChanged( Phonon::State newState, Phonon::State oldState ) +{ + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << oldState << newState << expectStop << q_ptr->state(); + + if ( newState == Phonon::LoadingState ) + { + // We don't emit this state to listeners - yet. + state = AudioEngine::Loading; + } + if ( newState == Phonon::BufferingState ) + { + if ( underrunCount > UNDERRUNTHRESHOLD && !underrunNotified ) + { + underrunNotified = true; + //FIXME: Actually notify + } + else + underrunCount++; + } + if ( newState == Phonon::ErrorState ) + { + q_ptr->stop( AudioEngine::UnknownError ); + + tDebug() << "Phonon Error:" << mediaObject->errorString() << mediaObject->errorType(); + + emit q_ptr->error( AudioEngine::UnknownError ); + q_ptr->setState( AudioEngine::Error ); + } + if ( newState == Phonon::PlayingState ) + { + if ( q_ptr->state() != AudioEngine::Paused && q_ptr->state() != AudioEngine::Playing ) + { + underrunCount = 0; + underrunNotified = false; + emit q_ptr->started( currentTrack ); + } + + q_ptr->setState( AudioEngine::Playing ); + } + if ( newState == Phonon::StoppedState && oldState == Phonon::PausedState ) + { + // GStreamer backend hack: instead of going from PlayingState to StoppedState, it traverses PausedState + q_ptr->setState( AudioEngine::Stopped ); + } + + if ( oldState == Phonon::PlayingState ) + { + bool stopped = false; + switch ( newState ) + { + case Phonon::PausedState: + { + if ( mediaObject && currentTrack ) + { + qint64 duration = mediaObject->totalTime() > 0 ? mediaObject->totalTime() : currentTrack->track()->duration() * 1000; + stopped = ( duration - 1000 < mediaObject->currentTime() ); + } + else + stopped = true; + + if ( !stopped ) + q_ptr->setState( AudioEngine::Paused ); + + break; + } + case Phonon::StoppedState: + { + stopped = true; + break; + } + default: + break; + } + + if ( stopped && expectStop ) + { + expectStop = false; + tDebug( LOGVERBOSE ) << "Finding next track."; + if ( q_ptr->canGoNext() ) + { + q_ptr->loadNextTrack(); + } + else + { + if ( !playlist.isNull() && playlist.data()->retryMode() == Tomahawk::PlaylistModes::Retry ) + waitingOnNewTrack = true; + + q_ptr->stop(); + } + } + } + + if ( newState == Phonon::PausedState || newState == Phonon::PlayingState || newState == Phonon::ErrorState ) + { + tDebug( LOGVERBOSE ) << "Phonon state now:" << newState; + if ( stateQueue.count() ) + { + /*/ AudioState qState = */ stateQueue.dequeue(); + q_ptr->checkStateQueue(); + } + } +} + + + + +AudioEngine* AudioEnginePrivate::s_instance = 0; AudioEngine* AudioEngine::instance() { - return s_instance; + return AudioEnginePrivate::s_instance; } AudioEngine::AudioEngine() : QObject() - , m_queue( 0 ) - , m_timeElapsed( 0 ) - , m_expectStop( false ) - , m_waitingOnNewTrack( false ) - , m_state( Stopped ) - , m_coverTempFile( 0 ) + , d_ptr( new AudioEnginePrivate( this ) ) { - s_instance = this; + Q_D( AudioEngine ); + + d->timeElapsed = 0; + d->expectStop = false; + d->waitingOnNewTrack = false; + d->state = Stopped; + d->coverTempFile = 0; + + d->s_instance = this; tDebug() << "Init AudioEngine"; qRegisterMetaType< AudioErrorCode >("AudioErrorCode"); qRegisterMetaType< AudioState >("AudioState"); - m_mediaObject = new Phonon::MediaObject( this ); - m_audioOutput = new Phonon::AudioOutput( Phonon::MusicCategory, this ); - Phonon::createPath( m_mediaObject, m_audioOutput ); + d->mediaObject = new Phonon::MediaObject( this ); + d->audioOutput = new Phonon::AudioOutput( Phonon::MusicCategory, this ); + Phonon::createPath( d->mediaObject, d->audioOutput ); - m_mediaObject->setTickInterval( 150 ); - connect( m_mediaObject, SIGNAL( stateChanged( Phonon::State, Phonon::State ) ), SLOT( onStateChanged( Phonon::State, Phonon::State ) ) ); - connect( m_mediaObject, SIGNAL( tick( qint64 ) ), SLOT( timerTriggered( qint64 ) ) ); - connect( m_mediaObject, SIGNAL( aboutToFinish() ), SLOT( onAboutToFinish() ) ); + d->mediaObject->setTickInterval( 150 ); + connect( d->mediaObject, SIGNAL( stateChanged( Phonon::State, Phonon::State ) ), d_func(), SLOT( onStateChanged( Phonon::State, Phonon::State ) ) ); + connect( d->mediaObject, SIGNAL( tick( qint64 ) ), SLOT( timerTriggered( qint64 ) ) ); + connect( d->mediaObject, SIGNAL( aboutToFinish() ), SLOT( onAboutToFinish() ) ); - connect( m_audioOutput, SIGNAL( volumeChanged( qreal ) ), SLOT( onVolumeChanged( qreal ) ) ); + connect( d->audioOutput, SIGNAL( volumeChanged( qreal ) ), SLOT( onVolumeChanged( qreal ) ) ); - m_stateQueueTimer.setInterval( 5000 ); - m_stateQueueTimer.setSingleShot( true ); - connect( &m_stateQueueTimer, SIGNAL( timeout() ), SLOT( queueStateSafety() ) ); + d->stateQueueTimer.setInterval( 5000 ); + d->stateQueueTimer.setSingleShot( true ); + connect( &d->stateQueueTimer, SIGNAL( timeout() ), SLOT( queueStateSafety() ) ); - onVolumeChanged( m_audioOutput->volume() ); + onVolumeChanged( d->audioOutput->volume() ); setVolume( TomahawkSettings::instance()->volume() ); } @@ -103,23 +210,26 @@ AudioEngine::~AudioEngine() { tDebug() << Q_FUNC_INFO; - m_mediaObject->stop(); + d_func()->mediaObject->stop(); TomahawkSettings::instance()->setVolume( volume() ); + + + delete d_ptr; } QStringList AudioEngine::supportedMimeTypes() const { - if ( m_supportedMimeTypes.isEmpty() ) + if ( d_func()->supportedMimeTypes.isEmpty() ) { - m_supportedMimeTypes = Phonon::BackendCapabilities::availableMimeTypes(); - m_supportedMimeTypes << "audio/basic"; + d_func()->supportedMimeTypes = Phonon::BackendCapabilities::availableMimeTypes(); + d_func()->supportedMimeTypes << "audio/basic"; - return m_supportedMimeTypes; + return d_func()->supportedMimeTypes; } else - return m_supportedMimeTypes; + return d_func()->supportedMimeTypes; } @@ -136,6 +246,8 @@ AudioEngine::playPause() void AudioEngine::play() { + Q_D( AudioEngine ); + tDebug( LOGEXTRA ) << Q_FUNC_INFO; if ( isPaused() ) @@ -147,7 +259,7 @@ AudioEngine::play() } else { - if ( !m_currentTrack && m_playlist && m_playlist->nextResult() ) + if ( !d->currentTrack && d->playlist && d->playlist->nextResult() ) { loadNextTrack(); } @@ -172,6 +284,8 @@ AudioEngine::pause() void AudioEngine::stop( AudioErrorCode errorCode ) { + Q_D( AudioEngine ); + tDebug() << Q_FUNC_INFO << errorCode << isStopped(); if ( isStopped() ) @@ -182,19 +296,19 @@ AudioEngine::stop( AudioErrorCode errorCode ) else setState( Error ); - if ( m_mediaObject->state() != Phonon::StoppedState ) - m_mediaObject->stop(); + if ( d->mediaObject->state() != Phonon::StoppedState ) + d->mediaObject->stop(); emit stopped(); - if ( !m_playlist.isNull() ) - m_playlist.data()->reset(); - if ( !m_currentTrack.isNull() ) - emit timerPercentage( ( (double)m_timeElapsed / (double)m_currentTrack->track()->duration() ) * 100.0 ); + if ( !d->playlist.isNull() ) + d->playlist.data()->reset(); + if ( !d->currentTrack.isNull() ) + emit timerPercentage( ( (double)d->timeElapsed / (double)d->currentTrack->track()->duration() ) * 100.0 ); setCurrentTrack( Tomahawk::result_ptr() ); - if ( m_waitingOnNewTrack ) + if ( d->waitingOnNewTrack ) sendWaitingNotification(); Tomahawk::InfoSystem::InfoPushData pushData( s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowStopped, QVariant(), Tomahawk::InfoSystem::PushNoFlag ); @@ -225,22 +339,24 @@ AudioEngine::next() bool AudioEngine::canGoNext() { + Q_D( AudioEngine ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; - if ( m_queue && m_queue->trackCount() ) + if ( d->queue && d->queue->trackCount() ) return true; - if ( m_playlist.isNull() ) + if ( d->playlist.isNull() ) return false; - if ( m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkip || - m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkipForwards ) + if ( d->playlist.data()->skipRestrictions() == PlaylistModes::NoSkip || + d->playlist.data()->skipRestrictions() == PlaylistModes::NoSkipForwards ) { return false; } - if ( !m_currentTrack.isNull() && !m_playlist->hasNextResult() && - ( m_playlist->currentItem().isNull() || ( m_currentTrack->id() == m_playlist->currentItem()->id() ) ) ) + if ( !d->currentTrack.isNull() && !d->playlist->hasNextResult() && + ( d->playlist->currentItem().isNull() || ( d->currentTrack->id() == d->playlist->currentItem()->id() ) ) ) { //For instance, when doing a catch-up while listening along, but the person //you're following hasn't started a new track yet...don't do anything @@ -248,44 +364,50 @@ AudioEngine::canGoNext() return false; } - return ( m_currentTrack && m_playlist.data()->hasNextResult() && - !m_playlist.data()->nextResult().isNull() && - m_playlist.data()->nextResult()->isOnline() ); + return ( d->currentTrack && d->playlist.data()->hasNextResult() && + !d->playlist.data()->nextResult().isNull() && + d->playlist.data()->nextResult()->isOnline() ); } bool AudioEngine::canGoPrevious() { - if ( m_playlist.isNull() ) + Q_D( AudioEngine ); + + if ( d->playlist.isNull() ) return false; - if ( m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkip || - m_playlist.data()->skipRestrictions() == PlaylistModes::NoSkipBackwards ) + if ( d->playlist.data()->skipRestrictions() == PlaylistModes::NoSkip || + d->playlist.data()->skipRestrictions() == PlaylistModes::NoSkipBackwards ) return false; - return ( m_currentTrack && m_playlist.data()->hasPreviousResult() && m_playlist.data()->previousResult()->isOnline() ); + return ( d->currentTrack && d->playlist.data()->hasPreviousResult() && d->playlist.data()->previousResult()->isOnline() ); } bool AudioEngine::canSeek() { + Q_D( AudioEngine ); + bool phononCanSeek = true; /* TODO: When phonon properly reports this, re-enable it - if ( m_mediaObject && m_mediaObject->isValid() ) - phononCanSeek = m_mediaObject->isSeekable(); + if ( d->mediaObject && d->mediaObject->isValid() ) + phononCanSeek = d->mediaObject->isSeekable(); */ - if ( m_playlist.isNull() ) + if ( d->playlist.isNull() ) return phononCanSeek; - return !m_playlist.isNull() && ( m_playlist.data()->seekRestrictions() != PlaylistModes::NoSeek ) && phononCanSeek; + return !d->playlist.isNull() && ( d->playlist.data()->seekRestrictions() != PlaylistModes::NoSeek ) && phononCanSeek; } void AudioEngine::seek( qint64 ms ) { + Q_D( AudioEngine ); + if ( !canSeek() ) { tDebug( LOGEXTRA ) << "Could not seek!"; @@ -295,7 +417,7 @@ AudioEngine::seek( qint64 ms ) if ( isPlaying() || isPaused() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << ms; - m_mediaObject->seek( ms ); + d->mediaObject->seek( ms ); emit seeked( ms ); } } @@ -311,10 +433,12 @@ AudioEngine::seek( int ms ) void AudioEngine::setVolume( int percentage ) { + Q_D( AudioEngine ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << percentage; percentage = qBound( 0, percentage, 100 ); - m_audioOutput->setVolume( (qreal)percentage / 100.0 ); + d->audioOutput->setVolume( (qreal)percentage / 100.0 ); emit volumeChanged( percentage ); } @@ -345,7 +469,7 @@ AudioEngine::sendWaitingNotification() const { tDebug( LOGVERBOSE ) << Q_FUNC_INFO; //since it's async, after this is triggered our result could come in, so don't show the popup in that case - if ( m_playlist && m_playlist->nextResult() && m_playlist->nextResult()->isOnline() ) + if ( d_func()->playlist && d_func()->playlist->nextResult() && d_func()->playlist->nextResult()->isOnline() ) return; Tomahawk::InfoSystem::InfoPushData pushData ( @@ -360,18 +484,20 @@ AudioEngine::sendWaitingNotification() const void AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType type ) { - if ( m_currentTrack.isNull() ) + Q_D( AudioEngine ); + + if ( d->currentTrack.isNull() ) return; #ifndef ENABLE_HEADLESS - if ( m_currentTrack->track()->coverLoaded() ) + if ( d->currentTrack->track()->coverLoaded() ) { onNowPlayingInfoReady( type ); } else { - NewClosure( m_currentTrack->track().data(), SIGNAL( coverChanged() ), const_cast< AudioEngine* >( this ), SLOT( sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ) ), type ); - m_currentTrack->track()->cover( QSize( 0, 0 ), true ); + NewClosure( d->currentTrack->track().data(), SIGNAL( coverChanged() ), const_cast< AudioEngine* >( this ), SLOT( sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ) ), type ); + d->currentTrack->track()->cover( QSize( 0, 0 ), true ); } #endif } @@ -380,32 +506,34 @@ AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ty void AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) { - if ( m_currentTrack.isNull() || - m_currentTrack->track()->artist().isEmpty() ) + Q_D( AudioEngine ); + + if ( d->currentTrack.isNull() || + d->currentTrack->track()->artist().isEmpty() ) return; QVariantMap playInfo; #ifndef ENABLE_HEADLESS QImage cover; - cover = m_currentTrack->track()->cover( QSize( 0, 0 ) ).toImage(); + cover = d->currentTrack->track()->cover( QSize( 0, 0 ) ).toImage(); if ( !cover.isNull() ) { playInfo["cover"] = cover; - delete m_coverTempFile; - m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->track()->artist() + "_" + m_currentTrack->track()->album() + "_tomahawk_cover.png" ) ); - if ( !m_coverTempFile->open() ) + delete d->coverTempFile; + d->coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + d->currentTrack->track()->artist() + "_" + d->currentTrack->track()->album() + "_tomahawk_cover.png" ) ); + if ( !d->coverTempFile->open() ) { tDebug() << Q_FUNC_INFO << "WARNING: could not write temporary file for cover art!"; } else { // Finally, save the image to the new temp file - if ( cover.save( m_coverTempFile, "PNG" ) ) + if ( cover.save( d->coverTempFile, "PNG" ) ) { - tDebug() << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *m_coverTempFile ).absoluteFilePath(); - playInfo["coveruri"] = QFileInfo( *m_coverTempFile ).absoluteFilePath(); + tDebug() << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *d->coverTempFile ).absoluteFilePath(); + playInfo["coveruri"] = QFileInfo( *d->coverTempFile ).absoluteFilePath(); } else tDebug() << Q_FUNC_INFO << "Failed to save cover image!"; @@ -416,11 +544,11 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) #endif Tomahawk::InfoSystem::InfoStringHash trackInfo; - trackInfo["title"] = m_currentTrack->track()->track(); - trackInfo["artist"] = m_currentTrack->track()->artist(); - trackInfo["album"] = m_currentTrack->track()->album(); - trackInfo["duration"] = QString::number( m_currentTrack->track()->duration() ); - trackInfo["albumpos"] = QString::number( m_currentTrack->track()->albumpos() ); + trackInfo["title"] = d->currentTrack->track()->track(); + trackInfo["artist"] = d->currentTrack->track()->artist(); + trackInfo["album"] = d->currentTrack->track()->album(); + trackInfo["duration"] = QString::number( d->currentTrack->track()->duration() ); + trackInfo["albumpos"] = QString::number( d->currentTrack->track()->albumpos() ); playInfo["trackinfo"] = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ); playInfo["private"] = TomahawkSettings::instance()->privateListeningMode(); @@ -433,6 +561,8 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) void AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) { + Q_D( AudioEngine ); + if ( result.isNull() ) { stop(); @@ -441,12 +571,12 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) setCurrentTrack( result ); - if ( !TomahawkUtils::isHttpResult( m_currentTrack->url() ) && - !TomahawkUtils::isLocalResult( m_currentTrack->url() ) ) + if ( !TomahawkUtils::isHttpResult( d->currentTrack->url() ) && + !TomahawkUtils::isLocalResult( d->currentTrack->url() ) ) { boost::function< void ( QSharedPointer< QIODevice >& ) > callback = boost::bind( &AudioEngine::performLoadTrack, this, result, _1 ); - Servent::instance()->getIODeviceForUrl( m_currentTrack, callback ); + Servent::instance()->getIODeviceForUrl( d->currentTrack, callback ); } else { @@ -459,10 +589,12 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) void AudioEngine::performLoadTrack( const Tomahawk::result_ptr& result, QSharedPointer< QIODevice >& io ) { + Q_D( AudioEngine ); + bool err = false; { - if ( !TomahawkUtils::isHttpResult( m_currentTrack->url() ) && - !TomahawkUtils::isLocalResult( m_currentTrack->url() ) && + if ( !TomahawkUtils::isHttpResult( d->currentTrack->url() ) && + !TomahawkUtils::isLocalResult( d->currentTrack->url() ) && ( !io || io.isNull() ) ) { tLog() << "Error getting iodevice for" << result->url(); @@ -471,57 +603,57 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr& result, QSharedPointe if ( !err ) { - tLog() << "Starting new song:" << m_currentTrack->url(); - m_state = Loading; - emit loading( m_currentTrack ); + tLog() << "Starting new song:" << d->currentTrack->url(); + d->state = Loading; + emit loading( d->currentTrack ); - if ( !TomahawkUtils::isHttpResult( m_currentTrack->url() ) && - !TomahawkUtils::isLocalResult( m_currentTrack->url() ) ) + if ( !TomahawkUtils::isHttpResult( d->currentTrack->url() ) && + !TomahawkUtils::isLocalResult( d->currentTrack->url() ) ) { if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) ) - m_mediaObject->setCurrentSource( new QNR_IODeviceStream( qnr_io, this ) ); + d->mediaObject->setCurrentSource( new QNR_IODeviceStream( qnr_io, this ) ); else - m_mediaObject->setCurrentSource( io.data() ); - m_mediaObject->currentSource().setAutoDelete( false ); + d->mediaObject->setCurrentSource( io.data() ); + d->mediaObject->currentSource().setAutoDelete( false ); } else { - if ( !TomahawkUtils::isLocalResult( m_currentTrack->url() ) ) + if ( !TomahawkUtils::isLocalResult( d->currentTrack->url() ) ) { - QUrl furl = m_currentTrack->url(); - if ( m_currentTrack->url().contains( "?" ) ) + QUrl furl = d->currentTrack->url(); + if ( d->currentTrack->url().contains( "?" ) ) { - furl = QUrl( m_currentTrack->url().left( m_currentTrack->url().indexOf( '?' ) ) ); - TomahawkUtils::urlSetQuery( furl, QString( m_currentTrack->url().mid( m_currentTrack->url().indexOf( '?' ) + 1 ) ) ); + furl = QUrl( d->currentTrack->url().left( d->currentTrack->url().indexOf( '?' ) ) ); + TomahawkUtils::urlSetQuery( furl, QString( d->currentTrack->url().mid( d->currentTrack->url().indexOf( '?' ) + 1 ) ) ); } tLog( LOGVERBOSE ) << "Passing to Phonon:" << furl; - m_mediaObject->setCurrentSource( furl ); + d->mediaObject->setCurrentSource( furl ); } else { - QString furl = m_currentTrack->url(); + QString furl = d->currentTrack->url(); if ( furl.startsWith( "file://" ) ) furl = furl.right( furl.length() - 7 ); tLog( LOGVERBOSE ) << "Passing to Phonon:" << QUrl::fromLocalFile( furl ); - m_mediaObject->setCurrentSource( QUrl::fromLocalFile( furl ) ); + d->mediaObject->setCurrentSource( QUrl::fromLocalFile( furl ) ); } - m_mediaObject->currentSource().setAutoDelete( true ); + d->mediaObject->currentSource().setAutoDelete( true ); } - if ( !m_input.isNull() ) + if ( !d->input.isNull() ) { - m_input->close(); - m_input.clear(); + d->input->close(); + d->input.clear(); } - m_input = io; + d->input = io; queueState( Playing ); if ( TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::FullyPrivate ) { - m_currentTrack->track()->startPlaying(); + d->currentTrack->track()->startPlaying(); } sendNowPlayingNotification( Tomahawk::InfoSystem::InfoNowPlaying ); @@ -534,7 +666,7 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr& result, QSharedPointe return; } - m_waitingOnNewTrack = false; + d->waitingOnNewTrack = false; return; } @@ -542,19 +674,21 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr& result, QSharedPointe void AudioEngine::loadPreviousTrack() { + Q_D( AudioEngine ); + tDebug( LOGEXTRA ) << Q_FUNC_INFO; - if ( m_playlist.isNull() ) + if ( d->playlist.isNull() ) { stop(); return; } Tomahawk::result_ptr result; - if ( m_playlist.data()->previousResult() ) + if ( d->playlist.data()->previousResult() ) { - result = m_playlist.data()->setSiblingResult( -1 ); - m_currentTrackPlaylist = m_playlist; + result = d->playlist.data()->setSiblingResult( -1 ); + d->currentTrackPlaylist = d->playlist; } if ( !result.isNull() ) @@ -567,35 +701,37 @@ AudioEngine::loadPreviousTrack() void AudioEngine::loadNextTrack() { + Q_D( AudioEngine ); + tDebug( LOGEXTRA ) << Q_FUNC_INFO; Tomahawk::result_ptr result; - if ( m_stopAfterTrack && m_currentTrack ) + if ( d->stopAfterTrack && d->currentTrack ) { - if ( m_stopAfterTrack->track()->equals( m_currentTrack->track() ) ) + if ( d->stopAfterTrack->track()->equals( d->currentTrack->track() ) ) { - m_stopAfterTrack.clear(); + d->stopAfterTrack.clear(); stop(); return; } } - if ( m_queue && m_queue->trackCount() ) + if ( d->queue && d->queue->trackCount() ) { - query_ptr query = m_queue->tracks().first(); + query_ptr query = d->queue->tracks().first(); if ( query && query->numResults() ) result = query->results().first(); } - if ( !m_playlist.isNull() && result.isNull() ) + if ( !d->playlist.isNull() && result.isNull() ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Loading playlist's next item" << m_playlist.data() << m_playlist->shuffled(); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Loading playlist's next item" << d->playlist.data() << d->playlist->shuffled(); - if ( m_playlist.data()->nextResult() ) + if ( d->playlist.data()->nextResult() ) { - result = m_playlist.data()->setSiblingResult( 1 ); - m_currentTrackPlaylist = m_playlist; + result = d->playlist.data()->setSiblingResult( 1 ); + d->currentTrackPlaylist = d->playlist; } } @@ -606,8 +742,8 @@ AudioEngine::loadNextTrack() } else { - if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistModes::Retry ) - m_waitingOnNewTrack = true; + if ( !d->playlist.isNull() && d->playlist.data()->retryMode() == Tomahawk::PlaylistModes::Retry ) + d->waitingOnNewTrack = true; stop(); } @@ -617,25 +753,27 @@ AudioEngine::loadNextTrack() void AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::result_ptr& result, const Tomahawk::query_ptr& fromQuery ) { + Q_D( AudioEngine ); + tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() ); - if ( !m_playlist.isNull() ) - m_playlist.data()->reset(); + if ( !d->playlist.isNull() ) + d->playlist.data()->reset(); setPlaylist( playlist ); if ( playlist.isNull() && !fromQuery.isNull() ) - m_currentTrackPlaylist = playlistinterface_ptr( new SingleTrackPlaylistInterface( fromQuery ) ); + d->currentTrackPlaylist = playlistinterface_ptr( new SingleTrackPlaylistInterface( fromQuery ) ); else - m_currentTrackPlaylist = playlist; + d->currentTrackPlaylist = playlist; if ( !result.isNull() ) { loadTrack( result ); } - else if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == PlaylistModes::Retry ) + else if ( !d->playlist.isNull() && d->playlist.data()->retryMode() == PlaylistModes::Retry ) { - m_waitingOnNewTrack = true; + d->waitingOnNewTrack = true; if ( isStopped() ) emit sendWaitingNotification(); else @@ -726,21 +864,23 @@ AudioEngine::playItem( const Tomahawk::album_ptr& album ) void AudioEngine::onPlaylistNextTrackAvailable() { + Q_D( AudioEngine ); + tDebug() << Q_FUNC_INFO; { // If in real-time and you have a few seconds left, you're probably lagging -- finish it up - if ( m_playlist && m_playlist->latchMode() == PlaylistModes::RealTime && ( m_waitingOnNewTrack || m_currentTrack.isNull() || m_currentTrack->id() == 0 || ( currentTrackTotalTime() - currentTime() > 6000 ) ) ) + if ( d->playlist && d->playlist->latchMode() == PlaylistModes::RealTime && ( d->waitingOnNewTrack || d->currentTrack.isNull() || d->currentTrack->id() == 0 || ( currentTrackTotalTime() - currentTime() > 6000 ) ) ) { - m_waitingOnNewTrack = false; + d->waitingOnNewTrack = false; loadNextTrack(); return; } - if ( !m_waitingOnNewTrack ) + if ( !d->waitingOnNewTrack ) return; - m_waitingOnNewTrack = false; + d->waitingOnNewTrack = false; loadNextTrack(); } } @@ -750,134 +890,30 @@ void AudioEngine::onAboutToFinish() { tDebug( LOGVERBOSE ) << Q_FUNC_INFO; - m_expectStop = true; + d_func()->expectStop = true; } - -void -AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState ) -{ - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << oldState << newState << m_expectStop << state(); - - if ( newState == Phonon::LoadingState ) - { - // We don't emit this state to listeners - yet. - m_state = Loading; - } - if ( newState == Phonon::BufferingState ) - { - if ( m_underrunCount > UNDERRUNTHRESHOLD && !m_underrunNotified ) - { - m_underrunNotified = true; - //FIXME: Actually notify - } - else - m_underrunCount++; - } - if ( newState == Phonon::ErrorState ) - { - stop( UnknownError ); - - tDebug() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType(); - - emit error( UnknownError ); - setState( Error ); - } - if ( newState == Phonon::PlayingState ) - { - if ( state() != Paused && state() != Playing ) - { - m_underrunCount = 0; - m_underrunNotified = false; - emit started( m_currentTrack ); - } - - setState( Playing ); - } - if ( newState == Phonon::StoppedState && oldState == Phonon::PausedState ) - { - // GStreamer backend hack: instead of going from PlayingState to StoppedState, it traverses PausedState - setState( Stopped ); - } - - if ( oldState == Phonon::PlayingState ) - { - bool stopped = false; - switch ( newState ) - { - case Phonon::PausedState: - { - if ( m_mediaObject && m_currentTrack ) - { - qint64 duration = m_mediaObject->totalTime() > 0 ? m_mediaObject->totalTime() : m_currentTrack->track()->duration() * 1000; - stopped = ( duration - 1000 < m_mediaObject->currentTime() ); - } - else - stopped = true; - - if ( !stopped ) - setState( Paused ); - - break; - } - case Phonon::StoppedState: - { - stopped = true; - break; - } - default: - break; - } - - if ( stopped && m_expectStop ) - { - m_expectStop = false; - tDebug( LOGVERBOSE ) << "Finding next track."; - if ( canGoNext() ) - { - loadNextTrack(); - } - else - { - if ( !m_playlist.isNull() && m_playlist.data()->retryMode() == Tomahawk::PlaylistModes::Retry ) - m_waitingOnNewTrack = true; - - stop(); - } - } - } - - if ( newState == Phonon::PausedState || newState == Phonon::PlayingState || newState == Phonon::ErrorState ) - { - tDebug( LOGVERBOSE ) << "Phonon state now:" << newState; - if ( m_stateQueue.count() ) - { - /*/ AudioState qState = */ m_stateQueue.dequeue(); - checkStateQueue(); - } - } -} - - void AudioEngine::timerTriggered( qint64 time ) { + Q_D( AudioEngine ); + emit timerMilliSeconds( time ); - if ( m_timeElapsed != time / 1000 ) + if ( d->timeElapsed != time / 1000 ) { - m_timeElapsed = time / 1000; - emit timerSeconds( m_timeElapsed ); + d->timeElapsed = time / 1000; + emit timerSeconds( d->timeElapsed ); - if ( !m_currentTrack.isNull() ) + if ( !d->currentTrack.isNull() ) { - if ( m_currentTrack->track()->duration() == 0 ) + if ( d->currentTrack->track()->duration() == 0 ) { emit timerPercentage( 0 ); } else { - emit timerPercentage( ( (double)m_timeElapsed / (double)m_currentTrack->track()->duration() ) * 100.0 ); + emit timerPercentage( ( (double) d->timeElapsed / (double) d->currentTrack->track()->duration() ) * 100.0 ); } } } @@ -887,18 +923,20 @@ AudioEngine::timerTriggered( qint64 time ) void AudioEngine::setQueue( const playlistinterface_ptr& queue ) { - if ( m_queue ) + Q_D( AudioEngine ); + + if ( d->queue ) { - disconnect( m_queue.data(), SIGNAL( previousTrackAvailable( bool ) ), this, SIGNAL( controlStateChanged() ) ); - disconnect( m_queue.data(), SIGNAL( nextTrackAvailable( bool ) ), this, SIGNAL( controlStateChanged() ) ); + disconnect( d->queue.data(), SIGNAL( previousTrackAvailable( bool ) ), this, SIGNAL( controlStateChanged() ) ); + disconnect( d->queue.data(), SIGNAL( nextTrackAvailable( bool ) ), this, SIGNAL( controlStateChanged() ) ); } - m_queue = queue; + d->queue = queue; - if ( m_queue ) + if ( d->queue ) { - connect( m_queue.data(), SIGNAL( previousTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); - connect( m_queue.data(), SIGNAL( nextTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); + connect( d->queue.data(), SIGNAL( previousTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); + connect( d->queue.data(), SIGNAL( nextTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); } } @@ -906,44 +944,46 @@ AudioEngine::setQueue( const playlistinterface_ptr& queue ) void AudioEngine::setPlaylist( Tomahawk::playlistinterface_ptr playlist ) { - if ( m_playlist == playlist ) + Q_D( AudioEngine ); + + if ( d->playlist == playlist ) return; - if ( !m_playlist.isNull() ) + if ( !d->playlist.isNull() ) { - if ( m_playlist.data() ) + if ( d->playlist.data() ) { - disconnect( m_playlist.data(), SIGNAL( previousTrackAvailable( bool ) ) ); - disconnect( m_playlist.data(), SIGNAL( nextTrackAvailable( bool ) ) ); - disconnect( m_playlist.data(), SIGNAL( shuffleModeChanged( bool ) ) ); - disconnect( m_playlist.data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); + disconnect( d->playlist.data(), SIGNAL( previousTrackAvailable( bool ) ) ); + disconnect( d->playlist.data(), SIGNAL( nextTrackAvailable( bool ) ) ); + disconnect( d->playlist.data(), SIGNAL( shuffleModeChanged( bool ) ) ); + disconnect( d->playlist.data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); } - m_playlist.data()->reset(); + d->playlist.data()->reset(); } if ( playlist.isNull() ) { - m_playlist.clear(); + d->playlist.clear(); emit playlistChanged( playlist ); return; } - m_playlist = playlist; - m_stopAfterTrack.clear(); + d->playlist = playlist; + d->stopAfterTrack.clear(); - if ( !m_playlist.isNull() ) + if ( !d->playlist.isNull() ) { - connect( m_playlist.data(), SIGNAL( nextTrackAvailable( bool ) ), SLOT( onPlaylistNextTrackAvailable() ) ); + connect( d->playlist.data(), SIGNAL( nextTrackAvailable( bool ) ), SLOT( onPlaylistNextTrackAvailable() ) ); - connect( m_playlist.data(), SIGNAL( previousTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); - connect( m_playlist.data(), SIGNAL( nextTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); + connect( d->playlist.data(), SIGNAL( previousTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); + connect( d->playlist.data(), SIGNAL( nextTrackAvailable( bool ) ), SIGNAL( controlStateChanged() ) ); - connect( m_playlist.data(), SIGNAL( shuffleModeChanged( bool ) ), SIGNAL( shuffleModeChanged( bool ) ) ); - connect( m_playlist.data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); + connect( d->playlist.data(), SIGNAL( shuffleModeChanged( bool ) ), SIGNAL( shuffleModeChanged( bool ) ) ); + connect( d->playlist.data(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ), SIGNAL( repeatModeChanged( Tomahawk::PlaylistModes::RepeatMode ) ) ); - emit shuffleModeChanged( m_playlist.data()->shuffled() ); - emit repeatModeChanged( m_playlist.data()->repeatMode() ); + emit shuffleModeChanged( d->playlist.data()->shuffled() ); + emit repeatModeChanged( d->playlist.data()->repeatMode() ); } emit playlistChanged( playlist ); @@ -953,9 +993,11 @@ AudioEngine::setPlaylist( Tomahawk::playlistinterface_ptr playlist ) void AudioEngine::setRepeatMode( Tomahawk::PlaylistModes::RepeatMode mode ) { - if ( !m_playlist.isNull() ) + Q_D( AudioEngine ); + + if ( !d->playlist.isNull() ) { - m_playlist.data()->setRepeatMode( mode ); + d->playlist.data()->setRepeatMode( mode ); } } @@ -963,9 +1005,11 @@ AudioEngine::setRepeatMode( Tomahawk::PlaylistModes::RepeatMode mode ) void AudioEngine::setShuffled( bool enabled ) { - if ( !m_playlist.isNull() ) + Q_D( AudioEngine ); + + if ( !d->playlist.isNull() ) { - m_playlist.data()->setShuffled( enabled ); + d->playlist.data()->setShuffled( enabled ); } } @@ -973,9 +1017,11 @@ AudioEngine::setShuffled( bool enabled ) void AudioEngine::setStopAfterTrack( const query_ptr& query ) { - if ( m_stopAfterTrack != query ) + Q_D( AudioEngine ); + + if ( d->stopAfterTrack != query ) { - m_stopAfterTrack = query; + d->stopAfterTrack = query; emit stopAfterTrackChanged(); } } @@ -984,23 +1030,25 @@ AudioEngine::setStopAfterTrack( const query_ptr& query ) void AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result ) { - if ( !m_currentTrack.isNull() ) + Q_D( AudioEngine ); + + if ( !d->currentTrack.isNull() ) { - if ( m_state != Error && TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening ) + if ( d->state != Error && TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::PublicListening ) { - m_currentTrack->track()->finishPlaying( m_timeElapsed ); + d->currentTrack->track()->finishPlaying( d->timeElapsed ); } - emit finished( m_currentTrack ); + emit finished( d->currentTrack ); } - m_currentTrack = result; + d->currentTrack = result; if ( result ) { - if ( m_playlist && m_playlist->currentItem() != result ) + if ( d->playlist && d->playlist->currentItem() != result ) { - m_playlist->setCurrentIndex( m_playlist->indexOfResult( result ) ); + d->playlist->setCurrentIndex( d->playlist->indexOfResult( result ) ); } } } @@ -1009,21 +1057,23 @@ AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result ) void AudioEngine::checkStateQueue() { - if ( m_stateQueue.count() ) + Q_D( AudioEngine ); + + if ( d->stateQueue.count() ) { - AudioState state = m_stateQueue.head(); + AudioState state = (AudioState) d->stateQueue.head(); tDebug( LOGVERBOSE ) << "Applying state command:" << state; switch ( state ) { case Playing: { - m_mediaObject->play(); + d->mediaObject->play(); break; } case Paused: { - m_mediaObject->pause(); + d->mediaObject->pause(); break; } @@ -1039,34 +1089,121 @@ AudioEngine::checkStateQueue() void AudioEngine::queueStateSafety() { + Q_D( AudioEngine ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; - m_stateQueue.clear(); + d->stateQueue.clear(); } void AudioEngine::queueState( AudioState state ) { - if ( m_stateQueueTimer.isActive() ) - m_stateQueueTimer.stop(); + Q_D( AudioEngine ); + + if ( d->stateQueueTimer.isActive() ) + d->stateQueueTimer.stop(); - tDebug( LOGVERBOSE ) << "Enqueuing state command:" << state << m_stateQueue.count(); - m_stateQueue.enqueue( state ); + tDebug( LOGVERBOSE ) << "Enqueuing state command:" << state << d->stateQueue.count(); + d->stateQueue.enqueue( state ); - if ( m_stateQueue.count() == 1 ) + if ( d->stateQueue.count() == 1 ) { checkStateQueue(); } - m_stateQueueTimer.start(); + d->stateQueueTimer.start(); } void AudioEngine::setState( AudioState state ) { - AudioState oldState = m_state; - m_state = state; + Q_D( AudioEngine ); + + AudioState oldState = (AudioState) d->state; + d->state = state; emit stateChanged( state, oldState ); } + + +qint64 +AudioEngine::currentTime() const +{ + return d_func()->mediaObject->currentTime(); +} + + +qint64 +AudioEngine::currentTrackTotalTime() const +{ + return d_func()->mediaObject->totalTime(); +} + + +unsigned int +AudioEngine::volume() const +{ + return d_func()->audioOutput->volume() * 100.0; +} + + +AudioEngine::AudioState +AudioEngine::state() const +{ + return (AudioState) d_func()->state; +} + + +bool +AudioEngine::isPlaying() const +{ + return d_func()->state == Playing; +} + + +bool +AudioEngine::isPaused() const +{ + return d_func()->state == Paused; +} + + +bool +AudioEngine::isStopped() const +{ + return d_func()->state == Stopped; +} + + +playlistinterface_ptr +AudioEngine::currentTrackPlaylist() const +{ + return d_func()->currentTrackPlaylist; +} + + +playlistinterface_ptr +AudioEngine::playlist() const +{ + return d_func()->playlist; +} + + +result_ptr AudioEngine::currentTrack() const +{ + return d_func()->currentTrack; +} + + +query_ptr AudioEngine::stopAfterTrack() const +{ + return d_func()->stopAfterTrack; +} + + +void +AudioEngine::onVolumeChanged(qreal volume) { + emit volumeChanged( volume * 100 ); +} diff --git a/src/libtomahawk/audio/AudioEngine.h b/src/libtomahawk/audio/AudioEngine.h index 444fef465a..0e052f0a76 100644 --- a/src/libtomahawk/audio/AudioEngine.h +++ b/src/libtomahawk/audio/AudioEngine.h @@ -21,22 +21,13 @@ #ifndef AUDIOENGINE_H #define AUDIOENGINE_H -#include "infosystem/InfoSystem.h" -#include "Typedefs.h" -#include "Result.h" -#include "PlaylistInterface.h" +#include "../Typedefs.h" -#include "DllMacro.h" - -#include -#include -#include +#include -#include -#include -#include -#include +#include "DllMacro.h" +class AudioEnginePrivate; class DLLEXPORT AudioEngine : public QObject { @@ -52,24 +43,24 @@ Q_OBJECT ~AudioEngine(); QStringList supportedMimeTypes() const; - unsigned int volume() const { return m_audioOutput->volume() * 100.0; } // in percent + unsigned int volume() const; // in percent - AudioState state() const { return m_state; } - bool isPlaying() const { return m_state == Playing; } - bool isPaused() const { return m_state == Paused; } - bool isStopped() const { return m_state == Stopped; } + AudioState state() const; + bool isPlaying() const; + bool isPaused() const; + bool isStopped() const; /* Returns the PlaylistInterface of the currently playing track. Note: This might be different to the current playlist! */ - Tomahawk::playlistinterface_ptr currentTrackPlaylist() const { return m_currentTrackPlaylist; } + Tomahawk::playlistinterface_ptr currentTrackPlaylist() const; /* Returns the PlaylistInterface of the current playlist. Note: The currently playing track might still be from a different playlist! */ - Tomahawk::playlistinterface_ptr playlist() const { return m_playlist; } + Tomahawk::playlistinterface_ptr playlist() const; - Tomahawk::result_ptr currentTrack() const { return m_currentTrack; } - Tomahawk::query_ptr stopAfterTrack() const { return m_stopAfterTrack; } + Tomahawk::result_ptr currentTrack() const; + Tomahawk::query_ptr stopAfterTrack() const; - qint64 currentTime() const { return m_mediaObject->currentTime(); } - qint64 currentTrackTotalTime() const { return m_mediaObject->totalTime(); } + qint64 currentTime() const; + qint64 currentTrackTotalTime() const; public slots: void playPause(); @@ -136,8 +127,7 @@ private slots: void loadNextTrack(); void onAboutToFinish(); - void onStateChanged( Phonon::State newState, Phonon::State oldState ); - void onVolumeChanged( qreal volume ) { emit volumeChanged( volume * 100 ); } + void onVolumeChanged( qreal volume ); void timerTriggered( qint64 time ); void setCurrentTrack( const Tomahawk::result_ptr& result ); @@ -152,36 +142,10 @@ private slots: private: void checkStateQueue(); void queueState( AudioState state ); - void setState( AudioState state ); - QSharedPointer m_input; - - Tomahawk::query_ptr m_stopAfterTrack; - Tomahawk::result_ptr m_currentTrack; - Tomahawk::playlistinterface_ptr m_playlist; - Tomahawk::playlistinterface_ptr m_currentTrackPlaylist; - Tomahawk::playlistinterface_ptr m_queue; - - Phonon::MediaObject* m_mediaObject; - Phonon::AudioOutput* m_audioOutput; - - unsigned int m_timeElapsed; - bool m_expectStop; - bool m_waitingOnNewTrack; - - mutable QStringList m_supportedMimeTypes; - - AudioState m_state; - QQueue< AudioState > m_stateQueue; - QTimer m_stateQueueTimer; - - uint_fast8_t m_underrunCount; - bool m_underrunNotified; - - QTemporaryFile* m_coverTempFile; - - static AudioEngine* s_instance; + Q_DECLARE_PRIVATE( AudioEngine ); + AudioEnginePrivate* d_ptr; }; #endif // AUDIOENGINE_H From 45b63409527587638b7d2540aaf0b0b7a9c3ff98 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sat, 25 May 2013 15:02:08 +0200 Subject: [PATCH 096/565] Forgotten files are forgotten --- src/libtomahawk/audio/AudioEngine_p.h | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/libtomahawk/audio/AudioEngine_p.h diff --git a/src/libtomahawk/audio/AudioEngine_p.h b/src/libtomahawk/audio/AudioEngine_p.h new file mode 100644 index 0000000000..f0e2be2454 --- /dev/null +++ b/src/libtomahawk/audio/AudioEngine_p.h @@ -0,0 +1,55 @@ + +#include +#include +#include + +#include +#include +#include +#include + +class AudioEnginePrivate : public QObject +{ +Q_OBJECT + +public: + AudioEnginePrivate( AudioEngine* q ) + : q_ptr ( q ) + { + } + AudioEngine* q_ptr; + Q_DECLARE_PUBLIC ( AudioEngine ) + + +public slots: + void onStateChanged( Phonon::State newState, Phonon::State oldState ); + +private: + QSharedPointer input; + + Tomahawk::query_ptr stopAfterTrack; + Tomahawk::result_ptr currentTrack; + Tomahawk::playlistinterface_ptr playlist; + Tomahawk::playlistinterface_ptr currentTrackPlaylist; + Tomahawk::playlistinterface_ptr queue; + + Phonon::MediaObject* mediaObject; + Phonon::AudioOutput* audioOutput; + + unsigned int timeElapsed; + bool expectStop; + bool waitingOnNewTrack; + + mutable QStringList supportedMimeTypes; + + AudioState state; + QQueue< AudioState > stateQueue; + QTimer stateQueueTimer; + + uint_fast8_t underrunCount; + bool underrunNotified; + + QTemporaryFile* coverTempFile; + + static AudioEngine* s_instance; +}; From da2308db3349a89c6851ca2cc2280d236eaeba10 Mon Sep 17 00:00:00 2001 From: Florian Richter Date: Sat, 25 May 2013 13:28:26 +0200 Subject: [PATCH 097/565] listen to media key events of gnome settings daemon * add shortcuthandler, which listens to the media key event provided by the gnome settings daemon via dbus (https://github.com/GNOME/gnome-settings-daemon/blob/master/plugins/media-keys/README.media-keys-API) --- src/tomahawk/CMakeLists.txt | 2 + src/tomahawk/GnomeSettingsDaemonMediaKeys.xml | 17 +++ .../GnomeSettingsDaemonMediaKeysProxy.cpp | 26 ++++ .../GnomeSettingsDaemonMediaKeysProxy.h | 64 ++++++++++ src/tomahawk/GnomeShortcutHandler.cpp | 111 ++++++++++++++++++ src/tomahawk/GnomeShortcutHandler.h | 54 +++++++++ src/tomahawk/TomahawkApp.cpp | 7 ++ 7 files changed, 281 insertions(+) create mode 100644 src/tomahawk/GnomeSettingsDaemonMediaKeys.xml create mode 100644 src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp create mode 100644 src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h create mode 100644 src/tomahawk/GnomeShortcutHandler.cpp create mode 100644 src/tomahawk/GnomeShortcutHandler.h diff --git a/src/tomahawk/CMakeLists.txt b/src/tomahawk/CMakeLists.txt index 636ff2c3c6..da07a97c2b 100644 --- a/src/tomahawk/CMakeLists.txt +++ b/src/tomahawk/CMakeLists.txt @@ -22,6 +22,8 @@ ENDIF() SET( tomahawkSources ${tomahawkSources} AclRegistryImpl.cpp ShortcutHandler.cpp + GnomeShortcutHandler.cpp + GnomeSettingsDaemonMediaKeysProxy.cpp UbuntuUnityHack.cpp TomahawkApp.cpp main.cpp diff --git a/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml b/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml new file mode 100644 index 0000000000..9caf240f5d --- /dev/null +++ b/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp b/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp new file mode 100644 index 0000000000..c94b3cc46d --- /dev/null +++ b/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp @@ -0,0 +1,26 @@ +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp -p GnomeSettingsDaemonMediaKeysProxy -c GnomeSettingsDaemonMediaKeysProxy GnomeSettingsDaemonMediaKeys.xml + * + * qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "GnomeSettingsDaemonMediaKeysProxy.h" + +/* + * Implementation of interface class GnomeSettingsDaemonMediaKeysProxy + */ + +GnomeSettingsDaemonMediaKeysProxy::GnomeSettingsDaemonMediaKeysProxy(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +GnomeSettingsDaemonMediaKeysProxy::~GnomeSettingsDaemonMediaKeysProxy() +{ +} + diff --git a/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h b/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h new file mode 100644 index 0000000000..0336fd3f28 --- /dev/null +++ b/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h @@ -0,0 +1,64 @@ +/* + * This file was generated by qdbusxml2cpp version 0.7 + * Command line was: qdbusxml2cpp -p GnomeSettingsDaemonMediaKeysProxy -c GnomeSettingsDaemonMediaKeysProxy GnomeSettingsDaemonMediaKeys.xml + * + * qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef GNOMESETTINGSDAEMONMEDIAKEYSPROXY_H_1369414808 +#define GNOMESETTINGSDAEMONMEDIAKEYSPROXY_H_1369414808 + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Proxy class for interface org.gnome.SettingsDaemon.MediaKeys + */ +class GnomeSettingsDaemonMediaKeysProxy: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.gnome.SettingsDaemon.MediaKeys"; } + +public: + GnomeSettingsDaemonMediaKeysProxy(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); + + ~GnomeSettingsDaemonMediaKeysProxy(); + +public Q_SLOTS: // METHODS + inline QDBusPendingReply<> GrabMediaPlayerKeys(const QString &application, uint time) + { + QList argumentList; + argumentList << QVariant::fromValue(application) << QVariant::fromValue(time); + return asyncCallWithArgumentList(QLatin1String("GrabMediaPlayerKeys"), argumentList); + } + + inline QDBusPendingReply<> ReleaseMediaPlayerKeys(const QString &application) + { + QList argumentList; + argumentList << QVariant::fromValue(application); + return asyncCallWithArgumentList(QLatin1String("ReleaseMediaPlayerKeys"), argumentList); + } + +Q_SIGNALS: // SIGNALS + void MediaPlayerKeyPressed(const QString &in0, const QString &in1); +}; + +namespace org { + namespace gnome { + namespace SettingsDaemon { + typedef ::GnomeSettingsDaemonMediaKeysProxy MediaKeys; + } + } +} +#endif diff --git a/src/tomahawk/GnomeShortcutHandler.cpp b/src/tomahawk/GnomeShortcutHandler.cpp new file mode 100644 index 0000000000..6b04949615 --- /dev/null +++ b/src/tomahawk/GnomeShortcutHandler.cpp @@ -0,0 +1,111 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Florian Richter + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +// implement listen on media keys events provided by the gnome settings daemon +// as documented here: +// https://github.com/GNOME/gnome-settings-daemon/blob/master/plugins/media-keys/README.media-keys-API + +#include "GnomeShortcutHandler.h" + +#include "utils/Logger.h" + + +#ifdef QT_DBUS_LIB +# include +#endif + +#include + +using namespace Tomahawk; + +const char* GnomeShortcutHandler::kGsdService = "org.gnome.SettingsDaemon"; +const char* GnomeShortcutHandler::kGsdPath = "/org/gnome/SettingsDaemon/MediaKeys"; +const char* GnomeShortcutHandler::kGsdInterface = "org.gnome.SettingsDaemon.MediaKeys"; + + +GnomeShortcutHandler::GnomeShortcutHandler(QObject *parent) : + Tomahawk::ShortcutHandler(parent), + interface_(NULL) +{ + +} + +bool GnomeShortcutHandler::DoRegister() { +#ifdef QT_DBUS_LIB + tLog(LOGVERBOSE) << "registering for gnome media keys"; + // Check if the GSD service is available + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kGsdService)) { + tLog(LOGVERBOSE) << "gnome settings daemon not registered"; + return false; + } + + if (!interface_) { + interface_ = new GnomeSettingsDaemonMediaKeysProxy( + kGsdService, kGsdPath, QDBusConnection::sessionBus(), this->parent()); + } + + QDBusPendingReply<> reply = interface_->GrabMediaPlayerKeys( + QCoreApplication::applicationName(), 0); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(RegisterFinished(QDBusPendingCallWatcher*))); + + return true; +#else // QT_DBUS_LIB + tLog(LOGVERBOSE) << "dbus not available"; + return false; +#endif +} + +void GnomeShortcutHandler::RegisterFinished(QDBusPendingCallWatcher* watcher) { +#ifdef QT_DBUS_LIB + QDBusMessage reply = watcher->reply(); + watcher->deleteLater(); + + if (reply.type() == QDBusMessage::ErrorMessage) { + tLog(LOGVERBOSE) << "Failed to grab media keys" + << reply.errorName() < === + * + * Copyright 2013, Florian Richter + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef GNOMESHORTCUTHANDLER_H +#define GNOMESHORTCUTHANDLER_H + +#include "ShortcutHandler.h" +#include "GnomeSettingsDaemonMediaKeysProxy.h" + +#include + +namespace Tomahawk { + + +class GnomeShortcutHandler : public ShortcutHandler +{ + Q_OBJECT +public: + explicit GnomeShortcutHandler(QObject *parent = 0); + bool DoRegister(); + + static const char* kGsdService; + static const char* kGsdPath; + static const char* kGsdInterface; + + +public slots: + void RegisterFinished(QDBusPendingCallWatcher* watcher); + void GnomeMediaKeyPressed( const QString& application, const QString& key ); + +private: + GnomeSettingsDaemonMediaKeysProxy* interface_; + +}; + +} + +#endif // GNOMESHORTCUTHANDLER_H + diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index c77ca8e21c..c7f04f368c 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -46,6 +46,7 @@ #include "web/Api_v1.h" #include "SourceList.h" #include "ShortcutHandler.h" +#include "GnomeShortcutHandler.h" #include "filemetadata/ScanManager.h" #include "TomahawkSettings.h" #include "GlobalActionManager.h" @@ -232,6 +233,12 @@ TomahawkApp::init() increaseMaxFileDescriptors(); #endif +#if !defined(Q_WS_MAC) && !defined(Q_WS_WIN) + GnomeShortcutHandler *gnomeShortcutHandler = new GnomeShortcutHandler( this ); + gnomeShortcutHandler->DoRegister(); + m_shortcutHandler = QPointer( gnomeShortcutHandler ); +#endif + // Connect up shortcuts if ( !m_shortcutHandler.isNull() ) { From f64436f9bb6d89041932ba092fdb438d4f7dd120 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 25 May 2013 19:49:30 +0200 Subject: [PATCH 098/565] Add the possibility for JSResolvers to specify that a given URL is checked/valid. --- src/libtomahawk/Pipeline.cpp | 2 +- src/libtomahawk/Result.cpp | 1 + src/libtomahawk/Result.h | 8 ++++++++ src/libtomahawk/resolvers/JSResolver.cpp | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index 081e15d872..f747639dac 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -295,7 +295,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results ) if ( r.isNull() ) continue; - if ( r->url().startsWith( "http" ) && !r->url().startsWith( "http://localhost" ) ) + if ( !r->checked() && ( r->url().startsWith( "http" ) && !r->url().startsWith( "http://localhost" ) ) ) httpResults << r; else cleanResults << r; diff --git a/src/libtomahawk/Result.cpp b/src/libtomahawk/Result.cpp index f51d3f9a68..b3d959059f 100644 --- a/src/libtomahawk/Result.cpp +++ b/src/libtomahawk/Result.cpp @@ -90,6 +90,7 @@ Result::Result( const QString& url ) , m_modtime( 0 ) , m_score( 0 ) , m_fileId( 0 ) + , m_checked( false ) { connect( Pipeline::instance(), SIGNAL( resolverRemoved( Tomahawk::Resolver* ) ), SLOT( onResolverRemoved( Tomahawk::Resolver* ) ), Qt::QueuedConnection ); } diff --git a/src/libtomahawk/Result.h b/src/libtomahawk/Result.h index 422185a8c5..577e3daff1 100644 --- a/src/libtomahawk/Result.h +++ b/src/libtomahawk/Result.h @@ -74,6 +74,12 @@ friend class ::DatabaseCommand_LoadFile; collection_ptr collection() const; QString url() const { return m_url; } + /** + * Has the given url been checked that it is accessible/valid. + * + * Results marked as true will bypass the ResultUrlChecker. + */ + bool checked() const { return m_checked; } QString mimetype() const { return m_mimetype; } QString friendlySource() const; QString purchaseUrl() const { return m_purchaseUrl; } @@ -92,6 +98,7 @@ friend class ::DatabaseCommand_LoadFile; void setFriendlySource( const QString& s ) { m_friendlySource = s; } void setPurchaseUrl( const QString& u ) { m_purchaseUrl = u; } void setLinkUrl( const QString& u ) { m_linkUrl = u; } + void setChecked( bool checked ) { m_checked = checked; } void setMimetype( const QString& mimetype ) { m_mimetype = mimetype; } void setBitrate( unsigned int bitrate ) { m_bitrate = bitrate; } void setSize( unsigned int size ) { m_size = size; } @@ -133,6 +140,7 @@ private slots: QString m_mimetype; QString m_friendlySource; + bool m_checked; unsigned int m_bitrate; unsigned int m_size; unsigned int m_modtime; diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index d82d8e4c05..f01c64af66 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -760,6 +760,7 @@ JSResolver::parseResultVariantList( const QVariantList& reslist ) rp->setPurchaseUrl( m.value( "purchaseUrl" ).toString() ); rp->setLinkUrl( m.value( "linkUrl" ).toString() ); rp->setScore( m.value( "score" ).toFloat() ); + rp->setChecked( m.value( "checked" ).toBool() ); //FIXME if ( m.contains( "year" ) ) From 3e00b5c0e68fa184942bcd3c1de0afb997733cac Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sat, 25 May 2013 20:09:08 +0200 Subject: [PATCH 099/565] Revert "listen to media key events of gnome settings daemon" This reverts commit 966ab37b94527cdcd8c1b1fea8984f68f59a2be4. --- src/tomahawk/CMakeLists.txt | 2 - src/tomahawk/GnomeSettingsDaemonMediaKeys.xml | 17 --- .../GnomeSettingsDaemonMediaKeysProxy.cpp | 26 ---- .../GnomeSettingsDaemonMediaKeysProxy.h | 64 ---------- src/tomahawk/GnomeShortcutHandler.cpp | 111 ------------------ src/tomahawk/GnomeShortcutHandler.h | 54 --------- src/tomahawk/TomahawkApp.cpp | 7 -- 7 files changed, 281 deletions(-) delete mode 100644 src/tomahawk/GnomeSettingsDaemonMediaKeys.xml delete mode 100644 src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp delete mode 100644 src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h delete mode 100644 src/tomahawk/GnomeShortcutHandler.cpp delete mode 100644 src/tomahawk/GnomeShortcutHandler.h diff --git a/src/tomahawk/CMakeLists.txt b/src/tomahawk/CMakeLists.txt index da07a97c2b..636ff2c3c6 100644 --- a/src/tomahawk/CMakeLists.txt +++ b/src/tomahawk/CMakeLists.txt @@ -22,8 +22,6 @@ ENDIF() SET( tomahawkSources ${tomahawkSources} AclRegistryImpl.cpp ShortcutHandler.cpp - GnomeShortcutHandler.cpp - GnomeSettingsDaemonMediaKeysProxy.cpp UbuntuUnityHack.cpp TomahawkApp.cpp main.cpp diff --git a/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml b/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml deleted file mode 100644 index 9caf240f5d..0000000000 --- a/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp b/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp deleted file mode 100644 index c94b3cc46d..0000000000 --- a/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.7 - * Command line was: qdbusxml2cpp -p GnomeSettingsDaemonMediaKeysProxy -c GnomeSettingsDaemonMediaKeysProxy GnomeSettingsDaemonMediaKeys.xml - * - * qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). - * - * This is an auto-generated file. - * This file may have been hand-edited. Look for HAND-EDIT comments - * before re-generating it. - */ - -#include "GnomeSettingsDaemonMediaKeysProxy.h" - -/* - * Implementation of interface class GnomeSettingsDaemonMediaKeysProxy - */ - -GnomeSettingsDaemonMediaKeysProxy::GnomeSettingsDaemonMediaKeysProxy(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) - : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) -{ -} - -GnomeSettingsDaemonMediaKeysProxy::~GnomeSettingsDaemonMediaKeysProxy() -{ -} - diff --git a/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h b/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h deleted file mode 100644 index 0336fd3f28..0000000000 --- a/src/tomahawk/GnomeSettingsDaemonMediaKeysProxy.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.7 - * Command line was: qdbusxml2cpp -p GnomeSettingsDaemonMediaKeysProxy -c GnomeSettingsDaemonMediaKeysProxy GnomeSettingsDaemonMediaKeys.xml - * - * qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). - * - * This is an auto-generated file. - * Do not edit! All changes made to it will be lost. - */ - -#ifndef GNOMESETTINGSDAEMONMEDIAKEYSPROXY_H_1369414808 -#define GNOMESETTINGSDAEMONMEDIAKEYSPROXY_H_1369414808 - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Proxy class for interface org.gnome.SettingsDaemon.MediaKeys - */ -class GnomeSettingsDaemonMediaKeysProxy: public QDBusAbstractInterface -{ - Q_OBJECT -public: - static inline const char *staticInterfaceName() - { return "org.gnome.SettingsDaemon.MediaKeys"; } - -public: - GnomeSettingsDaemonMediaKeysProxy(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0); - - ~GnomeSettingsDaemonMediaKeysProxy(); - -public Q_SLOTS: // METHODS - inline QDBusPendingReply<> GrabMediaPlayerKeys(const QString &application, uint time) - { - QList argumentList; - argumentList << QVariant::fromValue(application) << QVariant::fromValue(time); - return asyncCallWithArgumentList(QLatin1String("GrabMediaPlayerKeys"), argumentList); - } - - inline QDBusPendingReply<> ReleaseMediaPlayerKeys(const QString &application) - { - QList argumentList; - argumentList << QVariant::fromValue(application); - return asyncCallWithArgumentList(QLatin1String("ReleaseMediaPlayerKeys"), argumentList); - } - -Q_SIGNALS: // SIGNALS - void MediaPlayerKeyPressed(const QString &in0, const QString &in1); -}; - -namespace org { - namespace gnome { - namespace SettingsDaemon { - typedef ::GnomeSettingsDaemonMediaKeysProxy MediaKeys; - } - } -} -#endif diff --git a/src/tomahawk/GnomeShortcutHandler.cpp b/src/tomahawk/GnomeShortcutHandler.cpp deleted file mode 100644 index 6b04949615..0000000000 --- a/src/tomahawk/GnomeShortcutHandler.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2013, Florian Richter - * - * Tomahawk 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 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 Tomahawk. If not, see . - */ - -// implement listen on media keys events provided by the gnome settings daemon -// as documented here: -// https://github.com/GNOME/gnome-settings-daemon/blob/master/plugins/media-keys/README.media-keys-API - -#include "GnomeShortcutHandler.h" - -#include "utils/Logger.h" - - -#ifdef QT_DBUS_LIB -# include -#endif - -#include - -using namespace Tomahawk; - -const char* GnomeShortcutHandler::kGsdService = "org.gnome.SettingsDaemon"; -const char* GnomeShortcutHandler::kGsdPath = "/org/gnome/SettingsDaemon/MediaKeys"; -const char* GnomeShortcutHandler::kGsdInterface = "org.gnome.SettingsDaemon.MediaKeys"; - - -GnomeShortcutHandler::GnomeShortcutHandler(QObject *parent) : - Tomahawk::ShortcutHandler(parent), - interface_(NULL) -{ - -} - -bool GnomeShortcutHandler::DoRegister() { -#ifdef QT_DBUS_LIB - tLog(LOGVERBOSE) << "registering for gnome media keys"; - // Check if the GSD service is available - if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kGsdService)) { - tLog(LOGVERBOSE) << "gnome settings daemon not registered"; - return false; - } - - if (!interface_) { - interface_ = new GnomeSettingsDaemonMediaKeysProxy( - kGsdService, kGsdPath, QDBusConnection::sessionBus(), this->parent()); - } - - QDBusPendingReply<> reply = interface_->GrabMediaPlayerKeys( - QCoreApplication::applicationName(), 0); - - QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); - connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(RegisterFinished(QDBusPendingCallWatcher*))); - - return true; -#else // QT_DBUS_LIB - tLog(LOGVERBOSE) << "dbus not available"; - return false; -#endif -} - -void GnomeShortcutHandler::RegisterFinished(QDBusPendingCallWatcher* watcher) { -#ifdef QT_DBUS_LIB - QDBusMessage reply = watcher->reply(); - watcher->deleteLater(); - - if (reply.type() == QDBusMessage::ErrorMessage) { - tLog(LOGVERBOSE) << "Failed to grab media keys" - << reply.errorName() < === - * - * Copyright 2013, Florian Richter - * - * Tomahawk 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 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 Tomahawk. If not, see . - */ - -#ifndef GNOMESHORTCUTHANDLER_H -#define GNOMESHORTCUTHANDLER_H - -#include "ShortcutHandler.h" -#include "GnomeSettingsDaemonMediaKeysProxy.h" - -#include - -namespace Tomahawk { - - -class GnomeShortcutHandler : public ShortcutHandler -{ - Q_OBJECT -public: - explicit GnomeShortcutHandler(QObject *parent = 0); - bool DoRegister(); - - static const char* kGsdService; - static const char* kGsdPath; - static const char* kGsdInterface; - - -public slots: - void RegisterFinished(QDBusPendingCallWatcher* watcher); - void GnomeMediaKeyPressed( const QString& application, const QString& key ); - -private: - GnomeSettingsDaemonMediaKeysProxy* interface_; - -}; - -} - -#endif // GNOMESHORTCUTHANDLER_H - diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index c7f04f368c..c77ca8e21c 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -46,7 +46,6 @@ #include "web/Api_v1.h" #include "SourceList.h" #include "ShortcutHandler.h" -#include "GnomeShortcutHandler.h" #include "filemetadata/ScanManager.h" #include "TomahawkSettings.h" #include "GlobalActionManager.h" @@ -233,12 +232,6 @@ TomahawkApp::init() increaseMaxFileDescriptors(); #endif -#if !defined(Q_WS_MAC) && !defined(Q_WS_WIN) - GnomeShortcutHandler *gnomeShortcutHandler = new GnomeShortcutHandler( this ); - gnomeShortcutHandler->DoRegister(); - m_shortcutHandler = QPointer( gnomeShortcutHandler ); -#endif - // Connect up shortcuts if ( !m_shortcutHandler.isNull() ) { From 4aed8bf75eacfa07bbc1eed373001060fd3ea932 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 25 May 2013 23:14:15 +0200 Subject: [PATCH 100/565] Add desktop notifications for received tracks --- data/images/inbox-512x512.png | Bin 0 -> 14355 bytes resources.qrc | 1 + .../linux/fdonotify/FdoNotifyPlugin.cpp | 73 +++++++++++++++++- .../linux/fdonotify/FdoNotifyPlugin.h | 1 + src/libtomahawk/Typedefs.h | 4 +- src/libtomahawk/infosystem/InfoSystem.cpp | 2 +- src/libtomahawk/jobview/InboxJobItem.h | 2 +- src/libtomahawk/playlist/InboxModel.cpp | 19 +++++ 8 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 data/images/inbox-512x512.png diff --git a/data/images/inbox-512x512.png b/data/images/inbox-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..806661b5e0475544d59d56f6f85b57da1010e21a GIT binary patch literal 14355 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 literal 0 HcmV?d00001 diff --git a/resources.qrc b/resources.qrc index 17cd5b71ac..b66ef22516 100644 --- a/resources.qrc +++ b/resources.qrc @@ -183,5 +183,6 @@ data/images/station-genre.svg data/images/station-year.svg data/images/outbox.svg + data/images/inbox-512x512.png diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp index 67c9028a01..425af986e7 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp @@ -63,7 +63,7 @@ FdoNotifyPlugin::FdoNotifyPlugin() , m_wmSupportsBodyMarkup( false ) { qDebug() << Q_FUNC_INFO; - m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped; + m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped << InfoInboxReceived; // Query the window manager for its capabilties in styling notifications. QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "GetCapabilities" ); @@ -127,6 +127,10 @@ FdoNotifyPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) nowPlaying( pushData.infoPair.second ); return; + case Tomahawk::InfoSystem::InfoInboxReceived: + inboxReceived( pushData.infoPair.second ); + return; + default: return; } @@ -166,6 +170,73 @@ FdoNotifyPlugin::notifyUser( const QString& messageText ) QDBusConnection::sessionBus().send( message ); } +void FdoNotifyPlugin::inboxReceived(const QVariant &input) +{ + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; + if ( !input.canConvert< QVariantMap >() ) + return; + + QVariantMap map = input.toMap(); + + if ( !map.contains( "trackinfo" ) || !map[ "trackinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + return; + + if ( !map.contains( "sourceinfo" ) || !map[ "sourceinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + return; + + InfoStringHash hash = map[ "trackinfo" ].value< Tomahawk::InfoSystem::InfoStringHash >(); + if ( !hash.contains( "title" ) || !hash.contains( "artist" ) ) + return; + + InfoStringHash src = map[ "sourceinfo" ].value< Tomahawk::InfoSystem::InfoStringHash >(); + if ( !src.contains( "friendlyname" ) ) + return; + + QString messageText; + // If the window manager supports notification styling then use it. + if ( m_wmSupportsBodyMarkup ) + { + // Remark: If using xml-based markup in notifications, the supplied strings need to be escaped. + messageText = tr( "%1 sent you\n%2%4 %3.", "%1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english)" ) + .arg( Qt::escape( src["friendlyname"] ) ) + .arg( Qt::escape( hash[ "title" ] ) ) + .arg( Qt::escape( hash[ "artist" ] ) ) + .arg( QString( "\n%1" ).arg( tr( "by", "preposition to link track and artist" ) ) ); + + // Dirty hack(TM) so that KNotify/QLabel recognizes the message as Rich Text + messageText = QString( "%1" ).arg( messageText ); + } + else + { + messageText = tr( "%1 sent you \"%2\" by %3.", "%1 is a nickname, %2 is a title, %3 is an artist" ) + .arg( src["friendlyname"] ) + .arg( hash[ "title" ] ) + .arg( hash[ "artist" ] ); + } + + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "sending message" << messageText; + + QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify" ); + QList arguments; + arguments << QString( "Tomahawk" ); //app_name + arguments << m_nowPlayingId; //notification_id + arguments << QString(); //app_icon + arguments << QString( "Tomahawk - Track received" ); //summary + arguments << messageText; //body + arguments << QStringList(); //actions + QVariantMap dict; + dict["desktop-entry"] = QString( "tomahawk" ); + + // Convert image to QVariant and scale to a consistent size. + dict[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "images/inbox-512x512.png" ).scaledToHeight( getNotificationIconHeight() ) ); + + arguments << dict; //hints + arguments << qint32( -1 ); //expire_timeout + message.setArguments( arguments ); + + // Handle reply in a callback, so that this a non-blocking call + QDBusConnection::sessionBus().send( message ); +} void FdoNotifyPlugin::nowPlaying( const QVariant& input ) diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h index ff4743cdd6..32003633f5 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h @@ -65,6 +65,7 @@ protected slots: int getNotificationIconHeight(); void notifyUser( const QString& messageText ); + void inboxReceived( const QVariant& input ); void nowPlaying( const QVariant& input ); quint32 m_nowPlayingId; diff --git a/src/libtomahawk/Typedefs.h b/src/libtomahawk/Typedefs.h index c876b084d7..639a245557 100644 --- a/src/libtomahawk/Typedefs.h +++ b/src/libtomahawk/Typedefs.h @@ -216,7 +216,9 @@ namespace Tomahawk InfoNotifyUser = 100, - InfoLastInfo = 101 //WARNING: *ALWAYS* keep this last! + InfoInboxReceived = 101, + + InfoLastInfo = 102 //WARNING: *ALWAYS* keep this last! }; class InfoPlugin; diff --git a/src/libtomahawk/infosystem/InfoSystem.cpp b/src/libtomahawk/infosystem/InfoSystem.cpp index 0ba7275350..fb303cffae 100644 --- a/src/libtomahawk/infosystem/InfoSystem.cpp +++ b/src/libtomahawk/infosystem/InfoSystem.cpp @@ -225,7 +225,7 @@ InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const bool InfoSystem::pushInfo( InfoPushData pushData ) { - tDebug() << Q_FUNC_INFO << "type is " << pushData.type; + tDebug() << Q_FUNC_INFO << "type is" << pushData.type; if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) { init(); diff --git a/src/libtomahawk/jobview/InboxJobItem.h b/src/libtomahawk/jobview/InboxJobItem.h index 91dd243b66..e9e90f35e2 100644 --- a/src/libtomahawk/jobview/InboxJobItem.h +++ b/src/libtomahawk/jobview/InboxJobItem.h @@ -33,7 +33,7 @@ class DLLEXPORT InboxJobItem : public JobStatusItem enum Side { Sending = 0, - Receiving + Receiving = 1 }; explicit InboxJobItem( Side side, diff --git a/src/libtomahawk/playlist/InboxModel.cpp b/src/libtomahawk/playlist/InboxModel.cpp index dd16e30d72..79aa0ed5b8 100644 --- a/src/libtomahawk/playlist/InboxModel.cpp +++ b/src/libtomahawk/playlist/InboxModel.cpp @@ -23,6 +23,7 @@ #include "database/DatabaseCommand_DeleteInboxEntry.h" #include "database/DatabaseCommand_ModifyInboxEntry.h" #include "SourceList.h" +#include "TomahawkSettings.h" #include "utils/Logger.h" #include "utils/Closure.h" #include "jobview/JobStatusModel.h" @@ -138,6 +139,24 @@ InboxModel::showNotification( InboxJobItem::Side side, JobStatusView::instance()->model()->addJob( new InboxJobItem( side, src->friendlyName(), track ) ); + + if ( side == InboxJobItem::Receiving ) + { + Tomahawk::InfoSystem::InfoStringHash trackInfo; + trackInfo["title"] = track->track(); + trackInfo["artist"] = track->artist(); + + Tomahawk::InfoSystem::InfoStringHash sourceInfo; + sourceInfo["friendlyname"] = src->friendlyName(); + + QVariantMap playInfo; + playInfo["trackinfo"] = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ); + playInfo["private"] = TomahawkSettings::instance()->privateListeningMode(); + playInfo["sourceinfo"] = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( sourceInfo ); + + Tomahawk::InfoSystem::InfoPushData pushData ( "InboxModel", Tomahawk::InfoSystem::InfoInboxReceived, playInfo, Tomahawk::InfoSystem::PushShortUrlFlag ); + Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData ); + } } From 8e4ccbee6d0d2f9fb7997da110f42894d60f246d Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sun, 26 May 2013 02:16:52 +0200 Subject: [PATCH 101/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_bg.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_bn_IN.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_ca.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_ca@valencia.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_cs.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_da.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_de.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_el.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_en.ts | 41 ++++++++++++++++++++++++------------ lang/tomahawk_es.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_fi.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_fr.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_gl.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_hi_IN.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_hu.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_id.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_it.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_ja.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_lt.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_pl.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_pt_BR.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_ru.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_sv.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_tr.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_zh_CN.ts | 40 +++++++++++++++++++++++------------ lang/tomahawk_zh_TW.ts | 40 +++++++++++++++++++++++------------ 27 files changed, 730 insertions(+), 351 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 5759f61702..3e099141aa 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -379,17 +379,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 نعتذر، لم نستطيع إيجاد الأغنية '%1' ل%2 - + Sorry, Tomahawk couldn't find the artist '%1' نعتذر، لم نستطيع إيجاد الفنان '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 نعتذر، لم نستطيع إيجاد الألبوم '%1' ل%2 @@ -1472,22 +1472,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 خطأ محلل النصي: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3217,31 +3217,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name في - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist للفنان - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name في "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" من قبل %2%3. @@ -3517,7 +3531,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network الشبكة المحلية diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 8ad774283b..a39566936a 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Съжалявам. Не успявам да открия изпълнение '%1' от '%2' - + Sorry, Tomahawk couldn't find the artist '%1' Съжалявам, но не откривам '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Съжалявам, но не откривам албум с име '%1' от '%2' @@ -1480,22 +1480,22 @@ Tomahawk създаде доклад относно това и изпращай ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Грешка на скриптът за извличане на данни: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3230,31 +3230,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3530,7 +3544,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Локална мрежа diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 53978487b7..75bd32030b 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 825c93b8f2..7921d7c558 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 No s'ha trobat la cançó «%1» de «%2» - + Sorry, Tomahawk couldn't find the artist '%1' No s'ha trobat l'artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 No s'ha trobat l'àlbum «%1» de «%2» @@ -1470,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3218,31 +3218,45 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3518,7 +3532,7 @@ introduïu el PIN aquí: TomahawkSettings - + Local Network Xarxa local diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 3f39bcc910..764660fa6e 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 No s'ha trobat la cançó «%1» de «%2» - + Sorry, Tomahawk couldn't find the artist '%1' No s'ha trobat l'artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 No s'ha trobat l'àlbum «%1» de «%2» @@ -1470,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3218,31 +3218,45 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3518,7 +3532,7 @@ introduïu el PIN ací: TomahawkSettings - + Local Network Xarxa local diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 0517166461..088c3df41a 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -379,17 +379,17 @@ se s vámi spojil? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Promiňte, Tomahawku se nepodařilo najít skladbu '%1' od %2 - + Sorry, Tomahawk couldn't find the artist '%1' Promiňte, Tomahawku se nepodařilo najít umělce '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Promiňte, Tomahawku se nepodařilo najít album '%1' od %2 @@ -1471,22 +1471,22 @@ se s vámi spojil? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Chyba řešitele skriptu: %1 %2 %3 %4 - + SSL Error Chyba SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Požádal jste Tomahawk o to, aby se bezpečně připojil k <b>%1</b>, ale nelze potvrdit, že je vaše připojení bezpečné:<br><br><b>%2</b><br><br>Chcete tomuto připojení důvěřovat? - + Trust certificate Důvěřovat certifikátu @@ -3216,31 +3216,45 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name na - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist od - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name na "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" od %2%3. @@ -3516,7 +3530,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TomahawkSettings - + Local Network Místní síť diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 5c3896355f..d4276ab084 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1470,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3206,31 +3206,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3505,7 +3519,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index e3aaf252c2..dce64223e5 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -379,17 +379,17 @@ erlauben sich mit dir zu verbinden? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Sorry, Tomahawk konnte '%1' von %2 nicht finden - + Sorry, Tomahawk couldn't find the artist '%1' Sorry, Tomahawk konnte den Künstler '%1' nicht finden - + Sorry, Tomahawk couldn't find the album '%1' by %2 Sorry, Tomahawk konnte das Album '%1' von %2 nicht finden @@ -1471,22 +1471,22 @@ erlauben sich mit dir zu verbinden? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Fehler: %1 %2 %3 %4 - + SSL Error SSL Fehler - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Sie wurden gebeten, Tomahawk sicher zu <b>%1</b> verbinden, aber wir können nicht bestätigen, dass die Verbindung sicher ist:<br><br><b>%2</b><br><br> Möchten Sie dieser Verbindung vertrauen? - + Trust certificate Zertifikat vertrauen @@ -3211,31 +3211,45 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name an - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist bei - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name in "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" in %2%3. @@ -3511,7 +3525,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TomahawkSettings - + Local Network Lokales Netzwerk diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 9de421a88a..2a3acd41c5 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Συγνωμη, το Tomahawk δεν βρηκε το τραγουδι '%1' απο %2 - + Sorry, Tomahawk couldn't find the artist '%1' Συγνωμη, το Tomahawk δεν μπορεσε να βρει τον καλλιτεχνη '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Συγνωμη, το Tomahawk δεν μπορεσε να βρει το αλμπουμ του '%1' απο %2 @@ -1470,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Σφάλμα Script Resolver: %1 %2 %3 %4 - + SSL Error SSL Σφάλμα - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Έχετε ζητήσει το Tomahawk να συνδεθεί με ασφάλεια στο <b>%1</b>, αλλά δεν μπορούμε να επιβεβαιώσουμε ότι η σύνδεσή σας είναι ασφαλής:<br><br><b>%2</b><br><br>Θέλετε να εμπιστεύθειτε αυτή τη σύνδεση; - + Trust certificate Εμπιστοσύνη πιστοποιητικόυ @@ -3217,31 +3217,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name ενεργοποιημένο - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist από - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name σε "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" από %2%3. @@ -3516,7 +3530,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Τοπικό Δίκτυο diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 0dc3fac856..4a0b8de560 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -379,17 +379,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1471,22 +1471,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Error: %1 %2 %3 %4 - + SSL Error SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate Trust certificate @@ -3219,31 +3219,46 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name on - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist by - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + %1 sent you +%2%4 %3. + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + %1 sent you "%2" by %3. + + + on "%1" %1 is an album name on "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" by %2%3. @@ -3519,7 +3534,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Local Network diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index ba9c99f342..361fb3a960 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -379,17 +379,17 @@ conectarse a usted y transmitir música? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawk no pudo encontrar la pista '%1' de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawk no pudo encontrar el artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawk no pudo encontrar el álbum '%1' de %2 @@ -1471,22 +1471,22 @@ conectarse a usted y transmitir música? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error del resolutor de script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3219,31 +3219,45 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name el - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist por - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name en «%1» - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing «%1» por %2%3. @@ -3519,7 +3533,7 @@ introduzca su número PIN aquí: TomahawkSettings - + Local Network Red local diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 4d24be4224..a5e40ff16c 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -379,17 +379,17 @@ yhdistää ja toistaa sinulta virtaa? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Valitettavasti Tomahawk ei löytänyt artistin %2 kappaletta ”%1” - + Sorry, Tomahawk couldn't find the artist '%1' Valitettavasti Tomahawk ei löytänyt artistia ”%1” - + Sorry, Tomahawk couldn't find the album '%1' by %2 Valitettavasti Tomahawk ei löytänyt artistin %2 albumia ”%1” @@ -1471,22 +1471,22 @@ yhdistää ja toistaa sinulta virtaa? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptiselvittimen virhe: %1 %2 %3 %4 - + SSL Error SSL-virhe - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Olet pyytänyt Tomahawkia yhdistämään turvallisesti palvelimeen <b>%1</b>, mutta yhteyden turvallisuutta ei voida varmistaa:<br><br><b>%2</b><br><br>Haluatko luottaa tähän yhteyteen? - + Trust certificate Luota varmenteeseen @@ -3222,31 +3222,45 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name albumilla - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist artistilta - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name albumilla ”%1” - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing ”%1” artistilta %2%3. @@ -3522,7 +3536,7 @@ anna siellä näytetty PIN-koodi tähän: TomahawkSettings - + Local Network Lähiverkko diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 66fcc353d5..ce7ebc99d9 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -379,17 +379,17 @@ de se connecter et streamer de vous? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Désolé, on a pas pu trouver la piste '%1' pour %2 - + Sorry, Tomahawk couldn't find the artist '%1' Désolé, on a pas pu trouver l'artiste '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Désolé, on a pas pu trouver l'album '%1' pour %2 @@ -1471,22 +1471,22 @@ de se connecter et streamer de vous? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erreur du script de résolution : %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3216,31 +3216,45 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name sur - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist par - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name sur "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" par %2%3. @@ -3516,7 +3530,7 @@ saisissez le numéro PIN ici : TomahawkSettings - + Local Network Réseau local diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 118bb6b05a..ccc8ed5877 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawk non atopa a pista «%1» de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawk non atopa o artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawk non atopa o álbum «%1» de %2 @@ -1470,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erro do solucionador de erros: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3218,31 +3218,45 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3517,7 +3531,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Rede local diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index b2ed6971f9..1ad87462e9 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 1942fd8807..1f8d1d7ed8 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index adbba263ff..db04ab2301 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 039f634ed8..5260d0eb27 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Spiacente, Tomahawk non ha trovato la traccia '%1' di %2 - + Sorry, Tomahawk couldn't find the artist '%1' Spiacente, Tomahawk non ha trovato l'artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Spiacente, Tomahawk non ha trovato l'album '%1' di '%2' @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Errore script resolver: %1 %2 %3 %4 - + SSL Error Errore SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Hai chiesto a Tomahawk di connettersi in modo sicuro a <b>%1</b>, ma non possiamo garantire che la conessione sia sicura: <br><br><b>%2</b><br><br> Vuoi fidarti di questa connessione? - + Trust certificate Certificato di trust @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name su - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist da - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name su "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" di %2%3. @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Network locale diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 6bfe2543a1..f4f6a02c6c 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawkは%2の%1を見つかりませんでした。 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawkは'%1'と言うアーティストを見つかりませんでした。 - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawkは%2の%1を見つかりませんでした。 @@ -1471,22 +1471,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3216,31 +3216,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3516,7 +3530,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network ローカルネットワーク diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 453dc5dabd..2ffb7054f6 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Atsiprašome, Tomahawk nepavyko rasti takelio '%1', atliekamo %2 - + Sorry, Tomahawk couldn't find the artist '%1' Atsiprašome, Tomahawk nepavyko rasti atlikėjo '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Atsiprašome, Tomahawk nepavyko rasti %2 atliekamo albumo '%1' @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 49eb85359b..47f5180bd4 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -379,17 +379,17 @@ połączyć się i strumieniować od ciebie? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Przepraszamy, Tomahawk nie mógł znaleźć utworu '%1' wykonawcy %2 - + Sorry, Tomahawk couldn't find the artist '%1' Przepraszamy, Tomahawk nie mógł znaleźć wykonawcy '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Przepraszamy, Tomahawk nie mógł znaleźć albumu '%1' wykonawcy %2 @@ -1471,22 +1471,22 @@ połączyć się i strumieniować od ciebie? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3213,31 +3213,45 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3513,7 +3527,7 @@ wprowadź pokazany numer PIN tutaj: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index cb3733059a..012c0c7acd 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -379,17 +379,17 @@ se conecte e faça o stream de você? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Desculpe, o Tomahawk não encontrou a faixa '%1' de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Desculpe, o Tomahawk não encontrou o artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Desculpe, o Tomahawk não encontrou o álbum '%1' de %2 @@ -1471,22 +1471,22 @@ se conecte e faça o stream de você? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3213,31 +3213,45 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3513,7 +3527,7 @@ colocar o número PIN mostrado aqui: TomahawkSettings - + Local Network Rede local diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 357ce35480..5f8d53a52d 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -382,17 +382,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 К сожалению, Tomahawk не смог найти песню '%1' %2 - + Sorry, Tomahawk couldn't find the artist '%1' К сожалению, Tomahawk не смог найти исполнителя '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 К сожалению, Tomahawk не смог найти альбом '%1' %2 @@ -1474,22 +1474,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3220,31 +3220,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3519,7 +3533,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network Домашняя сеть diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 6965643f3e..e160ab4ca3 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -379,17 +379,17 @@ ansluta och strömma från dig? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tyvärr! Tomahawk kunde inte hitta spåret '%1' av %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tyvärr! Tomahawk kunde inte hitta artisten '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tyvärr! Tomahawk kunde inte hitta albumet '%1' av %2 @@ -1471,22 +1471,22 @@ ansluta och strömma från dig? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptfel i resolvern: %1 %2 %3 %4 - + SSL Error SSL-Fel - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Du har bett Tomahawk att göra en säker uppkoppling mot <b>%1</b>, men vi kan inte bekräfta att din uppkoppling är säker: <br><br><b>%2</b><br><br>Litar du på denna uppkoppling? - + Trust certificate Tillförlitligt certifikat @@ -3217,31 +3217,45 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - + + by preposition to link track and artist av - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name på "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" av %2%3. @@ -3517,7 +3531,7 @@ anger du PIN-koden här: TomahawkSettings - + Local Network Lokalt nätverk diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index b7156bfe1b..a945bf2903 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 9f0b774530..b018e53b91 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 抱歉,Tomahawk 未找到 %2 的歌曲 '%1' - + Sorry, Tomahawk couldn't find the artist '%1' 抱歉,Tomahawk 无法找到艺术家 '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 抱歉,Tomahawk 无法找到 %2 的专辑 '%1' @@ -1470,22 +1470,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3214,31 +3214,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3514,7 +3528,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network 本地网络 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 09951121d6..c71d7c3cbb 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -378,17 +378,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -1469,22 +1469,22 @@ connect and stream from you? ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -3204,31 +3204,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + + by preposition to link track and artist - + + %1 sent you +%2%4 %3. + %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) + + + + + %1 sent you "%2" by %3. + %1 is a nickname, %2 is a title, %3 is an artist + + + + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing @@ -3503,7 +3517,7 @@ enter the displayed PIN number here: TomahawkSettings - + Local Network From 39da04b84357b345845f5f9df40fcfacb59dd2da Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 26 May 2013 13:44:41 +0200 Subject: [PATCH 102/565] Display friend's avatars on Dashboard even when they are offline * Fixes TWK-398 --- src/libtomahawk/Source.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 831b8e61cc..509068e87c 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -238,6 +238,19 @@ Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) break; } } + if ( result.isNull() ) + { + // Try to get the avatar from the cache + // Hint: We store the avatar for each xmpp peer using its contactId, the dbFriendlyName is a contactId of a peer + QByteArray avatarBuffer = TomahawkUtils::Cache::instance()->getData( "Sources", dbFriendlyName() ).toByteArray(); + if ( !avatarBuffer.isNull() ) + { + QPixmap avatar; + avatar.loadFromData( avatarBuffer ); + avatarBuffer.clear(); + result = QPixmap( TomahawkUtils::createRoundedImage( avatar, QSize( 0, 0 ) ) ); + } + } // tLog() << "****************************************************************************************"; return result; } From 9a6850def517248cb8c366f0e7f88c5c8e4ec662 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Mon, 27 May 2013 02:16:44 +0200 Subject: [PATCH 103/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 18 +++++++++--------- lang/tomahawk_bg.ts | 18 +++++++++--------- lang/tomahawk_bn_IN.ts | 18 +++++++++--------- lang/tomahawk_ca.ts | 18 +++++++++--------- lang/tomahawk_ca@valencia.ts | 18 +++++++++--------- lang/tomahawk_cs.ts | 23 ++++++++++++----------- lang/tomahawk_da.ts | 18 +++++++++--------- lang/tomahawk_de.ts | 18 +++++++++--------- lang/tomahawk_el.ts | 18 +++++++++--------- lang/tomahawk_en.ts | 18 +++++++++--------- lang/tomahawk_es.ts | 18 +++++++++--------- lang/tomahawk_fi.ts | 18 +++++++++--------- lang/tomahawk_fr.ts | 18 +++++++++--------- lang/tomahawk_gl.ts | 18 +++++++++--------- lang/tomahawk_hi_IN.ts | 18 +++++++++--------- lang/tomahawk_hu.ts | 18 +++++++++--------- lang/tomahawk_id.ts | 18 +++++++++--------- lang/tomahawk_it.ts | 18 +++++++++--------- lang/tomahawk_ja.ts | 18 +++++++++--------- lang/tomahawk_lt.ts | 18 +++++++++--------- lang/tomahawk_pl.ts | 18 +++++++++--------- lang/tomahawk_pt_BR.ts | 18 +++++++++--------- lang/tomahawk_ru.ts | 18 +++++++++--------- lang/tomahawk_sv.ts | 18 +++++++++--------- lang/tomahawk_tr.ts | 18 +++++++++--------- lang/tomahawk_zh_CN.ts | 18 +++++++++--------- lang/tomahawk_zh_TW.ts | 18 +++++++++--------- 27 files changed, 246 insertions(+), 245 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 3e099141aa..87f6136de6 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -3405,43 +3405,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index a39566936a..36b23bcb54 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -3419,43 +3419,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 песни) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 75bd32030b..405e2f880e 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 7921d7c558..aa84445363 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -3406,43 +3406,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 764660fa6e..a617cf3bef 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -3406,43 +3406,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 088c3df41a..573eeb0218 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -3239,13 +3239,14 @@ Zkuste vyladit filtry pro nové písně. %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 vám poslán⏎ +%2%4 %3. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 vám poslán "%2" od %3. @@ -3404,43 +3405,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index d4276ab084..a7ca8c7f8b 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -3394,43 +3394,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index dce64223e5..7143fa1b08 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -3399,43 +3399,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 2a3acd41c5..cdd2d86e1f 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -3405,43 +3405,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 4a0b8de560..5993804010 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -3408,43 +3408,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 361fb3a960..0cacbab495 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -3407,43 +3407,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index a5e40ff16c..d5f8609540 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -3410,43 +3410,43 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index ce7ebc99d9..0c09f02e7a 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -3404,43 +3404,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index ccc8ed5877..3c497053b9 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -3406,43 +3406,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 1ad87462e9..4437ecdf79 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 1f8d1d7ed8..d43f89bd89 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index db04ab2301..d18dd04650 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 5260d0eb27..a7ca6caa98 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index f4f6a02c6c..ff3eee6ae7 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -3404,43 +3404,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 2ffb7054f6..c1f62e3cd8 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 47f5180bd4..1def5be478 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -3401,43 +3401,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 012c0c7acd..fc064834d8 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -3401,43 +3401,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 5f8d53a52d..f59f838750 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -3408,43 +3408,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index e160ab4ca3..931f76b8e0 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -3405,43 +3405,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index a945bf2903..715ab68ecb 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index b018e53b91..82b9fb3f2c 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -3402,43 +3402,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index c71d7c3cbb..702fb09cea 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -3392,43 +3392,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline From 04a1f4e5950983c02972cb8816b9e47d897ad245 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 26 May 2013 18:41:31 +0200 Subject: [PATCH 104/565] * Some fixes to InfoSystem and loading. --- src/libtomahawk/infosystem/InfoSystem.h | 1 + .../infosystem/InfoSystemWorker.cpp | 1 + src/tomahawk/TomahawkApp.cpp | 176 +++++++++--------- src/tomahawk/TomahawkApp.h | 1 + 4 files changed, 95 insertions(+), 84 deletions(-) diff --git a/src/libtomahawk/infosystem/InfoSystem.h b/src/libtomahawk/infosystem/InfoSystem.h index 6a9e7f5272..48c0547ffe 100644 --- a/src/libtomahawk/infosystem/InfoSystem.h +++ b/src/libtomahawk/infosystem/InfoSystem.h @@ -214,6 +214,7 @@ public slots: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); void finished( QString target, Tomahawk::InfoSystem::InfoType type ); + void ready(); void updatedSupportedGetTypes( Tomahawk::InfoSystem::InfoTypeSet supportedTypes ); void updatedSupportedPushTypes( Tomahawk::InfoSystem::InfoTypeSet supportedTypes ); diff --git a/src/libtomahawk/infosystem/InfoSystemWorker.cpp b/src/libtomahawk/infosystem/InfoSystemWorker.cpp index eaaf64765c..57debb2718 100644 --- a/src/libtomahawk/infosystem/InfoSystemWorker.cpp +++ b/src/libtomahawk/infosystem/InfoSystemWorker.cpp @@ -43,6 +43,7 @@ namespace InfoSystem InfoSystemWorker::InfoSystemWorker() : QObject() + , m_cache( 0 ) { tDebug() << Q_FUNC_INFO; diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index c77ca8e21c..f1db0942d3 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -182,7 +182,6 @@ TomahawkApp::init() #endif TomahawkUtils::setHeadless( m_headless ); - TomahawkSettings* s = TomahawkSettings::instance(); new ACLRegistryImpl( this ); tDebug( LOGINFO ) << "Setting NAM."; @@ -248,89 +247,7 @@ TomahawkApp::init() tDebug() << "Init InfoSystem."; m_infoSystem = QPointer( Tomahawk::InfoSystem::InfoSystem::instance() ); - tDebug() << "Init AccountManager."; - m_accountManager = QPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); - connect( m_accountManager.data(), SIGNAL( readyForFactories() ), SLOT( initFactoriesForAccountManager() ) ); - connect( m_accountManager.data(), SIGNAL( readyForSip() ), SLOT( initSIP() ) ); - - Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); -#ifndef ENABLE_HEADLESS - EchonestGenerator::setupCatalogs(); - - if ( !m_headless ) - { - tDebug() << "Init MainWindow."; - m_mainwindow = new TomahawkWindow(); - m_mainwindow->setWindowTitle( "Tomahawk" ); - m_mainwindow->setObjectName( "TH_Main_Window" ); - if ( !arguments().contains( "--hide" ) ) - { - m_mainwindow->show(); - } - } -#endif - - tDebug() << "Init Local Collection."; - initLocalCollection(); - tDebug() << "Init Pipeline."; - initPipeline(); - -#ifndef ENABLE_HEADLESS - // load remote list of resolvers able to be installed - AtticaManager::instance(); -#endif - - if ( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) - { - initHTTP(); - } - connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( initHTTP() ) ); - -#ifndef ENABLE_HEADLESS - if ( !s->hasScannerPaths() ) - { - m_mainwindow->showSettingsDialog(); - } -#endif - -#ifdef LIBLASTFM_FOUND - tDebug() << "Init Scrobbler."; - m_scrobbler = new Scrobbler( this ); -#endif - - if ( arguments().contains( "--filescan" ) ) - { - m_scanManager.data()->runFullRescan(); - } - - // Set up echonest catalog synchronizer - Tomahawk::EchonestCatalogSynchronizer::instance(); - - PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); - PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); - - // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome - // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced - // with the default value of 3 in QApplicationPrivate::initialize. - { - QSettings qt_settings( QSettings::UserScope, "Trolltech" ); - qt_settings.beginGroup( "Qt" ); - QApplication::setWheelScrollLines( qt_settings.value( "wheelScrollLines", QApplication::wheelScrollLines() ).toInt() ); - } - -#ifndef ENABLE_HEADLESS - // Make sure to init GAM in the gui thread - GlobalActionManager::instance(); - - // check if our spotify playlist api server is up and running, and enable spotify playlist drops if so - QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/pong" ) ) ); - connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) ); -#endif - -#ifdef Q_OS_MAC - // Make sure to do this after main window is inited - Tomahawk::enableFullscreen( m_mainwindow ); -#endif + connect( m_infoSystem, SIGNAL( ready() ), SLOT( initAccountManager() ) ); } @@ -573,6 +490,97 @@ TomahawkApp::initHTTP() } +void +TomahawkApp::initAccountManager() +{ + tDebug() << "Init AccountManager."; + m_accountManager = QPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); + connect( m_accountManager.data(), SIGNAL( readyForFactories() ), SLOT( initFactoriesForAccountManager() ) ); + connect( m_accountManager.data(), SIGNAL( readyForSip() ), SLOT( initSIP() ) ); + + TomahawkSettings* s = TomahawkSettings::instance(); + + Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); +#ifndef ENABLE_HEADLESS + EchonestGenerator::setupCatalogs(); + + if ( !m_headless ) + { + tDebug() << "Init MainWindow."; + m_mainwindow = new TomahawkWindow(); + m_mainwindow->setWindowTitle( "Tomahawk" ); + m_mainwindow->setObjectName( "TH_Main_Window" ); + if ( !arguments().contains( "--hide" ) ) + { + m_mainwindow->show(); + } + } +#endif + + tDebug() << "Init Local Collection."; + initLocalCollection(); + tDebug() << "Init Pipeline."; + initPipeline(); + +#ifndef ENABLE_HEADLESS + // load remote list of resolvers able to be installed + AtticaManager::instance(); +#endif + + if ( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) + { + initHTTP(); + } + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( initHTTP() ) ); + +#ifndef ENABLE_HEADLESS + if ( !s->hasScannerPaths() ) + { + m_mainwindow->showSettingsDialog(); + } +#endif + +#ifdef LIBLASTFM_FOUND + tDebug() << "Init Scrobbler."; + m_scrobbler = new Scrobbler( this ); +#endif + + if ( arguments().contains( "--filescan" ) ) + { + m_scanManager.data()->runFullRescan(); + } + + // Set up echonest catalog synchronizer + Tomahawk::EchonestCatalogSynchronizer::instance(); + + PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); + PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); + + // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome + // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced + // with the default value of 3 in QApplicationPrivate::initialize. + { + QSettings qt_settings( QSettings::UserScope, "Trolltech" ); + qt_settings.beginGroup( "Qt" ); + QApplication::setWheelScrollLines( qt_settings.value( "wheelScrollLines", QApplication::wheelScrollLines() ).toInt() ); + } + +#ifndef ENABLE_HEADLESS + // Make sure to init GAM in the gui thread + GlobalActionManager::instance(); + + // check if our spotify playlist api server is up and running, and enable spotify playlist drops if so + QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/pong" ) ) ); + connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) ); +#endif + +#ifdef Q_OS_MAC + // Make sure to do this after main window is inited + Tomahawk::enableFullscreen( m_mainwindow ); +#endif +} + + void TomahawkApp::initPipeline() { diff --git a/src/tomahawk/TomahawkApp.h b/src/tomahawk/TomahawkApp.h index 761e63fdb9..52c6e97e48 100644 --- a/src/tomahawk/TomahawkApp.h +++ b/src/tomahawk/TomahawkApp.h @@ -112,6 +112,7 @@ private slots: void initSIP(); void initHTTP(); void initFactoriesForAccountManager(); + void initAccountManager(); void spotifyApiCheckFinished(); From 5272cd5f9e54388de1431e4cd8c36392c398c74a Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 26 May 2013 19:25:28 +0200 Subject: [PATCH 105/565] Add missing emit ready() --- src/libtomahawk/infosystem/InfoSystem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/infosystem/InfoSystem.cpp b/src/libtomahawk/infosystem/InfoSystem.cpp index fb303cffae..4d7e8cda20 100644 --- a/src/libtomahawk/infosystem/InfoSystem.cpp +++ b/src/libtomahawk/infosystem/InfoSystem.cpp @@ -182,6 +182,7 @@ InfoSystem::init() QMetaObject::invokeMethod( worker, "init", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoSystemCache*, cache ) ); m_inited = true; + emit ready(); } From 1ba923fd215c577fb285fbc17db494862ba59032 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 27 May 2013 12:09:55 +0200 Subject: [PATCH 106/565] Revert "* Some fixes to InfoSystem and loading." This reverts commit 8460d8f5227e889f10805a9af87851ce654ec460. --- src/libtomahawk/infosystem/InfoSystem.h | 1 - .../infosystem/InfoSystemWorker.cpp | 1 - src/tomahawk/TomahawkApp.cpp | 176 +++++++++--------- src/tomahawk/TomahawkApp.h | 1 - 4 files changed, 84 insertions(+), 95 deletions(-) diff --git a/src/libtomahawk/infosystem/InfoSystem.h b/src/libtomahawk/infosystem/InfoSystem.h index 48c0547ffe..6a9e7f5272 100644 --- a/src/libtomahawk/infosystem/InfoSystem.h +++ b/src/libtomahawk/infosystem/InfoSystem.h @@ -214,7 +214,6 @@ public slots: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); void finished( QString target, Tomahawk::InfoSystem::InfoType type ); - void ready(); void updatedSupportedGetTypes( Tomahawk::InfoSystem::InfoTypeSet supportedTypes ); void updatedSupportedPushTypes( Tomahawk::InfoSystem::InfoTypeSet supportedTypes ); diff --git a/src/libtomahawk/infosystem/InfoSystemWorker.cpp b/src/libtomahawk/infosystem/InfoSystemWorker.cpp index 57debb2718..eaaf64765c 100644 --- a/src/libtomahawk/infosystem/InfoSystemWorker.cpp +++ b/src/libtomahawk/infosystem/InfoSystemWorker.cpp @@ -43,7 +43,6 @@ namespace InfoSystem InfoSystemWorker::InfoSystemWorker() : QObject() - , m_cache( 0 ) { tDebug() << Q_FUNC_INFO; diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index f1db0942d3..c77ca8e21c 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -182,6 +182,7 @@ TomahawkApp::init() #endif TomahawkUtils::setHeadless( m_headless ); + TomahawkSettings* s = TomahawkSettings::instance(); new ACLRegistryImpl( this ); tDebug( LOGINFO ) << "Setting NAM."; @@ -247,7 +248,89 @@ TomahawkApp::init() tDebug() << "Init InfoSystem."; m_infoSystem = QPointer( Tomahawk::InfoSystem::InfoSystem::instance() ); - connect( m_infoSystem, SIGNAL( ready() ), SLOT( initAccountManager() ) ); + tDebug() << "Init AccountManager."; + m_accountManager = QPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); + connect( m_accountManager.data(), SIGNAL( readyForFactories() ), SLOT( initFactoriesForAccountManager() ) ); + connect( m_accountManager.data(), SIGNAL( readyForSip() ), SLOT( initSIP() ) ); + + Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); +#ifndef ENABLE_HEADLESS + EchonestGenerator::setupCatalogs(); + + if ( !m_headless ) + { + tDebug() << "Init MainWindow."; + m_mainwindow = new TomahawkWindow(); + m_mainwindow->setWindowTitle( "Tomahawk" ); + m_mainwindow->setObjectName( "TH_Main_Window" ); + if ( !arguments().contains( "--hide" ) ) + { + m_mainwindow->show(); + } + } +#endif + + tDebug() << "Init Local Collection."; + initLocalCollection(); + tDebug() << "Init Pipeline."; + initPipeline(); + +#ifndef ENABLE_HEADLESS + // load remote list of resolvers able to be installed + AtticaManager::instance(); +#endif + + if ( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) + { + initHTTP(); + } + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( initHTTP() ) ); + +#ifndef ENABLE_HEADLESS + if ( !s->hasScannerPaths() ) + { + m_mainwindow->showSettingsDialog(); + } +#endif + +#ifdef LIBLASTFM_FOUND + tDebug() << "Init Scrobbler."; + m_scrobbler = new Scrobbler( this ); +#endif + + if ( arguments().contains( "--filescan" ) ) + { + m_scanManager.data()->runFullRescan(); + } + + // Set up echonest catalog synchronizer + Tomahawk::EchonestCatalogSynchronizer::instance(); + + PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); + PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); + + // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome + // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced + // with the default value of 3 in QApplicationPrivate::initialize. + { + QSettings qt_settings( QSettings::UserScope, "Trolltech" ); + qt_settings.beginGroup( "Qt" ); + QApplication::setWheelScrollLines( qt_settings.value( "wheelScrollLines", QApplication::wheelScrollLines() ).toInt() ); + } + +#ifndef ENABLE_HEADLESS + // Make sure to init GAM in the gui thread + GlobalActionManager::instance(); + + // check if our spotify playlist api server is up and running, and enable spotify playlist drops if so + QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/pong" ) ) ); + connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) ); +#endif + +#ifdef Q_OS_MAC + // Make sure to do this after main window is inited + Tomahawk::enableFullscreen( m_mainwindow ); +#endif } @@ -490,97 +573,6 @@ TomahawkApp::initHTTP() } -void -TomahawkApp::initAccountManager() -{ - tDebug() << "Init AccountManager."; - m_accountManager = QPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); - connect( m_accountManager.data(), SIGNAL( readyForFactories() ), SLOT( initFactoriesForAccountManager() ) ); - connect( m_accountManager.data(), SIGNAL( readyForSip() ), SLOT( initSIP() ) ); - - TomahawkSettings* s = TomahawkSettings::instance(); - - Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); -#ifndef ENABLE_HEADLESS - EchonestGenerator::setupCatalogs(); - - if ( !m_headless ) - { - tDebug() << "Init MainWindow."; - m_mainwindow = new TomahawkWindow(); - m_mainwindow->setWindowTitle( "Tomahawk" ); - m_mainwindow->setObjectName( "TH_Main_Window" ); - if ( !arguments().contains( "--hide" ) ) - { - m_mainwindow->show(); - } - } -#endif - - tDebug() << "Init Local Collection."; - initLocalCollection(); - tDebug() << "Init Pipeline."; - initPipeline(); - -#ifndef ENABLE_HEADLESS - // load remote list of resolvers able to be installed - AtticaManager::instance(); -#endif - - if ( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) - { - initHTTP(); - } - connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( initHTTP() ) ); - -#ifndef ENABLE_HEADLESS - if ( !s->hasScannerPaths() ) - { - m_mainwindow->showSettingsDialog(); - } -#endif - -#ifdef LIBLASTFM_FOUND - tDebug() << "Init Scrobbler."; - m_scrobbler = new Scrobbler( this ); -#endif - - if ( arguments().contains( "--filescan" ) ) - { - m_scanManager.data()->runFullRescan(); - } - - // Set up echonest catalog synchronizer - Tomahawk::EchonestCatalogSynchronizer::instance(); - - PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); - PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); - - // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome - // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced - // with the default value of 3 in QApplicationPrivate::initialize. - { - QSettings qt_settings( QSettings::UserScope, "Trolltech" ); - qt_settings.beginGroup( "Qt" ); - QApplication::setWheelScrollLines( qt_settings.value( "wheelScrollLines", QApplication::wheelScrollLines() ).toInt() ); - } - -#ifndef ENABLE_HEADLESS - // Make sure to init GAM in the gui thread - GlobalActionManager::instance(); - - // check if our spotify playlist api server is up and running, and enable spotify playlist drops if so - QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/pong" ) ) ); - connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) ); -#endif - -#ifdef Q_OS_MAC - // Make sure to do this after main window is inited - Tomahawk::enableFullscreen( m_mainwindow ); -#endif -} - - void TomahawkApp::initPipeline() { diff --git a/src/tomahawk/TomahawkApp.h b/src/tomahawk/TomahawkApp.h index 52c6e97e48..761e63fdb9 100644 --- a/src/tomahawk/TomahawkApp.h +++ b/src/tomahawk/TomahawkApp.h @@ -112,7 +112,6 @@ private slots: void initSIP(); void initHTTP(); void initFactoriesForAccountManager(); - void initAccountManager(); void spotifyApiCheckFinished(); From cba83e5225c974c02199e09d145d14d76c40d4ff Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 27 May 2013 12:10:09 +0200 Subject: [PATCH 107/565] Revert "Add missing emit ready()" This reverts commit cc336e9d45f1f466251a43696c8a9417d0d272bf. --- src/libtomahawk/infosystem/InfoSystem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/infosystem/InfoSystem.cpp b/src/libtomahawk/infosystem/InfoSystem.cpp index 4d7e8cda20..fb303cffae 100644 --- a/src/libtomahawk/infosystem/InfoSystem.cpp +++ b/src/libtomahawk/infosystem/InfoSystem.cpp @@ -182,7 +182,6 @@ InfoSystem::init() QMetaObject::invokeMethod( worker, "init", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoSystemCache*, cache ) ); m_inited = true; - emit ready(); } From c41c47585295e2a865dd431573d9e52bf707d6b0 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 13:17:44 +0200 Subject: [PATCH 108/565] Servent should listen to IPv6 too --- src/tomahawk/TomahawkApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index c77ca8e21c..13765e795d 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -610,7 +610,7 @@ TomahawkApp::initServent() bool upnp = !arguments().contains( "--noupnp" ); int port = TomahawkSettings::instance()->externalPort(); - if ( !Servent::instance()->startListening( QHostAddress( QHostAddress::Any ), upnp, port ) ) + if ( !Servent::instance()->startListening( QHostAddress( QHostAddress::AnyIPv6 ), upnp, port ) ) { tLog() << "Failed to start listening with servent"; exit( 1 ); From 0807a3ce4ed4919d025c3a24dcd2b2d3a47e9d6f Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 13:18:26 +0200 Subject: [PATCH 109/565] Change option description to reflect new functionality --- src/tomahawk/Settings_Advanced.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tomahawk/Settings_Advanced.ui b/src/tomahawk/Settings_Advanced.ui index 08636c286f..5b50eda483 100644 --- a/src/tomahawk/Settings_Advanced.ui +++ b/src/tomahawk/Settings_Advanced.ui @@ -32,7 +32,7 @@ - None (outgoing connections only) + Active (your host needs to be directly reachable) From 812b4f6ee3c0af246820e0f955338eb0ea8381da Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 13:19:57 +0200 Subject: [PATCH 110/565] Support multiple SipInfos per peer --- src/accounts/xmpp/sip/TomahawkXmppMessage.cpp | 55 +-- src/accounts/xmpp/sip/TomahawkXmppMessage.h | 21 +- .../xmpp/sip/TomahawkXmppMessageFactory.cpp | 142 ++++-- .../xmpp/sip/TomahawkXmppMessageFactory.h | 11 +- src/accounts/xmpp/sip/XmppSip.cpp | 34 +- src/accounts/xmpp/sip/XmppSip.h | 2 +- src/accounts/zeroconf/Zeroconf.cpp | 4 +- src/accounts/zeroconf/Zeroconf.h | 2 +- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/network/QTcpSocketExtra.cpp | 64 +++ src/libtomahawk/network/QTcpSocketExtra.h | 82 ++++ src/libtomahawk/network/Servent.cpp | 426 +++++++++++------- src/libtomahawk/network/Servent.h | 70 ++- src/libtomahawk/sip/PeerInfo.cpp | 31 +- src/libtomahawk/sip/PeerInfo.h | 14 +- src/libtomahawk/sip/SipPlugin.h | 3 +- src/tomahawk/DiagnosticsDialog.cpp | 79 +--- 17 files changed, 630 insertions(+), 411 deletions(-) create mode 100644 src/libtomahawk/network/QTcpSocketExtra.cpp create mode 100644 src/libtomahawk/network/QTcpSocketExtra.h diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp index ef29518126..f7f5ca8e9c 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp @@ -21,60 +21,39 @@ #include "utils/Logger.h" - -class TomahawkXmppMessagePrivate -{ -public: - QString ip; - int port; - QString uniqname; - QString key; - bool visible; -}; - -TomahawkXmppMessage::TomahawkXmppMessage(const QString &ip, unsigned int port, const QString &uniqname, const QString &key) : d_ptr(new TomahawkXmppMessagePrivate) +TomahawkXmppMessage::TomahawkXmppMessage() : m_sipInfo() { - Q_D(TomahawkXmppMessage); - d->ip = ip; - d->port = port; - d->uniqname = uniqname; - d->key = key; - d->visible = true; } -TomahawkXmppMessage::TomahawkXmppMessage() : d_ptr(new TomahawkXmppMessagePrivate) +TomahawkXmppMessage::TomahawkXmppMessage( const QList &sipInfo ) : m_sipInfo( sipInfo ) { - Q_D(TomahawkXmppMessage); - d->visible = false; - d->port = -1; } - TomahawkXmppMessage::~TomahawkXmppMessage() { } -const QString TomahawkXmppMessage::ip() const +const QList +TomahawkXmppMessage::sipInfo() const { - return d_func()->ip; + return m_sipInfo; } -unsigned int TomahawkXmppMessage::port() const -{ - return d_func()->port; -} - -const QString TomahawkXmppMessage::uniqname() const -{ - return d_func()->uniqname; -} -const QString TomahawkXmppMessage::key() const +const QString +TomahawkXmppMessage::key() const { - return d_func()->key; + if ( m_sipInfo.length() > 0 ) + return m_sipInfo.first().key(); + else + return QString(); } -bool TomahawkXmppMessage::visible() const +const QString +TomahawkXmppMessage::uniqname() const { - return d_func()->visible; + if ( m_sipInfo.length() > 0 ) + return m_sipInfo.first().nodeId(); + else + return QString(); } diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessage.h b/src/accounts/xmpp/sip/TomahawkXmppMessage.h index d26ac30aec..9ea943b8b6 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessage.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessage.h @@ -22,30 +22,29 @@ #include +#include "sip/SipInfo.h" + #define TOMAHAWK_SIP_MESSAGE_NS QLatin1String("http://www.tomhawk-player.org/sip/transports") #include "accounts/AccountDllMacro.h" -class TomahawkXmppMessagePrivate; class ACCOUNTDLLEXPORT TomahawkXmppMessage : public Jreen::Payload { J_PAYLOAD(TomahawkXmppMessage) - Q_DECLARE_PRIVATE(TomahawkXmppMessage) public: - // sets visible to true - TomahawkXmppMessage(const QString &ip, unsigned int port, const QString &uniqname, const QString &key); - - // sets visible to false as we dont have any extra information TomahawkXmppMessage(); + TomahawkXmppMessage(const QList& sipInfo); ~TomahawkXmppMessage(); - const QString ip() const; - unsigned int port() const; - const QString uniqname() const; + //! The SipInfo objects that are wrapped in this XmppMessage + const QList sipInfo() const; + //! The name of the peer contained in this message const QString key() const; - bool visible() const; + //! The name of the peer contained in this message + const QString uniqname() const; + private: - QScopedPointer d_ptr; + QList m_sipInfo; }; #endif // ENTITYTIME_H diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index 5c6a3f81f4..04cb9b9e31 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -19,6 +19,7 @@ #include "TomahawkXmppMessageFactory.h" +#include "network/Servent.h" #include "utils/Logger.h" #include @@ -28,7 +29,7 @@ using namespace Jreen; -TomahawkXmppMessageFactory::TomahawkXmppMessageFactory() +TomahawkXmppMessageFactory::TomahawkXmppMessageFactory() : m_sipInfo() { m_depth = 0; m_state = AtNowhere; @@ -54,31 +55,35 @@ void TomahawkXmppMessageFactory::handleStartElement(const QStringRef &name, cons const QXmlStreamAttributes &attributes) { m_depth++; - if (m_depth == 1) { + if ( m_depth == 1 ) + { m_state = AtNowhere; - m_ip = QString(); - m_port = -1; m_uniqname = QString(); m_key = QString(); - m_visible = false; - } else if (m_depth == 2) { - if (name == QLatin1String("transport")) + m_sipInfo = QList(); + } + else if ( m_depth == 2 ) + { + if ( name == QLatin1String( "transport" ) ) { -// qDebug() << "Found Transport"; m_state = AtTransport; - - m_uniqname = attributes.value(QLatin1String("uniqname")).toString(); - m_key = attributes.value(QLatin1String("pwd")).toString(); + m_uniqname = attributes.value( QLatin1String( "uniqname" ) ).toString(); + m_key = attributes.value( QLatin1String( "pwd" ) ).toString(); } - } else if(m_depth == 3) { - if (name == QLatin1String("candidate")) + } + else if(m_depth == 3) + { + if ( name == QLatin1String( "candidate" ) ) { m_state = AtCandidate; -// qDebug() << "Found candidate"; - m_ip = attributes.value(QLatin1String("ip")).toString(); - m_port = attributes.value(QLatin1String("port")).toString().toInt(); - - m_visible = true; + SipInfo info = SipInfo(); + info.setVisible( true ); + info.setHost( attributes.value( QLatin1String( "ip" ) ).toString() ); + info.setPort( attributes.value( QLatin1String( "port" ) ).toString().toInt() ); + info.setKey( m_key ); + info.setNodeId( m_uniqname ); + Q_ASSERT( info.isValid() ); + m_sipInfo.append( info ); } } Q_UNUSED(uri); @@ -87,8 +92,22 @@ void TomahawkXmppMessageFactory::handleStartElement(const QStringRef &name, cons void TomahawkXmppMessageFactory::handleEndElement(const QStringRef &name, const QStringRef &uri) { - if (m_depth == 3) + if ( m_depth == 3 ) + m_state = AtTransport; + else if ( m_depth == 2 ) + { m_state = AtNowhere; + // Check that we have at least one SipInfo so that we provide some information about invisible peers. + if ( m_sipInfo.length() == 0 ) + { + SipInfo info = SipInfo(); + info.setVisible( false ); + info.setKey( m_key ); + info.setNodeId( m_uniqname ); + Q_ASSERT( info.isValid() ); + m_sipInfo.append( info ); + } + } Q_UNUSED(name); Q_UNUSED(uri); m_depth--; @@ -111,38 +130,69 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter { TomahawkXmppMessage *sipMessage = se_cast(extension); - writer->writeStartElement(QLatin1String("tomahawk")); - writer->writeDefaultNamespace(TOMAHAWK_SIP_MESSAGE_NS); + writer->writeStartElement( QLatin1String( "tomahawk" ) ); + writer->writeDefaultNamespace( TOMAHAWK_SIP_MESSAGE_NS ); - if(sipMessage->visible()) + // Get a copy of the list, so that we can modify it here. + QList sipInfo = QList( sipMessage->sipInfo() ); + QSharedPointer lastInfo = QSharedPointer(); + foreach ( SipInfo info, sipInfo ) + { + if ( info.isVisible() ) { - // add transport tag - writer->writeStartElement(QLatin1String("transport")); - writer->writeAttribute(QLatin1String("pwd"), sipMessage->key()); - writer->writeAttribute(QLatin1String("uniqname"), sipMessage->uniqname()); - - writer->writeEmptyElement(QLatin1String("candidate")); - writer->writeAttribute(QLatin1String("component"), "1"); - writer->writeAttribute(QLatin1String("id"), "el0747fg11"); // FIXME - writer->writeAttribute(QLatin1String("ip"), sipMessage->ip()); - writer->writeAttribute(QLatin1String("network"), "1"); - writer->writeAttribute(QLatin1String("port"), QVariant(sipMessage->port()).toString()); - writer->writeAttribute(QLatin1String("priority"), "1"); //TODO - writer->writeAttribute(QLatin1String("protocol"), "tcp"); - writer->writeAttribute(QLatin1String("type"), "host"); //FIXME: correct?! - writer->writeEndElement(); + QHostAddress ha = QHostAddress( info.host() ); + if ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) + { + // For comapability reasons, this shall be put as the last candidate + lastInfo = QSharedPointer( new SipInfo( info ) ); + sipInfo.removeOne( info ); + break; + } } - else - { - writer->writeEmptyElement(QLatin1String("transport")); - } - writer->writeEndElement(); + } + + writer->writeStartElement( QLatin1String( "transport" ) ); + writer->writeAttribute( QLatin1String( "pwd" ), sipMessage->key() ); + writer->writeAttribute( QLatin1String( "uniqname" ), sipMessage->uniqname() ); + + foreach ( SipInfo info, sipInfo ) + { + if ( info.isVisible() ) + serializeSipInfo( info, writer ); + } + + if ( !lastInfo.isNull() ) + { + Q_ASSERT( lastInfo->isVisible() ); + serializeSipInfo( *lastInfo, writer ); + } + + // + writer->writeEndElement(); } -Payload::Ptr TomahawkXmppMessageFactory::createPayload() +Payload::Ptr +TomahawkXmppMessageFactory::createPayload() { - if(m_visible) - return Payload::Ptr(new TomahawkXmppMessage(m_ip, m_port, m_uniqname, m_key)); + if ( ( ( m_sipInfo.length() == 1 ) && ( !m_sipInfo.first().isVisible() ) ) || ( m_sipInfo.length() < 1 ) ) + return Payload::Ptr( new TomahawkXmppMessage() ); else - return Payload::Ptr(new TomahawkXmppMessage()); + return Payload::Ptr( new TomahawkXmppMessage( m_sipInfo ) ); +} + +void +TomahawkXmppMessageFactory::serializeSipInfo(SipInfo &info, QXmlStreamWriter *writer) +{ + if ( info.isVisible() ) + { + writer->writeEmptyElement( QLatin1String( "candidate" ) ); + writer->writeAttribute( QLatin1String( "component" ), "1" ); + writer->writeAttribute( QLatin1String( "id" ), "el0747fg11" ); // FIXME + writer->writeAttribute( QLatin1String( "ip" ), info.host() ); + writer->writeAttribute( QLatin1String( "network" ), "1" ); + writer->writeAttribute( QLatin1String( "port" ), QVariant( info.port() ).toString() ); + writer->writeAttribute( QLatin1String( "priority" ), "1" ); //TODO + writer->writeAttribute( QLatin1String( "protocol" ), "tcp" ); + writer->writeAttribute( QLatin1String( "type" ), "host" ); //FIXME: correct?! + } } diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h index f1910cb400..ba06f7a2c2 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h @@ -39,13 +39,18 @@ class ACCOUNTDLLEXPORT TomahawkXmppMessageFactory : public Jreen::PayloadFactory void serialize(Jreen::Payload *extension, QXmlStreamWriter *writer); Jreen::Payload::Ptr createPayload(); private: + void serializeSipInfo(SipInfo& info, QXmlStreamWriter *writer); + enum State { AtNowhere, AtTransport, AtCandidate } m_state; + + //! All the provided Sip informations + QList m_sipInfo; + //! The current parsing depth int m_depth; - QString m_ip; - int m_port; + //! The unique name of the peer QString m_uniqname; + //! The authentication key of the peer QString m_key; - bool m_visible; }; #endif // ENTITYTIMEFACTORY_P_H diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index cd878d3928..a2463ce5f3 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -435,22 +435,15 @@ XmppSipPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) void -XmppSipPlugin::sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const SipInfo& info ) +XmppSipPlugin::sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << receiver << info; if ( !m_client ) return; - TomahawkXmppMessage *sipMessage; - if ( info.isVisible() ) - { - sipMessage = new TomahawkXmppMessage( info.host(), info.port(), info.nodeId(), info.key() ); - } - else - sipMessage = new TomahawkXmppMessage(); - - qDebug() << "Send sip messsage to" << receiver; + TomahawkXmppMessage* sipMessage = new TomahawkXmppMessage( info ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Send sip messsage to" << receiver; Jreen::IQ iq( Jreen::IQ::Set, receiver->id() ); iq.addExtension( sipMessage ); Jreen::IQReply *reply = m_client->send( iq ); @@ -687,6 +680,7 @@ XmppSipPlugin::onNewMessage( const Jreen::Message& message ) return; } + // FIXME: We do not sent SipInfo in JSON via XMPP, why do we receive it here? SipInfo info = SipInfo::fromJson( msg ); if ( !info.isValid() ) { @@ -912,30 +906,22 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) if ( sipMessage ) { iq.accept(); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Received Sip Information from:" << iq.from().full(); - qDebug() << Q_FUNC_INFO << "Got SipMessage ..." - << "ip" << sipMessage->ip() << "port" << sipMessage->port() << "nodeId" << sipMessage->uniqname() << "key" << sipMessage->key() << "visible" << sipMessage->visible(); - - SipInfo info; - info.setVisible( sipMessage->visible() ); - if ( sipMessage->visible() ) + // Check that all received SipInfos are valid. + foreach ( SipInfo info, sipMessage->sipInfo() ) { - info.setHost( sipMessage->ip() ); - info.setPort( sipMessage->port() ); - info.setNodeId( sipMessage->uniqname() ); - info.setKey( sipMessage->key() ); + Q_ASSERT( info.isValid() ); } - Q_ASSERT( info.isValid() ); - - qDebug() << Q_FUNC_INFO << "From:" << iq.from().full() << ":" << info; + // Get the peer information for the sender. Tomahawk::peerinfo_ptr peerInfo = PeerInfo::get( this, iq.from().full() ); if ( peerInfo.isNull() ) { tDebug() << Q_FUNC_INFO << "no valid peerInfo for" << iq.from().full(); return; } - peerInfo->setSipInfo( info ); + peerInfo->setSipInfo( sipMessage->sipInfo() ); } } } diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index fabcbf4380..43b43abb61 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -90,7 +90,7 @@ public slots: virtual void configurationChanged(); virtual void addContact( const QString& peerId, const QString& msg = QString() ); - virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const SipInfo& info ); + virtual void sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ); void showAddFriendDialog(); void publishTune( const QUrl& url, const Tomahawk::InfoSystem::InfoStringHash& trackInfo ); diff --git a/src/accounts/zeroconf/Zeroconf.cpp b/src/accounts/zeroconf/Zeroconf.cpp index 7a73d8d1c0..1893b90b8a 100644 --- a/src/accounts/zeroconf/Zeroconf.cpp +++ b/src/accounts/zeroconf/Zeroconf.cpp @@ -161,7 +161,9 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name sipInfo.setVisible( true ); Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( this, host, Tomahawk::PeerInfo::AutoCreate ); - peerInfo->setSipInfo( sipInfo ); + QList sipInfoList = QList(); + sipInfoList.append( sipInfo ); + peerInfo->setSipInfo( sipInfoList ); peerInfo->setContactId( host ); peerInfo->setFriendlyName( name ); peerInfo->setType( PeerInfo::Local ); diff --git a/src/accounts/zeroconf/Zeroconf.h b/src/accounts/zeroconf/Zeroconf.h index d6803f8f4c..d1d660b8c6 100644 --- a/src/accounts/zeroconf/Zeroconf.h +++ b/src/accounts/zeroconf/Zeroconf.h @@ -65,7 +65,7 @@ public slots: void advertise(); - void sendSipInfo( const Tomahawk::peerinfo_ptr&, const SipInfo& ) {} + virtual void sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) {} void broadcastMsg( const QString & ) {} void addContact( const QString &, const QString& ) {} diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 48b53970f1..1fc6fca8d2 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -310,6 +310,7 @@ list(APPEND libSources network/Servent.cpp network/Connection.cpp network/ControlConnection.cpp + network/QTcpSocketExtra.cpp playlist/PlaylistUpdaterInterface.cpp playlist/dynamic/DynamicPlaylist.cpp diff --git a/src/libtomahawk/network/QTcpSocketExtra.cpp b/src/libtomahawk/network/QTcpSocketExtra.cpp new file mode 100644 index 0000000000..25aa087eea --- /dev/null +++ b/src/libtomahawk/network/QTcpSocketExtra.cpp @@ -0,0 +1,64 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "QTcpSocketExtra.h" + +#include "utils/Logger.h" + +void +QTcpSocketExtra::connectToHost( const QHostAddress& host, quint16 port, OpenMode openMode ) +{ + if ( m_connectTimer->isActive() == true ) + { + tLog() << Q_FUNC_INFO << "Connection already establishing."; + return; + } + + QTcpSocket::connectToHost( host, port, openMode); + if ( m_connectTimeout > 0 ) + m_connectTimer->start( m_connectTimeout ); +} + +void +QTcpSocketExtra::connectToHost(const QString& host, quint16 port, OpenMode openMode) +{ + if ( m_connectTimer->isActive() == true ) + { + tLog() << Q_FUNC_INFO << "Connection already establishing."; + return; + } + + QTcpSocket::connectToHost( host, port, openMode); + if ( m_connectTimeout > 0 ) + m_connectTimer->start( m_connectTimeout ); +} + +void +QTcpSocketExtra::connectTimeout() +{ + m_connectTimer->stop(); + if ( state() != ConnectedState ) + { + // We did not manage to connect in the given timespan, so abort the attempt... + close(); + // .. and notify error handlers. + emit error( SocketTimeoutError ); + } +} diff --git a/src/libtomahawk/network/QTcpSocketExtra.h b/src/libtomahawk/network/QTcpSocketExtra.h new file mode 100644 index 0000000000..d42e1a5e7c --- /dev/null +++ b/src/libtomahawk/network/QTcpSocketExtra.h @@ -0,0 +1,82 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef QTCPSOCKETEXTRA_H +#define QTCPSOCKETEXTRA_H + +// time before new connection terminates if no auth received +#define AUTH_TIMEOUT 180000 + +#include +#include +#include +#include + +#include "Msg.h" +#include "DllMacro.h" + +class Connection; + +// this is used to hold a bit of state, so when a connected signal is emitted +// from a socket, we can associate it with a Connection object etc. +// In addition, functionality to limit the connection timeout is implemented. +class DLLEXPORT QTcpSocketExtra : public QTcpSocket +{ +Q_OBJECT + +public: + QTcpSocketExtra() : QTcpSocket(), m_connectTimeout( -1 ) + { + QTimer::singleShot( AUTH_TIMEOUT, this, SLOT( authTimeout() ) ) ; + m_connectTimer = new QTimer( this ); + connect( m_connectTimer, SIGNAL( timeout() ), this, SLOT( connectTimeout() ) ); + } + + void connectToHost(const QString& host, quint16 port, OpenMode openMode = ReadWrite ); + void connectToHost( const QHostAddress& host, quint16 port, OpenMode openMode = ReadWrite ); + + QPointer _conn; + bool _outbound; + bool _disowned; + msg_ptr _msg; + + //! Set a time limit for establishing a connection. + void setConnectTimeout( qint32 timeout ) { m_connectTimeout = timeout; } + //! Get the current timeout for establishing a connection. + qint32 connectTimeout() const { return m_connectTimeout; } + +private slots: + void connectTimeout(); + void authTimeout() + { + if( _disowned ) + return; + + qDebug() << "Connection timed out before providing a valid offer-key"; + this->disconnectFromHost(); + } +private: + //! How long we will wait for a connection to establish + qint32 m_connectTimeout; + //! Timer to measure the connection initialisation + QTimer* m_connectTimer; +}; + +#endif // QTCPSOCKETEXTRA_H diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index e222639768..625c31c89c 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -34,6 +34,7 @@ #include "sip/SipPlugin.h" #include "PortFwdThread.h" #include "TomahawkSettings.h" +#include "utils/Closure.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" #include "accounts/AccountManager.h" @@ -50,6 +51,10 @@ #include + +typedef QPair< QList< SipInfo >, Connection* > sipConnectionPair; +Q_DECLARE_METATYPE( sipConnectionPair ) + using namespace Tomahawk; Servent* Servent::s_instance = 0; @@ -70,7 +75,6 @@ Servent::Servent( QObject* parent ) { s_instance = this; - m_lanHack = qApp->arguments().contains( "--lanhack" ); m_noAuth = qApp->arguments().contains( "--noauth" ); setProxy( QNetworkProxy::NoProxy ); @@ -113,6 +117,7 @@ Servent::~Servent() bool Servent::startListening( QHostAddress ha, bool upnp, int port ) { + m_externalAddresses = QList(); m_port = port; int defPort = TomahawkSettings::instance()->defaultPort(); @@ -125,8 +130,8 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) { if ( !listen( ha, defPort ) ) { - tLog() << "Failed to listen on both port" << m_port << "and port" << defPort; - tLog() << "Error string is:" << errorString(); + tLog() << Q_FUNC_INFO << "Failed to listen on both port" << m_port << "and port" << defPort; + tLog() << Q_FUNC_INFO << "Error string is:" << errorString(); return false; } else @@ -134,38 +139,63 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) } } - TomahawkSettings::ExternalAddressMode mode = TomahawkSettings::instance()->externalAddressMode(); + if ( ha == QHostAddress::AnyIPv6 ) + { + // We are listening on all available addresses, so we should send a SipInfo for all of them. + foreach ( QHostAddress addr, QNetworkInterface::allAddresses() ) + { + if ( addr.toString() == "127.0.0.1" ) + continue; // IPv4 localhost + if ( addr.toString() == "::1" ) + continue; // IPv6 localhost + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Listening to " << addr.toString(); + m_externalAddresses.append( addr ); + } - tLog() << "Servent listening on port" << m_port << "- servent thread:" << thread() + } + else if ( ( ha.toString() != "127.0.0.1" ) && ( ha.toString() != "::1" ) ) + { + // We listen only to one specific Address, only announce this. + m_externalAddresses.append( ha ); + } + // If we only accept connections via localhost, we'll announce nothing. + + TomahawkSettings::ExternalAddressMode mode = TomahawkSettings::instance()->externalAddressMode(); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Servent listening on port" << m_port << "- servent thread:" << thread() << "- address mode:" << (int)( mode ); - // --lanhack means to advertise your LAN IP as if it were externally visible switch ( mode ) { case TomahawkSettings::Static: m_externalHostname = TomahawkSettings::instance()->externalHostname(); m_externalPort = TomahawkSettings::instance()->externalPort(); m_ready = true; + // All setup is made, were done. emit ready(); break; case TomahawkSettings::Lan: - setInternalAddress(); + // Nothing has to be done here. + m_ready = true; + emit ready(); break; case TomahawkSettings::Upnp: - if ( !upnp ) + if ( upnp ) { - setInternalAddress(); - break; + // upnp could be turned of on the cli with --noupnp + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "External address mode set to upnp..."; + m_portfwd = QPointer< PortFwdThread >( new PortFwdThread( m_port ) ); + Q_ASSERT( m_portfwd ); + connect( m_portfwd.data(), SIGNAL( externalAddressDetected( QHostAddress, unsigned int ) ), + SLOT( setExternalAddress( QHostAddress, unsigned int ) ) ); + m_portfwd.data()->start(); + } + else + { + m_ready = true; + emit ready(); } - // TODO check if we have a public/internet IP on this machine directly - tLog() << "External address mode set to upnp..."; - m_portfwd = QPointer< PortFwdThread >( new PortFwdThread( m_port ) ); - Q_ASSERT( m_portfwd ); - connect( m_portfwd.data(), SIGNAL( externalAddressDetected( QHostAddress, unsigned int ) ), - SLOT( setExternalAddress( QHostAddress, unsigned int ) ) ); - m_portfwd.data()->start(); break; } @@ -173,6 +203,25 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) } +void +Servent::setExternalAddress( QHostAddress ha, unsigned int port ) +{ + if ( isValidExternalIP( ha ) ) + { + m_externalHostname = ha.toString(); + m_externalPort = port; + } + + if ( m_externalPort == 0 || !isValidExternalIP( ha ) ) + tLog() << Q_FUNC_INFO << "UPnP failed, no further external address could be acquired!"; + else + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "UPnP setup successful"; + + m_ready = true; + emit ready(); +} + + QString Servent::createConnectionKey( const QString& name, const QString &nodeid, const QString &key, bool onceOnly ) { @@ -192,65 +241,62 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const bool -Servent::isValidExternalIP( const QHostAddress& addr ) const +Servent::isValidExternalIP( const QHostAddress& addr ) { QString ip = addr.toString(); - if ( !m_lanHack && ( ip.startsWith( "10." ) || ip.startsWith( "172.16." ) || ip.startsWith( "192.168." ) ) ) + if (addr.protocol() == QAbstractSocket::IPv4Protocol) { - return false; + // private network + if ( addr.isInSubnet(QHostAddress::parseSubnet("10.0.0.0/8")) ) + return false; + // localhost + if ( addr.isInSubnet(QHostAddress::parseSubnet("127.0.0.0/8")) ) + return false; + // private network + if ( addr.isInSubnet(QHostAddress::parseSubnet("169.254.0.0/16")) ) + return false; + // private network + if ( addr.isInSubnet(QHostAddress::parseSubnet("172.16.0.0/12")) ) + return false; + // private network + if ( addr.isInSubnet(QHostAddress::parseSubnet("192.168.0.0/16")) ) + return false; + // multicast + if ( addr.isInSubnet(QHostAddress::parseSubnet("224.0.0.0/4")) ) + return false; } - - return !addr.isNull(); -} - - -void -Servent::setInternalAddress() -{ - foreach ( QHostAddress ha, QNetworkInterface::allAddresses() ) + else if (addr.protocol() == QAbstractSocket::IPv4Protocol) { - if ( ha.toString() == "127.0.0.1" ) - continue; - if ( ha.toString().contains( ":" ) ) - continue; //ipv6 - - if ( m_lanHack && isValidExternalIP( ha ) ) - { - tLog() << "LANHACK: set external address to lan address" << ha.toString(); - setExternalAddress( ha, m_port ); - } - else - { - m_ready = true; - emit ready(); - } - break; + // "unspecified address" + if ( addr.isInSubnet(QHostAddress::parseSubnet("::/128")) ) + return false; + // link-local + if ( addr.isInSubnet(QHostAddress::parseSubnet("fe80::/10")) ) + return false; + // unique local addresses + if ( addr.isInSubnet(QHostAddress::parseSubnet("fc00::/7")) ) + return false; + // benchmarking only + if ( addr.isInSubnet(QHostAddress::parseSubnet("2001:2::/48")) ) + return false; + // non-routed IPv6 addresses used for Cryptographic Hash Identifiers + if ( addr.isInSubnet(QHostAddress::parseSubnet("2001:10::/28")) ) + return false; + // documentation prefix + if ( addr.isInSubnet(QHostAddress::parseSubnet("2001:db8::/32")) ) + return false; + // multicast + if ( addr.isInSubnet(QHostAddress::parseSubnet("ff00::0/8 ")) ) + return false; } -} - - -void -Servent::setExternalAddress( QHostAddress ha, unsigned int port ) -{ - if ( isValidExternalIP( ha ) ) - { - m_externalAddress = ha; - m_externalPort = port; - } - - if ( m_externalPort == 0 || !isValidExternalIP( ha ) ) + else { - tLog() << "UPnP failed, LAN and outbound connections only!"; - setInternalAddress(); - return; + return false; } - tLog() << "UPnP setup successful"; - m_ready = true; - emit ready(); + return !addr.isNull(); } - void Servent::registerOffer( const QString& key, Connection* conn ) { @@ -312,7 +358,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) if ( peerInfo->type() == Tomahawk::PeerInfo::Local ) { peerInfoDebug(peerInfo) << "we need to establish the connection now... thinking"; - if ( !connectedToSession( peerInfo->sipInfo().nodeId() ) ) + if ( !connectedToSession( peerInfo->sipInfo().first().nodeId() ) ) { connectToPeer( peerInfo ); } @@ -335,34 +381,47 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) } else { - SipInfo info; - QString peerId = peerInfo->id(); QString key = uuid(); - ControlConnection* conn = new ControlConnection( this ); - const QString& nodeid = Database::instance()->impl()->dbid(); + + ControlConnection* conn = new ControlConnection( this ); conn->setName( peerInfo->contactId() ); conn->setId( nodeid ); conn->addPeerInfo( peerInfo ); - if ( visibleExternally() ) + QList sipInfo = QList(); + foreach ( QHostAddress ha, m_externalAddresses ) { - registerOffer( key, conn ); + SipInfo info = SipInfo(); + info.setHost( ha.toString() ); + info.setPort( m_port ); + info.setKey( key ); info.setVisible( true ); - info.setHost( externalAddress() ); - info.setPort( externalPort() ); + info.setNodeId( nodeid ); + sipInfo.append( info ); + } + if ( m_externalHostname.length() > 0) + { + SipInfo info = SipInfo(); + info.setHost( m_externalHostname ); + info.setPort( m_externalPort ); info.setKey( key ); + info.setVisible( true ); info.setNodeId( nodeid ); - - tDebug() << "Asking them (" << peerInfo->id() << ") to connect to us:" << info; + sipInfo.append( info ); } - else + + if ( sipInfo.length() == 0 ) { + // We are not visible via any IP, send a dummy SipInfo + SipInfo info = SipInfo(); info.setVisible( false ); - tDebug() << "We are not visible externally:" << info; + info.setKey( key ); + info.setNodeId( nodeid ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Only accepting connections, no usable IP to listen to found."; } - peerInfo->sendLocalSipInfo( info ); + peerInfo->sendLocalSipInfo( sipInfo ); handleSipInfo( peerInfo ); connect( peerInfo.data(), SIGNAL( sipInfoChanged() ), SLOT( onSipInfoChanged() ) ); @@ -384,43 +443,29 @@ Servent::onSipInfoChanged() void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) { - tLog() << Q_FUNC_INFO << peerInfo->id() << peerInfo->sipInfo(); - - SipInfo info = peerInfo->sipInfo(); - if ( !info.isValid() ) - return; + // FIXME: Do we need this? + // SipInfo info = peerInfo->sipInfo(); + // if ( !info.isValid() ) + // return; - /* - If only one party is externally visible, connection is obvious - If both are, peer with lowest IP address initiates the connection. - - This avoids dupe connections. - */ - if ( info.isVisible() ) + foreach ( SipInfo info, peerInfo->sipInfo() ) { - if ( !visibleExternally() || - externalAddress() < info.host() || - ( externalAddress() == info.host() && externalPort() < info.port() ) ) + if (info.isVisible()) { - - tDebug() << "Initiate connection to" << peerInfo->id() << "at" << info.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); + // There is at least one SipInfo that may be visible. Try connecting. + // Duplicate Connections are checked by connectToPeer, so we do not need to take care of this + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << info.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); connectToPeer( peerInfo ); - } - else - { - tDebug() << Q_FUNC_INFO << "They should be conecting to us..."; + // We connected to the peer, so we are done here. + return; } } - else - { - tDebug() << Q_FUNC_INFO << "They are not visible, doing nothing atm"; - if ( !visibleExternally() ) - { - if ( peerInfo->controlConnection() ) - delete peerInfo->controlConnection(); - } - } + // If we reach this point none of the previous SipInfos was visible. + if ( peerInfo->controlConnection() ) + delete peerInfo->controlConnection(); + + tDebug() << Q_FUNC_INFO << peerInfo->id() << "They are not visible, doing nothing atm"; } void @@ -616,10 +661,13 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, // if we can connect to them directly: if ( orig_conn && orig_conn->outbound() ) { - connectToPeer( orig_conn->socket()->peerAddress().toString(), - orig_conn->peerPort(), - key, - new_conn ); + QList sipInfo = QList(); + SipInfo info = SipInfo(); + info.setKey( key ); + info.setHost( orig_conn->socket()->peerAddress().toString() ); + info.setPort( orig_conn->peerPort() ); + sipInfo.append( info ); + connectToPeer( sipInfo, new_conn ); } else // ask them to connect to us: { @@ -631,7 +679,6 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, m.insert( "conntype", "request-offer" ); m.insert( "key", tmpkey ); m.insert( "offer", key ); - m.insert( "port", externalPort() ); m.insert( "controlid", Database::instance()->impl()->dbid() ); QJson::Serializer ser; @@ -680,45 +727,11 @@ void Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) conn->start( sock ); } - -void -Servent::socketError( QAbstractSocket::SocketError e ) -{ - QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); - if ( !sock ) - { - tLog() << "SocketError, sock is null"; - return; - } - - if ( !sock->_conn.isNull() ) - { - Connection* conn = sock->_conn.data(); - tLog() << "Servent::SocketError:" << e << conn->id() << conn->name(); - - if ( !sock->_disowned ) - { - // connection will delete if we already transferred ownership, otherwise: - sock->deleteLater(); - } - - conn->markAsFailed(); // will emit failed, then finished - } - else - { - tLog() << "SocketError, connection is null"; - sock->deleteLater(); - } -} - - void Servent::connectToPeer( const peerinfo_ptr& peerInfo ) { Q_ASSERT( this->thread() == QThread::currentThread() ); - SipInfo sipInfo = peerInfo->sipInfo(); - peerInfoDebug( peerInfo ) << "connectToPeer: search for already established connections to the same nodeid:" << m_controlconnections.count() << "connections"; if ( peerInfo->controlConnection() ) delete peerInfo->controlConnection(); @@ -731,7 +744,7 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) { Q_ASSERT( c ); - if ( c->id() == sipInfo.nodeId() ) + if ( c->id() == peerInfo->nodeId() ) { conn = c; @@ -773,8 +786,7 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) QVariantMap m; m["conntype"] = "accept-offer"; - m["key"] = sipInfo.key(); - m["port"] = externalPort(); + m["key"] = peerInfo->key(); m["nodeid"] = Database::instance()->impl()->dbid(); peerInfoDebug(peerInfo) << "No match found, creating a new ControlConnection..."; @@ -784,58 +796,132 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) if ( peerInfo->id().length() ) conn->setName( peerInfo->contactId() ); - if ( sipInfo.nodeId().length() ) - conn->setId( sipInfo.nodeId() ); + if ( peerInfo->nodeId().length() ) + conn->setId( peerInfo->nodeId() ); - conn->setProperty( "nodeid", sipInfo.nodeId() ); + conn->setProperty( "nodeid", peerInfo->nodeId() ); registerControlConnection( conn ); - connectToPeer( sipInfo.host(), sipInfo.port(), sipInfo.key(), conn ); + connectToPeer( peerInfo->sipInfo(), conn ); } void -Servent::connectToPeer( const QString& ha, int port, const QString& key, Connection* conn ) +Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) { - tDebug( LOGVERBOSE ) << "Servent::connectToPeer:" << ha << ":" << port - << thread() << QThread::currentThread(); + if ( sipInfoList.isEmpty() ) + { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; + return; + } + QList sipInfo = QList(sipInfoList); + // Use first available SIP endpoint and remove it from the list + SipInfo info = sipInfo.takeFirst(); + if ( !info.isVisible() ) + { + // Try next SipInfo, we can't connect to this one + connectToPeer( sipInfo, conn ); + return; + } - Q_ASSERT( port > 0 ); + tDebug( LOGVERBOSE ) << "Servent::connectToPeer:" << info.host() << ":" << info.port() << thread() << QThread::currentThread(); + + Q_ASSERT( info.port() > 0 ); Q_ASSERT( conn ); - if ( ( ha == m_externalAddress.toString() || ha == m_externalHostname ) && - ( port == m_externalPort ) ) + // Check that we are not connecting to ourselves + foreach( QHostAddress ha, m_externalAddresses ) + { + if ( QHostAddress( info.host() ) == ha) + { + tDebug() << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; + connectToPeer( sipInfo, conn ); + return; + } + } + if ( info.host() == m_externalHostname ) { - tDebug() << "ERROR: Tomahawk won't try to connect to" << ha << ":" << port << ": identified as ourselves."; + tDebug() << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; + connectToPeer( sipInfo, conn ); return; } - if ( key.length() && conn->firstMessage().isNull() ) + if ( info.key().length() && conn->firstMessage().isNull() ) { QVariantMap m; m["conntype"] = "accept-offer"; - m["key"] = key; - m["port"] = externalPort(); + m["key"] = info.key(); m["controlid"] = Database::instance()->impl()->dbid(); conn->setFirstMessage( m ); } QTcpSocketExtra* sock = new QTcpSocketExtra(); + sock->setConnectTimeout( CONNECT_TIMEOUT ); sock->_disowned = false; sock->_conn = conn; sock->_outbound = true; connect( sock, SIGNAL( connected() ), SLOT( socketConnected() ) ); - connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), - SLOT( socketError( QAbstractSocket::SocketError ) ) ); + NewClosure( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), + this, SLOT( connectToPeerFailed( QAbstractSocket::SocketError, QPair, Connection*> ) ), + QPair, Connection*>(sipInfo, conn) ); if ( !conn->peerIpAddress().isNull() ) - sock->connectToHost( conn->peerIpAddress(), port, QTcpSocket::ReadWrite ); + sock->connectToHost( conn->peerIpAddress(), info.port(), QTcpSocket::ReadWrite ); else - sock->connectToHost( ha, port, QTcpSocket::ReadWrite ); + sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite ); sock->moveToThread( thread() ); } +void +Servent::connectToPeerFailed( QAbstractSocket::SocketError e, QPair, Connection*> pair ) +{ + QList sipInfo = pair.first; + Connection* conn = pair.second; + + // Call default handler + socketError( e ); + + if ( e != QAbstractSocket::SocketResourceError ) + { + // If we do not have run out of resource, try next SipInfo + connectToPeer( sipInfo, conn ); + } +} + +void +Servent::socketError( QAbstractSocket::SocketError e ) +{ + QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); + if ( !sock ) + { + tLog() << "SocketError, sock is null"; + return; + } + + if ( !sock->_conn.isNull() ) + { + Connection* conn = sock->_conn.data(); + tLog() << "Servent::SocketError:" << e << conn->id() << conn->name(); + + if ( !sock->_disowned ) + { + // connection will delete if we already transferred ownership, otherwise: + sock->deleteLater(); + } + + conn->markAsFailed(); // will emit failed, then finished + } + else + { + tLog() << "SocketError, connection is null"; + sock->deleteLater(); + } +} + + + + void Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& theirdbid, const QString& key, const QString& theirkey ) @@ -854,13 +940,11 @@ Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& their QVariantMap m; m["conntype"] = "push-offer"; m["key"] = theirkey; - m["port"] = externalPort(); m["controlid"] = Database::instance()->impl()->dbid(); new_conn->setFirstMessage( m ); createParallelConnection( orig_conn, new_conn, QString() ); } - // return the appropriate connection for a given offer key, or NULL if invalid Connection* Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer ) diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 622c3f84d1..44ac55ccef 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -21,8 +21,8 @@ #ifndef SERVENT_H #define SERVENT_H -// time before new connection terminates if no auth received -#define AUTH_TIMEOUT 180000 +// time before new connection terminate if it could not be established +#define CONNECT_TIMEOUT 10000 #include #include @@ -40,6 +40,7 @@ #include "Typedefs.h" #include "Msg.h" +#include "network/QTcpSocketExtra.h" #include @@ -58,48 +59,19 @@ class SipInfo; typedef boost::function< void( const Tomahawk::result_ptr&, boost::function< void( QSharedPointer< QIODevice >& ) > )> IODeviceFactoryFunc; -// this is used to hold a bit of state, so when a connected signal is emitted -// from a socket, we can associate it with a Connection object etc. -class DLLEXPORT QTcpSocketExtra : public QTcpSocket -{ -Q_OBJECT - -public: - QTcpSocketExtra() : QTcpSocket() - { - QTimer::singleShot( AUTH_TIMEOUT, this, SLOT( authTimeout() ) ) ; - } - - QPointer _conn; - bool _outbound; - bool _disowned; - msg_ptr _msg; - -private slots: - void authTimeout() - { - if( _disowned ) - return; - - qDebug() << "Connection timed out before providing a valid offer-key"; - this->disconnectFromHost(); - } -}; - class DLLEXPORT Servent : public QTcpServer { Q_OBJECT public: static Servent* instance(); + static bool isValidExternalIP( const QHostAddress& addr ); explicit Servent( QObject* parent = 0 ); virtual ~Servent(); bool startListening( QHostAddress ha, bool upnp, int port ); - int port() const { return m_port; } - // creates new token that allows a controlconnection to be set up QString createConnectionKey( const QString& name = "", const QString &nodeid = "", const QString &key = "", bool onceOnly = true ); @@ -118,12 +90,18 @@ public slots: public: void connectToPeer( const Tomahawk::peerinfo_ptr& ha ); - void connectToPeer( const QString& ha, int port, const QString& key, Connection* conn ); - void reverseOfferRequest( ControlConnection* orig_conn, const QString& theirdbid, const QString& key, const QString& theirkey ); + void connectToPeer( const QList& sipInfoList, Connection* conn ); + void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); - bool visibleExternally() const { return !m_externalHostname.isNull() || (m_externalPort > 0 && !m_externalAddress.isNull()); } - QString externalAddress() const { return !m_externalHostname.isNull() ? m_externalHostname : m_externalAddress.toString(); } - int externalPort() const { return m_externalPort; } + bool visibleExternally() const { return (!m_externalHostname.isNull()) || (m_externalAddresses.length() > 0); } + //! The port this Peer listens directly (per default) + int port() const { return m_port; } + //! The IP addresses this Peer listens directly (per default) + QList< QHostAddress > addresses() const { return m_externalAddresses; } + //! An additional address this peer listens to, e.g. via UPnP. + QString additionalAddress() const { return m_externalHostname; } + //! An additional port this peer listens to, e.g. via UPnP (only in combination with additionalAddress. + int additionalPort() const { return m_externalPort; } static bool isIPWhitelisted( QHostAddress ip ); @@ -138,7 +116,7 @@ public slots: void localFileIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); void httpIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); - bool isReady() const { return m_ready; }; + bool isReady() const { return m_ready; } signals: void dbSyncTriggered(); @@ -150,10 +128,10 @@ public slots: void incomingConnection( int sd ); public slots: - void setInternalAddress(); void setExternalAddress( QHostAddress ha, unsigned int port ); - void socketError( QAbstractSocket::SocketError ); + void connectToPeerFailed( QAbstractSocket::SocketError e, QPair, Connection*> pair ); + void socketError( QAbstractSocket::SocketError e ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key ); void registerStreamConnection( StreamConnection* ); @@ -168,7 +146,6 @@ private slots: Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); private: - bool isValidExternalIP( const QHostAddress& addr ) const; void handoverSocket( Connection* conn, QTcpSocketExtra* sock ); void printCurrentTransfers(); @@ -177,11 +154,16 @@ private slots: QMap< QString, QPointer< Connection > > m_offers; QStringList m_connectedNodes; - int m_port, m_externalPort; - QHostAddress m_externalAddress; + //! The external port used by all address except those obtained via UPnP or the static configuration option + int m_port; + //! Either the static set or the UPnP set external port + int m_externalPort; + //! All available external IPs + QList m_externalAddresses; + //! Either the static set or the UPnP set external host QString m_externalHostname; + bool m_ready; - bool m_lanHack; bool m_noAuth; // currently active file transfers: diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 6630f25011..7ca33ac422 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -202,9 +202,9 @@ PeerInfo::sipPlugin() const void -PeerInfo::sendLocalSipInfo( const SipInfo& sipInfo ) +PeerInfo::sendLocalSipInfo( const QList& sipInfoList ) { - sipPlugin()->sendSipInfo( weakRef().toStrongRef(), sipInfo ); + sipPlugin()->sendSipInfoList( weakRef().toStrongRef(), sipInfoList ); } @@ -228,6 +228,23 @@ PeerInfo::contactId() const return m_contactId; } +const QString +PeerInfo::nodeId() const +{ + Q_ASSERT( m_sipInfo.length() > 0 ); + // All sip infos share the same nodeId + return m_sipInfo.first().nodeId(); +} + +const QString +PeerInfo::key() const +{ + Q_ASSERT( m_sipInfo.length() > 0 ); + // All sip infos share the same key + return m_sipInfo.first().key(); +} + + void PeerInfo::setStatus( PeerInfo::Status status ) @@ -259,19 +276,16 @@ PeerInfo::status() const void -PeerInfo::setSipInfo( const SipInfo& sipInfo ) +PeerInfo::setSipInfo( const QList& sipInfo ) { - if ( sipInfo == m_sipInfo ) - return; - - m_sipInfo = sipInfo; + m_sipInfo = QList(sipInfo); tLog() << "id:" << id() << "info changed" << sipInfo; emit sipInfoChanged(); } -const SipInfo +const QList PeerInfo::sipInfo() const { return m_sipInfo; @@ -389,7 +403,6 @@ PeerInfo::setData( const QVariant& data ) m_data = data; } - const QVariant PeerInfo::data() const { diff --git a/src/libtomahawk/sip/PeerInfo.h b/src/libtomahawk/sip/PeerInfo.h index 2b49548b36..0f11cf0703 100644 --- a/src/libtomahawk/sip/PeerInfo.h +++ b/src/libtomahawk/sip/PeerInfo.h @@ -74,7 +74,7 @@ Q_OBJECT const QString id() const; SipPlugin* sipPlugin() const; const QString debugName() const; - void sendLocalSipInfo( const SipInfo& sipInfo ); + void sendLocalSipInfo( const QList& sipInfoList ); QWeakPointer< Tomahawk::PeerInfo > weakRef(); void setWeakRef( QWeakPointer< Tomahawk::PeerInfo > weakRef ); @@ -96,8 +96,8 @@ Q_OBJECT void setStatus( Status status ); Status status() const; - void setSipInfo( const SipInfo& sipInfo ); - const SipInfo sipInfo() const; + void setSipInfo( const QList& sipInfo ); + const QList sipInfo() const; void setFriendlyName( const QString& friendlyName ); const QString friendlyName() const; @@ -112,6 +112,12 @@ Q_OBJECT void setData( const QVariant& data ); const QVariant data() const; + //! Get the node id of this peer + const QString nodeId() const; + + //! Get the authentication key for this host + const QString key() const; + signals: void sipInfoChanged(); @@ -131,7 +137,7 @@ Q_OBJECT QString m_id; QString m_contactId; Status m_status; - SipInfo m_sipInfo; + QList m_sipInfo; QString m_friendlyName; QString m_versionString; QVariant m_data; diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index 6503c282e1..10d27c5657 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -70,7 +70,8 @@ public slots: virtual void configurationChanged() = 0; virtual void addContact( const QString& peerId, const QString& msg = QString() ) = 0; - virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const SipInfo& info ) = 0; + //! Send a list of SipInfos to all contacts. + virtual void sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) = 0; signals: void peerStatusChanged( const Tomahawk::peerinfo_ptr& ); diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index 77efc0a722..18d5153918 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -64,24 +64,26 @@ DiagnosticsDialog::updateLogView() log.append( QString( "TOMAHAWK DIAGNOSTICS LOG -%1 \n\n" ).arg( QDateTime::currentDateTime().toString() ) ); log.append( "TOMAHAWK-VERSION: " TOMAHAWK_VERSION "\n" ); log.append( "PLATFORM: " TOMAHAWK_SYSTEM "\n\n"); - log.append( "NETWORK:\n General:\n" ); + log.append( "NETWORK:\n Listening to:\n" ); if ( Servent::instance()->visibleExternally() ) { - log.append( - QString( - " visible: true\n" - " host: %1\n" - " port: %2\n" - "\n" - ).arg( Servent::instance()->externalAddress() ) - .arg( Servent::instance()->externalPort() ) - - ); + foreach ( QHostAddress ha, Servent::instance()->addresses() ) + { + if ( ha.protocol() == QAbstractSocket::IPv6Protocol ) + log.append( QString( " [%1]:%2\n" ).arg( ha.toString() ).arg( Servent::instance()->port() ) ); + else + log.append( QString( " %1:%2\n" ).arg( ha.toString() ).arg( Servent::instance()->port() ) ); + } + if ( !Servent::instance()->additionalAddress().isNull() ) + { + log.append( QString( " [%1]:%2\n" ).arg( Servent::instance()->additionalAddress() ).arg( Servent::instance()->additionalPort() ) ); + } + } else { - log.append( " visible: false\n" ); + log.append( " not listening to any interface, outgoing connections only\n" ); } log.append( "\n\nINFOPLUGINS:\n" ); @@ -162,54 +164,17 @@ DiagnosticsDialog::accountLog( Tomahawk::Accounts::Account* account ) foreach( const Tomahawk::peerinfo_ptr& peerInfo, account->sipPlugin()->peersOnline() ) { - QString peerId = peerInfo->id(); - QString versionString = peerInfo->versionString(); - SipInfo sipInfo = peerInfo->sipInfo(); - if ( !sipInfo.isValid() ) - { - accountInfo.append( - QString(" %1: %2 %3" /*"(%4)"*/) - .arg( peerInfo->id() ) - .arg( "sipinfo invalid" ) - .arg( versionString ) - // .arg( connected ? "connected" : "not connected") - ); - } - else if ( sipInfo.isVisible() ) - { - accountInfo.append( - QString(" %1: %2:%3 %4" /*" (%5)"*/) - .arg( peerId ) - .arg( sipInfo.host() ) - .arg( sipInfo.port() ) - .arg( versionString ) - // .arg( connected ? "connected" : "not connected") - ); - } - else - { - accountInfo.append( - QString(" %1: visible: false %2" /*" (%3)"*/) - .arg( peerId ) - .arg( versionString ) - // .arg( connected ? "connected" : "not connected") - ); - } - - if( sipInfo.isValid() ) + accountInfo.append( QString( " %1: " ).arg( peerInfo->id() ) ); + foreach ( SipInfo info, peerInfo->sipInfo() ) { - if( !Servent::instance()->visibleExternally() || - Servent::instance()->externalAddress() < sipInfo.host() || - ( Servent::instance()->externalAddress() == sipInfo.host() && Servent::instance()->externalPort() < sipInfo.port() ) ) - { - accountInfo.append(" (outbound)"); - } + if ( info.isValid() ) + accountInfo.append( QString( "[%1]:%2; " ).arg( info.host() ).arg( info.port() ) ); else - { - accountInfo.append(" (inbound)"); - } + accountInfo.append( "SipInfo invalid; " ); } - accountInfo.append("\n"); + if ( ( peerInfo->sipInfo().length() == 1 ) && ( !peerInfo->sipInfo().first().isVisible() ) || ( peerInfo->sipInfo().length() == 0 ) ) + accountInfo.append( "(outbound connections only) "); + accountInfo.append( QString( " (%1)\n" ).arg( peerInfo->versionString() ) ); } accountInfo.append( "\n" ); From 24384854333516bfbd5cda72a5e1d2bf7a9764ec Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 15:48:05 +0200 Subject: [PATCH 111/565] Remove asserts in getters as this makes debugging harder --- src/libtomahawk/sip/SipInfo.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/libtomahawk/sip/SipInfo.cpp b/src/libtomahawk/sip/SipInfo.cpp index 0162896029..5ea8824b48 100644 --- a/src/libtomahawk/sip/SipInfo.cpp +++ b/src/libtomahawk/sip/SipInfo.cpp @@ -120,8 +120,6 @@ SipInfo::setVisible( bool visible ) bool SipInfo::isVisible() const { - Q_ASSERT( isValid() ); - return d->visible.toBool(); } @@ -136,8 +134,6 @@ SipInfo::setHost( const QString& host ) const QString SipInfo::host() const { - Q_ASSERT( isValid() ); - return d->host; } @@ -152,8 +148,6 @@ SipInfo::setPort( int port ) int SipInfo::port() const { - Q_ASSERT( isValid() ); - return d->port; } @@ -168,8 +162,6 @@ SipInfo::setNodeId( const QString& nodeId ) const QString SipInfo::nodeId() const { - Q_ASSERT( isValid() ); - return d->nodeId; } @@ -184,8 +176,6 @@ SipInfo::setKey( const QString& key ) const QString SipInfo::key() const { - Q_ASSERT( isValid() ); - return d->key; } From 073f6f5b7a57de8e8d218a3ea49792f29759a382 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 15:48:43 +0200 Subject: [PATCH 112/565] Always return SipInfo, even if not visible --- src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index 04cb9b9e31..20ea810eb3 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -174,10 +174,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter Payload::Ptr TomahawkXmppMessageFactory::createPayload() { - if ( ( ( m_sipInfo.length() == 1 ) && ( !m_sipInfo.first().isVisible() ) ) || ( m_sipInfo.length() < 1 ) ) - return Payload::Ptr( new TomahawkXmppMessage() ); - else - return Payload::Ptr( new TomahawkXmppMessage( m_sipInfo ) ); + return Payload::Ptr( new TomahawkXmppMessage( m_sipInfo ) ); } void From c42054ea5cc9d8a4cfbf34e00c79be9ab2b3cbc4 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 15:49:05 +0200 Subject: [PATCH 113/565] Create SipInfo with the correct information. --- src/libtomahawk/network/Servent.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 625c31c89c..033becca45 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -663,9 +663,12 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, { QList sipInfo = QList(); SipInfo info = SipInfo(); + info.setVisible( true ); info.setKey( key ); + info.setNodeId( orig_conn->id() ); info.setHost( orig_conn->socket()->peerAddress().toString() ); info.setPort( orig_conn->peerPort() ); + Q_ASSERT( info.isValid() ); sipInfo.append( info ); connectToPeer( sipInfo, new_conn ); } From 2a721e89a5e4dc9fd6113ef170983aa2bb3c66e9 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 17:54:32 +0200 Subject: [PATCH 114/565] Ignore Zeroconf messages sent by ourselves --- src/accounts/zeroconf/TomahawkZeroconf.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/accounts/zeroconf/TomahawkZeroconf.h b/src/accounts/zeroconf/TomahawkZeroconf.h index c1d362fb43..afc7b36c58 100644 --- a/src/accounts/zeroconf/TomahawkZeroconf.h +++ b/src/accounts/zeroconf/TomahawkZeroconf.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -131,6 +132,10 @@ private slots: m_sock.readDatagram( datagram.data(), datagram.size(), &sender, &senderPort ); qDebug() << "DATAGRAM RCVD" << QString::fromLatin1( datagram ) << sender; + // Ignore our own requests + if ( QNetworkInterface::allAddresses().contains( sender ) ) + return; + // only process msgs originating on the LAN: if ( datagram.startsWith( "TOMAHAWKADVERT:" ) && Servent::isIPWhitelisted( sender ) ) From 8b7dba77aa40f8c865631dba2e96c2beb7ad447f Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 20:12:56 +0200 Subject: [PATCH 115/565] Get back accidently deleted registerOffer --- src/libtomahawk/network/Servent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 033becca45..8788cb3fe0 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -389,6 +389,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) conn->setId( nodeid ); conn->addPeerInfo( peerInfo ); + registerOffer( key, conn ); QList sipInfo = QList(); foreach ( QHostAddress ha, m_externalAddresses ) { From b9e42a03e97ee74d4f98413cc27418460cb17c0c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 20:13:12 +0200 Subject: [PATCH 116/565] Better way to get the id of a peer --- src/libtomahawk/network/Servent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 8788cb3fe0..c449ec62ae 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -358,7 +358,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) if ( peerInfo->type() == Tomahawk::PeerInfo::Local ) { peerInfoDebug(peerInfo) << "we need to establish the connection now... thinking"; - if ( !connectedToSession( peerInfo->sipInfo().first().nodeId() ) ) + if ( !connectedToSession( peerInfo->id() ) ) { connectToPeer( peerInfo ); } From 2c863611907d71de9c810bdba7c1b35aaaa15c80 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 21:53:19 +0200 Subject: [PATCH 117/565] Rename sipInfoList to sipInfo --- src/accounts/xmpp/sip/XmppSip.cpp | 2 +- src/accounts/xmpp/sip/XmppSip.h | 2 +- src/accounts/zeroconf/Zeroconf.h | 2 +- src/libtomahawk/sip/PeerInfo.cpp | 2 +- src/libtomahawk/sip/SipPlugin.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index a2463ce5f3..6a3d34204a 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -435,7 +435,7 @@ XmppSipPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) void -XmppSipPlugin::sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) +XmppSipPlugin::sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << receiver << info; diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index 43b43abb61..c6fb21c26c 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -90,7 +90,7 @@ public slots: virtual void configurationChanged(); virtual void addContact( const QString& peerId, const QString& msg = QString() ); - virtual void sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ); + virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ); void showAddFriendDialog(); void publishTune( const QUrl& url, const Tomahawk::InfoSystem::InfoStringHash& trackInfo ); diff --git a/src/accounts/zeroconf/Zeroconf.h b/src/accounts/zeroconf/Zeroconf.h index d1d660b8c6..66fd8dbff5 100644 --- a/src/accounts/zeroconf/Zeroconf.h +++ b/src/accounts/zeroconf/Zeroconf.h @@ -65,7 +65,7 @@ public slots: void advertise(); - virtual void sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) {} + virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) {} void broadcastMsg( const QString & ) {} void addContact( const QString &, const QString& ) {} diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 7ca33ac422..8e33f54e06 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -204,7 +204,7 @@ PeerInfo::sipPlugin() const void PeerInfo::sendLocalSipInfo( const QList& sipInfoList ) { - sipPlugin()->sendSipInfoList( weakRef().toStrongRef(), sipInfoList ); + sipPlugin()->sendSipInfo( weakRef().toStrongRef(), sipInfoList ); } diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index 10d27c5657..e8495a328e 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -71,7 +71,7 @@ public slots: virtual void addContact( const QString& peerId, const QString& msg = QString() ) = 0; //! Send a list of SipInfos to all contacts. - virtual void sendSipInfoList( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) = 0; + virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) = 0; signals: void peerStatusChanged( const Tomahawk::peerinfo_ptr& ); From d3afba1a90a184fa4b3dd73f041491fbe614d643 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 21:53:31 +0200 Subject: [PATCH 118/565] Add missing writeEndElement --- src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index 20ea810eb3..b7eb1714ff 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -169,6 +169,8 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter // writer->writeEndElement(); + // + writer->writeEndElement(); } Payload::Ptr From 85c7b16fa11a895be1daaa6a84cb3a6a2990b4d0 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 22:03:31 +0200 Subject: [PATCH 119/565] Ignore IPv4 localhost even if written as IPv6 address --- src/libtomahawk/network/Servent.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index c449ec62ae..81ff5d2cdb 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -148,12 +148,14 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) continue; // IPv4 localhost if ( addr.toString() == "::1" ) continue; // IPv6 localhost + if ( addr.toString() == "::7F00:1" ) + continue; // IPv4 localhost as IPv6 address tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Listening to " << addr.toString(); m_externalAddresses.append( addr ); } } - else if ( ( ha.toString() != "127.0.0.1" ) && ( ha.toString() != "::1" ) ) + else if ( ( ha.toString() != "127.0.0.1" ) && ( ha.toString() != "::1" ) && ( ha.toString() == "::7F00:1" ) ) { // We listen only to one specific Address, only announce this. m_externalAddresses.append( ha ); From 6172d507103ab2223605eae091755ffcae80352f Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 22:10:53 +0200 Subject: [PATCH 120/565] Do not try link-local addresses. --- src/libtomahawk/network/Servent.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 81ff5d2cdb..1b02f09da1 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -150,6 +150,8 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) continue; // IPv6 localhost if ( addr.toString() == "::7F00:1" ) continue; // IPv4 localhost as IPv6 address + if ( addr.isInSubnet( QHostAddress::parseSubnet( "fe80::/10" ) ) ) + continue; // Skip link local addresses tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Listening to " << addr.toString(); m_externalAddresses.append( addr ); } From 1c4baee0d451de64f38e75a84e49e08360c38a15 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 5 Apr 2013 22:12:29 +0200 Subject: [PATCH 121/565] Correctly ignore IPv4-as-IPv6 localhost --- src/libtomahawk/network/Servent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 1b02f09da1..3a07329ffa 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -157,7 +157,7 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) } } - else if ( ( ha.toString() != "127.0.0.1" ) && ( ha.toString() != "::1" ) && ( ha.toString() == "::7F00:1" ) ) + else if ( ( ha.toString() != "127.0.0.1" ) && ( ha.toString() != "::1" ) && ( ha.toString() != "::7F00:1" ) ) { // We listen only to one specific Address, only announce this. m_externalAddresses.append( ha ); From 1d82d0b80b9da15682444884d530a230dab2d47c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 6 Apr 2013 00:09:06 +0200 Subject: [PATCH 122/565] Fix multiple ip connection mechanism --- src/libtomahawk/network/QTcpSocketExtra.cpp | 2 +- src/libtomahawk/network/Servent.cpp | 73 ++++++++++++--------- src/libtomahawk/network/Servent.h | 3 +- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/libtomahawk/network/QTcpSocketExtra.cpp b/src/libtomahawk/network/QTcpSocketExtra.cpp index 25aa087eea..c77e125172 100644 --- a/src/libtomahawk/network/QTcpSocketExtra.cpp +++ b/src/libtomahawk/network/QTcpSocketExtra.cpp @@ -57,7 +57,7 @@ QTcpSocketExtra::connectTimeout() if ( state() != ConnectedState ) { // We did not manage to connect in the given timespan, so abort the attempt... - close(); + abort(); // .. and notify error handlers. emit error( SocketTimeoutError ); } diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 3a07329ffa..ae8ad69871 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -54,6 +54,9 @@ typedef QPair< QList< SipInfo >, Connection* > sipConnectionPair; Q_DECLARE_METATYPE( sipConnectionPair ) +Q_DECLARE_METATYPE( QList< SipInfo > ) +Q_DECLARE_METATYPE( Connection* ) +Q_DECLARE_METATYPE( QTcpSocketExtra* ) using namespace Tomahawk; @@ -735,6 +738,34 @@ void Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) conn->start( sock ); } +void +Servent::cleanupSocket( QTcpSocketExtra *sock ) +{ + if ( !sock ) + { + tLog() << "SocketError, sock is null"; + return; + } + + if ( !sock->_conn.isNull() ) + { + Connection* conn = sock->_conn.data(); + + if ( !sock->_disowned ) + { + // connection will delete if we already transferred ownership, otherwise: + sock->deleteLater(); + } + + conn->markAsFailed(); // will emit failed, then finished + } + else + { + tLog() << "SocketError, connection is null"; + sock->deleteLater(); + } +} + void Servent::connectToPeer( const peerinfo_ptr& peerInfo ) { @@ -871,8 +902,8 @@ Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) connect( sock, SIGNAL( connected() ), SLOT( socketConnected() ) ); NewClosure( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), - this, SLOT( connectToPeerFailed( QAbstractSocket::SocketError, QPair, Connection*> ) ), - QPair, Connection*>(sipInfo, conn) ); + this, SLOT( connectToPeerFailed( QList, Connection*, QTcpSocketExtra* ) ), + sipInfo, conn, sock ); if ( !conn->peerIpAddress().isNull() ) sock->connectToHost( conn->peerIpAddress(), info.port(), QTcpSocket::ReadWrite ); @@ -882,54 +913,32 @@ Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) } void -Servent::connectToPeerFailed( QAbstractSocket::SocketError e, QPair, Connection*> pair ) +Servent::connectToPeerFailed( QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) { - QList sipInfo = pair.first; - Connection* conn = pair.second; - // Call default handler - socketError( e ); + cleanupSocket( socket ); - if ( e != QAbstractSocket::SocketResourceError ) + if ( !socket ) { // If we do not have run out of resource, try next SipInfo connectToPeer( sipInfo, conn ); } + // Try next SipInfo + connectToPeer( sipInfo, conn ); } void Servent::socketError( QAbstractSocket::SocketError e ) { QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); - if ( !sock ) - { - tLog() << "SocketError, sock is null"; - return; - } - if ( !sock->_conn.isNull() ) { Connection* conn = sock->_conn.data(); - tLog() << "Servent::SocketError:" << e << conn->id() << conn->name(); - - if ( !sock->_disowned ) - { - // connection will delete if we already transferred ownership, otherwise: - sock->deleteLater(); - } - - conn->markAsFailed(); // will emit failed, then finished - } - else - { - tLog() << "SocketError, connection is null"; - sock->deleteLater(); + tLog() << Q_FUNC_INFO << e << conn->id() << conn->name(); } -} - - - + cleanupSocket( sock ); +} void Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& theirdbid, const QString& key, const QString& theirkey ) diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 44ac55ccef..89ecc7f226 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -130,7 +130,7 @@ public slots: public slots: void setExternalAddress( QHostAddress ha, unsigned int port ); - void connectToPeerFailed( QAbstractSocket::SocketError e, QPair, Connection*> pair ); + void connectToPeerFailed(QList sipInfo, Connection* conn , QTcpSocketExtra *socket); void socketError( QAbstractSocket::SocketError e ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key ); @@ -147,6 +147,7 @@ private slots: private: void handoverSocket( Connection* conn, QTcpSocketExtra* sock ); + void cleanupSocket( QTcpSocketExtra* sock ); void printCurrentTransfers(); QJson::Parser parser; From df0fbbc96e614c434ba4d47d983ff8f2d8fa2b19 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 6 Apr 2013 00:09:36 +0200 Subject: [PATCH 123/565] Always try next SipInfo --- src/libtomahawk/network/Servent.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index ae8ad69871..1a141a7de2 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -915,14 +915,8 @@ Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) void Servent::connectToPeerFailed( QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) { - cleanupSocket( socket ); - if ( !socket ) - { - // If we do not have run out of resource, try next SipInfo - connectToPeer( sipInfo, conn ); - } // Try next SipInfo connectToPeer( sipInfo, conn ); } From 785b616e07c787718b2ea993c50c116dc3a4453c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 9 Apr 2013 17:27:00 +0200 Subject: [PATCH 124/565] Change comment style to gobal default --- src/accounts/xmpp/sip/TomahawkXmppMessage.h | 14 +++++-- .../xmpp/sip/TomahawkXmppMessageFactory.h | 19 +++++++-- src/libtomahawk/network/QTcpSocketExtra.h | 18 +++++++-- src/libtomahawk/network/Servent.h | 39 +++++++++++++++---- src/libtomahawk/sip/PeerInfo.h | 8 +++- src/libtomahawk/sip/SipPlugin.h | 5 ++- 6 files changed, 81 insertions(+), 22 deletions(-) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessage.h b/src/accounts/xmpp/sip/TomahawkXmppMessage.h index 9ea943b8b6..d2e54b29bc 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessage.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessage.h @@ -36,11 +36,19 @@ class ACCOUNTDLLEXPORT TomahawkXmppMessage : public Jreen::Payload TomahawkXmppMessage(const QList& sipInfo); ~TomahawkXmppMessage(); - //! The SipInfo objects that are wrapped in this XmppMessage + /** + * The SipInfo objects that are wrapped in this XmppMessage + */ const QList sipInfo() const; - //! The name of the peer contained in this message + + /** + * The name of the peer contained in this message + */ const QString key() const; - //! The name of the peer contained in this message + + /** + * The name of the peer contained in this message + */ const QString uniqname() const; private: diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h index ba06f7a2c2..e4e59e5313 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h @@ -43,13 +43,24 @@ class ACCOUNTDLLEXPORT TomahawkXmppMessageFactory : public Jreen::PayloadFactory enum State { AtNowhere, AtTransport, AtCandidate } m_state; - //! All the provided Sip informations + /** + * All the provided Sip informations + */ QList m_sipInfo; - //! The current parsing depth + + /** + * The current parsing depth + */ int m_depth; - //! The unique name of the peer + + /** + * The unique name of the peer + */ QString m_uniqname; - //! The authentication key of the peer + + /** + * The authentication key of the peer + */ QString m_key; }; diff --git a/src/libtomahawk/network/QTcpSocketExtra.h b/src/libtomahawk/network/QTcpSocketExtra.h index d42e1a5e7c..366f93688c 100644 --- a/src/libtomahawk/network/QTcpSocketExtra.h +++ b/src/libtomahawk/network/QTcpSocketExtra.h @@ -57,9 +57,14 @@ Q_OBJECT bool _disowned; msg_ptr _msg; - //! Set a time limit for establishing a connection. + /** + * Set a time limit for establishing a connection. + */ void setConnectTimeout( qint32 timeout ) { m_connectTimeout = timeout; } - //! Get the current timeout for establishing a connection. + + /** + * Get the current timeout for establishing a connection. + */ qint32 connectTimeout() const { return m_connectTimeout; } private slots: @@ -73,9 +78,14 @@ private slots: this->disconnectFromHost(); } private: - //! How long we will wait for a connection to establish + /** + * How long we will wait for a connection to establish + */ qint32 m_connectTimeout; - //! Timer to measure the connection initialisation + + /** + * Timer to measure the connection initialisation + */ QTimer* m_connectTimer; }; diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 89ecc7f226..61f0700391 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -94,13 +94,25 @@ public slots: void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); bool visibleExternally() const { return (!m_externalHostname.isNull()) || (m_externalAddresses.length() > 0); } - //! The port this Peer listens directly (per default) + + /** + * The port this Peer listens directly (per default) + */ int port() const { return m_port; } - //! The IP addresses this Peer listens directly (per default) + + /** + * The IP addresses this Peer listens directly (per default) + */ QList< QHostAddress > addresses() const { return m_externalAddresses; } - //! An additional address this peer listens to, e.g. via UPnP. + + /** + * An additional address this peer listens to, e.g. via UPnP. + */ QString additionalAddress() const { return m_externalHostname; } - //! An additional port this peer listens to, e.g. via UPnP (only in combination with additionalAddress. + + /** + * An additional port this peer listens to, e.g. via UPnP (only in combination with additionalAddress. + */ int additionalPort() const { return m_externalPort; } static bool isIPWhitelisted( QHostAddress ip ); @@ -155,13 +167,24 @@ private slots: QMap< QString, QPointer< Connection > > m_offers; QStringList m_connectedNodes; - //! The external port used by all address except those obtained via UPnP or the static configuration option + /** + * The external port used by all address except those obtained via UPnP or the static configuration option + */ int m_port; - //! Either the static set or the UPnP set external port + + /** + * Either the static set or the UPnP set external port + */ int m_externalPort; - //! All available external IPs + + /** + * All available external IPs + */ QList m_externalAddresses; - //! Either the static set or the UPnP set external host + + /** + * Either the static set or the UPnP set external host + */ QString m_externalHostname; bool m_ready; diff --git a/src/libtomahawk/sip/PeerInfo.h b/src/libtomahawk/sip/PeerInfo.h index 0f11cf0703..b8b78d9d88 100644 --- a/src/libtomahawk/sip/PeerInfo.h +++ b/src/libtomahawk/sip/PeerInfo.h @@ -112,10 +112,14 @@ Q_OBJECT void setData( const QVariant& data ); const QVariant data() const; - //! Get the node id of this peer + /** + * Get the node id of this peer + */ const QString nodeId() const; - //! Get the authentication key for this host + /** + * Get the authentication key for this host + */ const QString key() const; signals: diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index e8495a328e..5a9349c573 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -70,7 +70,10 @@ public slots: virtual void configurationChanged() = 0; virtual void addContact( const QString& peerId, const QString& msg = QString() ) = 0; - //! Send a list of SipInfos to all contacts. + + /** + * Send a list of SipInfos to all contacts. + */ virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) = 0; signals: From a0c69ac45a08255b98b8b3d304d26d20809abc09 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 18 Apr 2013 11:19:26 +0200 Subject: [PATCH 125/565] Do nothing in handleSipInfo if we have not received valid SipInfo --- src/libtomahawk/network/Servent.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 1a141a7de2..0d5c3c0ed3 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -451,10 +451,10 @@ Servent::onSipInfoChanged() void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) { - // FIXME: Do we need this? - // SipInfo info = peerInfo->sipInfo(); - // if ( !info.isValid() ) - // return; + // We do not have received the initial SipInfo for this client yet, so wait for it. + // Each client will have at least one non-visible SipInfo + if ( peerInfo->sipInfo().length() == 0 ) + return; foreach ( SipInfo info, peerInfo->sipInfo() ) { From 11f2c24966e0ffb5f37f06d53c2ce63f01fc8ff1 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 18 Apr 2013 12:20:34 +0200 Subject: [PATCH 126/565] Clear up (Control)Connections if connecting failed. --- src/libtomahawk/network/Servent.cpp | 27 +++++++++++++++++---------- src/libtomahawk/network/Servent.h | 4 ++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 0d5c3c0ed3..47757a3e94 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -57,6 +57,7 @@ Q_DECLARE_METATYPE( sipConnectionPair ) Q_DECLARE_METATYPE( QList< SipInfo > ) Q_DECLARE_METATYPE( Connection* ) Q_DECLARE_METATYPE( QTcpSocketExtra* ) +Q_DECLARE_METATYPE( Tomahawk::peerinfo_ptr ) using namespace Tomahawk; @@ -678,7 +679,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, info.setPort( orig_conn->peerPort() ); Q_ASSERT( info.isValid() ); sipInfo.append( info ); - connectToPeer( sipInfo, new_conn ); + connectToPeer( peerinfo_ptr(), sipInfo, new_conn ); } else // ask them to connect to us: { @@ -841,16 +842,22 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) conn->setProperty( "nodeid", peerInfo->nodeId() ); registerControlConnection( conn ); - connectToPeer( peerInfo->sipInfo(), conn ); + connectToPeer( peerInfo, peerInfo->sipInfo(), conn ); } void -Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) +Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipInfoList, Connection* conn ) { if ( sipInfoList.isEmpty() ) { tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; + // If a peerinfo was supplied and has a ControlConnection which should be destroyed, than use this + if ( !peerInfo.isNull() && peerInfo->controlConnection() ) + delete peerInfo->controlConnection(); + else + // Connecting failed, so destroy this connection. + delete conn; return; } QList sipInfo = QList(sipInfoList); @@ -859,7 +866,7 @@ Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) if ( !info.isVisible() ) { // Try next SipInfo, we can't connect to this one - connectToPeer( sipInfo, conn ); + connectToPeer( peerInfo, sipInfo, conn ); return; } @@ -874,14 +881,14 @@ Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) if ( QHostAddress( info.host() ) == ha) { tDebug() << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; - connectToPeer( sipInfo, conn ); + connectToPeer( peerInfo, sipInfo, conn ); return; } } if ( info.host() == m_externalHostname ) { tDebug() << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; - connectToPeer( sipInfo, conn ); + connectToPeer( peerInfo, sipInfo, conn ); return; } @@ -902,8 +909,8 @@ Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) connect( sock, SIGNAL( connected() ), SLOT( socketConnected() ) ); NewClosure( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), - this, SLOT( connectToPeerFailed( QList, Connection*, QTcpSocketExtra* ) ), - sipInfo, conn, sock ); + this, SLOT( connectToPeerFailed( Tomahawk::peerinfo_ptr, QList, Connection*, QTcpSocketExtra* ) ), + peerInfo, sipInfo, conn, sock ); if ( !conn->peerIpAddress().isNull() ) sock->connectToHost( conn->peerIpAddress(), info.port(), QTcpSocket::ReadWrite ); @@ -913,12 +920,12 @@ Servent::connectToPeer(const QList& sipInfoList, Connection* conn ) } void -Servent::connectToPeerFailed( QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) +Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) { cleanupSocket( socket ); // Try next SipInfo - connectToPeer( sipInfo, conn ); + connectToPeer( peerInfo, sipInfo, conn ); } void diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 61f0700391..2243d6a92e 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -90,7 +90,7 @@ public slots: public: void connectToPeer( const Tomahawk::peerinfo_ptr& ha ); - void connectToPeer( const QList& sipInfoList, Connection* conn ); + void connectToPeer(const Tomahawk::peerinfo_ptr &peerInfo, const QList& sipInfoList, Connection* conn ); void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); bool visibleExternally() const { return (!m_externalHostname.isNull()) || (m_externalAddresses.length() > 0); } @@ -142,7 +142,7 @@ public slots: public slots: void setExternalAddress( QHostAddress ha, unsigned int port ); - void connectToPeerFailed(QList sipInfo, Connection* conn , QTcpSocketExtra *socket); + void connectToPeerFailed( const Tomahawk::peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn , QTcpSocketExtra *socket ); void socketError( QAbstractSocket::SocketError e ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key ); From d8bc1cc11d89b39df9c342db0fa3eb3fd0ae37df Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 14 May 2013 11:06:38 +0200 Subject: [PATCH 127/565] Rename sipInfo to sipInfos at various places --- src/accounts/xmpp/sip/TomahawkXmppMessage.cpp | 22 ++++++++++--------- src/accounts/xmpp/sip/TomahawkXmppMessage.h | 6 ++--- .../xmpp/sip/TomahawkXmppMessageFactory.cpp | 21 +++++++++--------- .../xmpp/sip/TomahawkXmppMessageFactory.h | 2 +- src/accounts/xmpp/sip/XmppSip.cpp | 6 ++--- src/accounts/xmpp/sip/XmppSip.h | 2 +- src/accounts/zeroconf/Zeroconf.cpp | 6 ++--- src/accounts/zeroconf/Zeroconf.h | 2 +- src/libtomahawk/network/Servent.cpp | 16 +++++++------- src/libtomahawk/sip/PeerInfo.cpp | 22 +++++++++---------- src/libtomahawk/sip/PeerInfo.h | 8 +++---- src/libtomahawk/sip/SipPlugin.h | 2 +- src/tomahawk/DiagnosticsDialog.cpp | 4 ++-- 13 files changed, 61 insertions(+), 58 deletions(-) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp index f7f5ca8e9c..84b5258335 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp @@ -21,11 +21,13 @@ #include "utils/Logger.h" -TomahawkXmppMessage::TomahawkXmppMessage() : m_sipInfo() +TomahawkXmppMessage::TomahawkXmppMessage() + : m_sipInfos() { } -TomahawkXmppMessage::TomahawkXmppMessage( const QList &sipInfo ) : m_sipInfo( sipInfo ) +TomahawkXmppMessage::TomahawkXmppMessage( const QList &sipInfos ) + : m_sipInfos( sipInfos ) { } @@ -34,26 +36,26 @@ TomahawkXmppMessage::~TomahawkXmppMessage() } const QList -TomahawkXmppMessage::sipInfo() const +TomahawkXmppMessage::sipInfos() const { - return m_sipInfo; + return m_sipInfos; } const QString TomahawkXmppMessage::key() const { - if ( m_sipInfo.length() > 0 ) - return m_sipInfo.first().key(); - else + if ( m_sipInfos.isEmpty() ) return QString(); + else + return m_sipInfos.first().key(); } const QString TomahawkXmppMessage::uniqname() const { - if ( m_sipInfo.length() > 0 ) - return m_sipInfo.first().nodeId(); - else + if ( m_sipInfos.isEmpty() ) return QString(); + else + return m_sipInfos.first().nodeId(); } diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessage.h b/src/accounts/xmpp/sip/TomahawkXmppMessage.h index d2e54b29bc..fb7e6d43db 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessage.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessage.h @@ -33,13 +33,13 @@ class ACCOUNTDLLEXPORT TomahawkXmppMessage : public Jreen::Payload J_PAYLOAD(TomahawkXmppMessage) public: TomahawkXmppMessage(); - TomahawkXmppMessage(const QList& sipInfo); + TomahawkXmppMessage(const QList& sipInfos); ~TomahawkXmppMessage(); /** * The SipInfo objects that are wrapped in this XmppMessage */ - const QList sipInfo() const; + const QList sipInfos() const; /** * The name of the peer contained in this message @@ -52,7 +52,7 @@ class ACCOUNTDLLEXPORT TomahawkXmppMessage : public Jreen::Payload const QString uniqname() const; private: - QList m_sipInfo; + QList m_sipInfos; }; #endif // ENTITYTIME_H diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index b7eb1714ff..02900a2f6b 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -29,7 +29,8 @@ using namespace Jreen; -TomahawkXmppMessageFactory::TomahawkXmppMessageFactory() : m_sipInfo() +TomahawkXmppMessageFactory::TomahawkXmppMessageFactory() + : m_sipInfos() { m_depth = 0; m_state = AtNowhere; @@ -60,7 +61,7 @@ void TomahawkXmppMessageFactory::handleStartElement(const QStringRef &name, cons m_state = AtNowhere; m_uniqname = QString(); m_key = QString(); - m_sipInfo = QList(); + m_sipInfos = QList(); } else if ( m_depth == 2 ) { @@ -83,7 +84,7 @@ void TomahawkXmppMessageFactory::handleStartElement(const QStringRef &name, cons info.setKey( m_key ); info.setNodeId( m_uniqname ); Q_ASSERT( info.isValid() ); - m_sipInfo.append( info ); + m_sipInfos.append( info ); } } Q_UNUSED(uri); @@ -98,14 +99,14 @@ void TomahawkXmppMessageFactory::handleEndElement(const QStringRef &name, const { m_state = AtNowhere; // Check that we have at least one SipInfo so that we provide some information about invisible peers. - if ( m_sipInfo.length() == 0 ) + if ( m_sipInfos.isEmpty() ) { SipInfo info = SipInfo(); info.setVisible( false ); info.setKey( m_key ); info.setNodeId( m_uniqname ); Q_ASSERT( info.isValid() ); - m_sipInfo.append( info ); + m_sipInfos.append( info ); } } Q_UNUSED(name); @@ -134,9 +135,9 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter writer->writeDefaultNamespace( TOMAHAWK_SIP_MESSAGE_NS ); // Get a copy of the list, so that we can modify it here. - QList sipInfo = QList( sipMessage->sipInfo() ); + QList sipInfos = QList( sipMessage->sipInfos() ); QSharedPointer lastInfo = QSharedPointer(); - foreach ( SipInfo info, sipInfo ) + foreach ( SipInfo info, sipInfos ) { if ( info.isVisible() ) { @@ -145,7 +146,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter { // For comapability reasons, this shall be put as the last candidate lastInfo = QSharedPointer( new SipInfo( info ) ); - sipInfo.removeOne( info ); + sipInfos.removeOne( info ); break; } } @@ -155,7 +156,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter writer->writeAttribute( QLatin1String( "pwd" ), sipMessage->key() ); writer->writeAttribute( QLatin1String( "uniqname" ), sipMessage->uniqname() ); - foreach ( SipInfo info, sipInfo ) + foreach ( SipInfo info, sipInfos ) { if ( info.isVisible() ) serializeSipInfo( info, writer ); @@ -176,7 +177,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter Payload::Ptr TomahawkXmppMessageFactory::createPayload() { - return Payload::Ptr( new TomahawkXmppMessage( m_sipInfo ) ); + return Payload::Ptr( new TomahawkXmppMessage( m_sipInfos ) ); } void diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h index e4e59e5313..5e2d53996b 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h @@ -46,7 +46,7 @@ class ACCOUNTDLLEXPORT TomahawkXmppMessageFactory : public Jreen::PayloadFactory /** * All the provided Sip informations */ - QList m_sipInfo; + QList m_sipInfos; /** * The current parsing depth diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index 6a3d34204a..3176048215 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -435,7 +435,7 @@ XmppSipPlugin::errorMessage( Jreen::Client::DisconnectReason reason ) void -XmppSipPlugin::sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) +XmppSipPlugin::sendSipInfos( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << receiver << info; @@ -909,7 +909,7 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Received Sip Information from:" << iq.from().full(); // Check that all received SipInfos are valid. - foreach ( SipInfo info, sipMessage->sipInfo() ) + foreach ( SipInfo info, sipMessage->sipInfos() ) { Q_ASSERT( info.isValid() ); } @@ -921,7 +921,7 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) tDebug() << Q_FUNC_INFO << "no valid peerInfo for" << iq.from().full(); return; } - peerInfo->setSipInfo( sipMessage->sipInfo() ); + peerInfo->setSipInfos( sipMessage->sipInfos() ); } } } diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index c6fb21c26c..302d12037a 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -90,7 +90,7 @@ public slots: virtual void configurationChanged(); virtual void addContact( const QString& peerId, const QString& msg = QString() ); - virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ); + virtual void sendSipInfos( const Tomahawk::peerinfo_ptr& receiver, const QList& info ); void showAddFriendDialog(); void publishTune( const QUrl& url, const Tomahawk::InfoSystem::InfoStringHash& trackInfo ); diff --git a/src/accounts/zeroconf/Zeroconf.cpp b/src/accounts/zeroconf/Zeroconf.cpp index 1893b90b8a..b1924f8bce 100644 --- a/src/accounts/zeroconf/Zeroconf.cpp +++ b/src/accounts/zeroconf/Zeroconf.cpp @@ -161,9 +161,9 @@ ZeroconfPlugin::lanHostFound( const QString& host, int port, const QString& name sipInfo.setVisible( true ); Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( this, host, Tomahawk::PeerInfo::AutoCreate ); - QList sipInfoList = QList(); - sipInfoList.append( sipInfo ); - peerInfo->setSipInfo( sipInfoList ); + QList sipInfos = QList(); + sipInfos.append( sipInfo ); + peerInfo->setSipInfos( sipInfos ); peerInfo->setContactId( host ); peerInfo->setFriendlyName( name ); peerInfo->setType( PeerInfo::Local ); diff --git a/src/accounts/zeroconf/Zeroconf.h b/src/accounts/zeroconf/Zeroconf.h index 66fd8dbff5..2de0e9b440 100644 --- a/src/accounts/zeroconf/Zeroconf.h +++ b/src/accounts/zeroconf/Zeroconf.h @@ -65,7 +65,7 @@ public slots: void advertise(); - virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) {} + virtual void sendSipInfos( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) {} void broadcastMsg( const QString & ) {} void addContact( const QString &, const QString& ) {} diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 47757a3e94..3243acac8d 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -398,7 +398,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) conn->addPeerInfo( peerInfo ); registerOffer( key, conn ); - QList sipInfo = QList(); + QList sipInfos = QList(); foreach ( QHostAddress ha, m_externalAddresses ) { SipInfo info = SipInfo(); @@ -407,7 +407,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) info.setKey( key ); info.setVisible( true ); info.setNodeId( nodeid ); - sipInfo.append( info ); + sipInfos.append( info ); } if ( m_externalHostname.length() > 0) { @@ -417,10 +417,10 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) info.setKey( key ); info.setVisible( true ); info.setNodeId( nodeid ); - sipInfo.append( info ); + sipInfos.append( info ); } - if ( sipInfo.length() == 0 ) + if ( sipInfos.length() == 0 ) { // We are not visible via any IP, send a dummy SipInfo SipInfo info = SipInfo(); @@ -430,7 +430,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Only accepting connections, no usable IP to listen to found."; } - peerInfo->sendLocalSipInfo( sipInfo ); + peerInfo->sendLocalSipInfos( sipInfos ); handleSipInfo( peerInfo ); connect( peerInfo.data(), SIGNAL( sipInfoChanged() ), SLOT( onSipInfoChanged() ) ); @@ -454,10 +454,10 @@ void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) { // We do not have received the initial SipInfo for this client yet, so wait for it. // Each client will have at least one non-visible SipInfo - if ( peerInfo->sipInfo().length() == 0 ) + if ( peerInfo->sipInfos().isEmpty() ) return; - foreach ( SipInfo info, peerInfo->sipInfo() ) + foreach ( SipInfo info, peerInfo->sipInfos() ) { if (info.isVisible()) { @@ -842,7 +842,7 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) conn->setProperty( "nodeid", peerInfo->nodeId() ); registerControlConnection( conn ); - connectToPeer( peerInfo, peerInfo->sipInfo(), conn ); + connectToPeer( peerInfo, peerInfo->sipInfos(), conn ); } diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 8e33f54e06..463badb83b 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -202,9 +202,9 @@ PeerInfo::sipPlugin() const void -PeerInfo::sendLocalSipInfo( const QList& sipInfoList ) +PeerInfo::sendLocalSipInfos( const QList& sipInfos ) { - sipPlugin()->sendSipInfo( weakRef().toStrongRef(), sipInfoList ); + sipPlugin()->sendSipInfos( weakRef().toStrongRef(), sipInfos ); } @@ -231,17 +231,17 @@ PeerInfo::contactId() const const QString PeerInfo::nodeId() const { - Q_ASSERT( m_sipInfo.length() > 0 ); + Q_ASSERT( !m_sipInfos.isEmpty() ); // All sip infos share the same nodeId - return m_sipInfo.first().nodeId(); + return m_sipInfos.first().nodeId(); } const QString PeerInfo::key() const { - Q_ASSERT( m_sipInfo.length() > 0 ); + Q_ASSERT( !m_sipInfos.isEmpty() ); // All sip infos share the same key - return m_sipInfo.first().key(); + return m_sipInfos.first().key(); } @@ -276,19 +276,19 @@ PeerInfo::status() const void -PeerInfo::setSipInfo( const QList& sipInfo ) +PeerInfo::setSipInfos( const QList& sipInfos ) { - m_sipInfo = QList(sipInfo); + m_sipInfos = QList( sipInfos ); - tLog() << "id:" << id() << "info changed" << sipInfo; + tLog() << "id:" << id() << "info changed" << sipInfos; emit sipInfoChanged(); } const QList -PeerInfo::sipInfo() const +PeerInfo::sipInfos() const { - return m_sipInfo; + return m_sipInfos; } diff --git a/src/libtomahawk/sip/PeerInfo.h b/src/libtomahawk/sip/PeerInfo.h index b8b78d9d88..74fd52c7c0 100644 --- a/src/libtomahawk/sip/PeerInfo.h +++ b/src/libtomahawk/sip/PeerInfo.h @@ -74,7 +74,7 @@ Q_OBJECT const QString id() const; SipPlugin* sipPlugin() const; const QString debugName() const; - void sendLocalSipInfo( const QList& sipInfoList ); + void sendLocalSipInfos( const QList& sipInfos ); QWeakPointer< Tomahawk::PeerInfo > weakRef(); void setWeakRef( QWeakPointer< Tomahawk::PeerInfo > weakRef ); @@ -96,8 +96,8 @@ Q_OBJECT void setStatus( Status status ); Status status() const; - void setSipInfo( const QList& sipInfo ); - const QList sipInfo() const; + void setSipInfos( const QList& sipInfos ); + const QList sipInfos() const; void setFriendlyName( const QString& friendlyName ); const QString friendlyName() const; @@ -141,7 +141,7 @@ Q_OBJECT QString m_id; QString m_contactId; Status m_status; - QList m_sipInfo; + QList m_sipInfos; QString m_friendlyName; QString m_versionString; QVariant m_data; diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index 5a9349c573..caa23877e7 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -74,7 +74,7 @@ public slots: /** * Send a list of SipInfos to all contacts. */ - virtual void sendSipInfo( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) = 0; + virtual void sendSipInfos( const Tomahawk::peerinfo_ptr& receiver, const QList& infos ) = 0; signals: void peerStatusChanged( const Tomahawk::peerinfo_ptr& ); diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index 18d5153918..3b72b6f933 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -165,14 +165,14 @@ DiagnosticsDialog::accountLog( Tomahawk::Accounts::Account* account ) foreach( const Tomahawk::peerinfo_ptr& peerInfo, account->sipPlugin()->peersOnline() ) { accountInfo.append( QString( " %1: " ).arg( peerInfo->id() ) ); - foreach ( SipInfo info, peerInfo->sipInfo() ) + foreach ( SipInfo info, peerInfo->sipInfos() ) { if ( info.isValid() ) accountInfo.append( QString( "[%1]:%2; " ).arg( info.host() ).arg( info.port() ) ); else accountInfo.append( "SipInfo invalid; " ); } - if ( ( peerInfo->sipInfo().length() == 1 ) && ( !peerInfo->sipInfo().first().isVisible() ) || ( peerInfo->sipInfo().length() == 0 ) ) + if ( ( ( peerInfo->sipInfos().length() == 1 ) && ( !peerInfo->sipInfos().first().isVisible() ) ) || ( peerInfo->sipInfos().isEmpty() ) ) accountInfo.append( "(outbound connections only) "); accountInfo.append( QString( " (%1)\n" ).arg( peerInfo->versionString() ) ); } From 9c1100d18001c599116ba5d7c5650ee98dbc2fc0 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 14 May 2013 11:25:27 +0200 Subject: [PATCH 128/565] Some more style changes --- src/libtomahawk/network/Servent.cpp | 8 ++++---- src/libtomahawk/network/Servent.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 3243acac8d..758c548a42 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -420,7 +420,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) sipInfos.append( info ); } - if ( sipInfos.length() == 0 ) + if ( sipInfos.isEmpty() ) { // We are not visible via any IP, send a dummy SipInfo SipInfo info = SipInfo(); @@ -847,9 +847,9 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) void -Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipInfoList, Connection* conn ) +Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipInfos, Connection* conn ) { - if ( sipInfoList.isEmpty() ) + if ( sipInfos.isEmpty() ) { tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; // If a peerinfo was supplied and has a ControlConnection which should be destroyed, than use this @@ -860,7 +860,7 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn delete conn; return; } - QList sipInfo = QList(sipInfoList); + QList sipInfo = QList(sipInfos); // Use first available SIP endpoint and remove it from the list SipInfo info = sipInfo.takeFirst(); if ( !info.isVisible() ) diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 2243d6a92e..27aabd710c 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -90,7 +90,7 @@ public slots: public: void connectToPeer( const Tomahawk::peerinfo_ptr& ha ); - void connectToPeer(const Tomahawk::peerinfo_ptr &peerInfo, const QList& sipInfoList, Connection* conn ); + void connectToPeer(const Tomahawk::peerinfo_ptr &peerInfo, const QList& sipInfos, Connection* conn ); void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); bool visibleExternally() const { return (!m_externalHostname.isNull()) || (m_externalAddresses.length() > 0); } From 35da9577651d460c4772361bc07c19b1c180da9b Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 14 May 2013 11:25:37 +0200 Subject: [PATCH 129/565] Use nodeId instead of id --- src/libtomahawk/network/Servent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 758c548a42..c727981538 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -366,7 +366,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) if ( peerInfo->type() == Tomahawk::PeerInfo::Local ) { peerInfoDebug(peerInfo) << "we need to establish the connection now... thinking"; - if ( !connectedToSession( peerInfo->id() ) ) + if ( !connectedToSession( peerInfo->nodeId() ) ) { connectToPeer( peerInfo ); } From f34dc1b75f95fda3eb37ae0e22f6de680fd4851c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 14 May 2013 11:53:33 +0200 Subject: [PATCH 130/565] Add ASSERTs again --- src/libtomahawk/sip/SipInfo.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libtomahawk/sip/SipInfo.cpp b/src/libtomahawk/sip/SipInfo.cpp index 5ea8824b48..d633cf1ef7 100644 --- a/src/libtomahawk/sip/SipInfo.cpp +++ b/src/libtomahawk/sip/SipInfo.cpp @@ -120,6 +120,7 @@ SipInfo::setVisible( bool visible ) bool SipInfo::isVisible() const { + Q_ASSERT( isValid() ); return d->visible.toBool(); } @@ -134,6 +135,7 @@ SipInfo::setHost( const QString& host ) const QString SipInfo::host() const { + Q_ASSERT( isValid() ); return d->host; } @@ -148,6 +150,7 @@ SipInfo::setPort( int port ) int SipInfo::port() const { + Q_ASSERT( isValid() ); return d->port; } @@ -162,6 +165,7 @@ SipInfo::setNodeId( const QString& nodeId ) const QString SipInfo::nodeId() const { + Q_ASSERT( isValid() ); return d->nodeId; } @@ -176,6 +180,7 @@ SipInfo::setKey( const QString& key ) const QString SipInfo::key() const { + Q_ASSERT( isValid() ); return d->key; } From f1ad4922ab81e8d8c4b99b04e29731ad406dc32f Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 14 May 2013 12:05:54 +0200 Subject: [PATCH 131/565] Sent non-IP hostname as a last hostname too (these are the user-supplied ones) --- src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index 02900a2f6b..d5a2c6c0ed 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -142,9 +142,9 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter if ( info.isVisible() ) { QHostAddress ha = QHostAddress( info.host() ); - if ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) + if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) ) { - // For comapability reasons, this shall be put as the last candidate + // For comapability reasons, this shall be put as the last candidate (this is the IP/host that would have been sent in previous versions) lastInfo = QSharedPointer( new SipInfo( info ) ); sipInfos.removeOne( info ); break; From 6c9ec62e7e564d917efda876e693157d02444fbc Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 14 May 2013 13:06:27 +0200 Subject: [PATCH 132/565] Handle disconnects during connection attempts (i.e. vanishing connection objects) --- src/libtomahawk/network/Servent.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index c727981538..aaa77c12cf 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -851,7 +851,11 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn { if ( sipInfos.isEmpty() ) { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; + if ( conn != NULL ) { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; + } else { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for skipping."; + } // If a peerinfo was supplied and has a ControlConnection which should be destroyed, than use this if ( !peerInfo.isNull() && peerInfo->controlConnection() ) delete peerInfo->controlConnection(); @@ -922,10 +926,13 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn void Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) { + bool connIsNull = socket->_conn.isNull(); cleanupSocket( socket ); - // Try next SipInfo - connectToPeer( peerInfo, sipInfo, conn ); + if ( !connIsNull ) { + // Try next SipInfo (don't do this if the connection was destroyed in between) + connectToPeer( peerInfo, sipInfo, conn ); + } } void From e38be974e424be9146ff1bfb47c792c03b6bf4f0 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 15 May 2013 09:55:45 +0200 Subject: [PATCH 133/565] Add a bit of logging to XmppMessage serialzation --- src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index d5a2c6c0ed..7cf4f6a882 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -165,6 +165,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter if ( !lastInfo.isNull() ) { Q_ASSERT( lastInfo->isVisible() ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Using " << lastInfo->host() << ":" << lastInfo->port() << " as the host which all older clients will only detect"; serializeSipInfo( *lastInfo, writer ); } From e2f9dad471ca152c190f97ed71b3fbcfe771d5fd Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 15 May 2013 13:49:26 +0200 Subject: [PATCH 134/565] Add more logging to connection handling --- src/libtomahawk/network/ControlConnection.cpp | 1 + src/libtomahawk/network/Servent.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 58c4ba7f35..feda66add0 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -221,6 +221,7 @@ ControlConnection::handleMsg( msg_ptr msg ) if ( !msg->is( Msg::JSON ) ) { Q_ASSERT( msg->is( Msg::JSON ) ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Received message was not in JSON format"; markAsFailed(); return; } diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index aaa77c12cf..d3fc30488b 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -308,6 +308,8 @@ Servent::isValidExternalIP( const QHostAddress& addr ) void Servent::registerOffer( const QString& key, Connection* conn ) { + // The registered connection should always be != NULL + Q_ASSERT( conn != NULL ); m_offers[key] = QPointer(conn); } @@ -926,6 +928,7 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn void Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Connecting to " << conn->peerIpAddress() << " failed"; bool connIsNull = socket->_conn.isNull(); cleanupSocket( socket ); From 09f47fe11c514a6c428c6d739236fc306ae004da Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 15 May 2013 14:47:00 +0200 Subject: [PATCH 135/565] Remove assert and fix logging --- src/libtomahawk/network/Servent.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index d3fc30488b..955b8554a2 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -308,9 +308,7 @@ Servent::isValidExternalIP( const QHostAddress& addr ) void Servent::registerOffer( const QString& key, Connection* conn ) { - // The registered connection should always be != NULL - Q_ASSERT( conn != NULL ); - m_offers[key] = QPointer(conn); + m_offers[key] = QPointer(conn); } @@ -928,7 +926,7 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn void Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Connecting to " << conn->peerIpAddress() << " failed"; + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Connecting to " << socket->peerAddress().toString() << " failed: " << socket->errorString(); bool connIsNull = socket->_conn.isNull(); cleanupSocket( socket ); From a861943222ab5afeb5bd8ff090997ad6e209256e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 16 May 2013 11:58:27 +0200 Subject: [PATCH 136/565] Add more verbose logging --- src/libtomahawk/network/Servent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 955b8554a2..8a4112062b 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -504,6 +504,7 @@ Servent::readyRead() { Q_ASSERT( this->thread() == QThread::currentThread() ); QPointer< QTcpSocketExtra > sock = (QTcpSocketExtra*)sender(); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Starting to read from new incoming connection from: " << sock->peerAddress().toString(); if ( sock.isNull() || sock.data()->_disowned ) { @@ -774,7 +775,10 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) peerInfoDebug( peerInfo ) << "connectToPeer: search for already established connections to the same nodeid:" << m_controlconnections.count() << "connections"; if ( peerInfo->controlConnection() ) + { + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "deleting the existing Controlconnection"; delete peerInfo->controlConnection(); + } bool isDupe = false; ControlConnection* conn = 0; From c5731c20d5969d7c5c9f04b798d1bd4743903358 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 16 May 2013 16:36:17 +0200 Subject: [PATCH 137/565] Refactor the creation of local SipInfos into its own method --- src/libtomahawk/network/Servent.cpp | 70 ++++++++++++++++------------- src/libtomahawk/network/Servent.h | 1 + 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 8a4112062b..239318f383 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -347,6 +347,44 @@ Servent::lookupControlConnection( const SipInfo& sipInfo ) } +QList +Servent::getLocalSipInfos( const QString& nodeid, QString key ) +{ + QList sipInfos = QList(); + foreach ( QHostAddress ha, m_externalAddresses ) + { + SipInfo info = SipInfo(); + info.setHost( ha.toString() ); + info.setPort( m_port ); + info.setKey( key ); + info.setVisible( true ); + info.setNodeId( nodeid ); + sipInfos.append( info ); + } + if ( m_externalHostname.length() > 0) + { + SipInfo info = SipInfo(); + info.setHost( m_externalHostname ); + info.setPort( m_externalPort ); + info.setKey( key ); + info.setVisible( true ); + info.setNodeId( nodeid ); + sipInfos.append( info ); + } + + if ( sipInfos.isEmpty() ) + { + // We are not visible via any IP, send a dummy SipInfo + SipInfo info = SipInfo(); + info.setVisible( false ); + info.setKey( key ); + info.setNodeId( nodeid ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Only accepting connections, no usable IP to listen to found."; + } + + return sipInfos; +} + void Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) { @@ -398,37 +436,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) conn->addPeerInfo( peerInfo ); registerOffer( key, conn ); - QList sipInfos = QList(); - foreach ( QHostAddress ha, m_externalAddresses ) - { - SipInfo info = SipInfo(); - info.setHost( ha.toString() ); - info.setPort( m_port ); - info.setKey( key ); - info.setVisible( true ); - info.setNodeId( nodeid ); - sipInfos.append( info ); - } - if ( m_externalHostname.length() > 0) - { - SipInfo info = SipInfo(); - info.setHost( m_externalHostname ); - info.setPort( m_externalPort ); - info.setKey( key ); - info.setVisible( true ); - info.setNodeId( nodeid ); - sipInfos.append( info ); - } - - if ( sipInfos.isEmpty() ) - { - // We are not visible via any IP, send a dummy SipInfo - SipInfo info = SipInfo(); - info.setVisible( false ); - info.setKey( key ); - info.setNodeId( nodeid ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Only accepting connections, no usable IP to listen to found."; - } + QList sipInfos = getLocalSipInfos( nodeid, key ); peerInfo->sendLocalSipInfos( sipInfos ); diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 27aabd710c..cce7cb388c 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -130,6 +130,7 @@ public slots: bool isReady() const { return m_ready; } + QList getLocalSipInfos(const QString& nodeid, QString key); signals: void dbSyncTriggered(); void streamStarted( StreamConnection* ); From 945cf58e7827877af3aa3e8934c719979158c9b1 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 16 May 2013 17:29:25 +0200 Subject: [PATCH 138/565] Add const to arguments in getLocalSipInfos --- src/libtomahawk/network/Servent.cpp | 2 +- src/libtomahawk/network/Servent.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 239318f383..36c4bfbe30 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -348,7 +348,7 @@ Servent::lookupControlConnection( const SipInfo& sipInfo ) QList -Servent::getLocalSipInfos( const QString& nodeid, QString key ) +Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) { QList sipInfos = QList(); foreach ( QHostAddress ha, m_externalAddresses ) diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index cce7cb388c..759822a074 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -130,7 +130,7 @@ public slots: bool isReady() const { return m_ready; } - QList getLocalSipInfos(const QString& nodeid, QString key); + QList getLocalSipInfos(const QString& nodeid, const QString &key); signals: void dbSyncTriggered(); void streamStarted( StreamConnection* ); From 08fb69abb97b25bd1ec0d5739a74918f95382d7c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 16 May 2013 17:29:46 +0200 Subject: [PATCH 139/565] Lazily create ControlConnection for offers --- src/libtomahawk/network/Servent.cpp | 28 +++++++++++++++++++++++----- src/libtomahawk/network/Servent.h | 2 ++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 36c4bfbe30..3dad48481f 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -308,7 +308,12 @@ Servent::isValidExternalIP( const QHostAddress& addr ) void Servent::registerOffer( const QString& key, Connection* conn ) { - m_offers[key] = QPointer(conn); + m_offers[key] = QPointer(conn); +} + +void Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, const QString &nodeid ) +{ + m_lazyoffers[key] = QPair< peerinfo_ptr, QString >( peerInfo, nodeid ); } @@ -430,12 +435,12 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) QString key = uuid(); const QString& nodeid = Database::instance()->impl()->dbid(); - ControlConnection* conn = new ControlConnection( this ); + /*ControlConnection* conn = new ControlConnection( this ); conn->setName( peerInfo->contactId() ); conn->setId( nodeid ); - conn->addPeerInfo( peerInfo ); + conn->addPeerInfo( peerInfo );*/ - registerOffer( key, conn ); + registerLazyOffer( key, peerInfo, nodeid ); QList sipInfos = getLocalSipInfos( nodeid, key ); peerInfo->sendLocalSipInfos( sipInfos ); @@ -1044,7 +1049,20 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString } } - if ( m_offers.contains( key ) ) + if ( m_lazyoffers.contains( key ) ) + { + ControlConnection* conn = new ControlConnection( this ); + conn->setName( m_lazyoffers.value( key ).first->contactId() ); + conn->addPeerInfo( m_lazyoffers.value( key ).first ); + conn->setId( m_lazyoffers.value( key ).second ); + + // Register as non-lazy offer + m_lazyoffers.remove( key ); + registerOffer( key, conn ); + + return conn; + } + else if ( m_offers.contains( key ) ) { QPointer conn = m_offers.value( key ); if ( conn.isNull() ) diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 759822a074..fd2dd6ba2e 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -76,6 +76,7 @@ Q_OBJECT QString createConnectionKey( const QString& name = "", const QString &nodeid = "", const QString &key = "", bool onceOnly = true ); void registerOffer( const QString& key, Connection* conn ); + void registerLazyOffer( const QString& key, const Tomahawk::peerinfo_ptr& peerInfo, const QString &nodeid ); void registerControlConnection( ControlConnection* conn ); void unregisterControlConnection( ControlConnection* conn ); @@ -166,6 +167,7 @@ private slots: QJson::Parser parser; QList< ControlConnection* > m_controlconnections; // canonical list of authed peers QMap< QString, QPointer< Connection > > m_offers; + QMap< QString, QPair< Tomahawk::peerinfo_ptr, QString > > m_lazyoffers; QStringList m_connectedNodes; /** From bc3c65a0270790a912b1c8f7eb2e9b019f6485cc Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 17 May 2013 00:22:10 +0200 Subject: [PATCH 140/565] Split up log messages to know the real cause --- src/libtomahawk/network/Connection.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 511bcaa3c0..620d5a182c 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -216,14 +216,19 @@ void Connection::checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ) { QString bareName = name().contains( '/' ) ? name().left( name().indexOf( "/" ) ) : name(); - if ( nodeid != property( "nodeid" ).toString() || username != bareName ) + if ( nodeid != property( "nodeid" ).toString() ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "nodeid not ours, or username not our barename"; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( property( "nodeid" ).toString() ).arg( username ); + return; + } + if ( username != bareName ) + { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "username not our barename"; return; } disconnect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ) ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "ACL status is" << peerStatus; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; if ( peerStatus == ACLRegistry::Stream ) { QTimer::singleShot( 0, this, SLOT( doSetup() ) ); From e974c0888359401c270f905f0644495986855fff Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 17 May 2013 00:23:08 +0200 Subject: [PATCH 141/565] Make more use of peerInfoDebug --- src/libtomahawk/network/Servent.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 3dad48481f..b70cd330cc 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -869,9 +869,9 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn if ( sipInfos.isEmpty() ) { if ( conn != NULL ) { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; + peerInfoDebug(peerInfo) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; } else { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "No more possible SIP endpoints for skipping."; + peerInfoDebug(peerInfo) << Q_FUNC_INFO << "No more possible SIP endpoints for skipping."; } // If a peerinfo was supplied and has a ControlConnection which should be destroyed, than use this if ( !peerInfo.isNull() && peerInfo->controlConnection() ) @@ -881,17 +881,17 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn delete conn; return; } - QList sipInfo = QList(sipInfos); + QList sipInfo = QList( sipInfos ); // Use first available SIP endpoint and remove it from the list SipInfo info = sipInfo.takeFirst(); if ( !info.isVisible() ) { - // Try next SipInfo, we can't connect to this one + peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one"; connectToPeer( peerInfo, sipInfo, conn ); return; } - tDebug( LOGVERBOSE ) << "Servent::connectToPeer:" << info.host() << ":" << info.port() << thread() << QThread::currentThread(); + peerInfoDebug(peerInfo) << Q_FUNC_INFO << info.host() << ":" << info.port() << thread() << QThread::currentThread(); Q_ASSERT( info.port() > 0 ); Q_ASSERT( conn ); @@ -901,14 +901,14 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn { if ( QHostAddress( info.host() ) == ha) { - tDebug() << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; + peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; connectToPeer( peerInfo, sipInfo, conn ); return; } } if ( info.host() == m_externalHostname ) { - tDebug() << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; + peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; connectToPeer( peerInfo, sipInfo, conn ); return; } @@ -943,7 +943,7 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn void Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Connecting to " << socket->peerAddress().toString() << " failed: " << socket->errorString(); + peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Connecting to " << socket->peerAddress().toString() << " failed: " << socket->errorString(); bool connIsNull = socket->_conn.isNull(); cleanupSocket( socket ); From 9d89f8a12e7bd949a6509988fada914680ed21b5 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 17 May 2013 00:25:37 +0200 Subject: [PATCH 142/565] Only destroy existing ControlConnection if it does not run anymore --- src/libtomahawk/network/Servent.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index b70cd330cc..aae34489b6 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -789,8 +789,15 @@ Servent::connectToPeer( const peerinfo_ptr& peerInfo ) peerInfoDebug( peerInfo ) << "connectToPeer: search for already established connections to the same nodeid:" << m_controlconnections.count() << "connections"; if ( peerInfo->controlConnection() ) { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "deleting the existing Controlconnection"; - delete peerInfo->controlConnection(); + if ( peerInfo->controlConnection()->isReady() && peerInfo->controlConnection()->isRunning() ) { + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "We have a running ControlConnection, so no use to connect."; + return; + } + else + { + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "deleting the existing ControlConnection"; + delete peerInfo->controlConnection(); + } } bool isDupe = false; From 3de5670de78b791d0f5fff12333f6e54c6db877d Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 17 May 2013 17:08:54 +0200 Subject: [PATCH 143/565] Sent old tomahawk versions SipInfos like before. --- src/libtomahawk/network/Servent.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index aae34489b6..9ceb819c50 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -435,15 +435,28 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) QString key = uuid(); const QString& nodeid = Database::instance()->impl()->dbid(); - /*ControlConnection* conn = new ControlConnection( this ); - conn->setName( peerInfo->contactId() ); - conn->setId( nodeid ); - conn->addPeerInfo( peerInfo );*/ - registerLazyOffer( key, peerInfo, nodeid ); QList sipInfos = getLocalSipInfos( nodeid, key ); - - peerInfo->sendLocalSipInfos( sipInfos ); + // SipInfos were single-value before 0.7.999 + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) + { + SipInfo info = SipInfo(); + info.setVisible( false ); + foreach ( SipInfo _info, sipInfos ) + { + QHostAddress ha = QHostAddress( _info.host() ); + if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) ) + { + info = _info; + break; + } + } + peerInfo->sendLocalSipInfos( QList() << info ); + } + else + { + peerInfo->sendLocalSipInfos( sipInfos ); + } handleSipInfo( peerInfo ); connect( peerInfo.data(), SIGNAL( sipInfoChanged() ), SLOT( onSipInfoChanged() ) ); From 652a506922ea624719a454a63fe394c278074af8 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 17 May 2013 18:11:12 +0200 Subject: [PATCH 144/565] For old clients, just connect if the magic sort says we should. --- src/libtomahawk/network/Servent.cpp | 46 ++++++++++++++++++++++------- src/libtomahawk/network/Servent.h | 1 + 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 9ceb819c50..9d9d0c662b 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -390,6 +390,24 @@ Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) return sipInfos; } +SipInfo +Servent::getSipInfoForOldVersions( const QList& sipInfos ) const +{ + SipInfo info = SipInfo(); + info.setVisible( false ); + foreach ( SipInfo _info, sipInfos ) + { + QHostAddress ha = QHostAddress( _info.host() ); + if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) ) + { + info = _info; + break; + } + } + + return info; +} + void Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) { @@ -440,17 +458,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) // SipInfos were single-value before 0.7.999 if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) { - SipInfo info = SipInfo(); - info.setVisible( false ); - foreach ( SipInfo _info, sipInfos ) - { - QHostAddress ha = QHostAddress( _info.host() ); - if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) ) - { - info = _info; - break; - } - } + SipInfo info = getSipInfoForOldVersions( sipInfos ); peerInfo->sendLocalSipInfos( QList() << info ); } else @@ -483,6 +491,22 @@ void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) if ( peerInfo->sipInfos().isEmpty() ) return; + // Respect different behaviour before 0.7.99 + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) + { + SipInfo we = getSipInfoForOldVersions( getLocalSipInfos( QString(), QString() ) ); + SipInfo they = peerInfo->sipInfos().first(); + if ( they.isVisible() ) + { + if ( !we.isVisible() || we.host() < they.host() || (we.host() == they.host() && we.port() < they.port())) + { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << they.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); + connectToPeer( peerInfo ); + // We connected to the peer, so we are done here. + return; + } + } + } foreach ( SipInfo info, peerInfo->sipInfos() ) { if (info.isVisible()) diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index fd2dd6ba2e..50be80d7b0 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -132,6 +132,7 @@ public slots: bool isReady() const { return m_ready; } QList getLocalSipInfos(const QString& nodeid, const QString &key); + SipInfo getSipInfoForOldVersions( const QList &sipInfos ) const; signals: void dbSyncTriggered(); void streamStarted( StreamConnection* ); From 26f827037255f2f2584753d641931194ce988b2e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 17 May 2013 19:02:38 +0200 Subject: [PATCH 145/565] More logging to find error causes --- src/libtomahawk/network/Servent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 9d9d0c662b..0f210854d9 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -995,6 +995,10 @@ Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipIn // Try next SipInfo (don't do this if the connection was destroyed in between) connectToPeer( peerInfo, sipInfo, conn ); } + else + { + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Connecting stopped because Connection is null."; + } } void From 9723d7b4cea62776dd81a53caca5fd247fe751a1 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 17 May 2013 19:14:05 +0200 Subject: [PATCH 146/565] Do not delete the Connection in Socket cleanup, we take care of this at another stage. --- src/libtomahawk/network/Servent.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 0f210854d9..88e3884852 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -799,23 +799,11 @@ Servent::cleanupSocket( QTcpSocketExtra *sock ) return; } - if ( !sock->_conn.isNull() ) - { - Connection* conn = sock->_conn.data(); - - if ( !sock->_disowned ) - { - // connection will delete if we already transferred ownership, otherwise: - sock->deleteLater(); - } - - conn->markAsFailed(); // will emit failed, then finished - } - else + if ( sock->_conn.isNull() ) { tLog() << "SocketError, connection is null"; - sock->deleteLater(); } + sock->deleteLater(); } void From b3549ac94519a29e53c4d3eb02690e9fec16b6a1 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 18 May 2013 11:24:38 +0200 Subject: [PATCH 147/565] Connection should be able to shutdown even if the socket already disappeared --- src/libtomahawk/network/Connection.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 620d5a182c..1e02d15b23 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -311,15 +311,20 @@ Connection::doSetup() void Connection::socketDisconnected() { + qint64 bytesAvailable = 0; + if ( !m_sock.isNull() ) + { + bytesAvailable = m_sock->bytesAvailable(); + } tDebug( LOGVERBOSE ) << "SOCKET DISCONNECTED" << this->name() << id() << "shutdown will happen after incoming queue empties." - << "bytesavail:" << m_sock->bytesAvailable() + << "bytesavail:" << bytesAvailable << "bytesRecvd" << bytesReceived(); m_peer_disconnected = true; emit socketClosed(); - if ( m_msgprocessor_in.length() == 0 && m_sock->bytesAvailable() == 0 ) + if ( m_msgprocessor_in.length() == 0 && bytesAvailable == 0 ) { handleIncomingQueueEmpty(); actualShutdown(); From cad21c17120a395674464e3b6937048b083f4776 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 22 May 2013 11:12:41 +0200 Subject: [PATCH 148/565] Make less usage of QHostAddress when dealing with DNS records. --- src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp | 2 +- src/libtomahawk/network/Servent.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index 7cf4f6a882..c7e2c5c42e 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -142,7 +142,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter if ( info.isVisible() ) { QHostAddress ha = QHostAddress( info.host() ); - if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) ) + if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) || ( ha.isNull() && !info.host().isEmpty() ) ) { // For comapability reasons, this shall be put as the last candidate (this is the IP/host that would have been sent in previous versions) lastInfo = QSharedPointer( new SipInfo( info ) ); diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 88e3884852..126bc932ad 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -398,7 +398,7 @@ Servent::getSipInfoForOldVersions( const QList& sipInfos ) const foreach ( SipInfo _info, sipInfos ) { QHostAddress ha = QHostAddress( _info.host() ); - if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) ) + if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) || ( ha.isNull() && !_info.host().isEmpty() )) { info = _info; break; @@ -931,7 +931,7 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn // Check that we are not connecting to ourselves foreach( QHostAddress ha, m_externalAddresses ) { - if ( QHostAddress( info.host() ) == ha) + if ( info.host() == ha.toString() ) { peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; connectToPeer( peerInfo, sipInfo, conn ); From baaddf22a47f042edaa6d7852082973187b0c02f Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 23 May 2013 11:44:52 +0200 Subject: [PATCH 149/565] Introduce ConnectionManager for outgoing connections --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/network/ConnectionManager.cpp | 261 ++++++++++++++++++ src/libtomahawk/network/ConnectionManager.h | 68 +++++ src/libtomahawk/network/ControlConnection.cpp | 6 +- src/libtomahawk/network/Servent.cpp | 249 ++++------------- src/libtomahawk/network/Servent.h | 7 +- 6 files changed, 391 insertions(+), 201 deletions(-) create mode 100644 src/libtomahawk/network/ConnectionManager.cpp create mode 100644 src/libtomahawk/network/ConnectionManager.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 1fc6fca8d2..57d02537f0 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -311,6 +311,7 @@ list(APPEND libSources network/Connection.cpp network/ControlConnection.cpp network/QTcpSocketExtra.cpp + network/ConnectionManager.cpp playlist/PlaylistUpdaterInterface.cpp playlist/dynamic/DynamicPlaylist.cpp diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp new file mode 100644 index 0000000000..d8cce351bc --- /dev/null +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -0,0 +1,261 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "ConnectionManager.h" +#include "ControlConnection.h" +#include "Servent.h" + +#include "database/Database.h" +#include "database/DatabaseImpl.h" +#include "sip/SipPlugin.h" +#include "utils/Logger.h" + +#include +#include + +ConnectionManager::ConnectionManager( const QString &nodeid ) + : m_nodeid( nodeid ) +{ + // TODO sth? +} + +void +ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo ) +{ + // Start handling in a separate thread so that we do not block the event loop. + QtConcurrent::run( this, &ConnectionManager::handleSipInfoPrivate, peerInfo ); +} + +void +ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo ) +{ + m_mutex.lock(); + // Respect different behaviour before 0.7.99 + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) + { + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Using old-style (<0.7.99) connection order."; + SipInfo we = Servent::getSipInfoForOldVersions( Servent::instance()->getLocalSipInfos( QString(), QString() ) ); + SipInfo they = peerInfo->sipInfos().first(); + if ( they.isVisible() ) + { + if ( !we.isVisible() || we.host() < they.host() || (we.host() == they.host() && we.port() < they.port())) + { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << they.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); + connectToPeer( peerInfo, false ); + return; + // We connected to the peer, so we are done here. + } + } + m_mutex.unlock(); + return; + } + foreach ( SipInfo info, peerInfo->sipInfos() ) + { + if (info.isVisible()) + { + // There is at least one SipInfo that may be visible. Try connecting. + // Duplicate Connections are checked by connectToPeer, so we do not need to take care of this + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << info.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); + connectToPeer( peerInfo, false ); + // We connected to the peer, so we are done here. + return; + } + } + m_mutex.unlock(); +} + +void +ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool lock ) +{ + // Lock, so that we will not attempt to do two parallell connects. + if (lock) + { + m_mutex.lock(); + } + // Check that we are not already connected to this peer + ControlConnection* cconn = Servent::instance()->lookupControlConnection( peerInfo->nodeId() ); + if ( cconn != NULL || !m_controlConnection.isNull() ) + { + // We are already connected to this peer, so just add some more details. + peerInfoDebug( peerInfo ) << "Existing connection found, not connecting."; + cconn->addPeerInfo( peerInfo ); + if ( cconn != NULL ) + { + m_controlConnection = QPointer(cconn); + } + m_mutex.unlock(); + return; + // TODO: Keep the peerInfo in mind for reconnecting + // FIXME: Do we need this for reconnecting if the connection drops? + } + + // If we are not connected, try to connect + m_currentPeerInfo = peerInfo; + peerInfoDebug( peerInfo ) << "No existing connection found, trying to connect."; + m_sipCandidates.append( peerInfo->sipInfos() ); + + QVariantMap m; + m["conntype"] = "accept-offer"; + m["key"] = peerInfo->key(); + m["nodeid"] = Database::instance()->impl()->dbid(); + + m_controlConnection = QPointer( new ControlConnection( Servent::instance() ) ); + m_controlConnection->addPeerInfo( peerInfo ); + m_controlConnection->setFirstMessage( m ); + + if ( peerInfo->id().length() ) + m_controlConnection->setName( peerInfo->contactId() ); + if ( peerInfo->nodeId().length() ) + m_controlConnection->setId( peerInfo->nodeId() ); + + m_controlConnection->setProperty( "nodeid", peerInfo->nodeId() ); + + Servent::instance()->registerControlConnection( m_controlConnection.data() ); + tryConnect(); +} + +void ConnectionManager::tryConnect() +{ + // ATTENTION: mutex should be already locked by the calling function. + Q_ASSERT( !m_controlConnection.isNull() ); + + if ( m_sipCandidates.isEmpty() ) + { + // No more possibilities to connect. + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << m_controlConnection->name() << " skipping."; + + // Clean up. + m_currentPeerInfo.clear(); + delete m_controlConnection.data(); + m_mutex.unlock(); + return; + } + + // Use first available SIP endpoint and remove it from the list + SipInfo info = m_sipCandidates.takeFirst(); + if ( !info.isVisible() ) + { + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one"; + tryConnect(); + return; + } + + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << info.host() << ":" << info.port(); + Q_ASSERT( info.port() > 0 ); + + // Check that we are not connecting to ourselves + foreach( QHostAddress ha, Servent::instance()->addresses() ) + { + if ( info.host() == ha.toString() && info.port() == Servent::instance()->port() ) + { + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; + tryConnect(); + return; + } + } + if ( info.host() == Servent::instance()->additionalAddress() && info.port() == Servent::instance()->additionalPort() ) + { + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; + tryConnect(); + return; + } + + // We should have already setup a first message in connectToPeer + Q_ASSERT( !m_controlConnection->firstMessage().isNull() ); + + QTcpSocketExtra* sock = new QTcpSocketExtra(); + sock->setConnectTimeout( CONNECT_TIMEOUT ); + sock->_disowned = false; + sock->_conn = m_controlConnection.data(); + sock->_outbound = true; + + connect( sock, SIGNAL( connected() ), this, SLOT( socketConnected() ) ); + connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); + + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting socket to " << info.host() << ":" << info.port(); + sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite ); + sock->moveToThread( thread() ); +} + +void +ConnectionManager::socketError( QAbstractSocket::SocketError error ) +{ + Q_UNUSED( error ); + Q_ASSERT( !m_controlConnection.isNull() ); + + QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << sock->peerAddress().toString() << " failed: " << sock->errorString(); + sock->deleteLater(); + + // Try to connect with the next available SipInfo. + tryConnect(); +} + + +void +ConnectionManager::socketConnected() +{ + QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); + + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connected to hostaddr: " << sock->peerAddress() << ", hostname:" << sock->peerName(); + + Q_ASSERT( !sock->_conn.isNull() ); + + handoverSocket( sock ); + m_currentPeerInfo.clear(); + m_mutex.unlock(); +} + +void +ConnectionManager::handoverSocket( QTcpSocketExtra* sock ) +{ + Q_ASSERT( !m_controlConnection.isNull() ); + Q_ASSERT( sock ); + Q_ASSERT( m_controlConnection->socket().isNull() ); + Q_ASSERT( sock->isValid() ); + + disconnect( sock, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); + disconnect( sock, SIGNAL( disconnected() ), sock, SLOT( deleteLater() ) ); + disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); + + sock->_disowned = true; + m_controlConnection->setOutbound( sock->_outbound ); + m_controlConnection->setPeerPort( sock->peerPort() ); + + m_controlConnection->start( sock ); + m_currentPeerInfo.clear(); + m_mutex.unlock(); +} + + +static QMutex* nodeMapMutex = new QMutex(); +static QMap< QString, QSharedPointer< ConnectionManager > > connectionManagers; + +QSharedPointer +ConnectionManager::getManagerForNodeId( const QString &nodeid ) +{ + QMutexLocker locker( nodeMapMutex ); + if ( connectionManagers.contains( nodeid ) && !connectionManagers.value( nodeid ).isNull() ) { + return connectionManagers.value( nodeid ); + } + + // There exists no connection for this nodeid + QSharedPointer< ConnectionManager > manager( new ConnectionManager( nodeid ) ); + connectionManagers[nodeid] = manager; + return manager; +} diff --git a/src/libtomahawk/network/ConnectionManager.h b/src/libtomahawk/network/ConnectionManager.h new file mode 100644 index 0000000000..4f405ebbf7 --- /dev/null +++ b/src/libtomahawk/network/ConnectionManager.h @@ -0,0 +1,68 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include "DllMacro.h" + +#include "sip/PeerInfo.h" + +#include +#include +#include + +class QTcpSocketExtra; + +class DLLEXPORT ConnectionManager : public QObject +{ + Q_OBJECT + +public: + static QSharedPointer getManagerForNodeId( const QString& nodeid ); + ConnectionManager( const QString& nodeid ); + + void handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ); + +private slots: + void socketConnected(); + void socketError( QAbstractSocket::SocketError error ); + +private: + void connectToPeer(const Tomahawk::peerinfo_ptr& peerInfo , bool lock); + void handleSipInfoPrivate( const Tomahawk::peerinfo_ptr& peerInfo ); + + /** + * Transfers ownership of socket to the ControlConnection and inits the ControlConnection + */ + void handoverSocket( QTcpSocketExtra* sock ); + + /** + * Attempt to connect to the peer using the current stored information. + */ + void tryConnect(); + + // We just keep this for debug purposes and only during connection attempts. + Tomahawk::peerinfo_ptr m_currentPeerInfo; + QString m_nodeid; + QPointer m_controlConnection; + QList m_sipCandidates; + QMutex m_mutex; +}; + +#endif // CONNECTIONMANAGER_H diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index feda66add0..4b7e082b46 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -276,7 +276,11 @@ void ControlConnection::addPeerInfo( const peerinfo_ptr& peerInfo ) { peerInfo->setControlConnection( this ); - m_peerInfos.insert( peerInfo ); + // Check if we already have added this peerInfo + if ( !m_peerInfos.contains( peerInfo ) ) + { + m_peerInfos.insert( peerInfo ); + } } diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 126bc932ad..bd5267a4e0 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -27,6 +27,7 @@ #include "ControlConnection.h" #include "database/Database.h" #include "database/DatabaseImpl.h" +#include "network/ConnectionManager.h" #include "StreamConnection.h" #include "SourceList.h" #include "sip/SipInfo.h" @@ -247,7 +248,6 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const return _key; } - bool Servent::isValidExternalIP( const QHostAddress& addr ) { @@ -351,6 +351,17 @@ Servent::lookupControlConnection( const SipInfo& sipInfo ) return NULL; } +ControlConnection* +Servent::lookupControlConnection( const QString& nodeid ) +{ + foreach ( ControlConnection* c, m_controlconnections ) + { + if ( c->id() == nodeid ) + return c; + } + return NULL; +} + QList Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) @@ -391,7 +402,7 @@ Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) } SipInfo -Servent::getSipInfoForOldVersions( const QList& sipInfos ) const +Servent::getSipInfoForOldVersions( const QList& sipInfos ) { SipInfo info = SipInfo(); info.setVisible( false ); @@ -429,7 +440,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) peerInfoDebug(peerInfo) << "we need to establish the connection now... thinking"; if ( !connectedToSession( peerInfo->nodeId() ) ) { - connectToPeer( peerInfo ); + ConnectionManager::getManagerForNodeId( peerInfo->nodeId() )->handleSipInfo( peerInfo ); } else { @@ -491,40 +502,7 @@ void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) if ( peerInfo->sipInfos().isEmpty() ) return; - // Respect different behaviour before 0.7.99 - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) - { - SipInfo we = getSipInfoForOldVersions( getLocalSipInfos( QString(), QString() ) ); - SipInfo they = peerInfo->sipInfos().first(); - if ( they.isVisible() ) - { - if ( !we.isVisible() || we.host() < they.host() || (we.host() == they.host() && we.port() < they.port())) - { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << they.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); - connectToPeer( peerInfo ); - // We connected to the peer, so we are done here. - return; - } - } - } - foreach ( SipInfo info, peerInfo->sipInfos() ) - { - if (info.isVisible()) - { - // There is at least one SipInfo that may be visible. Try connecting. - // Duplicate Connections are checked by connectToPeer, so we do not need to take care of this - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << info.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); - connectToPeer( peerInfo ); - // We connected to the peer, so we are done here. - return; - } - } - - // If we reach this point none of the previous SipInfos was visible. - if ( peerInfo->controlConnection() ) - delete peerInfo->controlConnection(); - - tDebug() << Q_FUNC_INFO << peerInfo->id() << "They are not visible, doing nothing atm"; + ConnectionManager::getManagerForNodeId( peerInfo->nodeId() )->handleSipInfo( peerInfo ); } void @@ -721,7 +699,6 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, // if we can connect to them directly: if ( orig_conn && orig_conn->outbound() ) { - QList sipInfo = QList(); SipInfo info = SipInfo(); info.setVisible( true ); info.setKey( key ); @@ -729,8 +706,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, info.setHost( orig_conn->socket()->peerAddress().toString() ); info.setPort( orig_conn->peerPort() ); Q_ASSERT( info.isValid() ); - sipInfo.append( info ); - connectToPeer( peerinfo_ptr(), sipInfo, new_conn ); + initiateConnection( info, new_conn ); } else // ask them to connect to us: { @@ -755,13 +731,13 @@ Servent::socketConnected() { QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << thread() << "socket:" << sock << ", hostaddr:" << sock->peerAddress() << ", hostname:" << sock->peerName(); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << thread() << "socket:" << sock << ", hostaddr:" << sock->peerAddress() << ", hostname:" << sock->peerName(); if ( sock->_conn.isNull() ) { sock->close(); sock->deleteLater(); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Socket's connection was null, could have timed out or been given an invalid address"; + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Socket's connection was null, could have timed out or been given an invalid address"; return; } @@ -769,9 +745,9 @@ Servent::socketConnected() handoverSocket( conn, sock ); } - // transfers ownership of socket to the connection and inits the connection -void Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) +void +Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) { Q_ASSERT( conn ); Q_ASSERT( sock ); @@ -780,8 +756,7 @@ void Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) disconnect( sock, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); disconnect( sock, SIGNAL( disconnected() ), sock, SLOT( deleteLater() ) ); - disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), - this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); + disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); sock->_disowned = true; conn->setOutbound( sock->_outbound ); @@ -807,149 +782,33 @@ Servent::cleanupSocket( QTcpSocketExtra *sock ) } void -Servent::connectToPeer( const peerinfo_ptr& peerInfo ) +Servent::initiateConnection( const SipInfo& sipInfo, Connection* conn ) { - Q_ASSERT( this->thread() == QThread::currentThread() ); - - peerInfoDebug( peerInfo ) << "connectToPeer: search for already established connections to the same nodeid:" << m_controlconnections.count() << "connections"; - if ( peerInfo->controlConnection() ) - { - if ( peerInfo->controlConnection()->isReady() && peerInfo->controlConnection()->isRunning() ) { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "We have a running ControlConnection, so no use to connect."; - return; - } - else - { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "deleting the existing ControlConnection"; - delete peerInfo->controlConnection(); - } - } - - bool isDupe = false; - ControlConnection* conn = 0; - // try to find a ControlConnection with the same SipInfo, then we dont need to try to connect again - - foreach ( ControlConnection* c, m_controlconnections ) - { - Q_ASSERT( c ); - - if ( c->id() == peerInfo->nodeId() ) - { - conn = c; - - foreach ( const peerinfo_ptr& currentPeerInfo, c->peerInfos() ) - { - peerInfoDebug( currentPeerInfo ) << "Same object:" << ( peerInfo == currentPeerInfo ) << ( peerInfo.data() == currentPeerInfo.data() ) << ( peerInfo->debugName() == currentPeerInfo->debugName() ); - - if ( peerInfo == currentPeerInfo ) - { - isDupe = true; - peerInfoDebug( currentPeerInfo ) << "Not adding, because it's a dupe: peerInfoCount remains the same" << conn->peerInfos().count(); - break; - } - } - - if ( !c->peerInfos().contains( peerInfo ) ) - { - c->addPeerInfo( peerInfo ); -// peerInfoDebug(peerInfo) << "Adding " << peerInfo->debugName() << ", not a dupe... new peerInfoCount:" << c->peerInfos().count(); -// foreach ( const peerinfo_ptr& kuh, c->peerInfos() ) -// { -// peerInfoDebug(peerInfo) << " ** " << kuh->debugName(); -// } - } - - break; - } - } - - peerInfoDebug( peerInfo ) << "connectToPeer: found a match:" << ( conn ? conn->name() : "false" ) << "dupe:" << isDupe; - - if ( isDupe ) - { - peerInfoDebug( peerInfo ) << "it's a dupe, nothing to do here, returning and stopping processing: peerInfoCount:" << conn->peerInfos().count(); - } - - if ( conn ) - return; - - QVariantMap m; - m["conntype"] = "accept-offer"; - m["key"] = peerInfo->key(); - m["nodeid"] = Database::instance()->impl()->dbid(); - - peerInfoDebug(peerInfo) << "No match found, creating a new ControlConnection..."; - conn = new ControlConnection( this ); - conn->addPeerInfo( peerInfo ); - conn->setFirstMessage( m ); - - if ( peerInfo->id().length() ) - conn->setName( peerInfo->contactId() ); - if ( peerInfo->nodeId().length() ) - conn->setId( peerInfo->nodeId() ); - - conn->setProperty( "nodeid", peerInfo->nodeId() ); - - registerControlConnection( conn ); - connectToPeer( peerInfo, peerInfo->sipInfos(), conn ); -} - - -void -Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipInfos, Connection* conn ) -{ - if ( sipInfos.isEmpty() ) - { - if ( conn != NULL ) { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; - } else { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "No more possible SIP endpoints for skipping."; - } - // If a peerinfo was supplied and has a ControlConnection which should be destroyed, than use this - if ( !peerInfo.isNull() && peerInfo->controlConnection() ) - delete peerInfo->controlConnection(); - else - // Connecting failed, so destroy this connection. - delete conn; - return; - } - QList sipInfo = QList( sipInfos ); - // Use first available SIP endpoint and remove it from the list - SipInfo info = sipInfo.takeFirst(); - if ( !info.isVisible() ) - { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one"; - connectToPeer( peerInfo, sipInfo, conn ); - return; - } - - peerInfoDebug(peerInfo) << Q_FUNC_INFO << info.host() << ":" << info.port() << thread() << QThread::currentThread(); - - Q_ASSERT( info.port() > 0 ); + Q_ASSERT( sipInfo.isValid() ); + Q_ASSERT( sipInfo.isVisible() ); + Q_ASSERT( sipInfo.port() > 0 ); Q_ASSERT( conn ); // Check that we are not connecting to ourselves foreach( QHostAddress ha, m_externalAddresses ) { - if ( info.host() == ha.toString() ) + if ( sipInfo.host() == ha.toString() ) { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; - connectToPeer( peerInfo, sipInfo, conn ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << sipInfo.host() << ":" << sipInfo.port() << ": same IP as ourselves."; return; } } - if ( info.host() == m_externalHostname ) + if ( sipInfo.host() == m_externalHostname ) { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; - connectToPeer( peerInfo, sipInfo, conn ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << sipInfo.host() << ":" << sipInfo.port() << ": same IP as ourselves."; return; } - if ( info.key().length() && conn->firstMessage().isNull() ) + if ( !sipInfo.key().isEmpty() && conn->firstMessage().isNull() ) { QVariantMap m; m["conntype"] = "accept-offer"; - m["key"] = info.key(); + m["key"] = sipInfo.key(); m["controlid"] = Database::instance()->impl()->dbid(); conn->setFirstMessage( m ); } @@ -961,45 +820,43 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn sock->_outbound = true; connect( sock, SIGNAL( connected() ), SLOT( socketConnected() ) ); - NewClosure( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), - this, SLOT( connectToPeerFailed( Tomahawk::peerinfo_ptr, QList, Connection*, QTcpSocketExtra* ) ), - peerInfo, sipInfo, conn, sock ); + connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); if ( !conn->peerIpAddress().isNull() ) - sock->connectToHost( conn->peerIpAddress(), info.port(), QTcpSocket::ReadWrite ); + sock->connectToHost( conn->peerIpAddress(), sipInfo.port(), QTcpSocket::ReadWrite ); else - sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite ); + sock->connectToHost( sipInfo.host(), sipInfo.port(), QTcpSocket::ReadWrite ); sock->moveToThread( thread() ); } void -Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) +Servent::socketError( QAbstractSocket::SocketError e ) { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Connecting to " << socket->peerAddress().toString() << " failed: " << socket->errorString(); - bool connIsNull = socket->_conn.isNull(); - cleanupSocket( socket ); - - if ( !connIsNull ) { - // Try next SipInfo (don't do this if the connection was destroyed in between) - connectToPeer( peerInfo, sipInfo, conn ); - } - else + QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); + if ( !sock ) { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Connecting stopped because Connection is null."; + tLog() << "SocketError, sock is null"; + return; } -} -void -Servent::socketError( QAbstractSocket::SocketError e ) -{ - QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); if ( !sock->_conn.isNull() ) { Connection* conn = sock->_conn.data(); - tLog() << Q_FUNC_INFO << e << conn->id() << conn->name(); - } + tLog() << "Servent::SocketError:" << e << conn->id() << conn->name(); + + if ( !sock->_disowned ) + { + // connection will delete if we already transferred ownership, otherwise: + sock->deleteLater(); + } - cleanupSocket( sock ); + conn->markAsFailed(); // will emit failed, then finished + } + else + { + tLog() << "SocketError, connection is null"; + sock->deleteLater(); + } } void diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 50be80d7b0..9724f46ff7 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -66,6 +66,7 @@ Q_OBJECT public: static Servent* instance(); static bool isValidExternalIP( const QHostAddress& addr ); + static SipInfo getSipInfoForOldVersions( const QList &sipInfos ); explicit Servent( QObject* parent = 0 ); virtual ~Servent(); @@ -81,6 +82,7 @@ Q_OBJECT void registerControlConnection( ControlConnection* conn ); void unregisterControlConnection( ControlConnection* conn ); ControlConnection* lookupControlConnection( const SipInfo& sipInfo ); + ControlConnection* lookupControlConnection( const QString& nodeid ); // you may call this method as often as you like for the same peerInfo, dupe checking is done inside void registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ); @@ -90,8 +92,7 @@ public slots: void onSipInfoChanged(); public: - void connectToPeer( const Tomahawk::peerinfo_ptr& ha ); - void connectToPeer(const Tomahawk::peerinfo_ptr &peerInfo, const QList& sipInfos, Connection* conn ); + void initiateConnection( const SipInfo& sipInfo, Connection* conn ); void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); bool visibleExternally() const { return (!m_externalHostname.isNull()) || (m_externalAddresses.length() > 0); } @@ -132,7 +133,6 @@ public slots: bool isReady() const { return m_ready; } QList getLocalSipInfos(const QString& nodeid, const QString &key); - SipInfo getSipInfoForOldVersions( const QList &sipInfos ) const; signals: void dbSyncTriggered(); void streamStarted( StreamConnection* ); @@ -145,7 +145,6 @@ public slots: public slots: void setExternalAddress( QHostAddress ha, unsigned int port ); - void connectToPeerFailed( const Tomahawk::peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn , QTcpSocketExtra *socket ); void socketError( QAbstractSocket::SocketError e ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key ); From 008b7e32a6ddd04a4456be1a8822b77ce9a8d43e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 23 May 2013 15:37:01 +0200 Subject: [PATCH 150/565] Weaken references to PeerInfos --- src/accounts/xmpp/sip/XmppSip.cpp | 11 +++++++++++ src/accounts/xmpp/sip/XmppSip.h | 1 + src/libtomahawk/network/Servent.cpp | 22 ++++++++++++++++++++-- src/libtomahawk/network/Servent.h | 3 ++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index 3176048215..16dee9d43b 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -922,6 +922,11 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) return; } peerInfo->setSipInfos( sipMessage->sipInfos() ); + // If we stored a reference for this peer in our sip-waiting-queue, remove it. + if ( peersWaitingForSip.contains( iq.from().full() ) ) + { + peersWaitingForSip.remove( iq.from().full() ); + } } } } @@ -964,6 +969,11 @@ XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type pr if ( !peerInfo.isNull() ) { peerInfo->setStatus( PeerInfo::Offline ); + // If we stored a reference for this peer in our sip-waiting-queue, remove it. + if ( peersWaitingForSip.contains( fulljid ) ) + { + peersWaitingForSip.remove( fulljid ); + } } return; @@ -981,6 +991,7 @@ XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type pr peerInfo->setContactId( jid.bare() ); peerInfo->setStatus( PeerInfo::Online ); peerInfo->setFriendlyName( m_jidsNames.value( jid.bare() ) ); + peersWaitingForSip[fulljid] = peerInfo; #ifndef ENABLE_HEADLESS if ( !m_avatarManager->avatar( jid.bare() ).isNull() ) diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index 302d12037a..b40456a946 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -153,6 +153,7 @@ private slots: enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard, RequestVersion, RequestedVersion }; AvatarManager* m_avatarManager; Jreen::PubSub::Manager* m_pubSubManager; + QMap peersWaitingForSip; }; #endif diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index bd5267a4e0..2744289884 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -311,11 +311,28 @@ Servent::registerOffer( const QString& key, Connection* conn ) m_offers[key] = QPointer(conn); } -void Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, const QString &nodeid ) +void +Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, const QString &nodeid, const int timeout ) { m_lazyoffers[key] = QPair< peerinfo_ptr, QString >( peerInfo, nodeid ); + QTimer* timer = new QTimer( this ); + timer->setSingleShot( true ); + NewClosure( timer, SIGNAL( timeout() ), this, SLOT( deleteLazyOffer( const QString& ) ), key ); + timer->start(); } +void +Servent::deleteLazyOffer( const QString& key ) +{ + m_lazyoffers.remove( key ); + + // Cleanup. + QTimer* timer = (QTimer*)sender(); + if ( timer ) + { + timer->deleteLater(); + } +} void Servent::registerControlConnection( ControlConnection* conn ) @@ -464,8 +481,9 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) QString key = uuid(); const QString& nodeid = Database::instance()->impl()->dbid(); - registerLazyOffer( key, peerInfo, nodeid ); QList sipInfos = getLocalSipInfos( nodeid, key ); + // The offer should be removed after some time or we will build up a heap of unused PeerInfos + registerLazyOffer( key, peerInfo, nodeid, sipInfos.length() * 1.5 * CONNECT_TIMEOUT ); // SipInfos were single-value before 0.7.999 if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) { diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 9724f46ff7..303ecd45ff 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -77,7 +77,7 @@ Q_OBJECT QString createConnectionKey( const QString& name = "", const QString &nodeid = "", const QString &key = "", bool onceOnly = true ); void registerOffer( const QString& key, Connection* conn ); - void registerLazyOffer( const QString& key, const Tomahawk::peerinfo_ptr& peerInfo, const QString &nodeid ); + void registerLazyOffer( const QString& key, const Tomahawk::peerinfo_ptr& peerInfo, const QString &nodeid , const int timeout ); void registerControlConnection( ControlConnection* conn ); void unregisterControlConnection( ControlConnection* conn ); @@ -156,6 +156,7 @@ public slots: private slots: void readyRead(); + void deleteLazyOffer( const QString& key ); Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); From b362e2a9bd185588494a35510ffd1e0c2025a5ca Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 23 May 2013 18:36:06 +0200 Subject: [PATCH 151/565] Wait for versionString in Xmpp until telling Servent SipInfos --- src/accounts/xmpp/sip/XmppSip.cpp | 32 ++++++++++++++++++- src/accounts/xmpp/sip/XmppSip.h | 5 ++- src/libtomahawk/network/ConnectionManager.cpp | 5 +-- src/libtomahawk/network/Servent.cpp | 2 +- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index 16dee9d43b..e97e7e642b 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -879,12 +879,22 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) Jreen::SoftwareVersion::Ptr softwareVersion = iq.payload(); if ( softwareVersion ) { + QMutexLocker locker( &peerQueueMutex ); QString versionString = QString( "%1 %2 %3" ).arg( softwareVersion->name(), softwareVersion->os(), softwareVersion->version() ); qDebug() << Q_FUNC_INFO << "Received software version for" << iq.from().full() << ":" << versionString; Tomahawk::peerinfo_ptr peerInfo = PeerInfo::get( this, iq.from().full() ); if ( !peerInfo.isNull() ) { peerInfo->setVersionString( versionString ); + if ( sipinfosQueue.contains( iq.from().full() ) ) + { + peerInfo->setSipInfos( sipinfosQueue.value( iq.from().full() ) ); + sipinfosQueue.remove( iq.from().full() ); + } + if ( peersWaitingForVersionString.contains( iq.from().full() ) ) + { + peersWaitingForVersionString.remove( iq.from().full() ); + } } } } @@ -905,6 +915,7 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) TomahawkXmppMessage::Ptr sipMessage = iq.payload< TomahawkXmppMessage >(); if ( sipMessage ) { + QMutexLocker locker( &peerQueueMutex ); iq.accept(); tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Received Sip Information from:" << iq.from().full(); @@ -921,7 +932,15 @@ XmppSipPlugin::onNewIq( const Jreen::IQ& iq ) tDebug() << Q_FUNC_INFO << "no valid peerInfo for" << iq.from().full(); return; } - peerInfo->setSipInfos( sipMessage->sipInfos() ); + if ( peerInfo->versionString().isEmpty() ) + { + // If we do not have a version string, this peerInfo is still queued. So we queue its SipInfo until we have a valid version string. + sipinfosQueue[iq.from().full()] = sipMessage->sipInfos(); + } + else + { + peerInfo->setSipInfos( sipMessage->sipInfos() ); + } // If we stored a reference for this peer in our sip-waiting-queue, remove it. if ( peersWaitingForSip.contains( iq.from().full() ) ) { @@ -968,12 +987,21 @@ XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type pr Tomahawk::peerinfo_ptr peerInfo = PeerInfo::get( this, fulljid ); if ( !peerInfo.isNull() ) { + QMutexLocker locker( &peerQueueMutex ); peerInfo->setStatus( PeerInfo::Offline ); // If we stored a reference for this peer in our sip-waiting-queue, remove it. if ( peersWaitingForSip.contains( fulljid ) ) { peersWaitingForSip.remove( fulljid ); } + if ( peersWaitingForVersionString.contains( fulljid ) ) + { + peersWaitingForVersionString.remove( fulljid ); + } + if ( sipinfosQueue.contains( fulljid ) ) + { + sipinfosQueue.remove( fulljid ); + } } return; @@ -985,6 +1013,7 @@ XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type pr { qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid; + QMutexLocker locker( &peerQueueMutex ); m_peers[ jid ] = presenceType; Tomahawk::peerinfo_ptr peerInfo = PeerInfo::get( this, fulljid, PeerInfo::AutoCreate ); @@ -992,6 +1021,7 @@ XmppSipPlugin::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type pr peerInfo->setStatus( PeerInfo::Online ); peerInfo->setFriendlyName( m_jidsNames.value( jid.bare() ) ); peersWaitingForSip[fulljid] = peerInfo; + peersWaitingForVersionString[fulljid] = peerInfo; #ifndef ENABLE_HEADLESS if ( !m_avatarManager->avatar( jid.bare() ).isNull() ) diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index b40456a946..8f03d9dbdf 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -153,7 +153,10 @@ private slots: enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard, RequestVersion, RequestedVersion }; AvatarManager* m_avatarManager; Jreen::PubSub::Manager* m_pubSubManager; - QMap peersWaitingForSip; + QMap< QString, Tomahawk::peerinfo_ptr > peersWaitingForSip; + QMap< QString, Tomahawk::peerinfo_ptr > peersWaitingForVersionString; + QMap< QString, QList< SipInfo > > sipinfosQueue; + QMutex peerQueueMutex; }; #endif diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index d8cce351bc..407ee4d5ed 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -46,10 +46,11 @@ ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo { m_mutex.lock(); // Respect different behaviour before 0.7.99 - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Trying to connect to client with version " << peerInfo->versionString().split(' ').last() << TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ); + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ) < 0) { peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Using old-style (<0.7.99) connection order."; - SipInfo we = Servent::getSipInfoForOldVersions( Servent::instance()->getLocalSipInfos( QString(), QString() ) ); + SipInfo we = Servent::getSipInfoForOldVersions( Servent::instance()->getLocalSipInfos( QString( "default" ), QString( "default" ) ) ); SipInfo they = peerInfo->sipInfos().first(); if ( they.isVisible() ) { diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 2744289884..3d65ecacd6 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -485,7 +485,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) // The offer should be removed after some time or we will build up a heap of unused PeerInfos registerLazyOffer( key, peerInfo, nodeid, sipInfos.length() * 1.5 * CONNECT_TIMEOUT ); // SipInfos were single-value before 0.7.999 - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ) < 0) { SipInfo info = getSipInfoForOldVersions( sipInfos ); peerInfo->sendLocalSipInfos( QList() << info ); From b9b24e8161abca10b4992d509b6da911b405853c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 23 May 2013 19:43:38 +0200 Subject: [PATCH 152/565] readyRead is not used as a Slot/Signal --- src/libtomahawk/network/ConnectionManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 407ee4d5ed..b13437c562 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -230,7 +230,6 @@ ConnectionManager::handoverSocket( QTcpSocketExtra* sock ) Q_ASSERT( m_controlConnection->socket().isNull() ); Q_ASSERT( sock->isValid() ); - disconnect( sock, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); disconnect( sock, SIGNAL( disconnected() ), sock, SLOT( deleteLater() ) ); disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); From 926f42ed1f7ef42bccbd68cf3a416647db017947 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 23 May 2013 19:43:50 +0200 Subject: [PATCH 153/565] Set interval to timeout --- src/libtomahawk/network/Servent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 3d65ecacd6..7430a46eff 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -316,6 +316,7 @@ Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, con { m_lazyoffers[key] = QPair< peerinfo_ptr, QString >( peerInfo, nodeid ); QTimer* timer = new QTimer( this ); + timer->setInterval( timeout ); timer->setSingleShot( true ); NewClosure( timer, SIGNAL( timeout() ), this, SLOT( deleteLazyOffer( const QString& ) ), key ); timer->start(); From e8286b2995078254ea45d8b18d5aed3ef5e3a9e0 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 24 May 2013 10:49:00 +0200 Subject: [PATCH 154/565] Change ContolContection* to QPointer --- src/libtomahawk/Source.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 842665cf20..f46d633a52 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -25,6 +25,7 @@ #include #include "Typedefs.h" +#include "network/ControlConnection.h" #include "network/DbSyncConnection.h" #include "collection/Collection.h" #include "Query.h" @@ -32,7 +33,6 @@ #include "DllMacro.h" -class ControlConnection; class DatabaseCommand_DeleteFiles; class DatabaseCommand_LoadAllSources; class DatabaseCommand_LogPlayback; @@ -85,7 +85,7 @@ friend class ::MusicScanner; void removeCollection( const Tomahawk::collection_ptr& c ); int id() const { return m_id; } - ControlConnection* controlConnection() const { return m_cc; } + ControlConnection* controlConnection() const { return m_cc.data(); } void setControlConnection( ControlConnection* cc ); const QSet< Tomahawk::peerinfo_ptr > peerInfos() const; @@ -168,7 +168,7 @@ private slots: DBSyncConnection::State m_state; QTimer m_currentTrackTimer; - ControlConnection* m_cc; + QPointer m_cc; QList< QSharedPointer > m_cmds; int m_commandCount; QString m_lastCmdGuid; From 7b7dffbf0a3a4709ceaec684dd6a4887e2779917 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 26 May 2013 19:34:31 +0200 Subject: [PATCH 155/565] Credits to me --- src/accounts/xmpp/sip/TomahawkXmppMessage.cpp | 1 + src/accounts/xmpp/sip/TomahawkXmppMessage.h | 1 + src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp | 1 + src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h | 1 + src/accounts/xmpp/sip/XmppSip.cpp | 1 + src/accounts/xmpp/sip/XmppSip.h | 1 + src/accounts/zeroconf/TomahawkZeroconf.h | 1 + src/libtomahawk/Source.h | 1 + src/libtomahawk/network/Connection.cpp | 1 + src/libtomahawk/network/QTcpSocketExtra.cpp | 1 + src/libtomahawk/network/QTcpSocketExtra.h | 1 + src/libtomahawk/network/Servent.cpp | 1 + src/libtomahawk/network/Servent.h | 1 + src/libtomahawk/sip/PeerInfo.cpp | 1 + src/tomahawk/DiagnosticsDialog.cpp | 1 + 15 files changed, 15 insertions(+) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp index 84b5258335..df15f9b777 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessage.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessage.h b/src/accounts/xmpp/sip/TomahawkXmppMessage.h index fb7e6d43db..63d5bc09f4 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessage.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessage.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index c7e2c5c42e..b28e3bc40e 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h index 5e2d53996b..f2d2a9de32 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/accounts/xmpp/sip/XmppSip.cpp b/src/accounts/xmpp/sip/XmppSip.cpp index e97e7e642b..530f65ed63 100644 --- a/src/accounts/xmpp/sip/XmppSip.cpp +++ b/src/accounts/xmpp/sip/XmppSip.cpp @@ -4,6 +4,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2011, Leo Franchi * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index 8f03d9dbdf..0a70b734fa 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -4,6 +4,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2011, Leo Franchi * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/accounts/zeroconf/TomahawkZeroconf.h b/src/accounts/zeroconf/TomahawkZeroconf.h index afc7b36c58..87d45845a3 100644 --- a/src/accounts/zeroconf/TomahawkZeroconf.h +++ b/src/accounts/zeroconf/TomahawkZeroconf.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index f46d633a52..389ddf25ce 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 1e02d15b23..8b12446081 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/network/QTcpSocketExtra.cpp b/src/libtomahawk/network/QTcpSocketExtra.cpp index c77e125172..4c1a51e5f1 100644 --- a/src/libtomahawk/network/QTcpSocketExtra.cpp +++ b/src/libtomahawk/network/QTcpSocketExtra.cpp @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/network/QTcpSocketExtra.h b/src/libtomahawk/network/QTcpSocketExtra.h index 366f93688c..0ca56da1da 100644 --- a/src/libtomahawk/network/QTcpSocketExtra.h +++ b/src/libtomahawk/network/QTcpSocketExtra.h @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 7430a46eff..69c97bc060 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 303ecd45ff..67a2a89e59 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 463badb83b..9daae2b7ca 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2012, Dominik Schmidt + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index 3b72b6f933..4091a7af04 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -2,6 +2,7 @@ * * Copyright 2011, Dominik Schmidt * Copyright 2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 8b63388eadf61bb9b3053b0020e87ecb6c32c627 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 27 May 2013 15:36:28 +0200 Subject: [PATCH 156/565] Remove trailing semicolons --- src/libtomahawk/network/Connection.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index ba8d92e28c..769e05a580 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -60,28 +60,28 @@ Q_OBJECT void setFirstMessage( const QVariant& m ); void setFirstMessage( msg_ptr m ); - msg_ptr firstMessage() const { return m_firstmsg; }; + msg_ptr firstMessage() const { return m_firstmsg; } - const QPointer& socket() { return m_sock; }; + const QPointer& socket() { return m_sock; } - void setOutbound( bool o ) { m_outbound = o; }; + void setOutbound( bool o ) { m_outbound = o; } bool outbound() const { return m_outbound; } - Servent* servent() { return m_servent; }; + Servent* servent() { return m_servent; } // get public port of remote peer: - int peerPort() { return m_peerport; }; - void setPeerPort( int p ) { m_peerport = p; }; + int peerPort() { return m_peerport; } + void setPeerPort( int p ) { m_peerport = p; } void markAsFailed(); - void setName( const QString& n ) { m_name = n; }; - QString name() const { return m_name; }; + void setName( const QString& n ) { m_name = n; } + QString name() const { return m_name; } - void setOnceOnly( bool b ) { m_onceonly = b; }; - bool onceOnly() const { return m_onceonly; }; + void setOnceOnly( bool b ) { m_onceonly = b; } + bool onceOnly() const { return m_onceonly; } - bool isReady() const { return m_ready; } ; + bool isReady() const { return m_ready; } bool isRunning() const { return m_sock != 0; } qint64 bytesSent() const { return m_tx_bytes; } From 3fd379e81cb2a6c8eee832d6bca21e439af54e41 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 27 May 2013 15:37:41 +0200 Subject: [PATCH 157/565] Detect duplicate ControlConnections --- src/libtomahawk/Source.cpp | 24 +++++++++++++-- src/libtomahawk/Source.h | 2 +- src/libtomahawk/network/ControlConnection.cpp | 30 ++++++++++++------- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 509068e87c..eb4a247107 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -30,6 +30,7 @@ #include "database/DatabaseCommand_LoadAllSources.h" #include "database/DatabaseCommand_SourceOffline.h" #include "database/DatabaseCommand_UpdateSearchIndex.h" +#include "database/DatabaseImpl.h" #include "database/Database.h" #include @@ -81,10 +82,29 @@ Source::~Source() } -void +bool Source::setControlConnection( ControlConnection* cc ) { - m_cc = cc; + if ( !m_cc.isNull() && m_cc->isReady() && m_cc->isRunning() ) + { + const QString& nodeid = Database::instance()->impl()->dbid(); + if ( cc->id() < nodeid && m_cc->outbound() ) + { + m_cc = cc; + // This ControlConnection is not needed anymore, get rid of it! + m_cc->deleteLater(); + return true; + } + else + { + return false; + } + } + else + { + m_cc = cc; + return true; + } } diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 389ddf25ce..860369615d 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -87,7 +87,7 @@ friend class ::MusicScanner; int id() const { return m_id; } ControlConnection* controlConnection() const { return m_cc.data(); } - void setControlConnection( ControlConnection* cc ); + bool setControlConnection( ControlConnection* cc ); const QSet< Tomahawk::peerinfo_ptr > peerInfos() const; diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 4b7e082b46..880626edcb 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -100,20 +100,28 @@ ControlConnection::setup() // setup source and remote collection for this peer m_source = SourceList::instance()->get( id(), friendlyName, true ); - m_source->setControlConnection( this ); + if ( m_source->setControlConnection( this ) ) + { + // We are the new ControlConnection for this source - // delay setting up collection/etc until source is synced. - // we need it DB synced so it has an ID + exists in DB. - connect( m_source.data(), SIGNAL( syncedWithDatabase() ), - SLOT( registerSource() ), Qt::QueuedConnection ); + // delay setting up collection/etc until source is synced. + // we need it DB synced so it has an ID + exists in DB. + connect( m_source.data(), SIGNAL( syncedWithDatabase() ), + SLOT( registerSource() ), Qt::QueuedConnection ); - m_source->setOnline(); + m_source->setOnline(); - m_pingtimer = new QTimer; - m_pingtimer->setInterval( 5000 ); - connect( m_pingtimer, SIGNAL( timeout() ), SLOT( onPingTimer() ) ); - m_pingtimer->start(); - m_pingtimer_mark.start(); + m_pingtimer = new QTimer; + m_pingtimer->setInterval( 5000 ); + connect( m_pingtimer, SIGNAL( timeout() ), SLOT( onPingTimer() ) ); + m_pingtimer->start(); + m_pingtimer_mark.start(); + } + else + { + // There is already another ControlConnection in use, we are useless. + deleteLater(); + } } From e7be3ac8a2056a776af94cff613a9445c2637491 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Mon, 27 May 2013 17:54:21 +0200 Subject: [PATCH 158/565] Make tracks sending more discoverable. --- src/tomahawk/sourcetree/SourceDelegate.cpp | 73 ++++++++++++++++++---- src/tomahawk/sourcetree/SourceTreeView.cpp | 1 + 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/tomahawk/sourcetree/SourceDelegate.cpp b/src/tomahawk/sourcetree/SourceDelegate.cpp index 1fe0bb4331..ac2b8bb718 100644 --- a/src/tomahawk/sourcetree/SourceDelegate.cpp +++ b/src/tomahawk/sourcetree/SourceDelegate.cpp @@ -218,7 +218,8 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >(); SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() ); - QRect iconRect = option.rect.adjusted( 4, 6, -option.rect.width() + option.rect.height() - 12 + 4, -6 ); + const int iconRectVertMargin = 6; + QRect iconRect = option.rect.adjusted( 4, iconRectVertMargin, -option.rect.width() + option.rect.height() - 12 + 4, -iconRectVertMargin ); QString name = index.data().toString(); QPixmap avatar; int figWidth = 0; @@ -226,16 +227,30 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& QString desc; QString tracks; + bool shouldDrawDropHint = false; + if ( type == SourcesModel::Collection ) { + // If the user is about to drop a track on a peer + QRect itemsRect = option.rect; + QPoint cursorPos = m_parent->mapFromGlobal( QCursor::pos() ); + bool cursorInRect = itemsRect.contains( cursorPos ); + if ( cursorInRect && m_dropHoverIndex.isValid() && m_dropHoverIndex == index ) + shouldDrawDropHint = true; + SourceItem* colItem = qobject_cast< SourceItem* >( item ); Q_ASSERT( colItem ); bool status = !( !colItem || colItem->source().isNull() || !colItem->source()->isOnline() ); + if ( !colItem->source().isNull() && colItem->source()->isLocal() ) + shouldDrawDropHint = false; //can't send tracks to our own inbox + if ( status && colItem && !colItem->source().isNull() ) { tracks = QString::number( colItem->source()->trackCount() ); figWidth = QFontMetrics( figFont ).width( tracks ); + if ( shouldDrawDropHint ) + figWidth = iconRect.width(); name = colItem->source()->friendlyName(); } @@ -306,7 +321,7 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& ActionCollection::instance()->getAction( "togglePrivacy" )->icon().paint( painter, pmRect ); textRect.adjust( pmRect.width() + 3, 0, 0, 0 ); } - if ( isPlaying || ( !colItem->source().isNull() && colItem->source()->isLocal() ) ) + if ( ( isPlaying || ( !colItem->source().isNull() && colItem->source()->isLocal() ) ) && !shouldDrawDropHint ) { // Show a listen icon TomahawkUtils::ImageType listenAlongPixmap = TomahawkUtils::Invalid; @@ -363,10 +378,19 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& painter->setFont( font ); } textRect.adjust( 0, 0, 0, 2 ); + + if ( shouldDrawDropHint ) + { + descColor = option.palette.color( QPalette::Text ).lighter( 180 ); + desc = tr( "Drop to send tracks" ); + } + text = painter->fontMetrics().elidedText( desc, Qt::ElideRight, textRect.width() - 8 ); { QTextOption to( Qt::AlignVCenter ); + to.setWrapMode( QTextOption::NoWrap ); + painter->setPen( descColor ); painter->drawText( textRect, text, to ); } @@ -391,21 +415,32 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& shouldPaintTrackCount = true; } - if ( shouldPaintTrackCount ) + if ( shouldPaintTrackCount || shouldDrawDropHint ) { painter->setRenderHint( QPainter::Antialiasing ); - QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - 13, 0, -14, -option.rect.height() + option.fontMetrics.height() * 1.1 ); - int hd = ( option.rect.height() - figRect.height() ) / 2; - figRect.adjust( 0, hd, 0, hd ); + if ( shouldDrawDropHint ) + { + QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - iconRectVertMargin, iconRectVertMargin, -iconRectVertMargin, -iconRectVertMargin ); + painter->drawPixmap( figRect, TomahawkUtils::defaultPixmap( TomahawkUtils::Inbox, + TomahawkUtils::Original, + figRect.size() ) ); + } + else + { + QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - 13, 0, -14, -option.rect.height() + option.fontMetrics.height() * 1.1 ); + + int hd = ( option.rect.height() - figRect.height() ) / 2; + figRect.adjust( 0, hd, 0, hd ); - painter->setFont( figFont ); + painter->setFont( figFont ); - QColor figColor( TomahawkStyle::SIDEBAR_ROUNDFIGURE_BACKGROUND ); - painter->setPen( figColor ); - painter->setBrush( figColor ); + QColor figColor( TomahawkStyle::SIDEBAR_ROUNDFIGURE_BACKGROUND ); + painter->setPen( figColor ); + painter->setBrush( figColor ); - TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, figRect ); + TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, figRect ); + } } painter->restore(); @@ -964,6 +999,8 @@ SourceDelegate::hoveredDropType() const void SourceDelegate::hovered( const QModelIndex& index, const QMimeData* mimeData ) { + SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() ); + if ( !index.isValid() ) { foreach ( AnimationHelper* helper, m_expandedMap ) @@ -972,8 +1009,8 @@ SourceDelegate::hovered( const QModelIndex& index, const QMimeData* mimeData ) } return; } - - if ( !m_expandedMap.contains( index ) ) + if ( ( type == SourcesModel::StaticPlaylist || type == SourcesModel::CategoryAdd ) && + !m_expandedMap.contains( index ) ) { foreach ( AnimationHelper* helper, m_expandedMap ) { @@ -990,6 +1027,15 @@ SourceDelegate::hovered( const QModelIndex& index, const QMimeData* mimeData ) m_expandedMap.insert( m_newDropHoverIndex, new AnimationHelper( m_newDropHoverIndex ) ); connect( m_expandedMap.value( m_newDropHoverIndex ), SIGNAL( finished( QModelIndex ) ), SLOT( animationFinished( QModelIndex ) ) ); } + else if ( type == SourcesModel::Collection ) + { + m_dropMimeData->clear(); + foreach ( const QString& mimeDataFormat, mimeData->formats() ) + { + m_dropMimeData->setData( mimeDataFormat, mimeData->data( mimeDataFormat ) ); + } + m_dropHoverIndex = index; + } else qDebug() << "expandedMap already contains index" << index; } @@ -1002,6 +1048,7 @@ SourceDelegate::dragLeaveEvent() { helper->collapse( true ); } + m_dropHoverIndex = QModelIndex(); } diff --git a/src/tomahawk/sourcetree/SourceTreeView.cpp b/src/tomahawk/sourcetree/SourceTreeView.cpp index 69e3795f48..7ce9973d6a 100644 --- a/src/tomahawk/sourcetree/SourceTreeView.cpp +++ b/src/tomahawk/sourcetree/SourceTreeView.cpp @@ -729,6 +729,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event ) { case SourcesModel::StaticPlaylist: case SourcesModel::CategoryAdd: + case SourcesModel::Collection: //drop to send tracks to peers m_delegate->hovered( index, event->mimeData() ); dataChanged( index, index ); break; From 5e3d14c9508a34ac9a952d91b4d21c66d5850cd7 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 27 May 2013 20:27:33 +0200 Subject: [PATCH 159/565] Lock setControlConnection to prevent races --- src/libtomahawk/Source.cpp | 1 + src/libtomahawk/Source.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index eb4a247107..98d6c32904 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -85,6 +85,7 @@ Source::~Source() bool Source::setControlConnection( ControlConnection* cc ) { + QMutexLocker locker( &m_setControlConnectionMutex ); if ( !m_cc.isNull() && m_cc->isReady() && m_cc->isRunning() ) { const QString& nodeid = Database::instance()->impl()->dbid(); diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 860369615d..7273bc6b9a 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -174,6 +174,7 @@ private slots: int m_commandCount; QString m_lastCmdGuid; mutable QMutex m_cmdMutex; + QMutex m_setControlConnectionMutex; Tomahawk::playlistinterface_ptr m_playlistInterface; }; From 8215e5be009a06e743d6485de9a770e3f256274b Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Mon, 27 May 2013 21:35:40 +0200 Subject: [PATCH 160/565] Send tracks via context menu. --- src/libtomahawk/ContextMenu.cpp | 54 ++++++++++++++++++++++++++++++--- src/libtomahawk/ContextMenu.h | 29 ++++++++++-------- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/libtomahawk/ContextMenu.cpp b/src/libtomahawk/ContextMenu.cpp index 9096d3c801..277723b8c4 100644 --- a/src/libtomahawk/ContextMenu.cpp +++ b/src/libtomahawk/ContextMenu.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,12 +40,14 @@ using namespace Tomahawk; ContextMenu::ContextMenu( QWidget* parent ) : QMenu( parent ) + , m_playlists_sigmap( 0 ) + , m_sources_sigmap( 0 ) , m_loveAction( 0 ) { m_sigmap = new QSignalMapper( this ); connect( m_sigmap, SIGNAL( mapped( int ) ), SLOT( onTriggered( int ) ) ); - m_supportedActions = ActionPlay | ActionQueue | ActionPlaylist | ActionCopyLink | ActionLove | ActionStopAfter | ActionPage | ActionEditMetadata; + m_supportedActions = ActionPlay | ActionQueue | ActionPlaylist | ActionCopyLink | ActionLove | ActionStopAfter | ActionPage | ActionEditMetadata | ActionSend; } @@ -79,13 +82,31 @@ ContextMenu::addToPlaylist( int playlistIdx ) } +void +ContextMenu::sendToSource( int sourceIdx ) +{ + const Tomahawk::source_ptr &src = m_sources.at( sourceIdx ); + foreach ( Tomahawk::query_ptr query, m_queries ) + { + query->queryTrack()->share( src ); + } +} + + bool -caseInsensitiveLessThan( Tomahawk::playlist_ptr &s1, Tomahawk::playlist_ptr &s2 ) +playlistsLessThan( const Tomahawk::playlist_ptr& s1, const Tomahawk::playlist_ptr& s2 ) { return s1->title().toLower() < s2->title().toLower(); } +bool +sourcesLessThan( const Tomahawk::source_ptr& s1, const Tomahawk::source_ptr& s2 ) +{ + return s1->friendlyName().toLower() < s2->friendlyName().toLower(); +} + + void ContextMenu::setQueries( const QList& queries ) { @@ -107,21 +128,44 @@ ContextMenu::setQueries( const QList& queries ) // Get the current list of all playlists. m_playlists = QList< Tomahawk::playlist_ptr >( SourceList::instance()->getLocal()->dbCollection()->playlists() ); // Sort the playlist - qSort( m_playlists.begin(), m_playlists.end(), caseInsensitiveLessThan ); + qSort( m_playlists.begin(), m_playlists.end(), playlistsLessThan ); + if ( m_playlists_sigmap != 0 ) + m_playlists_sigmap->deleteLater(); m_playlists_sigmap = new QSignalMapper( this ); // Build the menu listing all available playlists QMenu* playlistMenu = addMenu( tr( "Add to &Playlist" ) ); for ( int i = 0; i < m_playlists.length(); ++i ) { - QAction* action = new QAction( m_playlists.at(i)->title() , this ); + QAction* action = new QAction( m_playlists.at( i )->title() , this ); playlistMenu->addAction(action); m_playlists_sigmap->setMapping( action, i ); - connect( action, SIGNAL( triggered() ), m_playlists_sigmap, SLOT( map() )); + connect( action, SIGNAL( triggered() ), m_playlists_sigmap, SLOT( map() ) ); } connect( m_playlists_sigmap, SIGNAL( mapped( int ) ), this, SLOT( addToPlaylist( int ) ) ); } + if ( m_supportedActions & ActionSend ) //Send to someone's Inbox! + { + // Get the buddies list + m_sources = SourceList::instance()->sources( true ); + qSort( m_sources.begin(), m_sources.end(), sourcesLessThan ); + + if ( m_sources_sigmap != 0 ) + m_sources_sigmap->deleteLater(); + m_sources_sigmap = new QSignalMapper( this ); + + QMenu* sourcesMenu = addMenu( tr( "Send to &Friend" ) ); + for ( int i = 0; i < m_sources.length(); ++i ) + { + QAction* action = new QAction( m_sources.at( i )->friendlyName(), this ); + sourcesMenu->addAction( action ); + m_sources_sigmap->setMapping( action, i ); + connect( action, SIGNAL( triggered() ), m_sources_sigmap, SLOT( map() ) ); + } + connect( m_sources_sigmap, SIGNAL( mapped( int ) ), this, SLOT( sendToSource( int ) ) ); + } + if ( m_supportedActions & ActionStopAfter && itemCount() == 1 ) { if ( AudioEngine::instance()->stopAfterTrack() == queries.first() ) diff --git a/src/libtomahawk/ContextMenu.h b/src/libtomahawk/ContextMenu.h index 19484669df..5b60324d67 100644 --- a/src/libtomahawk/ContextMenu.h +++ b/src/libtomahawk/ContextMenu.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,18 +37,19 @@ Q_OBJECT public: enum MenuActions { - ActionPlay = 1, - ActionQueue = 2, - ActionDelete = 4, - ActionCopyLink = 8, - ActionLove = 16, - ActionStopAfter = 32, - ActionPage = 64, - ActionTrackPage = 65, - ActionArtistPage = 66, - ActionAlbumPage = 67, - ActionEditMetadata = 128, - ActionPlaylist = 256 + ActionPlay = 1, + ActionQueue = 2, + ActionDelete = 4, + ActionCopyLink = 8, + ActionLove = 16, + ActionStopAfter = 32, + ActionPage = 64, + ActionTrackPage = 65, + ActionArtistPage = 66, + ActionAlbumPage = 67, + ActionEditMetadata = 128, + ActionPlaylist = 256, + ActionSend = 512 }; explicit ContextMenu( QWidget* parent = 0 ); @@ -80,12 +82,14 @@ private slots: void openPage( MenuActions action ); void addToQueue(); void addToPlaylist( int playlistIdx ); + void sendToSource( int sourceIdx ); void onSocialActionsLoaded(); private: QSignalMapper* m_sigmap; QSignalMapper* m_playlists_sigmap; + QSignalMapper* m_sources_sigmap; int m_supportedActions; QAction* m_loveAction; @@ -94,6 +98,7 @@ private slots: QList< Tomahawk::query_ptr > m_queries; QList< Tomahawk::artist_ptr > m_artists; QList< Tomahawk::album_ptr > m_albums; + QList< Tomahawk::source_ptr > m_sources; Tomahawk::playlistinterface_ptr m_interface; }; From d440061228076c1a2d307b446df3f4bad7310a5c Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Tue, 28 May 2013 02:16:50 +0200 Subject: [PATCH 161/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_bg.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_bn_IN.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_ca.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_ca@valencia.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_cs.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_da.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_de.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_el.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_en.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_es.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_fi.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_fr.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_gl.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_hi_IN.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_hu.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_id.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_it.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_ja.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_lt.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_pl.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_pt_BR.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_ru.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_sv.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_tr.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_zh_CN.ts | 64 +++++++++++++++++++++--------------- lang/tomahawk_zh_TW.ts | 64 +++++++++++++++++++++--------------- 27 files changed, 999 insertions(+), 729 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 87f6136de6..5ad66462da 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1800,19 +1800,24 @@ connect and stream from you? توب ١٠ - + All available tracks جميع الأغاني المتاحة - - + + Drop to send tracks + + + + + Show أظهر - - + + Hide إخفي @@ -2620,84 +2625,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &إستمع - - - + + + Add to &Queue أضف إلى &قائمة الانتظار - + Add to &Playlist أضف إلى &لوائح الأغاني - + + Send to &Friend + + + + Continue Playback after this &Track أكمل الاستماع بعد هذه &الأغنية - + Stop Playback after this &Track أوقف الاستماع بعد هذه &الأغنية - - + + &Love &أحب - - - + + + &Go to "%1" ا&ذهب إلى "%1" - - - + + + Go to "%1" اذهب إلى "%1" - + &Copy Track Link &نسخ رابط الأغنية - + &Remove Items &إزالة البنود - + &Remove Item &إزالة البند - + Copy Album &Link نسخ &رابط الالبوم - + Copy Artist &Link نسخ &رابط قائمة الأغاني - + Un-&Love لا &أحب - + Properties... خصائص... diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 36b23bcb54..a2330317c0 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1811,19 +1811,24 @@ Tomahawk създаде доклад относно това и изпращай Първите 10 - + All available tracks Всички налични изпълнения - - + + Drop to send tracks + + + + + Show Покажи - - + + Hide Скрий @@ -2634,84 +2639,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Изпълни - - - + + + Add to &Queue Добави към &опашката - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Продължи изпълнението след тази песен - + Stop Playback after this &Track Спри изпълнението след тази песен - - + + &Love &Харесай - - - + + + &Go to "%1" &Иди на "%1" - - - + + + Go to "%1" Иди на "%1" - + &Copy Track Link &Копирай адреса на изпълнението - + &Remove Items - + &Remove Item - + Copy Album &Link Копирай адреса на албума - + Copy Artist &Link Копирай адреса на изпълнителя - + Un-&Love Не-&харесай - + Properties... Подробности: diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 405e2f880e..c5a69a586e 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1797,19 +1797,24 @@ connect and stream from you? - + All available tracks - - + + Drop to send tracks + + + + + Show - - + + Hide @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play - - - + + + Add to &Queue - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track - + Stop Playback after this &Track - - + + &Love - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love - + Properties... diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index aa84445363..beb6173bb3 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1801,19 +1801,24 @@ i emissores basades en els vostres gusts musicals. Top 10 - + All available tracks Totes les cançons disponibles - - + + Drop to send tracks + + + + + Show Mostra - - + + Hide Amaga @@ -2621,84 +2626,89 @@ nomdusuari@jabber.org Tomahawk::ContextMenu - + &Play &Reprodueix - - - + + + Add to &Queue &Afegeix a la Cua - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Continua la reproducció després d'aques&ta pista - + Stop Playback after this &Track Atura la reproducció després d'aques&ta pista - - + + &Love &M'encanta - - - + + + &Go to "%1" &Ves a «%1» - - - + + + Go to "%1" Vés a «%1» - + &Copy Track Link &Copia l'Enllaç de la Cançó - + &Remove Items - + &Remove Item - + Copy Album &Link Copia l'enllaç de &l'àlbum - + Copy Artist &Link Copia l'enllça de &l'artista - + Un-&Love &Treu de les preferides - + Properties... Propietats... diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index a617cf3bef..1d7df1b0cb 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1801,19 +1801,24 @@ i emissores basades en els vostres gusts musicals. Top 10 - + All available tracks Totes les cançons disponibles - - + + Drop to send tracks + + + + + Show Mostra - - + + Hide Amaga @@ -2621,84 +2626,89 @@ nomdusuari@jabber.org Tomahawk::ContextMenu - + &Play &Reprodueix - - - + + + Add to &Queue &Afig a la Cua - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Continua la reproducció després d'aques&ta pista - + Stop Playback after this &Track Atura la reproducció després d'aques&ta pista - - + + &Love &M'encanta - - - + + + &Go to "%1" &Ves a «%1» - - - + + + Go to "%1" Vés a «%1» - + &Copy Track Link &Copia l'Enllaç de la Cançó - + &Remove Items - + &Remove Item - + Copy Album &Link Copia l'enllaç de &l'àlbum - + Copy Artist &Link Copia l'enllça de &l'artista - + Un-&Love &Treu de les preferides - + Properties... Propietats... diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 573eeb0218..ec6ddd1ecc 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1799,19 +1799,24 @@ se s vámi spojil? Nejlepších deset - + All available tracks Všechny dostupné skladby - - + + Drop to send tracks + + + + + Show Ukázat - - + + Hide Skrýt @@ -2619,84 +2624,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Přehrát - - - + + + Add to &Queue Přidat do řa&dy - + Add to &Playlist Přidat do &seznamu skladeb - + + Send to &Friend + + + + Continue Playback after this &Track Po této skladbě &pokračovat v přehrávání - + Stop Playback after this &Track Po této skladbě &zastavit přehrávání - - + + &Love &Oblíbená píseň - - - + + + &Go to "%1" &Jít na "%1" - - - + + + Go to "%1" Jít na "%1" - + &Copy Track Link &Kopírovat odkaz pro tuto skladbu - + &Remove Items &Odstranit položky - + &Remove Item Odstranit &položku - + Copy Album &Link Kopírovat odkaz pro toto &album - + Copy Artist &Link Kopírovat odkaz pro tohoto &umělce - + Un-&Love Zrušit zařazení mezi o&blíbené písně - + Properties... Vlastnosti... diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index a7ca8c7f8b..4c87fd10a4 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1798,19 +1798,24 @@ connect and stream from you? Top 10 - + All available tracks Alle tilgængelige numre - - + + Drop to send tracks + + + + + Show Vis - - + + Hide Skjul @@ -2613,84 +2618,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Afspil - - - + + + Add to &Queue Tilføj til &Kø - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track - + Stop Playback after this &Track - - + + &Love &Elsk - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link &Kopier Nummer Link - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love - + Properties... diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 7143fa1b08..0ef945ebd8 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1799,19 +1799,24 @@ erlauben sich mit dir zu verbinden? Top 10 - + All available tracks Alle verfügbaren Stücke - - + + Drop to send tracks + + + + + Show Einblenden - - + + Hide Verstecken @@ -2614,84 +2619,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Abspielen - - - + + + Add to &Queue In &Warteschlange einreihen - + Add to &Playlist In die Playlist hinzufügen - + + Send to &Friend + + + + Continue Playback after this &Track Nach diesem Lied &weiterspielen - + Stop Playback after this &Track Wiedergabe nach diesem Lied &stoppen - - + + &Love &Lieblingslied - - - + + + &Go to "%1" &Gehe zu "%1" - - - + + + Go to "%1" Gehe zu "%1" - + &Copy Track Link &Kopiere Link zu diesem Stück - + &Remove Items &Elemente entfernen - + &Remove Item &Element entfernen - + Copy Album &Link Album-&Link kopieren - + Copy Artist &Link Künstler-&Link kopieren - + Un-&Love Kein &Lieblingslied - + Properties... Eigenschaften... diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index cdd2d86e1f..21bd14235e 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1799,19 +1799,24 @@ connect and stream from you? Κορυφαία 10 - + All available tracks Όλα τα διαθέσιμα τραγούδια - - + + Drop to send tracks + + + + + Show Εμφάνιση - - + + Hide Απόκρυψη @@ -2620,84 +2625,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Αναπαραγωγή - - - + + + Add to &Queue Προσθήκη στην &Σειρά - + Add to &Playlist Προσθήκη στην &Λίστα αναπαραγωγής - + + Send to &Friend + + + + Continue Playback after this &Track Συνέχιση Αναπαραγωγής μετά το τρέχον τραγουδι - + Stop Playback after this &Track Διακοπή Αναπαραγωγής μετά το τρέχον Τραγουδι - - + + &Love &Αγάπησε - - - + + + &Go to "%1" &Πήγαινε στο "%1" - - - + + + Go to "%1" Πήγαινε στο "%1" - + &Copy Track Link &Αντιγραφή Συνδέσμου Κομματιού - + &Remove Items &Διαγραφή Αντικειμένων - + &Remove Item &Διαγραφή Αντικειμένου - + Copy Album &Link Αντιγραφη Λινκ του Αλμπουμ - + Copy Artist &Link Αντιγραφη του Λινκ του Καλλιτεχνη - + Un-&Love &Μίσησε - + Properties... Ιδιότητες diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 5993804010..28711d350a 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1802,19 +1802,24 @@ connect and stream from you? Top 10 - + All available tracks All available tracks - - + + Drop to send tracks + Drop to send tracks + + + + Show Show - - + + Hide Hide @@ -2622,84 +2627,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Play - - - + + + Add to &Queue Add to &Queue - + Add to &Playlist Add to &Playlist - + + Send to &Friend + Send to &Friend + + + Continue Playback after this &Track Continue Playback after this &Track - + Stop Playback after this &Track Stop Playback after this &Track - - + + &Love &Love - - - + + + &Go to "%1" &Go to "%1" - - - + + + Go to "%1" Go to "%1" - + &Copy Track Link &Copy Track Link - + &Remove Items &Remove Items - + &Remove Item &Remove Item - + Copy Album &Link Copy Album &Link - + Copy Artist &Link Copy Artist &Link - + Un-&Love Un-&Love - + Properties... Properties... diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 0cacbab495..a36e072325 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1802,19 +1802,24 @@ y estaciones basadas en sus gustos personales. 10 mejores - + All available tracks Todas las pistas disponibles - - + + Drop to send tracks + + + + + Show Mostrar - - + + Hide Ocultar @@ -2622,84 +2627,89 @@ usuario@jabber.org Tomahawk::ContextMenu - + &Play &Reproducir - - - + + + Add to &Queue Añadir a la &cola - + Add to &Playlist Añadir a &lista de reproducción - + + Send to &Friend + + + + Continue Playback after this &Track Continuar la reproducción tras esta pis&ta - + Stop Playback after this &Track Detener la reproducción tras esta pis&ta - - + + &Love &Favorito - - - + + + &Go to "%1" &Ir a "%1" - - - + + + Go to "%1" &Ir a "%1" - + &Copy Track Link &Copiar enlace del tema - + &Remove Items - + &Remove Item - + Copy Album &Link Copiar en&lace del álbum - + Copy Artist &Link Copiar en&lace del artista - + Un-&Love Quitar de &favoritos - + Properties... Propiedades… diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index d5f8609540..513ebdbd49 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1804,19 +1804,24 @@ käyttäjäradion käyttöönottamiseksi Kymmenen kärki - + All available tracks Kaikki saatavilla olevat kappaleet - - + + Drop to send tracks + + + + + Show Näytä - - + + Hide Piilota @@ -2625,84 +2630,89 @@ käyttäjätunnus@jabber.org Tomahawk::ContextMenu - + &Play &Soita - - - + + + Add to &Queue &Lisää jonoon - + Add to &Playlist Lisää s&oittolistalle - + + Send to &Friend + + + + Continue Playback after this &Track Jatka s&oittoa tämän kappaleen jälkeen - + Stop Playback after this &Track Pysäytä s&oitto tämän &kappaleen jälkeen - - + + &Love &Tykkää - - - + + + &Go to "%1" Sii&rry sivulle ”%1” - - - + + + Go to "%1" Siirry sivulle ”%1” - + &Copy Track Link &Kopioi kappaleen linkki - + &Remove Items &Poista kohteet - + &Remove Item &Poista kohde - + Copy Album &Link &Kopioi albumin linkki - + Copy Artist &Link &Kopioi artistin linkki - + Un-&Love Lakkaa &tykkäämästä - + Properties... Ominaisuudet... diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 0c09f02e7a..c788e79428 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1799,19 +1799,24 @@ de se connecter et streamer de vous? Top 10 - + All available tracks Tous les titres disponibles - - + + Drop to send tracks + + + + + Show Afficher - - + + Hide Masquer @@ -2619,84 +2624,89 @@ utilisateur@jabber.org Tomahawk::ContextMenu - + &Play &Lecture - - - + + + Add to &Queue &Ajouter à la file d'attente - + Add to &Playlist Ajouter à la &liste de lecture - + + Send to &Friend + + + + Continue Playback after this &Track Continuer la lecture après cette &piste - + Stop Playback after this &Track Stopper la lecture après cette &piste - - + + &Love &Favori - - - + + + &Go to "%1" Alle&r à "%1" - - - + + + Go to "%1" Aller à "%1" - + &Copy Track Link &Copier le lien de la piste - + &Remove Items &Supprimer les éléments - + &Remove Item &Supprimer l'élément - + Copy Album &Link Copier le &lien de l'album - + Copy Artist &Link Copier le &lien de l'artiste - + Un-&Love &Supprimer des Favoris - + Properties... Propriétés... diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 3c497053b9..0fb69e7208 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1798,19 +1798,24 @@ connect and stream from you? Os 10 primeiros - + All available tracks Tódalas pistas dispoñíbeis - - + + Drop to send tracks + + + + + Show Mostrar - - + + Hide Agochar @@ -2621,84 +2626,89 @@ nomedeusuario@jabber.org Tomahawk::ContextMenu - + &Play &Reproducir - - - + + + Add to &Queue Engadir á &ringleira - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Continuar á reprodución despois desta &pista - + Stop Playback after this &Track Deter a reprodución despois desta &pista - - + + &Love &Gústame - - - + + + &Go to "%1" &Ir a «%1» - - - + + + Go to "%1" Ir a «%1» - + &Copy Track Link &Copiar a ligazón da pista - + &Remove Items - + &Remove Item - + Copy Album &Link Copiar a &ligazón do álbum - + Copy Artist &Link Copiar a &ligazón do artista - + Un-&Love Xa non me &gusta - + Properties... Propiedades... diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 4437ecdf79..de55d081e0 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1797,19 +1797,24 @@ connect and stream from you? - + All available tracks - - + + Drop to send tracks + + + + + Show दिखाओ - - + + Hide छुपाओ @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play - - - + + + Add to &Queue - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track - + Stop Playback after this &Track - - + + &Love - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love - + Properties... diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index d43f89bd89..38e58a70cc 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1797,19 +1797,24 @@ connect and stream from you? Top 10 - + All available tracks Összes elérhető zeneszám - - + + Drop to send tracks + + + + + Show Mutatás - - + + Hide Elrejtés @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play - - - + + + Add to &Queue - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track - + Stop Playback after this &Track - - + + &Love - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love - + Properties... Tulajdonságok... diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index d18dd04650..03051a9ac7 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1797,19 +1797,24 @@ connect and stream from you? - + All available tracks - - + + Drop to send tracks + + + + + Show - - + + Hide @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play - - - + + + Add to &Queue - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track - + Stop Playback after this &Track - - + + &Love - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love - + Properties... diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index a7ca6caa98..b845ec6522 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1797,19 +1797,24 @@ connect and stream from you? Top 10 - + All available tracks Tutte le tracce disponibili - - + + Drop to send tracks + + + + + Show Mostra - - + + Hide Nascondi @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Play - - - + + + Add to &Queue Aggiungi alla &coda - + Add to &Playlist Aggiungi alla &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Continua la riproduzione dopo &Track - + Stop Playback after this &Track Ferma la riproduzione dopo &Track - - + + &Love &Love - - - + + + &Go to "%1" &Vai a "%1" - - - + + + Go to "%1" Vai a "%1" - + &Copy Track Link &Copia link della traccia - + &Remove Items &Cancella elementi - + &Remove Item &Cancella elemento - + Copy Album &Link Copia - + Copy Artist &Link Copia il &link dell'artista - + Un-&Love Un-&love - + Properties... Proprietà... diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index ff3eee6ae7..7d154fc4bb 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1802,19 +1802,24 @@ connect and stream from you? トップ10 - + All available tracks 利用可能トラック - - + + Drop to send tracks + + + + + Show 表示 - - + + Hide 隠す @@ -2619,84 +2624,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play 再生 - - - + + + Add to &Queue キューに追加 - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track このトラックの再生後、再生を続く - + Stop Playback after this &Track このトラックの再生後、再生を停止する - - + + &Love &Love - - - + + + &Go to "%1" %1に移動 - - - + + + Go to "%1" %1に移動 - + &Copy Track Link トラックリンクをコピー - + &Remove Items - + &Remove Item - + Copy Album &Link アルバムリンクをコピー - + Copy Artist &Link アーティストリンクをコピー - + Un-&Love Loveじゃないトラック - + Properties... 情報... diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index c1f62e3cd8..72c54e35e7 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1797,19 +1797,24 @@ connect and stream from you? Top 10 - + All available tracks Visi prieinami takeliai - - + + Drop to send tracks + + + + + Show Rodyti - - + + Hide Slėpti @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Groti - - - + + + Add to &Queue Pridėti į &eilę - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Tęsti grojimą po šio &takelio - + Stop Playback after this &Track Sustabdyti grojimą po šio &takelio - - + + &Love My&liu - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link &Kopijuoti takelio nuorodą - + &Remove Items - + &Remove Item - + Copy Album &Link Kopijuoti albumo &nuorodą - + Copy Artist &Link Kopijuoti atlikėjo &nuorodą - + Un-&Love Nemy&liu - + Properties... Savybės... diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 1def5be478..3f87534c3d 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1799,19 +1799,24 @@ połączyć się i strumieniować od ciebie? Top 10 - + All available tracks Wszystkie dostępne utwory - - + + Drop to send tracks + + + + + Show Pokaż - - + + Hide Ukryj @@ -2616,84 +2621,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Odtwarzaj - - - + + + Add to &Queue Dodaj do &kolejki - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Kontynuuj odtwarzanie po &tym utworze - + Stop Playback after this &Track Zatrzymaj odtwarzanie po &tym utworze - - + + &Love &Lubię - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link - + &Remove Items - + &Remove Item - + Copy Album &Link Kopiuj &link do albumu - + Copy Artist &Link Kopiuj &link do wykonawcy - + Un-&Love Przestań &lubić - + Properties... diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index fc064834d8..5dce804518 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1799,19 +1799,24 @@ se conecte e faça o stream de você? 10 Mais - + All available tracks Todas as faixas disponíveis - - + + Drop to send tracks + + + + + Show Mostrar - - + + Hide Ocultar @@ -2616,84 +2621,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Play - - - + + + Add to &Queue Adicionar à &lista - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Continuar a reprodução após esta &Faixa - + Stop Playback after this &Track Parar a reprodução após esta &Faixa - - + + &Love &Favorita - - - + + + &Go to "%1" &Ir para "%1" - - - + + + Go to "%1" Ir para "%1" - + &Copy Track Link &Copiar Link da Faixa - + &Remove Items - + &Remove Item - + Copy Album &Link Copiar &Link do Álbum - + Copy Artist &Link Copiar &Link do Artista - + Un-&Love &Desfavoritar - + Properties... Propriedades... diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index f59f838750..c673f08334 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1805,19 +1805,24 @@ connect and stream from you? Топ 10 - + All available tracks Доступные песни - - + + Drop to send tracks + + + + + Show Показать - - + + Hide Спрятать @@ -2625,84 +2630,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play &Играть - - - + + + Add to &Queue Добавить в &Очередь - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track Продолжить воспроизведение после этого &Песни - + Stop Playback after this &Track Остановить Воспроизведение После Этой &Песни - - + + &Love &Любимая - - - + + + &Go to "%1" &Перейти к "%1" - - - + + + Go to "%1" Перейти к "%1" - + &Copy Track Link &Скопировать Ссылку на Песню - + &Remove Items - + &Remove Item - + Copy Album &Link Копировать &Ссылку Альбома - + Copy Artist &Link Копировать &Ссылку Исполнителя - + Un-&Love &НеЛюбимая - + Properties... Свойства... diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 931f76b8e0..0a271b19e0 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1801,19 +1801,24 @@ och radiostationer baserat på din personliga profil Topp 10 - + All available tracks Alla tillgängliga spår - - + + Drop to send tracks + + + + + Show Visa - - + + Hide Göm @@ -2620,84 +2625,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play Spela &upp - - - + + + Add to &Queue Lägg till i &kö - + Add to &Playlist Lägg till i S&pellistan - + + Send to &Friend + + + + Continue Playback after this &Track Fortsätt uppspelningen efter detta &spåret - + Stop Playback after this &Track Avsluta uppspelningen efter detta &spåret - - + + &Love &Kärlek - - - + + + &Go to "%1" &Gå till "%1" - - - + + + Go to "%1" Gå till "%1" - + &Copy Track Link &Kopiera Låtlänk - + &Remove Items &Ta bort - + &Remove Item &Ta bort - + Copy Album &Link Kopiera album-&länk - + Copy Artist &Link Kopiera artist-&länk - + Un-&Love Av-&älska - + Properties... Egenskaper... diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 715ab68ecb..b860d31cbd 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1797,19 +1797,24 @@ connect and stream from you? - + All available tracks - - + + Drop to send tracks + + + + + Show - - + + Hide @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play - - - + + + Add to &Queue - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track - + Stop Playback after this &Track - - + + &Love - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love - + Properties... diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 82b9fb3f2c..3c57e7f1df 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1800,19 +1800,24 @@ connect and stream from you? Top 10 - + All available tracks 所有可用的歌曲 - - + + Drop to send tracks + + + + + Show 显示 - - + + Hide 隐藏 @@ -2617,84 +2622,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play 播放 - - - + + + Add to &Queue 添加到队列 - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track 此歌曲后继续播放 - + Stop Playback after this &Track 此歌曲后停止播放 - - + + &Love 喜欢 - - - + + + &Go to "%1" 转到 "%1" - - - + + + Go to "%1" 转到 "%1" - + &Copy Track Link 复制歌曲链接 - + &Remove Items - + &Remove Item - + Copy Album &Link 复制专辑链接 - + Copy Artist &Link 复制艺术家链接 - + Un-&Love 不喜欢 - + Properties... 属性... diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 702fb09cea..366ca22737 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1797,19 +1797,24 @@ connect and stream from you? 前10名 - + All available tracks - - + + Drop to send tracks + + + + + Show 顯示 - - + + Hide 隱藏 @@ -2611,84 +2616,89 @@ username@jabber.org Tomahawk::ContextMenu - + &Play 播放 - - - + + + Add to &Queue 添加至佇列 - + Add to &Playlist - + + Send to &Friend + + + + Continue Playback after this &Track - + Stop Playback after this &Track - - + + &Love - - - + + + &Go to "%1" - - - + + + Go to "%1" - + &Copy Track Link - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love - + Properties... From c3a1ee84a1daf1cb0edd299a5e0fbeeb4cdaa111 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 28 May 2013 14:55:45 +0200 Subject: [PATCH 162/565] Even more race precautions * It did not break yet, but I could not sleep without this commit. --- src/libtomahawk/Source.cpp | 5 +++++ src/libtomahawk/Source.h | 3 +++ src/libtomahawk/network/ControlConnection.cpp | 18 ++++++++++++------ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 98d6c32904..8295748d3c 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -477,6 +477,11 @@ Source::playlistInterface() return m_playlistInterface; } +QSharedPointer Source::acquireLock() +{ + return QSharedPointer( new QMutexLocker( &m_mutex ) ); +} + void Source::onPlaybackStarted( const Tomahawk::track_ptr& track, unsigned int duration ) diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 7273bc6b9a..6db8affcdf 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -102,6 +102,8 @@ friend class ::MusicScanner; Tomahawk::playlistinterface_ptr playlistInterface(); + QSharedPointer acquireLock(); + signals: void syncedWithDatabase(); void synced(); @@ -175,6 +177,7 @@ private slots: QString m_lastCmdGuid; mutable QMutex m_cmdMutex; QMutex m_setControlConnectionMutex; + QMutex m_mutex; Tomahawk::playlistinterface_ptr m_playlistInterface; }; diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 880626edcb..69014c6318 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -100,6 +100,7 @@ ControlConnection::setup() // setup source and remote collection for this peer m_source = SourceList::instance()->get( id(), friendlyName, true ); + QSharedPointer locker = m_source->acquireLock(); if ( m_source->setControlConnection( this ) ) { // We are the new ControlConnection for this source @@ -129,13 +130,18 @@ ControlConnection::setup() void ControlConnection::registerSource() { - qDebug() << Q_FUNC_INFO << m_source->id(); - Source* source = (Source*) sender(); - Q_UNUSED( source ) - Q_ASSERT( source == m_source.data() ); + QSharedPointer locker = m_source->acquireLock(); + // Only continue if we are still the ControlConnection associated with this source. + if ( m_source->controlConnection() == this ) + { + qDebug() << Q_FUNC_INFO << m_source->id(); + Source* source = (Source*) sender(); + Q_UNUSED( source ) + Q_ASSERT( source == m_source.data() ); - m_registered = true; - setupDbSyncConnection(); + m_registered = true; + setupDbSyncConnection(); + } } From 91feefc4331421917c3daa234f60b289935a660e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 28 May 2013 15:19:19 +0200 Subject: [PATCH 163/565] Fix compilation --- src/libtomahawk/Source.cpp | 3 ++- src/libtomahawk/Source.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 8295748d3c..ab397a7695 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -477,7 +477,8 @@ Source::playlistInterface() return m_playlistInterface; } -QSharedPointer Source::acquireLock() +QSharedPointer +Source::acquireLock() { return QSharedPointer( new QMutexLocker( &m_mutex ) ); } diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 6db8affcdf..aaa6faf2f6 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -102,7 +102,7 @@ friend class ::MusicScanner; Tomahawk::playlistinterface_ptr playlistInterface(); - QSharedPointer acquireLock(); + QSharedPointer acquireLock(); signals: void syncedWithDatabase(); From 1637b325d788f5d20cd98999e94f8bff99e76074 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Tue, 28 May 2013 17:56:06 +0200 Subject: [PATCH 164/565] Add "Mark as Listened" to Inbox. --- src/libtomahawk/ContextMenu.cpp | 3 +++ src/libtomahawk/ContextMenu.h | 3 ++- src/libtomahawk/Track.cpp | 7 ++++++- src/libtomahawk/Track.h | 1 + src/libtomahawk/playlist/InboxModel.cpp | 14 +++++++++++++ src/libtomahawk/playlist/InboxModel.h | 2 ++ src/libtomahawk/playlist/InboxView.cpp | 27 +++++++++++++++++++++++++ src/libtomahawk/playlist/InboxView.h | 2 ++ src/libtomahawk/playlist/TrackView.cpp | 5 ++++- src/libtomahawk/playlist/TrackView.h | 2 +- 10 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/ContextMenu.cpp b/src/libtomahawk/ContextMenu.cpp index 277723b8c4..6945b878e4 100644 --- a/src/libtomahawk/ContextMenu.cpp +++ b/src/libtomahawk/ContextMenu.cpp @@ -214,6 +214,9 @@ ContextMenu::setQueries( const QList& queries ) addSeparator(); + if ( m_supportedActions & ActionMarkListened ) + m_sigmap->setMapping( addAction( tr( "Mark as &Listened" ) ), ActionMarkListened ); + if ( m_supportedActions & ActionDelete ) m_sigmap->setMapping( addAction( queries.count() > 1 ? tr( "&Remove Items" ) : tr( "&Remove Item" ) ), ActionDelete ); diff --git a/src/libtomahawk/ContextMenu.h b/src/libtomahawk/ContextMenu.h index 5b60324d67..2e523eafe2 100644 --- a/src/libtomahawk/ContextMenu.h +++ b/src/libtomahawk/ContextMenu.h @@ -49,7 +49,8 @@ Q_OBJECT ActionAlbumPage = 67, ActionEditMetadata = 128, ActionPlaylist = 256, - ActionSend = 512 + ActionSend = 512, + ActionMarkListened = 1024 }; explicit ContextMenu( QWidget* parent = 0 ); diff --git a/src/libtomahawk/Track.cpp b/src/libtomahawk/Track.cpp index 96d500020d..927baca323 100644 --- a/src/libtomahawk/Track.cpp +++ b/src/libtomahawk/Track.cpp @@ -172,7 +172,7 @@ Track::startPlaying() DatabaseCommand_LogPlayback::Started ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); - + markAsListened(); } @@ -183,7 +183,12 @@ Track::finishPlaying( int timeElapsed ) DatabaseCommand_LogPlayback::Finished, timeElapsed ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); +} + +void +Track::markAsListened() +{ bool isUnlistened = false; foreach ( Tomahawk::SocialAction action, allSocialActions() ) { diff --git a/src/libtomahawk/Track.h b/src/libtomahawk/Track.h index abc63119cf..3d48093342 100644 --- a/src/libtomahawk/Track.h +++ b/src/libtomahawk/Track.h @@ -112,6 +112,7 @@ friend class Pipeline; void startPlaying(); void finishPlaying( int timeElapsed ); + void markAsListened(); signals: void coverChanged(); diff --git a/src/libtomahawk/playlist/InboxModel.cpp b/src/libtomahawk/playlist/InboxModel.cpp index 79aa0ed5b8..4deec10307 100644 --- a/src/libtomahawk/playlist/InboxModel.cpp +++ b/src/libtomahawk/playlist/InboxModel.cpp @@ -171,6 +171,20 @@ InboxModel::showNotification( InboxJobItem::Side side, } +void +InboxModel::markAsListened( const QModelIndexList& indexes ) +{ + foreach ( QModelIndex index, indexes ) + { + PlayableItem* item = itemFromIndex( index ); + if ( item && !item->query().isNull() ) + { + item->query()->queryTrack()->markAsListened(); + } + } +} + + void InboxModel::loadTracks() { diff --git a/src/libtomahawk/playlist/InboxModel.h b/src/libtomahawk/playlist/InboxModel.h index f593cd8105..7e3decd82c 100644 --- a/src/libtomahawk/playlist/InboxModel.h +++ b/src/libtomahawk/playlist/InboxModel.h @@ -53,6 +53,8 @@ public slots: const QString& dbid, const Tomahawk::trackdata_ptr& track ); + virtual void markAsListened( const QModelIndexList& indexes ); + private slots: void loadTracks(); diff --git a/src/libtomahawk/playlist/InboxView.cpp b/src/libtomahawk/playlist/InboxView.cpp index 83ea65a705..090c65df59 100644 --- a/src/libtomahawk/playlist/InboxView.cpp +++ b/src/libtomahawk/playlist/InboxView.cpp @@ -20,6 +20,8 @@ #include "InboxView.h" #include "InboxModel.h" #include "PlayableProxyModel.h" +#include "ContextMenu.h" +#include "utils/Logger.h" InboxView::InboxView(QWidget *parent) : TrackView(parent) @@ -36,3 +38,28 @@ InboxView::deleteSelectedItems() proxyModel()->removeIndexes( selectedIndexes() ); } } + + +void +InboxView::onMenuTriggered( int action ) +{ + if ( action == Tomahawk::ContextMenu::ActionMarkListened ) + { + tDebug() << Q_FUNC_INFO << "Mark as Listened"; + InboxModel* inboxModel = qobject_cast< InboxModel* >( model() ); + if ( inboxModel != 0 ) + { + QModelIndexList sourceIndexes; + foreach ( const QModelIndex& index, selectedIndexes() ) + { + if ( index.column() ) + continue; + + sourceIndexes << proxyModel()->mapToSource( index ); + } + inboxModel->markAsListened( sourceIndexes ); + } + } + else + TrackView::onMenuTriggered( action ); +} diff --git a/src/libtomahawk/playlist/InboxView.h b/src/libtomahawk/playlist/InboxView.h index 298422b693..88052b8169 100644 --- a/src/libtomahawk/playlist/InboxView.h +++ b/src/libtomahawk/playlist/InboxView.h @@ -33,6 +33,8 @@ public slots: * Reimplemented in order to ignore PlayableModel::isReadOnly() */ virtual void deleteSelectedItems(); + + virtual void onMenuTriggered( int action ); }; #endif // INBOXVIEW_H diff --git a/src/libtomahawk/playlist/TrackView.cpp b/src/libtomahawk/playlist/TrackView.cpp index 9dd24300c5..aedae4d894 100644 --- a/src/libtomahawk/playlist/TrackView.cpp +++ b/src/libtomahawk/playlist/TrackView.cpp @@ -34,6 +34,7 @@ #include "utils/Closure.h" #include "utils/AnimatedSpinner.h" #include "utils/Logger.h" +#include "InboxModel.h" #include @@ -45,7 +46,6 @@ using namespace Tomahawk; - TrackView::TrackView( QWidget* parent ) : QTreeView( parent ) , m_model( 0 ) @@ -651,6 +651,9 @@ TrackView::onCustomContextMenu( const QPoint& pos ) if ( model() && !model()->isReadOnly() ) m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionDelete ); + if ( model() && qobject_cast< InboxModel* >( model() ) ) + m_contextMenu->setSupportedActions( m_contextMenu->supportedActions() | ContextMenu::ActionMarkListened + | ContextMenu::ActionDelete ); QList queries; foreach ( const QModelIndex& index, selectedIndexes() ) diff --git a/src/libtomahawk/playlist/TrackView.h b/src/libtomahawk/playlist/TrackView.h index a793f6d163..947da07fbb 100644 --- a/src/libtomahawk/playlist/TrackView.h +++ b/src/libtomahawk/playlist/TrackView.h @@ -93,7 +93,7 @@ public slots: virtual void deleteSelectedItems(); void playItem(); - void onMenuTriggered( int action ); + virtual void onMenuTriggered( int action ); void onViewChanged(); void onScrollTimeout(); From 72b326a3be6741c9e998e64e7a247ead8671b1fb Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 29 May 2013 00:10:05 +0200 Subject: [PATCH 165/565] We are handling a set, so we need no duplicate detection. --- src/libtomahawk/network/ControlConnection.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 69014c6318..10596fbe45 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -290,11 +290,7 @@ void ControlConnection::addPeerInfo( const peerinfo_ptr& peerInfo ) { peerInfo->setControlConnection( this ); - // Check if we already have added this peerInfo - if ( !m_peerInfos.contains( peerInfo ) ) - { - m_peerInfos.insert( peerInfo ); - } + m_peerInfos.insert( peerInfo ); } From a87b0268d48733ada480e61479b6bca37b967c6c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 29 May 2013 00:26:17 +0200 Subject: [PATCH 166/565] Remove usage of QSharedPointer around SipInfo --- src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp index b28e3bc40e..f5c8f925e3 100644 --- a/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp +++ b/src/accounts/xmpp/sip/TomahawkXmppMessageFactory.cpp @@ -137,7 +137,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter // Get a copy of the list, so that we can modify it here. QList sipInfos = QList( sipMessage->sipInfos() ); - QSharedPointer lastInfo = QSharedPointer(); + SipInfo lastInfo; foreach ( SipInfo info, sipInfos ) { if ( info.isVisible() ) @@ -146,7 +146,7 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter if ( ( Servent::isValidExternalIP( ha ) && ha.protocol() == QAbstractSocket::IPv4Protocol ) || ( ha.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol ) || ( ha.isNull() && !info.host().isEmpty() ) ) { // For comapability reasons, this shall be put as the last candidate (this is the IP/host that would have been sent in previous versions) - lastInfo = QSharedPointer( new SipInfo( info ) ); + lastInfo = info; sipInfos.removeOne( info ); break; } @@ -163,11 +163,11 @@ void TomahawkXmppMessageFactory::serialize(Payload *extension, QXmlStreamWriter serializeSipInfo( info, writer ); } - if ( !lastInfo.isNull() ) + if ( lastInfo.isValid() ) { - Q_ASSERT( lastInfo->isVisible() ); - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Using " << lastInfo->host() << ":" << lastInfo->port() << " as the host which all older clients will only detect"; - serializeSipInfo( *lastInfo, writer ); + Q_ASSERT( lastInfo.isVisible() ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Using " << lastInfo.host() << ":" << lastInfo.port() << " as the host which all older clients will only detect"; + serializeSipInfo( lastInfo, writer ); } // From af0504fe41f368674823cc7340239c71e9b98c61 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 29 May 2013 00:26:51 +0200 Subject: [PATCH 167/565] Bump Version to 0.7.999 --- CMakeLists.txt | 2 +- src/libtomahawk/network/ConnectionManager.cpp | 6 +++--- src/libtomahawk/network/Servent.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f74590f5a7..c71cbe0cf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ ENDIF() SET( TOMAHAWK_VERSION_MAJOR 0 ) SET( TOMAHAWK_VERSION_MINOR 7 ) -SET( TOMAHAWK_VERSION_PATCH 99 ) +SET( TOMAHAWK_VERSION_PATCH 999 ) #SET( TOMAHAWK_VERSION_RC 0 ) SET( TOMAHAWK_TRANSLATION_LANGUAGES ar bg bn_IN ca cs de en el es fi fr hi_IN hu gl it ja lt pl pt_BR ru sv tr zh_CN zh_TW ) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index b13437c562..234e41869e 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -45,11 +45,11 @@ void ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo ) { m_mutex.lock(); - // Respect different behaviour before 0.7.99 + // Respect different behaviour before 0.7.999 peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Trying to connect to client with version " << peerInfo->versionString().split(' ').last() << TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ); - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ) < 0) + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.999" ) < 0) { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Using old-style (<0.7.99) connection order."; + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Using old-style (<0.7.999) connection order."; SipInfo we = Servent::getSipInfoForOldVersions( Servent::instance()->getLocalSipInfos( QString( "default" ), QString( "default" ) ) ); SipInfo they = peerInfo->sipInfos().first(); if ( they.isVisible() ) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 69c97bc060..3284e88e1c 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -487,7 +487,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) // The offer should be removed after some time or we will build up a heap of unused PeerInfos registerLazyOffer( key, peerInfo, nodeid, sipInfos.length() * 1.5 * CONNECT_TIMEOUT ); // SipInfos were single-value before 0.7.999 - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ) < 0) + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.999" ) < 0) { SipInfo info = getSipInfoForOldVersions( sipInfos ); peerInfo->sendLocalSipInfos( QList() << info ); From 0c1a10ec6a6e171489305febc121cfd83ee42c07 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 29 May 2013 00:27:09 +0200 Subject: [PATCH 168/565] Copy-Constructor is automatically called --- src/libtomahawk/sip/PeerInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 9daae2b7ca..047e46830f 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -279,7 +279,7 @@ PeerInfo::status() const void PeerInfo::setSipInfos( const QList& sipInfos ) { - m_sipInfos = QList( sipInfos ); + m_sipInfos = sipInfos; tLog() << "id:" << id() << "info changed" << sipInfos; emit sipInfoChanged(); From ba23f701f8dccf8f01960a18c4199f29643b6ab2 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 29 May 2013 00:27:21 +0200 Subject: [PATCH 169/565] Make private things private --- src/libtomahawk/network/Servent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 67a2a89e59..d18300dd55 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -146,7 +146,6 @@ public slots: public slots: void setExternalAddress( QHostAddress ha, unsigned int port ); - void socketError( QAbstractSocket::SocketError e ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key ); void registerStreamConnection( StreamConnection* ); @@ -156,8 +155,9 @@ public slots: void triggerDBSync(); private slots: - void readyRead(); void deleteLazyOffer( const QString& key ); + void readyRead(); + void socketError( QAbstractSocket::SocketError e ); Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); From 13f8ad5daad6aaedc2409e15b07288382e7a24cb Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Wed, 29 May 2013 02:16:54 +0200 Subject: [PATCH 170/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 63 +++++++++++++++++++----------------- lang/tomahawk_bg.ts | 41 ++++++++++++----------- lang/tomahawk_bn_IN.ts | 41 ++++++++++++----------- lang/tomahawk_ca.ts | 41 ++++++++++++----------- lang/tomahawk_ca@valencia.ts | 41 ++++++++++++----------- lang/tomahawk_cs.ts | 41 ++++++++++++----------- lang/tomahawk_da.ts | 41 ++++++++++++----------- lang/tomahawk_de.ts | 41 ++++++++++++----------- lang/tomahawk_el.ts | 58 ++++++++++++++++++--------------- lang/tomahawk_en.ts | 41 ++++++++++++----------- lang/tomahawk_es.ts | 41 ++++++++++++----------- lang/tomahawk_fi.ts | 41 ++++++++++++----------- lang/tomahawk_fr.ts | 41 ++++++++++++----------- lang/tomahawk_gl.ts | 41 ++++++++++++----------- lang/tomahawk_hi_IN.ts | 41 ++++++++++++----------- lang/tomahawk_hu.ts | 41 ++++++++++++----------- lang/tomahawk_id.ts | 41 ++++++++++++----------- lang/tomahawk_it.ts | 41 ++++++++++++----------- lang/tomahawk_ja.ts | 41 ++++++++++++----------- lang/tomahawk_lt.ts | 41 ++++++++++++----------- lang/tomahawk_pl.ts | 41 ++++++++++++----------- lang/tomahawk_pt_BR.ts | 41 ++++++++++++----------- lang/tomahawk_ru.ts | 41 ++++++++++++----------- lang/tomahawk_sv.ts | 41 ++++++++++++----------- lang/tomahawk_tr.ts | 41 ++++++++++++----------- lang/tomahawk_zh_CN.ts | 41 ++++++++++++----------- lang/tomahawk_zh_TW.ts | 41 ++++++++++++----------- 27 files changed, 641 insertions(+), 505 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 5ad66462da..e29a0ae108 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -689,7 +689,7 @@ connect and stream from you? Sent %1 by %2 to %3. - + ارسلت %1 للفنان %2 إلى %3. @@ -720,7 +720,7 @@ connect and stream from you? Script Resolver Warning: API call %1 returned data synchronously. - + تحذير المحلل النصي: إستدعاء API %1 إرجاع البيانات بشكل متزامن. @@ -1405,12 +1405,12 @@ connect and stream from you? No query - + لا استعلام Parameter count mismatch - + عدم تطابق عدد المعلمات @@ -1479,17 +1479,17 @@ connect and stream from you? SSL Error - + خطأ SSL You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + لقد طلبت من توماهوك الإتصال بشكل أمن إلى <b>%1</b> ولكن لا يمكننا التأكد من أن اتصالك آمن: <br><br><b>%2</b></br></br> هل تريد أن تثق بهذا الإتصال؟ Trust certificate - + شهادة الثقة @@ -1807,7 +1807,7 @@ connect and stream from you? Drop to send tracks - + أنزل لإرسال الأغاني @@ -2631,8 +2631,8 @@ username@jabber.org - - + + Add to &Queue أضف إلى &قائمة الانتظار @@ -2644,7 +2644,7 @@ username@jabber.org Send to &Friend - + أرسل &لصديق @@ -2658,21 +2658,21 @@ username@jabber.org - + &Love &أحب - - + + &Go to "%1" ا&ذهب إلى "%1" - + Go to "%1" اذهب إلى "%1" @@ -2683,26 +2683,31 @@ username@jabber.org + Mark as &Listened + ضع علامة على أنها &سمعت + + + &Remove Items &إزالة البنود - + &Remove Item &إزالة البند - + Copy Album &Link نسخ &رابط الالبوم - + Copy Artist &Link نسخ &رابط قائمة الأغاني - + Un-&Love لا &أحب @@ -3250,13 +3255,13 @@ Try tweaking the filters for a new set of songs to play. %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 أرسل لك %2%4 %3. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 أرسل لك "%2" للفنان %3. @@ -3467,42 +3472,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and و - + You أنت - + you أنت - + and و - + %n other(s) %n آخر%n آخر%n آخرين%n آخرين%n آخرين%n آخرين - + %n people %n شخصشخصشخصين%n أشخاص%n شخص%n شخص - + loved this track أحب هذه الأغنية - + sent you this track %1 %1 أرسل لك هذه الأغنية diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index a2330317c0..7231a3631e 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -2645,8 +2645,8 @@ username@jabber.org - - + + Add to &Queue Добави към &опашката @@ -2672,21 +2672,21 @@ username@jabber.org - + &Love &Харесай - - + + &Go to "%1" &Иди на "%1" - + Go to "%1" Иди на "%1" @@ -2697,26 +2697,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Копирай адреса на албума - + Copy Artist &Link Копирай адреса на изпълнителя - + Un-&Love Не-&харесай @@ -3481,42 +3486,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index c5a69a586e..12ca3717af 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index beb6173bb3..11b678b6cc 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -2632,8 +2632,8 @@ nomdusuari@jabber.org - - + + Add to &Queue &Afegeix a la Cua @@ -2659,21 +2659,21 @@ nomdusuari@jabber.org - + &Love &M'encanta - - + + &Go to "%1" &Ves a «%1» - + Go to "%1" Vés a «%1» @@ -2684,26 +2684,31 @@ nomdusuari@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Copia l'enllaç de &l'àlbum - + Copy Artist &Link Copia l'enllça de &l'artista - + Un-&Love &Treu de les preferides @@ -3468,42 +3473,42 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 1d7df1b0cb..3e5852dbf7 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -2632,8 +2632,8 @@ nomdusuari@jabber.org - - + + Add to &Queue &Afig a la Cua @@ -2659,21 +2659,21 @@ nomdusuari@jabber.org - + &Love &M'encanta - - + + &Go to "%1" &Ves a «%1» - + Go to "%1" Vés a «%1» @@ -2684,26 +2684,31 @@ nomdusuari@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Copia l'enllaç de &l'àlbum - + Copy Artist &Link Copia l'enllça de &l'artista - + Un-&Love &Treu de les preferides @@ -3468,42 +3473,42 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index ec6ddd1ecc..9b0041d0a1 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -2630,8 +2630,8 @@ username@jabber.org - - + + Add to &Queue Přidat do řa&dy @@ -2657,21 +2657,21 @@ username@jabber.org - + &Love &Oblíbená píseň - - + + &Go to "%1" &Jít na "%1" - + Go to "%1" Jít na "%1" @@ -2682,26 +2682,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items &Odstranit položky - + &Remove Item Odstranit &položku - + Copy Album &Link Kopírovat odkaz pro toto &album - + Copy Artist &Link Kopírovat odkaz pro tohoto &umělce - + Un-&Love Zrušit zařazení mezi o&blíbené písně @@ -3467,42 +3472,42 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Track - + and a - + You Vy - + you vy - + and a - + %n other(s) %n dalšímu%n dalším%n dalším - + %n people %n člověku%n lidem%n lidem - + loved this track se tato skladba líbí - + sent you this track %1 vám poslal tuto skladbu %1 diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 4c87fd10a4..c5be0be00b 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -2624,8 +2624,8 @@ username@jabber.org - - + + Add to &Queue Tilføj til &Kø @@ -2651,21 +2651,21 @@ username@jabber.org - + &Love &Elsk - - + + &Go to "%1" - + Go to "%1" @@ -2676,26 +2676,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3456,42 +3461,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 0ef945ebd8..722008cbb5 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -2625,8 +2625,8 @@ username@jabber.org - - + + Add to &Queue In &Warteschlange einreihen @@ -2652,21 +2652,21 @@ username@jabber.org - + &Love &Lieblingslied - - + + &Go to "%1" &Gehe zu "%1" - + Go to "%1" Gehe zu "%1" @@ -2677,26 +2677,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items &Elemente entfernen - + &Remove Item &Element entfernen - + Copy Album &Link Album-&Link kopieren - + Copy Artist &Link Künstler-&Link kopieren - + Un-&Love Kein &Lieblingslied @@ -3461,42 +3466,42 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Track - + and und - + You Du - + you du - + and und - + %n other(s) %n andere%n andere - + %n people %n Personen%n Personen - + loved this track gefällt dieses Lied - + sent you this track %1 dieser track wurde gesendet %1 diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 21bd14235e..0bf2d09b36 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -688,7 +688,7 @@ connect and stream from you? Sent %1 by %2 to %3. - + %1 αποστελθηκε σε εσενα %2 απο %3. @@ -719,7 +719,7 @@ connect and stream from you? Script Resolver Warning: API call %1 returned data synchronously. - + Σριπτ προειδοποίηση αναλυτή: API κλήση% 1 επέστρεψε δεδομένα συγχρονισμένα. @@ -1403,12 +1403,12 @@ connect and stream from you? No query - + Δεν υπαρχει ερώτημα Parameter count mismatch - + Παράμετρος αναντιστοιχία μετράει @@ -1806,7 +1806,7 @@ connect and stream from you? Drop to send tracks - + Πτώση για αποστολή κομμάτιων @@ -2631,8 +2631,8 @@ username@jabber.org - - + + Add to &Queue Προσθήκη στην &Σειρά @@ -2644,7 +2644,7 @@ username@jabber.org Send to &Friend - + Αποστολή σε εναν φίλο @@ -2658,21 +2658,21 @@ username@jabber.org - + &Love &Αγάπησε - - + + &Go to "%1" &Πήγαινε στο "%1" - + Go to "%1" Πήγαινε στο "%1" @@ -2683,26 +2683,31 @@ username@jabber.org + Mark as &Listened + Επισήμανση ως &Ακρόαση + + + &Remove Items &Διαγραφή Αντικειμένων - + &Remove Item &Διαγραφή Αντικειμένου - + Copy Album &Link Αντιγραφη Λινκ του Αλμπουμ - + Copy Artist &Link Αντιγραφη του Λινκ του Καλλιτεχνη - + Un-&Love &Μίσησε @@ -3250,13 +3255,14 @@ Try tweaking the filters for a new set of songs to play. %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 αποστελθηκε σε εσενα %2 απο %3. +%2%4 %3. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 αποστελθηκε σε εσενα %2 απο %3. @@ -3467,42 +3473,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and και - + You Εσυ - + you εσυ - + and και - + %n other(s) %n άλλοι%n άλλοι - + %n people %n άτομα%n άτομα - + loved this track Αγαπημένο τραγούδι - + sent you this track %1 αυτο το τραγουδι σταλθηκε %1 diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 28711d350a..82969e829d 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -2633,8 +2633,8 @@ username@jabber.org - - + + Add to &Queue Add to &Queue @@ -2660,21 +2660,21 @@ username@jabber.org - + &Love &Love - - + + &Go to "%1" &Go to "%1" - + Go to "%1" Go to "%1" @@ -2685,26 +2685,31 @@ username@jabber.org + Mark as &Listened + Mark as &Listened + + + &Remove Items &Remove Items - + &Remove Item &Remove Item - + Copy Album &Link Copy Album &Link - + Copy Artist &Link Copy Artist &Link - + Un-&Love Un-&Love @@ -3470,42 +3475,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and and - + You You - + you you - + and and - + %n other(s) %n other%n others - + %n people %n people%n people - + loved this track loved this track - + sent you this track %1 sent you this track %1 diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index a36e072325..654cc2c98e 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -2633,8 +2633,8 @@ usuario@jabber.org - - + + Add to &Queue Añadir a la &cola @@ -2660,21 +2660,21 @@ usuario@jabber.org - + &Love &Favorito - - + + &Go to "%1" &Ir a "%1" - + Go to "%1" &Ir a "%1" @@ -2685,26 +2685,31 @@ usuario@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Copiar en&lace del álbum - + Copy Artist &Link Copiar en&lace del artista - + Un-&Love Quitar de &favoritos @@ -3469,42 +3474,42 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 513ebdbd49..1925ef743d 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -2636,8 +2636,8 @@ käyttäjätunnus@jabber.org - - + + Add to &Queue &Lisää jonoon @@ -2663,21 +2663,21 @@ käyttäjätunnus@jabber.org - + &Love &Tykkää - - + + &Go to "%1" Sii&rry sivulle ”%1” - + Go to "%1" Siirry sivulle ”%1” @@ -2688,26 +2688,31 @@ käyttäjätunnus@jabber.org + Mark as &Listened + + + + &Remove Items &Poista kohteet - + &Remove Item &Poista kohde - + Copy Album &Link &Kopioi albumin linkki - + Copy Artist &Link &Kopioi artistin linkki - + Un-&Love Lakkaa &tykkäämästä @@ -3472,42 +3477,42 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::Track - + and ja - + You Sinä - + you sinä - + and ja - + %n other(s) %n muu%n muuta - + %n people %n ihminen%n ihmistä - + loved this track tykkäsi tästä kappaleesta - + sent you this track %1 lähetti sinulle kappaleen %1 diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index c788e79428..fd38d6f1e7 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -2630,8 +2630,8 @@ utilisateur@jabber.org - - + + Add to &Queue &Ajouter à la file d'attente @@ -2657,21 +2657,21 @@ utilisateur@jabber.org - + &Love &Favori - - + + &Go to "%1" Alle&r à "%1" - + Go to "%1" Aller à "%1" @@ -2682,26 +2682,31 @@ utilisateur@jabber.org + Mark as &Listened + + + + &Remove Items &Supprimer les éléments - + &Remove Item &Supprimer l'élément - + Copy Album &Link Copier le &lien de l'album - + Copy Artist &Link Copier le &lien de l'artiste - + Un-&Love &Supprimer des Favoris @@ -3466,42 +3471,42 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Track - + and et - + You Votre - + you votre - + and et - + %n other(s) %n autre%n autres - + %n people %n personne%n personnes - + loved this track a aimé cette piste - + sent you this track %1 vous a envoyé cette piste %1 diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 0fb69e7208..57ebfc517f 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -2632,8 +2632,8 @@ nomedeusuario@jabber.org - - + + Add to &Queue Engadir á &ringleira @@ -2659,21 +2659,21 @@ nomedeusuario@jabber.org - + &Love &Gústame - - + + &Go to "%1" &Ir a «%1» - + Go to "%1" Ir a «%1» @@ -2684,26 +2684,31 @@ nomedeusuario@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Copiar a &ligazón do álbum - + Copy Artist &Link Copiar a &ligazón do artista - + Un-&Love Xa non me &gusta @@ -3468,42 +3473,42 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index de55d081e0..4b372f7fb0 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 38e58a70cc..189e3d5c5f 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 03051a9ac7..d63c5383c7 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index b845ec6522..1ccf2a702d 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue Aggiungi alla &coda @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love &Love - - + + &Go to "%1" &Vai a "%1" - + Go to "%1" Vai a "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items &Cancella elementi - + &Remove Item &Cancella elemento - + Copy Album &Link Copia - + Copy Artist &Link Copia il &link dell'artista - + Un-&Love Un-&love @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and e - + You Tu - + you tu - + and e - + %n other(s) U%n altro%n altri - + %n people %n persona%n persone - + loved this track amato questa traccia - + sent you this track %1 ti ha spedito questa traccia %1 diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 7d154fc4bb..583268d25e 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -2630,8 +2630,8 @@ username@jabber.org - - + + Add to &Queue キューに追加 @@ -2657,21 +2657,21 @@ username@jabber.org - + &Love &Love - - + + &Go to "%1" %1に移動 - + Go to "%1" %1に移動 @@ -2682,26 +2682,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link アルバムリンクをコピー - + Copy Artist &Link アーティストリンクをコピー - + Un-&Love Loveじゃないトラック @@ -3466,42 +3471,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 72c54e35e7..71a0b3633d 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue Pridėti į &eilę @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love My&liu - - + + &Go to "%1" - + Go to "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Kopijuoti albumo &nuorodą - + Copy Artist &Link Kopijuoti atlikėjo &nuorodą - + Un-&Love Nemy&liu @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 3f87534c3d..45b013d068 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -2627,8 +2627,8 @@ username@jabber.org - - + + Add to &Queue Dodaj do &kolejki @@ -2654,21 +2654,21 @@ username@jabber.org - + &Love &Lubię - - + + &Go to "%1" - + Go to "%1" @@ -2679,26 +2679,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Kopiuj &link do albumu - + Copy Artist &Link Kopiuj &link do wykonawcy - + Un-&Love Przestań &lubić @@ -3463,42 +3468,42 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 5dce804518..861bca5b33 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -2627,8 +2627,8 @@ username@jabber.org - - + + Add to &Queue Adicionar à &lista @@ -2654,21 +2654,21 @@ username@jabber.org - + &Love &Favorita - - + + &Go to "%1" &Ir para "%1" - + Go to "%1" Ir para "%1" @@ -2679,26 +2679,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Copiar &Link do Álbum - + Copy Artist &Link Copiar &Link do Artista - + Un-&Love &Desfavoritar @@ -3463,42 +3468,42 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index c673f08334..afae3d4e4b 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -2636,8 +2636,8 @@ username@jabber.org - - + + Add to &Queue Добавить в &Очередь @@ -2663,21 +2663,21 @@ username@jabber.org - + &Love &Любимая - - + + &Go to "%1" &Перейти к "%1" - + Go to "%1" Перейти к "%1" @@ -2688,26 +2688,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link Копировать &Ссылку Альбома - + Copy Artist &Link Копировать &Ссылку Исполнителя - + Un-&Love &НеЛюбимая @@ -3470,42 +3475,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 0a271b19e0..8b105b78e7 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -2631,8 +2631,8 @@ username@jabber.org - - + + Add to &Queue Lägg till i &kö @@ -2658,21 +2658,21 @@ username@jabber.org - + &Love &Kärlek - - + + &Go to "%1" &Gå till "%1" - + Go to "%1" Gå till "%1" @@ -2683,26 +2683,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items &Ta bort - + &Remove Item &Ta bort - + Copy Album &Link Kopiera album-&länk - + Copy Artist &Link Kopiera artist-&länk - + Un-&Love Av-&älska @@ -3467,42 +3472,42 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Track - + and och - + You Du - + you du - + and och - + %n other(s) %n annan%n andra - + %n people %n person%n personer - + loved this track älskade detta spåret - + sent you this track %1 skickade spåret '%1' till dig diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index b860d31cbd..6b0b9919b9 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 3c57e7f1df..83fbff4fce 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -2628,8 +2628,8 @@ username@jabber.org - - + + Add to &Queue 添加到队列 @@ -2655,21 +2655,21 @@ username@jabber.org - + &Love 喜欢 - - + + &Go to "%1" 转到 "%1" - + Go to "%1" 转到 "%1" @@ -2680,26 +2680,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link 复制专辑链接 - + Copy Artist &Link 复制艺术家链接 - + Un-&Love 不喜欢 @@ -3464,42 +3469,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 366ca22737..3638623073 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue 添加至佇列 @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2674,26 +2674,31 @@ username@jabber.org + Mark as &Listened + + + + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3454,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 From 1bbfe1599a93e5c1a7c71d6a865c2b8b11c4b952 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 29 May 2013 10:58:22 +0200 Subject: [PATCH 171/565] Remove debug spam. --- src/libtomahawk/TomahawkSettings.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 4416360bd3..ea9812e819 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -623,7 +623,6 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) beginGroup( QString( "accounts/%1" ).arg( account ) ); const QVariantHash creds = value( "credentials" ).toHash(); - tDebug() << "creds:" << creds; if ( !creds.isEmpty() ) { QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "Tomahawk" ), this ); @@ -636,8 +635,6 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) j->setBinaryData( data ); j->start(); - - qDebug() << "Migrating account credentials for account:" << account; } remove( "credentials" ); From cb4816d6623b675411259f3331bf5c46bf93836c Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 29 May 2013 15:22:16 +0200 Subject: [PATCH 172/565] Replaced GenericSelect with a proper dbcmd. Consequences: * we avoid the use of QObject properties, which are not thread safe * we get social actions right away, without having to wait for every track's loadSocialActions to finish --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/Track.cpp | 7 ++ src/libtomahawk/Track.h | 4 + .../DatabaseCommand_LoadInboxEntries.cpp | 75 +++++++++++++++++++ .../DatabaseCommand_LoadInboxEntries.h | 38 ++++++++++ src/libtomahawk/playlist/InboxModel.cpp | 22 +----- 6 files changed, 127 insertions(+), 20 deletions(-) create mode 100644 src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp create mode 100644 src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 57d02537f0..534c1538a5 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -250,6 +250,7 @@ list(APPEND libSources database/DatabaseCommand_CollectionStats.cpp database/DatabaseCommand_TrackStats.cpp database/DatabaseCommand_LoadPlaylistEntries.cpp + database/DatabaseCommand_LoadInboxEntries.cpp database/DatabaseCommand_ModifyPlaylist.cpp database/DatabaseCommand_PlaybackHistory.cpp database/DatabaseCommand_SetPlaylistRevision.cpp diff --git a/src/libtomahawk/Track.cpp b/src/libtomahawk/Track.cpp index 927baca323..eefb1d209e 100644 --- a/src/libtomahawk/Track.cpp +++ b/src/libtomahawk/Track.cpp @@ -227,6 +227,13 @@ Track::updateSortNames() } +void +Track::setAllSocialActions( const QList< SocialAction >& socialActions ) +{ + m_trackData->setAllSocialActions( socialActions ); +} + + bool Track::equals( const Tomahawk::track_ptr& other, bool ignoreCase ) const { diff --git a/src/libtomahawk/Track.h b/src/libtomahawk/Track.h index 3d48093342..ac01a2933d 100644 --- a/src/libtomahawk/Track.h +++ b/src/libtomahawk/Track.h @@ -29,6 +29,7 @@ #include "DllMacro.h" +class DatabaseCommand_LoadInboxEntries; namespace Tomahawk { @@ -38,6 +39,7 @@ class DLLEXPORT Track : public QObject Q_OBJECT friend class Pipeline; +friend class ::DatabaseCommand_LoadInboxEntries; // for setAllSocialActions public: enum DescriptionMode @@ -135,6 +137,8 @@ public slots: void updateSortNames(); + void setAllSocialActions( const QList< SocialAction >& socialActions ); + QString m_composer; QString m_album; QString m_composerSortname; diff --git a/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp new file mode 100644 index 0000000000..04d67cfbf2 --- /dev/null +++ b/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp @@ -0,0 +1,75 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + + +#include "DatabaseCommand_LoadInboxEntries.h" + +#include "DatabaseImpl.h" +#include "Query.h" +#include "SourceList.h" +#include "TomahawkSqlQuery.h" + + +DatabaseCommand_LoadInboxEntries::DatabaseCommand_LoadInboxEntries( QObject* parent ) + : DatabaseCommand( parent ) +{ +} + + +void DatabaseCommand_LoadInboxEntries::exec( DatabaseImpl* dbi ) +{ + TomahawkSqlQuery sqlQuery = dbi->newquery(); + + QString sql = QString( "SELECT track.name as title, artist.name as artist, source, v as unlistened, social_attributes.timestamp " + "FROM social_attributes, track, artist " + "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Inbox' " + "ORDER BY social_attributes.timestamp" ); + + sqlQuery.prepare( sql ); + sqlQuery.exec(); + + QList< Tomahawk::query_ptr > queries; + while ( sqlQuery.next() ) + { + QString track, artist; + track = sqlQuery.value( 0 ).toString(); + artist = sqlQuery.value( 1 ).toString(); + + Tomahawk::query_ptr query = Tomahawk::Query::get( artist, track, QString() ); + if ( query.isNull() ) + continue; + + int sourceId = sqlQuery.value( 2 ).toInt(); + bool unlistened = sqlQuery.value( 3 ).toBool(); + uint timestamp = sqlQuery.value( 4 ).toUInt(); + + Tomahawk::SocialAction action; + action.action = "Inbox"; + action.source = SourceList::instance()->get( sourceId ); + action.value = unlistened; + action.timestamp = timestamp; + + QList< Tomahawk::SocialAction > actions; + actions << action; + + query->queryTrack()->setAllSocialActions( actions ); + queries << query; + } + + emit tracks( queries ); +} diff --git a/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.h b/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.h new file mode 100644 index 0000000000..0ee116fa8c --- /dev/null +++ b/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.h @@ -0,0 +1,38 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DATABASECOMMAND_LOADINBOXENTRIES_H +#define DATABASECOMMAND_LOADINBOXENTRIES_H + +#include "DatabaseCommand.h" + +class DatabaseCommand_LoadInboxEntries : public DatabaseCommand +{ + Q_OBJECT +public: + explicit DatabaseCommand_LoadInboxEntries( QObject* parent = 0 ); + + virtual void exec( DatabaseImpl* ); + virtual bool doesMutates() const { return false; } + virtual QString commandname() const { return "loadinboxentries"; } + +signals: + void tracks( QList< Tomahawk::query_ptr > ); +}; + +#endif // DATABASECOMMAND_LOADINBOXENTRIES_H diff --git a/src/libtomahawk/playlist/InboxModel.cpp b/src/libtomahawk/playlist/InboxModel.cpp index 4deec10307..872a5c94ff 100644 --- a/src/libtomahawk/playlist/InboxModel.cpp +++ b/src/libtomahawk/playlist/InboxModel.cpp @@ -19,7 +19,7 @@ #include "InboxModel.h" #include "database/Database.h" -#include "database/DatabaseCommand_GenericSelect.h" +#include "database/DatabaseCommand_LoadInboxEntries.h" #include "database/DatabaseCommand_DeleteInboxEntry.h" #include "database/DatabaseCommand_ModifyInboxEntry.h" #include "SourceList.h" @@ -190,13 +190,7 @@ InboxModel::loadTracks() { startLoading(); - //extra fields end up in Tomahawk query objects as qt properties - QString sql = QString( "SELECT track.name as title, artist.name as artist, source, v as unlistened, social_attributes.timestamp " - "FROM social_attributes, track, artist " - "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Inbox' " - "ORDER BY social_attributes.timestamp" ); - - DatabaseCommand_GenericSelect* cmd = new DatabaseCommand_GenericSelect( sql, DatabaseCommand_GenericSelect::Track, -1, 0 ); + DatabaseCommand_LoadInboxEntries* cmd = new DatabaseCommand_LoadInboxEntries(); connect( cmd, SIGNAL( tracks( QList ) ), this, SLOT( tracksLoaded( QList ) ) ); Database::instance()->enqueue( QSharedPointer( cmd ) ); } @@ -223,19 +217,7 @@ InboxModel::tracksLoaded( QList< Tomahawk::query_ptr > incoming ) foreach ( Tomahawk::query_ptr newQuery, newTracks ) { - QVariantList extraData = newQuery->property( "data" ).toList(); - - Tomahawk::SocialAction action; - action.action = "Inbox"; - action.source = SourceList::instance()->get( extraData.at( 0 ).toInt() ); - action.value = extraData.at( 1 ).toBool(); //unlistened - action.timestamp = extraData.at( 2 ).toUInt(); - - QList< Tomahawk::SocialAction > actions; - actions << action; newQuery->queryTrack()->loadSocialActions(); - - newQuery->setProperty( "data", QVariant() ); //clear } bool changed = false; From 2b1df0e6645a3bac75517ad2ee3bb58f32731f18 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 29 May 2013 15:55:40 +0200 Subject: [PATCH 173/565] * x.y.999 is invalid on OSX. --- CMakeLists.txt | 2 +- src/libtomahawk/network/ConnectionManager.cpp | 6 +++--- src/libtomahawk/network/Servent.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c71cbe0cf6..e9186ccdae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ ENDIF() SET( TOMAHAWK_VERSION_MAJOR 0 ) SET( TOMAHAWK_VERSION_MINOR 7 ) -SET( TOMAHAWK_VERSION_PATCH 999 ) +SET( TOMAHAWK_VERSION_PATCH 100 ) #SET( TOMAHAWK_VERSION_RC 0 ) SET( TOMAHAWK_TRANSLATION_LANGUAGES ar bg bn_IN ca cs de en el es fi fr hi_IN hu gl it ja lt pl pt_BR ru sv tr zh_CN zh_TW ) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 234e41869e..adfcaf1ad1 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -45,11 +45,11 @@ void ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo ) { m_mutex.lock(); - // Respect different behaviour before 0.7.999 + // Respect different behaviour before 0.7.100 peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Trying to connect to client with version " << peerInfo->versionString().split(' ').last() << TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ); - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.999" ) < 0) + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.100" ) < 0) { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Using old-style (<0.7.999) connection order."; + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Using old-style (<0.7.100) connection order."; SipInfo we = Servent::getSipInfoForOldVersions( Servent::instance()->getLocalSipInfos( QString( "default" ), QString( "default" ) ) ); SipInfo they = peerInfo->sipInfos().first(); if ( they.isVisible() ) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 3284e88e1c..76156b882a 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -486,8 +486,8 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) QList sipInfos = getLocalSipInfos( nodeid, key ); // The offer should be removed after some time or we will build up a heap of unused PeerInfos registerLazyOffer( key, peerInfo, nodeid, sipInfos.length() * 1.5 * CONNECT_TIMEOUT ); - // SipInfos were single-value before 0.7.999 - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.999" ) < 0) + // SipInfos were single-value before 0.7.100 + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.100" ) < 0) { SipInfo info = getSipInfoForOldVersions( sipInfos ); peerInfo->sendLocalSipInfos( QList() << info ); From cae5e75f2a073044c35bc72d1eb211bafeec8ce7 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Wed, 29 May 2013 19:03:45 +0200 Subject: [PATCH 174/565] Only show the mark as listened action if unlistened tracks are selected. --- src/libtomahawk/ContextMenu.cpp | 15 ++++++++++++++- src/libtomahawk/Track.cpp | 30 +++++++++++++++++++----------- src/libtomahawk/Track.h | 2 ++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/ContextMenu.cpp b/src/libtomahawk/ContextMenu.cpp index 6945b878e4..f62e272cde 100644 --- a/src/libtomahawk/ContextMenu.cpp +++ b/src/libtomahawk/ContextMenu.cpp @@ -215,7 +215,20 @@ ContextMenu::setQueries( const QList& queries ) addSeparator(); if ( m_supportedActions & ActionMarkListened ) - m_sigmap->setMapping( addAction( tr( "Mark as &Listened" ) ), ActionMarkListened ); + { + bool thereAreUnlistenedTracks = false; + foreach ( const Tomahawk::query_ptr& query, m_queries ) + { + if ( !query->queryTrack()->isListened() ) + { + thereAreUnlistenedTracks = true; + break; + } + } + + if ( thereAreUnlistenedTracks ) + m_sigmap->setMapping( addAction( tr( "Mark as &Listened" ) ), ActionMarkListened ); + } if ( m_supportedActions & ActionDelete ) m_sigmap->setMapping( addAction( queries.count() > 1 ? tr( "&Remove Items" ) : tr( "&Remove Item" ) ), ActionDelete ); diff --git a/src/libtomahawk/Track.cpp b/src/libtomahawk/Track.cpp index eefb1d209e..68dedd1184 100644 --- a/src/libtomahawk/Track.cpp +++ b/src/libtomahawk/Track.cpp @@ -189,17 +189,7 @@ Track::finishPlaying( int timeElapsed ) void Track::markAsListened() { - bool isUnlistened = false; - foreach ( Tomahawk::SocialAction action, allSocialActions() ) - { - if ( action.action == "Inbox" && action.value.toBool() == true ) - { - isUnlistened = true; - break; - } - } - - if ( isUnlistened ) + if ( !isListened() ) { DatabaseCommand_ModifyInboxEntry* cmd = new DatabaseCommand_ModifyInboxEntry( toQuery(), false ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); @@ -214,8 +204,26 @@ Track::markAsListened() it->value = false; //listened! } } + m_trackData->blockSignals( true ); m_trackData->setAllSocialActions( actions ); //emits socialActionsLoaded which gets propagated here + m_trackData->blockSignals( false ); + } +} + + +bool +Track::isListened() const +{ + bool isUnlistened = false; + foreach ( Tomahawk::SocialAction action, allSocialActions() ) + { + if ( action.action == "Inbox" && action.value.toBool() == true ) + { + isUnlistened = true; + break; + } } + return !isUnlistened; } diff --git a/src/libtomahawk/Track.h b/src/libtomahawk/Track.h index ac01a2933d..324f2714a5 100644 --- a/src/libtomahawk/Track.h +++ b/src/libtomahawk/Track.h @@ -114,7 +114,9 @@ friend class ::DatabaseCommand_LoadInboxEntries; // for setAllSocialActions void startPlaying(); void finishPlaying( int timeElapsed ); + void markAsListened(); + bool isListened() const; signals: void coverChanged(); From bd33824202ba7c4e5739915f6fa61b5193bb9168 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Thu, 30 May 2013 02:17:11 +0200 Subject: [PATCH 175/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 106 +++++++++++++++---------------- lang/tomahawk_bg.ts | 106 +++++++++++++++---------------- lang/tomahawk_bn_IN.ts | 104 +++++++++++++++--------------- lang/tomahawk_ca.ts | 106 +++++++++++++++---------------- lang/tomahawk_ca@valencia.ts | 106 +++++++++++++++---------------- lang/tomahawk_cs.ts | 106 +++++++++++++++---------------- lang/tomahawk_da.ts | 104 +++++++++++++++--------------- lang/tomahawk_de.ts | 106 +++++++++++++++---------------- lang/tomahawk_el.ts | 106 +++++++++++++++---------------- lang/tomahawk_en.ts | 106 +++++++++++++++---------------- lang/tomahawk_es.ts | 106 +++++++++++++++---------------- lang/tomahawk_fi.ts | 119 ++++++++++++++++++----------------- lang/tomahawk_fr.ts | 106 +++++++++++++++---------------- lang/tomahawk_gl.ts | 106 +++++++++++++++---------------- lang/tomahawk_hi_IN.ts | 104 +++++++++++++++--------------- lang/tomahawk_hu.ts | 104 +++++++++++++++--------------- lang/tomahawk_id.ts | 104 +++++++++++++++--------------- lang/tomahawk_it.ts | 106 +++++++++++++++---------------- lang/tomahawk_ja.ts | 106 +++++++++++++++---------------- lang/tomahawk_lt.ts | 104 +++++++++++++++--------------- lang/tomahawk_pl.ts | 104 +++++++++++++++--------------- lang/tomahawk_pt_BR.ts | 106 +++++++++++++++---------------- lang/tomahawk_ru.ts | 106 +++++++++++++++---------------- lang/tomahawk_sv.ts | 106 +++++++++++++++---------------- lang/tomahawk_tr.ts | 104 +++++++++++++++--------------- lang/tomahawk_zh_CN.ts | 106 +++++++++++++++---------------- lang/tomahawk_zh_TW.ts | 104 +++++++++++++++--------------- 27 files changed, 1429 insertions(+), 1428 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index e29a0ae108..392ba70b62 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1608,8 +1608,8 @@ connect and stream from you? - None (outgoing connections only) - لا شيء (الاتصالات الصادرة فقط) + Active (your host needs to be directly reachable) + @@ -2631,8 +2631,8 @@ username@jabber.org - - + + Add to &Queue أضف إلى &قائمة الانتظار @@ -2658,21 +2658,21 @@ username@jabber.org - + &Love &أحب - - + + &Go to "%1" ا&ذهب إلى "%1" - + Go to "%1" اذهب إلى "%1" @@ -2682,32 +2682,32 @@ username@jabber.org &نسخ رابط الأغنية - + Mark as &Listened ضع علامة على أنها &سمعت - + &Remove Items &إزالة البنود - + &Remove Item &إزالة البند - + Copy Album &Link نسخ &رابط الالبوم - + Copy Artist &Link نسخ &رابط قائمة الأغاني - + Un-&Love لا &أحب @@ -3420,43 +3420,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل @@ -3472,42 +3472,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and و - + You أنت - + you أنت - + and و - + %n other(s) %n آخر%n آخر%n آخرين%n آخرين%n آخرين%n آخرين - + %n people %n شخصشخصشخصين%n أشخاص%n شخص%n شخص - + loved this track أحب هذه الأغنية - + sent you this track %1 %1 أرسل لك هذه الأغنية @@ -4258,112 +4258,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction تفاعل المستخدم - + Host is unknown المضيف مجهول - + Item not found البند غير موجودة - + Authorization Error خطا في الترخيص - + Remote Stream Error خطأ في التحميل (المجرى) البعيد - + Remote Connection failed فشل في الاتصال البعيد - + Internal Server Error خطأ داخلي في الخادم - + System shutdown إغلاق النظام - + Conflict تضارب - + Unknown مجهول - + Do you want to add <b>%1</b> to your friend list? هل تريد اضافة <b>%1</b> الى قائمة اصدقائك؟ - + No Compression Support لا دعم للضغط - + Enter Jabber ID أدخل تعريف جابر (Jabber ID) - + No Encryption Support لا دعم للتشفير - + No Authorization Support لا دعم للترخيص - + No Supported Feature لا ميزة معتمدة - + Add Friend أضف صديق - + Enter Xmpp ID: أدخل تعريف أكسمبب (XMPP ID): - + Add Friend... أضف صديق... - + XML Console... وحدة التحكم XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! أنا أسف... أنا مجرد وجود ألي مستخدم من قبل توماهوك (http://gettomahawk.com). في حال الحصول على هذه الرسالة، فإن الشخص اللذي تحاول الوصول إليه خارج الخدمة، فنرجو المحاولة لاحقاً! - + Authorize User أعطي الإذن للمستخدم diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 7231a3631e..39f65ff54d 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1617,8 +1617,8 @@ Tomahawk създаде доклад относно това и изпращай - None (outgoing connections only) - (Само изходящи връзки) + Active (your host needs to be directly reachable) + @@ -2645,8 +2645,8 @@ username@jabber.org - - + + Add to &Queue Добави към &опашката @@ -2672,21 +2672,21 @@ username@jabber.org - + &Love &Харесай - - + + &Go to "%1" &Иди на "%1" - + Go to "%1" Иди на "%1" @@ -2696,32 +2696,32 @@ username@jabber.org &Копирай адреса на изпълнението - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Копирай адреса на албума - + Copy Artist &Link Копирай адреса на изпълнителя - + Un-&Love Не-&харесай @@ -3434,43 +3434,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 песни) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия @@ -3486,42 +3486,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4271,107 +4271,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Действие от потребителя - + Host is unknown Непознат адрес - + Item not found Обектът не е открит - + Authorization Error Грешка при даване на достъп - + Remote Stream Error Грешка в стриймът от отдалечената машина - + Remote Connection failed Отдалечената връзка е неуспешна - + Internal Server Error Вътрешна грешка на сървъра - + System shutdown Изключване на системата - + Conflict Конфликт - + Unknown Неизвестно - + Do you want to add <b>%1</b> to your friend list? Желаеш ли да добавиш <b>%1</b> към приятелите ти? - + No Compression Support Няма поддръжка на компресия - + Enter Jabber ID Въведи Jabber адрес - + No Encryption Support Няма поддръжка на криптиране - + No Authorization Support Няма поддръжка на удостоверяване - + No Supported Feature Неподдържана функция - + Add Friend Добави приятел - + Enter Xmpp ID: Въведи Xmpp ID: - + Add Friend... Добави приятел... - + XML Console... XML Конзола... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Извинявай.. Аз съм режимът за автоматични отговори изпълзван от Tomahawk. @@ -4379,7 +4379,7 @@ Lyrics for "%1" by %2: Щом получаваш това съобщение, този с когото се опитваш да се свържеш вероятно не е на линия, така че опитай отново по-късно. - + Authorize User Оправомощяване на потребител diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 12ca3717af..6aaad91351 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1605,7 +1605,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2673,32 +2673,32 @@ username@jabber.org - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support - + Enter Jabber ID - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 11b678b6cc..b22d4ef4b8 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1606,8 +1606,8 @@ connect and stream from you? - None (outgoing connections only) - Cap (només connexions de sortida) + Active (your host needs to be directly reachable) + @@ -2632,8 +2632,8 @@ nomdusuari@jabber.org - - + + Add to &Queue &Afegeix a la Cua @@ -2659,21 +2659,21 @@ nomdusuari@jabber.org - + &Love &M'encanta - - + + &Go to "%1" &Ves a «%1» - + Go to "%1" Vés a «%1» @@ -2683,32 +2683,32 @@ nomdusuari@jabber.org &Copia l'Enllaç de la Cançó - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Copia l'enllaç de &l'àlbum - + Copy Artist &Link Copia l'enllça de &l'artista - + Un-&Love &Treu de les preferides @@ -3421,43 +3421,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia @@ -3473,42 +3473,42 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4257,112 +4257,112 @@ Lletres de la cancó "%1" de %2: XmppSipPlugin - + User Interaction Interacció d'usuari - + Host is unknown El nom de l'ordinador és desconegut - + Item not found No s'ha trobat l'element - + Authorization Error Error d'autorització - + Remote Stream Error Error de flux remot - + Remote Connection failed Ha fallat la connexió remota - + Internal Server Error Error del servidor intern - + System shutdown Sistema apagat - + Conflict Conflicte - + Unknown Desconegut - + Do you want to add <b>%1</b> to your friend list? Voleu afegir <b>%1</b> a la vostra llista d'amics? - + No Compression Support Compressió no suportada - + Enter Jabber ID Introduïu l'identificador Jabber - + No Encryption Support Encriptació no suportada - + No Authorization Support Autorització no suportada - + No Supported Feature Característica no suportada - + Add Friend Afegeix un Amic - + Enter Xmpp ID: Introduiu la ID XMPP: - + Add Friend... Afegeix un Amic... - + XML Console... Consola XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Sóc una presència automàtica emprada pel Reproductor Tomahawk. (http://gettomahawk.com. Si rebeu aquest missatge, la persona amb qui intenteu contactar probablement no està en línia, intenteu-ho més tard! - + Authorize User Autorització d'Usuari diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 3e5852dbf7..cdf94877cd 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1606,8 +1606,8 @@ connect and stream from you? - None (outgoing connections only) - Cap (només connexions d'eixida) + Active (your host needs to be directly reachable) + @@ -2632,8 +2632,8 @@ nomdusuari@jabber.org - - + + Add to &Queue &Afig a la Cua @@ -2659,21 +2659,21 @@ nomdusuari@jabber.org - + &Love &M'encanta - - + + &Go to "%1" &Ves a «%1» - + Go to "%1" Vés a «%1» @@ -2683,32 +2683,32 @@ nomdusuari@jabber.org &Copia l'Enllaç de la Cançó - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Copia l'enllaç de &l'àlbum - + Copy Artist &Link Copia l'enllça de &l'artista - + Un-&Love &Treu de les preferides @@ -3421,43 +3421,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia @@ -3473,42 +3473,42 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4257,112 +4257,112 @@ Lletres de la cancó "%1" de %2: XmppSipPlugin - + User Interaction Interacció d'usuari - + Host is unknown El nom de l'ordinador és desconegut - + Item not found No s'ha trobat l'element - + Authorization Error Error d'autorització - + Remote Stream Error Error de flux remot - + Remote Connection failed Ha fallat la connexió remota - + Internal Server Error Error del servidor intern - + System shutdown Sistema apagat - + Conflict Conflicte - + Unknown Desconegut - + Do you want to add <b>%1</b> to your friend list? Voleu afegir <b>%1</b> a la vostra llista d'amics? - + No Compression Support Compressió no suportada - + Enter Jabber ID Introduïu l'identificador Jabber - + No Encryption Support Encriptació no suportada - + No Authorization Support Autorització no suportada - + No Supported Feature Característica no suportada - + Add Friend Afig un Amic - + Enter Xmpp ID: Introduiu la ID XMPP: - + Add Friend... Afig un Amic... - + XML Console... Consola XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Sóc una presència automàtica emprada pel Reproductor Tomahawk. (http://gettomahawk.com. Si rebeu este missatge, la persona amb qui intenteu contactar probablement no està en línia, intenteu-ho més tard! - + Authorize User Autorització d'Usuari diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 9b0041d0a1..abbfd65647 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1607,8 +1607,8 @@ se s vámi spojil? - None (outgoing connections only) - Žádné (možná jen odchozí spojení) + Active (your host needs to be directly reachable) + @@ -2630,8 +2630,8 @@ username@jabber.org - - + + Add to &Queue Přidat do řa&dy @@ -2657,21 +2657,21 @@ username@jabber.org - + &Love &Oblíbená píseň - - + + &Go to "%1" &Jít na "%1" - + Go to "%1" Jít na "%1" @@ -2681,32 +2681,32 @@ username@jabber.org &Kopírovat odkaz pro tuto skladbu - + Mark as &Listened - + &Remove Items &Odstranit položky - + &Remove Item Odstranit &položku - + Copy Album &Link Kopírovat odkaz pro toto &album - + Copy Artist &Link Kopírovat odkaz pro tohoto &umělce - + Un-&Love Zrušit zařazení mezi o&blíbené písně @@ -3420,43 +3420,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený @@ -3472,42 +3472,42 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Track - + and a - + You Vy - + you vy - + and a - + %n other(s) %n dalšímu%n dalším%n dalším - + %n people %n člověku%n lidem%n lidem - + loved this track se tato skladba líbí - + sent you this track %1 vám poslal tuto skladbu %1 @@ -4256,112 +4256,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Interakce uživatele - + Host is unknown Hostitel je neznámý - + Item not found Záznam nenalezen - + Authorization Error Chyba při ověřování - + Remote Stream Error Chyba spojení - + Remote Connection failed Chyba spojení - + Internal Server Error Chyba vnitřního serveru - + System shutdown Vypnutí systému - + Conflict Střet - + Unknown Neznámý - + Do you want to add <b>%1</b> to your friend list? Chcete přidat <b>%1</b> ke svým přátelům? - + No Compression Support Žádná podpora pro kompresi - + Enter Jabber ID Zadat ID pro Jabber - + No Encryption Support Žádná podpora pro šifrování - + No Authorization Support Žádná podpora pro povolování - + No Supported Feature Žádná podporovaná vlastnost - + Add Friend Přidat přítele - + Enter Xmpp ID: Identifikátor uživatele XMPP: - + Add Friend... Přidat přítele... - + XML Console... Konzole XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Promiňte-- Jsem jen automatická přítomnost používaná přehrávačem Tomahawk (http://gettomahawk.com). Pokud jste dostal tuto zprávu, osoba, již se pokoušíte zastihnout, pravděpodobně není přihlášena. Zkuste to proto, prosím, později znovu! - + Authorize User Povolit uživatele diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index c5be0be00b..0c14812be2 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1606,7 +1606,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2624,8 +2624,8 @@ username@jabber.org - - + + Add to &Queue Tilføj til &Kø @@ -2651,21 +2651,21 @@ username@jabber.org - + &Love &Elsk - - + + &Go to "%1" - + Go to "%1" @@ -2675,32 +2675,32 @@ username@jabber.org &Kopier Nummer Link - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3409,43 +3409,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline @@ -3461,42 +3461,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4242,112 +4242,112 @@ Sangtekster for "%1" af %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support - + Enter Jabber ID - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 722008cbb5..60cf5ef39d 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1607,8 +1607,8 @@ erlauben sich mit dir zu verbinden? - None (outgoing connections only) - Keine (nur ausgehende Verbindungen möglich) + Active (your host needs to be directly reachable) + @@ -2625,8 +2625,8 @@ username@jabber.org - - + + Add to &Queue In &Warteschlange einreihen @@ -2652,21 +2652,21 @@ username@jabber.org - + &Love &Lieblingslied - - + + &Go to "%1" &Gehe zu "%1" - + Go to "%1" Gehe zu "%1" @@ -2676,32 +2676,32 @@ username@jabber.org &Kopiere Link zu diesem Stück - + Mark as &Listened - + &Remove Items &Elemente entfernen - + &Remove Item &Element entfernen - + Copy Album &Link Album-&Link kopieren - + Copy Artist &Link Künstler-&Link kopieren - + Un-&Love Kein &Lieblingslied @@ -3414,43 +3414,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline @@ -3466,42 +3466,42 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Track - + and und - + You Du - + you du - + and und - + %n other(s) %n andere%n andere - + %n people %n Personen%n Personen - + loved this track gefällt dieses Lied - + sent you this track %1 dieser track wurde gesendet %1 @@ -4248,112 +4248,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Nutzer Interaktion - + Host is unknown Host ist unbekannt - + Item not found Eintrag nicht gefunden - + Authorization Error Authentifizierungs Fehler - + Remote Stream Error Verbindungsfehler - + Remote Connection failed Verbindungsfehler - + Internal Server Error Interner Server Fehler - + System shutdown System Shutdown - + Conflict Konflikt - + Unknown Unbekannt - + Do you want to add <b>%1</b> to your friend list? Willst du <b>%1</b> zu deinen Freunden hinzufügen? - + No Compression Support Keine Kompressions Option - + Enter Jabber ID Jabber ID eingeben - + No Encryption Support Keine Verschluesselungs Option - + No Authorization Support Keine Authorisierungs Option - + No Supported Feature Keine unterstuetzte Faehigkeit - + Add Friend Freund hinzufügen... - + Enter Xmpp ID: XMPP-Benutzer: - + Add Friend... Freund hinzufügen... - + XML Console... XML-Konsole... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Authorisiere Nutzer diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 0bf2d09b36..1d9626d693 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1606,8 +1606,8 @@ connect and stream from you? - None (outgoing connections only) - Καμμια (μονο εξαρχομενες συνδεσεις) + Active (your host needs to be directly reachable) + Ενεργό (ο host σας πρέπει να είναι απευθείας προσβάσιμος) @@ -2631,8 +2631,8 @@ username@jabber.org - - + + Add to &Queue Προσθήκη στην &Σειρά @@ -2658,21 +2658,21 @@ username@jabber.org - + &Love &Αγάπησε - - + + &Go to "%1" &Πήγαινε στο "%1" - + Go to "%1" Πήγαινε στο "%1" @@ -2682,32 +2682,32 @@ username@jabber.org &Αντιγραφή Συνδέσμου Κομματιού - + Mark as &Listened Επισήμανση ως &Ακρόαση - + &Remove Items &Διαγραφή Αντικειμένων - + &Remove Item &Διαγραφή Αντικειμένου - + Copy Album &Link Αντιγραφη Λινκ του Αλμπουμ - + Copy Artist &Link Αντιγραφη του Λινκ του Καλλιτεχνη - + Un-&Love &Μίσησε @@ -3421,43 +3421,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης @@ -3473,42 +3473,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and και - + You Εσυ - + you εσυ - + and και - + %n other(s) %n άλλοι%n άλλοι - + %n people %n άτομα%n άτομα - + loved this track Αγαπημένο τραγούδι - + sent you this track %1 αυτο το τραγουδι σταλθηκε %1 @@ -4254,113 +4254,113 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Αλληλεπίδραση χρήστη - + Host is unknown Ο ξενιστής είναι άγνωστος - + Item not found Το στοιχείο δεν βρέθηκε - + Authorization Error Σφάλμα Πιστοποίησης - + Remote Stream Error Remote Stream Σφάλμα - + Remote Connection failed Η παρακολουθούμενη σύνδεση απέτυχε - + Internal Server Error Εσωτερικό σφάλμα του διακομιστή - + System shutdown Διακοπή λειτουργίας του συστήματος - + Conflict Σύγκρουση - + Unknown Άγνωστο - + Do you want to add <b>%1</b> to your friend list? Θέλετε να προσθέσετε τον <b>%1</b> στην λίστα φίλων σας; - + No Compression Support Καμία υποστήριξη συμπίεσης - + Enter Jabber ID Εισαγωγη του Jabber ID - + No Encryption Support Καμία υποστήριξη κρυπτογράφησης - + No Authorization Support Καμία υποστήριξη πιστοποίησης - + No Supported Feature Κανένα υποστηριζόμενο χαρακτηριστικό - + Add Friend Προσθήκη φίλου - + Enter Xmpp ID: Εισαγωγη του Xmpp ID: - + Add Friend... Προσθήκη Φίλου... - + XML Console... XML Κονσολα... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Λυπάμαι - Είμαι μια αυτόματο παρουσία που χρησιμοποιείται από τον Tomahawk Player (http://gettomahawk.com). Αν παίρνετε αυτό το μήνυμα, το πρόσωπο που προσπαθείτε να φτάσετε δεν είναι πιθανόν να υπογραφεί, οπότε δοκιμάστε ξανά αργότερα ! - + Authorize User Πιστοποιηση χρηστη diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 82969e829d..aa8d35b51f 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1607,8 +1607,8 @@ connect and stream from you? - None (outgoing connections only) - None (outgoing connections only) + Active (your host needs to be directly reachable) + Active (your host needs to be directly reachable) @@ -2633,8 +2633,8 @@ username@jabber.org - - + + Add to &Queue Add to &Queue @@ -2660,21 +2660,21 @@ username@jabber.org - + &Love &Love - - + + &Go to "%1" &Go to "%1" - + Go to "%1" Go to "%1" @@ -2684,32 +2684,32 @@ username@jabber.org &Copy Track Link - + Mark as &Listened Mark as &Listened - + &Remove Items &Remove Items - + &Remove Item &Remove Item - + Copy Album &Link Copy Album &Link - + Copy Artist &Link Copy Artist &Link - + Un-&Love Un-&Love @@ -3423,43 +3423,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline @@ -3475,42 +3475,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and and - + You You - + you you - + and and - + %n other(s) %n other%n others - + %n people %n people%n people - + loved this track loved this track - + sent you this track %1 sent you this track %1 @@ -4263,112 +4263,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction User Interaction - + Host is unknown Host is unknown - + Item not found Item not found - + Authorization Error Authorization Error - + Remote Stream Error Remote Stream Error - + Remote Connection failed Remote Connection failed - + Internal Server Error Internal Server Error - + System shutdown System shutdown - + Conflict Conflict - + Unknown Unknown - + Do you want to add <b>%1</b> to your friend list? Do you want to add <b>%1</b> to your friend list? - + No Compression Support No Compression Support - + Enter Jabber ID Enter Jabber ID - + No Encryption Support No Encryption Support - + No Authorization Support No Authorization Support - + No Supported Feature No Supported Feature - + Add Friend Add Friend - + Enter Xmpp ID: Enter Xmpp ID: - + Add Friend... Add Friend... - + XML Console... XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Authorize User diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 654cc2c98e..06cd339072 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1607,8 +1607,8 @@ conectarse a usted y transmitir música? - None (outgoing connections only) - Ninguna (sólo conexiones salientes) + Active (your host needs to be directly reachable) + @@ -2633,8 +2633,8 @@ usuario@jabber.org - - + + Add to &Queue Añadir a la &cola @@ -2660,21 +2660,21 @@ usuario@jabber.org - + &Love &Favorito - - + + &Go to "%1" &Ir a "%1" - + Go to "%1" &Ir a "%1" @@ -2684,32 +2684,32 @@ usuario@jabber.org &Copiar enlace del tema - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Copiar en&lace del álbum - + Copy Artist &Link Copiar en&lace del artista - + Un-&Love Quitar de &favoritos @@ -3422,43 +3422,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado @@ -3474,42 +3474,42 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4261,112 +4261,112 @@ Letras de "%1" por %2: XmppSipPlugin - + User Interaction Interacción del usuario - + Host is unknown Máquina desconocida - + Item not found Elemento no encontrado - + Authorization Error Error de autorización - + Remote Stream Error Error de stream remoto - + Remote Connection failed Fallo en la conexión remota - + Internal Server Error Error interno del servidor - + System shutdown Sistema apagado - + Conflict Conflicto - + Unknown Desconocido - + Do you want to add <b>%1</b> to your friend list? ¿Le gustaría añadir <b>%1</b> a su lista de amigos? - + No Compression Support Compresión no soportada - + Enter Jabber ID Introducir ID de Jabber - + No Encryption Support Sin soporte de cifrado - + No Authorization Support Sin soporte de autorización - + No Supported Feature Característica no soportada - + Add Friend Añadir amigo - + Enter Xmpp ID: Introducir ID XMPP: - + Add Friend... Añadir amigo… - + XML Console... Consola XML… - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Lo siento -- soy un robot del reproductor Tomahawk (http://gettomahawk.com). Si recibe este mensaje, la persona con quién intenta contactar probablemente no esté conectada. ¡Inténtelo más tarde! - + Authorize User Autorizar usuario diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 1925ef743d..dc89da435b 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -689,7 +689,7 @@ yhdistää ja toistaa sinulta virtaa? Sent %1 by %2 to %3. - + Lähetettiin artistin %2 kappale %1 kaverille %3. @@ -1607,8 +1607,8 @@ yhdistää ja toistaa sinulta virtaa? - None (outgoing connections only) - Ei yhteyttä (vain lähtevät yhteydet) + Active (your host needs to be directly reachable) + Aktiivinen (koneesi tarvitsee olla tavoitettavissa verkosta suoraan) @@ -1811,7 +1811,7 @@ käyttäjäradion käyttöönottamiseksi Drop to send tracks - + Lähetä kappaleet pudottamalla @@ -2636,8 +2636,8 @@ käyttäjätunnus@jabber.org - - + + Add to &Queue &Lisää jonoon @@ -2649,7 +2649,7 @@ käyttäjätunnus@jabber.org Send to &Friend - + L&ähetä kaverille @@ -2663,21 +2663,21 @@ käyttäjätunnus@jabber.org - + &Love &Tykkää - - + + &Go to "%1" Sii&rry sivulle ”%1” - + Go to "%1" Siirry sivulle ”%1” @@ -2687,32 +2687,32 @@ käyttäjätunnus@jabber.org &Kopioi kappaleen linkki - + Mark as &Listened - + &Merkitse kuunnelluksi - + &Remove Items &Poista kohteet - + &Remove Item &Poista kohde - + Copy Album &Link &Kopioi albumin linkki - + Copy Artist &Link &Kopioi artistin linkki - + Un-&Love Lakkaa &tykkäämästä @@ -3260,13 +3260,14 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 lähetti sinulle +kappaleen %2%4 %3. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 lähetti sinulle kappaleen ”%2” artistilta %3. @@ -3425,43 +3426,43 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa @@ -3477,42 +3478,42 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::Track - + and ja - + You Sinä - + you sinä - + and ja - + %n other(s) %n muu%n muuta - + %n people %n ihminen%n ihmistä - + loved this track tykkäsi tästä kappaleesta - + sent you this track %1 lähetti sinulle kappaleen %1 @@ -4262,112 +4263,112 @@ Sanat artistin %2 kappaleelle ”%1”: XmppSipPlugin - + User Interaction Käyttäjän toiminta - + Host is unknown Kone on tuntematon - + Item not found Kohdetta ei löydy - + Authorization Error Valtuutusvirhe - + Remote Stream Error Etävirran virhe - + Remote Connection failed Etäyhteys epäonnistui - + Internal Server Error Sisäinen palvelinvirhe - + System shutdown Järjestelmän sammutus - + Conflict Ristiriita - + Unknown Tuntematon - + Do you want to add <b>%1</b> to your friend list? Haluatko lisätä käyttäjän <b>%1</b> kaverilistallesi? - + No Compression Support Ei pakkaustukea - + Enter Jabber ID Anna Jabber-tunnus - + No Encryption Support Ei salaustukea - + No Authorization Support Ei valtuutustukea - + No Supported Feature Ei-tuettu ominaisuus - + Add Friend Lisää kaveri - + Enter Xmpp ID: Anna XMPP-tunnus: - + Add Friend... Lisää kaveri... - + XML Console... XML-konsoli... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Pahoittelen – olen pelkkä Tomahawk-soittimen (http://gettomahawk.com) automaattinen läsnäoloviesti. Jos näet tämän viestin, tavoittelemasi henkilö ei todennäköisesti ole kirjautuneena, joten yritä myöhemmin uudelleen! - + Authorize User Salli käyttäjä diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index fd38d6f1e7..3fa5e3765a 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1607,8 +1607,8 @@ de se connecter et streamer de vous? - None (outgoing connections only) - Aucune (Connexions sortantes uniquement) + Active (your host needs to be directly reachable) + @@ -2630,8 +2630,8 @@ utilisateur@jabber.org - - + + Add to &Queue &Ajouter à la file d'attente @@ -2657,21 +2657,21 @@ utilisateur@jabber.org - + &Love &Favori - - + + &Go to "%1" Alle&r à "%1" - + Go to "%1" Aller à "%1" @@ -2681,32 +2681,32 @@ utilisateur@jabber.org &Copier le lien de la piste - + Mark as &Listened - + &Remove Items &Supprimer les éléments - + &Remove Item &Supprimer l'élément - + Copy Album &Link Copier le &lien de l'album - + Copy Artist &Link Copier le &lien de l'artiste - + Un-&Love &Supprimer des Favoris @@ -3419,43 +3419,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne @@ -3471,42 +3471,42 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Track - + and et - + You Votre - + you votre - + and et - + %n other(s) %n autre%n autres - + %n people %n personne%n personnes - + loved this track a aimé cette piste - + sent you this track %1 vous a envoyé cette piste %1 @@ -4259,112 +4259,112 @@ Paroles de "%1" par %2 : XmppSipPlugin - + User Interaction Interaction utilisateur - + Host is unknown L'hôte est inconnu - + Item not found Objet non trouvé - + Authorization Error Erreur d'autorisation - + Remote Stream Error Erreur de lecture à distance - + Remote Connection failed Erreur de connexion à distance - + Internal Server Error Erreur interne du serveur - + System shutdown Arrêt du système - + Conflict Conflit - + Unknown Inconnu - + Do you want to add <b>%1</b> to your friend list? Voulez-vous ajouter <b>%1</b> à votre liste d'amis? - + No Compression Support Pas de support de la compression - + Enter Jabber ID Saisir l'ID Jabber - + No Encryption Support Pas de support du chiffrement - + No Authorization Support Pas de support de l'authorization - + No Supported Feature Fonctionnalité non supportée - + Add Friend Ajouter un ami - + Enter Xmpp ID: Entrer l'ID XMPP: - + Add Friend... Ajouter un ami... - + XML Console... Console XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Désolé -- Je suis une présence automatique utilisé par le lecteur Tomahawk (http://gettomahawk.com). Si vous lisez ce message, la personne que vous essayez de joindre n'est probablement pas connecter, donc essayez plus tard ! Merci ! - + Authorize User Autoriser l'utilisateur diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 57ebfc517f..e78f43552c 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1606,8 +1606,8 @@ connect and stream from you? - None (outgoing connections only) - Ningún (só conexións saíntes) + Active (your host needs to be directly reachable) + @@ -2632,8 +2632,8 @@ nomedeusuario@jabber.org - - + + Add to &Queue Engadir á &ringleira @@ -2659,21 +2659,21 @@ nomedeusuario@jabber.org - + &Love &Gústame - - + + &Go to "%1" &Ir a «%1» - + Go to "%1" Ir a «%1» @@ -2683,32 +2683,32 @@ nomedeusuario@jabber.org &Copiar a ligazón da pista - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Copiar a &ligazón do álbum - + Copy Artist &Link Copiar a &ligazón do artista - + Un-&Love Xa non me &gusta @@ -3421,43 +3421,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado @@ -3473,42 +3473,42 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4255,112 +4255,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Interacción de usuarios - + Host is unknown Descoñécese o servidor - + Item not found Elemento non encontrado - + Authorization Error Erro de autorización - + Remote Stream Error - + Remote Connection failed Fallou a conexión remota - + Internal Server Error Erro interno do servidor. - + System shutdown Apagar o sistema - + Conflict Conflito - + Unknown Descoñecido - + Do you want to add <b>%1</b> to your friend list? Queres engadir |b>%1</b> a túa lista de amizades? - + No Compression Support Non se soporta a compresión - + Enter Jabber ID Introducir a ID de Jabber - + No Encryption Support Non se soporta o encriptado - + No Authorization Support Non hai soporte de autorización - + No Supported Feature Característica non soportada - + Add Friend Engadir a un amigo - + Enter Xmpp ID: Introduce a ID de XMPP: - + Add Friend... Engadir amigo... - + XML Console... Consola XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Síntocho -- Son unha mensaxe automática empregada por Tomahawk Player (http://gettomahawk.com). Se ves esta mensaxe é porque a quen te dirixes case seguro non está conectado. Inténtao máis tarde! - + Authorize User Autorizar o usuario diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 4b372f7fb0..9d5178e998 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1605,7 +1605,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2673,32 +2673,32 @@ username@jabber.org - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support - + Enter Jabber ID - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 189e3d5c5f..dd0ab891c7 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1605,7 +1605,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2673,32 +2673,32 @@ username@jabber.org - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Felhasználói interakció - + Host is unknown Ismeretlen hoszt - + Item not found - + Authorization Error Azonosítási hiba - + Remote Stream Error Távoli stream hiba - + Remote Connection failed Távoli kapcsolódási hiba - + Internal Server Error Belső szerver hiba - + System shutdown Rendszer leállítás - + Conflict - + Unknown Ismeretlen - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support - + Enter Jabber ID - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index d63c5383c7..8f90af9544 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1605,7 +1605,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2673,32 +2673,32 @@ username@jabber.org - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support - + Enter Jabber ID - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 1ccf2a702d..4041b910e1 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1605,8 +1605,8 @@ connect and stream from you? - None (outgoing connections only) - Nessuno (connessioni solo in uscita) + Active (your host needs to be directly reachable) + @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue Aggiungi alla &coda @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love &Love - - + + &Go to "%1" &Vai a "%1" - + Go to "%1" Vai a "%1" @@ -2673,32 +2673,32 @@ username@jabber.org &Copia link della traccia - + Mark as &Listened - + &Remove Items &Cancella elementi - + &Remove Item &Cancella elemento - + Copy Album &Link Copia - + Copy Artist &Link Copia il &link dell'artista - + Un-&Love Un-&love @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and e - + You Tu - + you tu - + and e - + %n other(s) U%n altro%n altri - + %n people %n persona%n persone - + loved this track amato questa traccia - + sent you this track %1 ti ha spedito questa traccia %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Interazione con l'utente - + Host is unknown Host sconosciuto - + Item not found Oggetto non trovato - + Authorization Error Errore di autenticazione - + Remote Stream Error Errore dello streaming remoto - + Remote Connection failed Connessione remota fallita - + Internal Server Error Errore interno del server - + System shutdown Spegni il sistema operativo - + Conflict Conflitto - + Unknown Sconosciuto - + Do you want to add <b>%1</b> to your friend list? Vuoi aggiungere <b>%1</b> alla lista dei tuoi amici? - + No Compression Support Nessun supporto per la compressione - + Enter Jabber ID Inserisci ID Jabber - + No Encryption Support Nessun supporto per la criptazione - + No Authorization Support Nessun supporto per l'autenticazione - + No Supported Feature Aspetto non supportato - + Add Friend Aggiungi un amico - + Enter Xmpp ID: Inserisci l'ID Xmpp: - + Add Friend... Aggiungi un amico... - + XML Console... Console XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Mi spiace -- Sono solo un robot utilizzato dal programma Tomahawk (http://gettomahawk.com). Se vedi questo messaggio, la persona che cerchi probabilmente non è connessa. Prova più tardi! - + Authorize User Autorizza l'utente diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 583268d25e..7e8ae9e59f 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1607,8 +1607,8 @@ connect and stream from you? - None (outgoing connections only) - 無(出接続のみ) + Active (your host needs to be directly reachable) + @@ -2630,8 +2630,8 @@ username@jabber.org - - + + Add to &Queue キューに追加 @@ -2657,21 +2657,21 @@ username@jabber.org - + &Love &Love - - + + &Go to "%1" %1に移動 - + Go to "%1" %1に移動 @@ -2681,32 +2681,32 @@ username@jabber.org トラックリンクをコピー - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link アルバムリンクをコピー - + Copy Artist &Link アーティストリンクをコピー - + Un-&Love Loveじゃないトラック @@ -3419,43 +3419,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン @@ -3471,42 +3471,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4259,112 +4259,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction ユーザインタラクション - + Host is unknown 不明なホスト - + Item not found 項目が見つかりません - + Authorization Error 認証エラー - + Remote Stream Error 配信エラー - + Remote Connection failed 接続に失敗しました - + Internal Server Error サーバ内部エラー - + System shutdown システム終了 - + Conflict コンフリクト - + Unknown 不明 - + Do you want to add <b>%1</b> to your friend list? <b>%1</b>を友達のリストに追加しますか? - + No Compression Support 圧縮に対応していません - + Enter Jabber ID Jabber IDを入力 - + No Encryption Support 暗号化に対応していません - + No Authorization Support Authorizationに対応していません - + No Supported Feature 未対応昨機能 - + Add Friend 友達を追加 - + Enter Xmpp ID: XMPP IDを入力: - + Add Friend... 友達を追加... - + XML Console... XMLコンソール... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! これはTomahawk Player (http://gettomahawk.com)の自動プレセンスです。このメッセージが出ている場合は、おそらく連絡したい人はログインしていないので、後でもう一度試して見て下さい。 - + Authorize User ユーザーを認証 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 71a0b3633d..c118079715 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1605,7 +1605,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue Pridėti į &eilę @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love My&liu - - + + &Go to "%1" - + Go to "%1" @@ -2673,32 +2673,32 @@ username@jabber.org &Kopijuoti takelio nuorodą - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Kopijuoti albumo &nuorodą - + Copy Artist &Link Kopijuoti atlikėjo &nuorodą - + Un-&Love Nemy&liu @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Vartotojo sąveika - + Host is unknown Serveris nežinomas - + Item not found Elementas nerastas - + Authorization Error Tapatybės patvirtinimo klaida - + Remote Stream Error Nuotolinio srauto klaida - + Remote Connection failed Nuotolinis prisijungimas nepavyko - + Internal Server Error Vidinė serverio klaida - + System shutdown - + Conflict Konfliktas - + Unknown - + Do you want to add <b>%1</b> to your friend list? Ar norite pridėti <b>%1</b> prie savo draugų sąrašo? - + No Compression Support Nėra glaudinimo palaikymo - + Enter Jabber ID - + No Encryption Support Nėra užšifravimo palaikymo - + No Authorization Support - + No Supported Feature - + Add Friend Pridėti draugą - + Enter Xmpp ID: Įveskite Xmpp ID: - + Add Friend... Pridėti draugą... - + XML Console... XML pultas... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 45b013d068..b89c76bdf6 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1607,7 +1607,7 @@ połączyć się i strumieniować od ciebie? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2627,8 +2627,8 @@ username@jabber.org - - + + Add to &Queue Dodaj do &kolejki @@ -2654,21 +2654,21 @@ username@jabber.org - + &Love &Lubię - - + + &Go to "%1" - + Go to "%1" @@ -2678,32 +2678,32 @@ username@jabber.org - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Kopiuj &link do albumu - + Copy Artist &Link Kopiuj &link do wykonawcy - + Un-&Love Przestań &lubić @@ -3416,43 +3416,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline @@ -3468,42 +3468,42 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4252,112 +4252,112 @@ Tekst dla "%1" wykonawcy %2: XmppSipPlugin - + User Interaction - + Host is unknown Nieznany Host - + Item not found - + Authorization Error Błąd uwierzytelnienia - + Remote Stream Error - + Remote Connection failed Połączenie sieciowe się nie powiodło - + Internal Server Error Wewnętrzny błąd serwera - + System shutdown Wyłączenie systemu - + Conflict Konflikt - + Unknown Nieznany - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support Brak obsługi kompresji - + Enter Jabber ID - + No Encryption Support Brak obsługi szyfrowania - + No Authorization Support Brak obsługi autoryzacji - + No Supported Feature Brak obsługi danej funkcji - + Add Friend Dodaj Znajomego - + Enter Xmpp ID: Podaj ID XMPP: - + Add Friend... Dodaj Znajomego... - + XML Console... Konsola XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Przepraszam -- Jestem automatyczną sekretarką Tomahawk Player (http://gettomahawk.com). Jeżeli czytasz tą wiadomość, osoba z którą próbujesz się skontaktować prawdopodobnie nie jest zalogowana, proszę spróbuj ponownie później! - + Authorize User Autoryzuj Użytkownika diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 861bca5b33..9d0fef9129 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1607,8 +1607,8 @@ se conecte e faça o stream de você? - None (outgoing connections only) - Nenhum (somente conexões de saída) + Active (your host needs to be directly reachable) + @@ -2627,8 +2627,8 @@ username@jabber.org - - + + Add to &Queue Adicionar à &lista @@ -2654,21 +2654,21 @@ username@jabber.org - + &Love &Favorita - - + + &Go to "%1" &Ir para "%1" - + Go to "%1" Ir para "%1" @@ -2678,32 +2678,32 @@ username@jabber.org &Copiar Link da Faixa - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Copiar &Link do Álbum - + Copy Artist &Link Copiar &Link do Artista - + Un-&Love &Desfavoritar @@ -3416,43 +3416,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline @@ -3468,42 +3468,42 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4253,112 +4253,112 @@ Letras de "%1" por %2: XmppSipPlugin - + User Interaction Interação com usuário - + Host is unknown Servidor desconhecido - + Item not found Item não encontrado - + Authorization Error Erro de Autorização - + Remote Stream Error Erro do Stream Remoto - + Remote Connection failed Conexão Remota falhou - + Internal Server Error Erro Interno do Servidor - + System shutdown Desligamento do sistema - + Conflict Conflito - + Unknown Desconhecido - + Do you want to add <b>%1</b> to your friend list? Gostaria de adicionar <b>%1</b> à sua lista de amigos? - + No Compression Support Não há suporte para Compressão - + Enter Jabber ID Informar o Jabber ID - + No Encryption Support Não há suporte para Criptografia - + No Authorization Support Não há suporte para Autorização - + No Supported Feature Recurso não suportado - + Add Friend Adicionar Amigo - + Enter Xmpp ID: Entre o ID do Xmpp - + Add Friend... Adicionar Amigo... - + XML Console... Console XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Desculpe -- Sou apenas uma presença automáica usada pelo Tomahawk (http://gettomahawk.com). Se você esta recebendo esta mensagem, a pessoa que esta tentando alcançar provavelmente não esta logada, então tente mais tarde! - + Authorize User Autorizar Usuário diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index afae3d4e4b..dbbc4bee51 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1610,8 +1610,8 @@ connect and stream from you? - None (outgoing connections only) - Нет (только исходящие соединения) + Active (your host needs to be directly reachable) + @@ -2636,8 +2636,8 @@ username@jabber.org - - + + Add to &Queue Добавить в &Очередь @@ -2663,21 +2663,21 @@ username@jabber.org - + &Love &Любимая - - + + &Go to "%1" &Перейти к "%1" - + Go to "%1" Перейти к "%1" @@ -2687,32 +2687,32 @@ username@jabber.org &Скопировать Ссылку на Песню - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link Копировать &Ссылку Альбома - + Copy Artist &Link Копировать &Ссылку Исполнителя - + Un-&Love &НеЛюбимая @@ -3423,43 +3423,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети @@ -3475,42 +3475,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4260,112 +4260,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Взаимодействие с пользователем - + Host is unknown Неизвестный хост - + Item not found Песня не найдена - + Authorization Error Ошибка авторизации - + Remote Stream Error Удаленный поток ошибок - + Remote Connection failed Ошибка подключения - + Internal Server Error Внутренняя ошибка сервера - + System shutdown Выключение системы - + Conflict Конфликт - + Unknown Неизвестный - + Do you want to add <b>%1</b> to your friend list? Вы хотите добавить <b>%1</b> в списке ваших друзей? - + No Compression Support Нет поддержки сжатия - + Enter Jabber ID Ввести Jabber ID - + No Encryption Support Нет поддержки шифрования - + No Authorization Support Нет поддержки авторизации - + No Supported Feature Не поддерживаемые функции - + Add Friend Добавить друга - + Enter Xmpp ID: Введите XMPP ID: - + Add Friend... Добавить друга... - + XML Console... XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Простите - я просто автоматическое присутствие Tomahawk Player (http://gettomahawk.com). Если Вы получили это сообщение, человека, которого вы пытаетесь достичь, вероятно, не подписан, поэтому, пожалуйста, повторите попытку позже! - + Authorize User Авторизация пользователя diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 8b105b78e7..d95d19297b 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1607,8 +1607,8 @@ ansluta och strömma från dig? - None (outgoing connections only) - Inga (Enbart utgående anslutningar) + Active (your host needs to be directly reachable) + @@ -2631,8 +2631,8 @@ username@jabber.org - - + + Add to &Queue Lägg till i &kö @@ -2658,21 +2658,21 @@ username@jabber.org - + &Love &Kärlek - - + + &Go to "%1" &Gå till "%1" - + Go to "%1" Gå till "%1" @@ -2682,32 +2682,32 @@ username@jabber.org &Kopiera Låtlänk - + Mark as &Listened - + &Remove Items &Ta bort - + &Remove Item &Ta bort - + Copy Album &Link Kopiera album-&länk - + Copy Artist &Link Kopiera artist-&länk - + Un-&Love Av-&älska @@ -3420,43 +3420,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline @@ -3472,42 +3472,42 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Track - + and och - + You Du - + you du - + and och - + %n other(s) %n annan%n andra - + %n people %n person%n personer - + loved this track älskade detta spåret - + sent you this track %1 skickade spåret '%1' till dig @@ -4258,112 +4258,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Användarinteraktion - + Host is unknown Värden är okänd - + Item not found Artikeln kunde inte hittas - + Authorization Error Auktorisationsfel - + Remote Stream Error Ström-fel - + Remote Connection failed Fjärranslutningen misslyckades - + Internal Server Error Internt serverfel - + System shutdown systemavstängning - + Conflict Konflikt - + Unknown Okänd - + Do you want to add <b>%1</b> to your friend list? Vill du lägga till <b>%1</b> till din vänlista? - + No Compression Support Inget kompression-stöd - + Enter Jabber ID Ange Jabber-ID - + No Encryption Support Inget krypteringsstöd - + No Authorization Support Inget auktoriseringsstöd - + No Supported Feature Ingen stödd funktion - + Add Friend Lägg till vän - + Enter Xmpp ID: Ange XMPP-id: - + Add Friend... Lägg till vän... - + XML Console... XML-konsol... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Tyvärr! Det här är bara en automatisk närvaro använt av Tomahawk Player (http://gettomahawk.com). Om du får det här meddelandet så är personen du försöker nå antagligen inte online, så var god och försök igen senare! - + Authorize User Auktorisera användare diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 6b0b9919b9..46402d03cd 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1605,7 +1605,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2673,32 +2673,32 @@ username@jabber.org - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support - + Enter Jabber ID - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 83fbff4fce..bfac1a15f0 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1606,8 +1606,8 @@ connect and stream from you? - None (outgoing connections only) - 停用(只启用对外连接) + Active (your host needs to be directly reachable) + @@ -2628,8 +2628,8 @@ username@jabber.org - - + + Add to &Queue 添加到队列 @@ -2655,21 +2655,21 @@ username@jabber.org - + &Love 喜欢 - - + + &Go to "%1" 转到 "%1" - + Go to "%1" 转到 "%1" @@ -2679,32 +2679,32 @@ username@jabber.org 复制歌曲链接 - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link 复制专辑链接 - + Copy Artist &Link 复制艺术家链接 - + Un-&Love 不喜欢 @@ -3417,43 +3417,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 @@ -3469,42 +3469,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4249,112 +4249,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction 用户交互 - + Host is unknown 服务器未知 - + Item not found 项目未找到 - + Authorization Error 认证错误 - + Remote Stream Error 远端数据流错误 - + Remote Connection failed 远程连接失败 - + Internal Server Error 内部服务器错误 - + System shutdown 系统挂起 - + Conflict 冲突 - + Unknown 未知 - + Do you want to add <b>%1</b> to your friend list? 你希望把 <b>%1</b> 添加到你的朋友列表吗? - + No Compression Support 无压缩支持 - + Enter Jabber ID 输入 Jabber ID - + No Encryption Support 无加密支持 - + No Authorization Support 无认证支持 - + No Supported Feature 无支持的特性 - + Add Friend 添加朋友 - + Enter Xmpp ID: 输入 Xmpp ID: - + Add Friend... 添加朋友 - + XML Console... XML 终端... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! 对不起 —— 我只是一个由 Tomahawk 播放器(http://gettomahawk.com)自动生成的文字。如果你看到这条信息,说明你尝试连接的用户并不可用。请稍后再试! - + Authorize User 认证用户 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 3638623073..c8420bd47b 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1605,7 +1605,7 @@ connect and stream from you? - None (outgoing connections only) + Active (your host needs to be directly reachable) @@ -2622,8 +2622,8 @@ username@jabber.org - - + + Add to &Queue 添加至佇列 @@ -2649,21 +2649,21 @@ username@jabber.org - + &Love - - + + &Go to "%1" - + Go to "%1" @@ -2673,32 +2673,32 @@ username@jabber.org - + Mark as &Listened - + &Remove Items - + &Remove Item - + Copy Album &Link - + Copy Artist &Link - + Un-&Love @@ -3407,43 +3407,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3459,42 +3459,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -4231,112 +4231,112 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction 使用者互動 - + Host is unknown 主機是未知 - + Item not found - + Authorization Error 授權錯誤 - + Remote Stream Error 遠端串流錯誤 - + Remote Connection failed 遠端連線失敗 - + Internal Server Error 內部服務器錯誤 - + System shutdown 系統關閉 - + Conflict 衝突 - + Unknown 未知 - + Do you want to add <b>%1</b> to your friend list? - + No Compression Support 沒有壓縮支持 - + Enter Jabber ID - + No Encryption Support 沒有加密支持 - + No Authorization Support 沒有授權支持 - + No Supported Feature 沒有支持的功能 - + Add Friend 加為好友 - + Enter Xmpp ID: 輸入XMPP識別碼: - + Add Friend... 加為好友... - + XML Console... XML的控制台... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User 授權用戶 From 606a86e016002c482b9f4264382784cf55332120 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 30 May 2013 04:22:06 +0200 Subject: [PATCH 176/565] * Disable / enable alternate row colors when required. --- src/libtomahawk/playlist/TrackView.cpp | 20 +++++++++++++++++++- src/libtomahawk/playlist/TrackView.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/TrackView.cpp b/src/libtomahawk/playlist/TrackView.cpp index aedae4d894..19f4400868 100644 --- a/src/libtomahawk/playlist/TrackView.cpp +++ b/src/libtomahawk/playlist/TrackView.cpp @@ -64,7 +64,6 @@ TrackView::TrackView( QWidget* parent ) setContentsMargins( 0, 0, 0, 0 ); setMouseTracking( true ); - setAlternatingRowColors( true ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); setDragEnabled( true ); @@ -147,6 +146,8 @@ TrackView::setProxyModel( PlayableProxyModel* model ) { if ( m_proxyModel ) { + disconnect( m_proxyModel, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ), this, SLOT( onModelFilling() ) ); + disconnect( m_proxyModel, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( onModelEmptyCheck() ) ); disconnect( m_proxyModel, SIGNAL( filterChanged( QString ) ), this, SLOT( onFilterChanged( QString ) ) ); disconnect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( onViewChanged() ) ); disconnect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( verifySize() ) ); @@ -155,6 +156,8 @@ TrackView::setProxyModel( PlayableProxyModel* model ) m_proxyModel = model; + connect( m_proxyModel, SIGNAL( rowsAboutToBeInserted( QModelIndex, int, int ) ), SLOT( onModelFilling() ) ); + connect( m_proxyModel, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( onModelEmptyCheck() ) ); connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) ); connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) ); connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( verifySize() ) ); @@ -227,6 +230,21 @@ TrackView::setEmptyTip( const QString& tip ) } +void +TrackView::onModelFilling() +{ + setAlternatingRowColors( true ); +} + + +void +TrackView::onModelEmptyCheck() +{ + if ( !m_proxyModel->rowCount( QModelIndex() ) ) + setAlternatingRowColors( false ); +} + + void TrackView::onViewChanged() { diff --git a/src/libtomahawk/playlist/TrackView.h b/src/libtomahawk/playlist/TrackView.h index 947da07fbb..3549a06a98 100644 --- a/src/libtomahawk/playlist/TrackView.h +++ b/src/libtomahawk/playlist/TrackView.h @@ -121,6 +121,8 @@ protected slots: private slots: void onItemResized( const QModelIndex& index ); void onFilterChanged( const QString& filter ); + void onModelFilling(); + void onModelEmptyCheck(); void onCustomContextMenu( const QPoint& pos ); From 9d0c8e1b0366bf697eee8698488d0398d3768248 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 30 May 2013 04:23:38 +0200 Subject: [PATCH 177/565] * Fixed AnimatedSpinner flickering when resizing window. --- src/libtomahawk/utils/AnimatedSpinner.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/utils/AnimatedSpinner.cpp b/src/libtomahawk/utils/AnimatedSpinner.cpp index 6b7166fba8..dc6e145e3c 100644 --- a/src/libtomahawk/utils/AnimatedSpinner.cpp +++ b/src/libtomahawk/utils/AnimatedSpinner.cpp @@ -124,7 +124,6 @@ AnimatedSpinner::paintEvent( QPaintEvent* event ) if ( center != pos() ) { move( center ); - return; } } From 8c10a0c82196581d3921b030196d1f1c032beac2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 30 May 2013 04:24:21 +0200 Subject: [PATCH 178/565] * Try simpler OverlayWidget layout. --- src/libtomahawk/widgets/OverlayWidget.cpp | 25 +++++++++-------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/libtomahawk/widgets/OverlayWidget.cpp b/src/libtomahawk/widgets/OverlayWidget.cpp index 58f8d0311e..ba518c8e33 100644 --- a/src/libtomahawk/widgets/OverlayWidget.cpp +++ b/src/libtomahawk/widgets/OverlayWidget.cpp @@ -48,13 +48,7 @@ OverlayWidget::OverlayWidget( QAbstractItemView* parent ) { init(); - if ( m_itemView->model() ) - { - connect( m_itemView->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ), Qt::UniqueConnection ); - connect( m_itemView->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( onViewChanged() ), Qt::UniqueConnection ); - connect( m_itemView->model(), SIGNAL( loadingStarted() ), SLOT( onViewChanged() ), Qt::UniqueConnection ); - connect( m_itemView->model(), SIGNAL( loadingFinished() ), SLOT( onViewChanged() ), Qt::UniqueConnection ); - } + onViewModelChanged(); connect( m_itemView, SIGNAL( modelChanged() ), SLOT( onViewModelChanged() ) ); } @@ -203,7 +197,6 @@ OverlayWidget::paintEvent( QPaintEvent* event ) if ( center != pos() ) { move( center ); - return; } QPainter p( this ); @@ -213,12 +206,11 @@ OverlayWidget::paintEvent( QPaintEvent* event ) p.setRenderHint( QPainter::Antialiasing ); p.setOpacity( m_opacity ); - QPen pen( palette().dark().color(), .5 ); +/* QPen pen( palette().dark().color(), .5 ); p.setPen( pen ); //FIXME const color p.setBrush( QColor( 30, 30, 30, 255.0 * OPACITY ) ); - - p.drawRoundedRect( r, CORNER_ROUNDNESS, CORNER_ROUNDNESS ); + p.drawRoundedRect( r, CORNER_ROUNDNESS, CORNER_ROUNDNESS );*/ QTextOption to( Qt::AlignCenter ); to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere ); @@ -233,9 +225,9 @@ OverlayWidget::paintEvent( QPaintEvent* event ) QFontMetricsF fm( f ); qreal textHeight = fm.boundingRect( textRect, Qt::AlignCenter | Qt::TextWordWrap, text() ).height(); - while( textHeight > availHeight ) + while ( textHeight > availHeight ) { - if( f.pointSize() <= 4 ) // don't try harder + if ( f.pointSize() <= 4 ) // don't try harder break; f.setPointSize( f.pointSize() - 1 ); @@ -244,7 +236,10 @@ OverlayWidget::paintEvent( QPaintEvent* event ) } p.setFont( f ); -// p.setPen( palette().highlightedText().color() ); - p.setPen( Qt::white ); +#ifdef Q_OS_MAC + p.setPen( Qt::gray ); +#else + p.setPen( palette().text().color().lighter( 100 ) ); +#endif p.drawText( r.adjusted( 8, 8, -8, -8 ), text(), to ); } From 795e525889497c1b93255ffe11bdaf40560bbe19 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 30 May 2013 15:29:04 -0400 Subject: [PATCH 179/565] Add hatchet plugin to master --- CMakeLists.txt | 1 + src/accounts/CMakeLists.txt | 11 +- src/accounts/hatchet/CMakeLists.txt | 52 ++ .../hatchet/account/HatchetAccount.cpp | 417 ++++++++++++ src/accounts/hatchet/account/HatchetAccount.h | 132 ++++ .../hatchet/account/HatchetAccountConfig.cpp | 182 ++++++ .../hatchet/account/HatchetAccountConfig.h | 69 ++ .../hatchet/account/HatchetAccountConfig.ui | 150 +++++ .../hatchet/admin/certs/dreamcatcher.pem | 14 + src/accounts/hatchet/admin/certs/mandella.pem | 14 + .../hatchet/admin/certs/startcomroot.pem | 44 ++ .../admin/icons/hatchet-icon-512x512.png | Bin 0 -> 28842 bytes .../hatchet/cmake/Modules/FindQCA2.cmake | 37 ++ src/accounts/hatchet/resources.qrc | 8 + src/accounts/hatchet/sip/HatchetSip.cpp | 605 ++++++++++++++++++ src/accounts/hatchet/sip/HatchetSip.h | 95 +++ src/accounts/hatchet/sip/WebSocket.cpp | 324 ++++++++++ src/accounts/hatchet/sip/WebSocket.h | 84 +++ .../hatchet/sip/WebSocketThreadController.cpp | 71 ++ .../hatchet/sip/WebSocketThreadController.h | 49 ++ src/accounts/hatchet/sip/hatchet_config.hpp | 72 +++ src/libtomahawk/Source.cpp | 15 +- src/libtomahawk/Source.h | 2 + src/libtomahawk/accounts/AccountManager.cpp | 3 +- src/libtomahawk/accounts/AccountManager.h | 12 +- src/libtomahawk/utils/Closure.cpp | 2 +- src/libtomahawk/utils/Closure.h | 10 +- 27 files changed, 2461 insertions(+), 14 deletions(-) create mode 100644 src/accounts/hatchet/CMakeLists.txt create mode 100644 src/accounts/hatchet/account/HatchetAccount.cpp create mode 100644 src/accounts/hatchet/account/HatchetAccount.h create mode 100644 src/accounts/hatchet/account/HatchetAccountConfig.cpp create mode 100644 src/accounts/hatchet/account/HatchetAccountConfig.h create mode 100644 src/accounts/hatchet/account/HatchetAccountConfig.ui create mode 100644 src/accounts/hatchet/admin/certs/dreamcatcher.pem create mode 100644 src/accounts/hatchet/admin/certs/mandella.pem create mode 100644 src/accounts/hatchet/admin/certs/startcomroot.pem create mode 100644 src/accounts/hatchet/admin/icons/hatchet-icon-512x512.png create mode 100644 src/accounts/hatchet/cmake/Modules/FindQCA2.cmake create mode 100644 src/accounts/hatchet/resources.qrc create mode 100644 src/accounts/hatchet/sip/HatchetSip.cpp create mode 100644 src/accounts/hatchet/sip/HatchetSip.h create mode 100644 src/accounts/hatchet/sip/WebSocket.cpp create mode 100644 src/accounts/hatchet/sip/WebSocket.h create mode 100644 src/accounts/hatchet/sip/WebSocketThreadController.cpp create mode 100644 src/accounts/hatchet/sip/WebSocketThreadController.h create mode 100644 src/accounts/hatchet/sip/hatchet_config.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e9186ccdae..c42abb444a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ add_definitions( "-DQT_STRICT_ITERATORS" ) option(BUILD_GUI "Build Tomahawk with GUI" ON) option(BUILD_RELEASE "Generate TOMAHAWK_VERSION without GIT info" OFF) option(BUILD_TESTS "Build Tomahawk with unit tests" ON) +option(BUILD_HATCHET "Build the Hatchet plugin" OFF) option(WITH_BREAKPAD "Build with breakpad integration" ON) option(WITH_CRASHREPORTER "Build with CrashReporter" ON) diff --git a/src/accounts/CMakeLists.txt b/src/accounts/CMakeLists.txt index a6604bef39..8359bc4dfa 100644 --- a/src/accounts/CMakeLists.txt +++ b/src/accounts/CMakeLists.txt @@ -3,16 +3,17 @@ include( ${CMAKE_CURRENT_LIST_DIR}/../../TomahawkAddPlugin.cmake ) file(GLOB SUBDIRECTORIES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*") foreach(SUBDIRECTORY ${SUBDIRECTORIES}) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}" AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}/CMakeLists.txt") - if(SUBDIRECTORY STREQUAL "xmpp") + if(SUBDIRECTORY STREQUAL "twitter") + elseif(SUBDIRECTORY STREQUAL "xmpp") if( JREEN_FOUND ) add_subdirectory( xmpp ) endif() - elseif(SUBDIRECTORY STREQUAL "twitter") - if(QTWEETLIB_FOUND AND BUILD_GUI) - add_subdirectory( twitter ) + elseif(SUBDIRECTORY STREQUAL "hatchet") + if(BUILD_HATCHET AND BUILD_GUI) + add_subdirectory(hatchet) endif() else() - add_subdirectory( ${SUBDIRECTORY} ) + add_subdirectory(${SUBDIRECTORY}) endif() endif() endforeach() diff --git a/src/accounts/hatchet/CMakeLists.txt b/src/accounts/hatchet/CMakeLists.txt new file mode 100644 index 0000000000..7f2e833017 --- /dev/null +++ b/src/accounts/hatchet/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 2.8) +CMAKE_POLICY(SET CMP0017 NEW) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules") + +if(NOT TOMAHAWK_LIBRARIES) + message(STATUS "BUILDING OUTSIDE TOMAHAWK") + find_package(Tomahawk REQUIRED) +else() + message(STATUS "BUILDING INSIDE TOMAHAWK") + set(TOMAHAWK_USE_FILE "${CMAKE_SOURCE_DIR}/TomahawkUse.cmake") +endif() +include( ${TOMAHAWK_USE_FILE} ) + +find_package(OpenSSL REQUIRED) +find_package(QCA2 REQUIRED) +find_package(websocketpp 0.2.99 REQUIRED) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${WEBSOCKETPP_INCLUDE_DIR} + ${TOMAHAWK_INCLUDE_DIRS} + ${QCA2_INCLUDE_DIR} +) + +add_definitions(-D_WEBSOCKETPP_CPP11_STL_) + +if(APPLE) + # http://stackoverflow.com/questions/7226753/osx-lion-xcode-4-1-how-do-i-setup-a-c0x-project/7236451#7236451 + add_definitions(-std=c++11 -stdlib=libc++ -U__STRICT_ANSI__) + set(PLATFORM_SPECIFIC_LINK_LIBRARIES "/usr/lib/libc++.dylib") +else() + add_definitions(-std=c++0x) +endif() + +tomahawk_add_plugin(hatchet + TYPE account + EXPORT_MACRO ACCOUNTDLLEXPORT_PRO + SOURCES + account/HatchetAccount.cpp + account/HatchetAccountConfig.cpp + sip/WebSocket.cpp + sip/WebSocketThreadController.cpp + sip/HatchetSip.cpp + UI + account/HatchetAccountConfig.ui + LINK_LIBRARIES + ${TOMAHAWK_LIBRARIES} + ${QCA2_LIBRARIES} + ${PLATFORM_SPECIFIC_LINK_LIBRARIES} +) + diff --git a/src/accounts/hatchet/account/HatchetAccount.cpp b/src/accounts/hatchet/account/HatchetAccount.cpp new file mode 100644 index 0000000000..5884d1a074 --- /dev/null +++ b/src/accounts/hatchet/account/HatchetAccount.cpp @@ -0,0 +1,417 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "HatchetAccount.h" + +#include "HatchetAccountConfig.h" +#include "utils/Closure.h" +#include "utils/Logger.h" +#include "sip/HatchetSip.h" +#include "utils/TomahawkUtils.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Tomahawk; +using namespace Accounts; + +static QPixmap* s_icon = 0; +HatchetAccount* HatchetAccount::s_instance = 0; + +const QString c_loginServer("https://mandella.hatchet.is/v1"); +const QString c_accessTokenServer("https://mandella.hatchet.is/v1"); + +HatchetAccountFactory::HatchetAccountFactory() +{ +#ifndef ENABLE_HEADLESS + if ( s_icon == 0 ) + s_icon = new QPixmap( ":/hatchet-account/hatchet-icon-512x512.png" ); +#endif +} + + +HatchetAccountFactory::~HatchetAccountFactory() +{ + +} + + +QPixmap +HatchetAccountFactory::icon() const +{ + return *s_icon; +} + + +Account* +HatchetAccountFactory::createAccount( const QString& pluginId ) +{ + return new HatchetAccount( pluginId.isEmpty() ? generateId( factoryId() ) : pluginId ); +} + + +// Hatchet account + +HatchetAccount::HatchetAccount( const QString& accountId ) + : Account( accountId ) + , m_publicKey( nullptr ) +{ + s_instance = this; + + QFile pemFile( ":/hatchet-account/mandella.pem" ); + pemFile.open( QIODevice::ReadOnly ); + tDebug() << Q_FUNC_INFO << "certs/mandella.pem: " << pemFile.readAll(); + pemFile.close(); + pemFile.open( QIODevice::ReadOnly ); + QCA::ConvertResult conversionResult; + QCA::PublicKey publicKey = QCA::PublicKey::fromPEM(pemFile.readAll(), &conversionResult); + if ( QCA::ConvertGood != conversionResult ) + { + tLog() << Q_FUNC_INFO << "INVALID PUBKEY READ"; + return; + } + m_publicKey = new QCA::PublicKey( publicKey ); +} + + +HatchetAccount::~HatchetAccount() +{ + +} + + +HatchetAccount* +HatchetAccount::instance() +{ + return s_instance; +} + + +AccountConfigWidget* +HatchetAccount::configurationWidget() +{ + if ( m_configWidget.isNull() ) + m_configWidget = QWeakPointer( new HatchetAccountConfig( this ) ); + + return m_configWidget.data(); +} + + +void +HatchetAccount::authenticate() +{ + if ( connectionState() == Connected ) + return; + + if ( !authToken().isEmpty() ) + { + qDebug() << "Have saved credentials with auth token:" << authToken(); + if ( sipPlugin() ) + sipPlugin()->connectPlugin(); + } + else if ( !username().isEmpty() ) + { + // Need to re-prompt for password, since we don't save it! + } +} + + +void +HatchetAccount::deauthenticate() +{ + if ( !m_tomahawkSipPlugin.isNull() ) + sipPlugin()->disconnectPlugin(); + emit deauthenticated(); +} + + +void +HatchetAccount::setConnectionState( Account::ConnectionState connectionState ) +{ + m_state = connectionState; + + emit connectionStateChanged( connectionState ); +} + + +Account::ConnectionState +HatchetAccount::connectionState() const +{ + return m_state; +} + + +SipPlugin* +HatchetAccount::sipPlugin() +{ + if ( m_tomahawkSipPlugin.isNull() ) + { + tLog() << Q_FUNC_INFO; + m_tomahawkSipPlugin = QWeakPointer< HatchetSipPlugin >( new HatchetSipPlugin( this ) ); + connect( m_tomahawkSipPlugin.data(), SIGNAL( authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service, QString ) ), + this, SLOT( authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service, QString ) ) ); + + return m_tomahawkSipPlugin.data(); + } + return m_tomahawkSipPlugin.data(); +} + + +QPixmap +HatchetAccount::icon() const +{ + return *s_icon; +} + + +bool +HatchetAccount::isAuthenticated() const +{ + return credentials().contains( "authtoken" ); +} + + +QString +HatchetAccount::username() const +{ + return credentials().value( "username" ).toString(); +} + + +QByteArray +HatchetAccount::authToken() const +{ + return credentials().value( "authtoken" ).toByteArray(); +} + + +uint +HatchetAccount::authTokenExpiration() const +{ + bool ok; + return credentials().value( "expiration" ).toUInt( &ok ); +} + + +void +HatchetAccount::loginWithPassword( const QString& username, const QString& password, const QString &otp ) +{ + if ( username.isEmpty() || password.isEmpty() || !m_publicKey ) + { + tLog() << "No tomahawk account username or pw or public key, not logging in"; + return; + } + + m_uuid = QUuid::createUuid().toString(); + QCA::SecureArray sa( m_uuid.toLatin1() ); + QCA::SecureArray result = m_publicKey->encrypt( sa, QCA::EME_PKCS1_OAEP ); + + QVariantMap params; + params[ "password" ] = password; + params[ "username" ] = username; + if ( !otp.isEmpty() ) + params[ "otp" ] = otp; + params[ "client" ] = "Tomahawk (" + QHostInfo::localHostName() + ")"; + params[ "nonce" ] = QString( result.toByteArray().toBase64() ); + + QJson::Serializer s; + const QByteArray msgJson = s.serialize( params ); + + QNetworkRequest req( QUrl( c_loginServer + "/auth/credentials") ); + req.setHeader( QNetworkRequest::ContentTypeHeader, "application/json; charset=utf-8" ); + QNetworkReply* reply = TomahawkUtils::nam()->post( req, msgJson ); + + NewClosure( reply, SIGNAL( finished() ), this, SLOT( onPasswordLoginFinished( QNetworkReply*, const QString& ) ), reply, username ); +} + + +void +HatchetAccount::fetchAccessTokens( const QString& type ) +{ + if ( username().isEmpty() || authToken().isEmpty() ) + { + tLog() << "No authToken, not logging in"; + return; + } + + if ( authTokenExpiration() < ( QDateTime::currentMSecsSinceEpoch() / 1000 ) ) + tLog() << "Auth token has expired, but may still be valid on the server"; + + tLog() << "Fetching access tokens"; + QNetworkRequest req( QUrl( c_accessTokenServer + "/tokens/" + type + "?authtoken=" + authToken() ) ); + + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + + connect( reply, SIGNAL( finished() ), this, SLOT( onFetchAccessTokensFinished() ) ); +} + + +void +HatchetAccount::onPasswordLoginFinished( QNetworkReply* reply, const QString& username ) +{ + Q_ASSERT( reply ); + bool ok; + const QVariantMap resp = parseReply( reply, ok ); + if ( !ok ) + { + tLog() << Q_FUNC_INFO << "Error getting parsed reply from auth server"; + emit authError( "An error occurred reading the reply from the server"); + deauthenticate(); + return; + } + + if ( !resp.value( "error" ).toString().isEmpty() ) + { + tLog() << Q_FUNC_INFO << "Auth server returned an error"; + emit authError( resp.value( "error" ).toString() ); + deauthenticate(); + return; + } + + const QString nonce = resp.value( "data" ).toMap().value( "nonce" ).toString(); + if ( nonce != m_uuid ) + { + tLog() << Q_FUNC_INFO << "Auth server nonce value does not match!"; + emit authError( "The nonce value was incorrect. YOUR ACCOUNT MAY BE COMPROMISED." ); + deauthenticate(); + return; + } + + const QByteArray authenticationToken = resp.value( "data" ).toMap().value( "token" ).toByteArray(); + uint expiration = resp.value( "data" ).toMap().value( "expiration" ).toUInt( &ok ); + + QVariantHash creds = credentials(); + creds[ "username" ] = username; + creds[ "authtoken" ] = authenticationToken; + creds[ "expiration" ] = expiration; + setCredentials( creds ); + syncConfig(); + + if ( !authenticationToken.isEmpty() ) + { + if ( sipPlugin() ) + sipPlugin()->connectPlugin(); + } +} + + +void +HatchetAccount::onFetchAccessTokensFinished() +{ + tLog() << Q_FUNC_INFO; + QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() ); + Q_ASSERT( reply ); + bool ok; + const QVariantMap resp = parseReply( reply, ok ); + if ( !ok || !resp.value( "error" ).toString().isEmpty() ) + { + tLog() << Q_FUNC_INFO << "Auth server returned an error"; + if ( ok ) + emit authError( resp.value( "error" ).toString() ); + deauthenticate(); + return; + } + + QVariantHash creds = credentials(); + QStringList tokenTypesFound; + + tDebug() << Q_FUNC_INFO << "resp: " << resp; + + foreach( QVariant tokenVariant, resp[ "data" ].toMap()[ "tokens" ].toList() ) + { + QVariantMap tokenMap = tokenVariant.toMap(); + QString tokenTypeName = tokenMap[ "type" ].toString() + "tokens"; + if ( !tokenTypesFound.contains( tokenTypeName ) ) + { + creds[ tokenTypeName ] = QVariantList(); + tokenTypesFound.append( tokenTypeName ); + } + creds[ tokenTypeName ] = creds[ tokenTypeName ].toList() << tokenMap; + } + + tDebug() << Q_FUNC_INFO << "Creds: " << creds; + + setCredentials( creds ); + syncConfig(); + + tLog() << Q_FUNC_INFO << "Access tokens fetched successfully"; + + emit accessTokensFetched(); +} + + +QString +HatchetAccount::authUrlForService( const Service &service ) const +{ + return m_extraAuthUrls.value( service, QString() ); +} + + +void +HatchetAccount::authUrlDiscovered( Service service, const QString &authUrl ) +{ + m_extraAuthUrls[ service ] = authUrl; +} + + +QVariantMap +HatchetAccount::parseReply( QNetworkReply* reply, bool& okRet ) const +{ + QVariantMap resp; + + reply->deleteLater(); + + bool ok; + int statusCode = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt( &ok ); + if ( reply->error() != QNetworkReply::NoError && statusCode != 400 && statusCode != 500 ) + { + tLog() << Q_FUNC_INFO << "Network error in command:" << reply->error() << reply->errorString(); + okRet = false; + return resp; + } + + QJson::Parser p; + resp = p.parse( reply, &ok ).toMap(); + + if ( !ok ) + { + tLog() << Q_FUNC_INFO << "Error parsing JSON from server"; + okRet = false; + return resp; + } + + if ( !resp.value( "error", "" ).toString().isEmpty() ) + { + tLog() << "Error from tomahawk server response, or in parsing from json:" << resp.value( "error" ).toString() << resp; + } + + tDebug() << Q_FUNC_INFO << "Got keys" << resp.keys(); + tDebug() << Q_FUNC_INFO << "Got values" << resp.values(); + okRet = true; + return resp; +} + +Q_EXPORT_PLUGIN2( Tomahawk::Accounts::AccountFactory, Tomahawk::Accounts::HatchetAccountFactory ) diff --git a/src/accounts/hatchet/account/HatchetAccount.h b/src/accounts/hatchet/account/HatchetAccount.h new file mode 100644 index 0000000000..6a3537bda3 --- /dev/null +++ b/src/accounts/hatchet/account/HatchetAccount.h @@ -0,0 +1,132 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef HATCHET_ACCOUNT_H +#define HATCHET_ACCOUNT_H + + +#include +#include + +#include + +class QNetworkReply; + +class HatchetSipPlugin; + +namespace Tomahawk +{ +namespace Accounts +{ + +class HatchetAccountConfig; + +class ACCOUNTDLLEXPORT HatchetAccountFactory : public AccountFactory +{ + Q_OBJECT + Q_INTERFACES( Tomahawk::Accounts::AccountFactory ) +public: + HatchetAccountFactory(); + virtual ~HatchetAccountFactory(); + + virtual QString factoryId() const { return "hatchetaccount"; } + virtual QString prettyName() const { return "Hatchet"; } + virtual QString description() const { return tr( "Connect to your Hatchet account" ); } + virtual bool isUnique() const { return true; } + AccountTypes types() const { return AccountTypes( SipType ); }; +// virtual bool allowUserCreation() const { return false; } +#ifndef ENABLE_HEADLESS + virtual QPixmap icon() const; +#endif + + + virtual Account* createAccount ( const QString& pluginId = QString() ); +}; + +class ACCOUNTDLLEXPORT HatchetAccount : public Account +{ + Q_OBJECT +public: + enum Service { + Facebook = 0 + }; + + HatchetAccount( const QString &accountId ); + virtual ~HatchetAccount(); + + static HatchetAccount* instance(); + + QPixmap icon() const; + + void authenticate(); + void deauthenticate(); + bool isAuthenticated() const; + + void setConnectionState( Account::ConnectionState connectionState ); + ConnectionState connectionState() const; + + + virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); } + SipPlugin* sipPlugin(); + + AccountConfigWidget* configurationWidget(); + QWidget* aclWidget() { return 0; } + + QString username() const; + + void fetchAccessTokens( const QString& type = "dreamcatcher" ); + + QString authUrlForService( const Service& service ) const; + +signals: + void authError( QString error ); + void deauthenticated(); + void accessTokensFetched(); + +private slots: + void onPasswordLoginFinished( QNetworkReply*, const QString& username ); + void onFetchAccessTokensFinished(); + void authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service service, const QString& authUrl ); + +private: + QByteArray authToken() const; + uint authTokenExpiration() const; + + void loginWithPassword( const QString& username, const QString& password, const QString &otp ); + + QVariantMap parseReply( QNetworkReply* reply, bool& ok ) const; + + QWeakPointer m_configWidget; + + Account::ConnectionState m_state; + + QWeakPointer< HatchetSipPlugin > m_tomahawkSipPlugin; + QHash< Service, QString > m_extraAuthUrls; + + static HatchetAccount* s_instance; + friend class HatchetAccountConfig; + + QCA::PublicKey* m_publicKey; + QString m_uuid; +}; + +} +} + + +#endif diff --git a/src/accounts/hatchet/account/HatchetAccountConfig.cpp b/src/accounts/hatchet/account/HatchetAccountConfig.cpp new file mode 100644 index 0000000000..cecd2016a2 --- /dev/null +++ b/src/accounts/hatchet/account/HatchetAccountConfig.cpp @@ -0,0 +1,182 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "HatchetAccountConfig.h" +#include "HatchetAccount.h" +#include "utils/TomahawkUtils.h" +#include "utils/Logger.h" + +#include "ui_HatchetAccountConfig.h" + +#include + +using namespace Tomahawk; +using namespace Accounts; + +namespace { + enum ButtonAction { + Login, + Register, + Logout + }; +} + +HatchetAccountConfig::HatchetAccountConfig( HatchetAccount* account ) + : AccountConfigWidget( 0 ) + , m_ui( new Ui::HatchetAccountConfig ) + , m_account( account ) +{ + Q_ASSERT( m_account ); + + m_ui->setupUi( this ); + + m_ui->label->setPixmap( m_ui->label->pixmap()->scaled( QSize( 128, 127 ), Qt::KeepAspectRatio, Qt::SmoothTransformation ) ); + + m_ui->loginButton->setDefault( true ); + connect( m_ui->loginButton, SIGNAL( clicked( bool ) ), this, SLOT( login() ) ); + + connect( m_ui->usernameEdit, SIGNAL( textChanged( QString ) ), this, SLOT( fieldsChanged() ) ); + connect( m_ui->passwordEdit, SIGNAL( textChanged( QString ) ), this, SLOT( fieldsChanged() ) ); + connect( m_ui->otpEdit, SIGNAL( textChanged( QString ) ), this, SLOT( fieldsChanged() ) ); + + connect( m_account, SIGNAL( authError( QString ) ), this, SLOT( authError( QString ) ) ); + connect( m_account, SIGNAL( deauthenticated() ), this, SLOT( showLoggedOut() ) ); + connect( m_account, SIGNAL( accessTokensFetched() ), this, SLOT( accountInfoUpdated() ) ); + + if ( !m_account->authToken().isEmpty() ) + accountInfoUpdated(); + else + { + m_ui->usernameEdit->setText( m_account->username() ); + showLoggedOut(); + } +} + +HatchetAccountConfig::~HatchetAccountConfig() +{ + +} + + +void +HatchetAccountConfig::login() +{ + const ButtonAction action = static_cast< ButtonAction>( m_ui->loginButton->property( "action" ).toInt() ); + + if ( action == Login ) + { + // Log in mode + m_account->loginWithPassword( m_ui->usernameEdit->text(), m_ui->passwordEdit->text(), m_ui->otpEdit->text() ); + } + else if ( action == Logout ) + { + // TODO + m_ui->usernameEdit->clear(); + m_ui->passwordEdit->clear(); + m_ui->otpEdit->clear(); + + QVariantHash creds = m_account->credentials(); + creds.clear(); + m_account->setCredentials( creds ); + m_account->sync(); + m_account->deauthenticate(); + } +} + + +void +HatchetAccountConfig::fieldsChanged() +{ + const QString username = m_ui->usernameEdit->text(); + const QString password = m_ui->passwordEdit->text(); + + const ButtonAction action = static_cast< ButtonAction>( m_ui->loginButton->property( "action" ).toInt() ); + + m_ui->loginButton->setEnabled( !username.isEmpty() && !password.isEmpty() && action == Login ); + + m_ui->errorLabel->clear(); + + if ( action == Login ) + m_ui->loginButton->setText( tr( "Login" ) ); +} + + +void +HatchetAccountConfig::showLoggedIn() +{ + m_ui->usernameLabel->hide(); + m_ui->usernameEdit->hide(); + m_ui->otpLabel->hide(); + m_ui->otpEdit->hide(); + m_ui->passwordLabel->hide(); + m_ui->passwordEdit->hide(); + + m_ui->loggedInLabel->setText( tr( "Logged in as: %1" ).arg( m_account->username() ) ); + m_ui->loggedInLabel->show(); + + m_ui->errorLabel->clear(); + m_ui->errorLabel->hide(); + + m_ui->loginButton->setText( "Log out" ); + m_ui->loginButton->setProperty( "action", Logout ); + m_ui->loginButton->setDefault( true ); +} + + +void +HatchetAccountConfig::showLoggedOut() +{ + m_ui->usernameLabel->show(); + m_ui->usernameEdit->show(); + m_ui->passwordLabel->show(); + m_ui->passwordEdit->show(); + m_ui->otpEdit->show(); + m_ui->otpLabel->show(); + + m_ui->loggedInLabel->clear(); + m_ui->loggedInLabel->hide(); + + m_ui->errorLabel->clear(); + + m_ui->loginButton->setText( "Login" ); + m_ui->loginButton->setProperty( "action", Login ); + m_ui->loginButton->setDefault( true ); +} + + +void +HatchetAccountConfig::accountInfoUpdated() +{ + showLoggedIn(); + return; +} + + +void +HatchetAccountConfig::authError( const QString &error ) +{ + QMessageBox::critical( this, "An error was encountered logging in:", error ); +} + + +void +HatchetAccountConfig::showEvent( QShowEvent *event ) +{ + AccountConfigWidget::showEvent( event ); + m_ui->loginButton->setDefault( true ); +} diff --git a/src/accounts/hatchet/account/HatchetAccountConfig.h b/src/accounts/hatchet/account/HatchetAccountConfig.h new file mode 100644 index 0000000000..2cc5e6692f --- /dev/null +++ b/src/accounts/hatchet/account/HatchetAccountConfig.h @@ -0,0 +1,69 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef HATCHET_ACCOUNT_CONFIG_H +#define HATCHET_ACCOUNT_CONFIG_H + +#include + +#include +#include + +class QNetworkReply; + +namespace Ui { + class HatchetAccountConfig; +} + +namespace Tomahawk { +namespace Accounts { + +class HatchetAccount; + +class HatchetAccountConfig : public AccountConfigWidget +{ + Q_OBJECT +public: + explicit HatchetAccountConfig( HatchetAccount* account ); + virtual ~HatchetAccountConfig(); + +private slots: + void login(); + + void fieldsChanged(); + + void showLoggedIn(); + void showLoggedOut(); + + void accountInfoUpdated(); + + void authError( const QString& error ); + +protected: + //virtual void changeEvent( QEvent* event ); + virtual void showEvent( QShowEvent* event ); + +private: + Ui::HatchetAccountConfig* m_ui; + HatchetAccount* m_account; +}; + +} +} + +#endif diff --git a/src/accounts/hatchet/account/HatchetAccountConfig.ui b/src/accounts/hatchet/account/HatchetAccountConfig.ui new file mode 100644 index 0000000000..76d33a4a94 --- /dev/null +++ b/src/accounts/hatchet/account/HatchetAccountConfig.ui @@ -0,0 +1,150 @@ + + + HatchetAccountConfig + + + + 0 + 0 + 301 + 404 + + + + Form + + + + + + + + + :/hatchet-account/hatchet-icon-512x512.png + + + Qt::AlignCenter + + + + + + + Connect to your Hatchet account + + + Qt::AlignCenter + + + + + + + + 10 + 75 + true + + + + + + + Qt::AlignCenter + + + + + + + + + One-time +Password + + + + + + + Username + + + + + + + Hatchet username + + + + + + + Password: + + + + + + + + + + QLineEdit::Password + + + Hatchet password + + + + + + + (Only if configured) + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + true + + + + + + + Login + + + + + + + + + + + + diff --git a/src/accounts/hatchet/admin/certs/dreamcatcher.pem b/src/accounts/hatchet/admin/certs/dreamcatcher.pem new file mode 100644 index 0000000000..6489d80bb5 --- /dev/null +++ b/src/accounts/hatchet/admin/certs/dreamcatcher.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyT6j9B1hiRHwV96BSZJp +vLnGS0p6h1fxJiVGOjpcct/pA1rfVW66LTC7seYT6mF15flccIn5H8srAvH2a57S +uCMHDrHkQIoNDoOf+Ersw4tYzVYv+5h8IN9apYr6iqfT7rn3Fzb5oEB+GqcDurX3 +4aaDjm+Wq0QjJAZf0fOaKor0ASejkj6EywpaHLwj51DKtf6SYbDEf52vDOzsDDnx +2AzVeqzPNFnnau6/v7rCi081b33qOrbJBtJjSNAJcM3iVf465k8KP2VD8nX3wzwT +9H0TEp67UMm0lz2i6gSAyEcIR2FNdTjVa1ZHYF2tnOihFDu0H4U4/4mPswpNNdOp +mtc5WxxK5ouKqxD8ArSoclmZMybIBbL0lLRjo4VC/olfbd1RQ57L9ETtNIf0xxKU +aweUkfWyJfU5ueaSlBaGoKSGugqp50ql3Td0m2JnJmaopdSi12L3EdlaO0+/OL3f +0aXbtnsZXlIHoq7FBEYcLhCNbwEeceV5Cveb5Os8w2331jkgpcPoXQFOtIVyva2B +3RadI0aALIbX5Tt0/AcbLeq10BJH8Ny2RSrhF2cxkjXEwQ68OUbCv1U4Si+EL44E +tctg8zj9rB8SG+miE18cXaQEUxA/olOU/Axkm8dLLLTxqWsD3VQYDxBgBemaY1OT +O32yipB9ko1sLCx6ZaQQ56sCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/src/accounts/hatchet/admin/certs/mandella.pem b/src/accounts/hatchet/admin/certs/mandella.pem new file mode 100644 index 0000000000..b654270c7c --- /dev/null +++ b/src/accounts/hatchet/admin/certs/mandella.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwqJWocwqceJBfO1Bbfdr +ozqYW+3T5VlmpBILw1OREDuUjdCO8mYvgfFTq5/JTZo/grLmd+LqntU3Y3E/u5Ij +Y92hRbPVmsQGIBcbTXuaasFLwjF9HAHaDcTbD0hS1mg1ZeO8r+uVvGmtdGDjulo/ +lCqo8PP2SEu7Bx1w2XQLQSiCnKVZ5Zk25JQDiiDzsBmdTPhOaihU3kyo32qTOovs +xXv9d6J/Uziv/DcuoIDKS1Uvn1h91QTn2wDdW6NcPgjHq/BD0o7cHeuI6d7XXZm3 +XM2TGQLl489h3kT3j6Tn30K3vXobYQvRXH76xjvYuWHlFaT/eSyEx0W/dIoN1Ot/ +88SEaR0hlgBhL1e9sLbQUXKhHooeWApWUDVtioQGRklcZZV3eP3YUUkvUmhfls8R +dgcI68cfZp0JFtbosHtJdzOcDwncKwJCUOP0XIx/5xn4wrAUJqjq08IcRG5s6GUn +lw/n8Tb/paG2Ip5XhzNYX8fSklj2C6/5jZP2jTnAnFVy314U3vFEqLe/kQxIPWen +pPlTqLSBq1J0HomxSUuizjSF4u1oW7TchCO+zV1EjOD3FEsBpWgkG9NIyqOtdUVo +QH4c5HsflspmSAnM3zuap9NHPjFo2Q1tqoqqA2arTzO5zb1d0IQxe193dZ4zm4vj +fb5OBuKW3xwYpJbezb2rHH8CAwEAAQ== +-----END PUBLIC KEY----- diff --git a/src/accounts/hatchet/admin/certs/startcomroot.pem b/src/accounts/hatchet/admin/certs/startcomroot.pem new file mode 100644 index 0000000000..960f2657be --- /dev/null +++ b/src/accounts/hatchet/admin/certs/startcomroot.pem @@ -0,0 +1,44 @@ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j +ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js +LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM +BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy +dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh +cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh +YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg +dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp +bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ +YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT +TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ +9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8 +jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW +FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz +ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1 +ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L +EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu +L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC +O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V +um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh +NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= +-----END CERTIFICATE----- diff --git a/src/accounts/hatchet/admin/icons/hatchet-icon-512x512.png b/src/accounts/hatchet/admin/icons/hatchet-icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..fabab44dd22f9a9e886a38af47cbfe0288f83e83 GIT binary patch literal 28842 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 literal 0 HcmV?d00001 diff --git a/src/accounts/hatchet/cmake/Modules/FindQCA2.cmake b/src/accounts/hatchet/cmake/Modules/FindQCA2.cmake new file mode 100644 index 0000000000..888937196c --- /dev/null +++ b/src/accounts/hatchet/cmake/Modules/FindQCA2.cmake @@ -0,0 +1,37 @@ +# - Try to find QCA2 (Qt Cryptography Architecture 2) +# Once done this will define +# +# QCA2_FOUND - system has QCA2 +# QCA2_INCLUDE_DIR - the QCA2 include directory +# QCA2_LIBRARIES - the libraries needed to use QCA2 +# QCA2_DEFINITIONS - Compiler switches required for using QCA2 +# +# use pkg-config to get the directories and then use these values +# in the FIND_PATH() and FIND_LIBRARY() calls + +# Copyright (c) 2006, Michael Larouche, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if (NOT WIN32) + find_package(PkgConfig) + pkg_check_modules(PC_QCA2 qca2) + set(QCA2_DEFINITIONS ${PC_QCA2_CFLAGS_OTHER}) +endif (NOT WIN32) + +find_library(QCA2_LIBRARIES + NAMES qca + HINTS ${PC_QCA2_LIBDIR} ${PC_QCA2_LIBRARY_DIRS} +) + +find_path(QCA2_INCLUDE_DIR qca.h + HINTS ${PC_QCA2_INCLUDEDIR} ${PC_QCA2_INCLUDE_DIRS} + PATH_SUFFIXES QtCrypto + PATHS /usr/local/lib/qca.framework/Headers/ +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QCA2 DEFAULT_MSG QCA2_LIBRARIES QCA2_INCLUDE_DIR) + +mark_as_advanced(QCA2_INCLUDE_DIR QCA2_LIBRARIES) diff --git a/src/accounts/hatchet/resources.qrc b/src/accounts/hatchet/resources.qrc new file mode 100644 index 0000000000..43ff7e63d3 --- /dev/null +++ b/src/accounts/hatchet/resources.qrc @@ -0,0 +1,8 @@ + + + admin/icons/hatchet-icon-512x512.png + admin/certs/mandella.pem + admin/certs/dreamcatcher.pem + admin/certs/startcomroot.pem + + diff --git a/src/accounts/hatchet/sip/HatchetSip.cpp b/src/accounts/hatchet/sip/HatchetSip.cpp new file mode 100644 index 0000000000..8a965a49ba --- /dev/null +++ b/src/accounts/hatchet/sip/HatchetSip.cpp @@ -0,0 +1,605 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Jeff Mitchell + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "HatchetSip.h" + +#include "account/HatchetAccount.h" +#include "WebSocketThreadController.h" +//#include "WebSocket.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +HatchetSipPlugin::HatchetSipPlugin( Tomahawk::Accounts::Account *account ) + : SipPlugin( account ) + , m_sipState( Closed ) + , m_version( 0 ) + , m_publicKey( nullptr ) + , m_reconnectTimer( this ) +{ + tLog() << Q_FUNC_INFO; + + connect( m_account, SIGNAL( accessTokensFetched() ), this, SLOT( connectWebSocket() ) ); + connect( Servent::instance(), SIGNAL( dbSyncTriggered() ), this, SLOT( dbSyncTriggered() )); + + QFile pemFile( ":/hatchet-account/dreamcatcher.pem" ); + pemFile.open( QIODevice::ReadOnly ); + tDebug() << Q_FUNC_INFO << "certs/dreamcatcher.pem: " << pemFile.readAll(); + pemFile.close(); + pemFile.open( QIODevice::ReadOnly ); + QCA::ConvertResult conversionResult; + QCA::PublicKey publicKey = QCA::PublicKey::fromPEM(pemFile.readAll(), &conversionResult); + if ( QCA::ConvertGood != conversionResult ) + { + tLog() << Q_FUNC_INFO << "INVALID PUBKEY READ"; + return; + } + m_publicKey = new QCA::PublicKey( publicKey ); + + m_reconnectTimer.setInterval( 0 ); + m_reconnectTimer.setSingleShot( true ); + connect( &m_reconnectTimer, SIGNAL( timeout() ), SLOT( connectPlugin() ) ); +} + + +HatchetSipPlugin::~HatchetSipPlugin() +{ + if ( m_webSocketThreadController ) + { + m_webSocketThreadController->quit(); + m_webSocketThreadController->wait( 60000 ); + + delete m_webSocketThreadController.data(); + } + + m_sipState = Closed; + + hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Disconnected ); +} + + +bool +HatchetSipPlugin::isValid() const +{ + return m_account->enabled() && m_account->isAuthenticated() && m_publicKey; +} + + +Tomahawk::Accounts::HatchetAccount* +HatchetSipPlugin::hatchetAccount() const +{ + return qobject_cast< Tomahawk::Accounts::HatchetAccount* >( m_account ); +} + + +void +HatchetSipPlugin::connectPlugin() +{ + tLog() << Q_FUNC_INFO; + if ( !m_account->isAuthenticated() ) + { + tLog() << Q_FUNC_INFO << "Account not authenticated, not continuing"; + //FIXME: Prompt user for password? + return; + } + + hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Connecting ); + hatchetAccount()->fetchAccessTokens(); +} + + +void +HatchetSipPlugin::disconnectPlugin() +{ + tLog() << Q_FUNC_INFO; + if ( m_webSocketThreadController && m_webSocketThreadController->isRunning() ) + emit disconnectWebSocket(); + else + webSocketDisconnected(); +} + + +///////////////////////////// Connection methods //////////////////////////////////// + + +void +HatchetSipPlugin::connectWebSocket() +{ + tLog() << Q_FUNC_INFO; + if ( m_webSocketThreadController ) + { + tLog() << Q_FUNC_INFO << "Already have a thread running, bailing"; + return; + } + + m_webSocketThreadController = QPointer< WebSocketThreadController >( new WebSocketThreadController( this ) ); + + if ( !m_webSocketThreadController ) + { + tLog() << Q_FUNC_INFO << "Could not create a new thread, bailing"; + disconnectPlugin(); + return; + } + + if ( !isValid() ) + { + tLog() << Q_FUNC_INFO << "Invalid state, not continuing with connection"; + return; + } + + m_token.clear(); + + QVariantList tokensCreds = m_account->credentials()[ "dreamcatchertokens" ].toList(); + + //FIXME: Don't blindly pick the first one that matches? Most likely, cycle through if the first one fails + QVariantMap connectVals; + foreach ( QVariant credObj, tokensCreds ) + { + QVariantMap creds = credObj.toMap(); + if ( creds.contains( "type" ) && creds[ "type" ].toString() == "dreamcatcher" ) + { + connectVals = creds; + m_token = creds[ "token" ].toString(); + break; + } + } + + QString url; + if ( !connectVals.isEmpty() ) + { + QString port = connectVals[ "port" ].toString(); + if ( port == "443" ) + url = "wss://"; + else + url = "ws://"; + url += connectVals[ "host" ].toString() + ':' + connectVals[ "port" ].toString(); + } + + if ( url.isEmpty() || m_token.isEmpty() ) + { + tLog() << Q_FUNC_INFO << "Unable to find a proper connection endpoint; bailing"; + disconnectPlugin(); + return; + } + else + tLog() << Q_FUNC_INFO << "Connecting to Dreamcatcher endpoint at: " << url; + + m_webSocketThreadController->setUrl( url ); + m_webSocketThreadController->start(); +} + + +void +HatchetSipPlugin::webSocketConnected() +{ + tLog() << Q_FUNC_INFO << "WebSocket connected"; + + if ( m_token.isEmpty() || !m_account->credentials().contains( "username" ) ) + { + tLog() << Q_FUNC_INFO << "access token or username is empty, aborting"; + disconnectPlugin(); + return; + } + + hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Connected ); + m_sipState = AcquiringVersion; + + m_uuid = QUuid::createUuid().toString(); + QCA::SecureArray sa( m_uuid.toLatin1() ); + QCA::SecureArray result = m_publicKey->encrypt( sa, QCA::EME_PKCS1_OAEP ); + + tDebug() << Q_FUNC_INFO << "uuid:" << m_uuid << ", size of uuid:" << m_uuid.size() << ", size of sa:" << sa.size() << ", size of result:" << result.size(); + + QVariantMap nonceVerMap; + nonceVerMap[ "version" ] = VERSION; + nonceVerMap[ "nonce" ] = QString( result.toByteArray().toBase64() ); + sendBytes( nonceVerMap ); +} + + +void +HatchetSipPlugin::webSocketDisconnected() +{ + tLog() << Q_FUNC_INFO << "WebSocket disconnected"; + + m_reconnectTimer.stop(); + + if ( m_webSocketThreadController ) + { + m_webSocketThreadController->quit(); + m_webSocketThreadController->wait( 60000 ); + + delete m_webSocketThreadController.data(); + } + + m_sipState = Closed; + m_version = 0; + + hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Disconnected ); + + if ( hatchetAccount()->enabled() ) + { + // Work on the assumption that we were disconnected because Dreamcatcher shut down + // Reconnect after a time; use reasonable backoff + random + int interval = m_reconnectTimer.interval() <= 25000 ? m_reconnectTimer.interval() + 5000 : 30000; + interval += QCA::Random::randomInt() % 30; + m_reconnectTimer.setInterval( interval ); + m_reconnectTimer.start(); + } +} + + +bool +HatchetSipPlugin::sendBytes( const QVariantMap& jsonMap ) const +{ + tLog() << Q_FUNC_INFO; + if ( m_sipState == Closed ) + { + tLog() << Q_FUNC_INFO << "was told to send bytes on a closed connection, not gonna do it"; + return false; + } + + QJson::Serializer serializer; + QByteArray bytes = serializer.serialize( jsonMap ); + if ( bytes.isEmpty() ) + { + tLog() << Q_FUNC_INFO << "could not serialize register structure to JSON"; + return false; + } + + tDebug() << Q_FUNC_INFO << "Sending bytes of size" << bytes.size(); + emit rawBytes( bytes ); + return true; +} + + +void +HatchetSipPlugin::messageReceived( const QByteArray &msg ) +{ + tDebug() << Q_FUNC_INFO << "WebSocket message: " << msg; + + QJson::Parser parser; + bool ok; + QVariant jsonVariant = parser.parse( msg, &ok ); + if ( !jsonVariant.isValid() ) + { + tLog() << Q_FUNC_INFO << "Failed to parse message back from server"; + return; + } + + QVariantMap retMap = jsonVariant.toMap(); + + if ( m_sipState == AcquiringVersion ) + { + tLog() << Q_FUNC_INFO << "In acquiring version state, expecting version/nonce information"; + if ( !retMap.contains( "version" ) || !retMap.contains( "nonce" ) ) + { + tLog() << Q_FUNC_INFO << "Failed to acquire version or nonce information"; + disconnectPlugin(); + return; + } + bool ok = false; + int ver = retMap[ "version" ].toInt( &ok ); + if ( ver == 0 || !ok ) + { + tLog() << Q_FUNC_INFO << "Failed to acquire version information"; + disconnectPlugin(); + return; + } + + if ( retMap[ "nonce" ].toString() != m_uuid ) + { + tLog() << Q_FUNC_INFO << "Failed to validate nonce"; + disconnectPlugin(); + return; + } + + m_version = ver; + + QVariantMap registerMap; + registerMap[ "command" ] = "register"; + registerMap[ "token" ] = m_token; + registerMap[ "dbid" ] = Database::instance()->impl()->dbid(); + registerMap[ "alias" ] = QHostInfo::localHostName(); + + QList< SipInfo > sipinfos = Servent::instance()->getLocalSipInfos( "default", "default" ); + QVariantList hostinfos; + foreach ( SipInfo sipinfo, sipinfos ) + { + QVariantMap hostinfo; + hostinfo[ "host" ] = sipinfo.host(); + hostinfo[ "port" ] = sipinfo.port(); + hostinfos << hostinfo; + } + + registerMap[ "hostinfo" ] = hostinfos; + + if ( !sendBytes( registerMap ) ) + { + tLog() << Q_FUNC_INFO << "Failed sending message"; + disconnectPlugin(); + return; + } + + m_sipState = Registering; + } + else if ( m_sipState == Registering ) + { + tLog() << Q_FUNC_INFO << "In registering state, checking status of registration"; + if ( retMap.contains( "status" ) && + retMap[ "status" ].toString() == "success" ) + { + tLog() << Q_FUNC_INFO << "Registered successfully"; + m_sipState = Connected; + hatchetAccount()->setConnectionState( Tomahawk::Accounts::Account::Connected ); + m_reconnectTimer.setInterval( 0 ); + QTimer::singleShot(0, this, SLOT( dbSyncTriggered() ) ); + return; + } + else + { + tLog() << Q_FUNC_INFO << "Failed to register successfully"; + //m_ws.data()->stop(); + return; + } + } + else if ( m_sipState != Connected ) + { + // ...erm? + tLog() << Q_FUNC_INFO << "Got a message from a non connected socket?"; + return; + } + else if ( !retMap.contains( "command" ) || + !retMap[ "command" ].canConvert< QString >() ) + { + tDebug() << Q_FUNC_INFO << "Unable to convert and/or interepret command from server"; + return; + } + + QString command = retMap[ "command" ].toString(); + + if ( command == "new-peer" ) + newPeer( retMap ); + else if ( command == "peer-authorization" ) + peerAuthorization( retMap ); + else if ( command == "synclastseen" ) + sendOplog( retMap ); +} + + +bool +HatchetSipPlugin::checkKeys( QStringList keys, const QVariantMap& map ) const +{ + foreach ( QString key, keys ) + { + if ( !map.contains( key ) ) + { + tLog() << Q_FUNC_INFO << "Did not find the value" << key << "in the new-peer structure"; + return false; + } + } + return true; +} + + +///////////////////////////// Peer handling methods //////////////////////////////////// + + +void +HatchetSipPlugin::newPeer( const QVariantMap& valMap ) +{ + const QString username = valMap[ "username" ].toString(); + const QVariantList hostinfo = valMap[ "hostinfo" ].toList(); + const QString dbid = valMap[ "dbid" ].toString(); + + tDebug() << Q_FUNC_INFO << "username:" << username << "dbid" << dbid; + + QStringList keys( QStringList() << "command" << "username" << "hostinfo" << "dbid" ); + if ( !checkKeys( keys, valMap ) ) + return; + + Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( this, dbid, Tomahawk::PeerInfo::AutoCreate ); + peerInfo->setContactId( username ); + peerInfo->setFriendlyName( username ); + QVariantMap data; + data.insert( "dbid", dbid ); + peerInfo->setData( data ); + + QList< SipInfo > sipInfos; + + foreach ( const QVariant listItem, hostinfo ) + { + if ( !listItem.canConvert< QVariantMap >() ) + continue; + + QVariantMap hostpair = listItem.toMap(); + + if ( !hostpair.contains( "host" ) || !hostpair.contains( "port" ) ) + continue; + + const QString host = hostpair[ "host" ].toString(); + unsigned int port = hostpair[ "port" ].toUInt(); + + if ( host.isEmpty() || port == 0 ) + continue; + + SipInfo sipInfo; + sipInfo.setNodeId( dbid ); + sipInfo.setHost( host ); + sipInfo.setPort( port ); + sipInfo.setVisible( true ); + sipInfos << sipInfo; + } + + m_sipInfoHash[ dbid ] = sipInfos; + + peerInfo->setStatus( Tomahawk::PeerInfo::Online ); +} + + +void +HatchetSipPlugin::sendSipInfos(const Tomahawk::peerinfo_ptr& receiver, const QList< SipInfo >& infos) +{ + if ( infos.size() == 0 ) + { + tLog() << Q_FUNC_INFO << "Got no sipinfo data (list size 0)"; + return; + } + + const QString dbid = receiver->data().toMap().value( "dbid" ).toString(); + tDebug() << Q_FUNC_INFO << "Send local info to " << receiver->friendlyName() << "(" << dbid << ") we are" << infos[ 0 ].nodeId() << "with offerkey " << infos[ 0 ].key(); + + QVariantMap sendMap; + sendMap[ "command" ] = "authorize-peer"; + sendMap[ "dbid" ] = dbid; + sendMap[ "offerkey" ] = infos[ 0 ].key(); + + if ( !sendBytes( sendMap ) ) + tLog() << Q_FUNC_INFO << "Failed sending message"; +} + + +void +HatchetSipPlugin::peerAuthorization( const QVariantMap& valMap ) +{ + tDebug() << Q_FUNC_INFO << "dbid:" << valMap[ "dbid" ].toString() << "offerkey" << valMap[ "offerkey" ].toString(); + + QStringList keys( QStringList() << "command" << "dbid" << "offerkey" ); + if ( !checkKeys( keys, valMap ) ) + return; + + QString dbid = valMap[ "dbid" ].toString(); + + Tomahawk::peerinfo_ptr peerInfo = Tomahawk::PeerInfo::get( this, dbid ); + if( peerInfo.isNull() ) + { + tLog() << Q_FUNC_INFO << "Received a peer-authorization for a peer we don't know about"; + return; + } + + QList< SipInfo > sipInfos = m_sipInfoHash[ dbid ]; + for (int i = 0; i < sipInfos.size(); i++) + sipInfos[i].setKey( valMap[ "offerkey" ].toString() ); + peerInfo->setSipInfos( sipInfos ); + m_sipInfoHash.remove( dbid ); +} + + +///////////////////////////// Syncing methods //////////////////////////////////// + +void +HatchetSipPlugin::dbSyncTriggered() +{ + if ( m_sipState != Connected ) + return; + + if ( !SourceList::instance() || SourceList::instance()->getLocal().isNull() ) + return; + + QVariantMap sourceMap; + sourceMap[ "command" ] = "synctrigger"; + const Tomahawk::source_ptr src = SourceList::instance()->getLocal(); + sourceMap[ "name" ] = src->friendlyName(); + sourceMap[ "alias" ] = QHostInfo::localHostName(); + sourceMap[ "friendlyname" ] = src->dbFriendlyName(); + + if ( !sendBytes( sourceMap ) ) + { + tLog() << Q_FUNC_INFO << "Failed sending message"; + return; + } +} + + +void +HatchetSipPlugin::sendOplog( const QVariantMap& valMap ) const +{ + tDebug() << Q_FUNC_INFO; + DatabaseCommand_loadOps* cmd = new DatabaseCommand_loadOps( SourceList::instance()->getLocal(), valMap[ "lastrevision" ].toString() ); + connect( cmd, SIGNAL( done( QString, QString, QList< dbop_ptr > ) ), SLOT( oplogFetched( QString, QString, QList< dbop_ptr > ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); +} + + +void +HatchetSipPlugin::oplogFetched( const QString& sinceguid, const QString& /* lastguid */, const QList< dbop_ptr > ops ) +{ + tDebug() << Q_FUNC_INFO; + const uint_fast32_t byteMax = 1 << 25; + int currBytes = 0; + QVariantMap commandMap; + commandMap[ "command" ] = "oplog"; + commandMap[ "startingrevision" ] = sinceguid; + currBytes += 60; //baseline for quotes, keys, commas, colons, etc. + QVariantList revisions; + tDebug() << Q_FUNC_INFO << "Found" << ops.size() << "ops"; + foreach( const dbop_ptr op, ops ) + { + currBytes += 80; //baseline for quotes, keys, commas, colons, etc. + QVariantMap revMap; + revMap[ "revision" ] = op->guid; + currBytes += op->guid.length(); + revMap[ "singleton" ] = op->singleton; + currBytes += 5; //true or false + revMap[ "command" ] = op->command; + currBytes += op->command.length(); + currBytes += 5; //true or false + if ( op->compressed ) + { + revMap[ "compressed" ] = true; + QByteArray b64 = op->payload.toBase64(); + revMap[ "payload" ] = op->payload.toBase64(); + currBytes += b64.length(); + } + else + { + revMap[ "compressed" ] = false; + revMap[ "payload" ] = op->payload; + currBytes += op->payload.length(); + } + if ( currBytes >= (int)(byteMax - 1000000) ) // tack on an extra 1M for safety as it seems qjson puts in spaces + break; + else + revisions << revMap; + } + tDebug() << Q_FUNC_INFO << "Sending" << revisions.size() << "revisions"; + commandMap[ "revisions" ] = revisions; + + if ( !sendBytes( commandMap ) ) + { + tLog() << Q_FUNC_INFO << "Failed sending message, attempting to send a blank message to clear sync state"; + QVariantMap rescueMap; + rescueMap[ "command" ] = "oplog"; + if ( !sendBytes( rescueMap ) ) + { + tLog() << Q_FUNC_INFO << "Failed to send rescue map; state may be out-of-sync with server; reconnecting"; + disconnectPlugin(); + } + } +} + + diff --git a/src/accounts/hatchet/sip/HatchetSip.h b/src/accounts/hatchet/sip/HatchetSip.h new file mode 100644 index 0000000000..7ade8c9678 --- /dev/null +++ b/src/accounts/hatchet/sip/HatchetSip.h @@ -0,0 +1,95 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Jeff Mitchell + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef HATCHET_SIP_H +#define HATCHET_SIP_H + +#include "accounts/AccountDllMacro.h" +#include "database/Op.h" +#include "sip/SipPlugin.h" +#include "account/HatchetAccount.h" + +#include +#include +#include + +class WebSocketThreadController; + +const int VERSION = 1; + +class ACCOUNTDLLEXPORT HatchetSipPlugin : public SipPlugin +{ + Q_OBJECT + + enum SipState { + AcquiringVersion, + Registering, + Connected, + Closed + }; + +public: + HatchetSipPlugin( Tomahawk::Accounts::Account *account ); + + virtual ~HatchetSipPlugin(); + + virtual bool isValid() const; + + virtual void sendSipInfos( const Tomahawk::peerinfo_ptr& receiver, const QList< SipInfo >& infos ); + +public slots: + virtual void connectPlugin(); + void disconnectPlugin(); + void checkSettings() {} + void configurationChanged() {} + void addContact( const QString &, const QString& ) {} + void sendMsg( const QString&, const SipInfo& ) {} + void webSocketConnected(); + void webSocketDisconnected(); + +signals: + void connectWebSocket() const; + void disconnectWebSocket() const; + void authUrlDiscovered( Tomahawk::Accounts::HatchetAccount::Service service, const QString& authUrl ) const; + void rawBytes( QByteArray bytes ) const; + +private slots: + void dbSyncTriggered(); + void messageReceived( const QByteArray& msg ); + void connectWebSocket(); + void oplogFetched( const QString& sinceguid, const QString& lastguid, const QList< dbop_ptr > ops ); + +private: + bool sendBytes( const QVariantMap& jsonMap ) const; + bool checkKeys( QStringList keys, const QVariantMap& map ) const; + void newPeer( const QVariantMap& valMap ); + void peerAuthorization( const QVariantMap& valMap ); + void sendOplog( const QVariantMap& valMap ) const; + Tomahawk::Accounts::HatchetAccount* hatchetAccount() const; + + QPointer< WebSocketThreadController > m_webSocketThreadController; + QString m_token; + QString m_uuid; + SipState m_sipState; + int m_version; + QCA::PublicKey* m_publicKey; + QTimer m_reconnectTimer; + QHash< QString, QList< SipInfo > > m_sipInfoHash; +}; + +#endif diff --git a/src/accounts/hatchet/sip/WebSocket.cpp b/src/accounts/hatchet/sip/WebSocket.cpp new file mode 100644 index 0000000000..bbbe70b63a --- /dev/null +++ b/src/accounts/hatchet/sip/WebSocket.cpp @@ -0,0 +1,324 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ +#include "WebSocket.h" + +#include "utils/Logger.h" + +#include + +typedef typename websocketpp::lib::error_code error_code; + +WebSocket::WebSocket( const QString& url ) + : QObject( nullptr ) + , m_disconnecting( false ) + , m_url( url ) + , m_outputStream() + , m_lastSocketState( QAbstractSocket::UnconnectedState ) + , m_connectionTimer( this ) +{ + tLog() << Q_FUNC_INFO << "WebSocket constructing"; + m_client = std::unique_ptr< hatchet_client >( new hatchet_client() ); + m_client->set_message_handler( std::bind(&onMessage, this, std::placeholders::_1, std::placeholders::_2 ) ); + m_client->set_close_handler( std::bind(&onClose, this, std::placeholders::_1 ) ); + m_client->register_ostream( &m_outputStream ); + + m_connectionTimer.setSingleShot( true ); + m_connectionTimer.setInterval( 30000 ); + connect( &m_connectionTimer, SIGNAL( timeout() ), SLOT( disconnectWs() ) ); +} + + +WebSocket::~WebSocket() +{ + if ( m_connection ) + m_connection.reset(); + + m_client.reset(); + + if ( m_socket ) + delete m_socket.data(); +} + + +void +WebSocket::setUrl( const QString &url ) +{ + tLog() << Q_FUNC_INFO << "Setting url to" << url; + if ( m_url == url ) + return; + + // We'll let automatic reconnection handle things + if ( m_socket && m_socket->isEncrypted() ) + disconnectWs(); +} + + +void +WebSocket::connectWs() +{ + tLog() << Q_FUNC_INFO << "Connecting"; + m_disconnecting = false; + if ( m_socket ) + { + if ( m_socket->isEncrypted() ) + return; + + if ( m_socket->state() == QAbstractSocket::ClosingState ) + QMetaObject::invokeMethod( this, "connectWs", Qt::QueuedConnection ); + + return; + } + + tLog() << Q_FUNC_INFO << "Establishing new connection"; + m_socket = QPointer< QSslSocket >( new QSslSocket( nullptr ) ); + m_socket->addCaCertificate( QSslCertificate::fromPath( ":/hatchet-account/startcomroot.pem").first() ); + QObject::connect( m_socket, SIGNAL( stateChanged( QAbstractSocket::SocketState ) ), SLOT( socketStateChanged( QAbstractSocket::SocketState ) ) ); + QObject::connect( m_socket, SIGNAL( sslErrors( const QList< QSslError >& ) ), SLOT( sslErrors( const QList< QSslError >& ) ) ); + QObject::connect( m_socket, SIGNAL( encrypted() ), SLOT( encrypted() ) ); + QObject::connect( m_socket, SIGNAL( readyRead() ), SLOT( socketReadyRead() ) ); + m_socket->connectToHostEncrypted( m_url.host(), m_url.port() ); + m_connectionTimer.start(); +} + + +void +WebSocket::disconnectWs( websocketpp::close::status::value status, const QString &reason ) +{ + tLog() << Q_FUNC_INFO << "Disconnecting"; + m_disconnecting = true; + + error_code ec; + if ( m_connection ) + { + m_connection->close( status, reason.toAscii().constData(), ec ); + QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection ); + QTimer::singleShot( 5000, this, SLOT( disconnectSocket() ) ); //safety + return; + } + + disconnectSocket(); +} + + +void +WebSocket::disconnectSocket() +{ + if ( m_socket && m_socket->state() == QAbstractSocket::ConnectedState ) + m_socket->disconnectFromHost(); + else + QMetaObject::invokeMethod( this, "cleanup", Qt::QueuedConnection ); + + QTimer::singleShot( 5000, this, SLOT( cleanup() ) ); //safety +} + + +void +WebSocket::cleanup() +{ + tLog() << Q_FUNC_INFO << "Cleaning up"; + m_outputStream.seekg( std::ios_base::end ); + m_outputStream.seekp( std::ios_base::end ); + m_queuedMessagesToSend.empty(); + if ( m_connection ) + m_connection.reset(); + + emit disconnected(); +} + + +void +WebSocket::socketStateChanged( QAbstractSocket::SocketState state ) +{ + tLog() << Q_FUNC_INFO << "Socket state changed to" << state; + switch ( state ) + { + case QAbstractSocket::ClosingState: + if ( m_lastSocketState == QAbstractSocket::ClosingState ) + { + // It seems like it does not actually properly close, so force it + tLog() << Q_FUNC_INFO << "Got a double closing state, cleaning up and emitting disconnected"; + m_socket->deleteLater(); + m_lastSocketState = QAbstractSocket::UnconnectedState; + QMetaObject::invokeMethod( this, "cleanup", Qt::QueuedConnection ); + return; + } + break; + case QAbstractSocket::UnconnectedState: + if ( m_lastSocketState == QAbstractSocket::UnconnectedState ) + return; + tLog() << Q_FUNC_INFO << "Socket now unconnected, cleaning up and emitting disconnected"; + m_socket->deleteLater(); + m_lastSocketState = QAbstractSocket::UnconnectedState; + QMetaObject::invokeMethod( this, "cleanup", Qt::QueuedConnection ); + return; + default: + ; + } + m_lastSocketState = state; +} + + +void +WebSocket::sslErrors( const QList< QSslError >& errors ) +{ + tLog() << Q_FUNC_INFO << "Encountered errors when trying to connect via SSL"; + foreach( QSslError error, errors ) + tLog() << Q_FUNC_INFO << "Error: " << error.errorString(); + QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection ); +} + + +void +WebSocket::encrypted() +{ + tLog() << Q_FUNC_INFO << "Encrypted connection to Dreamcatcher established"; + error_code ec; + + QUrl url(m_url); + websocketpp::uri_ptr uri(new websocketpp::uri(false, url.host().toStdString(), url.port(), "/")); + + m_connection = m_client->get_connection( uri, ec ); + if ( !m_connection || ec.value() != 0 ) + { + tLog() << Q_FUNC_INFO << "Got error creating WS connection, error is:" << QString::fromStdString( ec.message() ); + disconnectWs(); + return; + } + m_client->connect( m_connection ); + QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection ); + emit connected(); +} + + +void +WebSocket::readOutput() +{ + if ( !m_connection ) + return; + + tDebug() << Q_FUNC_INFO; + + std::string outputString = m_outputStream.str(); + if ( outputString.size() > 0 ) + { + m_outputStream.str(""); + + tDebug() << Q_FUNC_INFO << "Got string of size" << outputString.size() << "from ostream"; + qint64 sizeWritten = m_socket->write( outputString.data(), outputString.size() ); + tDebug() << Q_FUNC_INFO << "Wrote" << sizeWritten << "bytes to the socket"; + if ( sizeWritten == -1 ) + { + tLog() << Q_FUNC_INFO << "Error during writing, closing connection"; + QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection ); + return; + } + } + + if ( m_queuedMessagesToSend.size() ) + { + if ( m_connection->get_state() == websocketpp::session::state::open ) + { + foreach( QByteArray message, m_queuedMessagesToSend ) + { + tDebug() << Q_FUNC_INFO << "Sending queued message of size" << message.size(); + m_connection->send( std::string( message.constData(), message.size() ), websocketpp::frame::opcode::TEXT ); + } + + m_queuedMessagesToSend.clear(); + QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection ); + m_connectionTimer.stop(); + } + else if ( !m_disconnecting ) + QTimer::singleShot( 200, this, SLOT( readOutput() ) ); + } + else + m_connectionTimer.stop(); +} + +void +WebSocket::socketReadyRead() +{ + tDebug() << Q_FUNC_INFO; + + if ( !m_socket || !m_socket->isEncrypted() ) + return; + + if ( !m_socket->isValid() ) + { + tLog() << Q_FUNC_INFO << "Socket appears to no longer be valid. Something is wrong; disconnecting"; + QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection ); + return; + } + + if ( qint64 bytes = m_socket->bytesAvailable() ) + { + tDebug() << Q_FUNC_INFO << "Bytes available:" << bytes; + QByteArray buf; + buf.resize( bytes ); + qint64 bytesRead = m_socket->read( buf.data(), bytes ); + tDebug() << Q_FUNC_INFO << "Bytes read:" << bytesRead; // << ", content is" << websocketpp::utility::to_hex( buf.constData(), bytesRead ).data(); + if ( bytesRead != bytes ) + { + tLog() << Q_FUNC_INFO << "Error occurred during socket read. Something is wrong; disconnecting"; + QMetaObject::invokeMethod( this, "disconnectWs", Qt::QueuedConnection ); + return; + } + std::stringstream ss( std::string( buf.constData(), bytesRead ) ); + ss >> *m_connection; + } + + QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection ); +} + + +void +WebSocket::encodeMessage( const QByteArray &bytes ) +{ + tDebug() << Q_FUNC_INFO << "Encoding message"; //, message is" << bytes.constData(); + if ( !m_connection ) + { + tLog() << Q_FUNC_INFO << "Asked to send message but do not have a valid connection!"; + return; + } + + if ( m_connection->get_state() != websocketpp::session::state::open ) + { + tLog() << Q_FUNC_INFO << "Connection not yet open/upgraded, queueing work to send"; + m_queuedMessagesToSend.append( bytes ); + m_connectionTimer.start(); + } + else + m_connection->send( std::string( bytes.constData() ), websocketpp::frame::opcode::TEXT ); + + QMetaObject::invokeMethod( this, "readOutput", Qt::QueuedConnection ); +} + +void +onMessage( WebSocket* ws, websocketpp::connection_hdl, hatchet_client::message_ptr msg ) +{ + tDebug() << Q_FUNC_INFO << "Handling message"; + std::string payload = msg->get_payload(); + ws->decodedMessage( QByteArray( payload.data(), payload.length() ) ); +} + +void +onClose( WebSocket *ws, websocketpp::connection_hdl ) +{ + tDebug() << Q_FUNC_INFO << "Handling message"; + QMetaObject::invokeMethod( ws, "disconnectSocket", Qt::QueuedConnection ); +} diff --git a/src/accounts/hatchet/sip/WebSocket.h b/src/accounts/hatchet/sip/WebSocket.h new file mode 100644 index 0000000000..62b23c897c --- /dev/null +++ b/src/accounts/hatchet/sip/WebSocket.h @@ -0,0 +1,84 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ +#ifndef WEBSOCKET__H +#define WEBSOCKET__H + +#include "DllMacro.h" + +#include "hatchet_config.hpp" +#include + +#include +#include +#include +#include + +#include + +typedef typename websocketpp::client< websocketpp::config::hatchet_client > hatchet_client; + +class WebSocket; + +void onMessage( WebSocket* ws, websocketpp::connection_hdl, hatchet_client::message_ptr msg ); +void onClose( WebSocket* ws, websocketpp::connection_hdl ); + +class DLLEXPORT WebSocket : public QObject +{ + Q_OBJECT +public: + explicit WebSocket( const QString& url ); + virtual ~WebSocket(); + +signals: + void connected(); + void disconnected(); + void decodedMessage( QByteArray bytes ); + +public slots: + void setUrl( const QString& url ); + void connectWs(); + void disconnectWs( websocketpp::close::status::value status = websocketpp::close::status::normal, const QString& reason = QString( "Disconnecting" ) ); + void encodeMessage( const QByteArray& bytes ); + +private slots: + void socketStateChanged( QAbstractSocket::SocketState state ); + void sslErrors( const QList< QSslError >& errors ); + void disconnectSocket(); + void cleanup(); + void encrypted(); + void readOutput(); + void socketReadyRead(); + +private: + Q_DISABLE_COPY( WebSocket ) + + friend void onMessage( WebSocket *ws, websocketpp::connection_hdl, hatchet_client::message_ptr msg ); + friend void onClose( WebSocket *ws, websocketpp::connection_hdl ); + + bool m_disconnecting; + QUrl m_url; + std::stringstream m_outputStream; + std::unique_ptr< hatchet_client > m_client; + hatchet_client::connection_ptr m_connection; + QPointer< QSslSocket > m_socket; + QAbstractSocket::SocketState m_lastSocketState; + QList< QByteArray > m_queuedMessagesToSend; + QTimer m_connectionTimer; +}; + +#endif diff --git a/src/accounts/hatchet/sip/WebSocketThreadController.cpp b/src/accounts/hatchet/sip/WebSocketThreadController.cpp new file mode 100644 index 0000000000..86beed07a3 --- /dev/null +++ b/src/accounts/hatchet/sip/WebSocketThreadController.cpp @@ -0,0 +1,71 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ +#include "WebSocketThreadController.h" +#include "WebSocket.h" + +#include "utils/Logger.h" + +WebSocketThreadController::WebSocketThreadController( QObject* sip ) + : QThread( nullptr ) + , m_webSocket( nullptr ) + , m_sip( sip ) +{ +} + + +WebSocketThreadController::~WebSocketThreadController() +{ + if ( m_webSocket ) + { + delete m_webSocket; + m_webSocket = 0; + } +} + + +void +WebSocketThreadController::setUrl( const QString &url ) +{ + m_url = url; + if ( m_webSocket ) + { + QMetaObject::invokeMethod( m_webSocket, "setUrl", Qt::QueuedConnection, Q_ARG( QString, url )); + } +} + + +void +WebSocketThreadController::run() +{ + tLog() << Q_FUNC_INFO << "Starting"; + m_webSocket = QPointer< WebSocket >( new WebSocket( m_url ) ); + if ( m_webSocket && m_sip ) + { + tLog() << Q_FUNC_INFO << "Have a valid websocket and parent"; + connect( m_sip, SIGNAL( connectWebSocket() ), m_webSocket, SLOT( connectWs() ), Qt::QueuedConnection ); + connect( m_sip, SIGNAL( disconnectWebSocket() ), m_webSocket, SLOT( disconnectWs() ), Qt::QueuedConnection ); + connect( m_sip, SIGNAL( rawBytes( QByteArray ) ), m_webSocket, SLOT( encodeMessage( QByteArray ) ), Qt::QueuedConnection ); + connect( m_webSocket, SIGNAL( connected() ), m_sip, SLOT( webSocketConnected() ), Qt::QueuedConnection ); + connect( m_webSocket, SIGNAL( disconnected() ), m_sip, SLOT( webSocketDisconnected() ), Qt::QueuedConnection ); + connect( m_webSocket, SIGNAL( decodedMessage( QByteArray ) ), m_sip, SLOT( messageReceived( QByteArray ) ), Qt::QueuedConnection ); + QMetaObject::invokeMethod( m_webSocket, "connectWs", Qt::QueuedConnection ); + exec(); + delete m_webSocket; + m_webSocket = 0; + } +} diff --git a/src/accounts/hatchet/sip/WebSocketThreadController.h b/src/accounts/hatchet/sip/WebSocketThreadController.h new file mode 100644 index 0000000000..a7511a4b36 --- /dev/null +++ b/src/accounts/hatchet/sip/WebSocketThreadController.h @@ -0,0 +1,49 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Leo Franchi + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ +#ifndef WEBSOCKET_THREAD_CONTROLLER_H +#define WEBSOCKET_THREAD_CONTROLLER_H + +#include "DllMacro.h" + +#include +#include + +class WebSocket; + +class DLLEXPORT WebSocketThreadController : public QThread +{ + Q_OBJECT + +public: + explicit WebSocketThreadController( QObject* sip ); + virtual ~WebSocketThreadController(); + + void setUrl( const QString &url ); + +protected: + void run(); + +private: + Q_DISABLE_COPY( WebSocketThreadController ) + + QPointer< WebSocket > m_webSocket; + QPointer< QObject > m_sip; + QString m_url; +}; + +#endif diff --git a/src/accounts/hatchet/sip/hatchet_config.hpp b/src/accounts/hatchet/sip/hatchet_config.hpp new file mode 100644 index 0000000000..8a33295bc2 --- /dev/null +++ b/src/accounts/hatchet/sip/hatchet_config.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, Peter Thorson. All rights reserved. + * Copyright (c) 2013, Jeff Mitchell . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the WebSocket++ Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef WEBSOCKETPP_CONFIG_HATCHET_CLIENT_HPP +#define WEBSOCKETPP_CONFIG_HATCHET_CLIENT_HPP + +#include +#include +#include + +namespace websocketpp { +namespace config { + +struct hatchet_client : public core_client { + typedef hatchet_client type; + + typedef websocketpp::concurrency::none concurrency_type; + + typedef core_client::request_type request_type; + typedef core_client::response_type response_type; + + typedef core_client::message_type message_type; + typedef core_client::con_msg_manager_type con_msg_manager_type; + typedef core_client::endpoint_msg_manager_type endpoint_msg_manager_type; + + typedef core_client::alog_type alog_type; + typedef core_client::elog_type elog_type; + + typedef core_client::rng_type rng_type; + + struct transport_config { + typedef type::concurrency_type concurrency_type; + typedef type::alog_type alog_type; + typedef type::elog_type elog_type; + typedef type::request_type request_type; + typedef type::response_type response_type; + }; + + typedef websocketpp::transport::iostream::endpoint transport_type; + + //static const websocketpp::log::level alog_level = websocketpp::log::alevel::all; +}; + +} // namespace config +} // namespace websocketpp + +#endif // WEBSOCKETPP_CONFIG_HATCHET_CLIENT_HPP diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index ab397a7695..2a87199b11 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -70,8 +70,12 @@ Source::Source( int id, const QString& nodeId ) if ( m_isLocal ) { - connect( Accounts::AccountManager::instance(), SIGNAL( connected( Tomahawk::Accounts::Account* ) ), SLOT( setOnline() ) ); - connect( Accounts::AccountManager::instance(), SIGNAL( disconnected( Tomahawk::Accounts::Account* ) ), SLOT( setOffline() ) ); + connect( Accounts::AccountManager::instance(), + SIGNAL( connected( Tomahawk::Accounts::Account* ) ), + SLOT( setOnline() ) ); + connect( Accounts::AccountManager::instance(), + SIGNAL( disconnected( Tomahawk::Accounts::Account*, Tomahawk::Accounts::AccountManager::DisconnectReason ) ), + SLOT( handleDisconnect( Tomahawk::Accounts::Account*, Tomahawk::Accounts::AccountManager::DisconnectReason ) ) ); } } @@ -330,6 +334,13 @@ Source::removeCollection( const collection_ptr& c ) emit collectionRemoved( c ); } +void +Source::handleDisconnect( Tomahawk::Accounts::Account*, Tomahawk::Accounts::AccountManager::DisconnectReason reason ) +{ + if ( reason == Tomahawk::Accounts::AccountManager::Disabled ) + setOffline(); +} + void Source::setOffline() diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index aaa6faf2f6..f5fa9ba74e 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -26,6 +26,7 @@ #include #include "Typedefs.h" +#include "accounts/AccountManager.h" #include "network/ControlConnection.h" #include "network/DbSyncConnection.h" #include "collection/Collection.h" @@ -136,6 +137,7 @@ private slots: void dbLoaded( unsigned int id, const QString& fname ); void updateIndexWhenSynced(); + void handleDisconnect( Tomahawk::Accounts::Account*, Tomahawk::Accounts::AccountManager::DisconnectReason reason ); void setOffline(); void setOnline(); diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index fdc26c2166..b72d1f4135 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -512,7 +512,8 @@ AccountManager::onStateChanged( Account::ConnectionState state ) if ( account->connectionState() == Account::Disconnected ) { m_connectedAccounts.removeAll( account ); - emit disconnected( account ); + DisconnectReason reason = account->enabled() ? Disconnected : Disabled; + emit disconnected( account, reason ); } else if ( account->connectionState() == Account::Connected ) { diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 99963dd454..420b73859a 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -21,7 +21,8 @@ #ifndef ACCOUNTMANAGER_H #define ACCOUNTMANAGER_H -#include +#include +#include #include "Typedefs.h" #include "DllMacro.h" @@ -42,6 +43,11 @@ class DLLEXPORT AccountManager : public QObject Q_OBJECT public: + enum DisconnectReason { + Disconnected, + Disabled + }; + static AccountManager* instance(); explicit AccountManager( QObject *parent ); @@ -61,7 +67,7 @@ class DLLEXPORT AccountManager : public QObject void hookupAndEnable( Account* account, bool startup = false ); /// Hook up signals and start the plugin void removeAccount( Account* account ); - QList< Account* > accounts() const { return m_accounts; }; + QList< Account* > accounts() const { return m_accounts; } QList< Account* > accounts( Tomahawk::Accounts::AccountType type ) const { return m_accountsByAccountType[ type ]; } QList< Account* > accountsFromFactory( Tomahawk::Accounts::AccountFactory* factory ) const; @@ -105,7 +111,7 @@ public slots: void removed( Tomahawk::Accounts::Account* ); void connected( Tomahawk::Accounts::Account* ); - void disconnected( Tomahawk::Accounts::Account* ); + void disconnected( Tomahawk::Accounts::Account*, Tomahawk::Accounts::AccountManager::DisconnectReason ); void authError( Tomahawk::Accounts::Account* ); void stateChanged( Account* p, Accounts::Account::ConnectionState state ); diff --git a/src/libtomahawk/utils/Closure.cpp b/src/libtomahawk/utils/Closure.cpp index daa1003b2d..e77321d5d9 100644 --- a/src/libtomahawk/utils/Closure.cpp +++ b/src/libtomahawk/utils/Closure.cpp @@ -54,7 +54,7 @@ Closure::Closure(QObject* sender, Closure::Closure(QObject* sender, const char* signal, - std::tr1::function callback) + function callback) : callback_(callback) { Connect(sender, signal); } diff --git a/src/libtomahawk/utils/Closure.h b/src/libtomahawk/utils/Closure.h index 7e1cdf53b6..fa89257d51 100644 --- a/src/libtomahawk/utils/Closure.h +++ b/src/libtomahawk/utils/Closure.h @@ -21,7 +21,13 @@ #include "DllMacro.h" +#ifdef _WEBSOCKETPP_CPP11_STL_ +#include +using std::function; +#else #include +using std::tr1::function; +#endif #include #include @@ -64,7 +70,7 @@ class DLLEXPORT Closure : public QObject, boost::noncopyable { const ClosureArgumentWrapper* val3 = 0); Closure(QObject* sender, const char* signal, - std::tr1::function callback); + function callback); void setAutoDelete( bool autoDelete ) { autoDelete_ = autoDelete; } @@ -87,7 +93,7 @@ class DLLEXPORT Closure : public QObject, boost::noncopyable { void Connect(QObject* sender, const char* signal); QMetaMethod slot_; - std::tr1::function callback_; + function callback_; bool autoDelete_; QObject* outOfThreadReceiver_; From ee4186c51d8220dbf97c0f666792cb0dcaee5554 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Fri, 31 May 2013 02:16:45 +0200 Subject: [PATCH 180/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_bg.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_bn_IN.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_ca.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_ca@valencia.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_cs.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_da.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_de.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_el.ts | 91 ++++++++++++++++++++++++++++++++---- lang/tomahawk_en.ts | 91 ++++++++++++++++++++++++++++++++---- lang/tomahawk_es.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_fi.ts | 91 ++++++++++++++++++++++++++++++++---- lang/tomahawk_fr.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_gl.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_hi_IN.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_hu.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_id.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_it.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_ja.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_lt.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_pl.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_pt_BR.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_ru.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_sv.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_tr.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_zh_CN.ts | 90 +++++++++++++++++++++++++++++++---- lang/tomahawk_zh_TW.ts | 90 +++++++++++++++++++++++++++++++---- 27 files changed, 2163 insertions(+), 270 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 392ba70b62..18c0a5d427 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -676,6 +676,55 @@ connect and stream from you? هذه القائمة فارغة حاليا. أضف لها بعض الأغاني واستمتع للموسيقى! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2235,6 +2284,27 @@ connect and stream from you? أدخل الحساب الخاص بجوجل (Google): + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3420,43 +3490,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل @@ -3811,7 +3881,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. عذراً، ترشيحك "%1" لم يطابق أي نتائج. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 39f65ff54d..0d6c4995be 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -682,6 +682,55 @@ Tomahawk създаде доклад относно това и изпращай Добави няколко песни и се наслади на музиката. + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2250,6 +2299,27 @@ Tomahawk създаде доклад относно това и изпращай Въведи Google регистрация: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3434,43 +3504,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 песни) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия @@ -3827,7 +3897,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Съжалявам, твоят филтър %1 не върна никакъв резултат. diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 6aaad91351..d7b1a8c1b3 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index b22d4ef4b8..85591251bd 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -675,6 +675,55 @@ connect and stream from you? La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2236,6 +2285,27 @@ i emissores basades en els vostres gusts musicals. Introduïu l'adreça de Google: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3421,43 +3491,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia @@ -3812,7 +3882,7 @@ introduïu el PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index cdf94877cd..8d7f524204 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -675,6 +675,55 @@ connect and stream from you? La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2236,6 +2285,27 @@ i emissores basades en els vostres gusts musicals. Introduïu l'adreça de Google: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3421,43 +3491,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia @@ -3812,7 +3882,7 @@ introduïu el PIN ací: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index abbfd65647..57922c3ff6 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -676,6 +676,55 @@ se s vámi spojil? Tento seznam skladeb je nyní prázdný. Přidejte do něj nějaké skladby a vychutnávejte hudbu! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2234,6 +2283,27 @@ se s vámi spojil? Adresa Google: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3420,43 +3490,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený @@ -3811,7 +3881,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackView - + Sorry, your filter '%1' did not match any results. Promiňte, vašemu filtru '%1' se nepodařilo najít žádné výsledky. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 0c14812be2..65198b99ac 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2233,6 +2282,27 @@ connect and stream from you? Indtast Google Adresse: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3409,43 +3479,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline @@ -3799,7 +3869,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Beklager, din filter '%1' matchede ikke nogle resultater diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 60cf5ef39d..71ca95a01f 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -676,6 +676,55 @@ erlauben sich mit dir zu verbinden? Diese Playlist ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2234,6 +2283,27 @@ erlauben sich mit dir zu verbinden? Google Adresse: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3414,43 +3484,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline @@ -3805,7 +3875,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackView - + Sorry, your filter '%1' did not match any results. Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 1d9626d693..409316b07d 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -675,6 +675,56 @@ connect and stream from you? Αυτή η λίστα αναπαραγωγής είναι άδεια προς το παρόν. Προσθέστε μερικά κομμάτια σε αυτήν και απολαύστε την μουσική! + + HatchetAccountConfig + + + Form + Μορφή + + + + Connect to your Hatchet account + Συνδεθείτε στον Hatchet λογαριασμό σας + + + + One-time +Password + Μία φορά ⏎ +Κωδικός + + + + Username + Όνομα Χρήστη + + + + Hatchet username + Hatchet όνομα Χρήστη + + + + Password: + Κωδικός: + + + + Hatchet password + Hatchet Κωδικός: + + + + (Only if configured) + (Μόνο εάν έχει ρυθμιστεί) + + + + Login + Σύνδεση + + InboxItem @@ -2235,6 +2285,27 @@ connect and stream from you? Εισαγωγή διεύθυνσης Google: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + Σύνδεση + + + + Logged in as: %1 + Σύνδεση ως %1 + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + Συνδεθείτε στο Hatchet λογαριασμό σας + + Tomahawk::Accounts::LastFmAccountFactory @@ -3421,43 +3492,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης @@ -3811,7 +3882,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Συγγνώμη, το φίλτρο «%1» δεν αντιστοίχισε αποτελέσματα. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index aa8d35b51f..e78a008f4c 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -676,6 +676,56 @@ connect and stream from you? This playlist is currently empty. Add some tracks to it and enjoy the music! + + HatchetAccountConfig + + + Form + Form + + + + Connect to your Hatchet account + Connect to your Hatchet account + + + + One-time +Password + One-time +Password + + + + Username + Username + + + + Hatchet username + Hatchet username + + + + Password: + Password: + + + + Hatchet password + Hatchet password + + + + (Only if configured) + (Only if configured) + + + + Login + Login + + InboxItem @@ -2237,6 +2287,27 @@ connect and stream from you? Enter Google Address: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + Login + + + + Logged in as: %1 + Logged in as: %1 + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + Connect to your Hatchet account + + Tomahawk::Accounts::LastFmAccountFactory @@ -3423,43 +3494,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline @@ -3814,7 +3885,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 06cd339072..94aa1735bc 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -676,6 +676,55 @@ conectarse a usted y transmitir música? Lista de reproducción vacía. ¡Añada pistas y disfrute de la música! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2237,6 +2286,27 @@ y estaciones basadas en sus gustos personales. Escriba la dirección de correo de Google: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3422,43 +3492,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado @@ -3813,7 +3883,7 @@ introduzca su número PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. Lo siento, tu filtro '%1' no ha encontrado resultados. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index dc89da435b..2b375c51df 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -676,6 +676,56 @@ yhdistää ja toistaa sinulta virtaa? Tämä soittolista on parhaillaan tyhjä. Lisää kappaleita ja nauti musiikista! + + HatchetAccountConfig + + + Form + Lomake + + + + Connect to your Hatchet account + Yhdistä Hatchet-tiliisi + + + + One-time +Password + Kerta- +salasana + + + + Username + Käyttäjätunnus + + + + Hatchet username + Hatchet-käyttäjätunnus + + + + Password: + Salasana: + + + + Hatchet password + Hatchet-salasana + + + + (Only if configured) + (Vain, jos asetettu) + + + + Login + Kirjaudu + + InboxItem @@ -2240,6 +2290,27 @@ napsauttamalla hiiren oikealla. Anna Google-osoite: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + Kirjaudu + + + + Logged in as: %1 + Kirjauduttu käyttäjänä: %1 + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + Yhdistä Hatchet-tiliisi + + Tomahawk::Accounts::LastFmAccountFactory @@ -3426,43 +3497,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa @@ -3817,7 +3888,7 @@ anna siellä näytetty PIN-koodi tähän: TrackView - + Sorry, your filter '%1' did not match any results. Valitettavasti suodattimesi ”%1” ei tuottanut yhtään tuloksia. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 3fa5e3765a..4742af3659 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -676,6 +676,55 @@ de se connecter et streamer de vous? Cette liste de lecture est vide. Ajoutez des morceaux et profitez de la musique ! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2234,6 +2283,27 @@ de se connecter et streamer de vous? Saisir l'adresse google : + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3419,43 +3489,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne @@ -3810,7 +3880,7 @@ saisissez le numéro PIN ici : TrackView - + Sorry, your filter '%1' did not match any results. Désolé, votre filtre '%1' ne correspond à aucun résultat. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index e78f43552c..438a097d57 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -675,6 +675,55 @@ connect and stream from you? Esta lista de reprodución está baleira. Engádelle algunhas pistas para gozar da música! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2235,6 +2284,27 @@ connect and stream from you? Introducir a dirección de Google: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3421,43 +3491,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado @@ -3812,7 +3882,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. O filtro «%1» non dá ningún resultado. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 9d5178e998..7fd2483c56 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index dd0ab891c7..a4ee5a2778 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? Google cím beírása: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 8f90af9544..77097ae0c7 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 4041b910e1..7808d34610 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -675,6 +675,55 @@ connect and stream from you? Questa playlist al momento è vuota. Aggiungi qualche traccia e goditi la musica + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? Inserisci indirizzo Google + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Spiacente, il tuo filtro %1 non ha trovato nessun risultato. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 7e8ae9e59f..d56de6af66 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -676,6 +676,55 @@ connect and stream from you? プレイリストには何も入っていません。トラックを追加して、音楽を楽しみましょう! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2237,6 +2286,27 @@ connect and stream from you? Googleのアドレスを入力: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3419,43 +3489,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン @@ -3810,7 +3880,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. %1に一致する結果は見つかりませんでした。 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index c118079715..b45a814f44 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? Įveskite Google adresą: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index b89c76bdf6..663d4dc3d6 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -676,6 +676,55 @@ połączyć się i strumieniować od ciebie? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2234,6 +2283,27 @@ połączyć się i strumieniować od ciebie? Podaj Adres Google + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3416,43 +3486,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline @@ -3807,7 +3877,7 @@ wprowadź pokazany numer PIN tutaj: TrackView - + Sorry, your filter '%1' did not match any results. Przepraszamy, twój filtr '%1' nie dopasował żadnych wyników. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 9d0fef9129..d126031e47 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -676,6 +676,55 @@ se conecte e faça o stream de você? Esta lista de reprodução está vazia no momento. Adicione algumas faixas a ela e aproveite a música! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2234,6 +2283,27 @@ se conecte e faça o stream de você? Insira o endereço de email do Google: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3416,43 +3486,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline @@ -3807,7 +3877,7 @@ colocar o número PIN mostrado aqui: TrackView - + Sorry, your filter '%1' did not match any results. Desculpe, o seu filtro '%1' não encontreou nenhum resultado. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index dbbc4bee51..8dbc70ca5a 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -679,6 +679,55 @@ connect and stream from you? Этот плейлист пуст. Добавьте какие-нибудь песни и наслаждайтесь музыкой! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2240,6 +2289,27 @@ connect and stream from you? Введите Google Адрес: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3423,43 +3493,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети @@ -3813,7 +3883,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Ваш поиск '%1' недал результатов. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index d95d19297b..b8ed012010 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -676,6 +676,55 @@ ansluta och strömma från dig? Spellistan är för tillfället tom. Lägg till några låtar och avnjut musiken! + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2236,6 +2285,27 @@ och radiostationer baserat på din personliga profil Ange Google-adress: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3420,43 +3490,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline @@ -3811,7 +3881,7 @@ anger du PIN-koden här: TrackView - + Sorry, your filter '%1' did not match any results. Tyvärr, ditt filter '%1' gav inga träffar diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 46402d03cd..263edf8ff2 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index bfac1a15f0..1ce0086db9 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2235,6 +2284,27 @@ connect and stream from you? 输入 Google 地址: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3417,43 +3487,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 @@ -3808,7 +3878,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. 抱歉,未找到任何匹配 '%1' 的结果。 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index c8420bd47b..f34fcb3cd6 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -675,6 +675,55 @@ connect and stream from you? + + HatchetAccountConfig + + + Form + + + + + Connect to your Hatchet account + + + + + One-time +Password + + + + + Username + + + + + Hatchet username + + + + + Password: + + + + + Hatchet password + + + + + (Only if configured) + + + + + Login + + + InboxItem @@ -2232,6 +2281,27 @@ connect and stream from you? 輸入谷歌地址: + + Tomahawk::Accounts::HatchetAccountConfig + + + Login + + + + + Logged in as: %1 + + + + + Tomahawk::Accounts::HatchetAccountFactory + + + Connect to your Hatchet account + + + Tomahawk::Accounts::LastFmAccountFactory @@ -3407,43 +3477,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3797,7 +3867,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. From a0d988e63ad853dda117fe028767a91a57b29404 Mon Sep 17 00:00:00 2001 From: Florian Richter Date: Fri, 31 May 2013 15:39:14 +0200 Subject: [PATCH 181/565] autogenerate dbus adaptors for mpris --- src/infoplugins/linux/CMakeLists.txt | 8 +- .../linux/mpris/MprisPluginPlayerAdaptor.cpp | 205 ------------------ .../linux/mpris/MprisPluginPlayerAdaptor.h | 140 ------------ .../linux/mpris/MprisPluginRootAdaptor.cpp | 92 -------- .../linux/mpris/MprisPluginRootAdaptor.h | 77 ------- 5 files changed, 6 insertions(+), 516 deletions(-) delete mode 100644 src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.cpp delete mode 100644 src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.h delete mode 100644 src/infoplugins/linux/mpris/MprisPluginRootAdaptor.cpp delete mode 100644 src/infoplugins/linux/mpris/MprisPluginRootAdaptor.h diff --git a/src/infoplugins/linux/CMakeLists.txt b/src/infoplugins/linux/CMakeLists.txt index 6c23ef9806..7ab51bb268 100644 --- a/src/infoplugins/linux/CMakeLists.txt +++ b/src/infoplugins/linux/CMakeLists.txt @@ -18,12 +18,16 @@ if(NOT Qt5Core_DIR) endif() +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ) SET(mpris_srcs - mpris/MprisPluginRootAdaptor.cpp - mpris/MprisPluginPlayerAdaptor.cpp mpris/MprisPlugin.cpp ) +qt4_add_dbus_adaptor(mpris_srcs mpris/MprisPluginRootAdaptor.xml + mpris/MprisPlugin.h Tomahawk::InfoSystem::MprisPlugin MprisPluginRootAdaptor MprisPluginRootAdaptor) +qt4_add_dbus_adaptor(mpris_srcs mpris/MprisPluginPlayerAdaptor.xml + mpris/MprisPlugin.h Tomahawk::InfoSystem::MprisPlugin MprisPluginPlayerAdaptor MprisPluginPlayerAdaptor) + tomahawk_add_plugin(mpris TYPE infoplugin EXPORT_MACRO INFOPLUGINDLLEXPORT_PRO SOURCES "${mpris_srcs}" diff --git a/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.cpp b/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.cpp deleted file mode 100644 index f707ce3a48..0000000000 --- a/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.7 - * Command line was: qdbusxml2cpp -a mprispluginplayeradaptor -c MprisPluginPlayerAdaptor MprisPluginPlayerAdaptor.xml - * - * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - * - * This is an auto-generated file. - * Do not edit! All changes made to it will be lost. - */ - -#include "MprisPluginPlayerAdaptor.h" -#include -#include -#include -#include -#include -#include -#include - -/* - * Implementation of adaptor class MprisPluginPlayerAdaptor - */ - -MprisPluginPlayerAdaptor::MprisPluginPlayerAdaptor(QObject *parent) - : QDBusAbstractAdaptor(parent) -{ - // constructor - setAutoRelaySignals(true); -} - -MprisPluginPlayerAdaptor::~MprisPluginPlayerAdaptor() -{ - // destructor -} - -bool MprisPluginPlayerAdaptor::canControl() const -{ - // get the value of property CanControl - return qvariant_cast< bool >(parent()->property("CanControl")); -} - -bool MprisPluginPlayerAdaptor::canGoNext() const -{ - // get the value of property CanGoNext - return qvariant_cast< bool >(parent()->property("CanGoNext")); -} - -bool MprisPluginPlayerAdaptor::canGoPrevious() const -{ - // get the value of property CanGoPrevious - return qvariant_cast< bool >(parent()->property("CanGoPrevious")); -} - -bool MprisPluginPlayerAdaptor::canPause() const -{ - // get the value of property CanPause - return qvariant_cast< bool >(parent()->property("CanPause")); -} - -bool MprisPluginPlayerAdaptor::canPlay() const -{ - // get the value of property CanPlay - return qvariant_cast< bool >(parent()->property("CanPlay")); -} - -bool MprisPluginPlayerAdaptor::canSeek() const -{ - // get the value of property CanSeek - return qvariant_cast< bool >(parent()->property("CanSeek")); -} - -QString MprisPluginPlayerAdaptor::loopStatus() const -{ - // get the value of property LoopStatus - return qvariant_cast< QString >(parent()->property("LoopStatus")); -} - -void MprisPluginPlayerAdaptor::setLoopStatus(const QString &value) -{ - // set the value of property LoopStatus - parent()->setProperty("LoopStatus", qVariantFromValue(value)); -} - -double MprisPluginPlayerAdaptor::maximumRate() const -{ - // get the value of property MaximumRate - return qvariant_cast< double >(parent()->property("MaximumRate")); -} - -QVariantMap MprisPluginPlayerAdaptor::metadata() const -{ - // get the value of property Metadata - return qvariant_cast< QVariantMap >(parent()->property("Metadata")); -} - -double MprisPluginPlayerAdaptor::minimumRate() const -{ - // get the value of property MinimumRate - return qvariant_cast< double >(parent()->property("MinimumRate")); -} - -QString MprisPluginPlayerAdaptor::playbackStatus() const -{ - // get the value of property PlaybackStatus - return qvariant_cast< QString >(parent()->property("PlaybackStatus")); -} - -qlonglong MprisPluginPlayerAdaptor::position() const -{ - // get the value of property Position - return qvariant_cast< qlonglong >(parent()->property("Position")); -} - -double MprisPluginPlayerAdaptor::rate() const -{ - // get the value of property Rate - return qvariant_cast< double >(parent()->property("Rate")); -} - -void MprisPluginPlayerAdaptor::setRate(double value) -{ - // set the value of property Rate - parent()->setProperty("Rate", qVariantFromValue(value)); -} - -bool MprisPluginPlayerAdaptor::shuffle() const -{ - // get the value of property Shuffle - return qvariant_cast< bool >(parent()->property("Shuffle")); -} - -void MprisPluginPlayerAdaptor::setShuffle(bool value) -{ - // set the value of property Shuffle - parent()->setProperty("Shuffle", qVariantFromValue(value)); -} - -double MprisPluginPlayerAdaptor::volume() const -{ - // get the value of property Volume - return qvariant_cast< double >(parent()->property("Volume")); -} - -void MprisPluginPlayerAdaptor::setVolume(double value) -{ - // set the value of property Volume - parent()->setProperty("Volume", qVariantFromValue(value)); -} - -void MprisPluginPlayerAdaptor::Next() -{ - // handle method call org.mpris.MediaPlayer2.Player.Next - QMetaObject::invokeMethod(parent(), "Next"); -} - -void MprisPluginPlayerAdaptor::OpenUri(const QString &Uri) -{ - // handle method call org.mpris.MediaPlayer2.Player.OpenUri - QMetaObject::invokeMethod(parent(), "OpenUri", Q_ARG(QString, Uri)); -} - -void MprisPluginPlayerAdaptor::Pause() -{ - // handle method call org.mpris.MediaPlayer2.Player.Pause - QMetaObject::invokeMethod(parent(), "Pause"); -} - -void MprisPluginPlayerAdaptor::Play() -{ - // handle method call org.mpris.MediaPlayer2.Player.Play - QMetaObject::invokeMethod(parent(), "Play"); -} - -void MprisPluginPlayerAdaptor::PlayPause() -{ - // handle method call org.mpris.MediaPlayer2.Player.PlayPause - QMetaObject::invokeMethod(parent(), "PlayPause"); -} - -void MprisPluginPlayerAdaptor::Previous() -{ - // handle method call org.mpris.MediaPlayer2.Player.Previous - QMetaObject::invokeMethod(parent(), "Previous"); -} - -void MprisPluginPlayerAdaptor::Seek(qlonglong Offset) -{ - qDebug() << Q_FUNC_INFO; - // handle method call org.mpris.MediaPlayer2.Player.Seek - QMetaObject::invokeMethod(parent(), "Seek", Q_ARG(qlonglong, Offset)); -} - -void MprisPluginPlayerAdaptor::SetPosition(const QDBusObjectPath &TrackId, qlonglong Position) -{ - qDebug() << Q_FUNC_INFO; - // handle method call org.mpris.MediaPlayer2.Player.SetPosition - QMetaObject::invokeMethod(parent(), "SetPosition", Q_ARG(QDBusObjectPath, TrackId), Q_ARG(qlonglong, Position)); -} - -void MprisPluginPlayerAdaptor::Stop() -{ - // handle method call org.mpris.MediaPlayer2.Player.Stop - QMetaObject::invokeMethod(parent(), "Stop"); -} - diff --git a/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.h b/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.h deleted file mode 100644 index ebef4698eb..0000000000 --- a/src/infoplugins/linux/mpris/MprisPluginPlayerAdaptor.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.7 - * Command line was: qdbusxml2cpp -a mprispluginplayeradaptor -c MprisPluginPlayerAdaptor MprisPluginPlayerAdaptor.xml - * - * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - * - * This is an auto-generated file. - * This file may have been hand-edited. Look for HAND-EDIT comments - * before re-generating it. - */ - -#ifndef MPRISPLUGINPLAYERADAPTOR_H_1313089554 -#define MPRISPLUGINPLAYERADAPTOR_H_1313089554 - -#include -#include - -class QByteArray; -template class QList; -template class QMap; -class QString; -class QStringList; -class QVariant; - -/* - * Adaptor class for interface org.mpris.MediaPlayer2.Player - */ -class MprisPluginPlayerAdaptor: public QDBusAbstractAdaptor -{ - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player") - Q_CLASSINFO("D-Bus Introspection", "" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" - "") -public: - MprisPluginPlayerAdaptor(QObject *parent); - virtual ~MprisPluginPlayerAdaptor(); - -public: // PROPERTIES - Q_PROPERTY(bool CanControl READ canControl) - bool canControl() const; - - Q_PROPERTY(bool CanGoNext READ canGoNext) - bool canGoNext() const; - - Q_PROPERTY(bool CanGoPrevious READ canGoPrevious) - bool canGoPrevious() const; - - Q_PROPERTY(bool CanPause READ canPause) - bool canPause() const; - - Q_PROPERTY(bool CanPlay READ canPlay) - bool canPlay() const; - - Q_PROPERTY(bool CanSeek READ canSeek) - bool canSeek() const; - - Q_PROPERTY(QString LoopStatus READ loopStatus WRITE setLoopStatus) - QString loopStatus() const; - void setLoopStatus(const QString &value); - - Q_PROPERTY(double MaximumRate READ maximumRate) - double maximumRate() const; - - Q_PROPERTY(QVariantMap Metadata READ metadata) - QVariantMap metadata() const; - - Q_PROPERTY(double MinimumRate READ minimumRate) - double minimumRate() const; - - Q_PROPERTY(QString PlaybackStatus READ playbackStatus) - QString playbackStatus() const; - - Q_PROPERTY(qlonglong Position READ position) - qlonglong position() const; - - Q_PROPERTY(double Rate READ rate WRITE setRate) - double rate() const; - void setRate(double value); - - Q_PROPERTY(bool Shuffle READ shuffle WRITE setShuffle) - bool shuffle() const; - void setShuffle(bool value); - - Q_PROPERTY(double Volume READ volume WRITE setVolume) - double volume() const; - void setVolume(double value); - -public Q_SLOTS: // METHODS - void Next(); - void OpenUri(const QString &Uri); - void Pause(); - void Play(); - void PlayPause(); - void Previous(); - void Seek(qlonglong Offset); - void SetPosition(const QDBusObjectPath &TrackId, qlonglong Position); - void Stop(); -Q_SIGNALS: // SIGNALS - void Seeked(qlonglong Position); -}; - -#endif diff --git a/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.cpp b/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.cpp deleted file mode 100644 index 38caea346c..0000000000 --- a/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.7 - * Command line was: qdbusxml2cpp -a mprispluginrootadaptor -c MprisPluginRootAdaptor MprisPluginRootAdaptor.xml - * - * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - * - * This is an auto-generated file. - * Do not edit! All changes made to it will be lost. - */ - -#include "MprisPluginRootAdaptor.h" -#include -#include -#include -#include -#include -#include -#include - -/* - * Implementation of adaptor class MprisPluginRootAdaptor - */ - -MprisPluginRootAdaptor::MprisPluginRootAdaptor(QObject *parent) - : QDBusAbstractAdaptor(parent) -{ - // constructor - setAutoRelaySignals(true); -} - -MprisPluginRootAdaptor::~MprisPluginRootAdaptor() -{ - // destructor -} - -bool MprisPluginRootAdaptor::canQuit() const -{ - // get the value of property CanQuit - return qvariant_cast< bool >(parent()->property("CanQuit")); -} - -bool MprisPluginRootAdaptor::canRaise() const -{ - qDebug() << Q_FUNC_INFO; - // get the value of property CanRaise - bool ret = qvariant_cast< bool >(parent()->property("CanRaise")); - qDebug() << "ret: " << ret; - return ret; -} - -QString MprisPluginRootAdaptor::desktopEntry() const -{ - // get the value of property DesktopEntry - return qvariant_cast< QString >(parent()->property("DesktopEntry")); -} - -bool MprisPluginRootAdaptor::hasTrackList() const -{ - // get the value of property HasTrackList - return qvariant_cast< bool >(parent()->property("HasTrackList")); -} - -QString MprisPluginRootAdaptor::identity() const -{ - // get the value of property Identity - return qvariant_cast< QString >(parent()->property("Identity")); -} - -QStringList MprisPluginRootAdaptor::supportedMimeTypes() const -{ - // get the value of property SupportedMimeTypes - return qvariant_cast< QStringList >(parent()->property("SupportedMimeTypes")); -} - -QStringList MprisPluginRootAdaptor::supportedUriSchemes() const -{ - // get the value of property SupportedUriSchemes - return qvariant_cast< QStringList >(parent()->property("SupportedUriSchemes")); -} - -void MprisPluginRootAdaptor::Quit() -{ - // handle method call org.mpris.MediaPlayer2.Quit - QMetaObject::invokeMethod(parent(), "Quit"); -} - -void MprisPluginRootAdaptor::Raise() -{ - // handle method call org.mpris.MediaPlayer2.Raise - QMetaObject::invokeMethod(parent(), "Raise"); -} - diff --git a/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.h b/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.h deleted file mode 100644 index a875f00650..0000000000 --- a/src/infoplugins/linux/mpris/MprisPluginRootAdaptor.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file was generated by qdbusxml2cpp version 0.7 - * Command line was: qdbusxml2cpp -a mprispluginrootadaptor -c MprisPluginRootAdaptor MprisPluginRootAdaptor.xml - * - * qdbusxml2cpp is Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - * - * This is an auto-generated file. - * This file may have been hand-edited. Look for HAND-EDIT comments - * before re-generating it. - */ - -#ifndef MPRISPLUGINROOTADAPTOR_H_1312900930 -#define MPRISPLUGINROOTADAPTOR_H_1312900930 - -#include -#include - -class QByteArray; -template class QList; -template class QMap; -class QString; -class QStringList; -class QVariant; - -/* - * Adaptor class for interface org.mpris.MediaPlayer2 - */ -class MprisPluginRootAdaptor: public QDBusAbstractAdaptor -{ - Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2") - Q_CLASSINFO("D-Bus Introspection", "" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" - "") -public: - MprisPluginRootAdaptor(QObject *parent); - virtual ~MprisPluginRootAdaptor(); - -public: // PROPERTIES - Q_PROPERTY(bool CanQuit READ canQuit) - bool canQuit() const; - - Q_PROPERTY(bool CanRaise READ canRaise) - bool canRaise() const; - - Q_PROPERTY(QString DesktopEntry READ desktopEntry) - QString desktopEntry() const; - - Q_PROPERTY(bool HasTrackList READ hasTrackList) - bool hasTrackList() const; - - Q_PROPERTY(QString Identity READ identity) - QString identity() const; - - Q_PROPERTY(QStringList SupportedMimeTypes READ supportedMimeTypes) - QStringList supportedMimeTypes() const; - - Q_PROPERTY(QStringList SupportedUriSchemes READ supportedUriSchemes) - QStringList supportedUriSchemes() const; - -public Q_SLOTS: // METHODS - void Quit(); - void Raise(); -Q_SIGNALS: // SIGNALS -}; - -#endif From 701c5ff61cf3b82b6498945182d79eedeaca2125 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 31 May 2013 15:56:16 +0200 Subject: [PATCH 182/565] Add SIP improvements to ChangeLog --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 45eabb1384..557f1594ac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,8 @@ Version 0.8.0: * Added "Inbox" feature, showing incoming song recommendations. Dropping a track on a user in the sidebar sends a recommendation to them. * You will now be asked whether you want to trust invalid SSL certificates. + * Improve connecting between Tomahawk peers and support having multiple IPs + (including IPv6) Version 0.7.1: * Heavily reduced memory footprint during and after indexing the database. From a6113204d2732a015a12b76f2ef6594523b5b711 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 31 May 2013 16:57:42 +0300 Subject: [PATCH 183/565] Fix time in spelling --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 557f1594ac..b8fb6973ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,7 +3,7 @@ Version 0.8.0: * Added "Inbox" feature, showing incoming song recommendations. Dropping a track on a user in the sidebar sends a recommendation to them. * You will now be asked whether you want to trust invalid SSL certificates. - * Improve connecting between Tomahawk peers and support having multiple IPs + * Improved connecting between Tomahawk peers and support having multiple IPs (including IPv6) Version 0.7.1: From c79a671802a0e4d0e260ec5b82939c36e1593323 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 31 May 2013 10:05:50 -0400 Subject: [PATCH 184/565] Add Network Activity icon for sidebar --- data/images/network-activity.svg | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 data/images/network-activity.svg diff --git a/data/images/network-activity.svg b/data/images/network-activity.svg new file mode 100644 index 0000000000..bdbd895455 --- /dev/null +++ b/data/images/network-activity.svg @@ -0,0 +1,49 @@ + + + Network Activity + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From d4d45220d16ac567cfbd0ec407e7aa650c19ff60 Mon Sep 17 00:00:00 2001 From: Florian Richter Date: Fri, 31 May 2013 16:43:29 +0200 Subject: [PATCH 185/565] use autogenerated dbus interface for fdonotify --- src/infoplugins/linux/CMakeLists.txt | 2 + .../linux/fdonotify/FdoNotifyPlugin.cpp | 136 +++++++++--------- .../linux/fdonotify/FdoNotifyPlugin.h | 8 +- .../org.freedesktop.Notifications.xml | 38 +++++ 4 files changed, 112 insertions(+), 72 deletions(-) create mode 100644 src/infoplugins/linux/fdonotify/org.freedesktop.Notifications.xml diff --git a/src/infoplugins/linux/CMakeLists.txt b/src/infoplugins/linux/CMakeLists.txt index 7ab51bb268..7e02fa2889 100644 --- a/src/infoplugins/linux/CMakeLists.txt +++ b/src/infoplugins/linux/CMakeLists.txt @@ -9,6 +9,8 @@ if(NOT Qt5Core_DIR) ${THIRDPARTY_DIR}/libqnetwm/libqnetwm/netwm.cpp ) SET(FDO_LINK_LIBRARIES ${LINK_LIBRARIES} ${X11_LIBRARIES}) + qt4_add_dbus_interface(fdo_srcs fdonotify/org.freedesktop.Notifications.xml + FreedesktopNotificationsProxy) tomahawk_add_plugin(fdonotify TYPE infoplugin EXPORT_MACRO INFOPLUGINDLLEXPORT_PRO diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp index 425af986e7..80d1044ae9 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp @@ -40,6 +40,7 @@ #include "FdoNotifyPlugin.h" #include "utils/TomahawkUtils.h" #include "ImageConverter.h" +#include "FreedesktopNotificationsProxy.h" #include "TomahawkSettings.h" @@ -66,8 +67,12 @@ FdoNotifyPlugin::FdoNotifyPlugin() m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped << InfoInboxReceived; // Query the window manager for its capabilties in styling notifications. - QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "GetCapabilities" ); - QDBusConnection::sessionBus().callWithCallback( message, this, SLOT( dbusCapabiltiesReplyReceived( QDBusMessage ) ) ); + notifications_interface = new org::freedesktop::Notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", + QDBusConnection::sessionBus(), this); + QDBusPendingReply reply = notifications_interface->GetCapabilities(); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(dbusCapabilitiesReplyReceived(QDBusPendingCallWatcher*))); } @@ -78,24 +83,17 @@ FdoNotifyPlugin::~FdoNotifyPlugin() void -FdoNotifyPlugin::dbusCapabiltiesReplyReceived( const QDBusMessage& reply ) +FdoNotifyPlugin::dbusCapabilitiesReplyReceived( QDBusPendingCallWatcher* watcher ) { - if ( reply.type() != QDBusMessage::ReplyMessage ) - { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Did not receive a ReplyMessage"; - } + QDBusMessage reply = watcher->reply(); + watcher->deleteLater(); - const QStringList &list = reply.arguments().at( 0 ).toStringList(); - QListIterator iter( list ); - while ( iter.hasNext() ) - { - QString capabilty = iter.next(); - if ( capabilty.compare( "body-markup" ) == 0 ) - { - m_wmSupportsBodyMarkup = true; - break; - } + if (reply.type() == QDBusMessage::ErrorMessage) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Failed to request capabilities of notifications"; } + + const QStringList &capability_list = reply.arguments().at( 0 ).toStringList(); + m_wmSupportsBodyMarkup = capability_list.contains("body-markup"); } @@ -151,23 +149,19 @@ FdoNotifyPlugin::getNotificationIconHeight() void FdoNotifyPlugin::notifyUser( const QString& messageText ) { - QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify" ); - - QList arguments; - arguments << QString( "Tomahawk" ); //app_name - arguments << quint32( 0 ); //notification_id - arguments << QString(); //app_icon - arguments << QString( "Tomahawk" ); //summary - arguments << QString( messageText ); //body - arguments << QStringList(); //actions - - QVariantMap dict; - dict["desktop-entry"] = QString( "tomahawk" ); - dict[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "icons/tomahawk-icon-512x512.png" ).scaledToHeight( getNotificationIconHeight() ) ); - arguments << dict; //hints - arguments << qint32( -1 ); //expire_timeout - message.setArguments( arguments ); - QDBusConnection::sessionBus().send( message ); + QVariantMap hints; + hints["desktop-entry"] = QString( "tomahawk" ); + hints[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "icons/tomahawk-icon-512x512.png" ).scaledToHeight( getNotificationIconHeight() ) ); + notifications_interface->Notify( + "Tomahawk", // app_name + 0, // notification_id + "", // app_icon + "Tomahawk", // summary + messageText, // body + QStringList(), // actions + hints, // hints + -1 // expire_timeout + ); } void FdoNotifyPlugin::inboxReceived(const QVariant &input) @@ -216,26 +210,21 @@ void FdoNotifyPlugin::inboxReceived(const QVariant &input) tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "sending message" << messageText; - QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify" ); - QList arguments; - arguments << QString( "Tomahawk" ); //app_name - arguments << m_nowPlayingId; //notification_id - arguments << QString(); //app_icon - arguments << QString( "Tomahawk - Track received" ); //summary - arguments << messageText; //body - arguments << QStringList(); //actions - QVariantMap dict; - dict["desktop-entry"] = QString( "tomahawk" ); + QVariantMap hints; + hints["desktop-entry"] = QString( "tomahawk" ); // Convert image to QVariant and scale to a consistent size. - dict[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "images/inbox-512x512.png" ).scaledToHeight( getNotificationIconHeight() ) ); - - arguments << dict; //hints - arguments << qint32( -1 ); //expire_timeout - message.setArguments( arguments ); - - // Handle reply in a callback, so that this a non-blocking call - QDBusConnection::sessionBus().send( message ); + hints[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "images/inbox-512x512.png" ).scaledToHeight( getNotificationIconHeight() ) ); + notifications_interface->Notify( + "Tomahawk", // app_name + m_nowPlayingId, // notification_id + "", // app_icon + "Tomahawk - Track received", // summary + messageText, // body + QStringList(), // actions + hints, // hints + -1 // expire_timeout + ); } void @@ -286,16 +275,8 @@ FdoNotifyPlugin::nowPlaying( const QVariant& input ) tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "sending message" << messageText; - QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify" ); - QList arguments; - arguments << QString( "Tomahawk" ); //app_name - arguments << m_nowPlayingId; //notification_id - arguments << QString(); //app_icon - arguments << QString( "Tomahawk - Now Playing" ); //summary - arguments << messageText; //body - arguments << QStringList(); //actions - QVariantMap dict; - dict["desktop-entry"] = QString( "tomahawk" ); + QVariantMap hints; + hints["desktop-entry"] = QString( "tomahawk" ); // If there is a cover availble use it, else use Tomahawk logo as default. QImage image; @@ -304,14 +285,24 @@ FdoNotifyPlugin::nowPlaying( const QVariant& input ) else image = QImage( RESPATH "icons/tomahawk-icon-512x512.png" ); // Convert image to QVariant and scale to a consistent size. - dict[ "image_data" ] = ImageConverter::variantForImage( image.scaledToHeight( getNotificationIconHeight() ) ); + hints[ "image_data" ] = ImageConverter::variantForImage( image.scaledToHeight( getNotificationIconHeight() ) ); + + + QDBusPendingReply<> reply = notifications_interface->Notify( + "Tomahawk", // app_name + m_nowPlayingId, // notification_id + "", // app_icon + "Tomahawk - Now Playing", // summary + messageText, // body + QStringList(), // actions + hints, // hints + -1 // expire_timeout + ); - arguments << dict; //hints - arguments << qint32( -1 ); //expire_timeout - message.setArguments( arguments ); + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(RegisterFinished(dbusPlayingReplyReceived*))); - // Handle reply in a callback, so that this a non-blocking call - QDBusConnection::sessionBus().callWithCallback( message, this, SLOT( dbusPlayingReplyReceived( QDBusMessage ) ) ); } @@ -319,8 +310,15 @@ FdoNotifyPlugin::nowPlaying( const QVariant& input ) * Handle the DBus reply triggered by FdoNotifyPlugin::nowPlaying */ void -FdoNotifyPlugin::dbusPlayingReplyReceived( const QDBusMessage& reply ) +FdoNotifyPlugin::dbusPlayingReplyReceived( QDBusPendingCallWatcher* watcher ) { + QDBusMessage reply = watcher->reply(); + watcher->deleteLater(); + if (reply.type() == QDBusMessage::ErrorMessage) { + tLog(LOGVERBOSE) << "Failed to grab media keys" << reply.errorName() << reply.errorMessage(); + return; + } + const QVariantList& list = reply.arguments(); if ( list.count() > 0 ) m_nowPlayingId = list.at( 0 ).toInt(); diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h index 32003633f5..cd27ee6b84 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h @@ -23,8 +23,9 @@ #include "../../InfoPluginDllMacro.h" #include "infosystem/InfoSystem.h" +#include "FreedesktopNotificationsProxy.h" -#include +#include namespace Tomahawk { @@ -45,8 +46,8 @@ class INFOPLUGINDLLEXPORT FdoNotifyPlugin : public InfoPlugin protected slots: virtual void init() {} - virtual void dbusPlayingReplyReceived( const QDBusMessage& reply ); - virtual void dbusCapabiltiesReplyReceived( const QDBusMessage& reply ); + virtual void dbusPlayingReplyReceived( QDBusPendingCallWatcher* watcher ); + virtual void dbusCapabilitiesReplyReceived( QDBusPendingCallWatcher* watcher ); virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { @@ -72,6 +73,7 @@ protected slots: // Does the window manger support basic XML-based markup (a small HTML subset), see Desktop Notifications specification bool m_wmSupportsBodyMarkup; + org::freedesktop::Notifications* notifications_interface; }; } diff --git a/src/infoplugins/linux/fdonotify/org.freedesktop.Notifications.xml b/src/infoplugins/linux/fdonotify/org.freedesktop.Notifications.xml new file mode 100644 index 0000000000..bee75d68cf --- /dev/null +++ b/src/infoplugins/linux/fdonotify/org.freedesktop.Notifications.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 830d2c9391ce90a8b6924ffc48a987aec511ae35 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 31 May 2013 19:07:33 +0200 Subject: [PATCH 186/565] Add support for UPower --- CMakeLists.txt | 5 + src/tomahawk/CMakeLists.linux.cmake | 5 + src/tomahawk/Config.h.in | 1 + src/tomahawk/TomahawkApp.cpp | 14 + src/tomahawk/TomahawkApp.h | 1 + src/tomahawk/linux/CMakeLists.txt | 3 + src/tomahawk/linux/UPowerHandler.cpp | 67 +++ src/tomahawk/linux/UPowerHandler.h | 51 +++ src/tomahawk/linux/org.freedesktop.UPower.xml | 395 ++++++++++++++++++ 9 files changed, 542 insertions(+) create mode 100644 src/tomahawk/linux/CMakeLists.txt create mode 100644 src/tomahawk/linux/UPowerHandler.cpp create mode 100644 src/tomahawk/linux/UPowerHandler.h create mode 100644 src/tomahawk/linux/org.freedesktop.UPower.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index c42abb444a..4c005fbc43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ option(WITH_BREAKPAD "Build with breakpad integration" ON) option(WITH_CRASHREPORTER "Build with CrashReporter" ON) option(WITH_BINARY_ATTICA "Enable support for downloading binary resolvers automatically" ON) option(LEGACY_KDE_INTEGRATION "Install tomahawk.protocol file, deprecated since 4.6.0" OFF) +OPTION(WITH_UPOWER "Build with support for UPower events" OFF) IF( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" ) message(STATUS "Build of breakpad library disabled on this platform.") @@ -187,6 +188,10 @@ if(BUILD_GUI AND UNIX AND NOT APPLE) find_package( X11 ) endif() +IF( UNIX AND NOT APPLE AND QT_QTDBUS_FOUND ) + SET( WITH_UPOWER ON ) +ENDIF( UNIX AND NOT APPLE AND QT_QTDBUS_FOUND ) + macro_optional_find_package(Phonon 4.5.0) macro_log_feature(PHONON_FOUND "Phonon" "The Phonon multimedia library" "http://phonon.kde.org" TRUE "" "") if(PHONON_FOUND) diff --git a/src/tomahawk/CMakeLists.linux.cmake b/src/tomahawk/CMakeLists.linux.cmake index e3e207fa1c..30f0ee538b 100644 --- a/src/tomahawk/CMakeLists.linux.cmake +++ b/src/tomahawk/CMakeLists.linux.cmake @@ -5,6 +5,11 @@ FOREACH( _file ${_icons} ) INSTALL( FILES ${_file} RENAME tomahawk.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${_res}/apps ) ENDFOREACH( _file ) +IF( WITH_UPOWER ) + QT4_ADD_DBUS_INTERFACE(tomahawkSources "${CMAKE_CURRENT_SOURCE_DIR}/linux/org.freedesktop.UPower.xml" linux/UPowerProxy) +ENDIF( WITH_UPOWER ) +ADD_SUBDIRECTORY( "${CMAKE_CURRENT_SOURCE_DIR}/linux" ) + INSTALL( FILES ${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon.svg RENAME tomahawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps ) INSTALL( FILES ${CMAKE_SOURCE_DIR}/admin/unix/tomahawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications ) diff --git a/src/tomahawk/Config.h.in b/src/tomahawk/Config.h.in index 79e5bad39d..33effc143a 100644 --- a/src/tomahawk/Config.h.in +++ b/src/tomahawk/Config.h.in @@ -18,6 +18,7 @@ #cmakedefine WITH_CRASHREPORTER #cmakedefine WITH_BINARY_ATTICA #cmakedefine WITH_QtSparkle +#cmakedefine WITH_UPOWER #cmakedefine LIBLASTFM_FOUND diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index 13765e795d..ad5bae8a6d 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -79,6 +79,10 @@ #include "config.h" +#ifdef WITH_UPOWER + #include "linux/UPowerHandler.h" +#endif + #ifndef ENABLE_HEADLESS #include #endif @@ -148,6 +152,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) setOrganizationDomain( QLatin1String( TOMAHAWK_ORGANIZATION_DOMAIN ) ); setApplicationName( QLatin1String( TOMAHAWK_APPLICATION_NAME ) ); setApplicationVersion( QLatin1String( TOMAHAWK_VERSION ) ); + connect( this, SIGNAL( tomahawkLoaded() ), SLOT( initEnergyEventHandler() ) ); registerMetaTypes(); TomahawkUtils::installTranslator( this ); @@ -633,6 +638,15 @@ TomahawkApp::initFactoriesForAccountManager() Tomahawk::Accounts::AccountManager::instance()->loadFromConfig(); } +void +TomahawkApp::initEnergyEventHandler() +{ +#ifdef WITH_UPOWER + UPowerHandler* upower = new UPowerHandler( this ); + upower->registerHandler(); +#endif // WITH_UPOWER +} + // This method will be called twice during Tomahawk startup. // We don't know which is going to be ready first, AccountManager or Servent, but this goes through diff --git a/src/tomahawk/TomahawkApp.h b/src/tomahawk/TomahawkApp.h index 761e63fdb9..aca7eb1a78 100644 --- a/src/tomahawk/TomahawkApp.h +++ b/src/tomahawk/TomahawkApp.h @@ -112,6 +112,7 @@ private slots: void initSIP(); void initHTTP(); void initFactoriesForAccountManager(); + void initEnergyEventHandler(); void spotifyApiCheckFinished(); diff --git a/src/tomahawk/linux/CMakeLists.txt b/src/tomahawk/linux/CMakeLists.txt new file mode 100644 index 0000000000..29a7d697d9 --- /dev/null +++ b/src/tomahawk/linux/CMakeLists.txt @@ -0,0 +1,3 @@ +IF( WITH_UPOWER ) + SET( tomahawkSources ${tomahawkSources} "${CMAKE_CURRENT_SOURCE_DIR}/UPowerHandler.cpp" PARENT_SCOPE ) +ENDIF( WITH_UPOWER ) diff --git a/src/tomahawk/linux/UPowerHandler.cpp b/src/tomahawk/linux/UPowerHandler.cpp new file mode 100644 index 0000000000..bee306afe0 --- /dev/null +++ b/src/tomahawk/linux/UPowerHandler.cpp @@ -0,0 +1,67 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "accounts/AccountManager.h" +#include "UPowerHandler.h" +#include "utils/Logger.h" + +using namespace Tomahawk; + +const char* UPowerHandler::UPowerService = "org.freedesktop.UPower"; +const char* UPowerHandler::UPowerPath = "/org/freedesktop/UPower"; +const char* UPowerHandler::UPowerInterface = "org.freedesktop.UPower"; + +Tomahawk::UPowerHandler::UPowerHandler( QObject *parent ) + : QObject( parent ) +{ +} + +bool +UPowerHandler::registerHandler() +{ + // Check if the UPower is available + if ( !QDBusConnection::systemBus().interface()->isServiceRegistered( UPowerService ) ) { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "UPower is not available"; + return false; + } + + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "UPower available, will reconnect on wake from suspend."; + if ( m_interface.isNull() ) + { + m_interface = QSharedPointer( new org::freedesktop::UPower( UPowerService, UPowerPath, QDBusConnection::systemBus(), this ) ); + } + connect( m_interface.data(), SIGNAL( Sleeping() ), this, SLOT( handleSleep() )); + connect( m_interface.data(), SIGNAL( Resuming() ), this, SLOT( handleResume() )); + return true; +} + +void +UPowerHandler::handleSleep() +{ + QMutexLocker locker( &m_mutex ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "About to sleep so disconnecting all accounts"; + Tomahawk::Accounts::AccountManager::instance()->disconnectAll(); +} + +void +UPowerHandler::handleResume() +{ + QMutexLocker locker( &m_mutex ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Awake from sleep so connecting all accounts"; + Tomahawk::Accounts::AccountManager::instance()->connectAll(); +} diff --git a/src/tomahawk/linux/UPowerHandler.h b/src/tomahawk/linux/UPowerHandler.h new file mode 100644 index 0000000000..6ffd64f71e --- /dev/null +++ b/src/tomahawk/linux/UPowerHandler.h @@ -0,0 +1,51 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef UPOWERHANDLER_H +#define UPOWERHANDLER_H + +#include "linux/UPowerProxy.h" + +#include +#include +#include + +namespace Tomahawk { + +class UPowerHandler : public QObject +{ + Q_OBJECT +public: + explicit UPowerHandler( QObject *parent = 0 ); + bool registerHandler(); + + static const char* UPowerService; + static const char* UPowerPath; + static const char* UPowerInterface; +private: + QSharedPointer m_interface; + QMutex m_mutex; + +private slots: + void handleSleep(); + void handleResume(); +}; + +} + +#endif // UPOWERHANDLER_H diff --git a/src/tomahawk/linux/org.freedesktop.UPower.xml b/src/tomahawk/linux/org.freedesktop.UPower.xml new file mode 100644 index 0000000000..9582ab5df0 --- /dev/null +++ b/src/tomahawk/linux/org.freedesktop.UPower.xml @@ -0,0 +1,395 @@ + + + + + + + + The UPower service is available via the system message + bus. To access the service, use + the org.freedesktop.UPower interface on + the /org/freedesktop/UPower object on + the D-Bus system bus service with the well-known + name org.freedesktop.UPower. + + + + +$ dbus-send --print-reply \ + --system \ + --dest=org.freedesktop.UPower \ + /org/freedesktop/UPower \ + org.freedesktop.UPower.EnumerateDevices + +method return sender=:1.386 -> dest=:1.451 reply_serial=2 + array [ + object path "/org/freedesktop/UPower/devices/line_power_AC" + object path "/org/freedesktop/UPower/devices/battery_BAT0" + ] + + + + + + + + + + + + An array of object paths for devices. + + + + + + Enumerate all power objects on the system. + + + + + + + + + + Object path of device that was added. + + + + + + Emitted when a device is added. + + + + + + + + + + Object path of device that was removed. + + + + + + Emitted when a device is removed. + + + + + + + + + + Object path of device that was changed. + + + + + + Emitted when a device changed. + + + + + + + + + + + + Emitted when one or more properties on the object changes. + + + + + + + + + + + + This signal is sent when the session is about to be suspended or + hibernated. + + + This signal is DEPRECATED. Use NotifySleep() instead. + + + + + + + + + + + + This signal is sent when the session is about to be suspended or + hibernated. + Session and system programs have one second to do anything required + before the sleep action is taken (such as sending out Avahi or + Jabber messages). + + + + + + + The sleep action type, e.g. suspend, + hibernate or hybrid. + + + + + + + + + + + + This signal is sent when the session has just returned from + Suspend() or Hibernate(). + + + This signal is DEPRECATED. Use NotifyResume() instead. + + + + + + + + + + + + This signal is sent when the session has just returned from + Suspend() or Hibernate(). + Session and system programs can then do anything required (such as + sending out Avahi or Jabber messages). + + + + + + + The sleep action type, e.g. suspend, + hibernate or hybrid. + + + + + + + + + + + + + This method tells UPower that the Suspend() or Hibernate() method + is about to be called. + This allows UPower to emit the Suspending signal whilst + session activities are happening that have to be done before the + suspend process is started. + + + This method would typically be called by the session power + management daemon, before it locks the screen and waits for the + screen to fade to black. + The session power management component would then call Suspend() or + Hibernate() when these syncronous tasks have completed. + + + If this method is not called than nothing bad will happen and + Suspend() or Hibernate() will block for the required second. + + + + + + + The sleep action type, e.g. suspend or + hibernate. + + + + + + + + + + + + + Suspends the computer into a low power state. + System state is not preserved if the power is lost. + + + If AboutToSleep() has not been called then UPower will send + the Sleeping() signal and block for one second. + + + If AboutToSleep() has been called less than one second + before this method is called then UPower will block for the + remaining time to complete one second of delay. + + + + + + + + + + + TRUE if allowed, otherwise FALSE + + + + + Check if the caller has (or can get) the PolicyKit privilege to call + Suspend. + + + + + + + + + + + + + Hibernates the computer into a low power state. + System state is preserved if the power is lost. + + + If AboutToSleep() has not been called then UPower will send + the Sleeping() signal and block for one second. + + + If AboutToSleep() has been called less than one second + before this method is called then UPower will block for the + remaining time to complete one second of delay. + + + + + + + + + + + TRUE if allowed, otherwise FALSE + + + + + Check if the caller has (or can get) the PolicyKit privilege to call + Hibernate. + + + + + + + + + + Version of the running daemon, e.g. 002. + + + + + + Whether the system is able to suspend. + + + + + + Whether the system is able to hibernate. + + + + + + Indicates whether the system is running on battery power. + This property is provided for convenience. + + + + + + Indicates whether the system is running on battery power and if the battery is critically low. + This property is provided for convenience. + + + + + + + + Indicates if the laptop lid is closed where the display cannot be seen. + + + + + + + + + + If the system has a lid device. + + + + + + + + + + If the system really has to sleep when the lid is closed. + Some laptops actually melt (!) if the lid is closed and the + computer keeps running. We blacklist those, and do something + sane for the other machines. + + + This allows us to set the default session policy to not + suspend on lid close if the laptop is docked, and be sure + the machine is not going to melt. + + + + + + + + + + If the system is currently docked. + Note: the "is-docked" value is the result of a heuristic, + which may involve testing the display output. + + + + + + + + From cfb04bb1d7bfb16732a4b787b924a2baa9273117 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 31 May 2013 22:06:35 +0200 Subject: [PATCH 187/565] Delay resuming as we will most likely fail connecting directly --- src/tomahawk/linux/UPowerHandler.cpp | 14 +++++++++++++- src/tomahawk/linux/UPowerHandler.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/tomahawk/linux/UPowerHandler.cpp b/src/tomahawk/linux/UPowerHandler.cpp index bee306afe0..e5a74c0c80 100644 --- a/src/tomahawk/linux/UPowerHandler.cpp +++ b/src/tomahawk/linux/UPowerHandler.cpp @@ -20,6 +20,10 @@ #include "UPowerHandler.h" #include "utils/Logger.h" +#include + +#define UPOWER_RESUME_DELAY 2000 + using namespace Tomahawk; const char* UPowerHandler::UPowerService = "org.freedesktop.UPower"; @@ -61,7 +65,15 @@ UPowerHandler::handleSleep() void UPowerHandler::handleResume() { - QMutexLocker locker( &m_mutex ); + m_mutex.lock(); + // Delay resuming for other wakeup actions, e.g. reconnecting to the network, to take place. + QTimer::singleShot( UPOWER_RESUME_DELAY, this, SLOT( actualResume() ) ); +} + +void +UPowerHandler::actualResume() +{ tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Awake from sleep so connecting all accounts"; Tomahawk::Accounts::AccountManager::instance()->connectAll(); + m_mutex.unlock(); } diff --git a/src/tomahawk/linux/UPowerHandler.h b/src/tomahawk/linux/UPowerHandler.h index 6ffd64f71e..9ee501edf3 100644 --- a/src/tomahawk/linux/UPowerHandler.h +++ b/src/tomahawk/linux/UPowerHandler.h @@ -44,6 +44,7 @@ class UPowerHandler : public QObject private slots: void handleSleep(); void handleResume(); + void actualResume(); }; } From 062addd7e84dacb80a00964f2c8ea4e27fee6905 Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Sat, 1 Jun 2013 00:03:00 +0200 Subject: [PATCH 188/565] Fix some compiler/runtime warnings --- src/libtomahawk/Result.cpp | 2 +- src/libtomahawk/playlist/GridView.cpp | 3 --- src/libtomahawk/utils/TomahawkUtils.cpp | 2 -- src/libtomahawk/widgets/WelcomeWidget.cpp | 2 -- src/tomahawk/AclRegistryImpl.h | 3 --- 5 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/libtomahawk/Result.cpp b/src/libtomahawk/Result.cpp index b3d959059f..32691a3aab 100644 --- a/src/libtomahawk/Result.cpp +++ b/src/libtomahawk/Result.cpp @@ -85,12 +85,12 @@ Result::isCached( const QString& url ) Result::Result( const QString& url ) : QObject() , m_url( url ) + , m_checked( false ) , m_bitrate( 0 ) , m_size( 0 ) , m_modtime( 0 ) , m_score( 0 ) , m_fileId( 0 ) - , m_checked( false ) { connect( Pipeline::instance(), SIGNAL( resolverRemoved( Tomahawk::Resolver* ) ), SLOT( onResolverRemoved( Tomahawk::Resolver* ) ), Qt::QueuedConnection ); } diff --git a/src/libtomahawk/playlist/GridView.cpp b/src/libtomahawk/playlist/GridView.cpp index b122c2143e..7e0fda64b3 100644 --- a/src/libtomahawk/playlist/GridView.cpp +++ b/src/libtomahawk/playlist/GridView.cpp @@ -419,6 +419,3 @@ GridView::setPlaylistInterface( const Tomahawk::playlistinterface_ptr& playlistI { proxyModel()->setPlaylistInterface( playlistInterface ); } - - -#include "GridView.moc" diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index e4d3ddebc8..f4d1e0857d 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -1120,5 +1120,3 @@ urlSetQuery( QUrl& url, const QString& query ) } // ns - -#include "TomahawkUtils.moc" diff --git a/src/libtomahawk/widgets/WelcomeWidget.cpp b/src/libtomahawk/widgets/WelcomeWidget.cpp index 9f937f19dc..708d4ccdcb 100644 --- a/src/libtomahawk/widgets/WelcomeWidget.cpp +++ b/src/libtomahawk/widgets/WelcomeWidget.cpp @@ -356,5 +356,3 @@ PlaylistWidget::setModel( QAbstractItemModel* model ) QListView::setModel( model ); emit modelChanged(); } - -#include "WelcomeWidget.moc" diff --git a/src/tomahawk/AclRegistryImpl.h b/src/tomahawk/AclRegistryImpl.h index 96b808202d..d0a9037bcf 100644 --- a/src/tomahawk/AclRegistryImpl.h +++ b/src/tomahawk/AclRegistryImpl.h @@ -44,9 +44,6 @@ class ACLRegistryImpl : public ACLRegistry ACLRegistryImpl( QObject *parent = 0 ); virtual ~ACLRegistryImpl(); -signals: - void aclResult( QString nodeid, QString username, ACLRegistry::ACL peerStatus ); - public slots: /** * @brief Checks if peer is authorized; optionally, can authorize peer with given type if not found From f300b4f617ac75796bb8fdd2b4b65b547729bbb7 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sat, 1 Jun 2013 02:16:46 +0200 Subject: [PATCH 189/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 2 +- lang/tomahawk_bg.ts | 2 +- lang/tomahawk_bn_IN.ts | 2 +- lang/tomahawk_ca.ts | 2 +- lang/tomahawk_ca@valencia.ts | 2 +- lang/tomahawk_cs.ts | 33 +++++++++++++------------ lang/tomahawk_da.ts | 2 +- lang/tomahawk_de.ts | 2 +- lang/tomahawk_el.ts | 2 +- lang/tomahawk_en.ts | 2 +- lang/tomahawk_es.ts | 2 +- lang/tomahawk_fi.ts | 2 +- lang/tomahawk_fr.ts | 2 +- lang/tomahawk_gl.ts | 2 +- lang/tomahawk_hi_IN.ts | 2 +- lang/tomahawk_hu.ts | 2 +- lang/tomahawk_id.ts | 2 +- lang/tomahawk_it.ts | 2 +- lang/tomahawk_ja.ts | 2 +- lang/tomahawk_lt.ts | 2 +- lang/tomahawk_pl.ts | 2 +- lang/tomahawk_pt_BR.ts | 2 +- lang/tomahawk_ru.ts | 2 +- lang/tomahawk_sv.ts | 48 +++++++++++++++++++----------------- lang/tomahawk_tr.ts | 2 +- lang/tomahawk_zh_CN.ts | 2 +- lang/tomahawk_zh_TW.ts | 2 +- 27 files changed, 67 insertions(+), 64 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 18c0a5d427..32f45e833a 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 0d6c4995be..9e74ca58e9 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -3607,7 +3607,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index d7b1a8c1b3..ef7590bb69 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 85591251bd..00d126bc7e 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -3594,7 +3594,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 8d7f524204..46a0936648 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -3594,7 +3594,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 57922c3ff6..53c8c73f1d 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -681,48 +681,49 @@ se s vámi spojil? Form - + Formulář Connect to your Hatchet account - + Spojit se s účtem u Hatchet One-time Password - + Jednorázové +heslo Username - + Uživatelské jméno Hatchet username - + Uživatelské jméno u Hatchet Password: - + Heslo: Hatchet password - + Heslo pro Hatchet (Only if configured) - + (Pouze je-li nastaveno) Login - + Přihlášení @@ -1657,7 +1658,7 @@ Password Active (your host needs to be directly reachable) - + Činný (váš hostitel musí být přímo dosažitelný) @@ -2288,12 +2289,12 @@ Password Login - + Přihlášení Logged in as: %1 - + Přihlášen jako: %1 @@ -2301,7 +2302,7 @@ Password Connect to your Hatchet account - + Spojit se s účtem u Hatchet @@ -2713,7 +2714,7 @@ username@jabber.org Send to &Friend - + Poslat &příteli @@ -2753,7 +2754,7 @@ username@jabber.org Mark as &Listened - + Označit jako &poslechnuté @@ -3593,7 +3594,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 65198b99ac..1f5451556f 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -3582,7 +3582,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 71ca95a01f..d48ca31934 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -3587,7 +3587,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 409316b07d..5b5aa991dd 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -3595,7 +3595,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index e78a008f4c..cb2a115c52 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -3597,7 +3597,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 94aa1735bc..f7b2d4b8a0 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -3595,7 +3595,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 2b375c51df..2a85d0a676 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -3600,7 +3600,7 @@ kappaleen %2%4 %3. TomahawkApp - + My Collection Oma kokoelma diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 4742af3659..329763e4ac 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -3592,7 +3592,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 438a097d57..28d24a8523 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -3594,7 +3594,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 7fd2483c56..d77a10222f 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index a4ee5a2778..07486caeba 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 77097ae0c7..3d45365de7 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 7808d34610..484b12ba72 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index d56de6af66..b449ed18cd 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -3592,7 +3592,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index b45a814f44..936328de70 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 663d4dc3d6..a7433f79e3 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -3589,7 +3589,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index d126031e47..09ed2c0441 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -3589,7 +3589,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 8dbc70ca5a..cbe8959eb1 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -3596,7 +3596,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index b8ed012010..4a4e885e95 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -681,48 +681,49 @@ ansluta och strömma från dig? Form - + Formulär Connect to your Hatchet account - + Anslut till ditt Hatchet-konto One-time Password - + Engångslösenord + Username - + Användarnamn Hatchet username - + Hatchet-användarnamn Password: - + Lösenord: Hatchet password - + Hatchet-lösenord (Only if configured) - + (Endast om konfigurerat) Login - + Logga in @@ -738,7 +739,7 @@ Password Sent %1 by %2 to %3. - + Skickade %1 av %2 till %3 @@ -769,7 +770,7 @@ Password Script Resolver Warning: API call %1 returned data synchronously. - + Resovler-skriptvarning: API-kall %1 returnerade data synkront @@ -1453,12 +1454,12 @@ Password No query - + Ingen fråga Parameter count mismatch - + Antalet parametrar stämmer inte @@ -1657,7 +1658,7 @@ Password Active (your host needs to be directly reachable) - + Aktiv (Din host behöver vara direkt kontaktbar) @@ -1857,7 +1858,7 @@ och radiostationer baserat på din personliga profil Drop to send tracks - + Släpp för att skicka spåret @@ -2290,12 +2291,12 @@ och radiostationer baserat på din personliga profil Login - + Logga in Logged in as: %1 - + Inloggad som: %1 @@ -2303,7 +2304,7 @@ och radiostationer baserat på din personliga profil Connect to your Hatchet account - + Anslut till ditt Hatchet-konto @@ -2714,7 +2715,7 @@ username@jabber.org Send to &Friend - + Skicka till &Vän @@ -2754,7 +2755,7 @@ username@jabber.org Mark as &Listened - + Markera som &lyssnad @@ -3325,13 +3326,14 @@ Försök att ändra i filtrerna för att få en ny låtlista %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 skickade +%2%4 %3 till dig. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 skickade %2 av %3 till dig @@ -3593,7 +3595,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 263edf8ff2..8ec7871c95 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 1ce0086db9..2475040312 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -3590,7 +3590,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index f34fcb3cd6..ccf292d8df 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 From efddc70b7f2c2ca114720587890627dc3d431813 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Sat, 1 Jun 2013 13:13:43 +0200 Subject: [PATCH 190/565] Bump Echonest version. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c005fbc43..d57003a076 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,7 +198,7 @@ if(PHONON_FOUND) message(STATUS "Phonon found; ensure that phonon-vlc is at least 0.4") endif() -macro_optional_find_package(Echonest 2.0.3) +macro_optional_find_package(Echonest 2.1.0) macro_log_feature(ECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.0.3 is needed for dynamic playlists and the infosystem") macro_optional_find_package(CLucene 0.9.23) From e84316c325ad845fd7c22576ab4e740f23372aa4 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 1 Jun 2013 17:35:06 +0200 Subject: [PATCH 191/565] * Fixed liben comment. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d57003a076..97ba6b2c1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,7 +199,7 @@ if(PHONON_FOUND) endif() macro_optional_find_package(Echonest 2.1.0) -macro_log_feature(ECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.0.3 is needed for dynamic playlists and the infosystem") +macro_log_feature(ECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest 2.1.0 is needed for dynamic playlists and the infosystem") macro_optional_find_package(CLucene 0.9.23) macro_log_feature(CLucene_FOUND "CLucene" "The open-source, C++ search engine" "http://clucene.sf.net" TRUE "" "CLucene is used for indexing the collection") From cc104598dba0933e5cc2e4a289b800321218f258 Mon Sep 17 00:00:00 2001 From: Florian Richter Date: Sat, 25 May 2013 13:28:26 +0200 Subject: [PATCH 192/565] listen to media key events of gnome settings daemon * add shortcuthandler, which listens to the media key event provided by the gnome settings daemon via dbus (https://github.com/GNOME/gnome-settings-daemon/blob/master/plugins/media-keys/README.media-keys-API) * fixes TWK-983 --- CMakeLists.txt | 3 + src/tomahawk/CMakeLists.txt | 5 + src/tomahawk/Config.h.in | 1 + src/tomahawk/GnomeSettingsDaemonMediaKeys.xml | 17 +++ src/tomahawk/GnomeShortcutHandler.cpp | 102 ++++++++++++++++++ src/tomahawk/GnomeShortcutHandler.h | 54 ++++++++++ src/tomahawk/TomahawkApp.cpp | 10 ++ 7 files changed, 192 insertions(+) create mode 100644 src/tomahawk/GnomeSettingsDaemonMediaKeys.xml create mode 100644 src/tomahawk/GnomeShortcutHandler.cpp create mode 100644 src/tomahawk/GnomeShortcutHandler.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 97ba6b2c1c..216a9e8d16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ option(WITH_CRASHREPORTER "Build with CrashReporter" ON) option(WITH_BINARY_ATTICA "Enable support for downloading binary resolvers automatically" ON) option(LEGACY_KDE_INTEGRATION "Install tomahawk.protocol file, deprecated since 4.6.0" OFF) OPTION(WITH_UPOWER "Build with support for UPower events" OFF) +OPTION(WITH_GNOMESHORTCUTHANDLER "Build with shortcut handler for GNOME" OFF) IF( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" ) message(STATUS "Build of breakpad library disabled on this platform.") @@ -190,8 +191,10 @@ endif() IF( UNIX AND NOT APPLE AND QT_QTDBUS_FOUND ) SET( WITH_UPOWER ON ) + SET( WITH_GNOMESHORTCUTHANDLER ON ) ENDIF( UNIX AND NOT APPLE AND QT_QTDBUS_FOUND ) + macro_optional_find_package(Phonon 4.5.0) macro_log_feature(PHONON_FOUND "Phonon" "The Phonon multimedia library" "http://phonon.kde.org" TRUE "" "") if(PHONON_FOUND) diff --git a/src/tomahawk/CMakeLists.txt b/src/tomahawk/CMakeLists.txt index 636ff2c3c6..64fb365f92 100644 --- a/src/tomahawk/CMakeLists.txt +++ b/src/tomahawk/CMakeLists.txt @@ -27,6 +27,11 @@ SET( tomahawkSources ${tomahawkSources} main.cpp ) +IF( WITH_GNOMESHORTCUTHANDLER ) + SET( tomahawkSources ${tomahawkSources} GnomeShortcutHandler.cpp ) + qt4_add_dbus_interface(tomahawkSources GnomeSettingsDaemonMediaKeys.xml GnomeSettingsDaemonMediaKeysProxy) +ENDIF( WITH_GNOMESHORTCUTHANDLER ) + IF(LIBLASTFM_FOUND) SET(tomahawkSources ${tomahawkSources} Scrobbler.cpp diff --git a/src/tomahawk/Config.h.in b/src/tomahawk/Config.h.in index 33effc143a..d552b97859 100644 --- a/src/tomahawk/Config.h.in +++ b/src/tomahawk/Config.h.in @@ -19,6 +19,7 @@ #cmakedefine WITH_BINARY_ATTICA #cmakedefine WITH_QtSparkle #cmakedefine WITH_UPOWER +#cmakedefine WITH_GNOMESHORTCUTHANDLER #cmakedefine LIBLASTFM_FOUND diff --git a/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml b/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml new file mode 100644 index 0000000000..9caf240f5d --- /dev/null +++ b/src/tomahawk/GnomeSettingsDaemonMediaKeys.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/src/tomahawk/GnomeShortcutHandler.cpp b/src/tomahawk/GnomeShortcutHandler.cpp new file mode 100644 index 0000000000..56fe30a909 --- /dev/null +++ b/src/tomahawk/GnomeShortcutHandler.cpp @@ -0,0 +1,102 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Florian Richter + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +// implement listen on media keys events provided by the gnome settings daemon +// as documented here: +// https://github.com/GNOME/gnome-settings-daemon/blob/master/plugins/media-keys/README.media-keys-API + +#include "GnomeShortcutHandler.h" +#include "utils/Logger.h" + + +# include + +#include + +using namespace Tomahawk; + +const char* GnomeShortcutHandler::kGsdService = "org.gnome.SettingsDaemon"; +const char* GnomeShortcutHandler::kGsdPath = "/org/gnome/SettingsDaemon/MediaKeys"; +const char* GnomeShortcutHandler::kGsdInterface = "org.gnome.SettingsDaemon.MediaKeys"; + + +GnomeShortcutHandler::GnomeShortcutHandler(QObject *parent) : + Tomahawk::ShortcutHandler(parent), + interface_(NULL) +{ + +} + +bool GnomeShortcutHandler::DoRegister() { + tLog(LOGVERBOSE) << "registering for gnome media keys"; + + // Check if the GSD service is available + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kGsdService)) { + tLog(LOGVERBOSE) << "gnome settings daemon not registered"; + return false; + } + + if (!interface_) { + interface_ = new org::gnome::SettingsDaemon::MediaKeys( + kGsdService, kGsdPath, QDBusConnection::sessionBus(), this->parent()); + } + + QDBusPendingReply<> reply = interface_->GrabMediaPlayerKeys( + QCoreApplication::applicationName(), 0); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(RegisterFinished(QDBusPendingCallWatcher*))); + + return true; +} + +void GnomeShortcutHandler::RegisterFinished(QDBusPendingCallWatcher* watcher) { + QDBusMessage reply = watcher->reply(); + watcher->deleteLater(); + + if (reply.type() == QDBusMessage::ErrorMessage) { + tLog(LOGVERBOSE) << "Failed to grab media keys" + << reply.errorName() < === + * + * Copyright 2013, Florian Richter + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef GNOMESHORTCUTHANDLER_H +#define GNOMESHORTCUTHANDLER_H + +#include "ShortcutHandler.h" +#include "GnomeSettingsDaemonMediaKeysProxy.h" + +#include + +namespace Tomahawk { + + +class GnomeShortcutHandler : public ShortcutHandler +{ + Q_OBJECT +public: + explicit GnomeShortcutHandler(QObject *parent = 0); + bool DoRegister(); + + static const char* kGsdService; + static const char* kGsdPath; + static const char* kGsdInterface; + + +public slots: + void RegisterFinished(QDBusPendingCallWatcher* watcher); + void GnomeMediaKeyPressed( const QString& application, const QString& key ); + +private: + org::gnome::SettingsDaemon::MediaKeys* interface_; + +}; + +} + +#endif // GNOMESHORTCUTHANDLER_H + diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index ad5bae8a6d..5ada569b56 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -94,6 +94,10 @@ #include #endif +#ifdef WITH_GNOMESHORTCUTHANDLER +#include "GnomeShortcutHandler.h" +#endif + #include #include #include @@ -237,6 +241,12 @@ TomahawkApp::init() increaseMaxFileDescriptors(); #endif +#ifdef WITH_GNOMESHORTCUTHANDLER + GnomeShortcutHandler *gnomeShortcutHandler = new GnomeShortcutHandler( this ); + gnomeShortcutHandler->DoRegister(); + m_shortcutHandler = QPointer( gnomeShortcutHandler ); +#endif + // Connect up shortcuts if ( !m_shortcutHandler.isNull() ) { From de5ddc925d8e1d107d6e5a5fb8703fc3a9cd710c Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sun, 2 Jun 2013 02:16:42 +0200 Subject: [PATCH 193/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 2 +- lang/tomahawk_bg.ts | 2 +- lang/tomahawk_bn_IN.ts | 2 +- lang/tomahawk_ca.ts | 2 +- lang/tomahawk_ca@valencia.ts | 2 +- lang/tomahawk_cs.ts | 2 +- lang/tomahawk_da.ts | 2 +- lang/tomahawk_de.ts | 48 +++++++++++++++++++----------------- lang/tomahawk_el.ts | 2 +- lang/tomahawk_en.ts | 2 +- lang/tomahawk_es.ts | 2 +- lang/tomahawk_fi.ts | 2 +- lang/tomahawk_fr.ts | 2 +- lang/tomahawk_gl.ts | 2 +- lang/tomahawk_hi_IN.ts | 2 +- lang/tomahawk_hu.ts | 2 +- lang/tomahawk_id.ts | 2 +- lang/tomahawk_it.ts | 2 +- lang/tomahawk_ja.ts | 2 +- lang/tomahawk_lt.ts | 2 +- lang/tomahawk_pl.ts | 2 +- lang/tomahawk_pt_BR.ts | 2 +- lang/tomahawk_ru.ts | 2 +- lang/tomahawk_sv.ts | 2 +- lang/tomahawk_tr.ts | 2 +- lang/tomahawk_zh_CN.ts | 2 +- lang/tomahawk_zh_TW.ts | 2 +- 27 files changed, 51 insertions(+), 49 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 32f45e833a..8b066a6665 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 9e74ca58e9..1e3d4fb90c 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -3607,7 +3607,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index ef7590bb69..092ae1f070 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 00d126bc7e..7218a76b66 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -3594,7 +3594,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 46a0936648..d049970bfa 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -3594,7 +3594,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 53c8c73f1d..fd1cecd7ea 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -3594,7 +3594,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 1f5451556f..3e8b322881 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -3582,7 +3582,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index d48ca31934..36c58f5074 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -681,48 +681,49 @@ erlauben sich mit dir zu verbinden? Form - + Form Connect to your Hatchet account - + Eine Verbindung zu Ihrem Hatchet Konto aufbauen One-time Password - + Einmal⏎ +Passwort Username - + Benutzername Hatchet username - + Hatchet benutzername Password: - + Passwort: Hatchet password - + Hatchet Passwort (Only if configured) - + (Nur falls konfiguriert) Login - + Anmelden @@ -738,7 +739,7 @@ Password Sent %1 by %2 to %3. - + %1 gesendet an dir %2 von %3. @@ -769,7 +770,7 @@ Password Script Resolver Warning: API call %1 returned data synchronously. - + Script Resolver Warnung: API aufruf %1 zurückgegebener Daten synchron. @@ -1453,12 +1454,12 @@ Password No query - + Κeine Abfrage Parameter count mismatch - + Nicht übereinstimmende Parameter @@ -1657,7 +1658,7 @@ Password Active (your host needs to be directly reachable) - + Aktiv (der Host muss direkt erreichbar sein) @@ -1855,7 +1856,7 @@ Password Drop to send tracks - + Ziehen um Titel zu senden @@ -2288,12 +2289,12 @@ Password Login - + Anmelden Logged in as: %1 - + Angemeldet als %1 @@ -2301,7 +2302,7 @@ Password Connect to your Hatchet account - + Eine Verbindung zu Ihrem Hatchet Konto aufbauen @@ -2708,7 +2709,7 @@ username@jabber.org Send to &Friend - + An Freund senden @@ -2748,7 +2749,7 @@ username@jabber.org Mark as &Listened - + Markieren als &gehört @@ -3319,13 +3320,14 @@ Versuch die Filter anzupassen für neue Lieder. %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 gesendet an dir⏎ +%2%4 %3. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 gesendet an dir %2 von %3. @@ -3587,7 +3589,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 5b5aa991dd..f554ab7fc7 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -3595,7 +3595,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index cb2a115c52..92f0e935da 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -3597,7 +3597,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index f7b2d4b8a0..ca44d8ba97 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -3595,7 +3595,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 2a85d0a676..7ef529f42b 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -3600,7 +3600,7 @@ kappaleen %2%4 %3. TomahawkApp - + My Collection Oma kokoelma diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 329763e4ac..5430a5dd48 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -3592,7 +3592,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 28d24a8523..de6ee9f90a 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -3594,7 +3594,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index d77a10222f..5275a2b364 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 07486caeba..d955a3904c 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 3d45365de7..5d275f18ca 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 484b12ba72..93f95118bd 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index b449ed18cd..9ce048b89f 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -3592,7 +3592,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 936328de70..1f048bf677 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index a7433f79e3..7950bdb120 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -3589,7 +3589,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 09ed2c0441..9023787776 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -3589,7 +3589,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index cbe8959eb1..f62800170a 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -3596,7 +3596,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 4a4e885e95..7071903869 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -3595,7 +3595,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 8ec7871c95..74d160564e 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 2475040312..949dfd4139 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -3590,7 +3590,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index ccf292d8df..0adbc9de04 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -3580,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 From 20df5ded1fd908cc96a68ff8fd56f8263629676a Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 3 Jun 2013 03:30:01 +0200 Subject: [PATCH 194/565] * Style fixes for FdoNotifyPlugin. --- .../linux/fdonotify/FdoNotifyPlugin.cpp | 96 ++++++++++--------- .../linux/fdonotify/FdoNotifyPlugin.h | 2 +- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp index 80d1044ae9..89c98b997c 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp @@ -38,16 +38,18 @@ */ #include "FdoNotifyPlugin.h" -#include "utils/TomahawkUtils.h" + #include "ImageConverter.h" #include "FreedesktopNotificationsProxy.h" #include "TomahawkSettings.h" +#include "utils/TomahawkUtils.h" #include "utils/Logger.h" #include "utils/TomahawkUtilsGui.h" #include +#include #include // QTextDocument provides Qt::escape() #include @@ -63,22 +65,22 @@ FdoNotifyPlugin::FdoNotifyPlugin() , m_nowPlayingId( 0 ) , m_wmSupportsBodyMarkup( false ) { - qDebug() << Q_FUNC_INFO; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped << InfoInboxReceived; // Query the window manager for its capabilties in styling notifications. - notifications_interface = new org::freedesktop::Notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", - QDBusConnection::sessionBus(), this); + notifications_interface = new org::freedesktop::Notifications( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", + QDBusConnection::sessionBus(), this ); QDBusPendingReply reply = notifications_interface->GetCapabilities(); - QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); - connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(dbusCapabilitiesReplyReceived(QDBusPendingCallWatcher*))); + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, this ); + connect( watcher, SIGNAL( finished( QDBusPendingCallWatcher* ) ), SLOT( dbusCapabilitiesReplyReceived( QDBusPendingCallWatcher* ) ) ); } FdoNotifyPlugin::~FdoNotifyPlugin() { - qDebug() << Q_FUNC_INFO; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; } @@ -88,19 +90,20 @@ FdoNotifyPlugin::dbusCapabilitiesReplyReceived( QDBusPendingCallWatcher* watcher QDBusMessage reply = watcher->reply(); watcher->deleteLater(); - if (reply.type() == QDBusMessage::ErrorMessage) { + if ( reply.type() == QDBusMessage::ErrorMessage ) + { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Failed to request capabilities of notifications"; } - const QStringList &capability_list = reply.arguments().at( 0 ).toStringList(); - m_wmSupportsBodyMarkup = capability_list.contains("body-markup"); + const QStringList& capability_list = reply.arguments().first().toStringList(); + m_wmSupportsBodyMarkup = capability_list.contains( "body-markup" ); } void FdoNotifyPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) { - qDebug() << Q_FUNC_INFO << "showing notification: " << TomahawkSettings::instance()->songChangeNotificationEnabled(); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "showing notification:" << TomahawkSettings::instance()->songChangeNotificationEnabled(); if ( !TomahawkSettings::instance()->songChangeNotificationEnabled() ) return; @@ -154,17 +157,18 @@ FdoNotifyPlugin::notifyUser( const QString& messageText ) hints[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "icons/tomahawk-icon-512x512.png" ).scaledToHeight( getNotificationIconHeight() ) ); notifications_interface->Notify( "Tomahawk", // app_name - 0, // notification_id - "", // app_icon - "Tomahawk", // summary - messageText, // body - QStringList(), // actions - hints, // hints - -1 // expire_timeout - ); + 0, // notification_id + "", // app_icon + "Tomahawk", // summary + messageText, // body + QStringList(), // actions + hints, // hints + -1 // expire_timeout + ); } -void FdoNotifyPlugin::inboxReceived(const QVariant &input) + +void FdoNotifyPlugin::inboxReceived( const QVariant& input ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO; if ( !input.canConvert< QVariantMap >() ) @@ -174,7 +178,6 @@ void FdoNotifyPlugin::inboxReceived(const QVariant &input) if ( !map.contains( "trackinfo" ) || !map[ "trackinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) return; - if ( !map.contains( "sourceinfo" ) || !map[ "sourceinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) return; @@ -217,16 +220,17 @@ void FdoNotifyPlugin::inboxReceived(const QVariant &input) hints[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "images/inbox-512x512.png" ).scaledToHeight( getNotificationIconHeight() ) ); notifications_interface->Notify( "Tomahawk", // app_name - m_nowPlayingId, // notification_id - "", // app_icon - "Tomahawk - Track received", // summary - messageText, // body - QStringList(), // actions - hints, // hints - -1 // expire_timeout - ); + m_nowPlayingId, // notification_id + "", // app_icon + "Tomahawk - Track received", // summary + messageText, // body + QStringList(), // actions + hints, // hints + -1 // expire_timeout + ); } + void FdoNotifyPlugin::nowPlaying( const QVariant& input ) { @@ -235,7 +239,6 @@ FdoNotifyPlugin::nowPlaying( const QVariant& input ) return; QVariantMap map = input.toMap(); - if ( !map.contains( "trackinfo" ) || !map[ "trackinfo" ].canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) return; @@ -290,18 +293,17 @@ FdoNotifyPlugin::nowPlaying( const QVariant& input ) QDBusPendingReply<> reply = notifications_interface->Notify( "Tomahawk", // app_name - m_nowPlayingId, // notification_id - "", // app_icon - "Tomahawk - Now Playing", // summary - messageText, // body - QStringList(), // actions - hints, // hints - -1 // expire_timeout - ); - - QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); - connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), - this, SLOT(RegisterFinished(dbusPlayingReplyReceived*))); + m_nowPlayingId, // notification_id + "", // app_icon + "Tomahawk - Now Playing", // summary + messageText, // body + QStringList(), // actions + hints, // hints + -1 // expire_timeout + ); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, this ); + connect( watcher, SIGNAL( finished( QDBusPendingCallWatcher* ) ), SLOT( RegisterFinished( dbusPlayingReplyReceived* ) ) ); } @@ -314,14 +316,16 @@ FdoNotifyPlugin::dbusPlayingReplyReceived( QDBusPendingCallWatcher* watcher ) { QDBusMessage reply = watcher->reply(); watcher->deleteLater(); - if (reply.type() == QDBusMessage::ErrorMessage) { + + if ( reply.type() == QDBusMessage::ErrorMessage ) + { tLog(LOGVERBOSE) << "Failed to grab media keys" << reply.errorName() << reply.errorMessage(); - return; + return; } const QVariantList& list = reply.arguments(); - if ( list.count() > 0 ) - m_nowPlayingId = list.at( 0 ).toInt(); + if ( !list.isEmpty() ) + m_nowPlayingId = list.first().toInt(); } } //ns InfoSystem diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h index cd27ee6b84..1f53c34112 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.h @@ -25,7 +25,7 @@ #include "infosystem/InfoSystem.h" #include "FreedesktopNotificationsProxy.h" -#include +class QDBusPendingCallWatcher; namespace Tomahawk { From b951f1f4bcbee957313063a035e9d045d53349b6 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 3 Jun 2013 05:41:49 +0200 Subject: [PATCH 195/565] * Improvements to item-delegates. --- src/libtomahawk/playlist/PlayableModel.cpp | 54 +++++++++++++------ src/libtomahawk/playlist/PlayableModel.h | 1 + .../playlist/PlaylistItemDelegate.cpp | 19 ++++--- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 9cf2daa5bf..1fed2226bf 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -266,20 +266,7 @@ PlayableModel::queryData( const query_ptr& query, int column, int role ) const case Score: { float score = query->results().first()->score(); - if ( score == 1.0 ) - return tr( "Perfect match" ); - if ( score > 0.9 ) - return tr( "Very good match" ); - if ( score > 0.7 ) - return tr( "Good match" ); - if ( score > 0.5 ) - return tr( "Vague match" ); - if ( score > 0.3 ) - return tr( "Bad match" ); - if ( score > 0.0 ) - return tr( "Very bad match" ); - - return tr( "Not available" ); + return scoreText( score ); break; } @@ -293,9 +280,9 @@ PlayableModel::queryData( const query_ptr& query, int column, int role ) const { case Score: if ( query->resolvingFinished() ) - return tr( "Not available" ); + return scoreText( 0.0 ); else - return tr( "Searching..." ); + return scoreText( -1.0 ); default: break; @@ -791,6 +778,41 @@ PlayableModel::columnAlignment( int column ) const } +QString +PlayableModel::scoreText( float score ) const +{ + static QMap texts; + if ( texts.isEmpty() ) + { + texts[ 1.0 ] = tr( "Perfect match" ); + texts[ 0.9 ] = tr( "Very good match" ); + texts[ 0.7 ] = tr( "Good match" ); + texts[ 0.5 ] = tr( "Vague match" ); + texts[ 0.3 ] = tr( "Bad match" ); + texts[ 0.1 ] = tr( "Very bad match" ); + texts[ 0.0 ] = tr( "Not available" ); + texts[ -1.0 ] = tr( "Searching..." ); + } + + if ( score == 1.0 ) + return texts[ 1.0 ]; + if ( score > 0.9 ) + return texts[ 0.9 ]; + if ( score > 0.7 ) + return texts[ 0.7 ]; + if ( score > 0.5 ) + return texts[ 0.5 ]; + if ( score > 0.3 ) + return texts[ 0.3 ]; + if ( score > 0.0 ) + return texts[ 0.1 ]; + if ( score == 0.0 ) + return texts[ 0.0 ]; + + return texts[ -1.0 ]; +} + + void PlayableModel::onDataChanged() { diff --git a/src/libtomahawk/playlist/PlayableModel.h b/src/libtomahawk/playlist/PlayableModel.h index 656fd254cb..c4968efb9e 100644 --- a/src/libtomahawk/playlist/PlayableModel.h +++ b/src/libtomahawk/playlist/PlayableModel.h @@ -191,6 +191,7 @@ private slots: template void insertInternal( const QList< T >& items, int row, const QList< Tomahawk::PlaybackLog >& logs = QList< Tomahawk::PlaybackLog >() ); + QString scoreText( float score ) const; Qt::Alignment columnAlignment( int column ) const; PlayableItem* m_rootItem; diff --git a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp index ea36822240..a6c5c69525 100644 --- a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp @@ -99,7 +99,7 @@ PlaylistItemDelegate::prepareStyleOption( QStyleOptionViewItemV4* option, const void PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { - int style = index.data( PlayableProxyModel::StyleRole ).toInt(); + const int style = index.data( PlayableProxyModel::StyleRole ).toInt(); switch ( style ) { case PlayableProxyModel::Detailed: @@ -160,7 +160,7 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& { const int pixMargin = 2; const int pixHeight = r.height() - pixMargin * 2; - QRect npr = r.adjusted( pixMargin, pixMargin + 1, pixHeight - r.width() + pixMargin, -pixMargin + 1 ); + const QRect npr = r.adjusted( pixMargin, pixMargin + 1, pixHeight - r.width() + pixMargin, -pixMargin + 1 ); painter->drawPixmap( npr, TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker, TomahawkUtils::Original, npr.size() ) ); r.adjust( pixHeight + 8, 0, 0, 0 ); } @@ -225,10 +225,8 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt ( index.column() == PlayableModel::Artist || index.column() == PlayableModel::Album || index.column() == PlayableModel::Track ) ) { opt.rect.setWidth( opt.rect.width() - opt.rect.height() - 2 ); - QRect arrowRect( opt.rect.x() + opt.rect.width(), opt.rect.y() + 1, opt.rect.height() - 2, opt.rect.height() - 2 ); - - QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() ); - painter->drawPixmap( arrowRect, infoIcon ); + const QRect arrowRect( opt.rect.x() + opt.rect.width(), opt.rect.y() + 1, opt.rect.height() - 2, opt.rect.height() - 2 ); + painter->drawPixmap( arrowRect, TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() ) ); m_infoButtonRects[ index ] = arrowRect; } @@ -257,7 +255,8 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt painter->drawRect( fillR ); } - else */ if ( item->isPlaying() ) + else */ + if ( item->isPlaying() ) { QRect r = opt.rect.adjusted( 3, 0, 0, 0 ); @@ -266,19 +265,19 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt { const int pixMargin = 1; const int pixHeight = r.height() - pixMargin * 2; - QRect npr = r.adjusted( pixMargin, pixMargin, pixHeight - r.width() + pixMargin, -pixMargin ); + const QRect npr = r.adjusted( pixMargin, pixMargin, pixHeight - r.width() + pixMargin, -pixMargin ); painter->drawPixmap( npr, TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker, TomahawkUtils::Original, npr.size() ) ); r.adjust( pixHeight + 6, 0, 0, 0 ); } painter->setPen( opt.palette.text().color() ); - QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, r.width() - 3 ); + const QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, r.width() - 3 ); painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, textOption ); } else { painter->setPen( opt.palette.text().color() ); - QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, opt.rect.width() - 6 ); + const QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, opt.rect.width() - 6 ); painter->drawText( opt.rect.adjusted( 3, 1, -3, 0 ), text, textOption ); } From 4c6ed3d368fb83d5739290243a04c13f500e73ef Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 3 Jun 2013 14:55:04 +0200 Subject: [PATCH 196/565] Remove hack to avoid resetting firstMessage on parallel connection --- src/libtomahawk/network/Servent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 76156b882a..ebbebc534f 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -824,7 +824,7 @@ Servent::initiateConnection( const SipInfo& sipInfo, Connection* conn ) return; } - if ( !sipInfo.key().isEmpty() && conn->firstMessage().isNull() ) + if ( conn->firstMessage().isNull() ) { QVariantMap m; m["conntype"] = "accept-offer"; @@ -898,7 +898,7 @@ Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& their m["key"] = theirkey; m["controlid"] = Database::instance()->impl()->dbid(); new_conn->setFirstMessage( m ); - createParallelConnection( orig_conn, new_conn, QString() ); + createParallelConnection( orig_conn, new_conn, theirkey ); } // return the appropriate connection for a given offer key, or NULL if invalid From ca08729d2428c0d85efa7a9db0a916da5e17cd56 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 3 Jun 2013 16:17:59 +0200 Subject: [PATCH 197/565] Show DBID/nodeId in DiagnosticsDialog --- src/tomahawk/DiagnosticsDialog.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index 4091a7af04..71505bd5c9 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -33,6 +33,8 @@ #include "utils/Logger.h" #include "infosystem/InfoSystem.h" #include "infosystem/InfoSystemWorker.h" +#include "database/Database.h" +#include "database/DatabaseImpl.h" #include #include @@ -64,7 +66,8 @@ DiagnosticsDialog::updateLogView() log.append( QString( "TOMAHAWK DIAGNOSTICS LOG -%1 \n\n" ).arg( QDateTime::currentDateTime().toString() ) ); log.append( "TOMAHAWK-VERSION: " TOMAHAWK_VERSION "\n" ); - log.append( "PLATFORM: " TOMAHAWK_SYSTEM "\n\n"); + log.append( "PLATFORM: " TOMAHAWK_SYSTEM "\n"); + log.append( QString( "DBID: %1\n\n" ).arg( Database::instance()->impl()->dbid() ) ); log.append( "NETWORK:\n Listening to:\n" ); if ( Servent::instance()->visibleExternally() ) From b6109b9bf922f0bc3f005ceca7d4ddf0823f783f Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Tue, 4 Jun 2013 02:16:42 +0200 Subject: [PATCH 198/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 35 +++++++++++++++++------------------ lang/tomahawk_bg.ts | 35 +++++++++++++++++------------------ lang/tomahawk_bn_IN.ts | 35 +++++++++++++++++------------------ lang/tomahawk_ca.ts | 35 +++++++++++++++++------------------ lang/tomahawk_ca@valencia.ts | 35 +++++++++++++++++------------------ lang/tomahawk_cs.ts | 35 +++++++++++++++++------------------ lang/tomahawk_da.ts | 35 +++++++++++++++++------------------ lang/tomahawk_de.ts | 35 +++++++++++++++++------------------ lang/tomahawk_el.ts | 35 +++++++++++++++++------------------ lang/tomahawk_en.ts | 35 +++++++++++++++++------------------ lang/tomahawk_es.ts | 35 +++++++++++++++++------------------ lang/tomahawk_fi.ts | 35 +++++++++++++++++------------------ lang/tomahawk_fr.ts | 35 +++++++++++++++++------------------ lang/tomahawk_gl.ts | 35 +++++++++++++++++------------------ lang/tomahawk_hi_IN.ts | 35 +++++++++++++++++------------------ lang/tomahawk_hu.ts | 35 +++++++++++++++++------------------ lang/tomahawk_id.ts | 35 +++++++++++++++++------------------ lang/tomahawk_it.ts | 35 +++++++++++++++++------------------ lang/tomahawk_ja.ts | 35 +++++++++++++++++------------------ lang/tomahawk_lt.ts | 35 +++++++++++++++++------------------ lang/tomahawk_pl.ts | 35 +++++++++++++++++------------------ lang/tomahawk_pt_BR.ts | 35 +++++++++++++++++------------------ lang/tomahawk_ru.ts | 35 +++++++++++++++++------------------ lang/tomahawk_sv.ts | 35 +++++++++++++++++------------------ lang/tomahawk_tr.ts | 35 +++++++++++++++++------------------ lang/tomahawk_zh_CN.ts | 35 +++++++++++++++++------------------ lang/tomahawk_zh_TW.ts | 35 +++++++++++++++++------------------ 27 files changed, 459 insertions(+), 486 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 8b066a6665..e21e06d311 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1125,49 +1125,48 @@ Password الدقة - + Perfect match تطابق تام - + Very good match تطابق جيد جدا - + Good match تطابق جيد - + Vague match تطابق مبهم - + Bad match تطابق سيء - + Very bad match تطابق سيء للغاية - - + Not available غير متوفر - + Searching... قيد البحث... - + Name إسم @@ -3302,45 +3301,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name في - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist للفنان - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) %1 أرسل لك %2%4 %3. - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist %1 أرسل لك "%2" للفنان %3. - + on "%1" %1 is an album name في "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" من قبل %2%3. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 1e3d4fb90c..52d254e765 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1131,49 +1131,48 @@ Password Съвпадение - + Perfect match Абсолютно - + Very good match Много добро - + Good match Добро - + Vague match Горе-долу - + Bad match Лошо - + Very bad match Много лошо - - + Not available Няма съвпадение - + Searching... - + Name Име @@ -3315,45 +3314,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 092ae1f070..46eba46a7d 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 7218a76b66..95921ea896 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1124,49 +1124,48 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - - + Not available No disponible - + Searching... - + Name Nom @@ -3303,45 +3302,45 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index d049970bfa..bc0fef95dd 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1124,49 +1124,48 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - - + Not available No disponible - + Searching... - + Name Nom @@ -3303,45 +3302,45 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index fd1cecd7ea..501d11d096 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1126,49 +1126,48 @@ heslo Přesnost - + Perfect match Přesná shoda - + Very good match Velmi dobrá shoda - + Good match Dobrá shoda - + Vague match Mlhavá shoda - + Bad match Špatná shoda - + Very bad match Velmi špatná shoda - - + Not available Nedostupné - + Searching... Hledá se... - + Name Název @@ -3302,26 +3301,26 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name na - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist od - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) @@ -3329,19 +3328,19 @@ Zkuste vyladit filtry pro nové písně. %2%4 %3. - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist %1 vám poslán "%2" od %3. - + on "%1" %1 is an album name na "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" od %2%3. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 3e8b322881..0e64a7355f 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name @@ -3291,45 +3290,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 36c58f5074..808ff4b480 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1126,49 +1126,48 @@ Passwort Treffsicherheit - + Perfect match Perfekt - + Very good match Sehr gut - + Good match Gut - + Vague match Vage - + Bad match Schlecht - + Very bad match Sehr schlecht - - + Not available Nicht verfügbar - + Searching... Suche läuft... - + Name Name @@ -3297,26 +3296,26 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name an - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist bei - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) @@ -3324,19 +3323,19 @@ Versuch die Filter anzupassen für neue Lieder. %2%4 %3. - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist %1 gesendet an dir %2 von %3. - + on "%1" %1 is an album name in "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" in %2%3. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index f554ab7fc7..63ea857f9b 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1125,49 +1125,48 @@ Password Ακρίβεια - + Perfect match Τέλειο ταίριασμα - + Very good match Πολύ καλό ταίριασμα - + Good match Καλό ταίριασμα - + Vague match Ασαφές ταίριασμα - + Bad match Κακό ταίριασμα - + Very bad match Πολύ κακό ταίριασμα - - + Not available Μη διαθέσιμο - + Searching... Αναζήτηση... - + Name Όνομα @@ -3303,26 +3302,26 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name ενεργοποιημένο - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist από - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) @@ -3330,19 +3329,19 @@ Try tweaking the filters for a new set of songs to play. %2%4 %3. - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist %1 αποστελθηκε σε εσενα %2 απο %3. - + on "%1" %1 is an album name σε "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" από %2%3. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 92f0e935da..ae9f9b8128 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1126,49 +1126,48 @@ Password Accuracy - + Perfect match Perfect match - + Very good match Very good match - + Good match Good match - + Vague match Vague match - + Bad match Bad match - + Very bad match Very bad match - - + Not available Not available - + Searching... Searching... - + Name Name @@ -3305,26 +3304,26 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name on - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist by - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) @@ -3332,19 +3331,19 @@ Try tweaking the filters for a new set of songs to play. %2%4 %3. - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist %1 sent you "%2" by %3. - + on "%1" %1 is an album name on "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" by %2%3. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index ca44d8ba97..2ab32faa27 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1125,49 +1125,48 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia muy buena - + Good match Coincidencia buena - + Vague match Coincidencia vaga - + Bad match Mala coincidencia - + Very bad match Muy mala coincidencia - - + Not available No disponible - + Searching... - + Name Título @@ -3304,45 +3303,45 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name el - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist por - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name en «%1» - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing «%1» por %2%3. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 7ef529f42b..635337605e 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1126,49 +1126,48 @@ salasana Tarkkuus - + Perfect match Täysosuma - + Very good match Erittäin hyvä osuma - + Good match Hyvä osuma - + Vague match Epämääräinen osuma - + Bad match Kehno osuma - + Very bad match Erittäin kehno osuma - - + Not available Ei saatavilla - + Searching... Haetaan... - + Name Nimi @@ -3308,26 +3307,26 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name albumilla - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist artistilta - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) @@ -3335,19 +3334,19 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< kappaleen %2%4 %3. - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist %1 lähetti sinulle kappaleen ”%2” artistilta %3. - + on "%1" %1 is an album name albumilla ”%1” - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing ”%1” artistilta %2%3. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 5430a5dd48..b203e4fd8d 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1125,49 +1125,48 @@ Password Précision - + Perfect match Correspondance parfaite - + Very good match Très bonne correspondance - + Good match Bonne correspondance - + Vague match Vague correspondance - + Bad match Mauvaise correspondance - + Very bad match Très mauvaise correspondance - - + Not available Indisponible - + Searching... Recherche... - + Name Nom @@ -3301,45 +3300,45 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name sur - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist par - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name sur "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" par %2%3. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index de6ee9f90a..b89446499d 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1124,49 +1124,48 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia moi boa - + Good match Boa coincidencia - + Vague match Parcialmente coincidente - + Bad match Mala coincidencia - + Very bad match Nada coincidentes - - + Not available Non está dispoñíbel - + Searching... - + Name Nome @@ -3303,45 +3302,45 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 5275a2b364..0ce5a574d0 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index d955a3904c..d570653e6c 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name Név @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 5d275f18ca..c7463b458f 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 93f95118bd..a0848d12ae 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1124,49 +1124,48 @@ Password Precisione - + Perfect match Abbinamento perfetto - + Very good match Abbinamento molto buono - + Good match Buon abbinamento - + Vague match Vaga corrispondenza - + Bad match Brutto abbinamento - + Very bad match Pessimo abbinamento - - + Not available Non disponibile - + Searching... Sto cercando... - + Name Nome @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name su - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist da - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name su "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" di %2%3. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 9ce048b89f..832bd5ce75 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1125,49 +1125,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name 名前 @@ -3301,45 +3300,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 1f048bf677..13123af552 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name Vardas @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 7950bdb120..e601964a96 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1125,49 +1125,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name @@ -3298,45 +3297,45 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 9023787776..336df3a60f 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1125,49 +1125,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name Nome @@ -3298,45 +3297,45 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index f62800170a..85259ebcc1 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1128,49 +1128,48 @@ Password Совпадение - + Perfect match Превосходное - + Very good match Очень Хорошое - + Good match Хорошое - + Vague match Расплывчатое - + Bad match Плохое совпадение - + Very bad match Очень плохое совпадение - - + Not available Недоступно - + Searching... - + Name Имя @@ -3305,45 +3304,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 7071903869..ecbcdd4485 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1126,49 +1126,48 @@ Password Exakthet - + Perfect match Perfekt matchning - + Very good match Mycket bra matchning - + Good match Bra matchning - + Vague match Svag matchning - + Bad match Dålig matchning - + Very bad match Väldigt dålig matchning - - + Not available Inte tillgänglig - + Searching... Söker... - + Name Namn @@ -3303,26 +3302,26 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) %1%4 %2%3. - - + + by preposition to link track and artist av - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) @@ -3330,19 +3329,19 @@ Försök att ändra i filtrerna för att få en ny låtlista %2%4 %3 till dig. - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist %1 skickade %2 av %3 till dig - + on "%1" %1 is an album name på "%1" - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing "%1" av %2%3. diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 74d160564e..097b7f9e67 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 949dfd4139..feef93352e 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1124,49 +1124,48 @@ Password 准确度 - + Perfect match 完美匹配 - + Very good match 极高匹配 - + Good match 高匹配 - + Vague match 模糊匹配 - + Bad match 低匹配 - + Very bad match 极低匹配 - - + Not available - + Searching... - + Name 名字 @@ -3299,45 +3298,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 0adbc9de04..3e5e4eb0bf 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1124,49 +1124,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - - + Not available - + Searching... - + Name @@ -3289,45 +3288,45 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + on 'on' is followed by an album name - + %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - - + + by preposition to link track and artist - + %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + on "%1" %1 is an album name - + "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing From 991bacd6675b9b976cbb29c234c8351d38cd3e93 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 3 Jun 2013 14:46:51 +0200 Subject: [PATCH 199/565] Add network-activity.svg to resources --- resources.qrc | 1 + 1 file changed, 1 insertion(+) diff --git a/resources.qrc b/resources.qrc index b66ef22516..67594db1fb 100644 --- a/resources.qrc +++ b/resources.qrc @@ -184,5 +184,6 @@ data/images/station-year.svg data/images/outbox.svg data/images/inbox-512x512.png + data/images/network-activity.svg From aa104fce1057b778953e9974213acc2339298585 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 3 Jun 2013 14:48:45 +0200 Subject: [PATCH 200/565] Add empty NetworkActivity page --- src/libtomahawk/CMakeLists.txt | 2 + src/libtomahawk/ViewManager.cpp | 19 ++++ src/libtomahawk/ViewManager.h | 4 + src/libtomahawk/utils/TomahawkUtils.h | 3 +- src/libtomahawk/utils/TomahawkUtilsGui.cpp | 4 + .../widgets/NetworkActivityWidget.cpp | 90 +++++++++++++++++++ .../widgets/NetworkActivityWidget.h | 58 ++++++++++++ .../widgets/NetworkActivityWidget.ui | 55 ++++++++++++ src/tomahawk/sourcetree/SourcesModel.cpp | 5 ++ 9 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/libtomahawk/widgets/NetworkActivityWidget.cpp create mode 100644 src/libtomahawk/widgets/NetworkActivityWidget.h create mode 100644 src/libtomahawk/widgets/NetworkActivityWidget.ui diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 534c1538a5..f155665dff 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -154,6 +154,7 @@ set( libGuiSources widgets/PlayableCover.cpp widgets/SocialPlaylistWidget.cpp widgets/SourceTreePopupDialog.cpp + widgets/NetworkActivityWidget.cpp widgets/infowidgets/SourceInfoWidget.cpp widgets/infowidgets/ArtistInfoWidget.cpp widgets/infowidgets/AlbumInfoWidget.cpp @@ -368,6 +369,7 @@ set( libUI ${libUI} widgets/WhatsHotWidget.ui widgets/NewReleasesWidget.ui widgets/SocialPlaylistWidget.ui + widgets/NetworkActivityWidget.ui widgets/infowidgets/SourceInfoWidget.ui widgets/infowidgets/ArtistInfoWidget.ui widgets/infowidgets/AlbumInfoWidget.ui diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index af79f7a1ab..d7171b2331 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -48,6 +48,7 @@ #include "widgets/NewReleasesWidget.h" #include "widgets/WelcomeWidget.h" #include "widgets/WhatsHotWidget.h" +#include "widgets/NetworkActivityWidget.h" #include "widgets/infowidgets/SourceInfoWidget.h" #include "widgets/infowidgets/ArtistInfoWidget.h" #include "widgets/infowidgets/AlbumInfoWidget.h" @@ -84,6 +85,7 @@ ViewManager::ViewManager( QObject* parent ) , m_recentPlaysWidget( 0 ) , m_inboxWidget( 0 ) , m_radioView( 0 ) + , m_networkActivityWidget( 0 ) , m_currentPage( 0 ) , m_loaded( false ) { @@ -131,6 +133,7 @@ ViewManager::ViewManager( QObject* parent ) ViewManager::~ViewManager() { + delete m_networkActivityWidget; delete m_whatsHotWidget; delete m_newReleasesWidget; delete m_welcomeWidget; @@ -507,6 +510,17 @@ ViewManager::showInboxPage() return show( m_inboxWidget ); } +ViewPage *ViewManager::showNetworkActivityPage() +{ + if ( !m_networkActivityWidget ) + { + m_networkActivityWidget = new NetworkActivityWidget( m_widget ); + m_networkActivityWidget->fetchData(); + } + + return show( m_networkActivityWidget ); +} + void ViewManager::setFilter( const QString& filter ) @@ -900,6 +914,11 @@ ViewManager::inboxWidget() const return m_inboxWidget; } +ViewPage *ViewManager::networkActivityWidget() const +{ + return m_networkActivityWidget; +} + Tomahawk::ViewPage* ViewManager::superCollectionView() const diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index dbe3d46ef9..7bb713d2b0 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -56,6 +56,7 @@ class WelcomeWidget; class WhatsHotWidget; class QPushButton; class InboxModel; +class NetworkActivityWidget; namespace Tomahawk { @@ -94,6 +95,7 @@ Q_OBJECT Tomahawk::ViewPage* recentPlaysWidget() const; Tomahawk::ViewPage* superCollectionView() const; Tomahawk::ViewPage* inboxWidget() const; + Tomahawk::ViewPage* networkActivityWidget() const; InboxModel* inboxModel(); @@ -140,6 +142,7 @@ public slots: Tomahawk::ViewPage* showNewReleasesPage(); Tomahawk::ViewPage* showRecentPlaysPage(); Tomahawk::ViewPage* showInboxPage(); + Tomahawk::ViewPage* showNetworkActivityPage(); void showCurrentTrack(); // Returns the shown viewpage @@ -194,6 +197,7 @@ private slots: Tomahawk::ViewPage* m_inboxWidget; Tomahawk::DynamicQmlWidget* m_radioView; InboxModel* m_inboxModel; + NetworkActivityWidget* m_networkActivityWidget; QList< Tomahawk::collection_ptr > m_superCollections; diff --git a/src/libtomahawk/utils/TomahawkUtils.h b/src/libtomahawk/utils/TomahawkUtils.h index 7d86515fec..a481874300 100644 --- a/src/libtomahawk/utils/TomahawkUtils.h +++ b/src/libtomahawk/utils/TomahawkUtils.h @@ -124,7 +124,8 @@ namespace TomahawkUtils Inbox, Invalid, InboxNewItem, - Outbox + Outbox, + NetworkActivity }; enum ImageMode diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index 3e270df881..d5445dcea7 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -722,6 +722,10 @@ defaultPixmap( ImageType type, ImageMode mode, const QSize& size ) pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/outbox.svg", size ); break; + case NetworkActivity: + pixmap = ImageRegistry::instance()->pixmap( RESPATH "images/network-activity.svg", size ); + break; + default: break; } diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp new file mode 100644 index 0000000000..34926a275a --- /dev/null +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -0,0 +1,90 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "NetworkActivityWidget.h" +#include "ui_NetworkActivityWidget.h" + +#include "utils/AnimatedSpinner.h" +#include "utils/TomahawkUtilsGui.h" + +#include + +NetworkActivityWidget::NetworkActivityWidget( QWidget *parent ) + : QWidget( parent ) + , ui( new Ui::NetworkActivityWidget ) + //, m_sortedProxy( 0 ) + //, m_loading( true ) +{ + ui->setupUi( this ); + + TomahawkUtils::unmarginLayout( layout() ); + TomahawkUtils::unmarginLayout( ui->stackLeft->layout() ); + TomahawkUtils::unmarginLayout( ui->horizontalLayout->layout() ); + TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() ); + +// m_crumbModelLeft = new QStandardItemModel( this ); +// m_sortedProxy = new QSortFilterProxyModel( this ); +// m_sortedProxy->setDynamicSortFilter( true ); +// m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); + + ui->breadCrumbLeft->setRootIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ) ); +// connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged( QModelIndex ) ) ); + + ui->tracksViewLeft->setHeaderHidden( true ); + ui->tracksViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); +// PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( ui->tracksViewLeft, ui->tracksViewLeft->proxyModel() ); +// ui->tracksViewLeft->setItemDelegate( del ); + ui->tracksViewLeft->setUniformRowHeights( false ); + + m_playlistInterface = ui->tracksViewLeft->playlistInterface(); + + // Lets have a spinner until loaded + ui->breadCrumbLeft->setVisible( false ); + m_spinner = QSharedPointer( new AnimatedSpinner( ui->tracksViewLeft ) ); + m_spinner->fadeIn(); +} + +NetworkActivityWidget::~NetworkActivityWidget() +{ + delete ui; +} + +Tomahawk::playlistinterface_ptr +NetworkActivityWidget::playlistInterface() const +{ + return m_playlistInterface; +} + +bool +NetworkActivityWidget::jumpToCurrentTrack() +{ + // TODO + return false; +} + +void +NetworkActivityWidget::fetchData() +{ + // Do not block the UI thread + QtConcurrent::run( this, &NetworkActivityWidget::actualFetchData ); +} + +void +NetworkActivityWidget::actualFetchData() +{ +} diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.h b/src/libtomahawk/widgets/NetworkActivityWidget.h new file mode 100644 index 0000000000..92b6e5fa7e --- /dev/null +++ b/src/libtomahawk/widgets/NetworkActivityWidget.h @@ -0,0 +1,58 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef NETWORKACTIVITYWIDGET_H +#define NETWORKACTIVITYWIDGET_H + +#include "ViewPage.h" + +class AnimatedSpinner; +namespace Ui +{ + class NetworkActivityWidget; +} + +class NetworkActivityWidget : public QWidget, public Tomahawk::ViewPage +{ + Q_OBJECT +public: + NetworkActivityWidget(QWidget *parent = 0); + ~NetworkActivityWidget(); + + virtual QWidget* widget() { return this; } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const; + + virtual QString title() const { return tr( "Network Activity" ); } + virtual QString description() const { return QString(); } + + virtual bool jumpToCurrentTrack(); + + void fetchData(); +signals: + +public slots: + +private: + void actualFetchData(); + + Ui::NetworkActivityWidget* ui; + Tomahawk::playlistinterface_ptr m_playlistInterface; + QSharedPointer m_spinner; +}; + +#endif // NETWORKACTIVITYWIDGET_H diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.ui b/src/libtomahawk/widgets/NetworkActivityWidget.ui new file mode 100644 index 0000000000..6ed86a3c55 --- /dev/null +++ b/src/libtomahawk/widgets/NetworkActivityWidget.ui @@ -0,0 +1,55 @@ + + + NetworkActivityWidget + + + + 0 + 0 + 875 + 513 + + + + + + + + + + 0 + + + + + + + + 320 + 0 + + + + + + + + + + + + + PlaylistView + QTreeView +
playlist/PlaylistView.h
+
+ + Tomahawk::Breadcrumb + QWidget +
widgets/Breadcrumb.h
+ 1 +
+
+ + +
diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index c068674ea8..a0112ba09c 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -317,6 +317,11 @@ SourcesModel::appendGroups() LovedTracksItem* loved = new LovedTracksItem( this, browse ); loved->setSortValue( 3 ); + GenericPageItem* networkActivity = new GenericPageItem( this, browse, tr( "Network Activity" ), TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ), + boost::bind( &ViewManager::showNetworkActivityPage, ViewManager::instance() ), + boost::bind( &ViewManager::networkActivityWidget, ViewManager::instance() ) ); + networkActivity->setSortValue( 3 ); + GenericPageItem* recent = new GenericPageItem( this, browse, tr( "Recently Played" ), ImageRegistry::instance()->icon( RESPATH "images/recently-played.svg" ), boost::bind( &ViewManager::showRecentPlaysPage, ViewManager::instance() ), boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); From c808b0989b5eab340723780ee5b7d5e76c0c0c36 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 4 Jun 2013 20:19:17 +0200 Subject: [PATCH 201/565] Add NetworkCharts Database Command --- src/libtomahawk/CMakeLists.txt | 1 + .../DatabaseCommand_NetworkCharts.cpp | 74 +++++++++++++++++++ .../database/DatabaseCommand_NetworkCharts.h | 53 +++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp create mode 100644 src/libtomahawk/database/DatabaseCommand_NetworkCharts.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index f155665dff..459bf8b4d8 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -284,6 +284,7 @@ list(APPEND libSources database/DatabaseCommand_ShareTrack.cpp database/DatabaseCommand_DeleteInboxEntry.cpp database/DatabaseCommand_ModifyInboxEntry.cpp + database/DatabaseCommand_NetworkCharts.cpp database/Database.cpp database/TomahawkSqlQuery.cpp database/IdThreadWorker.cpp diff --git a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp new file mode 100644 index 0000000000..e7a17ee300 --- /dev/null +++ b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp @@ -0,0 +1,74 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "DatabaseCommand_NetworkCharts.h" + +#include "Track.h" +#include "DatabaseImpl.h" +#include "TomahawkSqlQuery.h" + +DatabaseCommand_NetworkCharts::DatabaseCommand_NetworkCharts( const QDateTime &from, const QDateTime &to, QObject *parent ) + : DatabaseCommand( parent ) + , m_amount( 0 ) + , m_from( from ) + , m_to( to ) +{ +} + +DatabaseCommand_NetworkCharts::~DatabaseCommand_NetworkCharts() +{ +} + +void +DatabaseCommand_NetworkCharts::exec( DatabaseImpl * dbi ) +{ + TomahawkSqlQuery query = dbi->newquery(); + + QString limit; + if ( m_amount > 0 ) + { + limit = QString( "LIMIT 0, %1" ).arg( m_amount ); + } + + QString sql = QString( + "SELECT COUNT(*) as counter, track.name, artist.name " + " FROM playback_log, track, artist " + " WHERE track.id = playback_log.track AND artist.id = track.artist " + " AND playback_log.playtime >= %1 AND playback_log.playtime <= %2 " // incorportrate timespan + " AND playback_log.source IS NOT NULL " // exclude self + " GROUP BY playback_log.track " + " ORDER BY counter DESC " + " %3" + ).arg( m_from.toTime_t() ).arg( m_to.toTime_t() ).arg( limit ); + + query.prepare( sql ); + query.exec(); + + QList tracks; + while ( query.next() ) + { + Tomahawk::track_ptr track = Tomahawk::Track::get( query.value( 2 ).toString(), query.value( 1 ).toString() ); + if ( !track ) + continue; + + tracks << track; + } + + emit done( tracks ); +} + diff --git a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.h b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.h new file mode 100644 index 0000000000..a9b7ec93d9 --- /dev/null +++ b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.h @@ -0,0 +1,53 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DATABASECOMMAND_NETWORKCHARTS_H +#define DATABASECOMMAND_NETWORKCHARTS_H + +#include +#include + +#include "Typedefs.h" +#include "DatabaseCommand.h" + +#include "DllMacro.h" + +class DLLEXPORT DatabaseCommand_NetworkCharts : public DatabaseCommand +{ +Q_OBJECT +public: + explicit DatabaseCommand_NetworkCharts( const QDateTime& from, const QDateTime& to, QObject* parent = 0 ); + virtual ~DatabaseCommand_NetworkCharts(); + + virtual void exec( DatabaseImpl* ); + + virtual bool doesMutates() const { return false; } + virtual QString commandname() const { return "networkcharts"; } + + void setLimit( unsigned int amount ) { m_amount = amount; } + +signals: + void done( const QList& tracks ); + +private: + unsigned int m_amount; + QDateTime m_from; + QDateTime m_to; +}; + +#endif // DATABASECOMMAND_NETWORKCHARTS_H From 4ffbb06baa40b0fdae9b5aa900b73efd9268a4a6 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 4 Jun 2013 20:37:33 +0200 Subject: [PATCH 202/565] NetworkActivity now features Charts --- .../widgets/NetworkActivityWidget.cpp | 157 ++++++++++++++++-- .../widgets/NetworkActivityWidget.h | 25 ++- 2 files changed, 167 insertions(+), 15 deletions(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 34926a275a..209f396833 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -19,16 +19,28 @@ #include "NetworkActivityWidget.h" #include "ui_NetworkActivityWidget.h" +#include "Pipeline.h" +#include "database/Database.h" +#include "database/DatabaseCommand_NetworkCharts.h" +#include "playlist/PlaylistChartItemDelegate.h" #include "utils/AnimatedSpinner.h" #include "utils/TomahawkUtilsGui.h" +#include +#include #include +#define NETWORKCHARTS_NUM_TRACKS 100 +#define NETWORKCHARTS_WEEK_CHARTS "week" +#define NETWORKCHARTS_MONTH_CHARTS "month" +#define NETWORKCHARTS_YEAR_CHARTS "year" + +using namespace Tomahawk; + NetworkActivityWidget::NetworkActivityWidget( QWidget *parent ) : QWidget( parent ) , ui( new Ui::NetworkActivityWidget ) - //, m_sortedProxy( 0 ) - //, m_loading( true ) + , m_sortedProxy( 0 ) { ui->setupUi( this ); @@ -37,31 +49,30 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget *parent ) TomahawkUtils::unmarginLayout( ui->horizontalLayout->layout() ); TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() ); -// m_crumbModelLeft = new QStandardItemModel( this ); -// m_sortedProxy = new QSortFilterProxyModel( this ); -// m_sortedProxy->setDynamicSortFilter( true ); -// m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); + m_crumbModelLeft = new QStandardItemModel( this ); + m_sortedProxy = new QSortFilterProxyModel( this ); + m_sortedProxy->setDynamicSortFilter( true ); + m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); ui->breadCrumbLeft->setRootIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ) ); -// connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged( QModelIndex ) ) ); + connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged( QModelIndex ) ) ); ui->tracksViewLeft->setHeaderHidden( true ); ui->tracksViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); -// PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( ui->tracksViewLeft, ui->tracksViewLeft->proxyModel() ); -// ui->tracksViewLeft->setItemDelegate( del ); + PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( ui->tracksViewLeft, ui->tracksViewLeft->proxyModel() ); + ui->tracksViewLeft->setItemDelegate( del ); ui->tracksViewLeft->setUniformRowHeights( false ); m_playlistInterface = ui->tracksViewLeft->playlistInterface(); // Lets have a spinner until loaded ui->breadCrumbLeft->setVisible( false ); - m_spinner = QSharedPointer( new AnimatedSpinner( ui->tracksViewLeft ) ); + m_spinner = new AnimatedSpinner( ui->tracksViewLeft ); m_spinner->fadeIn(); } NetworkActivityWidget::~NetworkActivityWidget() { - delete ui; } Tomahawk::playlistinterface_ptr @@ -73,7 +84,9 @@ NetworkActivityWidget::playlistInterface() const bool NetworkActivityWidget::jumpToCurrentTrack() { - // TODO + if ( ui->tracksViewLeft->model() && ui->tracksViewLeft->jumpToCurrentTrack() ) + return true; + return false; } @@ -84,7 +97,127 @@ NetworkActivityWidget::fetchData() QtConcurrent::run( this, &NetworkActivityWidget::actualFetchData ); } +void +NetworkActivityWidget::weeklyCharts( const QList& tracks ) +{ + QSharedPointer locker = QSharedPointer( new QMutexLocker( &m_retrieveMutex ) ); + m_weeklyChartsModel = new PlaylistModel( ui->tracksViewLeft ); + m_weeklyChartsModel->startLoading(); + // Pipeline::instance()->resolve( tracks ); + m_weeklyChartsModel->appendTracks( tracks ); + m_weeklyChartsModel->finishLoading(); + + checkDone( locker ); +} + +void +NetworkActivityWidget::monthlyCharts( const QList& tracks ) +{ + QSharedPointer locker = QSharedPointer( new QMutexLocker( &m_retrieveMutex ) ); + m_monthlyChartsModel = new PlaylistModel( ui->tracksViewLeft ); + m_monthlyChartsModel->startLoading(); + // Pipeline::instance()->resolve( tracks ); + m_monthlyChartsModel->appendTracks( tracks ); + m_monthlyChartsModel->finishLoading(); + + checkDone( locker ); +} + +void +NetworkActivityWidget::yearlyCharts( const QList& tracks ) +{ + QSharedPointer locker = QSharedPointer( new QMutexLocker( &m_retrieveMutex ) ); + m_yearlyChartsModel = new PlaylistModel( ui->tracksViewLeft ); + m_yearlyChartsModel->startLoading(); + // Pipeline::instance()->resolve( tracks ); + m_yearlyChartsModel->appendTracks( tracks ); + m_yearlyChartsModel->finishLoading(); + + checkDone( locker ); +} + +void +NetworkActivityWidget::leftCrumbIndexChanged( QModelIndex index ) +{ + QStandardItem* item = m_crumbModelLeft->itemFromIndex( m_sortedProxy->mapToSource( index ) ); + if ( !item ) + return; + if ( !item->data( Breadcrumb::ChartIdRole ).isValid() ) + return; + + const QString chartId = item->data( Breadcrumb::ChartIdRole ).toString(); + if ( chartId == NETWORKCHARTS_WEEK_CHARTS ) + { + ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + ui->tracksViewLeft->setPlaylistModel( m_weeklyChartsModel ); + ui->tracksViewLeft->proxyModel()->sort( -1 ); + } + else if ( chartId == NETWORKCHARTS_MONTH_CHARTS ) + { + ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + ui->tracksViewLeft->setPlaylistModel( m_monthlyChartsModel ); + ui->tracksViewLeft->proxyModel()->sort( -1 ); + } + else if ( chartId == NETWORKCHARTS_YEAR_CHARTS ) + { + ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + ui->tracksViewLeft->setPlaylistModel( m_yearlyChartsModel ); + ui->tracksViewLeft->proxyModel()->sort( -1 ); + } +} + void NetworkActivityWidget::actualFetchData() { + QDateTime to = QDateTime::currentDateTime(); + + // Weekly charts + QDateTime weekAgo = to.addDays( -7 ); + DatabaseCommand_NetworkCharts* weekCharts = new DatabaseCommand_NetworkCharts( weekAgo, to ); + weekCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); + connect( weekCharts, SIGNAL( done( QList ) ), SLOT( weeklyCharts( QList ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( weekCharts ) ); + + // Monthly charts + QDateTime monthAgo = to.addMonths( -1 ); + DatabaseCommand_NetworkCharts* monthCharts = new DatabaseCommand_NetworkCharts( monthAgo, to ); + monthCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); + connect( monthCharts, SIGNAL( done( QList ) ), SLOT( monthlyCharts( QList ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( monthCharts ) ); + + // Yearly charts + QDateTime yearAgo = to.addYears( -1 ); + DatabaseCommand_NetworkCharts* yearCharts = new DatabaseCommand_NetworkCharts( yearAgo, to ); + yearCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); + connect( yearCharts, SIGNAL( done( QList ) ), SLOT( yearlyCharts( QList ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( yearCharts ) ); +} + +void +NetworkActivityWidget::checkDone( QSharedPointer ) +{ + if ( !m_weeklyChartsModel.isNull() && !m_yearlyChartsModel.isNull() && !m_monthlyChartsModel.isNull() ) + { + // All charts are loaded, do the remaining work UI work. + + // Build up breadcrumb + QStandardItem* rootItem = m_crumbModelLeft->invisibleRootItem(); + QStandardItem* chartItem = new QStandardItem( tr( "Charts" ) ); + rootItem->appendRow( chartItem ); + QStandardItem* weekItem = new QStandardItem( tr( "Last Week" ) ); + weekItem->setData( NETWORKCHARTS_WEEK_CHARTS, Breadcrumb::ChartIdRole ); + chartItem->appendRow( weekItem ); + QStandardItem* monthItem = new QStandardItem( tr( "Last Month" ) ); + monthItem->setData( NETWORKCHARTS_MONTH_CHARTS, Breadcrumb::ChartIdRole ); + chartItem->appendRow( monthItem ); + QStandardItem* yearItem = new QStandardItem( tr( "Last Year" ) ); + yearItem->setData( NETWORKCHARTS_YEAR_CHARTS, Breadcrumb::ChartIdRole ); + chartItem->appendRow( yearItem ); + m_sortedProxy->setSourceModel( m_crumbModelLeft ); + m_sortedProxy->sort( 0, Qt::AscendingOrder ); + ui->breadCrumbLeft->setModel( m_sortedProxy ); + + m_spinner->fadeOut(); + ui->breadCrumbLeft->setVisible( true ); + } } diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.h b/src/libtomahawk/widgets/NetworkActivityWidget.h index 92b6e5fa7e..904bb886b0 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.h +++ b/src/libtomahawk/widgets/NetworkActivityWidget.h @@ -22,6 +22,10 @@ #include "ViewPage.h" class AnimatedSpinner; +class PlaylistModel; +class QModelIndex; +class QStandardItemModel; +class QSortFilterProxyModel; namespace Ui { class NetworkActivityWidget; @@ -45,14 +49,29 @@ class NetworkActivityWidget : public QWidget, public Tomahawk::ViewPage void fetchData(); signals: -public slots: +private slots: + void weeklyCharts( const QList& ); + void monthlyCharts( const QList& ); + void yearlyCharts( const QList& ); + + void leftCrumbIndexChanged( QModelIndex ); private: void actualFetchData(); + void checkDone( QSharedPointer ); + + QMutex m_retrieveMutex; - Ui::NetworkActivityWidget* ui; + QSharedPointer ui; Tomahawk::playlistinterface_ptr m_playlistInterface; - QSharedPointer m_spinner; + AnimatedSpinner* m_spinner; + QStandardItemModel* m_crumbModelLeft; + QSortFilterProxyModel* m_sortedProxy; + + QPointer m_weeklyChartsModel; + QPointer m_monthlyChartsModel; + QPointer m_yearlyChartsModel; + }; #endif // NETWORKACTIVITYWIDGET_H From 213edc6d128cf1b3b066267dfdd344eed07393dd Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 4 Jun 2013 21:25:37 +0200 Subject: [PATCH 203/565] Hide InfoBar and report isBeingPlayed status. --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 10 ++++++++++ src/libtomahawk/widgets/NetworkActivityWidget.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 209f396833..3391ac3edd 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -20,6 +20,7 @@ #include "ui_NetworkActivityWidget.h" #include "Pipeline.h" +#include "audio/AudioEngine.h" #include "database/Database.h" #include "database/DatabaseCommand_NetworkCharts.h" #include "playlist/PlaylistChartItemDelegate.h" @@ -81,6 +82,15 @@ NetworkActivityWidget::playlistInterface() const return m_playlistInterface; } +bool +NetworkActivityWidget::isBeingPlayed() const +{ + if ( AudioEngine::instance()->currentTrackPlaylist() == ui->tracksViewLeft->playlistInterface() ) + return true; + + return false; +} + bool NetworkActivityWidget::jumpToCurrentTrack() { diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.h b/src/libtomahawk/widgets/NetworkActivityWidget.h index 904bb886b0..398f22aef1 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.h +++ b/src/libtomahawk/widgets/NetworkActivityWidget.h @@ -44,6 +44,9 @@ class NetworkActivityWidget : public QWidget, public Tomahawk::ViewPage virtual QString title() const { return tr( "Network Activity" ); } virtual QString description() const { return QString(); } + virtual bool showInfoBar() const { return false; } + virtual bool isBeingPlayed() const; + virtual bool jumpToCurrentTrack(); void fetchData(); From 183d7be141cbe84160ab190fb00f8128dd6efdc6 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 4 Jun 2013 21:26:01 +0200 Subject: [PATCH 204/565] Do not delete connection if PeerInfo disappers during connect --- src/libtomahawk/network/ConnectionManager.cpp | 3 +++ src/libtomahawk/network/ControlConnection.cpp | 13 ++++++++++++- src/libtomahawk/network/ControlConnection.h | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index adfcaf1ad1..14a329d022 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -116,6 +116,7 @@ ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool l m["nodeid"] = Database::instance()->impl()->dbid(); m_controlConnection = QPointer( new ControlConnection( Servent::instance() ) ); + m_controlConnection->setShutdownOnEmptyPeerInfos( false ); m_controlConnection->addPeerInfo( peerInfo ); m_controlConnection->setFirstMessage( m ); @@ -238,6 +239,8 @@ ConnectionManager::handoverSocket( QTcpSocketExtra* sock ) m_controlConnection->setPeerPort( sock->peerPort() ); m_controlConnection->start( sock ); + // ControlConntection is now connected, now it can be destroyed if the PeerInfos disappear + m_controlConnection->setShutdownOnEmptyPeerInfos( true ); m_currentPeerInfo.clear(); m_mutex.unlock(); } diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 10596fbe45..3110517804 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -39,6 +39,7 @@ ControlConnection::ControlConnection( Servent* parent ) , m_dbsyncconn( 0 ) , m_registered( false ) , m_pingtimer( 0 ) + , m_shutdownOnEmptyPeerInfos( true ) { qDebug() << "CTOR controlconnection"; setId("ControlConnection()"); @@ -305,7 +306,17 @@ ControlConnection::removePeerInfo( const peerinfo_ptr& peerInfo ) m_peerInfos.remove( peerInfo ); - if ( m_peerInfos.isEmpty() ) + if ( m_peerInfos.isEmpty() && m_shutdownOnEmptyPeerInfos ) + { + shutdown( true ); + } +} + +void +ControlConnection::setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos ) +{ + m_shutdownOnEmptyPeerInfos = shutdownOnEmptyPeerInfos; + if ( m_peerInfos.isEmpty() && m_shutdownOnEmptyPeerInfos ) { shutdown( true ); } diff --git a/src/libtomahawk/network/ControlConnection.h b/src/libtomahawk/network/ControlConnection.h index 470f92cc20..a8bde2d158 100644 --- a/src/libtomahawk/network/ControlConnection.h +++ b/src/libtomahawk/network/ControlConnection.h @@ -50,6 +50,7 @@ Q_OBJECT void addPeerInfo( const Tomahawk::peerinfo_ptr& peerInfo ); void removePeerInfo( const Tomahawk::peerinfo_ptr& peerInfo ); + void setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos ); const QSet< Tomahawk::peerinfo_ptr > peerInfos() const; protected: @@ -71,6 +72,7 @@ private slots: QString m_dbconnkey; bool m_registered; + bool m_shutdownOnEmptyPeerInfos; QTimer* m_pingtimer; QTime m_pingtimer_mark; From f199f0828879c18eacad97ee06bbe29ab2d62e02 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Wed, 5 Jun 2013 02:16:57 +0200 Subject: [PATCH 205/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_bg.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_bn_IN.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_ca.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_ca@valencia.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_cs.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_da.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_de.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_el.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_en.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_es.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_fi.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_fr.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_gl.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_hi_IN.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_hu.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_id.ts | 65 +++++++++++++++++++++++++++--------- lang/tomahawk_it.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_ja.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_lt.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_pl.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_pt_BR.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_ru.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_sv.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_tr.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_zh_CN.ts | 63 +++++++++++++++++++++++++--------- lang/tomahawk_zh_TW.ts | 63 +++++++++++++++++++++++++--------- 27 files changed, 1297 insertions(+), 406 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index e21e06d311..188323ce2a 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1026,6 +1026,34 @@ Password خصائص
+ + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2074,7 +2102,12 @@ Password سوبر كولكشن - + + Network Activity + + + + Cloud سحابة @@ -2084,22 +2117,22 @@ Password لوحة القيادة
- + Recently Played تم الاستماع لها مؤخرا - + Charts الرسوم البيانية - + New Releases جديد الاصدارات - + Friends الأصدقاء @@ -4008,48 +4041,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox البريد الوارد - + Listening suggestions from your friends إقتراحات للإستماع من قبل اصدقاءك - - + + This playlist is empty! هذه المجموعة فارغة! - + SuperCollection سوبر كولكشن - + Combined libraries of all your online friends مكتبات مجمعة لكل اصحابك المتصلين - + Recently Played Tracks الأغاني التي إستمعت إليها مؤخرا - + Recently played tracks from all your friends جميع الأغاني التي استمع إليها أصدقائك مؤخرا - + Sorry, we could not find any recent plays! نعتذر، لم نستطيع إيجاد أغاني مسموعة مؤخرا! - + No listening suggestions here. لا إقتراحات للإستماع هنا. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 52d254e765..3f3f134dc3 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1032,6 +1032,34 @@ Password Настройки
+ + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2086,7 +2114,12 @@ Password Обща колекция - + + Network Activity + + + + Cloud @@ -2096,22 +2129,22 @@ Password Табло
- + Recently Played Наскоро изпълнени песни - + Charts Класации - + New Releases Нови албуми - + Friends Приятели @@ -4024,49 +4057,49 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Списъка е празен! - + SuperCollection Обща колекция /Сборен изглед от локалните и наличните в колекциите на приятелите ти изпълнения/ - + Combined libraries of all your online friends Обща колекция с всичките ми приятели на линия - + Recently Played Tracks Наскоро изпълени песни - + Recently played tracks from all your friends Наскоро изпълнени песни от всичките ти приятели - + Sorry, we could not find any recent plays! Съжалявам, но не откривам скорошни списъци - + No listening suggestions here. diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 46eba46a7d..9856310a9f 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1025,6 +1025,34 @@ Password
+ + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password - + + Network Activity + + + + Cloud @@ -2081,22 +2114,22 @@ Password
- + Recently Played - + Charts - + New Releases - + Friends @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 95921ea896..d901bd1123 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1025,6 +1025,34 @@ Password Propietats + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2075,7 +2103,12 @@ i emissores basades en els vostres gusts musicals. SuperCol·lecció - + + Network Activity + + + + Cloud @@ -2085,22 +2118,22 @@ i emissores basades en els vostres gusts musicals. Presentació - + Recently Played Escoltades Recentment - + Charts Llistes - + New Releases Nous Llançaments - + Friends Amics @@ -4009,48 +4042,48 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! La llista de reproducció és buida - + SuperCollection SuperCol·lecció - + Combined libraries of all your online friends Biblioteques combinades de tots els amis en línia - + Recently Played Tracks Cançons Escoltades Recentment - + Recently played tracks from all your friends Cançons escoltades recentment pels amics - + Sorry, we could not find any recent plays! No s'ha trobat cap reproducció recent - + No listening suggestions here. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index bc0fef95dd..f7fbde8682 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1025,6 +1025,34 @@ Password Propietats + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2075,7 +2103,12 @@ i emissores basades en els vostres gusts musicals. SuperCol·lecció - + + Network Activity + + + + Cloud @@ -2085,22 +2118,22 @@ i emissores basades en els vostres gusts musicals. Presentació - + Recently Played Escoltades Recentment - + Charts Llistes - + New Releases Nous Llançaments - + Friends Amics @@ -4009,48 +4042,48 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! La llista de reproducció és buida - + SuperCollection SuperCol·lecció - + Combined libraries of all your online friends Biblioteques combinades de tots els amis en línia - + Recently Played Tracks Cançons Escoltades Recentment - + Recently played tracks from all your friends Cançons escoltades recentment pels amics - + Sorry, we could not find any recent plays! No s'ha trobat cap reproducció recent - + No listening suggestions here. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 501d11d096..a4e54f7ac8 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1027,6 +1027,34 @@ heslo Vlastnosti + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2074,7 +2102,12 @@ heslo Supersbírka - + + Network Activity + + + + Cloud Mračno @@ -2084,22 +2117,22 @@ heslo Přístrojová deska - + Recently Played Nedávno poslouchané - + Charts Žebříčky - + New Releases Novinky - + Friends Přátelé @@ -4008,48 +4041,48 @@ Kdykoli můžete odeslat novou seřizovací zprávu. ViewManager - + Inbox Doručené - + Listening suggestions from your friends Sledování návrhů od vašich přátel - - + + This playlist is empty! Tento seznam skladeb je prázdný! - + SuperCollection Supersbírka - + Combined libraries of all your online friends Spojená sbírka všech vašich přátel - + Recently Played Tracks Nedávno poslouchané skladby - + Recently played tracks from all your friends Naposledy poslouchané skladby všech vašich přátel - + Sorry, we could not find any recent plays! Promiňte, ale nepodařilo se najít žádné nedávno poslouchané skladby! - + No listening suggestions here. Žádné sledování návrhů. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 0e64a7355f..8fcf6c35b5 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1025,6 +1025,34 @@ Password + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2072,7 +2100,12 @@ Password SuperSamling - + + Network Activity + + + + Cloud @@ -2082,22 +2115,22 @@ Password Instrumentbræt - + Recently Played Nyligt Afspillede - + Charts Lister - + New Releases - + Friends Venner @@ -3991,48 +4024,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection SuperSamling - + Combined libraries of all your online friends Alle dine venners kombineret bibliotek - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 808ff4b480..2ce7d2a4f7 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1027,6 +1027,34 @@ Passwort Eigenschaften + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2074,7 +2102,12 @@ Passwort Supersammlung - + + Network Activity + + + + Cloud Cloud @@ -2084,22 +2117,22 @@ Passwort Dashboard - + Recently Played Kürzlich gehörte Lieder - + Charts Charts - + New Releases Neuerscheinungen - + Friends Freunde @@ -4003,48 +4036,48 @@ Du kannst jederzeit eine neue Sync-Nachricht abschicken. ViewManager - + Inbox Posteingang - + Listening suggestions from your friends Hören sie sich die Vorschläge ihrer Freunde an - - + + This playlist is empty! Diese Playlist ist leer! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Kombinierte Sammlung all deiner Freunde - + Recently Played Tracks Zuletzt gehörte Lieder - + Recently played tracks from all your friends Zuletzt gehörte Lieder all deiner Freunde - + Sorry, we could not find any recent plays! Es konnten keine zuletzt gehörten Songs gefunden werden! - + No listening suggestions here. Es gibt keine Vorschläge hier. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 63ea857f9b..877afb3f25 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1026,6 +1026,34 @@ Password Ιδιότητες + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2074,7 +2102,12 @@ Password ΥπερΣυλλογή - + + Network Activity + + + + Cloud Σύννεφο @@ -2084,22 +2117,22 @@ Password Πίνακας Ελέγχου - + Recently Played Τελευταίες Αναπαραγωγές - + Charts Γραφήματα - + New Releases Νέες Κυκλοφορίες - + Friends Φίλοι @@ -4004,48 +4037,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Εισερχόμενα - + Listening suggestions from your friends Προτάσεις προς ακρόαση από τους φίλους σας - - + + This playlist is empty! Αυτη η λιστα αναπαραγωγης ειναι αδεια. - + SuperCollection ΥπερΣυλλογή - + Combined libraries of all your online friends Συνδυασμένες βιβλιοθήκες όλων των online φίλων σας - + Recently Played Tracks Τελευταίες Αναπαραγωγές Κομματιών - + Recently played tracks from all your friends Τελευταίες αναπαραγωγές από όλους τους φίλους σας - + Sorry, we could not find any recent plays! Συγγνωμη, δεν βρεθηκαν προσφατες αναπαραγωγες! - + No listening suggestions here. Δεν υπάρχουν προτάσεις για ακρόαση εδώ. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index ae9f9b8128..f6141e646e 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1027,6 +1027,34 @@ Password Properties + + NetworkActivityWidget + + + Charts + Charts + + + + Last Week + Last Week + + + + Last Month + Last Month + + + + Last Year + Last Year + + + + Network Activity + Network Activity + + NewPlaylistWidget @@ -2077,7 +2105,12 @@ Password SuperCollection - + + Network Activity + Network Activity + + + Cloud Cloud @@ -2087,22 +2120,22 @@ Password Dashboard - + Recently Played Recently Played - + Charts Charts - + New Releases New Releases - + Friends Friends @@ -4012,48 +4045,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Inbox - + Listening suggestions from your friends Listening suggestions from your friends - - + + This playlist is empty! This playlist is empty! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Combined libraries of all your online friends - + Recently Played Tracks Recently Played Tracks - + Recently played tracks from all your friends Recently played tracks from all your friends - + Sorry, we could not find any recent plays! Sorry, we could not find any recent plays! - + No listening suggestions here. No listening suggestions here. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 2ab32faa27..ac5a333103 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1026,6 +1026,34 @@ Password Propiedades + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2076,7 +2104,12 @@ y estaciones basadas en sus gustos personales. Supercolección - + + Network Activity + + + + Cloud Nube @@ -2086,22 +2119,22 @@ y estaciones basadas en sus gustos personales. Panel de inicio - + Recently Played Reproducido recientemente - + Charts Listas - + New Releases Últimas novedades - + Friends Amigos @@ -4010,48 +4043,48 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Lista de reproducción vacía - + SuperCollection Supercolección - + Combined libraries of all your online friends Colecciones combinadas de todos sus amigos - + Recently Played Tracks Pistas reproducidas recientemente - + Recently played tracks from all your friends Temas escuchados recientemente por mis amigos - + Sorry, we could not find any recent plays! No hay reproducciones recientes - + No listening suggestions here. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 635337605e..a2f1595b8f 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1027,6 +1027,34 @@ salasana Ominaisuudet + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2079,7 +2107,12 @@ käyttäjäradion käyttöönottamiseksi Superkokoelma - + + Network Activity + + + + Cloud Pilvi @@ -2089,22 +2122,22 @@ käyttäjäradion käyttöönottamiseksi Kojelauta - + Recently Played Viime aikoina kuunnellut - + Charts Listat - + New Releases Uudet julkaisut - + Friends Kaverit @@ -4015,48 +4048,48 @@ Voit lähettää synkronointiviestin uudelleen millä hetkellä hyvänsä lähet ViewManager - + Inbox Saapuneet - + Listening suggestions from your friends Kaveriesi lähettämät kuunteluehdotukset - - + + This playlist is empty! Tämä soittolista on tyhjä! - + SuperCollection Superkokoelma - + Combined libraries of all your online friends Kaikkien verkkokaveriesi yhdistetyt kirjastot - + Recently Played Tracks Viime aikoina kuunnellut kappaleet - + Recently played tracks from all your friends Kaikkien kaveriesi viime aikoina kuuntelemat kappaleet - + Sorry, we could not find any recent plays! Valitettavasti emme löytäneet yhtään viimeaikaisia soittoja! - + No listening suggestions here. Ei kuunteluehdotuksia. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index b203e4fd8d..10cb59b68b 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1026,6 +1026,34 @@ Password Propriétés + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2073,7 +2101,12 @@ Password SuperCollection - + + Network Activity + + + + Cloud Cloud @@ -2083,22 +2116,22 @@ Password Tableau de bord - + Recently Played Joués récemment - + Charts Charts - + New Releases Nouveautés - + Friends Amis @@ -4007,48 +4040,48 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env ViewManager - + Inbox Boîte de réception - + Listening suggestions from your friends Suggestions d'écoute de vos amis - - + + This playlist is empty! Cette liste de lecture est vide! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Collections regroupant toutes celles de vos amis en ligne - + Recently Played Tracks Derniers titres joués - + Recently played tracks from all your friends Derniers titres joués par vos amis - + Sorry, we could not find any recent plays! Désolé, aucune piste récemment jouée n'a pu être trouvée ! - + No listening suggestions here. Aucune suggestion d'écoute. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index b89446499d..940cd0ca13 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1025,6 +1025,34 @@ Password Propiedades + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2074,7 +2102,12 @@ Password Supercolección - + + Network Activity + + + + Cloud Nube @@ -2084,22 +2117,22 @@ Password Taboleiro - + Recently Played Escoitadas recentemente - + Charts Gráficos - + New Releases Novos lanzamentos - + Friends Amizades @@ -4009,48 +4042,48 @@ Podes reenviar e sincronizar as mensaxes en calquera momento simplemente enviand ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Esta lista de reprodución está baleira! - + SuperCollection Supercolección - + Combined libraries of all your online friends Bibliotecas combinadas de todas as túas amizades - + Recently Played Tracks Pistas recentemente reproducidas - + Recently played tracks from all your friends Pistas escoitadas recentemente polas túas amizades - + Sorry, we could not find any recent plays! Non se atoparan reproducións recentes! - + No listening suggestions here. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 0ce5a574d0..57faedbf51 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1025,6 +1025,34 @@ Password + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password - + + Network Activity + + + + Cloud @@ -2081,22 +2114,22 @@ Password - + Recently Played - + Charts - + New Releases - + Friends मित्रगण @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index d570653e6c..3b0b8b2249 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1025,6 +1025,34 @@ Password Tulajdonságok + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password Szuper kollekció - + + Network Activity + + + + Cloud @@ -2081,22 +2114,22 @@ Password - + Recently Played Monstanában játszott - + Charts - + New Releases Új kiadások - + Friends Barátok @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Ez a lejátszólista üres! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks Mostanában játszott zeneszámok - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index c7463b458f..b7b64b7f9b 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -301,7 +301,7 @@ connect and stream from you? All albums - + Semua album @@ -1025,6 +1025,34 @@ Password + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password - + + Network Activity + + + + Cloud @@ -2081,22 +2114,22 @@ Password - + Recently Played - + Charts - + New Releases - + Friends @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index a0848d12ae..6d50915a5f 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1025,6 +1025,34 @@ Password Proprietà + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password Supercollezione - + + Network Activity + + + + Cloud Cloud @@ -2081,22 +2114,22 @@ Password Cruscotto - + Recently Played Ascoltate recentemente - + Charts Classifiche - + New Releases Nuove uscite - + Friends Amici @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Inbox - + Listening suggestions from your friends Suggerimenti per l'ascolto dai tuoi amici. - - + + This playlist is empty! Questa playlist è vuota! - + SuperCollection Supercollezione - + Combined libraries of all your online friends Collezioni combinate di tutti i tuoi amici online - + Recently Played Tracks Tracce ascoltate di recente - + Recently played tracks from all your friends Tracce ascoltate di recente dai tuoi amici - + Sorry, we could not find any recent plays! Spiacente, non sono riuscito a trovare nessuna riproduzione recente! - + No listening suggestions here. Nessun suggerimento musicale qui. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 832bd5ce75..59dbbb65c4 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1026,6 +1026,34 @@ Password 情報 + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2076,7 +2104,12 @@ Password スーパーコレクション - + + Network Activity + + + + Cloud @@ -2086,22 +2119,22 @@ Password ダッシュボード - + Recently Played 最近聴いたトラック - + Charts チャート - + New Releases ニューリリース - + Friends 友達 @@ -4007,48 +4040,48 @@ Twitterを使っている友達にTomahawkを接続したいなら、ツイー ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! このプレイリストには何も入っていません。 - + SuperCollection スーパーコレクション - + Combined libraries of all your online friends オンラインの友達全員のライブラリ - + Recently Played Tracks 最近再生したトラック - + Recently played tracks from all your friends 友達の最近再生したトラック - + Sorry, we could not find any recent plays! 最近の再生した項目が見つかりませんでした。 - + No listening suggestions here. diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 13123af552..a1e0307285 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1025,6 +1025,34 @@ Password + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password Super Kolekcija - + + Network Activity + + + + Cloud @@ -2081,22 +2114,22 @@ Password Skydelis - + Recently Played Neseniai klausyta - + Charts - + New Releases Nauji leidimai - + Friends Draugai @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection Super kolekcija - + Combined libraries of all your online friends Jungtinė visų Jūsų prisijungusių draugų kolekcija - + Recently Played Tracks Neseniai groti takeliai - + Recently played tracks from all your friends Visų Jūsų draugų neseniai groti takeliai - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index e601964a96..28556fc398 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1026,6 +1026,34 @@ Password + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2073,7 +2101,12 @@ Password Superkolekcja - + + Network Activity + + + + Cloud @@ -2083,22 +2116,22 @@ Password Tablica kontrolna - + Recently Played Ostatnio Odtworzone - + Charts Listy Przebojów - + New Releases Nowe Wydania - + Friends Znajomi @@ -4004,48 +4037,48 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection SuperKolekcja - + Combined libraries of all your online friends Połączone biblioteki wszystkich twoich znajomych online - + Recently Played Tracks Ostatnio odtwarzane utwory - + Recently played tracks from all your friends Utwory ostatnio odtwarzane przez twoich znajomych - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 336df3a60f..e9c255c453 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1026,6 +1026,34 @@ Password Propriedades + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2073,7 +2101,12 @@ Password SuperColeção - + + Network Activity + + + + Cloud @@ -2083,22 +2116,22 @@ Password Painel - + Recently Played Ouvidas Recentemente - + Charts Charts - + New Releases Lançamentos - + Friends Amigos @@ -4004,48 +4037,48 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Essa lista de reprodução está vazia! - + SuperCollection SuperColeção - + Combined libraries of all your online friends Bibliotecas combinadas de todos os seus amigos online - + Recently Played Tracks Faixas Reproduzidas Recentemente - + Recently played tracks from all your friends Faixas reproduzidas recentemente por todos os seus amigos - + Sorry, we could not find any recent plays! Desculpe, não foi possível encontrar reproduções recentes! - + No listening suggestions here. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 85259ebcc1..46f78ed201 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1029,6 +1029,34 @@ Password Свойства + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2079,7 +2107,12 @@ Password Общая Коллекция - + + Network Activity + + + + Cloud Облако @@ -2089,22 +2122,22 @@ Password Главная Панель - + Recently Played Последние Воспроизводимые - + Charts Чарты - + New Releases Новые Релизы - + Friends Друзья @@ -4010,48 +4043,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Плейлист пуст. - + SuperCollection Общая Коллекция - + Combined libraries of all your online friends Комбинированная библиотека всех ваших друзей онлайн - + Recently Played Tracks Последние Воспроизводимые Песни - + Recently played tracks from all your friends Последние воспроизводимые песни все ваших друзей - + Sorry, we could not find any recent plays! К сожалению, мы не смогли найти никаких воспроизвидений песен! - + No listening suggestions here. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index ecbcdd4485..ec2eedc2f3 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1027,6 +1027,34 @@ Password Egenskaper + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2076,7 +2104,12 @@ och radiostationer baserat på din personliga profil Supersamling - + + Network Activity + + + + Cloud Moln @@ -2086,22 +2119,22 @@ och radiostationer baserat på din personliga profil Dashboard - + Recently Played Nyligen Spelade - + Charts Topplistor - + New Releases Nya släpp - + Friends Vänner @@ -4010,48 +4043,48 @@ Du kan skicka om ett synkat meddelande när som helst genom att skicka ett tweet ViewManager - + Inbox Inkorg - + Listening suggestions from your friends Lyssnar-rekommendationer från dina vänner - - + + This playlist is empty! Spellistan är tom! - + SuperCollection Supersamling - + Combined libraries of all your online friends Kombinerat bibliotek av alla dina vänner online - + Recently Played Tracks Senast spelade spår - + Recently played tracks from all your friends Alla dina vänners senast spelade spår - + Sorry, we could not find any recent plays! Tyvärr! Det gick inte hitta några nyligen spelade spår - + No listening suggestions here. Det finns inga rekommendationer här. diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 097b7f9e67..1f1a4d78d1 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1025,6 +1025,34 @@ Password + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password - + + Network Activity + + + + Cloud @@ -2081,22 +2114,22 @@ Password - + Recently Played - + Charts - + New Releases - + Friends @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index feef93352e..4ff98a919b 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1025,6 +1025,34 @@ Password 属性 + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2074,7 +2102,12 @@ Password 超级收藏 - + + Network Activity + + + + Cloud @@ -2084,22 +2117,22 @@ Password 概览 - + Recently Played 最近播放 - + Charts 排行榜 - + New Releases 新专辑 - + Friends 朋友们 @@ -4005,48 +4038,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! 当前播放列表为空! - + SuperCollection 超级收藏 - + Combined libraries of all your online friends 当前在线的朋友的音乐库集合 - + Recently Played Tracks 最近播放歌曲 - + Recently played tracks from all your friends 所有朋友最近播放的歌曲 - + Sorry, we could not find any recent plays! 对不起,找不到任何最近播放项目! - + No listening suggestions here. diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 3e5e4eb0bf..425210a7b7 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1025,6 +1025,34 @@ Password + + NetworkActivityWidget + + + Charts + + + + + Last Week + + + + + Last Month + + + + + Last Year + + + + + Network Activity + + + NewPlaylistWidget @@ -2071,7 +2099,12 @@ Password 超級收藏 - + + Network Activity + + + + Cloud @@ -2081,22 +2114,22 @@ Password 儀表板 - + Recently Played 最近播放的 - + Charts - + New Releases 新版本 - + Friends 朋友 @@ -3989,48 +4022,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection 超級收藏 - + Combined libraries of all your online friends 聯合您所有線上朋友的音樂庫 - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. From f8b2d792df55443351836825ec3e7b442c2baddd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 03:56:23 +0200 Subject: [PATCH 206/565] * Style fixes for build system. --- CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 216a9e8d16..666d891ea3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,8 +43,8 @@ option(WITH_BREAKPAD "Build with breakpad integration" ON) option(WITH_CRASHREPORTER "Build with CrashReporter" ON) option(WITH_BINARY_ATTICA "Enable support for downloading binary resolvers automatically" ON) option(LEGACY_KDE_INTEGRATION "Install tomahawk.protocol file, deprecated since 4.6.0" OFF) -OPTION(WITH_UPOWER "Build with support for UPower events" OFF) -OPTION(WITH_GNOMESHORTCUTHANDLER "Build with shortcut handler for GNOME" OFF) +option(WITH_UPOWER "Build with support for UPower events" OFF) +option(WITH_GNOMESHORTCUTHANDLER "Build with shortcut handler for GNOME" OFF) IF( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" ) message(STATUS "Build of breakpad library disabled on this platform.") @@ -75,7 +75,7 @@ ENDIF() IF( NOT BUILD_RELEASE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git/" ) INCLUDE( CMakeDateStamp ) SET( TOMAHAWK_VERSION_DATE "${CMAKE_DATESTAMP_YEAR}${CMAKE_DATESTAMP_MONTH}${CMAKE_DATESTAMP_DAY}" ) - IF( TOMAHAWK_VERSION_DATE GREATER 0) + IF( TOMAHAWK_VERSION_DATE GREATER 0 ) SET( TOMAHAWK_VERSION ${TOMAHAWK_VERSION}.${TOMAHAWK_VERSION_DATE} ) ENDIF() @@ -111,9 +111,9 @@ else() endif() -if(NOT BUILD_WITH_QT4) +if( NOT BUILD_WITH_QT4 ) find_package(Qt5Core QUIET) - if(Qt5Core_DIR) + if( Qt5Core_DIR ) find_package(Qt5Widgets QUIET) message(STATUS "Found Qt5! Be aware that Qt5-support is still experimental and not officially supported!") @@ -139,7 +139,7 @@ if(NOT BUILD_WITH_QT4) set(WITH_BREAKPAD OFF) endif() endif() -if(NOT Qt5Core_DIR) +if( NOT Qt5Core_DIR ) message(STATUS "Could not find Qt5, searching for Qt4 instead...") set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork") @@ -169,10 +169,10 @@ if(NOT Qt5Core_DIR) endmacro() macro(setup_qt) - if(NOT BUILD_GUI) + if( NOT BUILD_GUI ) set(QT_DONT_USE_QTGUI TRUE) endif() - if(UNIX AND NOT APPLE) + if( UNIX AND NOT APPLE ) set(QT_USE_QTDBUS TRUE) endif() set(QT_USE_QTSQL TRUE) @@ -185,7 +185,7 @@ if(NOT Qt5Core_DIR) endmacro() endif() -if(BUILD_GUI AND UNIX AND NOT APPLE) +if( BUILD_GUI AND UNIX AND NOT APPLE ) find_package( X11 ) endif() @@ -197,7 +197,7 @@ ENDIF( UNIX AND NOT APPLE AND QT_QTDBUS_FOUND ) macro_optional_find_package(Phonon 4.5.0) macro_log_feature(PHONON_FOUND "Phonon" "The Phonon multimedia library" "http://phonon.kde.org" TRUE "" "") -if(PHONON_FOUND) +if( PHONON_FOUND ) message(STATUS "Phonon found; ensure that phonon-vlc is at least 0.4") endif() @@ -229,7 +229,7 @@ macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloa macro_optional_find_package(Jreen 1.0.5) macro_log_feature(JREEN_FOUND "Jreen" "Qt XMPP Library" "http://qutim.org/jreen / https://github.com/euroelessar/jreen" FALSE "" "Jreen is needed for the Jabber SIP plugin.\n") -if(PC_JREEN_VERSION STREQUAL "1.1.0") +if( PC_JREEN_VERSION STREQUAL "1.1.0" ) message(FATAL_ERROR "Jreen 1.1.0 has a very annoying bug that breaks accepting auth requests in Tomahawk. Please upgrade to 1.1.1 or downgrade to 1.0.5.") endif() @@ -284,7 +284,7 @@ macro_optional_find_package(KDE4Installed) # for future kde integration # macro_optional_find_package(KDE4) -IF(KDE4_FOUND) +IF( KDE4_FOUND ) IF( CMAKE_C_FLAGS ) # KDE4 adds and removes some compiler flags that we don't like # (only for gcc not for clang e.g.) From 9a2594c644c10fb28d67c9cf55fb3e91318eaa83 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 09:15:07 +0200 Subject: [PATCH 207/565] * Style fixes for build system. --- src/tomahawk/CMakeLists.osx.cmake | 6 +++--- src/tomahawk/CMakeLists.txt | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tomahawk/CMakeLists.osx.cmake b/src/tomahawk/CMakeLists.osx.cmake index 97a40d2650..aa38c0c609 100644 --- a/src/tomahawk/CMakeLists.osx.cmake +++ b/src/tomahawk/CMakeLists.osx.cmake @@ -16,14 +16,14 @@ SET( OS_SPECIFIC_LINK_LIBRARIES ) -if (APPLE) +if( APPLE ) # find_library(GROWL Growl) option(ENABLE_SPARKLE "Sparkle updating" ON) find_library(SPARKLE Sparkle) - if (ENABLE_SPARKLE AND SPARKLE) + if( ENABLE_SPARKLE AND SPARKLE ) set(HAVE_SPARKLE ON) set( OS_SPECIFIC_LINK_LIBRARIES ${OS_SPECIFIC_LINK_LIBRARIES} ${SPARKLE} ) - endif(ENABLE_SPARKLE AND SPARKLE) + endif( ENABLE_SPARKLE AND SPARKLE ) # Uses Darwin kernel version. # 9.8.0 -> 10.5/Leopard # 10.4.0 -> 10.6/Snow Leopard diff --git a/src/tomahawk/CMakeLists.txt b/src/tomahawk/CMakeLists.txt index 64fb365f92..5501057499 100644 --- a/src/tomahawk/CMakeLists.txt +++ b/src/tomahawk/CMakeLists.txt @@ -32,11 +32,11 @@ IF( WITH_GNOMESHORTCUTHANDLER ) qt4_add_dbus_interface(tomahawkSources GnomeSettingsDaemonMediaKeys.xml GnomeSettingsDaemonMediaKeysProxy) ENDIF( WITH_GNOMESHORTCUTHANDLER ) -IF(LIBLASTFM_FOUND) +IF( LIBLASTFM_FOUND ) SET(tomahawkSources ${tomahawkSources} Scrobbler.cpp ) -ENDIF(LIBLASTFM_FOUND) +ENDIF( LIBLASTFM_FOUND ) SET( tomahawkSourcesGui ${tomahawkSourcesGui} sourcetree/SourcesModel.cpp @@ -111,7 +111,7 @@ INCLUDE_DIRECTORIES( ${LIBLASTFM_INCLUDE_DIRS} ) -IF(QXTWEB_FOUND) +IF( QXTWEB_FOUND ) LIST(APPEND tomahawkSources web/Api_v1.cpp) LIST(APPEND LINK_LIBRARIES ${QXTWEB_LIBRARIES}) INCLUDE_DIRECTORIES(${QXTWEB_INCLUDE_DIRS}) @@ -134,9 +134,9 @@ IF( APPLE ) SET( tomahawkSources ${tomahawkSources} mac/TomahawkApp_Mac.mm mac/MacShortcutHandler.cpp ) ENDIF( APPLE ) -IF(QCA2_FOUND) +IF( QCA2_FOUND ) INCLUDE_DIRECTORIES( ${QCA2_INCLUDE_DIR} ) -ENDIF(QCA2_FOUND) +ENDIF( QCA2_FOUND ) INCLUDE(GNUInstallDirs) @@ -175,13 +175,13 @@ qt5_use_modules(tomahawk Core Widgets Network Sql WebKitWidgets) MESSAGE( STATUS "OS_SPECIFIC_LINK_LIBRARIES: ${OS_SPECIFIC_LINK_LIBRARIES}" ) -IF(LIBLASTFM_FOUND) +IF( LIBLASTFM_FOUND ) LIST(APPEND LINK_LIBRARIES ${LINK_LIBRARIES} ${LIBLASTFM_LIBRARIES} ) -ENDIF(LIBLASTFM_FOUND) -IF(QCA2_FOUND) +ENDIF( LIBLASTFM_FOUND ) +IF( QCA2_FOUND ) LIST(APPEND LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} ) -ENDIF(QCA2_FOUND) -IF(WITH_BREAKPAD) +ENDIF( QCA2_FOUND ) +IF( WITH_BREAKPAD ) LIST(APPEND LINK_LIBRARIES ${LINK_LIBRARIES} tomahawk_breakpad ) ENDIF() @@ -198,11 +198,11 @@ TARGET_LINK_LIBRARIES( tomahawk ) IF( APPLE ) - IF(HAVE_SPARKLE) + IF( HAVE_SPARKLE ) MESSAGE("Sparkle Found, installing framekwork in bundle") INSTALL(DIRECTORY "${SPARKLE}/Versions/Current/Resources" DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/Frameworks/Sparkle.framework") - ENDIF(HAVE_SPARKLE) + ENDIF( HAVE_SPARKLE ) ENDIF( APPLE ) INSTALL( TARGETS tomahawk BUNDLE DESTINATION . RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) From 8c0b9828529659f6183553125e0323983f0d5bda Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 09:15:39 +0200 Subject: [PATCH 208/565] * Fixed building without breakpad. Thanks to Dinar Valeev. --- src/tomahawk/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tomahawk/main.cpp b/src/tomahawk/main.cpp index 1bb3452703..bde66a1213 100644 --- a/src/tomahawk/main.cpp +++ b/src/tomahawk/main.cpp @@ -34,7 +34,9 @@ #ifndef ENABLE_HEADLESS #include "TomahawkSettingsGui.h" - #include "breakpad/BreakPad.h" + #ifdef WITH_BREAKPAD + #include "breakpad/BreakPad.h" + #endif #endif From 27fcb1c43fb701c22815b4146853dee2235a3e5e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 09:23:44 +0200 Subject: [PATCH 209/565] * Style fixes for NetworkActivityWidget. --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 15 +++++++++++++-- src/libtomahawk/widgets/NetworkActivityWidget.h | 8 ++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 3391ac3edd..85285a0264 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -38,7 +38,7 @@ using namespace Tomahawk; -NetworkActivityWidget::NetworkActivityWidget( QWidget *parent ) +NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) : QWidget( parent ) , ui( new Ui::NetworkActivityWidget ) , m_sortedProxy( 0 ) @@ -72,16 +72,19 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget *parent ) m_spinner->fadeIn(); } + NetworkActivityWidget::~NetworkActivityWidget() { } + Tomahawk::playlistinterface_ptr NetworkActivityWidget::playlistInterface() const { return m_playlistInterface; } + bool NetworkActivityWidget::isBeingPlayed() const { @@ -91,6 +94,7 @@ NetworkActivityWidget::isBeingPlayed() const return false; } + bool NetworkActivityWidget::jumpToCurrentTrack() { @@ -100,6 +104,7 @@ NetworkActivityWidget::jumpToCurrentTrack() return false; } + void NetworkActivityWidget::fetchData() { @@ -107,6 +112,7 @@ NetworkActivityWidget::fetchData() QtConcurrent::run( this, &NetworkActivityWidget::actualFetchData ); } + void NetworkActivityWidget::weeklyCharts( const QList& tracks ) { @@ -120,6 +126,7 @@ NetworkActivityWidget::weeklyCharts( const QList& tracks ) checkDone( locker ); } + void NetworkActivityWidget::monthlyCharts( const QList& tracks ) { @@ -133,6 +140,7 @@ NetworkActivityWidget::monthlyCharts( const QList& tracks ) checkDone( locker ); } + void NetworkActivityWidget::yearlyCharts( const QList& tracks ) { @@ -146,8 +154,9 @@ NetworkActivityWidget::yearlyCharts( const QList& tracks ) checkDone( locker ); } + void -NetworkActivityWidget::leftCrumbIndexChanged( QModelIndex index ) +NetworkActivityWidget::leftCrumbIndexChanged( const QModelIndex& index ) { QStandardItem* item = m_crumbModelLeft->itemFromIndex( m_sortedProxy->mapToSource( index ) ); if ( !item ) @@ -176,6 +185,7 @@ NetworkActivityWidget::leftCrumbIndexChanged( QModelIndex index ) } } + void NetworkActivityWidget::actualFetchData() { @@ -203,6 +213,7 @@ NetworkActivityWidget::actualFetchData() Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( yearCharts ) ); } + void NetworkActivityWidget::checkDone( QSharedPointer ) { diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.h b/src/libtomahawk/widgets/NetworkActivityWidget.h index 398f22aef1..0bf85bc621 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.h +++ b/src/libtomahawk/widgets/NetworkActivityWidget.h @@ -35,9 +35,9 @@ class NetworkActivityWidget : public QWidget, public Tomahawk::ViewPage { Q_OBJECT public: - NetworkActivityWidget(QWidget *parent = 0); + NetworkActivityWidget(QWidget* parent = 0); ~NetworkActivityWidget(); - + virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const; @@ -51,13 +51,13 @@ class NetworkActivityWidget : public QWidget, public Tomahawk::ViewPage void fetchData(); signals: - + private slots: void weeklyCharts( const QList& ); void monthlyCharts( const QList& ); void yearlyCharts( const QList& ); - void leftCrumbIndexChanged( QModelIndex ); + void leftCrumbIndexChanged( const QModelIndex& ); private: void actualFetchData(); From 1da4cb83cadc352f1756fa2baf5deb27a69f70ac Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 09:34:41 +0200 Subject: [PATCH 210/565] * The mutex isn't needed. --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 11 ++++------- src/libtomahawk/widgets/NetworkActivityWidget.h | 4 +--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 85285a0264..41265a5a66 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -116,42 +116,39 @@ NetworkActivityWidget::fetchData() void NetworkActivityWidget::weeklyCharts( const QList& tracks ) { - QSharedPointer locker = QSharedPointer( new QMutexLocker( &m_retrieveMutex ) ); m_weeklyChartsModel = new PlaylistModel( ui->tracksViewLeft ); m_weeklyChartsModel->startLoading(); // Pipeline::instance()->resolve( tracks ); m_weeklyChartsModel->appendTracks( tracks ); m_weeklyChartsModel->finishLoading(); - checkDone( locker ); + checkDone(); } void NetworkActivityWidget::monthlyCharts( const QList& tracks ) { - QSharedPointer locker = QSharedPointer( new QMutexLocker( &m_retrieveMutex ) ); m_monthlyChartsModel = new PlaylistModel( ui->tracksViewLeft ); m_monthlyChartsModel->startLoading(); // Pipeline::instance()->resolve( tracks ); m_monthlyChartsModel->appendTracks( tracks ); m_monthlyChartsModel->finishLoading(); - checkDone( locker ); + checkDone(); } void NetworkActivityWidget::yearlyCharts( const QList& tracks ) { - QSharedPointer locker = QSharedPointer( new QMutexLocker( &m_retrieveMutex ) ); m_yearlyChartsModel = new PlaylistModel( ui->tracksViewLeft ); m_yearlyChartsModel->startLoading(); // Pipeline::instance()->resolve( tracks ); m_yearlyChartsModel->appendTracks( tracks ); m_yearlyChartsModel->finishLoading(); - checkDone( locker ); + checkDone(); } @@ -215,7 +212,7 @@ NetworkActivityWidget::actualFetchData() void -NetworkActivityWidget::checkDone( QSharedPointer ) +NetworkActivityWidget::checkDone() { if ( !m_weeklyChartsModel.isNull() && !m_yearlyChartsModel.isNull() && !m_monthlyChartsModel.isNull() ) { diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.h b/src/libtomahawk/widgets/NetworkActivityWidget.h index 0bf85bc621..b4b55e7e3d 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.h +++ b/src/libtomahawk/widgets/NetworkActivityWidget.h @@ -61,9 +61,7 @@ private slots: private: void actualFetchData(); - void checkDone( QSharedPointer ); - - QMutex m_retrieveMutex; + void checkDone(); QSharedPointer ui; Tomahawk::playlistinterface_ptr m_playlistInterface; From 6ef3dc30af0b3366733ca34c47d6ed7bd405ac63 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 5 Jun 2013 18:56:59 +0200 Subject: [PATCH 211/565] Use peerName instead of peerAddress * If connecting to a non-IP host, peerAddress is an empty string, peerName contains the DNS name --- src/libtomahawk/network/Servent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index ebbebc534f..870d2c980e 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -723,7 +723,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, info.setVisible( true ); info.setKey( key ); info.setNodeId( orig_conn->id() ); - info.setHost( orig_conn->socket()->peerAddress().toString() ); + info.setHost( orig_conn->socket()->peerName() ); info.setPort( orig_conn->peerPort() ); Q_ASSERT( info.isValid() ); initiateConnection( info, new_conn ); From 58ee8b35f4b766fe8cf3ced9830dca09c4379777 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 20:26:46 +0200 Subject: [PATCH 212/565] * Style fixes and removed spamy logging from TomahawkCache. --- src/libtomahawk/utils/TomahawkCache.cpp | 63 +++++++++++++++---------- src/libtomahawk/utils/TomahawkCache.h | 14 +++--- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkCache.cpp b/src/libtomahawk/utils/TomahawkCache.cpp index 51e58f313d..ac97547cbd 100644 --- a/src/libtomahawk/utils/TomahawkCache.cpp +++ b/src/libtomahawk/utils/TomahawkCache.cpp @@ -30,6 +30,7 @@ using namespace TomahawkUtils; Cache*Cache::s_instance = 0; + Cache* Cache::instance() { if ( !s_instance ) @@ -38,6 +39,7 @@ Cache* Cache::instance() return s_instance; } + Cache::Cache() : QObject ( 0 ) , m_cacheBaseDir ( TomahawkSettings::instance()->storageCacheLocation() + "/GenericCache/" ) @@ -49,12 +51,14 @@ Cache::Cache() m_pruneTimer.start(); } + Cache::~Cache() { - } -void Cache::pruneTimerFired() + +void +Cache::pruneTimerFired() { QMutexLocker mutex_locker( &m_mutex ); @@ -62,15 +66,18 @@ void Cache::pruneTimerFired() qlonglong currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); - foreach ( const QVariant &client, clients ) { + foreach ( const QVariant& client, clients ) + { const QString client_identifier = client.toString(); const QString cache_dir = m_cacheBaseDir + client_identifier; QSettings cached_settings ( cache_dir, QSettings::IniFormat ); const QStringList keys = cached_settings.allKeys(); - foreach ( const QString &key, keys ) { + foreach ( const QString& key, keys ) + { CacheData data = cached_settings.value ( key ).value(); - if ( data.maxAge < currentMSecsSinceEpoch ) { + if ( data.maxAge < currentMSecsSinceEpoch ) + { cached_settings.remove ( key ); tLog() << Q_FUNC_INFO << "Removed stale entry: " << client_identifier << key; } @@ -82,30 +89,35 @@ void Cache::pruneTimerFired() } -QVariant Cache::getData ( const QString& identifier, const QString& key ) +QVariant +Cache::getData( const QString& identifier, const QString& key ) { QMutexLocker mutex_locker( &m_mutex ); const QString cacheDir = m_cacheBaseDir + identifier; QSettings cached_settings ( cacheDir, QSettings::IniFormat ); - if ( cached_settings.contains ( key ) ) { + if ( cached_settings.contains ( key ) ) + { CacheData data = cached_settings.value ( key ).value(); - if ( data.maxAge < QDateTime::currentMSecsSinceEpoch() ) { + if ( data.maxAge < QDateTime::currentMSecsSinceEpoch() ) + { cached_settings.remove ( key ); - tLog() << Q_FUNC_INFO << "Removed stale entry: " << identifier << key; + tLog() << Q_FUNC_INFO << "Removed stale entry:" << identifier << key; return QVariant(); } - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Fetched data for" << identifier << key; +// tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Fetched data for" << identifier << key; return data.data; } - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No such key" << key; +// tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No such key" << key; return QVariant(); } -void Cache::putData ( const QString& identifier, qint64 maxAge, const QString& key, const QVariant& value ) + +void +Cache::putData( const QString& identifier, qint64 maxAge, const QString& key, const QVariant& value ) { QMutexLocker mutex_locker( &m_mutex ); @@ -113,13 +125,16 @@ void Cache::putData ( const QString& identifier, qint64 maxAge, const QString& k addClient ( identifier ); QSettings cached_settings ( cacheDir, QSettings::IniFormat ); cached_settings.setValue ( key, QVariant::fromValue ( CacheData ( QDateTime::currentMSecsSinceEpoch() + maxAge, value ) ) ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Storing from client " << identifier << maxAge << key << value; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Storing from client" << identifier << maxAge << key << value; } -void Cache::addClient ( const QString& identifier ) + +void +Cache::addClient( const QString& identifier ) { QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); - foreach ( const QVariant &client, clients ) { + foreach ( const QVariant& client, clients ) + { const QString client_identifier = client.toString(); if ( identifier == client_identifier ) return; } @@ -130,24 +145,24 @@ void Cache::addClient ( const QString& identifier ) m_cacheManifest.sync(); } -void Cache::removeClient ( const QString& identifier ) + +void +Cache::removeClient( const QString& identifier ) { QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); QVariantList::iterator it = clients.begin(); - while ( it != clients.end() ) { + while ( it != clients.end() ) + { const QString client_identifier = it->toString(); - if ( identifier == client_identifier ) { + if ( identifier == client_identifier ) + { tLog() << Q_FUNC_INFO << "removing client" << identifier; clients.erase ( it ); break; } ++it; } - m_cacheManifest.setValue ( "clients", clients ); + + m_cacheManifest.setValue( "clients", clients ); m_cacheManifest.sync(); } - - - - - diff --git a/src/libtomahawk/utils/TomahawkCache.h b/src/libtomahawk/utils/TomahawkCache.h index 0d70da82d6..5cc3ece17d 100644 --- a/src/libtomahawk/utils/TomahawkCache.h +++ b/src/libtomahawk/utils/TomahawkCache.h @@ -27,12 +27,14 @@ #include #include -namespace TomahawkUtils { +namespace TomahawkUtils +{ /** * Internal data structure. Don't use. */ -struct CacheData { +struct CacheData +{ CacheData(){} CacheData( qint64 maxAg, QVariant dat ) : maxAge( maxAg ) @@ -67,7 +69,7 @@ Q_OBJECT * @param key the key to store the data * @param value the data to store */ - void putData( const QString &identifier, qint64 maxAge, const QString &key, const QVariant& value ); + void putData( const QString& identifier, qint64 maxAge, const QString& key, const QVariant& value ); /** * Retrieve data from the cache. @@ -75,7 +77,7 @@ Q_OBJECT * @param key the key to store the data * @return the data, if found, if not found an invalid QVariant is returned. */ - QVariant getData( const QString &identifier, const QString &key ); + QVariant getData( const QString& identifier, const QString& key ); private slots: void pruneTimerFired(); @@ -88,13 +90,13 @@ private slots: * Adds a client to the manifest. * Does not lock the mutex. */ - void addClient( const QString &identifier ); + void addClient( const QString& identifier ); /** * Removes a client to the manifest. * Does not lock the mutex. */ - void removeClient( const QString &identifier ); + void removeClient( const QString& identifier ); QString m_cacheBaseDir; QSettings m_cacheManifest; From 03a84c574ec1e9694025be75d33c683480099aa0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 20:46:22 +0200 Subject: [PATCH 213/565] * Cache avatar instead of trying to get it from the cache over and over. --- src/libtomahawk/Source.cpp | 39 +++++++++++++++++++------------------- src/libtomahawk/Source.h | 5 +++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 2a87199b11..1bfdd0ca47 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -57,6 +57,7 @@ Source::Source( int id, const QString& nodeId ) , m_id( id ) , m_updateIndexWhenSynced( false ) , m_state( DBSyncConnection::UNKNOWN ) + , m_avatarLoaded( false ) , m_cc( 0 ) , m_commandCount( 0 ) { @@ -251,33 +252,31 @@ Source::friendlyNamesLessThan( const QString& first, const QString& second ) QPixmap Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) { -// tLog() << "****************************************************************************************"; -// tLog() << peerInfos().count() << "PEERS FOR " << friendlyName(); - QPixmap result; foreach ( const peerinfo_ptr& peerInfo, peerInfos() ) { -// peerInfoDebug(peerInfo) << !peerInfo->avatar().isNull(); - if ( !peerInfo.isNull() && !peerInfo->avatar( style, size ).isNull() ) + if ( peerInfo && !peerInfo->avatar( style, size ).isNull() ) { - result = peerInfo->avatar( style, size ); - break; + return peerInfo->avatar( style, size ); } } - if ( result.isNull() ) + + if ( m_avatarLoaded ) + return m_avatar; + + // Try to get the avatar from the cache + // Hint: We store the avatar for each xmpp peer using its contactId, the dbFriendlyName is a contactId of a peer + m_avatarLoaded = true; + QByteArray avatarBuffer = TomahawkUtils::Cache::instance()->getData( "Sources", dbFriendlyName() ).toByteArray(); + if ( !avatarBuffer.isNull() ) { - // Try to get the avatar from the cache - // Hint: We store the avatar for each xmpp peer using its contactId, the dbFriendlyName is a contactId of a peer - QByteArray avatarBuffer = TomahawkUtils::Cache::instance()->getData( "Sources", dbFriendlyName() ).toByteArray(); - if ( !avatarBuffer.isNull() ) - { - QPixmap avatar; - avatar.loadFromData( avatarBuffer ); - avatarBuffer.clear(); - result = QPixmap( TomahawkUtils::createRoundedImage( avatar, QSize( 0, 0 ) ) ); - } + QPixmap avatar; + avatar.loadFromData( avatarBuffer ); + avatarBuffer.clear(); + + m_avatar = QPixmap( TomahawkUtils::createRoundedImage( avatar, QSize( 0, 0 ) ) ); } -// tLog() << "****************************************************************************************"; - return result; + + return m_avatar; } #endif diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index f5fa9ba74e..1f5c1dddc3 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -70,13 +70,11 @@ friend class ::MusicScanner; QString friendlyName() const; void setFriendlyName( const QString& fname ); - // fallback when the normal friendlyname from cache is not available // this is usually the jabber id or whatever was used when first connected QString dbFriendlyName() const; void setDbFriendlyName( const QString& dbFriendlyName ); - #ifndef ENABLE_HEADLESS QPixmap avatar( TomahawkUtils::ImageMode style = TomahawkUtils::Original, const QSize& size = QSize() ); #endif @@ -173,6 +171,9 @@ private slots: DBSyncConnection::State m_state; QTimer m_currentTrackTimer; + QPixmap m_avatar; + bool m_avatarLoaded; + QPointer m_cc; QList< QSharedPointer > m_cmds; int m_commandCount; From b070ed8f82cf1f0a2ea435d2b4b75e1d303191f8 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 20:46:35 +0200 Subject: [PATCH 214/565] * Style fixes. --- src/libtomahawk/utils/TomahawkCache.cpp | 53 +++++++++++++------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkCache.cpp b/src/libtomahawk/utils/TomahawkCache.cpp index ac97547cbd..07a1f6707a 100644 --- a/src/libtomahawk/utils/TomahawkCache.cpp +++ b/src/libtomahawk/utils/TomahawkCache.cpp @@ -41,13 +41,13 @@ Cache* Cache::instance() Cache::Cache() - : QObject ( 0 ) - , m_cacheBaseDir ( TomahawkSettings::instance()->storageCacheLocation() + "/GenericCache/" ) - , m_cacheManifest ( m_cacheBaseDir + "cachemanifest.ini", QSettings::IniFormat ) + : QObject( 0 ) + , m_cacheBaseDir( TomahawkSettings::instance()->storageCacheLocation() + "/GenericCache/" ) + , m_cacheManifest( m_cacheBaseDir + "cachemanifest.ini", QSettings::IniFormat ) { - m_pruneTimer.setInterval ( 300000 ); - m_pruneTimer.setSingleShot ( false ); - connect ( &m_pruneTimer, SIGNAL ( timeout() ), SLOT ( pruneTimerFired() ) ); + m_pruneTimer.setInterval( 300000 ); + m_pruneTimer.setSingleShot( false ); + connect( &m_pruneTimer, SIGNAL( timeout() ), SLOT( pruneTimerFired() ) ); m_pruneTimer.start(); } @@ -65,26 +65,26 @@ Cache::pruneTimerFired() qDebug() << Q_FUNC_INFO << "Pruning tomahawkcache"; qlonglong currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); - QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); + QVariantList clients = m_cacheManifest.value( "clients" ).toList(); foreach ( const QVariant& client, clients ) { const QString client_identifier = client.toString(); const QString cache_dir = m_cacheBaseDir + client_identifier; - QSettings cached_settings ( cache_dir, QSettings::IniFormat ); + QSettings cached_settings( cache_dir, QSettings::IniFormat ); const QStringList keys = cached_settings.allKeys(); foreach ( const QString& key, keys ) { - CacheData data = cached_settings.value ( key ).value(); + CacheData data = cached_settings.value( key ).value(); if ( data.maxAge < currentMSecsSinceEpoch ) { - cached_settings.remove ( key ); + cached_settings.remove( key ); tLog() << Q_FUNC_INFO << "Removed stale entry: " << client_identifier << key; } } cached_settings.sync(); if ( cached_settings.allKeys().size() == 0 ) - removeClient ( client_identifier ); + removeClient( client_identifier ); } } @@ -95,23 +95,23 @@ Cache::getData( const QString& identifier, const QString& key ) QMutexLocker mutex_locker( &m_mutex ); const QString cacheDir = m_cacheBaseDir + identifier; - QSettings cached_settings ( cacheDir, QSettings::IniFormat ); + QSettings cached_settings( cacheDir, QSettings::IniFormat ); - if ( cached_settings.contains ( key ) ) + if ( cached_settings.contains( key ) ) { - CacheData data = cached_settings.value ( key ).value(); + CacheData data = cached_settings.value( key ).value(); if ( data.maxAge < QDateTime::currentMSecsSinceEpoch() ) { - cached_settings.remove ( key ); + cached_settings.remove( key ); tLog() << Q_FUNC_INFO << "Removed stale entry:" << identifier << key; return QVariant(); } -// tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Fetched data for" << identifier << key; + tDebug() << Q_FUNC_INFO << "Fetched data for" << identifier << key; return data.data; } -// tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No such key" << key; + tDebug() << Q_FUNC_INFO << "No such key" << key; return QVariant(); } @@ -122,9 +122,9 @@ Cache::putData( const QString& identifier, qint64 maxAge, const QString& key, co QMutexLocker mutex_locker( &m_mutex ); const QString cacheDir = m_cacheBaseDir + identifier; - addClient ( identifier ); - QSettings cached_settings ( cacheDir, QSettings::IniFormat ); - cached_settings.setValue ( key, QVariant::fromValue ( CacheData ( QDateTime::currentMSecsSinceEpoch() + maxAge, value ) ) ); + addClient( identifier ); + QSettings cached_settings( cacheDir, QSettings::IniFormat ); + cached_settings.setValue( key, QVariant::fromValue( CacheData( QDateTime::currentMSecsSinceEpoch() + maxAge, value ) ) ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Storing from client" << identifier << maxAge << key << value; } @@ -132,16 +132,17 @@ Cache::putData( const QString& identifier, qint64 maxAge, const QString& key, co void Cache::addClient( const QString& identifier ) { - QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); + QVariantList clients = m_cacheManifest.value( "clients" ).toList(); foreach ( const QVariant& client, clients ) { const QString client_identifier = client.toString(); - if ( identifier == client_identifier ) return; + if ( identifier == client_identifier ) + return; } tLog() << Q_FUNC_INFO << "adding client" << identifier; - clients.append ( identifier ); - m_cacheManifest.setValue ( "clients", clients ); + clients.append( identifier ); + m_cacheManifest.setValue( "clients", clients ); m_cacheManifest.sync(); } @@ -149,7 +150,7 @@ Cache::addClient( const QString& identifier ) void Cache::removeClient( const QString& identifier ) { - QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); + QVariantList clients = m_cacheManifest.value( "clients" ).toList(); QVariantList::iterator it = clients.begin(); while ( it != clients.end() ) { @@ -157,7 +158,7 @@ Cache::removeClient( const QString& identifier ) if ( identifier == client_identifier ) { tLog() << Q_FUNC_INFO << "removing client" << identifier; - clients.erase ( it ); + clients.erase( it ); break; } ++it; From 250c76b53cb1940048e94676da1d601ad42df75e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 5 Jun 2013 21:32:09 +0200 Subject: [PATCH 215/565] * When redirecting a request, make sure HEAD ops don't become a GET. --- src/libtomahawk/utils/NetworkReply.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/utils/NetworkReply.cpp b/src/libtomahawk/utils/NetworkReply.cpp index 6982040306..45f5873e97 100644 --- a/src/libtomahawk/utils/NetworkReply.cpp +++ b/src/libtomahawk/utils/NetworkReply.cpp @@ -21,6 +21,8 @@ #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +#include + using namespace Tomahawk; @@ -61,7 +63,19 @@ NetworkReply::load( const QUrl& url ) QNetworkRequest request( url ); Q_ASSERT( TomahawkUtils::nam() != 0 ); - m_reply = TomahawkUtils::nam()->get( request ); + + QNetworkAccessManager::Operation op = m_reply->operation(); + m_reply->deleteLater(); + + switch ( op ) + { + case QNetworkAccessManager::HeadOperation: + m_reply = TomahawkUtils::nam()->head( request ); + break; + + default: + m_reply = TomahawkUtils::nam()->get( request ); + } connect( m_reply, SIGNAL( finished() ), SLOT( networkLoadFinished() ) ); connect( m_reply, SIGNAL( error( QNetworkReply::NetworkError ) ), SIGNAL( error( QNetworkReply::NetworkError ) ) ); @@ -82,7 +96,6 @@ NetworkReply::networkLoadFinished() if ( redir.isValid() && !redir.toUrl().isEmpty() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Redirected HTTP request to" << redir; - m_reply->deleteLater(); load( redir.toUrl() ); emit redirected(); } From 3e678c55eed56ad3911fba0958b5abe1397e37d6 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Thu, 6 Jun 2013 02:17:09 +0200 Subject: [PATCH 216/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 26 +++++------ lang/tomahawk_bg.ts | 26 +++++------ lang/tomahawk_bn_IN.ts | 26 +++++------ lang/tomahawk_ca.ts | 26 +++++------ lang/tomahawk_ca@valencia.ts | 26 +++++------ lang/tomahawk_cs.ts | 40 ++++++++--------- lang/tomahawk_da.ts | 26 +++++------ lang/tomahawk_de.ts | 38 ++++++++-------- lang/tomahawk_el.ts | 38 ++++++++-------- lang/tomahawk_en.ts | 26 +++++------ lang/tomahawk_es.ts | 26 +++++------ lang/tomahawk_fi.ts | 26 +++++------ lang/tomahawk_fr.ts | 26 +++++------ lang/tomahawk_gl.ts | 26 +++++------ lang/tomahawk_hi_IN.ts | 26 +++++------ lang/tomahawk_hu.ts | 26 +++++------ lang/tomahawk_id.ts | 26 +++++------ lang/tomahawk_it.ts | 84 ++++++++++++++++++------------------ lang/tomahawk_ja.ts | 26 +++++------ lang/tomahawk_lt.ts | 26 +++++------ lang/tomahawk_pl.ts | 26 +++++------ lang/tomahawk_pt_BR.ts | 26 +++++------ lang/tomahawk_ru.ts | 26 +++++------ lang/tomahawk_sv.ts | 26 +++++------ lang/tomahawk_tr.ts | 26 +++++------ lang/tomahawk_zh_CN.ts | 26 +++++------ lang/tomahawk_zh_TW.ts | 26 +++++------ 27 files changed, 400 insertions(+), 398 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 188323ce2a..5e559a297c 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1029,22 +1029,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3522,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 3f3f134dc3..ce340955b2 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1035,22 +1035,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3536,43 +3536,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 песни) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 9856310a9f..3a177a5a2a 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3509,43 +3509,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index d901bd1123..c508673083 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3523,43 +3523,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index f7fbde8682..fe5a77fb03 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3523,43 +3523,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index a4e54f7ac8..6a542e0b5e 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1030,29 +1030,29 @@ heslo NetworkActivityWidget - + Charts - + Žebříčky - + Last Week - + Poslední týden - + Last Month - + Poslední měsíc - + Last Year - + Poslední rok Network Activity - + Činnost sítě @@ -1883,7 +1883,7 @@ heslo Drop to send tracks - + Upustit pro poslání skladeb @@ -2104,7 +2104,7 @@ heslo Network Activity - + Činnost sítě @@ -3523,43 +3523,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 8fcf6c35b5..1f1cff2b98 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3511,43 +3511,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 2ce7d2a4f7..cc74441a54 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1030,29 +1030,29 @@ Passwort NetworkActivityWidget - + Charts - + Charts - + Last Week - + Letzte Woche - + Last Month - + Letzter Monat - + Last Year - + Letztes Jahr Network Activity - + Netzwerk Aktivität @@ -2104,7 +2104,7 @@ Passwort Network Activity - + Netzwerk Aktivität @@ -3518,43 +3518,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 877afb3f25..e319d8fe14 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1029,29 +1029,29 @@ Password NetworkActivityWidget - + Charts - + Γραφήματα - + Last Week - + Τελευταία εβδομάδα - + Last Month - + Τελευταίος μήνας - + Last Year - + Τελευταίο ετος Network Activity - + Δραστηριότητα δικτύου @@ -2104,7 +2104,7 @@ Password Network Activity - + Δραστηριότητα δικτύου @@ -3524,43 +3524,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index f6141e646e..c88dac2919 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1030,22 +1030,22 @@ Password NetworkActivityWidget - + Charts Charts - + Last Week Last Week - + Last Month Last Month - + Last Year Last Year @@ -3526,43 +3526,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index ac5a333103..58306c709a 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1029,22 +1029,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3524,43 +3524,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index a2f1595b8f..0d3bf6cd36 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1030,22 +1030,22 @@ salasana NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3529,43 +3529,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 10cb59b68b..79dfb98834 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1029,22 +1029,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3521,43 +3521,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 940cd0ca13..d9c0ee7810 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3523,43 +3523,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 57faedbf51..3c73cec50d 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3509,43 +3509,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 3b0b8b2249..a9097b3745 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3509,43 +3509,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index b7b64b7f9b..0fa5d8b528 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3509,43 +3509,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 6d50915a5f..86c9e3435b 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -680,48 +680,49 @@ connect and stream from you? Form - + Corpo Connect to your Hatchet account - + Connettiti al tuo account Hatchet One-time Password - + Password⏎ +temporanea Username - + Nome utente Hatchet username - + Username Hatchet Password: - + Password: Hatchet password - + Password Hatchet (Only if configured) - + (Solo se configurato) Login - + Login @@ -737,7 +738,7 @@ Password Sent %1 by %2 to %3. - + Spedito %1 da %2 a %3. @@ -768,7 +769,7 @@ Password Script Resolver Warning: API call %1 returned data synchronously. - + Avvertimento Script Resolver: chiamata API 1% ha restituito dati sincroni. @@ -1028,29 +1029,29 @@ Password NetworkActivityWidget - + Charts - + Classifiche - + Last Week - + Ultima settimana - + Last Month - + Ultimo mese - + Last Year - + Ultimo anno Network Activity - + Attività network @@ -1478,12 +1479,12 @@ Password No query - + Nessuna richiesta Parameter count mismatch - + Conteggio parametri non corrispondente @@ -1682,7 +1683,7 @@ Password Active (your host needs to be directly reachable) - + Attivo (il tuo host dev'essere direttamente raggiungibile) @@ -1880,7 +1881,7 @@ Password Drop to send tracks - + Lascia cadere per spedire le tracce @@ -2101,7 +2102,7 @@ Password Network Activity - + Attività network @@ -2318,12 +2319,12 @@ Password Login - + Login Logged in as: %1 - + Connesso come: %1 @@ -2331,7 +2332,7 @@ Password Connect to your Hatchet account - + Connetti il tuo account Hatchet @@ -2737,7 +2738,7 @@ username@jabber.org Send to &Friend - + Spediscila ad un &amico @@ -2777,7 +2778,7 @@ username@jabber.org Mark as &Listened - + Segna come &Ascoltate @@ -3344,13 +3345,14 @@ Try tweaking the filters for a new set of songs to play. %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 ti ha spedito⏎ +%2%4 %3. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 ti ha spedito "%2" di %3. @@ -3509,43 +3511,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 59dbbb65c4..ee6f335eb3 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1029,22 +1029,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3521,43 +3521,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index a1e0307285..43f3485866 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3509,43 +3509,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 28556fc398..22c8a456d6 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1029,22 +1029,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3518,43 +3518,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index e9c255c453..d93c460996 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1029,22 +1029,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3518,43 +3518,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 46f78ed201..78d3255410 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1032,22 +1032,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3525,43 +3525,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index ec2eedc2f3..1ba58a41ab 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1030,22 +1030,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3524,43 +3524,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 1f1a4d78d1..9ef3dfcce3 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3509,43 +3509,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 4ff98a919b..f1f7126b58 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3519,43 +3519,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 425210a7b7..7fb4510d38 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1028,22 +1028,22 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year @@ -3509,43 +3509,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline From 0a815e06df9a72075a503329bfa2d2def381de3a Mon Sep 17 00:00:00 2001 From: Florian Richter Date: Thu, 6 Jun 2013 10:19:56 +0200 Subject: [PATCH 217/565] Fix signal signature --- src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp index 89c98b997c..f23d34ccc3 100644 --- a/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp +++ b/src/infoplugins/linux/fdonotify/FdoNotifyPlugin.cpp @@ -303,7 +303,7 @@ FdoNotifyPlugin::nowPlaying( const QVariant& input ) ); QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, this ); - connect( watcher, SIGNAL( finished( QDBusPendingCallWatcher* ) ), SLOT( RegisterFinished( dbusPlayingReplyReceived* ) ) ); + connect( watcher, SIGNAL( finished( QDBusPendingCallWatcher* ) ), SLOT( dbusPlayingReplyReceived( QDBusPendingCallWatcher* ) ) ); } From 5fc1d68a3ceafd746b4d34c89c3d68b4ce8e49fe Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Fri, 7 Jun 2013 02:17:06 +0200 Subject: [PATCH 218/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 5e559a297c..38b7e1f806 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -681,48 +681,49 @@ connect and stream from you? Form - + استمارة Connect to your Hatchet account - + الإتصال بحساب هاتشت الخاص One-time Password - + لمرة واحدة +كلمة السر Username - + اسم المستخدم Hatchet username - + إسم المستخدم على هاتشت Password: - + كلمة السر: Hatchet password - + كلمة سر هاتشت (Only if configured) - + (فقط إذا كان مكون) Login - + تسجيل الدخول @@ -1031,27 +1032,27 @@ Password Charts - + الرسوم البيانية Last Week - + الأسبوع الماضي Last Month - + الشهر الماضي Last Year - + العام الماضي Network Activity - + نشاط الشبكة @@ -1685,7 +1686,7 @@ Password Active (your host needs to be directly reachable) - + ناشط (على مضيفك أن يكون قابل للوصول إليه مباشرةً) @@ -2104,7 +2105,7 @@ Password Network Activity - + نشاط الشبكة @@ -2321,12 +2322,12 @@ Password Login - + تسجيل الدخول Logged in as: %1 - + مسجل تحت اسم: %1 @@ -2334,7 +2335,7 @@ Password Connect to your Hatchet account - + الإتصال بحساب هاتشت الخاص From 00662c2d8d49904bd6b916da0419c5af43296f13 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 08:55:26 +0200 Subject: [PATCH 219/565] * Load artist-stats. --- src/libtomahawk/Artist.cpp | 52 ++++++++++++-- src/libtomahawk/Artist.h | 9 ++- .../database/DatabaseCommand_ArtistStats.cpp | 68 +++++++++++++++++++ .../database/DatabaseCommand_ArtistStats.h | 48 +++++++++++++ 4 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp create mode 100644 src/libtomahawk/database/DatabaseCommand_ArtistStats.h diff --git a/src/libtomahawk/Artist.cpp b/src/libtomahawk/Artist.cpp index faf5622c8b..ff60d8a210 100644 --- a/src/libtomahawk/Artist.cpp +++ b/src/libtomahawk/Artist.cpp @@ -1,6 +1,6 @@ /* === This file is part of Tomahawk Player - === * - * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2013, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell * Copyright 2013, Teo Mrnjavac * @@ -25,6 +25,7 @@ #include "database/Database.h" #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_AllAlbums.h" +#include "database/DatabaseCommand_ArtistStats.h" #include "database/DatabaseCommand_TrackStats.h" #include "database/IdThreadWorker.h" #include "Source.h" @@ -43,6 +44,7 @@ QHash< QString, artist_ptr > Artist::s_artistsByCoverId = QHash< QString, artist static QMutex s_nameCacheMutex; static QReadWriteLock s_idMutex; +static QMutex s_memberMutex; Artist::~Artist() @@ -147,6 +149,8 @@ Artist::Artist( unsigned int id, const QString& name ) , m_simArtistsLoaded( false ) , m_biographyLoaded( false ) , m_infoJobs( 0 ) + , m_chartPosition( 0 ) + , m_chartCount( 0 ) #ifndef ENABLE_HEADLESS , m_cover( 0 ) #endif @@ -200,6 +204,16 @@ Artist::deleteLater() } +void +Artist::onArtistStatsLoaded( unsigned int /* plays */, unsigned int chartPos, unsigned int chartCount ) +{ + m_chartPosition = chartPos; + m_chartCount = chartCount; + + emit statsLoaded(); +} + + void Artist::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ) { @@ -395,8 +409,16 @@ Artist::loadStats() { artist_ptr a = m_ownRef.toStrongRef(); - DatabaseCommand_TrackStats* cmd = new DatabaseCommand_TrackStats( a ); - Database::instance()->enqueue( QSharedPointer(cmd) ); + { + DatabaseCommand_TrackStats* cmd = new DatabaseCommand_TrackStats( a ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + } + + { + DatabaseCommand_ArtistStats* cmd = new DatabaseCommand_ArtistStats( a ); + connect( cmd, SIGNAL( done( unsigned int, unsigned int, unsigned int ) ), SLOT( onArtistStatsLoaded( unsigned int, unsigned int, unsigned int ) ) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + } } @@ -420,14 +442,20 @@ Artist::playbackHistory( const Tomahawk::source_ptr& source ) const void Artist::setPlaybackHistory( const QList< Tomahawk::PlaybackLog >& playbackData ) { - m_playbackHistory = playbackData; + { + QMutexLocker locker( &s_memberMutex ); + m_playbackHistory = playbackData; + } + emit statsLoaded(); } unsigned int -Artist::playbackCount( const source_ptr& source ) +Artist::playbackCount( const source_ptr& source ) const { + QMutexLocker locker( &s_memberMutex ); + unsigned int count = 0; foreach ( const PlaybackLog& log, m_playbackHistory ) { @@ -439,6 +467,20 @@ Artist::playbackCount( const source_ptr& source ) } +unsigned int +Artist::chartPosition() const +{ + return m_chartPosition; +} + + +unsigned int +Artist::chartCount() const +{ + return m_chartCount; +} + + void Artist::onAlbumsFound( const QList< album_ptr >& albums, const QVariant& collectionIsNull ) { diff --git a/src/libtomahawk/Artist.h b/src/libtomahawk/Artist.h index 1336893d8c..420c5b5ea1 100644 --- a/src/libtomahawk/Artist.h +++ b/src/libtomahawk/Artist.h @@ -63,7 +63,10 @@ Q_OBJECT void loadStats(); QList< Tomahawk::PlaybackLog > playbackHistory( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ) const; void setPlaybackHistory( const QList< Tomahawk::PlaybackLog >& playbackData ); - unsigned int playbackCount( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ); + unsigned int playbackCount( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ) const; + + unsigned int chartPosition() const; + unsigned int chartCount() const; QString biography() const; @@ -93,6 +96,7 @@ public slots: void statsLoaded(); private slots: + void onArtistStatsLoaded( unsigned int plays, unsigned int chartPos, unsigned int chartCount ); void onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); void onAlbumsFound( const QList& albums, const QVariant& collectionIsNull = QVariant( false ) ); @@ -127,8 +131,9 @@ private slots: QList m_similarArtists; QString m_biography; - bool m_playbackHistoryLoaded; QList< PlaybackLog > m_playbackHistory; + unsigned int m_chartPosition; + unsigned int m_chartCount; mutable QByteArray m_coverBuffer; #ifndef ENABLE_HEADLESS diff --git a/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp new file mode 100644 index 0000000000..f7d2ebb11b --- /dev/null +++ b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp @@ -0,0 +1,68 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "DatabaseCommand_ArtistStats.h" + +#include "Artist.h" +#include "DatabaseImpl.h" +#include "SourceList.h" +#include "utils/Logger.h" + +using namespace Tomahawk; + + +DatabaseCommand_ArtistStats::DatabaseCommand_ArtistStats( const artist_ptr& artist, QObject* parent ) + : DatabaseCommand( parent ) + , m_artist( artist ) +{ +} + + +void +DatabaseCommand_ArtistStats::exec( DatabaseImpl* dbi ) +{ + TomahawkSqlQuery query = dbi->newquery(); + + query.prepare( "SELECT COUNT(*) AS counter, artist.id " + "FROM playback_log, track, artist " + "WHERE playback_log.source IS NULL AND track.id = playback_log.track AND artist.id = track.artist " + "GROUP BY track.artist " + "ORDER BY counter DESC" ); + query.exec(); + + unsigned int plays = 0; + unsigned int chartPos = 0; + unsigned int chartCount = 0; + + QHash< QString, unsigned int > charts; + while ( query.next() ) + { + chartCount++; + + if ( query.value( 1 ).toUInt() == m_artist->id() ) + { + chartPos = chartCount; + plays = query.value( 0 ).toUInt(); + } + } + + if ( plays == 0 ) + chartPos = chartCount; + + emit done( plays, chartPos, chartCount ); +} diff --git a/src/libtomahawk/database/DatabaseCommand_ArtistStats.h b/src/libtomahawk/database/DatabaseCommand_ArtistStats.h new file mode 100644 index 0000000000..eb509a4aac --- /dev/null +++ b/src/libtomahawk/database/DatabaseCommand_ArtistStats.h @@ -0,0 +1,48 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DATABASECOMMAND_ARTISTSTATS_H +#define DATABASECOMMAND_ARTISTSTATS_H + +#include + +#include "DatabaseCommand.h" +#include "Typedefs.h" +#include "Track.h" +#include "DllMacro.h" + +class DLLEXPORT DatabaseCommand_ArtistStats : public DatabaseCommand +{ +Q_OBJECT + +public: + explicit DatabaseCommand_ArtistStats( const Tomahawk::artist_ptr& artist, QObject* parent = 0 ); + + virtual void exec( DatabaseImpl* lib ); + virtual bool doesMutates() const { return false; } + virtual QString commandname() const { return "artiststats"; } + +signals: + void done( unsigned int totalPlays, unsigned int chartPosition, unsigned int chartCount ); + +private: + Tomahawk::trackdata_ptr m_track; + Tomahawk::artist_ptr m_artist; +}; + +#endif // DATABASECOMMAND_ARTISTSTATS_H From 02da2cba8d9b71ceb50a4eddd96c2e4c279351f9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 08:55:53 +0200 Subject: [PATCH 220/565] * Mutex protect TrackData. --- src/libtomahawk/TrackData.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/TrackData.cpp b/src/libtomahawk/TrackData.cpp index f559efa7f2..c15f5637ee 100644 --- a/src/libtomahawk/TrackData.cpp +++ b/src/libtomahawk/TrackData.cpp @@ -46,9 +46,9 @@ QHash< QString, trackdata_wptr > TrackData::s_trackDatasByName = QHash< QString, QHash< unsigned int, trackdata_wptr > TrackData::s_trackDatasById = QHash< unsigned int, trackdata_wptr >(); static QMutex s_datanameCacheMutex; +static QMutex s_memberMutex; static QReadWriteLock s_dataidMutex; - inline QString cacheKey( const QString& artist, const QString& track ) { @@ -257,8 +257,11 @@ TrackData::loadSocialActions() void TrackData::setAllSocialActions( const QList< SocialAction >& socialActions ) { - m_allSocialActions = socialActions; - parseSocialActions(); + { + QMutexLocker locker( &s_memberMutex ); + m_allSocialActions = socialActions; + parseSocialActions(); + } emit socialActionsLoaded(); } @@ -267,6 +270,7 @@ TrackData::setAllSocialActions( const QList< SocialAction >& socialActions ) QList< SocialAction > TrackData::allSocialActions() const { + QMutexLocker locker( &s_memberMutex ); return m_allSocialActions; } @@ -292,6 +296,8 @@ TrackData::parseSocialActions() bool TrackData::loved() { + QMutexLocker locker( &s_memberMutex ); + if ( m_socialActionsLoaded ) { return m_currentSocialActions[ "Love" ].toBool(); @@ -341,8 +347,9 @@ TrackData::loadStats() QList< Tomahawk::PlaybackLog > TrackData::playbackHistory( const Tomahawk::source_ptr& source ) const { - QList< Tomahawk::PlaybackLog > history; + QMutexLocker locker( &s_memberMutex ); + QList< Tomahawk::PlaybackLog > history; foreach ( const PlaybackLog& log, m_playbackHistory ) { if ( source.isNull() || log.source == source ) @@ -358,7 +365,10 @@ TrackData::playbackHistory( const Tomahawk::source_ptr& source ) const void TrackData::setPlaybackHistory( const QList< Tomahawk::PlaybackLog >& playbackData ) { - m_playbackHistory = playbackData; + { + QMutexLocker locker( &s_memberMutex ); + m_playbackHistory = playbackData; + } emit statsLoaded(); } @@ -366,6 +376,8 @@ TrackData::setPlaybackHistory( const QList< Tomahawk::PlaybackLog >& playbackDat unsigned int TrackData::playbackCount( const source_ptr& source ) { + QMutexLocker locker( &s_memberMutex ); + unsigned int count = 0; foreach ( const PlaybackLog& log, m_playbackHistory ) { From b15622f2753ba4ba1a24e82f5637f3d0b49a4bfc Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 08:56:30 +0200 Subject: [PATCH 221/565] * TrackView should restore externally set alternating-row-color setting. --- src/libtomahawk/playlist/TrackView.cpp | 15 ++++++++++++--- src/libtomahawk/playlist/TrackView.h | 3 +++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/TrackView.cpp b/src/libtomahawk/playlist/TrackView.cpp index 19f4400868..fc2766e595 100644 --- a/src/libtomahawk/playlist/TrackView.cpp +++ b/src/libtomahawk/playlist/TrackView.cpp @@ -57,6 +57,7 @@ TrackView::TrackView( QWidget* parent ) , m_resizing( false ) , m_dragging( false ) , m_updateContextView( true ) + , m_alternatingRowColors( true ) , m_contextMenu( new ContextMenu( this ) ) { setFrameShape( QFrame::NoFrame ); @@ -233,7 +234,7 @@ TrackView::setEmptyTip( const QString& tip ) void TrackView::onModelFilling() { - setAlternatingRowColors( true ); + QTreeView::setAlternatingRowColors( m_alternatingRowColors ); } @@ -241,7 +242,7 @@ void TrackView::onModelEmptyCheck() { if ( !m_proxyModel->rowCount( QModelIndex() ) ) - setAlternatingRowColors( false ); + QTreeView::setAlternatingRowColors( false ); } @@ -429,7 +430,7 @@ TrackView::resizeEvent( QResizeEvent* event ) int sortSection = m_header->sortIndicatorSection(); Qt::SortOrder sortOrder = m_header->sortIndicatorOrder(); - tDebug() << Q_FUNC_INFO << width(); +// tDebug() << Q_FUNC_INFO << width(); if ( m_header->checkState() && sortSection >= 0 ) { @@ -795,3 +796,11 @@ TrackView::setAutoResize( bool b ) if ( m_autoResize ) setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); } + + +void +TrackView::setAlternatingRowColors( bool enable ) +{ + m_alternatingRowColors = enable; + QTreeView::setAlternatingRowColors( enable ); +} diff --git a/src/libtomahawk/playlist/TrackView.h b/src/libtomahawk/playlist/TrackView.h index 3549a06a98..2281c79999 100644 --- a/src/libtomahawk/playlist/TrackView.h +++ b/src/libtomahawk/playlist/TrackView.h @@ -84,6 +84,8 @@ Q_OBJECT bool autoResize() const { return m_autoResize; } void setAutoResize( bool b ); + void setAlternatingRowColors( bool enable ); + // Starts playing from the beginning if resolved, or waits until a track is playable void startPlayingFromStart(); @@ -150,6 +152,7 @@ private slots: bool m_updateContextView; bool m_autoResize; + bool m_alternatingRowColors; QModelIndex m_contextMenuIndex; From 2e1a1385e17a8d1dface8141ec6c0af7945117a5 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 08:56:58 +0200 Subject: [PATCH 222/565] * Fixed pen settings for drawBackgroundAndNumbers. --- src/libtomahawk/utils/TomahawkUtilsGui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index d5445dcea7..f09970ad22 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -171,7 +171,7 @@ drawBackgroundAndNumbers( QPainter* painter, const QString& text, const QRect& f figRect.adjust( -painter->fontMetrics().averageCharWidth(), 0, 0, 0 ); QPen origpen = painter->pen(); - QPen pen = origpen; + QPen pen = painter->brush().color(); pen.setWidth( 1.0 ); painter->setPen( pen ); painter->drawRect( figRect ); @@ -195,7 +195,6 @@ drawBackgroundAndNumbers( QPainter* painter, const QString& text, const QRect& f figRect.adjust( -1, 0, 0, 0 ); painter->setPen( origpen ); - painter->setPen( Qt::white ); painter->drawText( figRect.adjusted( -5, 2, 6, 0 ), text, QTextOption( Qt::AlignCenter ) ); painter->restore(); From 74fdd6500c355f72ef8a05dd6e5a2e6df84719dd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 08:57:31 +0200 Subject: [PATCH 223/565] * Properly set pen before painting figure ovals. --- src/libtomahawk/widgets/WelcomeWidget.cpp | 2 +- src/tomahawk/sourcetree/SourceDelegate.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/WelcomeWidget.cpp b/src/libtomahawk/widgets/WelcomeWidget.cpp index 708d4ccdcb..c940279534 100644 --- a/src/libtomahawk/widgets/WelcomeWidget.cpp +++ b/src/libtomahawk/widgets/WelcomeWidget.cpp @@ -284,7 +284,7 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, rect.setBottom( pixmapRect.bottom() + 1 ); QColor figColor( TomahawkStyle::DASHBOARD_ROUNDFIGURE_BACKGROUND ); - painter->setPen( figColor ); + painter->setPen( Qt::white ); painter->setBrush( figColor ); TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, rect ); diff --git a/src/tomahawk/sourcetree/SourceDelegate.cpp b/src/tomahawk/sourcetree/SourceDelegate.cpp index ac2b8bb718..137aab0931 100644 --- a/src/tomahawk/sourcetree/SourceDelegate.cpp +++ b/src/tomahawk/sourcetree/SourceDelegate.cpp @@ -436,7 +436,7 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& painter->setFont( figFont ); QColor figColor( TomahawkStyle::SIDEBAR_ROUNDFIGURE_BACKGROUND ); - painter->setPen( figColor ); + painter->setPen( Qt::white ); painter->setBrush( figColor ); TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, figRect ); From 0f3cfb210c48b5ded4168eec9c7960eb7216d2a2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 08:58:47 +0200 Subject: [PATCH 224/565] * Work on artist page. Better layout / design? --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 52 +++- .../widgets/infowidgets/ArtistInfoWidget.h | 4 + .../widgets/infowidgets/ArtistInfoWidget.ui | 244 ++++++++++-------- 3 files changed, 177 insertions(+), 123 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index d6b0533272..7ee694467f 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -36,7 +36,9 @@ #include "Source.h" #include "GlobalActionManager.h" #include "Pipeline.h" +#include "SourceList.h" #include "MetaPlaylistInterface.h" +#include "widgets/StatsGauge.h" #include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -52,11 +54,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QWidget* widget = new QWidget; ui->setupUi( widget ); - QPalette pal = palette(); - pal.setColor( QPalette::Window, TomahawkStyle::PAGE_BACKGROUND ); - - widget->setPalette( pal ); - widget->setAutoFillBackground( true ); + artist->loadStats(); + connect( artist.data(), SIGNAL( statsLoaded() ), SLOT( onArtistStatsLoaded() ) ); /* TomahawkUtils::unmarginLayout( ui->layoutWidget->layout() ); TomahawkUtils::unmarginLayout( ui->layoutWidget1->layout() ); @@ -82,10 +81,10 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* AlbumItemDelegate* del = new AlbumItemDelegate( ui->topHits, ui->topHits->proxyModel() ); ui->topHits->setPlaylistItemDelegate( del ); - ui->relatedArtists->setAutoFitItems( false ); +/* ui->relatedArtists->setAutoFitItems( true ); ui->relatedArtists->setWrapping( false ); ui->relatedArtists->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - ui->relatedArtists->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); + ui->relatedArtists->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ ui->relatedArtists->delegate()->setItemSize( QSize( 170, 170 ) ); ui->albums->setAutoFitItems( false ); @@ -95,15 +94,32 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->albums->delegate()->setItemSize( QSize( 170, 170 ) ); ui->albums->proxyModel()->setHideDupeItems( true ); - ui->topHits->setFrameShape( QFrame::StyledPanel ); + QPalette trackViewPal = ui->topHits->palette(); + trackViewPal.setColor( QPalette::Foreground, Qt::white ); + trackViewPal.setColor( QPalette::Text, Qt::white ); + trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); + trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); + + ui->topHits->setPalette( trackViewPal ); + ui->topHits->setAlternatingRowColors( false ); + ui->topHits->setFrameShape( QFrame::NoFrame ); ui->topHits->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); + m_playStatsGauge = new StatsGauge( ui->statsWidget ); + m_playStatsGauge->setText( tr( "CHART #" ) ); + + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + l->addWidget( m_playStatsGauge ); + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + ui->statsWidget->setLayout( l ); + m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Original, QSize( 48, 48 ) ); ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Grid, ui->cover->size() ) ); ui->cover->setShowText( true ); QFont f = font(); - f.setPointSize( f.pointSize() + 1 ); + f.setPointSize( f.pointSize() + 3 ); ui->biography->setOpenLinks( false ); ui->biography->setOpenExternalLinks( true ); ui->biography->setFrameShape( QFrame::NoFrame ); @@ -125,7 +141,10 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); area->setWidget( widget ); - area->setStyleSheet( "QScrollArea { background-color: #454e59; }" ); + QPalette pal = palette(); + pal.setBrush( backgroundRole(), QColor( "#1e1e1e" ) ); //QBrush( QImage( ":/data/images/grey_wash_wall.png" ) ) ); + area->setPalette( pal ); + area->setAutoFillBackground( true ); area->setFrameShape( QFrame::NoFrame ); area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); @@ -152,7 +171,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); -// ui->topHits->setStyleSheet( "QTreeView#topHits { background-color: transparent; }" ); + ui->topHits->setStyleSheet( "QTreeView#topHits { background-color: transparent; }" ); ui->trackFrame->setStyleSheet( "QFrame#trackFrame { background-color: transparent; }" "QFrame#trackFrame { " "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" @@ -309,6 +328,17 @@ ArtistInfoWidget::onBiographyLoaded() } +void +ArtistInfoWidget::onArtistStatsLoaded() +{ +/* m_playStatsGauge->setValue( m_artist->playbackCount( SourceList::instance()->getLocal() ) ); + m_playStatsGauge->setMaximum( SourceList::instance()->getLocal()->playbackCount() ); */ + + m_playStatsGauge->setMaximum( m_artist->chartCount() ); + m_playStatsGauge->setValue( m_artist->chartPosition() ); +} + + void ArtistInfoWidget::onArtistImageUpdated() { diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h index 0e2b46ae16..23b55677cf 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h @@ -40,6 +40,7 @@ class PlayableModel; class PlaylistModel; +class StatsGauge; namespace Ui { @@ -91,6 +92,7 @@ Q_OBJECT void changeEvent( QEvent* e ); private slots: + void onArtistStatsLoaded(); void onArtistImageUpdated(); void onBiographyLoaded(); @@ -110,6 +112,8 @@ private slots: PlaylistModel* m_topHitsModel; Tomahawk::playlistinterface_ptr m_plInterface; + StatsGauge* m_playStatsGauge; + QString m_title; QString m_description; QString m_longDescription; diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 5f78b702a2..751ab256e3 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -13,7 +13,7 @@ Form - + 16 @@ -69,135 +69,155 @@ + + + + + 220 + 240 + + + + + 240 + 16777215 + + + + - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 8 - - - 4 - - - 8 - - - 8 - - - - - - 18 - 75 - true - + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 - - Top Hits + + 8 - - 0 + + 4 - - - - - - - 0 - 0 - + + 8 - - true + + 8 - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 8 - - - 4 - - - 8 - - - 4 - - - - - - Arial - 18 - 75 - true - + + + + + 18 + 75 + true + + + + Top Hits + + + 0 + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 - - Albums + + 8 - - 0 + + 4 - - - - - - - 0 - 0 - + + 8 - - - 0 - 190 - + + 4 - - - - + + + + + Arial + 18 + 75 + true + + + + Related Artists + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 190 + + + + + + + + - + QFrame::StyledPanel QFrame::Raised - + 4 @@ -214,7 +234,7 @@ 4 - + Arial @@ -224,7 +244,7 @@ - Related Artists + Albums 0 @@ -232,7 +252,7 @@ - + 0 From 86e201673f1883c1204c21e475ec71eb8454e9fc Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 08:59:08 +0200 Subject: [PATCH 225/565] * Added StatsGauge. --- src/libtomahawk/CMakeLists.txt | 2 + src/libtomahawk/widgets/StatsGauge.cpp | 129 +++++++++++++++++++++++++ src/libtomahawk/widgets/StatsGauge.h | 49 ++++++++++ 3 files changed, 180 insertions(+) create mode 100644 src/libtomahawk/widgets/StatsGauge.cpp create mode 100644 src/libtomahawk/widgets/StatsGauge.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 459bf8b4d8..d12b525cde 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -132,6 +132,7 @@ set( libGuiSources widgets/CheckDirTree.cpp widgets/QueryLabel.cpp widgets/ImageButton.cpp + widgets/StatsGauge.cpp widgets/AnimatedSplitter.cpp widgets/ElidedLabel.cpp widgets/NewPlaylistWidget.cpp @@ -250,6 +251,7 @@ list(APPEND libSources database/DatabaseCommand_SourceOffline.cpp database/DatabaseCommand_CollectionStats.cpp database/DatabaseCommand_TrackStats.cpp + database/DatabaseCommand_ArtistStats.cpp database/DatabaseCommand_LoadPlaylistEntries.cpp database/DatabaseCommand_LoadInboxEntries.cpp database/DatabaseCommand_ModifyPlaylist.cpp diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp new file mode 100644 index 0000000000..64b308c916 --- /dev/null +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -0,0 +1,129 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "StatsGauge.h" + +#include "utils/TomahawkStyle.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +#include +#include +#include +#include +#include +#include + + +StatsGauge::StatsGauge( QWidget* parent ) + : QProgressBar( parent ) +{ + QProgressBar::setValue( 0 ); + QProgressBar::setMaximum( 0 ); + + m_sizeHint = QSize( 200, 240 ); + resize( m_sizeHint ); + setFixedSize( m_sizeHint ); +} + + +void +StatsGauge::paintEvent( QPaintEvent* event ) +{ + QPainter p( this ); + p.setRenderHint( QPainter::Antialiasing ); + p.setClipRect( event->rect() ); + + QSize gaugeSize = m_sizeHint - QSize( 0, 40 ); + + QPen pen( TomahawkStyle::NOW_PLAYING_ITEM.lighter() ); + pen.setWidth( 16 ); + p.setPen( pen ); + + int fullCircle = 16 * 360; + p.drawArc( QRect( 12, 12, gaugeSize.width() - 24, gaugeSize.height() - 24 ), 4*360, (int)( -1.0 * (float)fullCircle * ( 1.0 - (float)value() / (float)maximum() ) ) ); + + pen = QPen( TomahawkStyle::NOW_PLAYING_ITEM.darker() ); + pen.setWidth( 6 ); + p.setPen( pen ); + + QBrush brush( QColor( "#252020" ) ); + p.setBrush( brush ); + p.drawEllipse( QRect( 28, 28, gaugeSize.width() - 56, gaugeSize.height() - 56 ) ); + + pen = QPen( Qt::white ); + p.setPen( pen ); + QFont font = p.font(); + font.setWeight( QFont::Black ); + font.setPixelSize( 60 ); + p.setFont( font ); + + QRect textRect( 0, gaugeSize.height() / 2 - 14, gaugeSize.width(), 62 ); + p.drawText( textRect, Qt::AlignCenter, value() > 0 ? QString::number( value() ) : "-" ); + + pen = QPen( QColor( "#8b8b8b" ) ); + p.setPen( pen ); + font = p.font(); + font.setWeight( QFont::Black ); + font.setPixelSize( 16 ); + p.setFont( font ); + + textRect = QRect( 0, gaugeSize.height() / 2 - 32, gaugeSize.width(), 20 ); + p.drawText( textRect, Qt::AlignCenter, maximum() > 0 ? QString( "out of %1" ).arg( maximum() ) : "-" ); + + if ( !m_text.isEmpty() ) + { + pen = QPen( Qt::white ); + p.setPen( pen ); + font = p.font(); + font.setWeight( QFont::Black ); + font.setPixelSize( 18 ); + p.setFont( font ); + + QColor figColor( "#3e3e3e" ); + p.setBrush( figColor ); + + QFontMetrics fm( font ); + int textWidth = fm.width( m_text ); + textRect = QRect( m_sizeHint.width() / 2 - ( textWidth / 2 + 12 ), m_sizeHint.height() - 32, textWidth + 24, 28 ); + + TomahawkUtils::drawBackgroundAndNumbers( &p, m_text, textRect ); + } +} + + +void +StatsGauge::setValue( int v ) +{ + QPropertyAnimation* a = new QPropertyAnimation( (QProgressBar*)this, "value" ); + a->setEasingCurve( QEasingCurve( QEasingCurve::InOutQuad ) ); + a->setStartValue( value() ); + a->setEndValue( v ); + a->setDuration( 2000 ); + + connect( a, SIGNAL( finished() ), a, SLOT( deleteLater() ) ); + a->start(); +} + + +void +StatsGauge::setText( const QString& text ) +{ + m_text = text; + repaint(); +} \ No newline at end of file diff --git a/src/libtomahawk/widgets/StatsGauge.h b/src/libtomahawk/widgets/StatsGauge.h new file mode 100644 index 0000000000..dc7720d752 --- /dev/null +++ b/src/libtomahawk/widgets/StatsGauge.h @@ -0,0 +1,49 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef STATS_GAUGE_H +#define STATS_GAUGE_H + +#include "DllMacro.h" + +#include + +class DLLEXPORT StatsGauge : public QProgressBar +{ +Q_OBJECT + +public: + /** this pixmap becomes the rest state pixmap and defines the size of the eventual widget */ + explicit StatsGauge( QWidget* parent = 0 ); + + virtual QSize sizeHint() const { return m_sizeHint; } + QString text() const { return m_text; } + +public slots: + void setValue( int value ); + void setText( const QString& text ); + +protected: + virtual void paintEvent( QPaintEvent* event ); + +private: + QSize m_sizeHint; + QString m_text; +}; + +#endif //STATS_GAUGE_H From b5090d28730d4c25dd364d5d4b66bdba271e9056 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 09:09:04 +0200 Subject: [PATCH 226/565] * Chart values need to be always initialized. --- src/libtomahawk/Artist.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/Artist.cpp b/src/libtomahawk/Artist.cpp index ff60d8a210..60b3bebe18 100644 --- a/src/libtomahawk/Artist.cpp +++ b/src/libtomahawk/Artist.cpp @@ -170,6 +170,8 @@ Artist::Artist( const QString& name ) , m_simArtistsLoaded( false ) , m_biographyLoaded( false ) , m_infoJobs( 0 ) + , m_chartPosition( 0 ) + , m_chartCount( 0 ) #ifndef ENABLE_HEADLESS , m_cover( 0 ) #endif From e9a33e3ed48612a32338b5734080223df011a421 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 09:09:29 +0200 Subject: [PATCH 227/565] * Start animating the gauge with value 1. --- src/libtomahawk/widgets/StatsGauge.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index 64b308c916..9d52a7655a 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -91,8 +91,8 @@ StatsGauge::paintEvent( QPaintEvent* event ) pen = QPen( Qt::white ); p.setPen( pen ); font = p.font(); - font.setWeight( QFont::Black ); - font.setPixelSize( 18 ); + font.setWeight( QFont::DemiBold ); + font.setPixelSize( 16 ); p.setFont( font ); QColor figColor( "#3e3e3e" ); @@ -112,7 +112,7 @@ StatsGauge::setValue( int v ) { QPropertyAnimation* a = new QPropertyAnimation( (QProgressBar*)this, "value" ); a->setEasingCurve( QEasingCurve( QEasingCurve::InOutQuad ) ); - a->setStartValue( value() ); + a->setStartValue( value() > 0 ? value() : 1 ); a->setEndValue( v ); a->setDuration( 2000 ); From df7fafced1fe99ae99036bf09d19c8c9c39b466f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 09:12:31 +0200 Subject: [PATCH 228/565] * Don't try animating with bogus values. --- src/libtomahawk/widgets/StatsGauge.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index 9d52a7655a..499fba9f09 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -28,6 +28,7 @@ #include #include #include +#include StatsGauge::StatsGauge( QWidget* parent ) @@ -110,8 +111,11 @@ StatsGauge::paintEvent( QPaintEvent* event ) void StatsGauge::setValue( int v ) { + if ( maximum() == 0 ) + return; + QPropertyAnimation* a = new QPropertyAnimation( (QProgressBar*)this, "value" ); - a->setEasingCurve( QEasingCurve( QEasingCurve::InOutQuad ) ); + a->setEasingCurve( QEasingCurve( QEasingCurve::OutQuad ) ); a->setStartValue( value() > 0 ? value() : 1 ); a->setEndValue( v ); a->setDuration( 2000 ); From 25f2c2db6641f7adec9f0106307adbc6d8df1ed6 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 10:31:08 +0200 Subject: [PATCH 229/565] * Changed PAGE_BACKGROUND color. --- src/libtomahawk/utils/TomahawkStyle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index 0d0113b805..ee7c81ad31 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -58,7 +58,7 @@ namespace TomahawkStyle static const QColor HEADER_TEXT = QColor( "#eaeaea" ); static const QColor HEADER_HIGHLIGHT = QColor( "#333" ); - static const QColor PAGE_BACKGROUND = QColor( "#272b2e" ); + static const QColor PAGE_BACKGROUND = QColor( "#1e1e1e" ); static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); From e11b1515a9ee82fe5fd0f8496fe8f6969b6aa9fe Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 10:31:31 +0200 Subject: [PATCH 230/565] * Offer inverted gauge animations. --- src/libtomahawk/widgets/StatsGauge.cpp | 19 +++++++++++++++---- src/libtomahawk/widgets/StatsGauge.h | 3 +++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index 499fba9f09..3bdc409f73 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -33,6 +33,7 @@ StatsGauge::StatsGauge( QWidget* parent ) : QProgressBar( parent ) + , m_inverted( false ) { QProgressBar::setValue( 0 ); QProgressBar::setMaximum( 0 ); @@ -57,7 +58,9 @@ StatsGauge::paintEvent( QPaintEvent* event ) p.setPen( pen ); int fullCircle = 16 * 360; - p.drawArc( QRect( 12, 12, gaugeSize.width() - 24, gaugeSize.height() - 24 ), 4*360, (int)( -1.0 * (float)fullCircle * ( 1.0 - (float)value() / (float)maximum() ) ) ); + float pct = m_inverted ? ( 1.0 - (float)value() / (float)maximum() ) : (float)value() / (float)maximum(); + + p.drawArc( QRect( 12, 12, gaugeSize.width() - 24, gaugeSize.height() - 24 ), 4*360, (int)( -1.0 * (float)fullCircle * pct ) ); pen = QPen( TomahawkStyle::NOW_PLAYING_ITEM.darker() ); pen.setWidth( 6 ); @@ -85,7 +88,7 @@ StatsGauge::paintEvent( QPaintEvent* event ) p.setFont( font ); textRect = QRect( 0, gaugeSize.height() / 2 - 32, gaugeSize.width(), 20 ); - p.drawText( textRect, Qt::AlignCenter, maximum() > 0 ? QString( "out of %1" ).arg( maximum() ) : "-" ); + p.drawText( textRect, Qt::AlignCenter, maximum() > 0 ? tr( "out of %1" ).arg( maximum() ) : "-" ); if ( !m_text.isEmpty() ) { @@ -111,7 +114,7 @@ StatsGauge::paintEvent( QPaintEvent* event ) void StatsGauge::setValue( int v ) { - if ( maximum() == 0 ) + if ( maximum() == 0 || v == 0 ) return; QPropertyAnimation* a = new QPropertyAnimation( (QProgressBar*)this, "value" ); @@ -130,4 +133,12 @@ StatsGauge::setText( const QString& text ) { m_text = text; repaint(); -} \ No newline at end of file +} + + +void +StatsGauge::setInvertedGauge( bool inverted ) +{ + m_inverted = inverted; + repaint(); +} diff --git a/src/libtomahawk/widgets/StatsGauge.h b/src/libtomahawk/widgets/StatsGauge.h index dc7720d752..9e27e68d83 100644 --- a/src/libtomahawk/widgets/StatsGauge.h +++ b/src/libtomahawk/widgets/StatsGauge.h @@ -34,6 +34,8 @@ Q_OBJECT virtual QSize sizeHint() const { return m_sizeHint; } QString text() const { return m_text; } + void setInvertedGauge( bool inverted ); + public slots: void setValue( int value ); void setText( const QString& text ); @@ -44,6 +46,7 @@ public slots: private: QSize m_sizeHint; QString m_text; + bool m_inverted; }; #endif //STATS_GAUGE_H From 1e579bc238c6584afbc8f780dddde040b1b2de29 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 10:31:57 +0200 Subject: [PATCH 231/565] * Adapt Album page to new style. --- .../widgets/infowidgets/AlbumInfoWidget.cpp | 27 ++- .../widgets/infowidgets/AlbumInfoWidget.ui | 229 +++++++++--------- 2 files changed, 135 insertions(+), 121 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index ce2fb1db61..c4fe1b9fab 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -51,18 +51,21 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par QWidget* widget = new QWidget; ui->setupUi( widget ); - QPalette pal = palette(); - pal.setColor( QPalette::Window, TomahawkStyle::PAGE_BACKGROUND ); - - widget->setPalette( pal ); - widget->setAutoFillBackground( true ); - m_albumsModel = new PlayableModel( ui->albums ); ui->albums->setPlayableModel( m_albumsModel ); ui->albums->setEmptyTip( tr( "Sorry, we could not find any other albums for this artist!" ) ); m_tracksModel = new TreeModel( ui->tracks ); m_tracksModel->setMode( Mixed ); + + QPalette trackViewPal = ui->tracks->palette(); + trackViewPal.setColor( QPalette::Foreground, Qt::white ); + trackViewPal.setColor( QPalette::Text, Qt::white ); + trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); + trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); + + ui->tracks->setPalette( trackViewPal ); + ui->tracks->setAlternatingRowColors( false ); ui->tracks->setRootIsDecorated( false ); ui->tracks->setEmptyTip( tr( "Sorry, we could not find any tracks for this album!" ) ); ui->tracks->proxyModel()->setStyle( PlayableProxyModel::Large ); @@ -72,14 +75,14 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par AlbumItemDelegate* del = new AlbumItemDelegate( ui->tracks, ui->tracks->proxyModel() ); ui->tracks->setPlaylistItemDelegate( del ); - ui->albums->setAutoFitItems( false ); +/* ui->albums->setAutoFitItems( false ); ui->albums->setWrapping( false ); ui->albums->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); + ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ ui->albums->delegate()->setItemSize( QSize( 170, 170 ) ); ui->albums->proxyModel()->setHideDupeItems( true ); - ui->tracks->setFrameShape( QFrame::StyledPanel ); + ui->tracks->setFrameShape( QFrame::NoFrame ); ui->tracks->setAttribute( Qt::WA_MacShowFocusRect, 0 ); m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::Original, QSize( 48, 48 ) ); @@ -103,7 +106,10 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); area->setWidget( widget ); - area->setStyleSheet( "QScrollArea { background-color: #454e59; }" ); + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + area->setPalette( pal ); + area->setAutoFillBackground( true ); area->setFrameShape( QFrame::NoFrame ); area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); @@ -123,6 +129,7 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + ui->tracks->setStyleSheet( "QTreeView#tracks { background-color: transparent; }" ); ui->trackFrame->setStyleSheet( "QFrame#trackFrame { background-color: transparent; }" "QFrame#trackFrame { " "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui index f931cbde43..ebe54566d5 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui @@ -13,7 +13,7 @@ Form - + 16 @@ -72,122 +72,129 @@ - - - QFrame::StyledPanel - - - QFrame::Raised + + + 0 - - - 4 - - - 8 - - - 4 - - - 8 - - - 8 - - - - - - 18 - 75 - true - + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 - - Tracklist + + 8 - - 0 + + 4 - - - - - - - 0 - 0 - + + 8 - - true + + 8 - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 8 - - - 4 - - - 8 - - - 4 - - - - - - Arial - 18 - 75 - true - + + + + + 18 + 75 + true + + + + Tracklist + + + 0 + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 - - Other Albums + + 8 - - 0 + + 4 - - - - - - - 0 - 0 - + + 8 - - - 0 - 190 - + + 4 - - - - + + + + + Arial + 18 + 75 + true + + + + Other Albums + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 190 + + + + + + + + @@ -210,16 +217,16 @@ QListView
playlist/GridView.h
- - TrackView - QTreeView -
playlist/TrackView.h
-
PlayableCover QLabel
widgets/PlayableCover.h
+ + TrackView + QTreeView +
playlist/TrackView.h
+
From 89202ae4451b59ccbc19d08fd8d745ceed5a0a39 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 10:32:19 +0200 Subject: [PATCH 232/565] * Fixed Artist page. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 3 ++- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 7ee694467f..9376cfa51b 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -108,6 +108,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); m_playStatsGauge->setText( tr( "CHART #" ) ); + m_playStatsGauge->setInvertedGauge( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); l->addWidget( m_playStatsGauge ); @@ -142,7 +143,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* area->setWidget( widget ); QPalette pal = palette(); - pal.setBrush( backgroundRole(), QColor( "#1e1e1e" ) ); //QBrush( QImage( ":/data/images/grey_wash_wall.png" ) ) ); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); area->setPalette( pal ); area->setAutoFillBackground( true ); area->setFrameShape( QFrame::NoFrame ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 751ab256e3..fa6e231996 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -13,7 +13,7 @@ Form - + 16 @@ -79,7 +79,7 @@ - 240 + 220 16777215 @@ -89,6 +89,9 @@
+ + 0 + From f1483bfe3f1e6591b281614b638c6b830520dd4f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 10:32:56 +0200 Subject: [PATCH 233/565] * Adapted Track page to new style. --- .../widgets/infowidgets/TrackInfoWidget.cpp | 28 +++++++++++++------ .../widgets/infowidgets/TrackInfoWidget.h | 2 ++ .../widgets/infowidgets/TrackInfoWidget.ui | 18 +++++++++++- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index b682b0d96b..dd354ca2bb 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -28,7 +28,7 @@ #include "SourceList.h" #include "playlist/PlayableModel.h" #include "audio/AudioEngine.h" - +#include "widgets/StatsGauge.h" #include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -44,12 +44,6 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par QWidget* widget = new QWidget; ui->setupUi( widget ); - QPalette pal = palette(); - pal.setColor( QPalette::Window, TomahawkStyle::PAGE_BACKGROUND ); - - widget->setPalette( pal ); - widget->setAutoFillBackground( true ); - ui->statsLabel->setStyleSheet( "QLabel { background-image:url(); border: 2px solid #dddddd; background-color: #faf9f9; border-radius: 4px; padding: 12px; }" ); ui->lyricsView->setStyleSheet( "QTextBrowser#lyricsView { background-color: transparent; }" ); @@ -91,10 +85,23 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par m_scrollArea->setWidget( widget ); m_scrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); - m_scrollArea->setStyleSheet( "QScrollArea { background-color: #454e59 }" ); + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + m_scrollArea->setPalette( pal ); + m_scrollArea->setAutoFillBackground( true ); m_scrollArea->setFrameShape( QFrame::NoFrame ); m_scrollArea->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); + m_playStatsGauge = new StatsGauge( ui->statsWidget ); + m_playStatsGauge->setText( tr( "PLAYS" ) ); + + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + l->addWidget( m_playStatsGauge ); + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + ui->statsWidget->setLayout( l ); + ui->statsLabel->setVisible( false ); + QVBoxLayout* layout = new QVBoxLayout(); layout->addWidget( m_scrollArea ); setLayout( layout ); @@ -223,7 +230,12 @@ TrackInfoWidget::onStatsLoaded() } if ( artistCounter ) + { stats += "\n" + tr( "You've listened to %1 %n time(s).", "", artistCounter ).arg( m_artist->name() ); + + m_playStatsGauge->setMaximum( artistCounter ); + m_playStatsGauge->setValue( trackCounter ); + } else stats += "\n" + tr( "You've never listened to %1 before." ).arg( m_artist->name() ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h index dfc8b26a14..7362660bd4 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h @@ -40,6 +40,7 @@ #include class PlayableModel; +class StatsGauge; class QScrollArea; namespace Ui @@ -94,6 +95,7 @@ private slots: Tomahawk::query_ptr m_query; Tomahawk::artist_ptr m_artist; + StatsGauge* m_playStatsGauge; PlayableModel* m_relatedTracksModel; QString m_title; QPixmap m_pixmap; diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index 9a36699d3a..c3fe0e8ac2 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -13,7 +13,7 @@ Form - + 16 @@ -85,6 +85,22 @@ + + + + + 220 + 240 + + + + + 220 + 16777215 + + + + From 62db235b3c00ee411c01f980f16991b9e4242c9e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 10:44:58 +0200 Subject: [PATCH 234/565] * Set pen color correctly for Inbox. --- src/tomahawk/sourcetree/SourceDelegate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tomahawk/sourcetree/SourceDelegate.cpp b/src/tomahawk/sourcetree/SourceDelegate.cpp index 137aab0931..f8840758de 100644 --- a/src/tomahawk/sourcetree/SourceDelegate.cpp +++ b/src/tomahawk/sourcetree/SourceDelegate.cpp @@ -711,7 +711,7 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co painter->setFont( figFont ); QColor figColor( TomahawkStyle::SIDEBAR_ROUNDFIGURE_INBOX_BACKGROUND ); - painter->setPen( figColor ); + painter->setPen( Qt::white ); painter->setBrush( figColor ); TomahawkUtils::drawBackgroundAndNumbers( painter, count, figRect ); From 004d84f1b06936a6ad9ed57d4b5d454222904726 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 11:26:52 +0200 Subject: [PATCH 235/565] * Fixed broken png causing libpng warning. --- .../admin/icons/hatchet-icon-512x512.png | Bin 28842 -> 26883 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/accounts/hatchet/admin/icons/hatchet-icon-512x512.png b/src/accounts/hatchet/admin/icons/hatchet-icon-512x512.png index fabab44dd22f9a9e886a38af47cbfe0288f83e83..cf0e6bd4288fb49fffe1ce14566a1839e3d76fc5 100644 GIT binary patch literal 26883 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�s&b4z^2QN*y4}2Kn+7`QI$_ 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 Fe*nZo59j~@ literal 28842 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 From 3de4042d6e69bacca23bf838958f8e8771ec060b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 11:27:15 +0200 Subject: [PATCH 236/565] * Fixed Accounts stuff causing QLayout warning. --- src/tomahawk/widgets/AccountListWidget.cpp | 2 +- src/tomahawk/widgets/AccountsToolButton.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tomahawk/widgets/AccountListWidget.cpp b/src/tomahawk/widgets/AccountListWidget.cpp index 4606043f49..4714be4be9 100644 --- a/src/tomahawk/widgets/AccountListWidget.cpp +++ b/src/tomahawk/widgets/AccountListWidget.cpp @@ -73,7 +73,7 @@ AccountListWidget::AccountListWidget( AccountModelFactoryProxy* model, QWidget* connect( m_toggleOnlineButton, SIGNAL( clicked() ), this, SLOT( toggleOnlineStateForAll() ) ); - QHBoxLayout *headerLayout = new QHBoxLayout( this ); + QHBoxLayout *headerLayout = new QHBoxLayout(); headerLayout->addWidget( connectionsLabel ); headerLayout->addSpacing( 30 ); headerLayout->addWidget( m_toggleOnlineButton ); diff --git a/src/tomahawk/widgets/AccountsToolButton.cpp b/src/tomahawk/widgets/AccountsToolButton.cpp index ecb4fedee1..a6288ea50d 100644 --- a/src/tomahawk/widgets/AccountsToolButton.cpp +++ b/src/tomahawk/widgets/AccountsToolButton.cpp @@ -89,7 +89,7 @@ AccountsToolButton::AccountsToolButton( QWidget* parent ) settingsButton->setText( tr( "Configure Accounts" ) ); connect( settingsButton, SIGNAL( clicked() ), window(), SLOT( showSettingsDialog() ) ); - QHBoxLayout *bottomLayout = new QHBoxLayout( w ); + QHBoxLayout *bottomLayout = new QHBoxLayout(); TomahawkUtils::unmarginLayout( bottomLayout ); bottomLayout->addStretch(); bottomLayout->addWidget( settingsButton ); From 3e068031623af99c3afe6fb3c471e5cf23865e18 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 11:27:58 +0200 Subject: [PATCH 237/565] * Fixed creating QPixmaps in the wrong thread. --- src/libtomahawk/Source.cpp | 14 +++++++++++--- src/libtomahawk/Source.h | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 1bfdd0ca47..035288f015 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -57,6 +57,7 @@ Source::Source( int id, const QString& nodeId ) , m_id( id ) , m_updateIndexWhenSynced( false ) , m_state( DBSyncConnection::UNKNOWN ) + , m_avatar( 0 ) , m_avatarLoaded( false ) , m_cc( 0 ) , m_commandCount( 0 ) @@ -261,7 +262,12 @@ Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) } if ( m_avatarLoaded ) - return m_avatar; + { + if ( m_avatar ) + return *m_avatar; + else + return QPixmap(); + } // Try to get the avatar from the cache // Hint: We store the avatar for each xmpp peer using its contactId, the dbFriendlyName is a contactId of a peer @@ -269,14 +275,16 @@ Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) QByteArray avatarBuffer = TomahawkUtils::Cache::instance()->getData( "Sources", dbFriendlyName() ).toByteArray(); if ( !avatarBuffer.isNull() ) { + tDebug() << Q_FUNC_INFO << QThread::currentThread(); QPixmap avatar; avatar.loadFromData( avatarBuffer ); avatarBuffer.clear(); - m_avatar = QPixmap( TomahawkUtils::createRoundedImage( avatar, QSize( 0, 0 ) ) ); + m_avatar = new QPixmap( TomahawkUtils::createRoundedImage( avatar, QSize( 0, 0 ) ) ); + return *m_avatar; } - return m_avatar; + return QPixmap(); } #endif diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 1f5c1dddc3..c4dcdf8680 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -171,7 +171,7 @@ private slots: DBSyncConnection::State m_state; QTimer m_currentTrackTimer; - QPixmap m_avatar; + QPixmap* m_avatar; bool m_avatarLoaded; QPointer m_cc; From 530d5402a1a07bccc8be4e81f094d75615fb6b54 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 26 May 2013 21:10:30 +0200 Subject: [PATCH 238/565] * Fix Logger with Qt5. --- src/libtomahawk/utils/Logger.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/utils/Logger.cpp b/src/libtomahawk/utils/Logger.cpp index a5a5d1aed4..457b881bd0 100644 --- a/src/libtomahawk/utils/Logger.cpp +++ b/src/libtomahawk/utils/Logger.cpp @@ -114,7 +114,8 @@ TomahawkLogHandler( QtMsgType type, const char* msg ) static QMutex s_mutex; #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) - const char* message = msg.toLatin1().constData(); + QByteArray ba = msg.toUtf8(); + const char* message = ba.constData(); #else const char* message = msg; #endif From 84faa1671c3ef3b9493a03b479cef398a14d51c9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 26 May 2013 21:38:46 +0200 Subject: [PATCH 239/565] * CreateIndex isn't the right place to connect signals. --- src/libtomahawk/playlist/PlayableModel.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 1fed2226bf..148d227440 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -69,12 +69,6 @@ PlayableModel::~PlayableModel() QModelIndex PlayableModel::createIndex( int row, int column, PlayableItem* item ) const { - if ( item->query() ) - { - connect( item->query().data(), SIGNAL( playableStateChanged( bool ) ), SLOT( onQueryBecamePlayable( bool ) ), Qt::UniqueConnection ); - connect( item->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( onQueryResolved( bool ) ), Qt::UniqueConnection ); - } - return QAbstractItemModel::createIndex( row, column, item ); } @@ -635,6 +629,11 @@ PlayableModel::insertInternal( const QList< T >& items, int row, const QList< To { plitem = new PlayableItem( item, m_rootItem, row + i ); plitem->index = createIndex( row + i, 0, plitem ); + if ( item->query() ) + { + connect( item->query().data(), SIGNAL( playableStateChanged( bool ) ), SLOT( onQueryBecamePlayable( bool ) ), Qt::UniqueConnection ); + connect( item->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( onQueryResolved( bool ) ), Qt::UniqueConnection ); + } if ( logs.count() > i ) plitem->setPlaybackLog( logs.at( i ) ); From b37e0d801ec671232afe809bcad59014e28ed9ba Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 26 May 2013 21:40:41 +0200 Subject: [PATCH 240/565] * Fixed typos. --- src/libtomahawk/playlist/PlayableModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 148d227440..391b8c1641 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -629,10 +629,10 @@ PlayableModel::insertInternal( const QList< T >& items, int row, const QList< To { plitem = new PlayableItem( item, m_rootItem, row + i ); plitem->index = createIndex( row + i, 0, plitem ); - if ( item->query() ) + if ( plitem->query() ) { - connect( item->query().data(), SIGNAL( playableStateChanged( bool ) ), SLOT( onQueryBecamePlayable( bool ) ), Qt::UniqueConnection ); - connect( item->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( onQueryResolved( bool ) ), Qt::UniqueConnection ); + connect( plitem->query().data(), SIGNAL( playableStateChanged( bool ) ), SLOT( onQueryBecamePlayable( bool ) ), Qt::UniqueConnection ); + connect( plitem->query().data(), SIGNAL( resolvingFinished( bool ) ), SLOT( onQueryResolved( bool ) ), Qt::UniqueConnection ); } if ( logs.count() > i ) From 5350330141716c4a6a6c3fd05c04826f1fd666b8 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 11:54:48 +0200 Subject: [PATCH 241/565] * InfoSystem now emits ready() when init is done. --- src/libtomahawk/infosystem/InfoSystem.cpp | 32 +++++++++++-------- src/libtomahawk/infosystem/InfoSystem.h | 11 ++++--- .../infosystem/InfoSystemWorker.cpp | 3 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/libtomahawk/infosystem/InfoSystem.cpp b/src/libtomahawk/infosystem/InfoSystem.cpp index fb303cffae..f0f2baa1bb 100644 --- a/src/libtomahawk/infosystem/InfoSystem.cpp +++ b/src/libtomahawk/infosystem/InfoSystem.cpp @@ -80,6 +80,7 @@ InfoPlugin::setFriendlyName( const QString& friendlyName ) m_friendlyName = friendlyName; } + const QString InfoPlugin::friendlyName() const { @@ -99,7 +100,7 @@ InfoSystem::instance() } -InfoSystem::InfoSystem( QObject *parent ) +InfoSystem::InfoSystem( QObject* parent ) : QObject( parent ) , m_inited( false ) , m_infoSystemCacheThreadController( 0 ) @@ -121,7 +122,7 @@ InfoSystem::InfoSystem( QObject *parent ) InfoSystem::~InfoSystem() { - tDebug() << Q_FUNC_INFO << " beginning"; + tDebug() << Q_FUNC_INFO << "beginning"; if ( m_infoSystemWorkerThreadController ) { @@ -131,9 +132,9 @@ InfoSystem::~InfoSystem() delete m_infoSystemWorkerThreadController; m_infoSystemWorkerThreadController = 0; } - tDebug() << Q_FUNC_INFO << " done deleting worker"; + tDebug() << Q_FUNC_INFO << "done deleting worker"; - if( m_infoSystemCacheThreadController ) + if ( m_infoSystemCacheThreadController ) { m_infoSystemCacheThreadController->quit(); m_infoSystemCacheThreadController->wait( 60000 ); @@ -142,7 +143,7 @@ InfoSystem::~InfoSystem() m_infoSystemCacheThreadController = 0; } - tDebug() << Q_FUNC_INFO << " done deleting cache"; + tDebug() << Q_FUNC_INFO << "done deleting cache"; } @@ -182,11 +183,12 @@ InfoSystem::init() QMetaObject::invokeMethod( worker, "init", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoSystemCache*, cache ) ); m_inited = true; + emit ready(); } bool -InfoSystem::getInfo( const InfoRequestData &requestData ) +InfoSystem::getInfo( const InfoRequestData& requestData ) { //qDebug() << Q_FUNC_INFO; if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) @@ -200,7 +202,7 @@ InfoSystem::getInfo( const InfoRequestData &requestData ) bool -InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const InfoTypeMap &inputMap, const InfoTimeoutMap &timeoutMap, bool allSources ) +InfoSystem::getInfo( const QString& caller, const QVariantMap& customData, const InfoTypeMap& inputMap, const InfoTimeoutMap& timeoutMap, bool allSources ) { if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) { @@ -211,7 +213,7 @@ InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const requestData.caller = caller; requestData.customData = customData; requestData.allSources = allSources; - Q_FOREACH( InfoType type, inputMap.keys() ) + foreach ( InfoType type, inputMap.keys() ) { requestData.type = type; requestData.input = inputMap[ type ]; @@ -241,7 +243,7 @@ InfoSystem::pushInfo( InfoPushData pushData ) bool -InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input, const PushInfoFlags pushFlags ) +InfoSystem::pushInfo( const QString& caller, const InfoTypeMap& input, const PushInfoFlags pushFlags ) { if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) { @@ -249,7 +251,7 @@ InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input, const Pus return false; } - Q_FOREACH( InfoType type, input.keys() ) + foreach ( InfoType type, input.keys() ) { InfoPushData pushData( caller, type, input[ type ], pushFlags ); pushData.infoPair = PushInfoPair( QVariantMap(), pushData.input ); @@ -341,7 +343,7 @@ InfoSystem::workerThread() const } -InfoSystemCacheThread::InfoSystemCacheThread( QObject *parent ) +InfoSystemCacheThread::InfoSystemCacheThread( QObject* parent ) : QThread( parent ) { tDebug() << Q_FUNC_INFO; @@ -373,26 +375,29 @@ InfoSystemCacheThread::cache() const } -InfoSystemWorkerThread::InfoSystemWorkerThread( QObject *parent ) +InfoSystemWorkerThread::InfoSystemWorkerThread( QObject* parent ) : QThread( parent ) { tDebug() << Q_FUNC_INFO; } + InfoSystemWorkerThread::~InfoSystemWorkerThread() { tDebug() << Q_FUNC_INFO; } + void InfoSystemWorkerThread::InfoSystemWorkerThread::run() { m_worker = QPointer< InfoSystemWorker >( new InfoSystemWorker() ); exec(); - if( !m_worker.isNull() ) + if ( !m_worker.isNull() ) delete m_worker.data(); } + InfoSystemWorker* InfoSystemWorkerThread::worker() const { @@ -401,7 +406,6 @@ InfoSystemWorkerThread::worker() const return m_worker.data(); } - } //namespace InfoSystem } //namespace Tomahawk diff --git a/src/libtomahawk/infosystem/InfoSystem.h b/src/libtomahawk/infosystem/InfoSystem.h index 6a9e7f5272..ea308eb241 100644 --- a/src/libtomahawk/infosystem/InfoSystem.h +++ b/src/libtomahawk/infosystem/InfoSystem.h @@ -65,7 +65,7 @@ struct DLLEXPORT InfoRequestData { InfoRequestData(); - InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom ); + InfoRequestData( const quint64 rId, const QString& callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant& inputvar, const QVariantMap& custom ); private: void init( const QString& callr, const InfoType typ, const QVariant& inputvar, const QVariantMap& custom); @@ -87,7 +87,7 @@ struct InfoPushData { , infoPair( Tomahawk::InfoSystem::PushInfoPair( QVariantMap(), QVariant() ) ) {} - InfoPushData( const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const Tomahawk::InfoSystem::PushInfoFlags pflags ) + InfoPushData( const QString& callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant& inputvar, const Tomahawk::InfoSystem::PushInfoFlags pflags ) : caller( callr ) , type( typ ) , input( inputvar ) @@ -152,7 +152,7 @@ class DLLEXPORT InfoSystemCacheThread : public QThread Q_OBJECT public: - InfoSystemCacheThread( QObject *parent ); + InfoSystemCacheThread( QObject* parent ); virtual ~InfoSystemCacheThread(); void run(); @@ -170,7 +170,7 @@ class DLLEXPORT InfoSystemWorkerThread : public QThread Q_OBJECT public: - InfoSystemWorkerThread( QObject *parent ); + InfoSystemWorkerThread( QObject* parent ); virtual ~InfoSystemWorkerThread(); void run(); @@ -196,7 +196,7 @@ class DLLEXPORT InfoSystem : public QObject bool getInfo( const InfoRequestData& requestData ); //WARNING: if changing timeoutMillis above, also change in below function in .cpp file - bool getInfo( const QString &caller, const QVariantMap& customData, const InfoTypeMap& inputMap, const InfoTimeoutMap& timeoutMap = InfoTimeoutMap(), bool allSources = false ); + bool getInfo( const QString& caller, const QVariantMap& customData, const InfoTypeMap& inputMap, const InfoTimeoutMap& timeoutMap = InfoTimeoutMap(), bool allSources = false ); bool pushInfo( InfoPushData pushData ); bool pushInfo( const QString& caller, const InfoTypeMap& input, const PushInfoFlags pushFlags ); @@ -214,6 +214,7 @@ public slots: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); void finished( QString target, Tomahawk::InfoSystem::InfoType type ); + void ready(); void updatedSupportedGetTypes( Tomahawk::InfoSystem::InfoTypeSet supportedTypes ); void updatedSupportedPushTypes( Tomahawk::InfoSystem::InfoTypeSet supportedTypes ); diff --git a/src/libtomahawk/infosystem/InfoSystemWorker.cpp b/src/libtomahawk/infosystem/InfoSystemWorker.cpp index eaaf64765c..92c95dae78 100644 --- a/src/libtomahawk/infosystem/InfoSystemWorker.cpp +++ b/src/libtomahawk/infosystem/InfoSystemWorker.cpp @@ -43,6 +43,7 @@ namespace InfoSystem InfoSystemWorker::InfoSystemWorker() : QObject() + , m_cache( 0 ) { tDebug() << Q_FUNC_INFO; @@ -128,7 +129,7 @@ InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), Qt::QueuedConnection ); - + QMetaObject::invokeMethod( plugin.data(), "init", Qt::QueuedConnection ); emit updatedSupportedGetTypes( QSet< InfoType >::fromList( m_infoGetMap.keys() ) ); From 6af1be8fa7f5acf36ce9f930adab2bed5aaabe31 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 11:55:12 +0200 Subject: [PATCH 242/565] * Init needs to wait for InfoSystem to be ready. --- src/tomahawk/TomahawkApp.cpp | 177 ++++++++++++++++++----------------- src/tomahawk/TomahawkApp.h | 1 + 2 files changed, 93 insertions(+), 85 deletions(-) diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index 5ada569b56..1158d39472 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -191,7 +191,6 @@ TomahawkApp::init() #endif TomahawkUtils::setHeadless( m_headless ); - TomahawkSettings* s = TomahawkSettings::instance(); new ACLRegistryImpl( this ); tDebug( LOGINFO ) << "Setting NAM."; @@ -262,90 +261,7 @@ TomahawkApp::init() tDebug() << "Init InfoSystem."; m_infoSystem = QPointer( Tomahawk::InfoSystem::InfoSystem::instance() ); - - tDebug() << "Init AccountManager."; - m_accountManager = QPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); - connect( m_accountManager.data(), SIGNAL( readyForFactories() ), SLOT( initFactoriesForAccountManager() ) ); - connect( m_accountManager.data(), SIGNAL( readyForSip() ), SLOT( initSIP() ) ); - - Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); -#ifndef ENABLE_HEADLESS - EchonestGenerator::setupCatalogs(); - - if ( !m_headless ) - { - tDebug() << "Init MainWindow."; - m_mainwindow = new TomahawkWindow(); - m_mainwindow->setWindowTitle( "Tomahawk" ); - m_mainwindow->setObjectName( "TH_Main_Window" ); - if ( !arguments().contains( "--hide" ) ) - { - m_mainwindow->show(); - } - } -#endif - - tDebug() << "Init Local Collection."; - initLocalCollection(); - tDebug() << "Init Pipeline."; - initPipeline(); - -#ifndef ENABLE_HEADLESS - // load remote list of resolvers able to be installed - AtticaManager::instance(); -#endif - - if ( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) - { - initHTTP(); - } - connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( initHTTP() ) ); - -#ifndef ENABLE_HEADLESS - if ( !s->hasScannerPaths() ) - { - m_mainwindow->showSettingsDialog(); - } -#endif - -#ifdef LIBLASTFM_FOUND - tDebug() << "Init Scrobbler."; - m_scrobbler = new Scrobbler( this ); -#endif - - if ( arguments().contains( "--filescan" ) ) - { - m_scanManager.data()->runFullRescan(); - } - - // Set up echonest catalog synchronizer - Tomahawk::EchonestCatalogSynchronizer::instance(); - - PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); - PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); - - // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome - // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced - // with the default value of 3 in QApplicationPrivate::initialize. - { - QSettings qt_settings( QSettings::UserScope, "Trolltech" ); - qt_settings.beginGroup( "Qt" ); - QApplication::setWheelScrollLines( qt_settings.value( "wheelScrollLines", QApplication::wheelScrollLines() ).toInt() ); - } - -#ifndef ENABLE_HEADLESS - // Make sure to init GAM in the gui thread - GlobalActionManager::instance(); - - // check if our spotify playlist api server is up and running, and enable spotify playlist drops if so - QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/pong" ) ) ); - connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) ); -#endif - -#ifdef Q_OS_MAC - // Make sure to do this after main window is inited - Tomahawk::enableFullscreen( m_mainwindow ); -#endif + connect( m_infoSystem, SIGNAL( ready() ), SLOT( onInfoSystemReady() ) ); } @@ -678,6 +594,97 @@ TomahawkApp::initSIP() } +void +TomahawkApp::onInfoSystemReady() +{ + tDebug() << "Init AccountManager."; + m_accountManager = QPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); + connect( m_accountManager.data(), SIGNAL( readyForFactories() ), SLOT( initFactoriesForAccountManager() ) ); + connect( m_accountManager.data(), SIGNAL( readyForSip() ), SLOT( initSIP() ) ); + + TomahawkSettings* s = TomahawkSettings::instance(); + + Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); +#ifndef ENABLE_HEADLESS + EchonestGenerator::setupCatalogs(); + + if ( !m_headless ) + { + tDebug() << "Init MainWindow."; + m_mainwindow = new TomahawkWindow(); + m_mainwindow->setWindowTitle( "Tomahawk" ); + m_mainwindow->setObjectName( "TH_Main_Window" ); + if ( !arguments().contains( "--hide" ) ) + { + m_mainwindow->show(); + } + } +#endif + + tDebug() << "Init Local Collection."; + initLocalCollection(); + tDebug() << "Init Pipeline."; + initPipeline(); + +#ifndef ENABLE_HEADLESS + // load remote list of resolvers able to be installed + AtticaManager::instance(); +#endif + + if ( arguments().contains( "--http" ) || TomahawkSettings::instance()->value( "network/http", true ).toBool() ) + { + initHTTP(); + } + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( initHTTP() ) ); + +#ifndef ENABLE_HEADLESS + if ( !s->hasScannerPaths() ) + { + m_mainwindow->showSettingsDialog(); + } +#endif + +#ifdef LIBLASTFM_FOUND + tDebug() << "Init Scrobbler."; + m_scrobbler = new Scrobbler( this ); +#endif + + if ( arguments().contains( "--filescan" ) ) + { + m_scanManager.data()->runFullRescan(); + } + + // Set up echonest catalog synchronizer + Tomahawk::EchonestCatalogSynchronizer::instance(); + + PlaylistUpdaterInterface::registerUpdaterFactory( new XspfUpdaterFactory ); + PlaylistUpdaterInterface::registerUpdaterFactory( new SpotifyUpdaterFactory ); + + // Following work-around/fix taken from Clementine rev. 13e13ccd9a95 and courtesy of David Sansome + // A bug in Qt means the wheel_scroll_lines setting gets ignored and replaced + // with the default value of 3 in QApplicationPrivate::initialize. + { + QSettings qt_settings( QSettings::UserScope, "Trolltech" ); + qt_settings.beginGroup( "Qt" ); + QApplication::setWheelScrollLines( qt_settings.value( "wheelScrollLines", QApplication::wheelScrollLines() ).toInt() ); + } + +#ifndef ENABLE_HEADLESS + // Make sure to init GAM in the gui thread + GlobalActionManager::instance(); + + // check if our spotify playlist api server is up and running, and enable spotify playlist drops if so + QNetworkReply* r = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( SPOTIFY_PLAYLIST_API_URL "/pong" ) ) ); + connect( r, SIGNAL( finished() ), this, SLOT( spotifyApiCheckFinished() ) ); +#endif + +#ifdef Q_OS_MAC + // Make sure to do this after main window is inited + Tomahawk::enableFullscreen( m_mainwindow ); +#endif +} + + void TomahawkApp::spotifyApiCheckFinished() { diff --git a/src/tomahawk/TomahawkApp.h b/src/tomahawk/TomahawkApp.h index aca7eb1a78..952ebfe5ba 100644 --- a/src/tomahawk/TomahawkApp.h +++ b/src/tomahawk/TomahawkApp.h @@ -115,6 +115,7 @@ private slots: void initEnergyEventHandler(); void spotifyApiCheckFinished(); + void onInfoSystemReady(); private: void registerMetaTypes(); From 7ea790d40bcbde8f48178c03b7a0dfe580d46c59 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 11:55:32 +0200 Subject: [PATCH 243/565] * Fixed a bunch of compiler warnings. --- src/accounts/zeroconf/Zeroconf.cpp | 1 + src/accounts/zeroconf/Zeroconf.h | 8 +++----- src/libtomahawk/jobview/InboxJobItem.cpp | 2 +- src/libtomahawk/network/ControlConnection.cpp | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/accounts/zeroconf/Zeroconf.cpp b/src/accounts/zeroconf/Zeroconf.cpp index b1924f8bce..442e403c88 100644 --- a/src/accounts/zeroconf/Zeroconf.cpp +++ b/src/accounts/zeroconf/Zeroconf.cpp @@ -32,6 +32,7 @@ using namespace Tomahawk; using namespace Accounts; +#define MYNAME "zeroconf" ZeroconfPlugin::ZeroconfPlugin ( ZeroconfAccount* parent ) : SipPlugin( parent ) diff --git a/src/accounts/zeroconf/Zeroconf.h b/src/accounts/zeroconf/Zeroconf.h index 2de0e9b440..54b44702fb 100644 --- a/src/accounts/zeroconf/Zeroconf.h +++ b/src/accounts/zeroconf/Zeroconf.h @@ -28,8 +28,6 @@ #include -#define MYNAME "zeroconf" - namespace Tomahawk { namespace Accounts @@ -65,9 +63,9 @@ public slots: void advertise(); - virtual void sendSipInfos( const Tomahawk::peerinfo_ptr& receiver, const QList& info ) {} - void broadcastMsg( const QString & ) {} - void addContact( const QString &, const QString& ) {} + virtual void sendSipInfos( const Tomahawk::peerinfo_ptr& /* receiver */, const QList& /* info */ ) {} + void broadcastMsg( const QString& ) {} + void addContact( const QString&, const QString& ) {} private slots: void lanHostFound( const QString& host, int port, const QString& name, const QString& nodeid ); diff --git a/src/libtomahawk/jobview/InboxJobItem.cpp b/src/libtomahawk/jobview/InboxJobItem.cpp index ddd0c54809..e3b0421412 100644 --- a/src/libtomahawk/jobview/InboxJobItem.cpp +++ b/src/libtomahawk/jobview/InboxJobItem.cpp @@ -31,7 +31,7 @@ InboxJobItem::InboxJobItem( Side side, const QString& prettyName, const Tomahawk::trackdata_ptr& track, - QObject* parent ) + QObject* /* parent */ ) : JobStatusItem() , m_track( track ) , m_prettyName( prettyName ) diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 3110517804..120c4b715e 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -38,8 +38,8 @@ ControlConnection::ControlConnection( Servent* parent ) : Connection( parent ) , m_dbsyncconn( 0 ) , m_registered( false ) - , m_pingtimer( 0 ) , m_shutdownOnEmptyPeerInfos( true ) + , m_pingtimer( 0 ) { qDebug() << "CTOR controlconnection"; setId("ControlConnection()"); From da47c0081378b17bfb21d711dbee6788c6d34fb7 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 7 Jun 2013 12:17:58 +0200 Subject: [PATCH 244/565] * Made StatsGauge animation smooth and use QProgressBar's own inverted state handling. --- src/libtomahawk/widgets/StatsGauge.cpp | 43 +++++++++++++------ src/libtomahawk/widgets/StatsGauge.h | 7 ++- .../widgets/infowidgets/ArtistInfoWidget.cpp | 2 +- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index 3bdc409f73..a1d91d1a49 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -33,7 +33,8 @@ StatsGauge::StatsGauge( QWidget* parent ) : QProgressBar( parent ) - , m_inverted( false ) + , m_percentage( 0 ) + , m_targetValue( 0 ) { QProgressBar::setValue( 0 ); QProgressBar::setMaximum( 0 ); @@ -58,9 +59,8 @@ StatsGauge::paintEvent( QPaintEvent* event ) p.setPen( pen ); int fullCircle = 16 * 360; - float pct = m_inverted ? ( 1.0 - (float)value() / (float)maximum() ) : (float)value() / (float)maximum(); - - p.drawArc( QRect( 12, 12, gaugeSize.width() - 24, gaugeSize.height() - 24 ), 4*360, (int)( -1.0 * (float)fullCircle * pct ) ); + p.drawArc( QRect( 12, 12, gaugeSize.width() - 24, gaugeSize.height() - 24 ), + 4*360, (int)( -1.0 * (float)fullCircle * ( invertedAppearance() ? ( 1.0 - m_percentage ) : m_percentage ) ) ); pen = QPen( TomahawkStyle::NOW_PLAYING_ITEM.darker() ); pen.setWidth( 6 ); @@ -116,15 +116,30 @@ StatsGauge::setValue( int v ) { if ( maximum() == 0 || v == 0 ) return; + if ( v == m_targetValue ) + return; - QPropertyAnimation* a = new QPropertyAnimation( (QProgressBar*)this, "value" ); - a->setEasingCurve( QEasingCurve( QEasingCurve::OutQuad ) ); - a->setStartValue( value() > 0 ? value() : 1 ); - a->setEndValue( v ); - a->setDuration( 2000 ); - - connect( a, SIGNAL( finished() ), a, SLOT( deleteLater() ) ); - a->start(); + m_targetValue = v; + { + QPropertyAnimation* a = new QPropertyAnimation( (QProgressBar*)this, "value" ); + a->setEasingCurve( QEasingCurve( QEasingCurve::OutQuad ) ); + a->setStartValue( value() > 0 ? value() : 1 ); + a->setEndValue( v ); + a->setDuration( 2000 ); + + connect( a, SIGNAL( finished() ), a, SLOT( deleteLater() ) ); + a->start(); + } + { + QPropertyAnimation* a = new QPropertyAnimation( (QProgressBar*)this, "percentage" ); + a->setEasingCurve( QEasingCurve( QEasingCurve::OutQuad ) ); + a->setStartValue( (float)0 ); + a->setEndValue( (float)v / (float)maximum() ); + a->setDuration( 2000 ); + + connect( a, SIGNAL( finished() ), a, SLOT( deleteLater() ) ); + a->start(); + } } @@ -137,8 +152,8 @@ StatsGauge::setText( const QString& text ) void -StatsGauge::setInvertedGauge( bool inverted ) +StatsGauge::setPercentage( float percentage ) { - m_inverted = inverted; + m_percentage = percentage; repaint(); } diff --git a/src/libtomahawk/widgets/StatsGauge.h b/src/libtomahawk/widgets/StatsGauge.h index 9e27e68d83..828c1fc4d4 100644 --- a/src/libtomahawk/widgets/StatsGauge.h +++ b/src/libtomahawk/widgets/StatsGauge.h @@ -26,6 +26,7 @@ class DLLEXPORT StatsGauge : public QProgressBar { Q_OBJECT +Q_PROPERTY( float percentage READ percentage WRITE setPercentage ) public: /** this pixmap becomes the rest state pixmap and defines the size of the eventual widget */ @@ -34,11 +35,12 @@ Q_OBJECT virtual QSize sizeHint() const { return m_sizeHint; } QString text() const { return m_text; } - void setInvertedGauge( bool inverted ); + float percentage() const { return m_percentage; } public slots: void setValue( int value ); void setText( const QString& text ); + void setPercentage( float percentage ); protected: virtual void paintEvent( QPaintEvent* event ); @@ -46,7 +48,8 @@ public slots: private: QSize m_sizeHint; QString m_text; - bool m_inverted; + float m_percentage; + int m_targetValue; }; #endif //STATS_GAUGE_H diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 9376cfa51b..0c359d33af 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -108,7 +108,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); m_playStatsGauge->setText( tr( "CHART #" ) ); - m_playStatsGauge->setInvertedGauge( true ); + m_playStatsGauge->setInvertedAppearance( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); l->addWidget( m_playStatsGauge ); From b48b0127f845c739bd39bac1249ff2e293bbdc78 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 7 Jun 2013 12:36:26 +0200 Subject: [PATCH 245/565] Add Boost_INCLUDE_DIR and make less usage of boost in headers * Make Servent member more private --- src/infoplugins/generic/CMakeLists.txt | 5 +- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/FuncTimeout.h | 5 +- src/libtomahawk/Pipeline.cpp | 2 +- src/libtomahawk/Playlist.h | 6 +- src/libtomahawk/Typedefs.h | 3 - src/libtomahawk/accounts/AccountManager.cpp | 1 + src/libtomahawk/audio/AudioEngine_p.h | 4 +- src/libtomahawk/collection/Collection.cpp | 1 + src/libtomahawk/network/Connection.cpp | 1 + src/libtomahawk/network/ConnectionManager.cpp | 1 + src/libtomahawk/network/Servent.cpp | 201 +++++++++++------- src/libtomahawk/network/Servent.h | 71 ++----- src/libtomahawk/network/Servent_p.h | 90 ++++++++ src/libtomahawk/network/StreamConnection.cpp | 1 + src/tomahawk/CMakeLists.txt | 1 + 16 files changed, 256 insertions(+), 138 deletions(-) create mode 100644 src/libtomahawk/network/Servent_p.h diff --git a/src/infoplugins/generic/CMakeLists.txt b/src/infoplugins/generic/CMakeLists.txt index 442bdf8865..549c327566 100644 --- a/src/infoplugins/generic/CMakeLists.txt +++ b/src/infoplugins/generic/CMakeLists.txt @@ -1,4 +1,7 @@ -include_directories(${ECHONEST_INCLUDE_DIR}) +include_directories( + ${ECHONEST_INCLUDE_DIR} + ${Boost_INCLUDE_DIR} +) list(APPEND simple_plugins Echonest diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index d12b525cde..058a7f9262 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -395,6 +395,7 @@ include_directories( ${ECHONEST_INCLUDE_DIR} ${CLUCENE_INCLUDE_DIRS} ${PHONON_INCLUDES} + ${Boost_INCLUDE_DIR} ${LIBPORTFWD_INCLUDE_DIR} ${QuaZip_INCLUDE_DIR} diff --git a/src/libtomahawk/FuncTimeout.h b/src/libtomahawk/FuncTimeout.h index 93b5e025a0..004e19d5f8 100644 --- a/src/libtomahawk/FuncTimeout.h +++ b/src/libtomahawk/FuncTimeout.h @@ -22,12 +22,13 @@ #include #include -#include "boost/function.hpp" -#include "boost/bind.hpp" +#include #include "DllMacro.h" /* +#include + I want to do: QTimer::singleShot(1000, this, SLOT(doSomething(x))); instead, I'm doing: diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index f747639dac..8ba82c2f34 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -30,7 +30,7 @@ #include "utils/ResultUrlChecker.h" #include "utils/Logger.h" -#include "boost/bind.hpp" +#include #define DEFAULT_CONCURRENT_QUERIES 4 #define MAX_CONCURRENT_QUERIES 16 diff --git a/src/libtomahawk/Playlist.h b/src/libtomahawk/Playlist.h index 8adcdb5492..3fe6fcede3 100644 --- a/src/libtomahawk/Playlist.h +++ b/src/libtomahawk/Playlist.h @@ -33,7 +33,6 @@ #include "PlaylistInterface.h" #include "playlist/PlaylistUpdaterInterface.h" #include "Query.h" -#include "utils/Closure.h" #include "DllMacro.h" @@ -44,6 +43,11 @@ class DatabaseCommand_SetPlaylistRevision; class DatabaseCommand_CreatePlaylist; class PlaylistModel; +namespace _detail +{ + class Closure; +} + namespace Tomahawk { diff --git a/src/libtomahawk/Typedefs.h b/src/libtomahawk/Typedefs.h index 639a245557..0150a31f72 100644 --- a/src/libtomahawk/Typedefs.h +++ b/src/libtomahawk/Typedefs.h @@ -26,8 +26,6 @@ #include #include -#include - //template class QSharedPointer; #include @@ -100,7 +98,6 @@ namespace Tomahawk }; class ExternalResolver; - typedef boost::function ResolverFactoryFunc; namespace PlaylistModes { enum RepeatMode { NoRepeat, RepeatOne, RepeatAll }; diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index b72d1f4135..fcd0ad6cac 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -29,6 +29,7 @@ #include "sip/SipStatusMessage.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" +#include "utils/Closure.h" #include #include diff --git a/src/libtomahawk/audio/AudioEngine_p.h b/src/libtomahawk/audio/AudioEngine_p.h index f0e2be2454..5102e86c4a 100644 --- a/src/libtomahawk/audio/AudioEngine_p.h +++ b/src/libtomahawk/audio/AudioEngine_p.h @@ -3,6 +3,8 @@ #include #include +#include + #include #include #include @@ -46,7 +48,7 @@ public slots: QQueue< AudioState > stateQueue; QTimer stateQueueTimer; - uint_fast8_t underrunCount; + quint8 underrunCount; bool underrunNotified; QTemporaryFile* coverTempFile; diff --git a/src/libtomahawk/collection/Collection.cpp b/src/libtomahawk/collection/Collection.cpp index 520970f3cc..b057189e8e 100644 --- a/src/libtomahawk/collection/Collection.cpp +++ b/src/libtomahawk/collection/Collection.cpp @@ -25,6 +25,7 @@ #include "playlist/PlaylistUpdaterInterface.h" #include "utils/ImageRegistry.h" #include "accounts/AccountManager.h" +#include "utils/Closure.h" #include #include diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 8b12446081..47986cb93a 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -20,6 +20,7 @@ #include "Connection.h" +#include "QTcpSocketExtra.h" #include "network/Servent.h" #include "utils/Logger.h" #include "Source.h" diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 14a329d022..3f6ae9dc32 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -18,6 +18,7 @@ #include "ConnectionManager.h" #include "ControlConnection.h" +#include "QTcpSocketExtra.h" #include "Servent.h" #include "database/Database.h" diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 870d2c980e..f2e835ac60 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -19,13 +19,14 @@ * along with Tomahawk. If not, see . */ -#include "Servent.h" +#include "Servent_p.h" #include "Result.h" #include "Source.h" #include "BufferIoDevice.h" #include "Connection.h" #include "ControlConnection.h" +#include "QTcpSocketExtra.h" #include "database/Database.h" #include "database/DatabaseImpl.h" #include "network/ConnectionManager.h" @@ -74,14 +75,12 @@ Servent::instance() Servent::Servent( QObject* parent ) - : QTcpServer( parent ) - , m_port( 0 ) - , m_externalPort( 0 ) - , m_ready( false ) + : QTcpServer( parent ), + d_ptr( new ServentPrivate( this ) ) { s_instance = this; - m_noAuth = qApp->arguments().contains( "--noauth" ); + d_func()->noAuth = qApp->arguments().contains( "--noauth" ); setProxy( QNetworkProxy::NoProxy ); @@ -108,14 +107,14 @@ Servent::~Servent() { tDebug() << Q_FUNC_INFO; - foreach ( ControlConnection* cc, m_controlconnections ) + foreach ( ControlConnection* cc, d_func()->controlconnections ) delete cc; - if ( m_portfwd ) + if ( d_func()->portfwd ) { - m_portfwd.data()->quit(); - m_portfwd.data()->wait( 60000 ); - delete m_portfwd.data(); + d_func()->portfwd.data()->quit(); + d_func()->portfwd.data()->wait( 60000 ); + delete d_func()->portfwd.data(); } } @@ -123,25 +122,25 @@ Servent::~Servent() bool Servent::startListening( QHostAddress ha, bool upnp, int port ) { - m_externalAddresses = QList(); - m_port = port; + d_func()->externalAddresses = QList(); + d_func()->port = port; int defPort = TomahawkSettings::instance()->defaultPort(); // Listen on both the selected port and, if not the same, the default port -- the latter sometimes necessary for zeroconf // TODO: only listen on both when zeroconf sip is enabled // TODO: use a real zeroconf system instead of a simple UDP broadcast? - if ( !listen( ha, m_port ) ) + if ( !listen( ha, d_func()->port ) ) { - if ( m_port != defPort ) + if ( d_func()->port != defPort ) { if ( !listen( ha, defPort ) ) { - tLog() << Q_FUNC_INFO << "Failed to listen on both port" << m_port << "and port" << defPort; + tLog() << Q_FUNC_INFO << "Failed to listen on both port" << d_func()->port << "and port" << defPort; tLog() << Q_FUNC_INFO << "Error string is:" << errorString(); return false; } else - m_port = defPort; + d_func()->port = defPort; } } @@ -159,34 +158,34 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) if ( addr.isInSubnet( QHostAddress::parseSubnet( "fe80::/10" ) ) ) continue; // Skip link local addresses tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Listening to " << addr.toString(); - m_externalAddresses.append( addr ); + d_func()->externalAddresses.append( addr ); } } else if ( ( ha.toString() != "127.0.0.1" ) && ( ha.toString() != "::1" ) && ( ha.toString() != "::7F00:1" ) ) { // We listen only to one specific Address, only announce this. - m_externalAddresses.append( ha ); + d_func()->externalAddresses.append( ha ); } // If we only accept connections via localhost, we'll announce nothing. TomahawkSettings::ExternalAddressMode mode = TomahawkSettings::instance()->externalAddressMode(); - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Servent listening on port" << m_port << "- servent thread:" << thread() + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Servent listening on port" << d_func()->port << "- servent thread:" << thread() << "- address mode:" << (int)( mode ); switch ( mode ) { case TomahawkSettings::Static: - m_externalHostname = TomahawkSettings::instance()->externalHostname(); - m_externalPort = TomahawkSettings::instance()->externalPort(); - m_ready = true; + d_func()->externalHostname = TomahawkSettings::instance()->externalHostname(); + d_func()->externalPort = TomahawkSettings::instance()->externalPort(); + d_func()->ready = true; // All setup is made, were done. emit ready(); break; case TomahawkSettings::Lan: // Nothing has to be done here. - m_ready = true; + d_func()->ready = true; emit ready(); break; @@ -195,15 +194,15 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) { // upnp could be turned of on the cli with --noupnp tLog( LOGVERBOSE ) << Q_FUNC_INFO << "External address mode set to upnp..."; - m_portfwd = QPointer< PortFwdThread >( new PortFwdThread( m_port ) ); - Q_ASSERT( m_portfwd ); - connect( m_portfwd.data(), SIGNAL( externalAddressDetected( QHostAddress, unsigned int ) ), + d_func()->portfwd = QPointer< PortFwdThread >( new PortFwdThread( d_func()->port ) ); + Q_ASSERT( d_func()->portfwd ); + connect( d_func()->portfwd.data(), SIGNAL( externalAddressDetected( QHostAddress, unsigned int ) ), SLOT( setExternalAddress( QHostAddress, unsigned int ) ) ); - m_portfwd.data()->start(); + d_func()->portfwd.data()->start(); } else { - m_ready = true; + d_func()->ready = true; emit ready(); } break; @@ -218,16 +217,16 @@ Servent::setExternalAddress( QHostAddress ha, unsigned int port ) { if ( isValidExternalIP( ha ) ) { - m_externalHostname = ha.toString(); - m_externalPort = port; + d_func()->externalHostname = ha.toString(); + d_func()->externalPort = port; } - if ( m_externalPort == 0 || !isValidExternalIP( ha ) ) + if ( d_func()->externalPort == 0 || !isValidExternalIP( ha ) ) tLog() << Q_FUNC_INFO << "UPnP failed, no further external address could be acquired!"; else tLog( LOGVERBOSE ) << Q_FUNC_INFO << "UPnP setup successful"; - m_ready = true; + d_func()->ready = true; emit ready(); } @@ -309,13 +308,13 @@ Servent::isValidExternalIP( const QHostAddress& addr ) void Servent::registerOffer( const QString& key, Connection* conn ) { - m_offers[key] = QPointer(conn); + d_func()->offers[key] = QPointer(conn); } void Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, const QString &nodeid, const int timeout ) { - m_lazyoffers[key] = QPair< peerinfo_ptr, QString >( peerInfo, nodeid ); + d_func()->lazyoffers[key] = QPair< peerinfo_ptr, QString >( peerInfo, nodeid ); QTimer* timer = new QTimer( this ); timer->setInterval( timeout ); timer->setSingleShot( true ); @@ -326,7 +325,7 @@ Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, con void Servent::deleteLazyOffer( const QString& key ) { - m_lazyoffers.remove( key ); + d_func()->lazyoffers.remove( key ); // Cleanup. QTimer* timer = (QTimer*)sender(); @@ -341,8 +340,8 @@ Servent::registerControlConnection( ControlConnection* conn ) { Q_ASSERT( conn ); tLog( LOGVERBOSE ) << Q_FUNC_INFO << conn->name(); - m_controlconnections << conn; - m_connectedNodes << conn->id(); + d_func()->controlconnections << conn; + d_func()->connectedNodes << conn->id(); } @@ -352,15 +351,15 @@ Servent::unregisterControlConnection( ControlConnection* conn ) Q_ASSERT( conn ); tLog( LOGVERBOSE ) << Q_FUNC_INFO << conn->name(); - m_connectedNodes.removeAll( conn->id() ); - m_controlconnections.removeAll( conn ); + d_func()->connectedNodes.removeAll( conn->id() ); + d_func()->controlconnections.removeAll( conn ); } ControlConnection* Servent::lookupControlConnection( const SipInfo& sipInfo ) { - foreach ( ControlConnection* c, m_controlconnections ) + foreach ( ControlConnection* c, d_func()->controlconnections ) { tLog() << sipInfo.port() << c->peerPort() << sipInfo.host() << c->peerIpAddress().toString(); if ( sipInfo.port() == c->peerPort() && sipInfo.host() == c->peerIpAddress().toString() ) @@ -373,7 +372,7 @@ Servent::lookupControlConnection( const SipInfo& sipInfo ) ControlConnection* Servent::lookupControlConnection( const QString& nodeid ) { - foreach ( ControlConnection* c, m_controlconnections ) + foreach ( ControlConnection* c, d_func()->controlconnections ) { if ( c->id() == nodeid ) return c; @@ -386,21 +385,21 @@ QList Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) { QList sipInfos = QList(); - foreach ( QHostAddress ha, m_externalAddresses ) + foreach ( QHostAddress ha, d_func()->externalAddresses ) { SipInfo info = SipInfo(); info.setHost( ha.toString() ); - info.setPort( m_port ); + info.setPort( d_func()->port ); info.setKey( key ); info.setVisible( true ); info.setNodeId( nodeid ); sipInfos.append( info ); } - if ( m_externalHostname.length() > 0) + if ( d_func()->externalHostname.length() > 0) { SipInfo info = SipInfo(); - info.setHost( m_externalHostname ); - info.setPort( m_externalPort ); + info.setHost( d_func()->externalHostname ); + info.setPort( d_func()->externalPort ); info.setKey( key ); info.setVisible( true ); info.setNodeId( nodeid ); @@ -585,7 +584,7 @@ Servent::readyRead() ControlConnection* cc = 0; bool ok; QString key, conntype, nodeid, controlid; - QVariantMap m = parser.parse( sock.data()->_msg->payload(), &ok ).toMap(); + QVariantMap m = d_func()->parser.parse( sock.data()->_msg->payload(), &ok ).toMap(); if ( !ok ) { tDebug() << "Invalid JSON on new connection, aborting"; @@ -601,13 +600,13 @@ Servent::readyRead() if ( !nodeid.isEmpty() ) // only control connections send nodeid { bool dupe = false; - if ( m_connectedNodes.contains( nodeid ) ) + if ( d_func()->connectedNodes.contains( nodeid ) ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Connected nodes contains it."; dupe = true; } - foreach ( ControlConnection* con, m_controlconnections ) + foreach ( ControlConnection* con, d_func()->controlconnections ) { Q_ASSERT( con ); @@ -620,7 +619,7 @@ Servent::readyRead() } // for zeroconf there might be no offer, that case is handled later - ControlConnection* ccMatch = qobject_cast< ControlConnection* >( m_offers.value( key ).data() ); + ControlConnection* ccMatch = qobject_cast< ControlConnection* >( d_func()->offers.value( key ).data() ); if ( dupe && ccMatch ) { tLog() << "Duplicate control connection detected, dropping:" << nodeid << conntype; @@ -631,7 +630,7 @@ Servent::readyRead() peerInfoDebug( currentPeerInfo ); } - foreach ( ControlConnection* keepConnection, m_controlconnections ) + foreach ( ControlConnection* keepConnection, d_func()->controlconnections ) { Q_ASSERT( keepConnection ); @@ -654,7 +653,7 @@ Servent::readyRead() } } - foreach ( ControlConnection* con, m_controlconnections ) + foreach ( ControlConnection* con, d_func()->controlconnections ) { Q_ASSERT( con ); @@ -810,7 +809,7 @@ Servent::initiateConnection( const SipInfo& sipInfo, Connection* conn ) Q_ASSERT( conn ); // Check that we are not connecting to ourselves - foreach( QHostAddress ha, m_externalAddresses ) + foreach( QHostAddress ha, d_func()->externalAddresses ) { if ( sipInfo.host() == ha.toString() ) { @@ -818,7 +817,7 @@ Servent::initiateConnection( const SipInfo& sipInfo, Connection* conn ) return; } } - if ( sipInfo.host() == m_externalHostname ) + if ( sipInfo.host() == d_func()->externalHostname ) { tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << sipInfo.host() << ":" << sipInfo.port() << ": same IP as ourselves."; return; @@ -901,6 +900,36 @@ Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& their createParallelConnection( orig_conn, new_conn, theirkey ); } +bool +Servent::visibleExternally() const +{ + return (!d_func()->externalHostname.isNull()) || (d_func()->externalAddresses.length() > 0); +} + +int +Servent::port() const +{ + return d_func()->port; +} + +QList +Servent::addresses() const +{ + return d_func()->externalAddresses; +} + +QString +Servent::additionalAddress() const +{ + return d_func()->externalHostname; +} + +int +Servent::additionalPort() const +{ + return d_func()->externalPort; +} + // return the appropriate connection for a given offer key, or NULL if invalid Connection* Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer ) @@ -909,10 +938,10 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString if ( key.startsWith( "FILE_REQUEST_KEY:" ) ) { // check if the source IP matches an existing, authenticated connection - if ( !m_noAuth && peer != QHostAddress::Any && !isIPWhitelisted( peer ) ) + if ( !d_func()->noAuth && peer != QHostAddress::Any && !isIPWhitelisted( peer ) ) { bool authed = false; - foreach ( ControlConnection* cc, m_controlconnections ) + foreach ( ControlConnection* cc, d_func()->controlconnections ) { tDebug() << Q_FUNC_INFO << "Probing:" << cc->name(); if ( cc->socket() && cc->socket()->peerAddress() == peer ) @@ -962,22 +991,22 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString } } - if ( m_lazyoffers.contains( key ) ) + if ( d_func()->lazyoffers.contains( key ) ) { ControlConnection* conn = new ControlConnection( this ); - conn->setName( m_lazyoffers.value( key ).first->contactId() ); - conn->addPeerInfo( m_lazyoffers.value( key ).first ); - conn->setId( m_lazyoffers.value( key ).second ); + conn->setName( d_func()->lazyoffers.value( key ).first->contactId() ); + conn->addPeerInfo( d_func()->lazyoffers.value( key ).first ); + conn->setId( d_func()->lazyoffers.value( key ).second ); // Register as non-lazy offer - m_lazyoffers.remove( key ); + d_func()->lazyoffers.remove( key ); registerOffer( key, conn ); return conn; } - else if ( m_offers.contains( key ) ) + else if ( d_func()->offers.contains( key ) ) { - QPointer conn = m_offers.value( key ); + QPointer conn = d_func()->offers.value( key ); if ( conn.isNull() ) { // This can happen if it's a streamconnection, but the audioengine has @@ -997,7 +1026,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString if ( conn.data()->onceOnly() ) { - m_offers.remove( key ); + d_func()->offers.remove( key ); return conn.data(); } else @@ -1005,7 +1034,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString return conn.data()->clone(); } } - else if ( m_noAuth ) + else if ( d_func()->noAuth ) { Connection* conn; conn = new ControlConnection( this ); @@ -1049,11 +1078,11 @@ Servent::remoteIODeviceFactory( const Tomahawk::result_ptr& result, void Servent::registerStreamConnection( StreamConnection* sc ) { - Q_ASSERT( !m_scsessions.contains( sc ) ); - tDebug( LOGVERBOSE ) << "Registering Stream" << m_scsessions.length() + 1; + Q_ASSERT( !d_func()->scsessions.contains( sc ) ); + tDebug( LOGVERBOSE ) << "Registering Stream" << d_func()->scsessions.length() + 1; - QMutexLocker lock( &m_ftsession_mut ); - m_scsessions.append( sc ); + QMutexLocker lock( &d_func()->ftsession_mut ); + d_func()->scsessions.append( sc ); printCurrentTransfers(); emit streamStarted( sc ); @@ -1066,8 +1095,8 @@ Servent::onStreamFinished( StreamConnection* sc ) Q_ASSERT( sc ); tDebug( LOGVERBOSE ) << "Stream Finished, unregistering" << sc->id(); - QMutexLocker lock( &m_ftsession_mut ); - m_scsessions.removeAll( sc ); + QMutexLocker lock( &d_func()->ftsession_mut ); + d_func()->scsessions.removeAll( sc ); printCurrentTransfers(); emit streamFinished( sc ); @@ -1080,7 +1109,7 @@ Servent::printCurrentTransfers() { int k = 1; // qDebug() << "~~~ Active file transfer connections:" << m_scsessions.length(); - foreach ( StreamConnection* i, m_scsessions ) + foreach ( StreamConnection* i, d_func()->scsessions ) { qDebug() << k << ") " << i->id(); } @@ -1118,7 +1147,7 @@ Servent::isIPWhitelisted( QHostAddress ip ) bool Servent::connectedToSession( const QString& session ) { - foreach ( ControlConnection* cc, m_controlconnections ) + foreach ( ControlConnection* cc, d_func()->controlconnections ) { Q_ASSERT( cc ); @@ -1129,6 +1158,18 @@ Servent::connectedToSession( const QString& session ) return false; } +unsigned int +Servent::numConnectedPeers() const +{ + return d_func()->controlconnections.length(); +} + +QList +Servent::streams() const +{ + return d_func()->scsessions; +} + void Servent::triggerDBSync() @@ -1152,7 +1193,7 @@ void Servent::registerIODeviceFactory( const QString &proto, IODeviceFactoryFunc fac ) { - m_iofactories.insert( proto, fac ); + d_func()->iofactories.insert( proto, fac ); } @@ -1170,14 +1211,14 @@ Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result, } const QString proto = rx.cap( 1 ); - if ( !m_iofactories.contains( proto ) ) + if ( !d_func()->iofactories.contains( proto ) ) { callback( sp ); return; } //JSResolverHelper::customIODeviceFactory is async! - m_iofactories.value( proto )( result, callback ); + d_func()->iofactories.value( proto )( result, callback ); } @@ -1207,3 +1248,9 @@ Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result, QSharedPointer< QIODevice > sp = QSharedPointer< QIODevice >( reply, &QObject::deleteLater ); callback( sp ); } + +bool +Servent::isReady() const +{ + return d_func()->ready; +} diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index d18300dd55..5a455cc482 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -32,18 +32,10 @@ #include #include #include -#include #include -#include -#include -#include - #include "Typedefs.h" #include "Msg.h" -#include "network/QTcpSocketExtra.h" - -#include #include "DllMacro.h" @@ -52,14 +44,22 @@ class Connector; class ControlConnection; class StreamConnection; class ProxyConnection; +class QTcpSocketExtra; class RemoteCollectionConnection; class PortFwdThread; class PeerInfo; class SipInfo; +namespace boost +{ + template class function; +} // boost + typedef boost::function< void( const Tomahawk::result_ptr&, boost::function< void( QSharedPointer< QIODevice >& ) > )> IODeviceFactoryFunc; +class ServentPrivate; + class DLLEXPORT Servent : public QTcpServer { Q_OBJECT @@ -96,34 +96,34 @@ public slots: void initiateConnection( const SipInfo& sipInfo, Connection* conn ); void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); - bool visibleExternally() const { return (!m_externalHostname.isNull()) || (m_externalAddresses.length() > 0); } + bool visibleExternally() const; /** * The port this Peer listens directly (per default) */ - int port() const { return m_port; } + int port() const; /** * The IP addresses this Peer listens directly (per default) */ - QList< QHostAddress > addresses() const { return m_externalAddresses; } + QList< QHostAddress > addresses() const; /** * An additional address this peer listens to, e.g. via UPnP. */ - QString additionalAddress() const { return m_externalHostname; } + QString additionalAddress() const; /** * An additional port this peer listens to, e.g. via UPnP (only in combination with additionalAddress. */ - int additionalPort() const { return m_externalPort; } + int additionalPort() const; static bool isIPWhitelisted( QHostAddress ip ); bool connectedToSession( const QString& session ); - unsigned int numConnectedPeers() const { return m_controlconnections.length(); } + unsigned int numConnectedPeers() const; - QList< StreamConnection* > streams() const { return m_scsessions; } + QList< StreamConnection* > streams() const; void getIODeviceForUrl( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); void registerIODeviceFactory( const QString &proto, IODeviceFactoryFunc fac ); @@ -131,7 +131,7 @@ public slots: void localFileIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); void httpIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); - bool isReady() const { return m_ready; } + bool isReady() const; QList getLocalSipInfos(const QString& nodeid, const QString &key); signals: @@ -162,46 +162,13 @@ private slots: Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); private: + Q_DECLARE_PRIVATE( Servent ) + ServentPrivate* d_ptr; + void handoverSocket( Connection* conn, QTcpSocketExtra* sock ); void cleanupSocket( QTcpSocketExtra* sock ); void printCurrentTransfers(); - QJson::Parser parser; - QList< ControlConnection* > m_controlconnections; // canonical list of authed peers - QMap< QString, QPointer< Connection > > m_offers; - QMap< QString, QPair< Tomahawk::peerinfo_ptr, QString > > m_lazyoffers; - QStringList m_connectedNodes; - - /** - * The external port used by all address except those obtained via UPnP or the static configuration option - */ - int m_port; - - /** - * Either the static set or the UPnP set external port - */ - int m_externalPort; - - /** - * All available external IPs - */ - QList m_externalAddresses; - - /** - * Either the static set or the UPnP set external host - */ - QString m_externalHostname; - - bool m_ready; - bool m_noAuth; - - // currently active file transfers: - QList< StreamConnection* > m_scsessions; - QMutex m_ftsession_mut; - - QMap< QString, IODeviceFactoryFunc > m_iofactories; - - QPointer< PortFwdThread > m_portfwd; static Servent* s_instance; }; diff --git a/src/libtomahawk/network/Servent_p.h b/src/libtomahawk/network/Servent_p.h new file mode 100644 index 0000000000..f0f06bd7f2 --- /dev/null +++ b/src/libtomahawk/network/Servent_p.h @@ -0,0 +1,90 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef SERVENT_P_H +#define SERVENT_P_H + +#include "Servent.h" + +#include +#include +#include + +#include + +class ServentPrivate : public QObject +{ +Q_OBJECT + +public: + ServentPrivate( Servent* q ) + : q_ptr ( q ) + , port( 0 ) + , externalPort( 0 ) + , ready( false ) + { + } + Servent* q_ptr; + Q_DECLARE_PUBLIC ( Servent ) + +private: + QMap< QString, IODeviceFactoryFunc > iofactories; + QMap< QString, QPointer< Connection > > offers; + QMap< QString, QPair< Tomahawk::peerinfo_ptr, QString > > lazyoffers; + QStringList connectedNodes; + QJson::Parser parser; + + /** + * canonical list of authed peers + */ + QList< ControlConnection* > controlconnections; + + /** + * The external port used by all address except those obtained via UPnP or the static configuration option + */ + int port; + + /** + * Either the static set or the UPnP set external port + */ + int externalPort; + + /** + * All available external IPs + */ + QList externalAddresses; + + /** + * Either the static set or the UPnP set external host + */ + QString externalHostname; + + bool ready; + bool noAuth; + + // currently active file transfers: + QList< StreamConnection* > scsessions; + QMutex ftsession_mut; + + QPointer< PortFwdThread > portfwd; +}; + +#endif // SERVENT_P_H diff --git a/src/libtomahawk/network/StreamConnection.cpp b/src/libtomahawk/network/StreamConnection.cpp index 52d4c1f558..1a02627a3b 100644 --- a/src/libtomahawk/network/StreamConnection.cpp +++ b/src/libtomahawk/network/StreamConnection.cpp @@ -30,6 +30,7 @@ #include "utils/Logger.h" #include +#include #include diff --git a/src/tomahawk/CMakeLists.txt b/src/tomahawk/CMakeLists.txt index 5501057499..4c0d83edb4 100644 --- a/src/tomahawk/CMakeLists.txt +++ b/src/tomahawk/CMakeLists.txt @@ -109,6 +109,7 @@ INCLUDE_DIRECTORIES( ${LIBATTICA_INCLUDE_DIR} ${ECHONEST_INCLUDE_DIR} ${LIBLASTFM_INCLUDE_DIRS} + ${Boost_INCLUDE_DIR} ) IF( QXTWEB_FOUND ) From 81a9da23da7155bc8cd3b8d92b8c6f421c49bc82 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 7 Jun 2013 15:49:15 +0200 Subject: [PATCH 246/565] Delete d_ptr in Servent desctructor --- src/libtomahawk/network/Servent.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index f2e835ac60..d4b106ce60 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -116,6 +116,8 @@ Servent::~Servent() d_func()->portfwd.data()->wait( 60000 ); delete d_func()->portfwd.data(); } + + delete d_ptr; } From fe7c1b1d733cdf8147c90ff83005f6f1e6062939 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 7 Jun 2013 16:35:29 +0200 Subject: [PATCH 247/565] Introduce the Dpointer concept to ConnectionManager --- src/libtomahawk/network/ConnectionManager.cpp | 98 ++++++++++--------- src/libtomahawk/network/ConnectionManager.h | 13 +-- src/libtomahawk/network/ConnectionManager_p.h | 48 +++++++++ 3 files changed, 105 insertions(+), 54 deletions(-) create mode 100644 src/libtomahawk/network/ConnectionManager_p.h diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 3f6ae9dc32..ebfeaeeec1 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -16,7 +16,8 @@ * along with Tomahawk. If not, see . */ -#include "ConnectionManager.h" +#include "ConnectionManager_p.h" + #include "ControlConnection.h" #include "QTcpSocketExtra.h" #include "Servent.h" @@ -30,11 +31,16 @@ #include ConnectionManager::ConnectionManager( const QString &nodeid ) - : m_nodeid( nodeid ) + : d_ptr( new ConnectionManagerPrivate( this, nodeid ) ) { // TODO sth? } +ConnectionManager::~ConnectionManager() +{ + delete d_ptr; +} + void ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo ) { @@ -45,7 +51,7 @@ ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo ) void ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo ) { - m_mutex.lock(); + d_func()->mutex.lock(); // Respect different behaviour before 0.7.100 peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Trying to connect to client with version " << peerInfo->versionString().split(' ').last() << TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ); if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.100" ) < 0) @@ -63,7 +69,7 @@ ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo // We connected to the peer, so we are done here. } } - m_mutex.unlock(); + d_func()->mutex.unlock(); return; } foreach ( SipInfo info, peerInfo->sipInfos() ) @@ -78,7 +84,7 @@ ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo return; } } - m_mutex.unlock(); + d_func()->mutex.unlock(); } void @@ -87,78 +93,78 @@ ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool l // Lock, so that we will not attempt to do two parallell connects. if (lock) { - m_mutex.lock(); + d_func()->mutex.lock(); } // Check that we are not already connected to this peer ControlConnection* cconn = Servent::instance()->lookupControlConnection( peerInfo->nodeId() ); - if ( cconn != NULL || !m_controlConnection.isNull() ) + if ( cconn != NULL || !d_func()->controlConnection.isNull() ) { // We are already connected to this peer, so just add some more details. peerInfoDebug( peerInfo ) << "Existing connection found, not connecting."; cconn->addPeerInfo( peerInfo ); if ( cconn != NULL ) { - m_controlConnection = QPointer(cconn); + d_func()->controlConnection = QPointer(cconn); } - m_mutex.unlock(); + d_func()->mutex.unlock(); return; // TODO: Keep the peerInfo in mind for reconnecting // FIXME: Do we need this for reconnecting if the connection drops? } // If we are not connected, try to connect - m_currentPeerInfo = peerInfo; + d_func()->currentPeerInfo = peerInfo; peerInfoDebug( peerInfo ) << "No existing connection found, trying to connect."; - m_sipCandidates.append( peerInfo->sipInfos() ); + d_func()->sipCandidates.append( peerInfo->sipInfos() ); QVariantMap m; m["conntype"] = "accept-offer"; m["key"] = peerInfo->key(); m["nodeid"] = Database::instance()->impl()->dbid(); - m_controlConnection = QPointer( new ControlConnection( Servent::instance() ) ); - m_controlConnection->setShutdownOnEmptyPeerInfos( false ); - m_controlConnection->addPeerInfo( peerInfo ); - m_controlConnection->setFirstMessage( m ); + d_func()->controlConnection = QPointer( new ControlConnection( Servent::instance() ) ); + d_func()->controlConnection->setShutdownOnEmptyPeerInfos( false ); + d_func()->controlConnection->addPeerInfo( peerInfo ); + d_func()->controlConnection->setFirstMessage( m ); if ( peerInfo->id().length() ) - m_controlConnection->setName( peerInfo->contactId() ); + d_func()->controlConnection->setName( peerInfo->contactId() ); if ( peerInfo->nodeId().length() ) - m_controlConnection->setId( peerInfo->nodeId() ); + d_func()->controlConnection->setId( peerInfo->nodeId() ); - m_controlConnection->setProperty( "nodeid", peerInfo->nodeId() ); + d_func()->controlConnection->setProperty( "nodeid", peerInfo->nodeId() ); - Servent::instance()->registerControlConnection( m_controlConnection.data() ); + Servent::instance()->registerControlConnection( d_func()->controlConnection.data() ); tryConnect(); } void ConnectionManager::tryConnect() { // ATTENTION: mutex should be already locked by the calling function. - Q_ASSERT( !m_controlConnection.isNull() ); + Q_ASSERT( !d_func()->controlConnection.isNull() ); - if ( m_sipCandidates.isEmpty() ) + if ( d_func()->sipCandidates.isEmpty() ) { // No more possibilities to connect. - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << m_controlConnection->name() << " skipping."; + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << d_func()->controlConnection->name() << " skipping."; // Clean up. - m_currentPeerInfo.clear(); - delete m_controlConnection.data(); - m_mutex.unlock(); + d_func()->currentPeerInfo.clear(); + delete d_func()->controlConnection.data(); + d_func()->mutex.unlock(); return; } // Use first available SIP endpoint and remove it from the list - SipInfo info = m_sipCandidates.takeFirst(); + SipInfo info = d_func()->sipCandidates.takeFirst(); if ( !info.isVisible() ) { - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one"; + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one"; tryConnect(); return; } - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << info.host() << ":" << info.port(); + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << info.host() << ":" << info.port(); Q_ASSERT( info.port() > 0 ); // Check that we are not connecting to ourselves @@ -166,31 +172,31 @@ void ConnectionManager::tryConnect() { if ( info.host() == ha.toString() && info.port() == Servent::instance()->port() ) { - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; tryConnect(); return; } } if ( info.host() == Servent::instance()->additionalAddress() && info.port() == Servent::instance()->additionalPort() ) { - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; tryConnect(); return; } // We should have already setup a first message in connectToPeer - Q_ASSERT( !m_controlConnection->firstMessage().isNull() ); + Q_ASSERT( !d_func()->controlConnection->firstMessage().isNull() ); QTcpSocketExtra* sock = new QTcpSocketExtra(); sock->setConnectTimeout( CONNECT_TIMEOUT ); sock->_disowned = false; - sock->_conn = m_controlConnection.data(); + sock->_conn = d_func()->controlConnection.data(); sock->_outbound = true; connect( sock, SIGNAL( connected() ), this, SLOT( socketConnected() ) ); connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting socket to " << info.host() << ":" << info.port(); + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connecting socket to " << info.host() << ":" << info.port(); sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite ); sock->moveToThread( thread() ); } @@ -199,10 +205,10 @@ void ConnectionManager::socketError( QAbstractSocket::SocketError error ) { Q_UNUSED( error ); - Q_ASSERT( !m_controlConnection.isNull() ); + Q_ASSERT( !d_func()->controlConnection.isNull() ); QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << sock->peerAddress().toString() << " failed: " << sock->errorString(); + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << sock->peerAddress().toString() << " failed: " << sock->errorString(); sock->deleteLater(); // Try to connect with the next available SipInfo. @@ -215,35 +221,35 @@ ConnectionManager::socketConnected() { QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); - peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connected to hostaddr: " << sock->peerAddress() << ", hostname:" << sock->peerName(); + peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connected to hostaddr: " << sock->peerAddress() << ", hostname:" << sock->peerName(); Q_ASSERT( !sock->_conn.isNull() ); handoverSocket( sock ); - m_currentPeerInfo.clear(); - m_mutex.unlock(); + d_func()->currentPeerInfo.clear(); + d_func()->mutex.unlock(); } void ConnectionManager::handoverSocket( QTcpSocketExtra* sock ) { - Q_ASSERT( !m_controlConnection.isNull() ); + Q_ASSERT( !d_func()->controlConnection.isNull() ); Q_ASSERT( sock ); - Q_ASSERT( m_controlConnection->socket().isNull() ); + Q_ASSERT( d_func()->controlConnection->socket().isNull() ); Q_ASSERT( sock->isValid() ); disconnect( sock, SIGNAL( disconnected() ), sock, SLOT( deleteLater() ) ); disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); sock->_disowned = true; - m_controlConnection->setOutbound( sock->_outbound ); - m_controlConnection->setPeerPort( sock->peerPort() ); + d_func()->controlConnection->setOutbound( sock->_outbound ); + d_func()->controlConnection->setPeerPort( sock->peerPort() ); - m_controlConnection->start( sock ); + d_func()->controlConnection->start( sock ); // ControlConntection is now connected, now it can be destroyed if the PeerInfos disappear - m_controlConnection->setShutdownOnEmptyPeerInfos( true ); - m_currentPeerInfo.clear(); - m_mutex.unlock(); + d_func()->controlConnection->setShutdownOnEmptyPeerInfos( true ); + d_func()->currentPeerInfo.clear(); + d_func()->mutex.unlock(); } diff --git a/src/libtomahawk/network/ConnectionManager.h b/src/libtomahawk/network/ConnectionManager.h index 4f405ebbf7..0335cf3b4f 100644 --- a/src/libtomahawk/network/ConnectionManager.h +++ b/src/libtomahawk/network/ConnectionManager.h @@ -25,8 +25,8 @@ #include #include -#include +class ConnectionManagerPrivate; class QTcpSocketExtra; class DLLEXPORT ConnectionManager : public QObject @@ -36,6 +36,7 @@ class DLLEXPORT ConnectionManager : public QObject public: static QSharedPointer getManagerForNodeId( const QString& nodeid ); ConnectionManager( const QString& nodeid ); + ~ConnectionManager(); void handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ); @@ -44,6 +45,9 @@ private slots: void socketError( QAbstractSocket::SocketError error ); private: + Q_DECLARE_PRIVATE( ConnectionManager ) + ConnectionManagerPrivate* d_ptr; + void connectToPeer(const Tomahawk::peerinfo_ptr& peerInfo , bool lock); void handleSipInfoPrivate( const Tomahawk::peerinfo_ptr& peerInfo ); @@ -56,13 +60,6 @@ private slots: * Attempt to connect to the peer using the current stored information. */ void tryConnect(); - - // We just keep this for debug purposes and only during connection attempts. - Tomahawk::peerinfo_ptr m_currentPeerInfo; - QString m_nodeid; - QPointer m_controlConnection; - QList m_sipCandidates; - QMutex m_mutex; }; #endif // CONNECTIONMANAGER_H diff --git a/src/libtomahawk/network/ConnectionManager_p.h b/src/libtomahawk/network/ConnectionManager_p.h new file mode 100644 index 0000000000..7033965735 --- /dev/null +++ b/src/libtomahawk/network/ConnectionManager_p.h @@ -0,0 +1,48 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef CONNECTIONMANAGER_P_H +#define CONNECTIONMANAGER_P_H + +#include "ConnectionManager.h" + +#include + +class ConnectionManagerPrivate : public QObject +{ +Q_OBJECT + +public: + ConnectionManagerPrivate( ConnectionManager* q, const QString& _nodeid ) + : q_ptr ( q ) + , nodeid( _nodeid ) + { + } + ConnectionManager* q_ptr; + Q_DECLARE_PUBLIC ( ConnectionManager ) + +private: + // We just keep this for debug purposes and only during connection attempts. + Tomahawk::peerinfo_ptr currentPeerInfo; + QString nodeid; + QPointer controlConnection; + QList sipCandidates; + QMutex mutex; +}; + +#endif // CONNECTIONMANAGER_P_H From 91df437e626e99fdd4266396f5f810376aec00fe Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 7 Jun 2013 19:41:29 +0200 Subject: [PATCH 248/565] Dpointerize NetworkActivityWidget --- .../widgets/NetworkActivityWidget.cpp | 110 +++++++++--------- .../widgets/NetworkActivityWidget.h | 13 +-- .../widgets/NetworkActivityWidget_p.h | 49 ++++++++ 3 files changed, 107 insertions(+), 65 deletions(-) create mode 100644 src/libtomahawk/widgets/NetworkActivityWidget_p.h diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 41265a5a66..785d18ba7e 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -16,8 +16,7 @@ * along with Tomahawk. If not, see . */ -#include "NetworkActivityWidget.h" -#include "ui_NetworkActivityWidget.h" +#include "NetworkActivityWidget_p.h" #include "Pipeline.h" #include "audio/AudioEngine.h" @@ -40,55 +39,56 @@ using namespace Tomahawk; NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) : QWidget( parent ) - , ui( new Ui::NetworkActivityWidget ) - , m_sortedProxy( 0 ) + , d_ptr( new NetworkActivityWidgetPrivate ( this ) ) { - ui->setupUi( this ); + d_func()->ui->setupUi( this ); TomahawkUtils::unmarginLayout( layout() ); - TomahawkUtils::unmarginLayout( ui->stackLeft->layout() ); - TomahawkUtils::unmarginLayout( ui->horizontalLayout->layout() ); - TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() ); + TomahawkUtils::unmarginLayout( d_func()->ui->stackLeft->layout() ); + TomahawkUtils::unmarginLayout( d_func()->ui->verticalLayout_2->layout() ); + TomahawkUtils::unmarginLayout( d_func()->ui->horizontalLayout->layout() ); + TomahawkUtils::unmarginLayout( d_func()->ui->breadCrumbLeft->layout() ); - m_crumbModelLeft = new QStandardItemModel( this ); - m_sortedProxy = new QSortFilterProxyModel( this ); - m_sortedProxy->setDynamicSortFilter( true ); - m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); + d_func()->crumbModelLeft = new QStandardItemModel( this ); + d_func()->sortedProxy = new QSortFilterProxyModel( this ); + d_func()->sortedProxy->setDynamicSortFilter( true ); + d_func()->sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); - ui->breadCrumbLeft->setRootIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ) ); - connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged( QModelIndex ) ) ); + d_func()->ui->breadCrumbLeft->setRootIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ) ); + connect( d_func()->ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged( QModelIndex ) ) ); - ui->tracksViewLeft->setHeaderHidden( true ); - ui->tracksViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( ui->tracksViewLeft, ui->tracksViewLeft->proxyModel() ); - ui->tracksViewLeft->setItemDelegate( del ); - ui->tracksViewLeft->setUniformRowHeights( false ); + d_func()->ui->tracksViewLeft->setHeaderHidden( true ); + d_func()->ui->tracksViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( d_func()->ui->tracksViewLeft, d_func()->ui->tracksViewLeft->proxyModel() ); + d_func()->ui->tracksViewLeft->setItemDelegate( del ); + d_func()->ui->tracksViewLeft->setUniformRowHeights( false ); - m_playlistInterface = ui->tracksViewLeft->playlistInterface(); + d_func()->playlistInterface = d_func()->ui->tracksViewLeft->playlistInterface(); // Lets have a spinner until loaded - ui->breadCrumbLeft->setVisible( false ); - m_spinner = new AnimatedSpinner( ui->tracksViewLeft ); - m_spinner->fadeIn(); + d_func()->ui->breadCrumbLeft->setVisible( false ); + d_func()->spinner = new AnimatedSpinner( d_func()->ui->tracksViewLeft ); + d_func()->spinner->fadeIn(); } NetworkActivityWidget::~NetworkActivityWidget() { + delete d_ptr; } Tomahawk::playlistinterface_ptr NetworkActivityWidget::playlistInterface() const { - return m_playlistInterface; + return d_func()->playlistInterface; } bool NetworkActivityWidget::isBeingPlayed() const { - if ( AudioEngine::instance()->currentTrackPlaylist() == ui->tracksViewLeft->playlistInterface() ) + if ( AudioEngine::instance()->currentTrackPlaylist() == d_func()->ui->tracksViewLeft->playlistInterface() ) return true; return false; @@ -98,7 +98,7 @@ NetworkActivityWidget::isBeingPlayed() const bool NetworkActivityWidget::jumpToCurrentTrack() { - if ( ui->tracksViewLeft->model() && ui->tracksViewLeft->jumpToCurrentTrack() ) + if ( d_func()->ui->tracksViewLeft->model() && d_func()->ui->tracksViewLeft->jumpToCurrentTrack() ) return true; return false; @@ -116,11 +116,11 @@ NetworkActivityWidget::fetchData() void NetworkActivityWidget::weeklyCharts( const QList& tracks ) { - m_weeklyChartsModel = new PlaylistModel( ui->tracksViewLeft ); - m_weeklyChartsModel->startLoading(); + d_func()->weeklyChartsModel = new PlaylistModel( d_func()->ui->tracksViewLeft ); + d_func()->weeklyChartsModel->startLoading(); // Pipeline::instance()->resolve( tracks ); - m_weeklyChartsModel->appendTracks( tracks ); - m_weeklyChartsModel->finishLoading(); + d_func()->weeklyChartsModel->appendTracks( tracks ); + d_func()->weeklyChartsModel->finishLoading(); checkDone(); } @@ -129,11 +129,11 @@ NetworkActivityWidget::weeklyCharts( const QList& tracks ) void NetworkActivityWidget::monthlyCharts( const QList& tracks ) { - m_monthlyChartsModel = new PlaylistModel( ui->tracksViewLeft ); - m_monthlyChartsModel->startLoading(); + d_func()->monthlyChartsModel = new PlaylistModel( d_func()->ui->tracksViewLeft ); + d_func()->monthlyChartsModel->startLoading(); // Pipeline::instance()->resolve( tracks ); - m_monthlyChartsModel->appendTracks( tracks ); - m_monthlyChartsModel->finishLoading(); + d_func()->monthlyChartsModel->appendTracks( tracks ); + d_func()->monthlyChartsModel->finishLoading(); checkDone(); } @@ -142,11 +142,11 @@ NetworkActivityWidget::monthlyCharts( const QList& tracks ) void NetworkActivityWidget::yearlyCharts( const QList& tracks ) { - m_yearlyChartsModel = new PlaylistModel( ui->tracksViewLeft ); - m_yearlyChartsModel->startLoading(); + d_func()->yearlyChartsModel = new PlaylistModel( d_func()->ui->tracksViewLeft ); + d_func()->yearlyChartsModel->startLoading(); // Pipeline::instance()->resolve( tracks ); - m_yearlyChartsModel->appendTracks( tracks ); - m_yearlyChartsModel->finishLoading(); + d_func()->yearlyChartsModel->appendTracks( tracks ); + d_func()->yearlyChartsModel->finishLoading(); checkDone(); } @@ -155,7 +155,7 @@ NetworkActivityWidget::yearlyCharts( const QList& tracks ) void NetworkActivityWidget::leftCrumbIndexChanged( const QModelIndex& index ) { - QStandardItem* item = m_crumbModelLeft->itemFromIndex( m_sortedProxy->mapToSource( index ) ); + QStandardItem* item = d_func()->crumbModelLeft->itemFromIndex( d_func()->sortedProxy->mapToSource( index ) ); if ( !item ) return; if ( !item->data( Breadcrumb::ChartIdRole ).isValid() ) @@ -164,21 +164,21 @@ NetworkActivityWidget::leftCrumbIndexChanged( const QModelIndex& index ) const QString chartId = item->data( Breadcrumb::ChartIdRole ).toString(); if ( chartId == NETWORKCHARTS_WEEK_CHARTS ) { - ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); - ui->tracksViewLeft->setPlaylistModel( m_weeklyChartsModel ); - ui->tracksViewLeft->proxyModel()->sort( -1 ); + d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->weeklyChartsModel ); + d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); } else if ( chartId == NETWORKCHARTS_MONTH_CHARTS ) { - ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); - ui->tracksViewLeft->setPlaylistModel( m_monthlyChartsModel ); - ui->tracksViewLeft->proxyModel()->sort( -1 ); + d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->monthlyChartsModel ); + d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); } else if ( chartId == NETWORKCHARTS_YEAR_CHARTS ) { - ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); - ui->tracksViewLeft->setPlaylistModel( m_yearlyChartsModel ); - ui->tracksViewLeft->proxyModel()->sort( -1 ); + d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->yearlyChartsModel ); + d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); } } @@ -214,12 +214,12 @@ NetworkActivityWidget::actualFetchData() void NetworkActivityWidget::checkDone() { - if ( !m_weeklyChartsModel.isNull() && !m_yearlyChartsModel.isNull() && !m_monthlyChartsModel.isNull() ) + if ( !d_func()->weeklyChartsModel.isNull() && !d_func()->yearlyChartsModel.isNull() && !d_func()->monthlyChartsModel.isNull() ) { // All charts are loaded, do the remaining work UI work. // Build up breadcrumb - QStandardItem* rootItem = m_crumbModelLeft->invisibleRootItem(); + QStandardItem* rootItem = d_func()->crumbModelLeft->invisibleRootItem(); QStandardItem* chartItem = new QStandardItem( tr( "Charts" ) ); rootItem->appendRow( chartItem ); QStandardItem* weekItem = new QStandardItem( tr( "Last Week" ) ); @@ -231,11 +231,11 @@ NetworkActivityWidget::checkDone() QStandardItem* yearItem = new QStandardItem( tr( "Last Year" ) ); yearItem->setData( NETWORKCHARTS_YEAR_CHARTS, Breadcrumb::ChartIdRole ); chartItem->appendRow( yearItem ); - m_sortedProxy->setSourceModel( m_crumbModelLeft ); - m_sortedProxy->sort( 0, Qt::AscendingOrder ); - ui->breadCrumbLeft->setModel( m_sortedProxy ); + d_func()->sortedProxy->setSourceModel( d_func()->crumbModelLeft ); + d_func()->sortedProxy->sort( 0, Qt::AscendingOrder ); + d_func()->ui->breadCrumbLeft->setModel( d_func()->sortedProxy ); - m_spinner->fadeOut(); - ui->breadCrumbLeft->setVisible( true ); + d_func()->spinner->fadeOut(); + d_func()->ui->breadCrumbLeft->setVisible( true ); } } diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.h b/src/libtomahawk/widgets/NetworkActivityWidget.h index b4b55e7e3d..35cb50b1fb 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.h +++ b/src/libtomahawk/widgets/NetworkActivityWidget.h @@ -22,6 +22,7 @@ #include "ViewPage.h" class AnimatedSpinner; +class NetworkActivityWidgetPrivate; class PlaylistModel; class QModelIndex; class QStandardItemModel; @@ -63,16 +64,8 @@ private slots: void actualFetchData(); void checkDone(); - QSharedPointer ui; - Tomahawk::playlistinterface_ptr m_playlistInterface; - AnimatedSpinner* m_spinner; - QStandardItemModel* m_crumbModelLeft; - QSortFilterProxyModel* m_sortedProxy; - - QPointer m_weeklyChartsModel; - QPointer m_monthlyChartsModel; - QPointer m_yearlyChartsModel; - + Q_DECLARE_PRIVATE( NetworkActivityWidget ) + NetworkActivityWidgetPrivate* d_ptr; }; #endif // NETWORKACTIVITYWIDGET_H diff --git a/src/libtomahawk/widgets/NetworkActivityWidget_p.h b/src/libtomahawk/widgets/NetworkActivityWidget_p.h new file mode 100644 index 0000000000..063d265d5d --- /dev/null +++ b/src/libtomahawk/widgets/NetworkActivityWidget_p.h @@ -0,0 +1,49 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef NETWORKACTIVITYWIDGET_P_H +#define NETWORKACTIVITYWIDGET_P_H + +#include "NetworkActivityWidget.h" +#include "ui_NetworkActivityWidget.h" + +class NetworkActivityWidgetPrivate +{ +public: + NetworkActivityWidgetPrivate( NetworkActivityWidget* q ) + : q_ptr ( q ) + , ui( new Ui::NetworkActivityWidget ) + , sortedProxy( 0 ) + { + } + NetworkActivityWidget* q_ptr; + Q_DECLARE_PUBLIC ( NetworkActivityWidget ) + +private: + QSharedPointer ui; + Tomahawk::playlistinterface_ptr playlistInterface; + AnimatedSpinner* spinner; + QStandardItemModel* crumbModelLeft; + QSortFilterProxyModel* sortedProxy; + + QPointer weeklyChartsModel; + QPointer monthlyChartsModel; + QPointer yearlyChartsModel; +}; + +#endif // NETWORKACTIVITYWIDGET_P_H From 797fa8d21cd12f34543a3fc5d2835b4d0b0caa4e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 7 Jun 2013 19:41:48 +0200 Subject: [PATCH 249/565] Dpointerize PeerInfo --- src/libtomahawk/sip/PeerInfo.cpp | 63 ++++++++++++++++---------------- src/libtomahawk/sip/PeerInfo.h | 28 ++++---------- src/libtomahawk/sip/PeerInfo_p.h | 61 +++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 src/libtomahawk/sip/PeerInfo_p.h diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 047e46830f..405174e4d2 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -17,7 +17,8 @@ * along with Tomahawk. If not, see . */ -#include "PeerInfo.h" +#include "PeerInfo_p.h" + #include "SipPlugin.h" #include "utils/TomahawkCache.h" #include "utils/TomahawkUtilsGui.h" @@ -112,10 +113,7 @@ PeerInfo::getAll() PeerInfo::PeerInfo( SipPlugin* parent, const QString& id ) : QObject() - , m_parent( parent ) - , m_type( External ) - , m_id( id ) - , m_status( Offline ) + , d_ptr( new Tomahawk::PeerInfoPrivate ( this, parent, id ) ) , m_avatar( 0 ) , m_fancyAvatar( 0 ) { @@ -128,6 +126,7 @@ PeerInfo::~PeerInfo() s_peersByCacheKey.remove( s_peersByCacheKey.key( weakRef() ) ); delete m_avatar; delete m_fancyAvatar; + delete d_ptr; } @@ -143,62 +142,62 @@ PeerInfo::announce() QWeakPointer< PeerInfo > PeerInfo::weakRef() { - return m_ownRef; + return d_func()->ownRef; } void PeerInfo::setWeakRef( QWeakPointer< PeerInfo > weakRef ) { - m_ownRef = weakRef; + d_func()->ownRef = weakRef; } void PeerInfo::setControlConnection( ControlConnection* controlConnection ) { - m_controlConnection = controlConnection; + d_func()->controlConnection = controlConnection; } ControlConnection* PeerInfo::controlConnection() const { - return m_controlConnection; + return d_func()->controlConnection; } bool PeerInfo::hasControlConnection() { - return !m_controlConnection.isNull(); + return !d_func()->controlConnection.isNull(); } void PeerInfo::setType( Tomahawk::PeerInfo::Type type ) { - m_type = type; + d_func()->type = type; } PeerInfo::Type PeerInfo::type() const { - return m_type; + return d_func()->type; } const QString PeerInfo::id() const { - return m_id; + return d_func()->id; } SipPlugin* PeerInfo::sipPlugin() const { - return m_parent; + return d_func()->parent; } @@ -219,30 +218,30 @@ PeerInfo::debugName() const void PeerInfo::setContactId ( const QString& contactId ) { - m_contactId = contactId; + d_func()->contactId = contactId; } const QString PeerInfo::contactId() const { - return m_contactId; + return d_func()->contactId; } const QString PeerInfo::nodeId() const { - Q_ASSERT( !m_sipInfos.isEmpty() ); + Q_ASSERT( !d_func()->sipInfos.isEmpty() ); // All sip infos share the same nodeId - return m_sipInfos.first().nodeId(); + return d_func()->sipInfos.first().nodeId(); } const QString PeerInfo::key() const { - Q_ASSERT( !m_sipInfos.isEmpty() ); + Q_ASSERT( !d_func()->sipInfos.isEmpty() ); // All sip infos share the same key - return m_sipInfos.first().key(); + return d_func()->sipInfos.first().key(); } @@ -250,7 +249,7 @@ PeerInfo::key() const void PeerInfo::setStatus( PeerInfo::Status status ) { - m_status = status; + d_func()->status = status; if ( status == Online ) { @@ -272,14 +271,14 @@ PeerInfo::setStatus( PeerInfo::Status status ) PeerInfo::Status PeerInfo::status() const { - return m_status; + return d_func()->status; } void PeerInfo::setSipInfos( const QList& sipInfos ) { - m_sipInfos = sipInfos; + d_func()->sipInfos = sipInfos; tLog() << "id:" << id() << "info changed" << sipInfos; emit sipInfoChanged(); @@ -289,21 +288,21 @@ PeerInfo::setSipInfos( const QList& sipInfos ) const QList PeerInfo::sipInfos() const { - return m_sipInfos; + return d_func()->sipInfos; } void PeerInfo::setFriendlyName( const QString& friendlyName ) { - m_friendlyName = friendlyName; + d_func()->friendlyName = friendlyName; } const QString PeerInfo::friendlyName() const { - return m_friendlyName; + return d_func()->friendlyName; } @@ -387,27 +386,27 @@ PeerInfo::avatar( TomahawkUtils::ImageMode style, const QSize& size ) const void PeerInfo::setVersionString( const QString& versionString ) { - m_versionString = versionString; + d_func()->versionString = versionString; } const QString PeerInfo::versionString() const { - return m_versionString; + return d_func()->versionString; } void PeerInfo::setData( const QVariant& data ) { - m_data = data; + d_func()->data = data; } -const -QVariant PeerInfo::data() const +const QVariant +PeerInfo::data() const { - return m_data; + return d_func()->data; } diff --git a/src/libtomahawk/sip/PeerInfo.h b/src/libtomahawk/sip/PeerInfo.h index 74fd52c7c0..12ffbf703f 100644 --- a/src/libtomahawk/sip/PeerInfo.h +++ b/src/libtomahawk/sip/PeerInfo.h @@ -19,26 +19,23 @@ #ifndef PEERINFO_H #define PEERINFO_H - - #include "DllMacro.h" - -#include "SipInfo.h" -#include "accounts/Account.h" #include "utils/TomahawkUtils.h" #include #include - #define peerInfoDebug(peerInfo) tDebug() << "PEERINFO:" << ( !peerInfo.isNull() ? peerInfo->debugName() : "Invalid PeerInfo" ).toLatin1().constData() -class SipPlugin; class ControlConnection; +class SipPlugin; +class SipInfo; namespace Tomahawk { +class PeerInfoPrivate; + class DLLEXPORT PeerInfo : public QObject { Q_OBJECT @@ -129,23 +126,12 @@ Q_OBJECT PeerInfo( SipPlugin* parent, const QString& id ); void announce(); + Q_DECLARE_PRIVATE( Tomahawk::PeerInfo ) + Tomahawk::PeerInfoPrivate* d_ptr; + static QHash< QString, peerinfo_wptr > s_peersByCacheKey; static QHash< SipPlugin*, peerinfo_ptr > s_selfPeersBySipPlugin; - QWeakPointer< Tomahawk::PeerInfo > m_ownRef; - QPointer< ControlConnection > m_controlConnection; - - SipPlugin* m_parent; - PeerInfo::Type m_type; - - QString m_id; - QString m_contactId; - Status m_status; - QList m_sipInfos; - QString m_friendlyName; - QString m_versionString; - QVariant m_data; - mutable QPixmap* m_avatar; mutable QPixmap* m_fancyAvatar; diff --git a/src/libtomahawk/sip/PeerInfo_p.h b/src/libtomahawk/sip/PeerInfo_p.h new file mode 100644 index 0000000000..78187672e5 --- /dev/null +++ b/src/libtomahawk/sip/PeerInfo_p.h @@ -0,0 +1,61 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Dominik Schmidt + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef PEERINFO_P_H +#define PEERINFO_P_H + +#include "PeerInfo.h" + +namespace Tomahawk +{ + +class PeerInfoPrivate +{ +public: + PeerInfoPrivate( PeerInfo* q, SipPlugin* parent, const QString& id ) + : q_ptr ( q ) + , parent( parent ) + , type( PeerInfo::External ) + , id( id ) + , status( PeerInfo::Offline ) + + { + } + PeerInfo* q_ptr; + Q_DECLARE_PUBLIC ( PeerInfo ) + +private: + QWeakPointer< Tomahawk::PeerInfo > ownRef; + QPointer< ControlConnection > controlConnection; + + SipPlugin* parent; + PeerInfo::Type type; + + QString id; + QString contactId; + PeerInfo::Status status; + QList sipInfos; + QString friendlyName; + QString versionString; + QVariant data; +}; + +} + +#endif // PEERINFO_P_H From 268537bdc6706510140edfa0263a36f4c35b5ded Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sat, 8 Jun 2013 02:16:49 +0200 Subject: [PATCH 250/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_bg.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_bn_IN.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_ca.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_ca@valencia.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_cs.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_da.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_de.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_el.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_en.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_es.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_fi.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_fr.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_gl.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_hi_IN.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_hu.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_id.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_it.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_ja.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_lt.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_pl.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_pt_BR.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_ru.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_sv.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_tr.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_zh_CN.ts | 102 ++++++++++++++++++++--------------- lang/tomahawk_zh_TW.ts | 102 ++++++++++++++++++++--------------- 27 files changed, 1620 insertions(+), 1134 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 38b7e1f806..87b446c1a8 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -266,27 +266,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist قائمة المسارات - + Other Albums ألبومات أخرى - + Sorry, we could not find any other albums for this artist! نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! - + Sorry, we could not find any tracks for this album! نعتذر، لم نستطيع إيجاد أغاني أخرى لهذا الألبوم! - + Other Albums by %1 ألبومات أخرى ل%1 @@ -308,35 +308,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits الأكثر شهرة - + Related Artists الفنانين ذات الذوق القريب - + Albums ألبومات - + Sorry, we could not find any albums for this artist! نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! - + Sorry, we could not find any related artists! نعتذر، لم نستطيع إيجاد فنانين! - + Sorry, we could not find any top hits for this artist! نعتذر، لم نستطيع إيجاد أغاني مشهورة جدا لهذا الفنان! + + + CHART # + + AudioControls @@ -1050,7 +1055,7 @@ Password العام الماضي
- + Network Activity نشاط الشبكة @@ -1154,48 +1159,48 @@ Password الدقة
- + Perfect match تطابق تام - + Very good match تطابق جيد جدا - + Good match تطابق جيد - + Vague match تطابق مبهم - + Bad match تطابق سيء - + Very bad match تطابق سيء للغاية - + Not available غير متوفر - + Searching... قيد البحث... - + Name إسم @@ -2204,6 +2209,14 @@ Password حذف قوائم الأغاني المرتبطة بسبوتيفي (Spotify)؟
+ + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2714,13 +2727,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection مجموعة - + This collection is empty. هذه المجموعة فارغة. @@ -3523,43 +3536,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل @@ -3626,7 +3639,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة @@ -3876,37 +3889,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks أغاني قريبة - + Sorry, but we could not find similar tracks for this song! نعتذر، لم نستطيع إيجاد أغاني قريبة من هذه الأغنية! + + + PLAYS + + - + You've listened to this track %n time(s). لقد استمعت إلى هذه الأغنية %n مرة.لقد استمعت إلى هذه الأغنية مرة %n.لقد استمعت إلى هذه الأغنية مرتين %n.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات. - + You've never listened to this track before. لم تستمع لهذه الأغنية من قبل. - + You first listened to it on %1. استمعت إليها أولاً في %1. - + You've listened to %1 %n time(s). لقد استمعت إلى %1 %n مرة.لقد استمعت إلى %1 مرة %n.لقد استمعت إلى %1 مرتين %n.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات. - + You've never listened to %1 before. لم تستمع إلى %1 من قبل. @@ -3914,7 +3932,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. عذراً، ترشيحك "%1" لم يطابق أي نتائج. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index ce340955b2..38ea21d10c 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Списък на песни за изпълняване - + Other Albums Други албуми - + Sorry, we could not find any other albums for this artist! Съжалявам, но не откривам нито един албум от този изпълнител! - + Sorry, we could not find any tracks for this album! Съжалявам, но не откривам нито една песен за този изпълнител! - + Other Albums by %1 Други албуми от %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Най-известни изпълнения - + Related Artists Изпълнители с подобно звучене - + Albums Албуми - + Sorry, we could not find any albums for this artist! Съжалявам, но не откривам нито един албум за този изпълнител! - + Sorry, we could not find any related artists! Съжалявам, но не откривам нито един подобен на този изпълнтел! - + Sorry, we could not find any top hits for this artist! Съжалявам, но не откривам нито една хитова песен на този изпълнител! + + + CHART # + + AudioControls @@ -1055,7 +1060,7 @@ Password - + Network Activity @@ -1159,48 +1164,48 @@ Password Съвпадение
- + Perfect match Абсолютно - + Very good match Много добро - + Good match Добро - + Vague match Горе-долу - + Bad match Лошо - + Very bad match Много лошо - + Not available Няма съвпадение - + Searching... - + Name Име @@ -2215,6 +2220,14 @@ Password Изтрий свъразнаните с Spotify списъци
+ + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2727,13 +2740,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Колекция - + This collection is empty. Тук няма нищо @@ -3536,43 +3549,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 песни) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия @@ -3639,7 +3652,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -3891,37 +3904,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Подобни песни - + Sorry, but we could not find similar tracks for this song! Съжалявам, но не откривам нито една подобна на тази песен! + + + PLAYS + + - + You've listened to this track %n time(s). Ти си слушал тази песен %n път(и)Ти си слушал тази песен %n път(и) - + You've never listened to this track before. Никога не си слушал тази песен преди - + You first listened to it on %1. Първоначално си я слушал на %1 - + You've listened to %1 %n time(s). Слушал си %1 път(и)Слушал си %1 %n път(и) - + You've never listened to %1 before. Никога не си слушал %1 преди @@ -3929,7 +3947,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Съжалявам, твоят филтър %1 не върна никакъв резултат. diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 3a177a5a2a..90122f9cad 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -2200,6 +2205,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2704,13 +2717,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3509,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3612,7 +3625,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3861,37 +3874,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3899,7 +3917,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index c508673083..e5d65d121f 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Llista de cançons - + Other Albums Altres àlbums - + Sorry, we could not find any other albums for this artist! No s'ha trobat cap altre àlbum d'aquest artista - + Sorry, we could not find any tracks for this album! No s'ha trobat cap altra cançó d'aquest àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'aquest artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'aquest artista + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... - + Name Nom @@ -2204,6 +2209,14 @@ i emissores basades en els vostres gusts musicals. Voleu suprimir la llista de reproducció associada de l'Spotify? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2714,13 +2727,13 @@ nomdusuari@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3523,43 +3536,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia @@ -3626,7 +3639,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció @@ -3876,37 +3889,42 @@ introduïu el PIN aquí: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a aquesta cançó + + + PLAYS + + - + You've listened to this track %n time(s). Heu escoltat aquesta cançó %n cop.Heu escoltat aquesta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai aquesta cançó abans. - + You first listened to it on %1. Vau escoltar aquesta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. @@ -3914,7 +3932,7 @@ introduïu el PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index fe5a77fb03..be67860ec6 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Llista de cançons - + Other Albums Altres àlbums - + Sorry, we could not find any other albums for this artist! No s'ha trobat cap altre àlbum d'este artista - + Sorry, we could not find any tracks for this album! No s'ha trobat cap altra cançó d'este àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'este artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'este artista + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... - + Name Nom @@ -2204,6 +2209,14 @@ i emissores basades en els vostres gusts musicals. Voleu suprimir la llista de reproducció associada de l'Spotify? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2714,13 +2727,13 @@ nomdusuari@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3523,43 +3536,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia @@ -3626,7 +3639,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció @@ -3876,37 +3889,42 @@ introduïu el PIN ací: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a esta cançó + + + PLAYS + + - + You've listened to this track %n time(s). Heu escoltat esta cançó %n cop.Heu escoltat esta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai esta cançó abans. - + You first listened to it on %1. Vau escoltar esta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. @@ -3914,7 +3932,7 @@ introduïu el PIN ací: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 6a542e0b5e..5c3e0d816b 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -266,27 +266,27 @@ se s vámi spojil? AlbumInfoWidget - + Tracklist Seznam skladeb - + Other Albums Jiná alba - + Sorry, we could not find any other albums for this artist! Promiňte, nepodařilo se najít žádná jiná alba tohoto umělce! - + Sorry, we could not find any tracks for this album! Promiňte, nepodařilo se najít žádné skladby pro toto album! - + Other Albums by %1 Jiná alba %1 @@ -308,35 +308,40 @@ se s vámi spojil? ArtistInfoWidget - + Top Hits Nejlepší písně - + Related Artists Podobní umělci - + Albums Alba - + Sorry, we could not find any albums for this artist! Promiňte, nepodařilo se najít žádná alba tohoto umělce! - + Sorry, we could not find any related artists! Promiňte, nepodařilo se najít žádné podobné umělce! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! + + + CHART # + + AudioControls @@ -1050,7 +1055,7 @@ heslo Poslední rok - + Network Activity Činnost sítě @@ -1154,48 +1159,48 @@ heslo Přesnost - + Perfect match Přesná shoda - + Very good match Velmi dobrá shoda - + Good match Dobrá shoda - + Vague match Mlhavá shoda - + Bad match Špatná shoda - + Very bad match Velmi špatná shoda - + Not available Nedostupné - + Searching... Hledá se... - + Name Název @@ -2203,6 +2208,14 @@ heslo Chcete smazat příslušný seznam skladeb Spotify? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2713,13 +2726,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Sbírka - + This collection is empty. Tato sbírka je prázdná. @@ -3523,43 +3536,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený @@ -3626,7 +3639,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka @@ -3876,37 +3889,42 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackInfoWidget - + Similar Tracks Podobné skladby - + Sorry, but we could not find similar tracks for this song! Promiňte, ale nepodařilo se najít podobné skladby pro tuto píseň! + + + PLAYS + + - + You've listened to this track %n time(s). Tuto píseň jste si poslechl jednou.Tuto píseň jste si poslechl %n krát.Tuto píseň jste si poslechl %n krát. - + You've never listened to this track before. Tuto píseň jste si ještě nikdy neposlechl. - + You first listened to it on %1. Tuto píseň jste si poprvé poslechl %1. - + You've listened to %1 %n time(s). %1 jste si poslechl jednou.%1 jste si poslechl %n krát.%1 jste si poslechl %n krát. - + You've never listened to %1 before. %1 jste si předtím ještě nikdy neposlechl. @@ -3914,7 +3932,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackView - + Sorry, your filter '%1' did not match any results. Promiňte, vašemu filtru '%1' se nepodařilo najít žádné výsledky. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 1f1cff2b98..8a38d8a729 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Andre Albums af %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Relaterede Kunstnere - + Albums Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -2201,6 +2206,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2706,13 +2719,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3511,43 +3524,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline @@ -3614,7 +3627,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling @@ -3863,37 +3876,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3901,7 +3919,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Beklager, din filter '%1' matchede ikke nogle resultater diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index cc74441a54..f329826cb8 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -266,27 +266,27 @@ erlauben sich mit dir zu verbinden? AlbumInfoWidget - + Tracklist Tracklist - + Other Albums Andere Alben - + Sorry, we could not find any other albums for this artist! Sorry, wir konnten keine anderen Alben für diesen Künstler finden! - + Sorry, we could not find any tracks for this album! Sorry, wir konnten keine anderen Lieder für dieses Album finden! - + Other Albums by %1 Andere Alben von %1 @@ -308,35 +308,40 @@ erlauben sich mit dir zu verbinden? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Ähnliche Künstler - + Albums Alben - + Sorry, we could not find any albums for this artist! Sorry, wir konnten keine Alben für diesen Künstler finden! - + Sorry, we could not find any related artists! Sorry, wir konnten keine ähnlichen Künstler finden! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! + + + CHART # + + AudioControls @@ -1050,7 +1055,7 @@ Passwort Letztes Jahr - + Network Activity Netzwerk Aktivität @@ -1154,48 +1159,48 @@ Passwort Treffsicherheit - + Perfect match Perfekt - + Very good match Sehr gut - + Good match Gut - + Vague match Vage - + Bad match Schlecht - + Very bad match Sehr schlecht - + Not available Nicht verfügbar - + Searching... Suche läuft... - + Name Name @@ -2203,6 +2208,14 @@ Passwort Möchtest du die zugehörige Spotify Playlist löschen? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2708,13 +2721,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Sammlung - + This collection is empty. Diese Sammlung ist leer. @@ -3518,43 +3531,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline @@ -3621,7 +3634,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -3871,37 +3884,42 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackInfoWidget - + Similar Tracks Ähnliche Lieder - + Sorry, but we could not find similar tracks for this song! Sorry, wir konnten keine ähnlichen Lieder finden! + + + PLAYS + + - + You've listened to this track %n time(s). Du hast dieses Lied einmal gehört.Du hast dieses Lied %n mal gehört. - + You've never listened to this track before. Du hast dieses Lied noch nie angehört. - + You first listened to it on %1. Du hast dieses Lied zum ersten mal am %1 gehört. - + You've listened to %1 %n time(s). Du hast %1 einmal angehört.Du hast %1 %n mal angehört. - + You've never listened to %1 before. Du hast %1 vorher noch nie gehört. @@ -3909,7 +3927,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackView - + Sorry, your filter '%1' did not match any results. Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index e319d8fe14..53957bbdd9 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Λιστα τραγουδιων - + Other Albums Αλλα Αλμπουμ - + Sorry, we could not find any other albums for this artist! Συγνωμη, δεν βρεθηκαν αλλα αλμπουμ αυτου του καλλιτεχνη! - + Sorry, we could not find any tracks for this album! Συγνωμη, δεν βρεθηκαν αλλα τραγουδια αυτου του αλμπουμ! - + Other Albums by %1 Άλλα Άλμπουμ από %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Κορυφαία τραγούδια - + Related Artists Παρόμοιοι Καλλιτέχνες - + Albums Άλμπουμ - + Sorry, we could not find any albums for this artist! Συγνωμη, δεν βρεθηκαν αλμπουμ αυτου του καλλιτεχνη! - + Sorry, we could not find any related artists! Συγνωμη, δεν βρεθηκαν καλλιτεχνες! - + Sorry, we could not find any top hits for this artist! Συγνωμη, δεν βρεθηκαν κορυφαια τραγουδια αυτου του καλλιτεχνη! + + + CHART # + + AudioControls @@ -1049,7 +1054,7 @@ Password Τελευταίο ετος - + Network Activity Δραστηριότητα δικτύου @@ -1153,48 +1158,48 @@ Password Ακρίβεια - + Perfect match Τέλειο ταίριασμα - + Very good match Πολύ καλό ταίριασμα - + Good match Καλό ταίριασμα - + Vague match Ασαφές ταίριασμα - + Bad match Κακό ταίριασμα - + Very bad match Πολύ κακό ταίριασμα - + Not available Μη διαθέσιμο - + Searching... Αναζήτηση... - + Name Όνομα @@ -2203,6 +2208,14 @@ Password Διαγραφή σχετιζόμενης λίστας αναπαραγωγής Spotify; + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2714,13 +2727,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Συλλογή - + This collection is empty. Αυτή η συλλογή ειναι άδεια. @@ -3524,43 +3537,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης @@ -3627,7 +3640,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου @@ -3876,37 +3889,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Παρόμοια Κομμάτια - + Sorry, but we could not find similar tracks for this song! Συγγνωμη, δεν βρεθηκαν παρόμοια κομμάτια για αυτό το τραγούδι! + + + PLAYS + + - + You've listened to this track %n time(s). Έχετε ακούσει το κομμάτι %n φορά.Έχετε ακούσει το κομμάτι %n φορές. - + You've never listened to this track before. Δεν έχετε ακούσει αυτό το κομμάτι παλιότερα. - + You first listened to it on %1. Το ακούσατε για πρώτη φορά στις %1. - + You've listened to %1 %n time(s). Ακούσατε το %1 %n φορά.Ακούσατε το %1 %n φορές. - + You've never listened to %1 before. Δεν έχετε ακούσει το %1 ποτέ πριν. @@ -3914,7 +3932,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Συγγνώμη, το φίλτρο «%1» δεν αντιστοίχισε αποτελέσματα. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index c88dac2919..bb7f4e4db8 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -266,27 +266,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Tracklist - + Other Albums Other Albums - + Sorry, we could not find any other albums for this artist! Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! Sorry, we could not find any tracks for this album! - + Other Albums by %1 Other Albums by %1 @@ -308,35 +308,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Related Artists - + Albums Albums - + Sorry, we could not find any albums for this artist! Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! Sorry, we could not find any top hits for this artist! + + + CHART # + CHART # + AudioControls @@ -1050,7 +1055,7 @@ Password Last Year - + Network Activity Network Activity @@ -1154,48 +1159,48 @@ Password Accuracy - + Perfect match Perfect match - + Very good match Very good match - + Good match Good match - + Vague match Vague match - + Bad match Bad match - + Very bad match Very bad match - + Not available Not available - + Searching... Searching... - + Name Name @@ -2206,6 +2211,14 @@ Password Delete associated Spotify playlist? + + StatsGauge + + + out of %1 + out of %1 + + TemporaryPageItem @@ -2716,13 +2729,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Collection - + This collection is empty. This collection is empty. @@ -3526,43 +3539,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline @@ -3629,7 +3642,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -3879,37 +3892,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Similar Tracks - + Sorry, but we could not find similar tracks for this song! Sorry, but we could not find similar tracks for this song! + + + PLAYS + PLAYS + - + You've listened to this track %n time(s). You've listened to this track %n time.You've listened to this track %n times. - + You've never listened to this track before. You've never listened to this track before. - + You first listened to it on %1. You first listened to it on %1. - + You've listened to %1 %n time(s). You've listened to %1 %n time.You've listened to %1 %n times. - + You've never listened to %1 before. You've never listened to %1 before. @@ -3917,7 +3935,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 58306c709a..36092db24a 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -266,27 +266,27 @@ conectarse a usted y transmitir música? AlbumInfoWidget - + Tracklist Pistas - + Other Albums Otros álbumes - + Sorry, we could not find any other albums for this artist! No se encontraron otros álbumes de este artista - + Sorry, we could not find any tracks for this album! No se encontraron pistas de este álbum - + Other Albums by %1 Otros álbumes de %1 @@ -308,35 +308,40 @@ conectarse a usted y transmitir música? ArtistInfoWidget - + Top Hits Grandes éxitos - + Related Artists Artistas relacionados - + Albums Álbumes - + Sorry, we could not find any albums for this artist! No se encontraron álbumes de este artista - + Sorry, we could not find any related artists! No se encontraron artistas relacionados - + Sorry, we could not find any top hits for this artist! No se encontraron éxitos de este artista + + + CHART # + + AudioControls @@ -1049,7 +1054,7 @@ Password - + Network Activity @@ -1153,48 +1158,48 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia muy buena - + Good match Coincidencia buena - + Vague match Coincidencia vaga - + Bad match Mala coincidencia - + Very bad match Muy mala coincidencia - + Not available No disponible - + Searching... - + Name Título @@ -2205,6 +2210,14 @@ y estaciones basadas en sus gustos personales. ¿Eliminar lista de reproducción de Spotify? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2715,13 +2728,13 @@ usuario@jabber.org Tomahawk::Collection - - + + Collection Colección - + This collection is empty. Esta colección está vacía. @@ -3524,43 +3537,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado @@ -3627,7 +3640,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección @@ -3877,37 +3890,42 @@ introduzca su número PIN aquí: TrackInfoWidget - + Similar Tracks Pistas similares - + Sorry, but we could not find similar tracks for this song! No se han encontrado pistas similares + + + PLAYS + + - + You've listened to this track %n time(s). Ha escuchado esta pista %n vez.Ha escuchado esta pista %n veces. - + You've never listened to this track before. Nunca ha escuchado esta pista antes. - + You first listened to it on %1. Escuchó esta pista por primera vez en %1. - + You've listened to %1 %n time(s). Ha escuchado %1 una vez.Ha escuchado %1 %n veces. - + You've never listened to %1 before. Nunca ha escuchado %1 antes. @@ -3915,7 +3933,7 @@ introduzca su número PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. Lo siento, tu filtro '%1' no ha encontrado resultados. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 0d3bf6cd36..60481ea7a6 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -266,27 +266,27 @@ yhdistää ja toistaa sinulta virtaa? AlbumInfoWidget - + Tracklist Kappalelista - + Other Albums Muita albumeita - + Sorry, we could not find any other albums for this artist! Valitettavasti emme löytänet mitään muita albumeita tältä artistilta! - + Sorry, we could not find any tracks for this album! Valitettavasti emme löytäneet mitään tämän albumin kappaleita! - + Other Albums by %1 Muita artistin %1 levyjä @@ -308,35 +308,40 @@ yhdistää ja toistaa sinulta virtaa? ArtistInfoWidget - + Top Hits Parhaat hitit - + Related Artists Samankaltaisia artisteja - + Albums Albumit - + Sorry, we could not find any albums for this artist! Valitettavasti emme löytänet yhtään tämän artistin albumia! - + Sorry, we could not find any related artists! Valitettavasti emme löytäneet yhtään samankaltaista artistia! - + Sorry, we could not find any top hits for this artist! Valitettavasti emme löytäneet yhtään tämän artistin parhaista hiteistä! + + + CHART # + + AudioControls @@ -1050,7 +1055,7 @@ salasana - + Network Activity @@ -1154,48 +1159,48 @@ salasana Tarkkuus - + Perfect match Täysosuma - + Very good match Erittäin hyvä osuma - + Good match Hyvä osuma - + Vague match Epämääräinen osuma - + Bad match Kehno osuma - + Very bad match Erittäin kehno osuma - + Not available Ei saatavilla - + Searching... Haetaan... - + Name Nimi @@ -2209,6 +2214,14 @@ napsauttamalla hiiren oikealla. Poistetaanko liitetty Spotify-soittolista? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2719,13 +2732,13 @@ käyttäjätunnus@jabber.org Tomahawk::Collection - - + + Collection Kokoelma - + This collection is empty. Tämä kokoelma on tyhjä. @@ -3529,43 +3542,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa @@ -3632,7 +3645,7 @@ kappaleen %2%4 %3. TomahawkApp - + My Collection Oma kokoelma @@ -3882,37 +3895,42 @@ anna siellä näytetty PIN-koodi tähän: TrackInfoWidget - + Similar Tracks Samankaltaisia kappaleita - + Sorry, but we could not find similar tracks for this song! Valitettavasti emme löytäneet tälle kappaleelle samankaltaisia kappaleita! + + + PLAYS + + - + You've listened to this track %n time(s). Olet kuunnellut tätä kappaletta %n kerran.Olet kuunnellut tätä kappaletta %n kertaa. - + You've never listened to this track before. Et ole kuunnellut tätä kappaletta aiemmin. - + You first listened to it on %1. Kuuntelit sitä ensi kerran %1. - + You've listened to %1 %n time(s). Olet kuunnellut artistia %1 %n kerran.Olet kuunnellut artistia %1 %n kertaa. - + You've never listened to %1 before. Et ole kuunnellut artistia %1 aiemmin. @@ -3920,7 +3938,7 @@ anna siellä näytetty PIN-koodi tähän: TrackView - + Sorry, your filter '%1' did not match any results. Valitettavasti suodattimesi ”%1” ei tuottanut yhtään tuloksia. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 79dfb98834..f23af3c85f 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -266,27 +266,27 @@ de se connecter et streamer de vous? AlbumInfoWidget - + Tracklist Liste des pistes - + Other Albums Autres Albums - + Sorry, we could not find any other albums for this artist! Désolé, aucun autre album n'a pu être trouvé pour cet artiste ! - + Sorry, we could not find any tracks for this album! Désolé, nous n'avons pu trouver aucune piste pour cet album ! - + Other Albums by %1 Autres albums de %1 @@ -308,35 +308,40 @@ de se connecter et streamer de vous? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artistes similaires - + Albums Albums - + Sorry, we could not find any albums for this artist! Désolé, on a pas pu trouver aucun album pour cet artiste! - + Sorry, we could not find any related artists! Désolé, on a rien trouvé par rapport a cet artite! - + Sorry, we could not find any top hits for this artist! Désolé, on a pas pu trouver aucun top hit pour cet artiste! + + + CHART # + + AudioControls @@ -1049,7 +1054,7 @@ Password - + Network Activity @@ -1153,48 +1158,48 @@ Password Précision - + Perfect match Correspondance parfaite - + Very good match Très bonne correspondance - + Good match Bonne correspondance - + Vague match Vague correspondance - + Bad match Mauvaise correspondance - + Very bad match Très mauvaise correspondance - + Not available Indisponible - + Searching... Recherche... - + Name Nom @@ -2202,6 +2207,14 @@ Password Supprimer la liste de lecture dans Spotify ? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2712,13 +2725,13 @@ utilisateur@jabber.org Tomahawk::Collection - - + + Collection Collection - + This collection is empty. Cette collection est vide. @@ -3521,43 +3534,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne @@ -3624,7 +3637,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection @@ -3874,37 +3887,42 @@ saisissez le numéro PIN ici : TrackInfoWidget - + Similar Tracks Piste similaire - + Sorry, but we could not find similar tracks for this song! Désolé, nous n'avons pu trouver aucune piste similaire pour cette chanson ! + + + PLAYS + + - + You've listened to this track %n time(s). Vous avez écouté cette piste %n fois.Vous avez écouté cette piste %n fois. - + You've never listened to this track before. Vous n'avez encore jamais écouté cette piste. - + You first listened to it on %1. Vous l'avez écouté pour la première fois le %1. - + You've listened to %1 %n time(s). Vous avez écouté %1 %n fois.Vous avez écouté %1 %n fois. - + You've never listened to %1 before. Vous n'avez encore jamais écouté %1. @@ -3912,7 +3930,7 @@ saisissez le numéro PIN ici : TrackView - + Sorry, your filter '%1' did not match any results. Désolé, votre filtre '%1' ne correspond à aucun résultat. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index d9c0ee7810..dc79af230c 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Lista de reprodución - + Other Albums Outros álbums - + Sorry, we could not find any other albums for this artist! Non se atopou ningún outro álbum para este artista! - + Sorry, we could not find any tracks for this album! Non se puido atopar ningunha outra pista para este álbum! - + Other Albums by %1 Outros álbums de %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Maiores éxitos - + Related Artists Artistas relacionados - + Albums Álbums - + Sorry, we could not find any albums for this artist! Non se atopou ningún álbum para este artista! - + Sorry, we could not find any related artists! Non se atopou ningún artista relacionado! - + Sorry, we could not find any top hits for this artist! Non se atopou ningún éxito deste artista! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia moi boa - + Good match Boa coincidencia - + Vague match Parcialmente coincidente - + Bad match Mala coincidencia - + Very bad match Nada coincidentes - + Not available Non está dispoñíbel - + Searching... - + Name Nome @@ -2203,6 +2208,14 @@ Password Eliminar a lista de reprodución asociada a Spotify? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2714,13 +2727,13 @@ nomedeusuario@jabber.org Tomahawk::Collection - - + + Collection Colección - + This collection is empty. Esta colección está baleira. @@ -3523,43 +3536,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado @@ -3626,7 +3639,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección @@ -3876,37 +3889,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Pistas parecidas - + Sorry, but we could not find similar tracks for this song! Non se atopan pistas parecidas a esta canción! + + + PLAYS + + - + You've listened to this track %n time(s). Escoitaches esta pista %n vece(s).Escoitaches esta pista %n vece(s). - + You've never listened to this track before. Nunca antes escoitaras esta pista. - + You first listened to it on %1. Escoitaches esta pista por primeira vez en %1. - + You've listened to %1 %n time(s). Escoitaches %1 %n veces.Escoitaches %1 %n veces. - + You've never listened to %1 before. Nunca antes escoitaras a %1. @@ -3914,7 +3932,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. O filtro «%1» non dá ningún resultado. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 3c73cec50d..f97d9a9644 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -2200,6 +2205,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2704,13 +2717,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3509,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3612,7 +3625,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3861,37 +3874,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3899,7 +3917,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index a9097b3745..b5e3aa234a 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Zeneszám lista - + Other Albums Egyéb albumok - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Kapcsolódó előadók - + Albums Albumok - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name Név @@ -2200,6 +2205,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2704,13 +2717,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3509,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető @@ -3612,7 +3625,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció @@ -3861,37 +3874,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Hasonló zeneszámok - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3899,7 +3917,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 0fa5d8b528..b70b8f18ab 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -2200,6 +2205,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2704,13 +2717,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3509,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3612,7 +3625,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3861,37 +3874,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3899,7 +3917,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 86c9e3435b..3ff4315115 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Elenco tracce - + Other Albums Altri album - + Sorry, we could not find any other albums for this artist! Siamo spiacenti, non è stato possibile trovare altri album di questo artista! - + Sorry, we could not find any tracks for this album! Ci dispiace, ma non abbiamo trovato nessuna traccia di questo album! - + Other Albums by %1 Altri album di %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artisti simili - + Albums Album - + Sorry, we could not find any albums for this artist! Ci dispiace, ma non abbiamo trovato nessun album di questo artista! - + Sorry, we could not find any related artists! Siamo spiacenti, non è stato possibile trovare artisti simili! - + Sorry, we could not find any top hits for this artist! Ci dispiace, ma non abbiamo trovato alcun risultato top per l'artista! + + + CHART # + + AudioControls @@ -1049,7 +1054,7 @@ temporanea Ultimo anno - + Network Activity Attività network @@ -1153,48 +1158,48 @@ temporanea Precisione - + Perfect match Abbinamento perfetto - + Very good match Abbinamento molto buono - + Good match Buon abbinamento - + Vague match Vaga corrispondenza - + Bad match Brutto abbinamento - + Very bad match Pessimo abbinamento - + Not available Non disponibile - + Searching... Sto cercando... - + Name Nome @@ -2201,6 +2206,14 @@ temporanea Elimina le playlist di Spotify associate? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2705,13 +2718,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Collezione - + This collection is empty. La collezione è vuota. @@ -3511,43 +3524,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso @@ -3614,7 +3627,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione @@ -3863,37 +3876,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Tracce simili - + Sorry, but we could not find similar tracks for this song! Ci dispiace, ma non abbiamo trovato tracce simili per questa canzone! + + + PLAYS + + - + You've listened to this track %n time(s). Hai ascoltato questa traccia una volta.Hai ascoltato questa traccia %n volte. - + You've never listened to this track before. Non hai mai ascoltato questa traccia. - + You first listened to it on %1. L'hai ascoltata la prima volta su %1. - + You've listened to %1 %n time(s). Hai ascoltato %1 una volta.Hai ascoltato %1 %n volte. - + You've never listened to %1 before. Non hai mai ascoltato %1. @@ -3901,7 +3919,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Spiacente, il tuo filtro %1 non ha trovato nessun risultato. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index ee6f335eb3..662819c921 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist トラックリスト - + Other Albums 他のアルバム - + Sorry, we could not find any other albums for this artist! このアーティストのアルバムは他に見つかりませんでした。 - + Sorry, we could not find any tracks for this album! このアルバムの曲は見つかりませんでした。 - + Other Albums by %1 %1の他のアルバム @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 大ヒット曲 - + Related Artists 似たアーティスト - + Albums アルバム - + Sorry, we could not find any albums for this artist! このアーティストのアルバムは見つかりませんでした。 - + Sorry, we could not find any related artists! 関連アーティストは見つかりませんでした。 - + Sorry, we could not find any top hits for this artist! このアーティストの大ヒット曲は見つかりませんでした。 + + + CHART # + + AudioControls @@ -1049,7 +1054,7 @@ Password - + Network Activity @@ -1153,48 +1158,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name 名前 @@ -2205,6 +2210,14 @@ Password 関連付けられたSpotifyのプレイリストも削除しますか? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2712,13 +2725,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3521,43 +3534,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン @@ -3624,7 +3637,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション @@ -3874,37 +3887,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 似ているトラック - + Sorry, but we could not find similar tracks for this song! この曲に似ているトラックが見つかりませんでした。 + + + PLAYS + + - + You've listened to this track %n time(s). このトラックは%n回聴いています。 - + You've never listened to this track before. このトラックを一度も聴いていません。 - + You first listened to it on %1. 初めてこの曲を聴いたのは、%1です。 - + You've listened to %1 %n time(s). %1を%n回聴いています。 - + You've never listened to %1 before. %1を一度も聴いていません。 @@ -3912,7 +3930,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. %1に一致する結果は見つかりませんでした。 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 43f3485866..ac19735c21 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! Atsiprašome, neradome jokių kitų šio atlikėjo albumų! - + Sorry, we could not find any tracks for this album! Atsiprašome, neradome jokių takelių iš šio albumo! - + Other Albums by %1 Kiti %1 albumai @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists Susiję atlikėjai - + Albums Albumai - + Sorry, we could not find any albums for this artist! Atsiprašome, neradome jokių šio atlikėjo albumų! - + Sorry, we could not find any related artists! Atsiprašome, neradome jokių susijusių atlikėjų! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name Vardas @@ -2200,6 +2205,14 @@ Password Pašalinti susietą Spotify grojaraštį? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2704,13 +2717,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3509,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs @@ -3612,7 +3625,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija @@ -3861,37 +3874,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Panašūs takeliai - + Sorry, but we could not find similar tracks for this song! Atsiprašome, neradome jokių į šią dainą panašių takelių! + + + PLAYS + + - + You've listened to this track %n time(s). Jūs klausėtės šio takelio %n kartą.Jūs klausėtės šio takelio %n kartus.Jūs klausėtės šio takelio %n kartų. - + You've never listened to this track before. Jūs niekad anksčiau nesiklausėte šio takelio. - + You first listened to it on %1. Pirmąkart klausėtės jo %1. - + You've listened to %1 %n time(s). Jūs klausėtės %1 %n kartą.Jūs klausėtės %1 %n kartus.Jūs klausėtės %1 %n kartų. - + You've never listened to %1 before. Jūs niekada anksčiau nesiklausėte %1. @@ -3899,7 +3917,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 22c8a456d6..72a7d3306a 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -266,27 +266,27 @@ połączyć się i strumieniować od ciebie? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Inne albumy %1 @@ -308,35 +308,40 @@ połączyć się i strumieniować od ciebie? ArtistInfoWidget - + Top Hits Hity na Topie - + Related Artists Powiązani artyści - + Albums Albumy - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1049,7 +1054,7 @@ Password - + Network Activity @@ -1153,48 +1158,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -2202,6 +2207,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2709,13 +2722,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3518,43 +3531,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline @@ -3621,7 +3634,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja @@ -3871,37 +3884,42 @@ wprowadź pokazany numer PIN tutaj: TrackInfoWidget - + Similar Tracks Podobne utwory - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). Słuchałeś tego utworu %n raz.Słuchałeś tego utworu %n razy.Słuchałeś tego utworu %n razy. - + You've never listened to this track before. Nie słuchałeś wcześniej tego utworu. - + You first listened to it on %1. Pierwszy raz słuchałeś tego utworu %1. - + You've listened to %1 %n time(s). Słuchałeś %1 %n raz.Słuchałeś %1 %n razy.Słuchałeś %1 %n razy. - + You've never listened to %1 before. Nie słuchałeś wcześniej %1. @@ -3909,7 +3927,7 @@ wprowadź pokazany numer PIN tutaj: TrackView - + Sorry, your filter '%1' did not match any results. Przepraszamy, twój filtr '%1' nie dopasował żadnych wyników. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index d93c460996..ec30b95c9d 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -266,27 +266,27 @@ se conecte e faça o stream de você? AlbumInfoWidget - + Tracklist Lista de faixas - + Other Albums Outros álbuns - + Sorry, we could not find any other albums for this artist! Desculpe, mas não conseguimos encontrar outro álbum para este artista! - + Sorry, we could not find any tracks for this album! Desculpe, mas não conseguimos encontrar outras faixas para este álbum! - + Other Albums by %1 Outros álbuns de %1 @@ -308,35 +308,40 @@ se conecte e faça o stream de você? ArtistInfoWidget - + Top Hits Mais Tocadas - + Related Artists Artistas Relacionados - + Albums Álbuns - + Sorry, we could not find any albums for this artist! Desculpe, mas não conseguimos encontrar outros álbuns para este artista! - + Sorry, we could not find any related artists! Desculpe, mas não conseguimos encontrar outros artistas relacionados a este! - + Sorry, we could not find any top hits for this artist! Desculpe, mas não conseguimos encontrar outras faixas mais tocadas deste artista! + + + CHART # + + AudioControls @@ -1049,7 +1054,7 @@ Password - + Network Activity @@ -1153,48 +1158,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name Nome @@ -2202,6 +2207,14 @@ Password Excluir lista de reprodução associada a Spotify? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2709,13 +2722,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3518,43 +3531,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline @@ -3621,7 +3634,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção @@ -3871,37 +3884,42 @@ colocar o número PIN mostrado aqui: TrackInfoWidget - + Similar Tracks Faixas Similares - + Sorry, but we could not find similar tracks for this song! Desculpe, mas não conseguimos encontrar faixas similares para esta música! + + + PLAYS + + - + You've listened to this track %n time(s). Você ouviu esta faixa %n vez.Você ouviu esta faixa %n vezes. - + You've never listened to this track before. Você nunca ouviu esta faixa antes. - + You first listened to it on %1. Você ouviu pela primeira vez em %1. - + You've listened to %1 %n time(s). Você ouviu %1 %n vez.Você ouviu %1 %n vezes. - + You've never listened to %1 before. Você nunca ouviu %1 antes. @@ -3909,7 +3927,7 @@ colocar o número PIN mostrado aqui: TrackView - + Sorry, your filter '%1' did not match any results. Desculpe, o seu filtro '%1' não encontreou nenhum resultado. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 78d3255410..f8f8bfba30 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -269,27 +269,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Список Песен - + Other Albums Другие Альбомы - + Sorry, we could not find any other albums for this artist! К сожалению, мы не смогли найти другие альбомы этого исполнителя! - + Sorry, we could not find any tracks for this album! К сожалению, мы не смогли найти никаких треков для этого альбома! - + Other Albums by %1 Другие альбомы %1 @@ -311,35 +311,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Хиты - + Related Artists Похожие исполнители - + Albums Альбомы - + Sorry, we could not find any albums for this artist! К сожалению, мы не смогли найти никаких альбомов этого исполнителя! - + Sorry, we could not find any related artists! К сожалению, мы не смогли найти никаких исполнители! - + Sorry, we could not find any top hits for this artist! К сожалению, мы не смогли найти никаких хитов этого исполнителя! + + + CHART # + + AudioControls @@ -1052,7 +1057,7 @@ Password - + Network Activity @@ -1156,48 +1161,48 @@ Password Совпадение - + Perfect match Превосходное - + Very good match Очень Хорошое - + Good match Хорошое - + Vague match Расплывчатое - + Bad match Плохое совпадение - + Very bad match Очень плохое совпадение - + Not available Недоступно - + Searching... - + Name Имя @@ -2208,6 +2213,14 @@ Password Удалить связанные плейлисты Spotify? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2718,13 +2731,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Коллекция - + This collection is empty. Коллекция пуста. @@ -3525,43 +3538,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети @@ -3628,7 +3641,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция @@ -3877,37 +3890,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Похожие Песни - + Sorry, but we could not find similar tracks for this song! Извините, но мы не смогли найти похожие на эту песни ! + + + PLAYS + + - + You've listened to this track %n time(s). Вы слушали эту песню %n раз.Вы слушали эту песню %n раз.Вы слушали эту песню %n раз. - + You've never listened to this track before. Вы никогда не слушали эту песню раньше. - + You first listened to it on %1. Первый раз слушали эту песню %1. - + You've listened to %1 %n time(s). Вы слушали %1 %n раз.Вы слушали %1 %n раза.Вы слушали %1 %n раз. - + You've never listened to %1 before. Вы никогда не слушали %1 до этого. @@ -3915,7 +3933,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Ваш поиск '%1' недал результатов. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 1ba58a41ab..6a04555fb4 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -266,27 +266,27 @@ ansluta och strömma från dig? AlbumInfoWidget - + Tracklist Spårlista - + Other Albums Andra album - + Sorry, we could not find any other albums for this artist! Tyvärr! Det gick inte hitta några andra album av denna artisten - + Sorry, we could not find any tracks for this album! Tyvärr! Det gick inte hitta några spår från detta albumet! - + Other Albums by %1 Andra album av %1 @@ -308,35 +308,40 @@ ansluta och strömma från dig? ArtistInfoWidget - + Top Hits Största hits - + Related Artists Relaterade artister - + Albums Album - + Sorry, we could not find any albums for this artist! Tyvärr! Det gick inte hitta några album av denna artisten! - + Sorry, we could not find any related artists! Tyvärr! Det gick inte hitta några relaterade artister! - + Sorry, we could not find any top hits for this artist! Tyvärr! Det gick inte hitta några tophits av denna artisten + + + CHART # + + AudioControls @@ -1050,7 +1055,7 @@ Password - + Network Activity @@ -1154,48 +1159,48 @@ Password Exakthet - + Perfect match Perfekt matchning - + Very good match Mycket bra matchning - + Good match Bra matchning - + Vague match Svag matchning - + Bad match Dålig matchning - + Very bad match Väldigt dålig matchning - + Not available Inte tillgänglig - + Searching... Söker... - + Name Namn @@ -2205,6 +2210,14 @@ och radiostationer baserat på din personliga profil Ta bort alla associerade spotify-spellistor + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2714,13 +2727,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection Samling - + This collection is empty. Denna samlingen är tom @@ -3524,43 +3537,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline @@ -3627,7 +3640,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling @@ -3877,37 +3890,42 @@ anger du PIN-koden här: TrackInfoWidget - + Similar Tracks Liknande spår - + Sorry, but we could not find similar tracks for this song! Tyvärr! Det gick inte hitta några spår som liknande denna låten! + + + PLAYS + + - + You've listened to this track %n time(s). Du har lyssnat på detta spåret %n gånger.Du har lyssnat på detta spåret %n gånger - + You've never listened to this track before. Du har inte lyssnat på detta spåret tidigare. - + You first listened to it on %1. Du har lyssnat på det på %1. - + You've listened to %1 %n time(s). Du har lyssnat på %1 %n gång.Du har lyssnat på %1 %n gånger. - + You've never listened to %1 before. Du har inte lyssnat på %1 tidigare. @@ -3915,7 +3933,7 @@ anger du PIN-koden här: TrackView - + Sorry, your filter '%1' did not match any results. Tyvärr, ditt filter '%1' gav inga träffar diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 9ef3dfcce3..d99fce8ed6 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Diğer %1 Albümleri @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits En Çok Dinlenenler - + Related Artists Benzer Sanatçılar - + Albums Albümler - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -2200,6 +2205,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2704,13 +2717,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3509,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3612,7 +3625,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3861,37 +3874,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3899,7 +3917,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index f1f7126b58..0579cf4543 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums 其他专辑 - + Sorry, we could not find any other albums for this artist! 抱歉,找到此艺术家的其他专辑! - + Sorry, we could not find any tracks for this album! 抱歉,没有找到这张专辑的其他歌曲! - + Other Albums by %1 %1 的其他专辑 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 最热歌曲 - + Related Artists 相关艺人 - + Albums 专辑 - + Sorry, we could not find any albums for this artist! 抱歉,未找到该艺术家的其他专辑! - + Sorry, we could not find any related artists! 抱歉,没有找到相关的艺术家! - + Sorry, we could not find any top hits for this artist! 抱歉,没有找到该艺术家的任何人气歌曲! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password 准确度 - + Perfect match 完美匹配 - + Very good match 极高匹配 - + Good match 高匹配 - + Vague match 模糊匹配 - + Bad match 低匹配 - + Very bad match 极低匹配 - + Not available - + Searching... - + Name 名字 @@ -2203,6 +2208,14 @@ Password 删除关联的 Spontify 播放列表? + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2710,13 +2723,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3519,43 +3532,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 @@ -3622,7 +3635,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3872,37 +3885,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 相似歌曲 - + Sorry, but we could not find similar tracks for this song! 抱歉,无法找到与这首歌类似的歌曲! + + + PLAYS + + - + You've listened to this track %n time(s). 你已经收听过此歌曲 %n 遍。 - + You've never listened to this track before. 你之前从未听过此歌曲。 - + You first listened to it on %1. 你第一次听的是 %1. - + You've listened to %1 %n time(s). 你已经听过 %1 有 %n 遍了。 - + You've never listened to %1 before. 你之前从未听过 %1。 @@ -3910,7 +3928,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. 抱歉,未找到任何匹配 '%1' 的结果。 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 7fb4510d38..b4e3f04265 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 列出所有其他專輯,依 %1 @@ -307,35 +307,40 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 流行精選 - + Related Artists 相關演出者 - + Albums 專輯 - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! + + + CHART # + + AudioControls @@ -1048,7 +1053,7 @@ Password - + Network Activity @@ -1152,48 +1157,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -2200,6 +2205,14 @@ Password + + StatsGauge + + + out of %1 + + + TemporaryPageItem @@ -2704,13 +2717,13 @@ username@jabber.org Tomahawk::Collection - - + + Collection - + This collection is empty. @@ -3509,43 +3522,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3612,7 +3625,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3861,37 +3874,42 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! + + + PLAYS + + - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3899,7 +3917,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. From 42cf9375a65be747ea6420d55224beed00d7f5bc Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 05:03:34 +0200 Subject: [PATCH 251/565] * Don't enforce resolving of incoming Artist's top-tracks. --- src/libtomahawk/ArtistPlaylistInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/ArtistPlaylistInterface.cpp b/src/libtomahawk/ArtistPlaylistInterface.cpp index cc1ae0fb4d..21b0b660d1 100644 --- a/src/libtomahawk/ArtistPlaylistInterface.cpp +++ b/src/libtomahawk/ArtistPlaylistInterface.cpp @@ -160,7 +160,6 @@ ArtistPlaylistInterface::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData r if ( query ) ql << query; } - Pipeline::instance()->resolve( ql ); m_queries << ql; checkQueries(); From ad40c8efa9f1f414b549133f320e786b52f2028f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 05:11:55 +0200 Subject: [PATCH 252/565] * Style clean up. --- src/libtomahawk/ArtistPlaylistInterface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libtomahawk/ArtistPlaylistInterface.cpp b/src/libtomahawk/ArtistPlaylistInterface.cpp index 21b0b660d1..bc901abcbb 100644 --- a/src/libtomahawk/ArtistPlaylistInterface.cpp +++ b/src/libtomahawk/ArtistPlaylistInterface.cpp @@ -25,7 +25,6 @@ #include "database/Database.h" #include "database/DatabaseCommand_AllTracks.h" #include "Source.h" -#include "Pipeline.h" #include "utils/Logger.h" @@ -55,7 +54,7 @@ ArtistPlaylistInterface::setCurrentIndex( qint64 index ) { PlaylistInterface::setCurrentIndex( index ); - m_currentItem = m_queries.at( index )->results().first(); + m_currentItem = m_queries.at( index )->results().first(); } From 6b6475f767b788a1ca3a03725cfc2c3dcf3c40ca Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 05:12:07 +0200 Subject: [PATCH 253/565] * Don't enforce resolving of similar tracks. --- src/libtomahawk/TrackData.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libtomahawk/TrackData.cpp b/src/libtomahawk/TrackData.cpp index c15f5637ee..7d79ff9c90 100644 --- a/src/libtomahawk/TrackData.cpp +++ b/src/libtomahawk/TrackData.cpp @@ -33,7 +33,6 @@ #include "database/IdThreadWorker.h" #include "Album.h" #include "collection/Collection.h" -#include "Pipeline.h" #include "resolvers/Resolver.h" #include "SourceList.h" #include "audio/AudioEngine.h" @@ -482,7 +481,6 @@ TrackData::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV { m_similarTracks << Query::get( artists.at( i ), tracks.at( i ), QString(), uuid(), false ); } - Pipeline::instance()->resolve( m_similarTracks ); m_simTracksLoaded = true; emit similarTracksLoaded(); From e9c4d8118b6aaf6b2744aaf170b96f451d89d061 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 06:06:19 +0200 Subject: [PATCH 254/565] * Renamed and pagified Dashboard. --- src/libtomahawk/CMakeLists.txt | 4 +- .../{WelcomeWidget.cpp => Dashboard.cpp} | 115 ++++++--- .../widgets/{WelcomeWidget.h => Dashboard.h} | 19 +- src/libtomahawk/widgets/Dashboard.ui | 230 ++++++++++++++++++ src/libtomahawk/widgets/WelcomeWidget.ui | 107 -------- 5 files changed, 323 insertions(+), 152 deletions(-) rename src/libtomahawk/widgets/{WelcomeWidget.cpp => Dashboard.cpp} (74%) rename src/libtomahawk/widgets/{WelcomeWidget.h => Dashboard.h} (88%) create mode 100644 src/libtomahawk/widgets/Dashboard.ui delete mode 100644 src/libtomahawk/widgets/WelcomeWidget.ui diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 058a7f9262..eab991cb5f 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -139,7 +139,7 @@ set( libGuiSources widgets/SearchWidget.cpp widgets/SeekSlider.cpp widgets/PlaylistTypeSelectorDialog.cpp - widgets/WelcomeWidget.cpp + widgets/Dashboard.cpp widgets/WhatsHotWidget.cpp widgets/NewReleasesWidget.cpp widgets/ChartDataLoader.cpp @@ -368,7 +368,7 @@ set( libUI ${libUI} widgets/PlaylistTypeSelectorDialog.ui widgets/NewPlaylistWidget.ui widgets/SearchWidget.ui - widgets/WelcomeWidget.ui + widgets/Dashboard.ui widgets/WhatsHotWidget.ui widgets/NewReleasesWidget.ui widgets/SocialPlaylistWidget.ui diff --git a/src/libtomahawk/widgets/WelcomeWidget.cpp b/src/libtomahawk/widgets/Dashboard.cpp similarity index 74% rename from src/libtomahawk/widgets/WelcomeWidget.cpp rename to src/libtomahawk/widgets/Dashboard.cpp index c940279534..1a69def7c8 100644 --- a/src/libtomahawk/widgets/WelcomeWidget.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -18,8 +18,8 @@ * along with Tomahawk. If not, see . */ -#include "WelcomeWidget.h" -#include "ui_WelcomeWidget.h" +#include "Dashboard.h" +#include "ui_Dashboard.h" #include "ViewManager.h" #include "SourceList.h" @@ -39,55 +39,106 @@ #include "utils/Logger.h" #include +#include #define HISTORY_PLAYLIST_ITEMS 10 +#define HISTORY_TRACK_ITEMS 15 using namespace Tomahawk; -WelcomeWidget::WelcomeWidget( QWidget* parent ) +Dashboard::Dashboard( QWidget* parent ) : QWidget( parent ) - , ui( new Ui::WelcomeWidget ) + , ui( new Ui::Dashboard ) { - ui->setupUi( this ); - - ui->splitter_2->setStretchFactor( 0, 3 ); - ui->splitter_2->setStretchFactor( 1, 1 ); - ui->splitter->setChildrenCollapsible( false ); - ui->splitter_2->setChildrenCollapsible( false ); + QWidget* widget = new QWidget; + ui->setupUi( widget ); RecentPlaylistsModel* model = new RecentPlaylistsModel( HISTORY_PLAYLIST_ITEMS, this ); + QPalette trackViewPal = ui->tracksView->palette(); + trackViewPal.setColor( QPalette::Foreground, Qt::white ); + trackViewPal.setColor( QPalette::Text, Qt::white ); + trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); + trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); + ui->playlistWidget->setFrameShape( QFrame::NoFrame ); ui->playlistWidget->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - TomahawkUtils::unmarginLayout( layout() ); - TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() ); - TomahawkUtils::unmarginLayout( ui->verticalLayout_2->layout() ); - TomahawkUtils::unmarginLayout( ui->verticalLayout_3->layout() ); - TomahawkUtils::unmarginLayout( ui->verticalLayout_4->layout() ); - ui->playlistWidget->setItemDelegate( new PlaylistDelegate() ); ui->playlistWidget->setModel( model ); ui->playlistWidget->overlay()->resize( 380, 86 ); ui->playlistWidget->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + ui->playlistWidget->setPalette( trackViewPal ); + ui->playlistWidget->setMinimumHeight( 400 ); updatePlaylists(); - m_tracksModel = new RecentlyPlayedModel( ui->tracksView ); + m_tracksModel = new RecentlyPlayedModel( ui->tracksView, HISTORY_TRACK_ITEMS ); ui->tracksView->proxyModel()->setStyle( PlayableProxyModel::ShortWithAvatars ); ui->tracksView->overlay()->setEnabled( false ); ui->tracksView->setPlaylistModel( m_tracksModel ); + ui->tracksView->setAutoResize( true ); m_tracksModel->setSource( source_ptr() ); + ui->tracksView->setPalette( trackViewPal ); + ui->tracksView->setAlternatingRowColors( false ); + ui->tracksView->setFrameShape( QFrame::NoFrame ); + ui->tracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + m_recentAlbumsModel = new AlbumModel( ui->additionsView ); + ui->additionsView->setPlayableModel( m_recentAlbumsModel ); + ui->additionsView->proxyModel()->sort( -1 ); + + QScrollArea* area = new QScrollArea(); + area->setWidgetResizable( true ); + area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + area->setWidget( widget ); + + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + area->setPalette( pal ); + area->setAutoFillBackground( true ); + area->setFrameShape( QFrame::NoFrame ); + area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget( area ); + setLayout( layout ); + TomahawkUtils::unmarginLayout( layout ); + + TomahawkUtils::styleScrollBar( ui->playlistWidget->verticalScrollBar() ); + TomahawkUtils::styleScrollBar( ui->additionsView->verticalScrollBar() ); + QFont f; f.setBold( true ); QFontMetrics fm( f ); ui->tracksView->setMinimumWidth( fm.width( tr( "Recently played tracks" ) ) * 2 ); - m_recentAlbumsModel = new AlbumModel( ui->additionsView ); - ui->additionsView->setPlayableModel( m_recentAlbumsModel ); - ui->additionsView->proxyModel()->sort( -1 ); + QPalette p = ui->label->palette(); + p.setColor( QPalette::Foreground, Qt::white ); + p.setColor( QPalette::Text, Qt::gray ); + + ui->label->setPalette( p ); + ui->label_2->setPalette( p ); + ui->label_3->setPalette( p ); + + ui->playlistWidget->setStyleSheet( "QListView { background-color: transparent; }" ); + ui->playlistFrame->setStyleSheet( "QFrame#playlistFrame { background-color: transparent; }" + "QFrame#playlistFrame { " + "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" + "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + + ui->additionsView->setStyleSheet( "QListView { background-color: transparent; }" ); + ui->additionsFrame->setStyleSheet( "QFrame#additionsFrame { background-color: transparent; }" + "QFrame#additionsFrame { " + "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" + "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + + ui->tracksView->setStyleSheet( "QTreeView#tracksView { background-color: transparent; }" ); + ui->trackFrame->setStyleSheet( "QFrame#trackFrame { background-color: transparent; }" + "QFrame#trackFrame { " + "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" + "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); MetaPlaylistInterface* mpl = new MetaPlaylistInterface(); mpl->addChildInterface( ui->tracksView->playlistInterface() ); @@ -101,28 +152,28 @@ WelcomeWidget::WelcomeWidget( QWidget* parent ) } -WelcomeWidget::~WelcomeWidget() +Dashboard::~Dashboard() { delete ui; } void -WelcomeWidget::loadData() +Dashboard::loadData() { m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime, true ); } Tomahawk::playlistinterface_ptr -WelcomeWidget::playlistInterface() const +Dashboard::playlistInterface() const { return m_playlistInterface; } bool -WelcomeWidget::jumpToCurrentTrack() +Dashboard::jumpToCurrentTrack() { if ( ui->tracksView->jumpToCurrentTrack() ) return true; @@ -135,7 +186,7 @@ WelcomeWidget::jumpToCurrentTrack() bool -WelcomeWidget::isBeingPlayed() const +Dashboard::isBeingPlayed() const { if ( ui->additionsView->isBeingPlayed() ) return true; @@ -145,7 +196,7 @@ WelcomeWidget::isBeingPlayed() const void -WelcomeWidget::onSourcesReady() +Dashboard::onSourcesReady() { foreach ( const source_ptr& source, SourceList::instance()->sources() ) onSourceAdded( source ); @@ -153,21 +204,21 @@ WelcomeWidget::onSourcesReady() void -WelcomeWidget::onSourceAdded( const Tomahawk::source_ptr& source ) +Dashboard::onSourceAdded( const Tomahawk::source_ptr& source ) { connect( source->dbCollection().data(), SIGNAL( changed() ), SLOT( updateRecentAdditions() ), Qt::UniqueConnection ); } void -WelcomeWidget::updateRecentAdditions() +Dashboard::updateRecentAdditions() { m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime, true ); } void -WelcomeWidget::updatePlaylists() +Dashboard::updatePlaylists() { int num = ui->playlistWidget->model()->rowCount( QModelIndex() ); if ( num == 0 ) @@ -181,7 +232,7 @@ WelcomeWidget::updatePlaylists() void -WelcomeWidget::onPlaylistActivated( const QModelIndex& item ) +Dashboard::onPlaylistActivated( const QModelIndex& item ) { Tomahawk::playlist_ptr pl = item.data( RecentlyPlayedPlaylistsModel::PlaylistRole ).value< Tomahawk::playlist_ptr >(); if( Tomahawk::dynplaylist_ptr dynplaylist = pl.dynamicCast< Tomahawk::DynamicPlaylist >() ) @@ -192,7 +243,7 @@ WelcomeWidget::onPlaylistActivated( const QModelIndex& item ) void -WelcomeWidget::changeEvent( QEvent* e ) +Dashboard::changeEvent( QEvent* e ) { QWidget::changeEvent( e ); switch ( e->type() ) diff --git a/src/libtomahawk/widgets/WelcomeWidget.h b/src/libtomahawk/widgets/Dashboard.h similarity index 88% rename from src/libtomahawk/widgets/WelcomeWidget.h rename to src/libtomahawk/widgets/Dashboard.h index 7e1e19b71d..3083b6f1a2 100644 --- a/src/libtomahawk/widgets/WelcomeWidget.h +++ b/src/libtomahawk/widgets/Dashboard.h @@ -17,8 +17,8 @@ * along with Tomahawk. If not, see . */ -#ifndef WELCOMEWIDGET_H -#define WELCOMEWIDGET_H +#ifndef DASHBOARD_H +#define DASHBOARD_H #include #include @@ -37,11 +37,10 @@ class AlbumModel; class RecentlyPlayedModel; class OverlayWidget; -class WelcomeWidgetInterface; namespace Ui { - class WelcomeWidget; + class Dashboard; } class DLLEXPORT PlaylistDelegate : public QStyledItemDelegate @@ -78,13 +77,13 @@ Q_OBJECT }; -class DLLEXPORT WelcomeWidget : public QWidget, public Tomahawk::ViewPage +class DLLEXPORT Dashboard : public QWidget, public Tomahawk::ViewPage { Q_OBJECT public: - WelcomeWidget( QWidget* parent = 0 ); - virtual ~WelcomeWidget(); + Dashboard( QWidget* parent = 0 ); + virtual ~Dashboard(); virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const; @@ -115,13 +114,11 @@ private slots: void onPlaylistActivated( const QModelIndex& ); private: - Ui::WelcomeWidget *ui; + Ui::Dashboard *ui; RecentlyPlayedModel* m_tracksModel; AlbumModel* m_recentAlbumsModel; Tomahawk::playlistinterface_ptr m_playlistInterface; - - friend class ::WelcomeWidgetInterface; }; -#endif // WELCOMEWIDGET_H +#endif // DASHBOARD_H diff --git a/src/libtomahawk/widgets/Dashboard.ui b/src/libtomahawk/widgets/Dashboard.ui new file mode 100644 index 0000000000..cebdebd935 --- /dev/null +++ b/src/libtomahawk/widgets/Dashboard.ui @@ -0,0 +1,230 @@ + + + Dashboard + + + + 0 + 0 + 965 + 1179 + + + + Form + + + + 16 + + + 12 + + + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 8 + + + 4 + + + 8 + + + 8 + + + + + + 18 + 75 + true + + + + Recently Played Tracks + + + 0 + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + Arial + 18 + 75 + true + + + + Recent Additions + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 190 + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + Arial + 18 + 75 + true + + + + Newest Stations & Playlists + + + 0 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + GridView + QListView +
playlist/GridView.h
+
+ + PlaylistView + QTreeView +
playlist/PlaylistView.h
+
+ + PlaylistWidget + QListWidget +
widgets/Dashboard.h
+
+
+ + +
diff --git a/src/libtomahawk/widgets/WelcomeWidget.ui b/src/libtomahawk/widgets/WelcomeWidget.ui deleted file mode 100644 index f420a1a967..0000000000 --- a/src/libtomahawk/widgets/WelcomeWidget.ui +++ /dev/null @@ -1,107 +0,0 @@ - - - WelcomeWidget - - - - 0 - 0 - 875 - 513 - - - - - - - Qt::Horizontal - - - 1 - - - - Qt::Vertical - - - 1 - - - - - - - Recent Additions - - - - - - - true - - - QAbstractItemView::ExtendedSelection - - - - - - - - - - - Newest Stations & Playlists - - - - - - - - - - - - - - - Recently Played Tracks - - - - - - - - - - - - - - - HeaderLabel - QLabel -
widgets/HeaderLabel.h
-
- - GridView - QListView -
playlist/GridView.h
-
- - PlaylistView - QTreeView -
playlist/PlaylistView.h
-
- - PlaylistWidget - QListWidget -
widgets/WelcomeWidget.h
-
-
- - -
From ee67518d473740e7b6ab116d8639f8096c79db3f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 06:07:07 +0200 Subject: [PATCH 255/565] * WelcomeWidget => Dashboard. --- src/libtomahawk/ViewManager.cpp | 16 ++++++++-------- src/libtomahawk/ViewManager.h | 8 ++++---- src/libtomahawk/widgets/SocialPlaylistWidget.ui | 2 +- src/tomahawk/TomahawkWindow.cpp | 2 +- src/tomahawk/sourcetree/SourcesModel.cpp | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index d7171b2331..f9f7c5d06a 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -46,7 +46,7 @@ #include "playlist/dynamic/widgets/DynamicQmlWidget.h" #include "widgets/NewReleasesWidget.h" -#include "widgets/WelcomeWidget.h" +#include "widgets/Dashboard.h" #include "widgets/WhatsHotWidget.h" #include "widgets/NetworkActivityWidget.h" #include "widgets/infowidgets/SourceInfoWidget.h" @@ -79,7 +79,7 @@ ViewManager::instance() ViewManager::ViewManager( QObject* parent ) : QObject( parent ) , m_widget( new QWidget() ) - , m_welcomeWidget( new WelcomeWidget() ) + , m_dashboard( new Dashboard() ) , m_whatsHotWidget( 0 ) , m_newReleasesWidget( 0 ) , m_recentPlaysWidget( 0 ) @@ -123,7 +123,7 @@ ViewManager::ViewManager( QObject* parent ) connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) ); connect( m_infobar, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) ); - connect( this, SIGNAL( tomahawkLoaded() ), m_welcomeWidget, SLOT( loadData() ) ); + connect( this, SIGNAL( tomahawkLoaded() ), m_dashboard, SLOT( loadData() ) ); /* connect( m_infobar, SIGNAL( flatMode() ), SLOT( setTableMode() ) ); connect( m_infobar, SIGNAL( artistMode() ), SLOT( setTreeMode() ) ); @@ -136,7 +136,7 @@ ViewManager::~ViewManager() delete m_networkActivityWidget; delete m_whatsHotWidget; delete m_newReleasesWidget; - delete m_welcomeWidget; + delete m_dashboard; delete m_recentPlaysWidget; delete m_inboxWidget; delete m_contextWidget; @@ -421,9 +421,9 @@ ViewManager::playlistInterfaceChanged( Tomahawk::playlistinterface_ptr interface Tomahawk::ViewPage* -ViewManager::showWelcomePage() +ViewManager::showDashboard() { - return show( m_welcomeWidget ); + return show( m_dashboard ); } @@ -882,9 +882,9 @@ ViewManager::showCurrentTrack() Tomahawk::ViewPage* -ViewManager::welcomeWidget() const +ViewManager::dashboard() const { - return m_welcomeWidget; + return m_dashboard; } diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 7bb713d2b0..093eec3e9f 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -52,7 +52,7 @@ class SourceInfoWidget; class InfoBar; class TrackInfoWidget; class NewReleasesWidget; -class WelcomeWidget; +class Dashboard; class WhatsHotWidget; class QPushButton; class InboxModel; @@ -89,7 +89,7 @@ Q_OBJECT Tomahawk::ViewPage* show( Tomahawk::ViewPage* page ); - Tomahawk::ViewPage* welcomeWidget() const; + Tomahawk::ViewPage* dashboard() const; Tomahawk::ViewPage* whatsHotWidget() const; Tomahawk::ViewPage* newReleasesWidget() const; Tomahawk::ViewPage* recentPlaysWidget() const; @@ -137,7 +137,7 @@ Q_OBJECT public slots: Tomahawk::ViewPage* showRadioPage(); Tomahawk::ViewPage* showSuperCollection(); - Tomahawk::ViewPage* showWelcomePage(); + Tomahawk::ViewPage* showDashboard(); Tomahawk::ViewPage* showWhatsHotPage(); Tomahawk::ViewPage* showNewReleasesPage(); Tomahawk::ViewPage* showRecentPlaysPage(); @@ -190,7 +190,7 @@ private slots: TreeModel* m_superCollectionModel; TreeWidget* m_superCollectionView; QueueView* m_queue; - WelcomeWidget* m_welcomeWidget; + Dashboard* m_dashboard; WhatsHotWidget* m_whatsHotWidget; NewReleasesWidget* m_newReleasesWidget; Tomahawk::ViewPage* m_recentPlaysWidget; diff --git a/src/libtomahawk/widgets/SocialPlaylistWidget.ui b/src/libtomahawk/widgets/SocialPlaylistWidget.ui index 1dfc5327b1..6c0e91f24f 100644 --- a/src/libtomahawk/widgets/SocialPlaylistWidget.ui +++ b/src/libtomahawk/widgets/SocialPlaylistWidget.ui @@ -99,7 +99,7 @@ PlaylistWidget QListWidget -
widgets/WelcomeWidget.h
+
widgets/Dashboard.h
diff --git a/src/tomahawk/TomahawkWindow.cpp b/src/tomahawk/TomahawkWindow.cpp index 631715f6fc..ee7b473a08 100644 --- a/src/tomahawk/TomahawkWindow.cpp +++ b/src/tomahawk/TomahawkWindow.cpp @@ -149,7 +149,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) audioStopped(); vm->setQueue( m_queueView ); - vm->showWelcomePage(); + vm->showDashboard(); if ( TomahawkSettings::instance()->fullscreenEnabled() ) { diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index a0112ba09c..b54a446faa 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -298,8 +298,8 @@ SourcesModel::appendGroups() m_myMusicGroup = new GroupItem( this, m_rootItem, tr( "My Music" ), 3 ); GenericPageItem* dashboard = new GenericPageItem( this, browse, tr( "Dashboard" ), ImageRegistry::instance()->icon( RESPATH "images/dashboard.svg" ), - boost::bind( &ViewManager::showWelcomePage, ViewManager::instance() ), - boost::bind( &ViewManager::welcomeWidget, ViewManager::instance() ) ); + boost::bind( &ViewManager::showDashboard, ViewManager::instance() ), + boost::bind( &ViewManager::dashboard, ViewManager::instance() ) ); dashboard->setSortValue( 0 ); // super collection From 371b8cff0eebf16140fb540b40bd8a08a6710743 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 06:07:54 +0200 Subject: [PATCH 256/565] * Style for the vertical scrollbars. --- src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp | 2 +- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index c4fe1b9fab..f6fb36a1ca 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -119,7 +119,7 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par TomahawkUtils::unmarginLayout( layout ); TomahawkUtils::styleScrollBar( ui->tracks->horizontalScrollBar() ); - TomahawkUtils::styleScrollBar( ui->albums->horizontalScrollBar() ); + TomahawkUtils::styleScrollBar( ui->albums->verticalScrollBar() ); ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 0c359d33af..8017368d7d 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -57,11 +57,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* artist->loadStats(); connect( artist.data(), SIGNAL( statsLoaded() ), SLOT( onArtistStatsLoaded() ) ); -/* TomahawkUtils::unmarginLayout( ui->layoutWidget->layout() ); - TomahawkUtils::unmarginLayout( ui->layoutWidget1->layout() ); - TomahawkUtils::unmarginLayout( ui->layoutWidget2->layout() ); - TomahawkUtils::unmarginLayout( ui->albumHeader->layout() );*/ - m_albumsModel = new PlayableModel( ui->albums ); ui->albums->setPlayableModel( m_albumsModel ); ui->albums->setEmptyTip( tr( "Sorry, we could not find any albums for this artist!" ) ); @@ -99,7 +94,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* trackViewPal.setColor( QPalette::Text, Qt::white ); trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); - ui->topHits->setPalette( trackViewPal ); ui->topHits->setAlternatingRowColors( false ); ui->topHits->setFrameShape( QFrame::NoFrame ); @@ -155,7 +149,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* TomahawkUtils::unmarginLayout( layout ); TomahawkUtils::styleScrollBar( ui->albums->horizontalScrollBar() ); - TomahawkUtils::styleScrollBar( ui->relatedArtists->horizontalScrollBar() ); + TomahawkUtils::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); ui->biography->document()->setDefaultStyleSheet( "a { text-decoration: none; font-weight: bold; color: #ffffff; }" ); From 6b7dbedc45797d9e4352e2a129f5b195895d0394 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 06:08:23 +0200 Subject: [PATCH 257/565] * Extra (optional) CTOR param for RecentlyPlayedModel: maxItems. --- src/libtomahawk/playlist/RecentlyPlayedModel.cpp | 4 ++-- src/libtomahawk/playlist/RecentlyPlayedModel.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/RecentlyPlayedModel.cpp b/src/libtomahawk/playlist/RecentlyPlayedModel.cpp index 29ab65beb9..0336790fb8 100644 --- a/src/libtomahawk/playlist/RecentlyPlayedModel.cpp +++ b/src/libtomahawk/playlist/RecentlyPlayedModel.cpp @@ -34,9 +34,9 @@ using namespace Tomahawk; -RecentlyPlayedModel::RecentlyPlayedModel( QObject* parent ) +RecentlyPlayedModel::RecentlyPlayedModel( QObject* parent, unsigned int maxItems ) : PlaylistModel( parent ) - , m_limit( HISTORY_TRACK_ITEMS ) + , m_limit( maxItems > 0 ? maxItems : HISTORY_TRACK_ITEMS ) { } diff --git a/src/libtomahawk/playlist/RecentlyPlayedModel.h b/src/libtomahawk/playlist/RecentlyPlayedModel.h index 35c411839f..c3a66eae1c 100644 --- a/src/libtomahawk/playlist/RecentlyPlayedModel.h +++ b/src/libtomahawk/playlist/RecentlyPlayedModel.h @@ -32,7 +32,7 @@ class DLLEXPORT RecentlyPlayedModel : public PlaylistModel Q_OBJECT public: - explicit RecentlyPlayedModel( QObject* parent = 0 ); + explicit RecentlyPlayedModel( QObject* parent = 0, unsigned int maxItems = 0 ); ~RecentlyPlayedModel(); unsigned int limit() const { return m_limit; } From 8a4caa411839ac942ff71547bf05fc0611b0c50f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:17:23 +0200 Subject: [PATCH 258/565] * Emit a signal when the Pipeline gets started. --- src/libtomahawk/Pipeline.cpp | 1 + src/libtomahawk/Pipeline.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index 8ba82c2f34..5e4776438e 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -95,6 +95,7 @@ Pipeline::start() { tDebug() << Q_FUNC_INFO << "Shunting" << m_queries_pending.size() << "queries!"; m_running = true; + emit running(); shuntNext(); } diff --git a/src/libtomahawk/Pipeline.h b/src/libtomahawk/Pipeline.h index 14ff448827..fdd1dbe5d4 100644 --- a/src/libtomahawk/Pipeline.h +++ b/src/libtomahawk/Pipeline.h @@ -82,6 +82,7 @@ public slots: void databaseReady(); signals: + void running(); void idle(); void resolving( const Tomahawk::query_ptr& query ); From 4939ffb6d39555b4e2a7b202c853447d32a5a47c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:17:53 +0200 Subject: [PATCH 259/565] * FlexibleView now can be initialized with an additional header widget. --- src/libtomahawk/playlist/FlexibleView.cpp | 4 +++- src/libtomahawk/playlist/FlexibleView.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/FlexibleView.cpp b/src/libtomahawk/playlist/FlexibleView.cpp index 7f209ce51e..76622c4153 100644 --- a/src/libtomahawk/playlist/FlexibleView.cpp +++ b/src/libtomahawk/playlist/FlexibleView.cpp @@ -35,7 +35,7 @@ using namespace Tomahawk; -FlexibleView::FlexibleView( QWidget* parent ) +FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) : QWidget( parent ) , m_header( new FlexibleHeader( this ) ) , m_trackView( new TrackView() ) @@ -63,6 +63,8 @@ FlexibleView::FlexibleView( QWidget* parent ) TomahawkUtils::unmarginLayout( layout() ); layout()->addWidget( m_header ); + if ( extraHeader ) + layout()->addWidget( extraHeader ); layout()->addWidget( m_stack ); m_stack->addWidget( m_trackView ); diff --git a/src/libtomahawk/playlist/FlexibleView.h b/src/libtomahawk/playlist/FlexibleView.h index 6bf782bf99..9a4abaf4e7 100644 --- a/src/libtomahawk/playlist/FlexibleView.h +++ b/src/libtomahawk/playlist/FlexibleView.h @@ -39,7 +39,7 @@ Q_OBJECT enum FlexibleViewMode { Flat = 0, Detailed = 1, Grid = 2 }; - explicit FlexibleView( QWidget* parent = 0 ); + explicit FlexibleView( QWidget* parent = 0, QWidget* extraHeader = 0 ); ~FlexibleView(); virtual QWidget* widget() { return this; } From ff0c3d4bef96cd9587d32d0a4bfda2cd90dc8604 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:19:01 +0200 Subject: [PATCH 260/565] * QueueView waits for the Pipeline to become ready. --- src/libtomahawk/playlist/QueueView.cpp | 10 +++++++++- src/libtomahawk/playlist/QueueView.h | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/QueueView.cpp b/src/libtomahawk/playlist/QueueView.cpp index 004dfe9efc..32cf3caf59 100644 --- a/src/libtomahawk/playlist/QueueView.cpp +++ b/src/libtomahawk/playlist/QueueView.cpp @@ -24,6 +24,7 @@ #include "widgets/HeaderLabel.h" #include "playlist/QueueProxyModel.h" #include "utils/Logger.h" +#include "Pipeline.h" #include "PlaylistView.h" #include "Source.h" #include "TomahawkSettings.h" @@ -67,7 +68,14 @@ QueueView::QueueView( AnimatedSplitter* parent ) // Set initial state onHidden( this, false ); - restoreState(); + if ( Pipeline::instance()->isRunning() ) + { + restoreState(); + } + else + { + connect( Pipeline::instance(), SIGNAL( running() ), SLOT( restoreState() ) ); + } } diff --git a/src/libtomahawk/playlist/QueueView.h b/src/libtomahawk/playlist/QueueView.h index 4988c81b4d..91e61c4c88 100644 --- a/src/libtomahawk/playlist/QueueView.h +++ b/src/libtomahawk/playlist/QueueView.h @@ -61,9 +61,10 @@ private slots: void updateLabel(); void onAnimationFinished(); + void restoreState(); + private: void saveState(); - void restoreState(); Ui::QueueView* ui; QTimer* m_dragTimer; From 6b36f3a737fb704a72c7b2a2fb06738dd55d1ce3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:19:35 +0200 Subject: [PATCH 261/565] * PlayableModel::ensureResolved() passes queries in one batch to the Pipeline now. --- src/libtomahawk/playlist/PlayableModel.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 391b8c1641..a1365e49bb 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -746,13 +746,16 @@ PlayableModel::onPlaybackStopped() void PlayableModel::ensureResolved() { - for( int i = 0; i < rowCount( QModelIndex() ); i++ ) + QList< query_ptr > ql; + for ( int i = 0; i < rowCount( QModelIndex() ); i++ ) { query_ptr query = itemFromIndex( index( i, 0, QModelIndex() ) )->query(); if ( !query->resolvingFinished() ) - Pipeline::instance()->resolve( query ); + ql << query; } + + Pipeline::instance()->resolve( ql ); } From c4f48abf7756176bfa782ea4633e4414d548e38b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:20:01 +0200 Subject: [PATCH 262/565] * DbCmd_PlaybackHistory can now deal with date ranges. --- .../database/DatabaseCommand_PlaybackHistory.cpp | 10 ++++++++-- .../database/DatabaseCommand_PlaybackHistory.h | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp b/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp index fd5695997a..51188e8611 100644 --- a/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp +++ b/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.cpp @@ -30,11 +30,17 @@ void DatabaseCommand_PlaybackHistory::exec( DatabaseImpl* dbi ) { TomahawkSqlQuery query = dbi->newquery(); - QString whereToken; + QString whereToken( "WHERE 1" ); if ( !source().isNull() ) { - whereToken = QString( "WHERE source %1" ).arg( source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( source()->id() ) ); + whereToken += QString( " AND source %1" ).arg( source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( source()->id() ) ); + } + if ( m_dateFrom.year() > 1900 && m_dateTo.year() > 1900 ) + { + whereToken += QString( " AND playtime >= %1 AND playtime <= %2" ) + .arg( QDateTime( m_dateFrom ).toUTC().toTime_t() ) + .arg( QDateTime( m_dateTo.addDays( 1 ) ).toUTC().toTime_t() ); } QString sql = QString( diff --git a/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.h b/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.h index 94d1949696..2896814971 100644 --- a/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.h +++ b/src/libtomahawk/database/DatabaseCommand_PlaybackHistory.h @@ -44,12 +44,16 @@ Q_OBJECT virtual QString commandname() const { return "playbackhistory"; } void setLimit( unsigned int amount ) { m_amount = amount; } + void setDateFrom( const QDate& date ) { m_dateFrom = date; } + void setDateTo( const QDate& date ) { m_dateTo = date; } signals: void tracks( const QList& tracks, QList logs ); private: unsigned int m_amount; + QDate m_dateFrom; + QDate m_dateTo; }; #endif // DATABASECOMMAND_PLAYBACKHISTORY_H From b4ccf7d6ea2ede10e3a4163eb7f404a2b134bc1c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:20:24 +0200 Subject: [PATCH 263/565] * RecentlyPlayedModel can now deal with date ranges. --- .../playlist/RecentlyPlayedModel.cpp | 17 +++++++++++++++++ src/libtomahawk/playlist/RecentlyPlayedModel.h | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/src/libtomahawk/playlist/RecentlyPlayedModel.cpp b/src/libtomahawk/playlist/RecentlyPlayedModel.cpp index 0336790fb8..3f63b83a37 100644 --- a/src/libtomahawk/playlist/RecentlyPlayedModel.cpp +++ b/src/libtomahawk/playlist/RecentlyPlayedModel.cpp @@ -56,6 +56,8 @@ RecentlyPlayedModel::loadHistory() startLoading(); DatabaseCommand_PlaybackHistory* cmd = new DatabaseCommand_PlaybackHistory( m_source ); + cmd->setDateFrom( m_dateFrom ); + cmd->setDateTo( m_dateTo ); cmd->setLimit( m_limit ); connect( cmd, SIGNAL( tracks( QList, QList ) ), @@ -150,3 +152,18 @@ RecentlyPlayedModel::isTemporary() const { return true; } + + +void +RecentlyPlayedModel::setDateFrom( const QDate& date ) +{ + m_dateFrom = date; +} + + +void +RecentlyPlayedModel::setDateTo( const QDate& date ) +{ + m_dateTo = date; + loadHistory(); +} diff --git a/src/libtomahawk/playlist/RecentlyPlayedModel.h b/src/libtomahawk/playlist/RecentlyPlayedModel.h index c3a66eae1c..1ac3d6a053 100644 --- a/src/libtomahawk/playlist/RecentlyPlayedModel.h +++ b/src/libtomahawk/playlist/RecentlyPlayedModel.h @@ -20,6 +20,7 @@ #define RECENTLYPLAYEDMODEL_H #include +#include #include #include "Typedefs.h" @@ -42,6 +43,8 @@ Q_OBJECT public slots: void setSource( const Tomahawk::source_ptr& source ); + void setDateFrom( const QDate& date ); + void setDateTo( const QDate& date ); private slots: void onSourcesReady(); @@ -53,6 +56,8 @@ private slots: private: Tomahawk::source_ptr m_source; unsigned int m_limit; + QDate m_dateFrom; + QDate m_dateTo; }; #endif // RECENTLYPLAYEDMODEL_H From d7629ff626d5692501cf019dbe8f669bfd5e2a9c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:21:06 +0200 Subject: [PATCH 264/565] * HistoryWidget is a FlexibleView with date range input fields as an extra header. --- src/libtomahawk/widgets/HistoryWidget.cpp | 99 +++++++++++++++++++++++ src/libtomahawk/widgets/HistoryWidget.h | 54 +++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 src/libtomahawk/widgets/HistoryWidget.cpp create mode 100644 src/libtomahawk/widgets/HistoryWidget.h diff --git a/src/libtomahawk/widgets/HistoryWidget.cpp b/src/libtomahawk/widgets/HistoryWidget.cpp new file mode 100644 index 0000000000..dff8ccf5f2 --- /dev/null +++ b/src/libtomahawk/widgets/HistoryWidget.cpp @@ -0,0 +1,99 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "HistoryWidget.h" + +#include "ViewManager.h" +#include "SourceList.h" +#include "TomahawkSettings.h" +#include "MetaPlaylistInterface.h" + +#include "playlist/RecentlyPlayedModel.h" +#include "playlist/TrackView.h" +#include "playlist/PlaylistLargeItemDelegate.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +#include +#include +#include + +using namespace Tomahawk; + +HistoryWidget::HistoryWidget( const source_ptr& source, QWidget* parent ) + : FlexibleView( parent, m_header = new QWidget() ) +{ + m_header->setMaximumHeight( 160 ); +/* QCalendarWidget* m_calendarFrom = new QCalendarWidget(); + QCalendarWidget* m_calendarTo = new QCalendarWidget(); + m_calendarFrom->setGridVisible( false ); + m_calendarTo->setGridVisible( false );*/ + m_calendarFrom = new QDateEdit( QDate::currentDate() ); + m_calendarTo = new QDateEdit( QDate::currentDate() ); + m_calendarFrom->setDisplayFormat( "yyyy MMMM dd" ); + m_calendarTo->setDisplayFormat( "yyyy MMMM dd" ); + + QHBoxLayout* layout = new QHBoxLayout( m_header ); + layout->addWidget( m_calendarFrom ); + layout->addWidget( m_calendarTo ); + m_header->setLayout( layout ); + + setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::RecentlyPlayed ) ); + + m_model = new RecentlyPlayedModel( this ); + m_model->setTitle( tr( "Recently Played Tracks" ) ); + + if ( source->isLocal() ) + m_model->setDescription( tr( "Your recently played tracks" ) ); + else + m_model->setDescription( tr( "%1's recently played tracks" ).arg( source->friendlyName() ) ); + + PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::RecentlyPlayed, trackView(), trackView()->proxyModel() ); + trackView()->setItemDelegate( del ); + + setPlayableModel( m_model ); + setEmptyTip( tr( "Sorry, we could not find any recent plays!" ) ); + m_model->setSource( source ); + + setGuid( QString( "recentplays/%1" ).arg( source->nodeId() ) ); + +/* connect( m_calendarFrom, SIGNAL( clicked( QDate ) ), SLOT( onDateClicked( QDate ) ) ); + connect( m_calendarTo, SIGNAL( clicked( QDate ) ), SLOT( onDateClicked( QDate ) ) );*/ + connect( m_calendarFrom, SIGNAL( dateChanged( QDate ) ), SLOT( onDateClicked( QDate ) ) ); + connect( m_calendarTo, SIGNAL( dateChanged( QDate ) ), SLOT( onDateClicked( QDate ) ) ); +} + + +HistoryWidget::~HistoryWidget() +{ +} + + +void +HistoryWidget::onDateClicked( const QDate& date ) +{ + QDateEdit* cw = qobject_cast< QDateEdit* >( sender() ); + if ( cw == m_calendarFrom ) + { + m_calendarTo->setDate( date ); + } + + m_model->setLimit( 0 ); + m_model->setDateFrom( m_calendarFrom->date() ); + m_model->setDateTo( m_calendarTo->date() ); +} diff --git a/src/libtomahawk/widgets/HistoryWidget.h b/src/libtomahawk/widgets/HistoryWidget.h new file mode 100644 index 0000000000..f3fa5d6743 --- /dev/null +++ b/src/libtomahawk/widgets/HistoryWidget.h @@ -0,0 +1,54 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef HISTORY_WIDGET_H +#define HISTORY_WIDGET_H + +#include "playlist/FlexibleView.h" +#include "Query.h" +#include "Source.h" +#include "DllMacro.h" + +class QCalendarWidget; +class QDateEdit; +class RecentlyPlayedModel; + +class DLLEXPORT HistoryWidget : public FlexibleView +{ +Q_OBJECT + +public: + explicit HistoryWidget( const Tomahawk::source_ptr& source, QWidget* parent = 0 ); + virtual ~HistoryWidget(); + +signals: + +private slots: + void onDateClicked( const QDate& date ); + +private: + QWidget* m_header; + RecentlyPlayedModel* m_model; + +/* QCalendarWidget* m_calendarFrom; + QCalendarWidget* m_calendarTo;*/ + QDateEdit* m_calendarFrom; + QDateEdit* m_calendarTo; +}; + +#endif // HISTORY_WIDGET_H From 7cd6af4933f874e7754934bbf0248810a10776e6 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:21:22 +0200 Subject: [PATCH 265/565] * Forgot to add HistoryWidget to CMakeLists.txt. --- src/libtomahawk/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index eab991cb5f..9159091dbc 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -140,6 +140,7 @@ set( libGuiSources widgets/SeekSlider.cpp widgets/PlaylistTypeSelectorDialog.cpp widgets/Dashboard.cpp + widgets/HistoryWidget.cpp widgets/WhatsHotWidget.cpp widgets/NewReleasesWidget.cpp widgets/ChartDataLoader.cpp From 45cc59501057057949274c39aeda8a99b749d029 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:21:44 +0200 Subject: [PATCH 266/565] * SourceItem now uses HistoryWidget to display recently played tracks of Sources. --- src/tomahawk/sourcetree/items/SourceItem.cpp | 27 +++----------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/tomahawk/sourcetree/items/SourceItem.cpp b/src/tomahawk/sourcetree/items/SourceItem.cpp index f68738e738..4c48f68313 100644 --- a/src/tomahawk/sourcetree/items/SourceItem.cpp +++ b/src/tomahawk/sourcetree/items/SourceItem.cpp @@ -39,6 +39,7 @@ #include "playlist/PlaylistLargeItemDelegate.h" #include "sip/PeerInfo.h" #include "sip/SipPlugin.h" +#include "widgets/HistoryWidget.h" #include "utils/ImageRegistry.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -62,10 +63,8 @@ SourceItem::SourceItem( SourcesModel* mdl, SourceTreeItem* parent, const Tomahaw , m_recentPlaysPage( 0 ) , m_whatsHotPage( 0 ) { - if ( m_source.isNull() ) - { + if ( !m_source ) return; - } connect( source.data(), SIGNAL( collectionAdded( Tomahawk::collection_ptr ) ), SLOT( onCollectionAdded( Tomahawk::collection_ptr ) ) ); @@ -642,27 +641,7 @@ SourceItem::recentPlaysClicked() { if ( !m_recentPlaysPage ) { - FlexibleView* pv = new FlexibleView( ViewManager::instance()->widget() ); - pv->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::RecentlyPlayed ) ); - - RecentlyPlayedModel* raModel = new RecentlyPlayedModel( pv ); - raModel->setTitle( tr( "Recently Played Tracks" ) ); - - if ( m_source->isLocal() ) - raModel->setDescription( tr( "Your recently played tracks" ) ); - else - raModel->setDescription( tr( "%1's recently played tracks" ).arg( m_source->friendlyName() ) ); - - PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::RecentlyPlayed, pv->trackView(), pv->trackView()->proxyModel() ); - pv->trackView()->setItemDelegate( del ); - - pv->setPlayableModel( raModel ); - pv->setEmptyTip( tr( "Sorry, we could not find any recent plays!" ) ); - raModel->setSource( m_source ); - - pv->setGuid( QString( "recentplays/%1" ).arg( m_source->nodeId() ) ); - - m_recentPlaysPage = pv; + m_recentPlaysPage = new HistoryWidget( m_source, ViewManager::instance()->widget() ); } ViewManager::instance()->show( m_recentPlaysPage ); From 408d4486d23044e2ed8417b8098f9d89b278417c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 10:21:58 +0200 Subject: [PATCH 267/565] * Slightly different tooltips for the gauges. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 2 +- src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 8017368d7d..ec90ea8595 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -101,7 +101,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); - m_playStatsGauge->setText( tr( "CHART #" ) ); + m_playStatsGauge->setText( tr( "# IN YOUR CHARTS" ) ); m_playStatsGauge->setInvertedAppearance( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index dd354ca2bb..65454c24d0 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -94,7 +94,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); - m_playStatsGauge->setText( tr( "PLAYS" ) ); + m_playStatsGauge->setText( tr( "# PLAYS / ARTIST" ) ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); l->addWidget( m_playStatsGauge ); From 9a9fac59e5c4d03318fee61e425e845fd1f9919b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 11:04:18 +0200 Subject: [PATCH 268/565] * Get rid of the header gradient. --- src/libtomahawk/infobar/InfoBar.cpp | 10 ++++++---- src/libtomahawk/widgets/BasicHeader.cpp | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/infobar/InfoBar.cpp b/src/libtomahawk/infobar/InfoBar.cpp index adcbaf15c3..b0acbdf465 100644 --- a/src/libtomahawk/infobar/InfoBar.cpp +++ b/src/libtomahawk/infobar/InfoBar.cpp @@ -90,13 +90,15 @@ InfoBar::InfoBar( QWidget* parent ) m_searchWidget = new QSearchField( this ); m_searchWidget->setPlaceholderText( tr( "Filter..." ) ); - m_searchWidget->setMinimumWidth( 180 ); + m_searchWidget->setMinimumWidth( 220 ); connect( m_searchWidget, SIGNAL( textChanged( QString ) ), this, SLOT( onFilterEdited() ) ); ui->horizontalLayout->addWidget( m_searchWidget ); - QPalette pal = palette(); + QPalette pal = m_whitePal; + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + setAutoFillBackground( true ); setPalette( pal ); setFixedHeight( 80 ); @@ -288,7 +290,7 @@ InfoBar::paintEvent( QPaintEvent* event ) { QWidget::paintEvent( event ); - QPainter painter( this ); +/* QPainter painter( this ); painter.setRenderHint( QPainter::Antialiasing ); QLinearGradient gradient( QPoint( 0, 0 ), QPoint( 0, 1 ) ); @@ -297,7 +299,7 @@ InfoBar::paintEvent( QPaintEvent* event ) gradient.setColorAt( 1.0, TomahawkStyle::HEADER_UPPER ); painter.setBrush( gradient ); - painter.fillRect( rect(), gradient ); + painter.fillRect( rect(), gradient );*/ } diff --git a/src/libtomahawk/widgets/BasicHeader.cpp b/src/libtomahawk/widgets/BasicHeader.cpp index b052040ea3..f689acafb1 100644 --- a/src/libtomahawk/widgets/BasicHeader.cpp +++ b/src/libtomahawk/widgets/BasicHeader.cpp @@ -58,6 +58,7 @@ BasicHeader::BasicHeader( QWidget* parent ) QPalette pal = palette(); pal.setColor( QPalette::Foreground, Qt::white ); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); m_captionLabel->setPalette( pal ); m_descriptionLabel->setPalette( pal ); @@ -90,6 +91,7 @@ BasicHeader::BasicHeader( QWidget* parent ) setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); setFixedHeight( 80 ); + setAutoFillBackground( true ); setPalette( pal ); } @@ -125,7 +127,7 @@ BasicHeader::paintEvent( QPaintEvent* event ) { QWidget::paintEvent( event ); - QPainter painter( this ); +/* QPainter painter( this ); painter.setRenderHint( QPainter::Antialiasing ); QLinearGradient gradient( QPoint( 0, 0 ), QPoint( 0, 1 ) ); @@ -134,5 +136,5 @@ BasicHeader::paintEvent( QPaintEvent* event ) gradient.setColorAt( 1.0, TomahawkStyle::HEADER_UPPER ); painter.setBrush( gradient ); - painter.fillRect( rect(), gradient ); + painter.fillRect( rect(), gradient );*/ } From 51e7fb4c183b1158581634f2f2b01d5744e71356 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 11:04:33 +0200 Subject: [PATCH 269/565] * Style the extra header a bit. --- src/libtomahawk/widgets/HistoryWidget.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libtomahawk/widgets/HistoryWidget.cpp b/src/libtomahawk/widgets/HistoryWidget.cpp index dff8ccf5f2..ac7bdc905e 100644 --- a/src/libtomahawk/widgets/HistoryWidget.cpp +++ b/src/libtomahawk/widgets/HistoryWidget.cpp @@ -26,11 +26,13 @@ #include "playlist/RecentlyPlayedModel.h" #include "playlist/TrackView.h" #include "playlist/PlaylistLargeItemDelegate.h" +#include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include #include +#include #include using namespace Tomahawk; @@ -48,9 +50,26 @@ HistoryWidget::HistoryWidget( const source_ptr& source, QWidget* parent ) m_calendarFrom->setDisplayFormat( "yyyy MMMM dd" ); m_calendarTo->setDisplayFormat( "yyyy MMMM dd" ); + // setting an empty style-sheet prevents the QDateEdits from adopting their parent's QPalette + QString calSheet = QString( "QDateEdit { }" ).arg( TomahawkStyle::PAGE_BACKGROUND.name() ); + m_calendarFrom->setStyleSheet( calSheet ); + m_calendarTo->setStyleSheet( calSheet ); + + QPalette pal = m_header->palette(); + pal.setColor( QPalette::Foreground, Qt::white ); + pal.setColor( QPalette::Text, Qt::white ); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + m_header->setPalette( pal ); + m_header->setAutoFillBackground( true ); + QHBoxLayout* layout = new QHBoxLayout( m_header ); + layout->addSpacerItem( new QSpacerItem( 1, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum ) ); + layout->addWidget( new QLabel( tr( "From:" ) ) ); layout->addWidget( m_calendarFrom ); + layout->addSpacerItem( new QSpacerItem( 16, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ) ); + layout->addWidget( new QLabel( tr( "To:" ) ) ); layout->addWidget( m_calendarTo ); + layout->addSpacerItem( new QSpacerItem( 1, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum ) ); m_header->setLayout( layout ); setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::RecentlyPlayed ) ); From edd3759126207018afcc9d8eaf5737804d0c267f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 11:47:25 +0200 Subject: [PATCH 270/565] * Correctly set initial alternating-row-color setting, so we don't rely on the proxymodel activating it for us on filling. --- src/libtomahawk/playlist/TrackView.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/playlist/TrackView.cpp b/src/libtomahawk/playlist/TrackView.cpp index fc2766e595..e9ca9a1205 100644 --- a/src/libtomahawk/playlist/TrackView.cpp +++ b/src/libtomahawk/playlist/TrackView.cpp @@ -75,6 +75,7 @@ TrackView::TrackView( QWidget* parent ) setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); setRootIsDecorated( false ); setUniformRowHeights( true ); + setAlternatingRowColors( true ); setAutoResize( false ); setHeader( m_header ); From 9d92e2689c85cad5ff8323c206c0e560abd2dca2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 12:07:10 +0200 Subject: [PATCH 271/565] * Unspectacular performance improvement. --- src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp index f7d2ebb11b..620c7af17f 100644 --- a/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp @@ -48,20 +48,21 @@ DatabaseCommand_ArtistStats::exec( DatabaseImpl* dbi ) unsigned int plays = 0; unsigned int chartPos = 0; unsigned int chartCount = 0; + const unsigned int artistId = m_artist->id(); QHash< QString, unsigned int > charts; while ( query.next() ) { chartCount++; - if ( query.value( 1 ).toUInt() == m_artist->id() ) + if ( chartPos == 0 && query.value( 1 ).toUInt() == artistId ) { chartPos = chartCount; plays = query.value( 0 ).toUInt(); } } - if ( plays == 0 ) + if ( chartPos == 0 ) chartPos = chartCount; emit done( plays, chartPos, chartCount ); From 4e83dbac2e4a61ee070ca8b321959229c914183c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 12:07:32 +0200 Subject: [PATCH 272/565] * Read chart position when loading TrackStats. --- .../database/DatabaseCommand_TrackStats.cpp | 27 +++++++++++++++++++ .../database/DatabaseCommand_TrackStats.h | 1 + 2 files changed, 28 insertions(+) diff --git a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp index a8bf44973b..7e9d3e9f63 100644 --- a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp +++ b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp @@ -50,6 +50,33 @@ DatabaseCommand_TrackStats::exec( DatabaseImpl* dbi ) if ( m_track->trackId() == 0 ) return; + query.prepare( "SELECT COUNT(*) AS counter, track.id " + "FROM playback_log, track " + "WHERE playback_log.source IS NULL AND track.id = playback_log.track " + "GROUP BY track.id " + "ORDER BY counter DESC" ); + query.exec(); + + unsigned int chartPos = 0; + unsigned int chartCount = 0; + const unsigned int trackId = m_track->trackId(); + + QHash< QString, unsigned int > charts; + while ( query.next() ) + { + chartCount++; + + if ( chartPos == 0 && query.value( 1 ).toUInt() == trackId ) + { + chartPos = chartCount; + } + } + + if ( chartPos == 0 ) + chartPos = chartCount; + + emit trackStats( chartPos, chartCount ); + query.prepare( "SELECT * " "FROM playback_log " "WHERE track = ? ORDER BY playtime ASC" ); diff --git a/src/libtomahawk/database/DatabaseCommand_TrackStats.h b/src/libtomahawk/database/DatabaseCommand_TrackStats.h index 1cf23eefe6..f7e8f591e1 100644 --- a/src/libtomahawk/database/DatabaseCommand_TrackStats.h +++ b/src/libtomahawk/database/DatabaseCommand_TrackStats.h @@ -39,6 +39,7 @@ Q_OBJECT virtual QString commandname() const { return "trackstats"; } signals: + void trackStats( unsigned int totalPlays, unsigned int chartPosition ); void done( const QList< Tomahawk::PlaybackLog >& playbackData ); private: From 252b809fe4731336d063f5e72c3665182e1ab79d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 12:08:01 +0200 Subject: [PATCH 273/565] * Expose track stats in Track(-Data). --- src/libtomahawk/Track.cpp | 14 ++++++++++++++ src/libtomahawk/Track.h | 3 +++ src/libtomahawk/TrackData.cpp | 27 +++++++++++++++++++++++++++ src/libtomahawk/TrackData.h | 7 +++++++ 4 files changed, 51 insertions(+) diff --git a/src/libtomahawk/Track.cpp b/src/libtomahawk/Track.cpp index 68dedd1184..cc04f335ef 100644 --- a/src/libtomahawk/Track.cpp +++ b/src/libtomahawk/Track.cpp @@ -320,6 +320,20 @@ Track::playbackCount( const source_ptr& source ) } +unsigned int +Track::chartPosition() const +{ + return m_trackData->chartPosition(); +} + + +unsigned int +Track::chartCount() const +{ + return m_trackData->chartCount(); +} + + void Track::loadAttributes() { diff --git a/src/libtomahawk/Track.h b/src/libtomahawk/Track.h index 324f2714a5..2bb82557e1 100644 --- a/src/libtomahawk/Track.h +++ b/src/libtomahawk/Track.h @@ -100,6 +100,9 @@ friend class ::DatabaseCommand_LoadInboxEntries; // for setAllSocialActions QList< Tomahawk::PlaybackLog > playbackHistory( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ) const; unsigned int playbackCount( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ); + unsigned int chartPosition() const; + unsigned int chartCount() const; + void loadSocialActions(); QList< Tomahawk::SocialAction > allSocialActions() const; QString socialActionDescription( const QString& action, DescriptionMode mode ) const; diff --git a/src/libtomahawk/TrackData.cpp b/src/libtomahawk/TrackData.cpp index 7d79ff9c90..3a02d7a022 100644 --- a/src/libtomahawk/TrackData.cpp +++ b/src/libtomahawk/TrackData.cpp @@ -105,6 +105,8 @@ TrackData::TrackData( unsigned int id, const QString& artist, const QString& tra , m_attributesLoaded( false ) , m_socialActionsLoaded( false ) , m_playbackHistoryLoaded( false ) + , m_chartPosition( 0 ) + , m_chartCount( 0 ) , m_simTracksLoaded( false ) , m_lyricsLoaded( false ) , m_infoJobs( 0 ) @@ -339,6 +341,7 @@ TrackData::loadStats() m_playbackHistoryLoaded = true; DatabaseCommand_TrackStats* cmd = new DatabaseCommand_TrackStats( m_ownRef.toStrongRef() ); + connect( cmd, SIGNAL( trackStats( unsigned int, unsigned int ) ), SLOT( onTrackStatsLoaded( unsigned int, unsigned int ) ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); } @@ -388,6 +391,30 @@ TrackData::playbackCount( const source_ptr& source ) } +void +TrackData::onTrackStatsLoaded( unsigned int chartPos, unsigned int chartCount ) +{ + m_chartPosition = chartPos; + m_chartCount = chartCount; + + emit statsLoaded(); +} + + +unsigned int +TrackData::chartPosition() const +{ + return m_chartPosition; +} + + +unsigned int +TrackData::chartCount() const +{ + return m_chartCount; +} + + QList TrackData::similarTracks() const { diff --git a/src/libtomahawk/TrackData.h b/src/libtomahawk/TrackData.h index 17729808c0..807628f8d5 100644 --- a/src/libtomahawk/TrackData.h +++ b/src/libtomahawk/TrackData.h @@ -100,6 +100,9 @@ Q_OBJECT void setPlaybackHistory( const QList< Tomahawk::PlaybackLog >& playbackData ); unsigned int playbackCount( const Tomahawk::source_ptr& source = Tomahawk::source_ptr() ); + unsigned int chartPosition() const; + unsigned int chartCount() const; + QList similarTracks() const; QStringList lyrics() const; @@ -114,6 +117,8 @@ public slots: void lyricsLoaded(); private slots: + void onTrackStatsLoaded( unsigned int chartPos, unsigned int chartCount ); + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void infoSystemFinished( QString target ); @@ -142,6 +147,8 @@ private slots: bool m_playbackHistoryLoaded; QList< PlaybackLog > m_playbackHistory; + unsigned int m_chartPosition; + unsigned int m_chartCount; bool m_simTracksLoaded; QList m_similarTracks; From aca0f513422a93b12489d061de844478202d1ff2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 12:08:17 +0200 Subject: [PATCH 274/565] * Shrink font size for bigger figures. --- src/libtomahawk/widgets/StatsGauge.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index a1d91d1a49..a32be994ae 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -74,9 +74,13 @@ StatsGauge::paintEvent( QPaintEvent* event ) p.setPen( pen ); QFont font = p.font(); font.setWeight( QFont::Black ); - font.setPixelSize( 60 ); - p.setFont( font ); + if ( value() <= 999 ) + font.setPixelSize( 60 ); + else + font.setPixelSize( 44 ); + + p.setFont( font ); QRect textRect( 0, gaugeSize.height() / 2 - 14, gaugeSize.width(), 62 ); p.drawText( textRect, Qt::AlignCenter, value() > 0 ? QString::number( value() ) : "-" ); From e0ad9d48d80592ef6cb7dd762435fb55ea1e3fe3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 12:08:43 +0200 Subject: [PATCH 275/565] * Added gauge for track chart position. --- .../widgets/infowidgets/TrackInfoWidget.cpp | 10 ++++++++-- src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h | 2 ++ src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 65454c24d0..affd54f7d8 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -95,10 +95,15 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); m_playStatsGauge->setText( tr( "# PLAYS / ARTIST" ) ); + m_playStatsTotalGauge = new StatsGauge( ui->statsWidget ); + m_playStatsTotalGauge->setText( tr( "# IN YOUR CHARTS" ) ); + m_playStatsTotalGauge->setInvertedAppearance( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); l->addWidget( m_playStatsGauge ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + l->addWidget( m_playStatsTotalGauge ); + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); ui->statsWidget->setLayout( l ); ui->statsLabel->setVisible( false ); @@ -213,12 +218,11 @@ TrackInfoWidget::onCoverUpdated() void TrackInfoWidget::onStatsLoaded() { + QString stats; QList< Tomahawk::PlaybackLog > history = m_query->track()->playbackHistory( SourceList::instance()->getLocal() ); const unsigned int trackCounter = m_query->track()->playbackCount( SourceList::instance()->getLocal() ); const unsigned int artistCounter = m_artist->playbackCount( SourceList::instance()->getLocal() ); - QString stats; - if ( trackCounter ) stats = tr( "You've listened to this track %n time(s).", "", trackCounter ); else @@ -240,6 +244,8 @@ TrackInfoWidget::onStatsLoaded() stats += "\n" + tr( "You've never listened to %1 before." ).arg( m_artist->name() ); ui->statsLabel->setText( stats ); + m_playStatsTotalGauge->setMaximum( m_query->track()->chartCount() ); + m_playStatsTotalGauge->setValue( m_query->track()->chartPosition() ); } diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h index 7362660bd4..12138bcde8 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h @@ -96,6 +96,8 @@ private slots: Tomahawk::artist_ptr m_artist; StatsGauge* m_playStatsGauge; + StatsGauge* m_playStatsTotalGauge; + PlayableModel* m_relatedTracksModel; QString m_title; QPixmap m_pixmap; diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index c3fe0e8ac2..41582ffda2 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -89,7 +89,7 @@ - 220 + 450 240 From f3b660f12306c344fb3730c70aa14b1c1cbaabbe Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 12:29:51 +0200 Subject: [PATCH 276/565] * Don't consider tracks with a playcount of 1 as part of the charts. --- src/libtomahawk/database/DatabaseCommand_TrackStats.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp index 7e9d3e9f63..dc763b9418 100644 --- a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp +++ b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp @@ -64,8 +64,10 @@ DatabaseCommand_TrackStats::exec( DatabaseImpl* dbi ) QHash< QString, unsigned int > charts; while ( query.next() ) { - chartCount++; + if ( query.value( 0 ).toUInt() < 2 ) + break; + chartCount++; if ( chartPos == 0 && query.value( 1 ).toUInt() == trackId ) { chartPos = chartCount; From d13f3f3c454a9a8b104f84b10500178a123b1583 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 12:47:26 +0200 Subject: [PATCH 277/565] * Don't consider artists with a playcount of 1 as part of the charts. --- src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp index 620c7af17f..52deff020b 100644 --- a/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp @@ -53,8 +53,10 @@ DatabaseCommand_ArtistStats::exec( DatabaseImpl* dbi ) QHash< QString, unsigned int > charts; while ( query.next() ) { - chartCount++; + if ( query.value( 0 ).toUInt() < 2 ) + break; + chartCount++; if ( chartPos == 0 && query.value( 1 ).toUInt() == artistId ) { chartPos = chartCount; From 090cc66fc70596efaf7cc9871e7e0ab16a75c942 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 8 Jun 2013 14:24:37 +0200 Subject: [PATCH 278/565] * Moved artist / track labels out of the PlayableCover. --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 6 +- .../widgets/infowidgets/ArtistInfoWidget.ui | 70 ++++++++++++--- .../widgets/infowidgets/TrackInfoWidget.cpp | 12 ++- .../widgets/infowidgets/TrackInfoWidget.ui | 87 ++++++++++++++++--- 4 files changed, 146 insertions(+), 29 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index ec90ea8595..953cf2515e 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -111,7 +111,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Original, QSize( 48, 48 ) ); ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Grid, ui->cover->size() ) ); - ui->cover->setShowText( true ); + ui->cover->setShowText( false ); QFont f = font(); f.setPointSize( f.pointSize() + 3 ); @@ -127,6 +127,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* p.setColor( QPalette::Text, Qt::gray ); ui->biography->setPalette( p ); + ui->artistLabel->setPalette( p ); ui->label->setPalette( p ); ui->label_2->setPalette( p ); ui->label_3->setPalette( p ); @@ -245,7 +246,7 @@ ArtistInfoWidget::jumpToCurrentTrack() void ArtistInfoWidget::load( const artist_ptr& artist ) { - if ( !m_artist.isNull() ) + if ( m_artist ) { disconnect( m_artist.data(), SIGNAL( updated() ), this, SLOT( onArtistImageUpdated() ) ); disconnect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), this, SLOT( onSimilarArtistsLoaded() ) ); @@ -258,6 +259,7 @@ ArtistInfoWidget::load( const artist_ptr& artist ) m_artist = artist; m_title = artist->name(); + ui->artistLabel->setText( artist->name() ); connect( m_artist.data(), SIGNAL( biographyLoaded() ), SLOT( onBiographyLoaded() ) ); connect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), SLOT( onSimilarArtistsLoaded() ) ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index fa6e231996..829ca723e9 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -51,23 +51,65 @@ - - - - 0 - 0 - + + + 8 - - - 0 - 240 - + + 0 - - Qt::ScrollBarAlwaysOff + + 8 - + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 18 + 75 + true + + + + ArtistName + + + 2 + + + + + + + + 0 + 0 + + + + + 0 + 220 + + + + Qt::ScrollBarAlwaysOff + + + + diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index affd54f7d8..1edad069ea 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -44,6 +44,11 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par QWidget* widget = new QWidget; ui->setupUi( widget ); + ui->artistLabel->setContentsMargins( 6, 2, 6, 2 ); + ui->artistLabel->setElideMode( Qt::ElideMiddle ); + ui->artistLabel->setType( QueryLabel::Artist ); + connect( ui->artistLabel, SIGNAL( clickedArtist() ), SLOT( onArtistClicked() ) ); + ui->statsLabel->setStyleSheet( "QLabel { background-image:url(); border: 2px solid #dddddd; background-color: #faf9f9; border-radius: 4px; padding: 12px; }" ); ui->lyricsView->setStyleSheet( "QTextBrowser#lyricsView { background-color: transparent; }" ); @@ -69,7 +74,8 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par ui->lyricsView->setPalette( p ); ui->label->setPalette( p ); -// ui->similarTracksLabel->setPalette( p ); + ui->artistLabel->setPalette( p ); + ui->trackLabel->setPalette( p ); m_relatedTracksModel = new PlayableModel( ui->similarTracksView ); ui->similarTracksView->setPlayableModel( m_relatedTracksModel ); @@ -78,7 +84,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Original, QSize( 48, 48 ) ); ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Grid, ui->cover->size() ) ); - ui->cover->setShowText( true ); + ui->cover->setShowText( false ); m_scrollArea = new QScrollArea(); m_scrollArea->setWidgetResizable( true ); @@ -171,6 +177,8 @@ TrackInfoWidget::load( const query_ptr& query ) m_query = query; m_artist = Artist::get( m_query->track()->artist() ); m_title = QString( "%1 - %2" ).arg( query->track()->artist() ).arg( query->track()->track() ); + ui->trackLabel->setText( m_query->track()->track() ); + ui->artistLabel->setArtist( m_query->track()->artistPtr() ); if ( !m_query.isNull() ) { diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index 41582ffda2..7305c7b237 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -6,7 +6,7 @@ 0 0 - 951 + 965 771 @@ -21,7 +21,7 @@ 12 - + 16 @@ -51,17 +51,77 @@ - - - Qt::Horizontal + + + 8 - - - 40 - 20 - + + 0 + + + 8 + + + 0 - + + 0 + + + + + + 0 + 0 + + + + + 18 + 75 + true + + + + TrackName + + + 4 + + + + + + + + 0 + 0 + + + + + 14 + + + + ArtistName + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + @@ -196,6 +256,11 @@ QLabel
widgets/PlayableCover.h
+ + QueryLabel + QLabel +
widgets/QueryLabel.h
+
From 1227d40c26c7376e34fede4be190dd1227b299dd Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 00:18:38 +0200 Subject: [PATCH 279/565] Lazyload Network Activities --- src/libtomahawk/ViewManager.cpp | 1 - .../DatabaseCommand_NetworkCharts.cpp | 21 +- .../database/DatabaseCommand_NetworkCharts.h | 1 + .../widgets/NetworkActivityWidget.cpp | 213 ++++++++++++------ .../widgets/NetworkActivityWidget.h | 15 +- .../widgets/NetworkActivityWidget_p.h | 3 +- 6 files changed, 175 insertions(+), 79 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index f9f7c5d06a..83a76fdfff 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -515,7 +515,6 @@ ViewPage *ViewManager::showNetworkActivityPage() if ( !m_networkActivityWidget ) { m_networkActivityWidget = new NetworkActivityWidget( m_widget ); - m_networkActivityWidget->fetchData(); } return show( m_networkActivityWidget ); diff --git a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp index e7a17ee300..ac7f267691 100644 --- a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp +++ b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp @@ -30,6 +30,12 @@ DatabaseCommand_NetworkCharts::DatabaseCommand_NetworkCharts( const QDateTime &f { } +DatabaseCommand_NetworkCharts::DatabaseCommand_NetworkCharts( QObject *parent ) + : DatabaseCommand( parent ) + , m_amount( 0 ) +{ +} + DatabaseCommand_NetworkCharts::~DatabaseCommand_NetworkCharts() { } @@ -44,17 +50,23 @@ DatabaseCommand_NetworkCharts::exec( DatabaseImpl * dbi ) { limit = QString( "LIMIT 0, %1" ).arg( m_amount ); } + QString timespan; + if ( m_from.isValid() && m_to.isValid() ) + { + timespan = QString( + " AND playback_log.playtime >= %1 AND playback_log.playtime <= %2 " + ).arg( m_from.toTime_t() ).arg( m_to.toTime_t() ); + } QString sql = QString( "SELECT COUNT(*) as counter, track.name, artist.name " " FROM playback_log, track, artist " " WHERE track.id = playback_log.track AND artist.id = track.artist " - " AND playback_log.playtime >= %1 AND playback_log.playtime <= %2 " // incorportrate timespan - " AND playback_log.source IS NOT NULL " // exclude self + " AND playback_log.source IS NOT NULL %1 " // exclude self " GROUP BY playback_log.track " " ORDER BY counter DESC " - " %3" - ).arg( m_from.toTime_t() ).arg( m_to.toTime_t() ).arg( limit ); + " %2" + ).arg( timespan ).arg( limit ); query.prepare( sql ); query.exec(); @@ -71,4 +83,3 @@ DatabaseCommand_NetworkCharts::exec( DatabaseImpl * dbi ) emit done( tracks ); } - diff --git a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.h b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.h index a9b7ec93d9..50f5fb303d 100644 --- a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.h +++ b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.h @@ -31,6 +31,7 @@ class DLLEXPORT DatabaseCommand_NetworkCharts : public DatabaseCommand { Q_OBJECT public: + explicit DatabaseCommand_NetworkCharts( QObject* parent = 0 ); explicit DatabaseCommand_NetworkCharts( const QDateTime& from, const QDateTime& to, QObject* parent = 0 ); virtual ~DatabaseCommand_NetworkCharts(); diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 785d18ba7e..a51c0bfb54 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -24,6 +24,7 @@ #include "database/DatabaseCommand_NetworkCharts.h" #include "playlist/PlaylistChartItemDelegate.h" #include "utils/AnimatedSpinner.h" +#include "utils/Logger.h" #include "utils/TomahawkUtilsGui.h" #include @@ -65,10 +66,26 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) d_func()->playlistInterface = d_func()->ui->tracksViewLeft->playlistInterface(); - // Lets have a spinner until loaded - d_func()->ui->breadCrumbLeft->setVisible( false ); - d_func()->spinner = new AnimatedSpinner( d_func()->ui->tracksViewLeft ); - d_func()->spinner->fadeIn(); + // Build up breadcrumb + QStandardItem* rootItem = d_func()->crumbModelLeft->invisibleRootItem(); + QStandardItem* chartItem = new QStandardItem( tr( "Charts" ) ); + rootItem->appendRow( chartItem ); + QStandardItem* weekItem = new QStandardItem( tr( "Last Week" ) ); + weekItem->setData( WeekChart, Breadcrumb::DefaultRole ); + chartItem->appendRow( weekItem ); + QStandardItem* monthItem = new QStandardItem( tr( "Last Month" ) ); + monthItem->setData( MonthChart, Breadcrumb::DefaultRole ); + chartItem->appendRow( monthItem ); + QStandardItem* yearItem = new QStandardItem( tr( "Last Year" ) ); + yearItem->setData( YearChart, Breadcrumb::DefaultRole ); + chartItem->appendRow( yearItem ); + QStandardItem* overallItem = new QStandardItem( tr( "Overall" ) ); + overallItem->setData( OverallChart, Breadcrumb::DefaultRole ); + chartItem->appendRow( overallItem ); + d_func()->sortedProxy->setSourceModel( d_func()->crumbModelLeft ); + d_func()->sortedProxy->sort( 0, Qt::AscendingOrder ); + d_func()->ui->breadCrumbLeft->setModel( d_func()->sortedProxy ); + d_func()->ui->breadCrumbLeft->setVisible( true ); } @@ -105,24 +122,18 @@ NetworkActivityWidget::jumpToCurrentTrack() } -void -NetworkActivityWidget::fetchData() -{ - // Do not block the UI thread - QtConcurrent::run( this, &NetworkActivityWidget::actualFetchData ); -} - - void NetworkActivityWidget::weeklyCharts( const QList& tracks ) { d_func()->weeklyChartsModel = new PlaylistModel( d_func()->ui->tracksViewLeft ); d_func()->weeklyChartsModel->startLoading(); - // Pipeline::instance()->resolve( tracks ); d_func()->weeklyChartsModel->appendTracks( tracks ); d_func()->weeklyChartsModel->finishLoading(); - checkDone(); + if ( d_func()->activeView == WeekChart ) + { + showWeekCharts(); + } } @@ -131,11 +142,13 @@ NetworkActivityWidget::monthlyCharts( const QList& tracks ) { d_func()->monthlyChartsModel = new PlaylistModel( d_func()->ui->tracksViewLeft ); d_func()->monthlyChartsModel->startLoading(); - // Pipeline::instance()->resolve( tracks ); d_func()->monthlyChartsModel->appendTracks( tracks ); d_func()->monthlyChartsModel->finishLoading(); - checkDone(); + if ( d_func()->activeView == MonthChart ) + { + showMonthCharts(); + } } @@ -144,11 +157,27 @@ NetworkActivityWidget::yearlyCharts( const QList& tracks ) { d_func()->yearlyChartsModel = new PlaylistModel( d_func()->ui->tracksViewLeft ); d_func()->yearlyChartsModel->startLoading(); - // Pipeline::instance()->resolve( tracks ); d_func()->yearlyChartsModel->appendTracks( tracks ); d_func()->yearlyChartsModel->finishLoading(); - checkDone(); + if ( d_func()->activeView == YearChart ) + { + showYearCharts(); + } +} + +void +NetworkActivityWidget::overallCharts( const QList& tracks ) +{ + d_func()->overallChartsModel = new PlaylistModel( d_func()->ui->tracksViewLeft ); + d_func()->overallChartsModel->startLoading(); + d_func()->overallChartsModel->appendTracks( tracks ); + d_func()->overallChartsModel->finishLoading(); + + if ( d_func()->activeView == OverallChart ) + { + showOverallCharts(); + } } @@ -158,84 +187,132 @@ NetworkActivityWidget::leftCrumbIndexChanged( const QModelIndex& index ) QStandardItem* item = d_func()->crumbModelLeft->itemFromIndex( d_func()->sortedProxy->mapToSource( index ) ); if ( !item ) return; - if ( !item->data( Breadcrumb::ChartIdRole ).isValid() ) + if ( !item->data( Breadcrumb::DefaultRole ).isValid() ) return; - const QString chartId = item->data( Breadcrumb::ChartIdRole ).toString(); - if ( chartId == NETWORKCHARTS_WEEK_CHARTS ) + int chartId = item->data( Breadcrumb::DefaultRole ).toInt(); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Showing chart" << chartId; + switch ( chartId ) { - d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); - d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->weeklyChartsModel ); - d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); - } - else if ( chartId == NETWORKCHARTS_MONTH_CHARTS ) - { - d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); - d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->monthlyChartsModel ); - d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); - } - else if ( chartId == NETWORKCHARTS_YEAR_CHARTS ) - { - d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); - d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->yearlyChartsModel ); - d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); + case WeekChart: + showWeekCharts(); + break; + case MonthChart: + showMonthCharts(); + break; + case YearChart: + showYearCharts(); + break; + case OverallChart: + showOverallCharts(); + break; } } void -NetworkActivityWidget::actualFetchData() +NetworkActivityWidget::fetchYearCharts() { QDateTime to = QDateTime::currentDateTime(); + QDateTime yearAgo = to.addYears( -1 ); + DatabaseCommand_NetworkCharts* yearCharts = new DatabaseCommand_NetworkCharts( yearAgo, to ); + yearCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); + connect( yearCharts, SIGNAL( done( QList ) ), SLOT( yearlyCharts( QList ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( yearCharts ) ); +} - // Weekly charts +void +NetworkActivityWidget::fetchOverallCharts() +{ + DatabaseCommand_NetworkCharts* overallCharts = new DatabaseCommand_NetworkCharts(); + overallCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); + connect( overallCharts, SIGNAL( done( QList ) ), SLOT( overallCharts( QList ) ) ); + Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( overallCharts ) ); +} + + +void +NetworkActivityWidget::fetchWeekCharts() +{ + QDateTime to = QDateTime::currentDateTime(); QDateTime weekAgo = to.addDays( -7 ); DatabaseCommand_NetworkCharts* weekCharts = new DatabaseCommand_NetworkCharts( weekAgo, to ); weekCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); connect( weekCharts, SIGNAL( done( QList ) ), SLOT( weeklyCharts( QList ) ) ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( weekCharts ) ); +} - // Monthly charts +void +NetworkActivityWidget::fetchMonthCharts() +{ + QDateTime to = QDateTime::currentDateTime(); QDateTime monthAgo = to.addMonths( -1 ); DatabaseCommand_NetworkCharts* monthCharts = new DatabaseCommand_NetworkCharts( monthAgo, to ); monthCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); connect( monthCharts, SIGNAL( done( QList ) ), SLOT( monthlyCharts( QList ) ) ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( monthCharts ) ); +} - // Yearly charts - QDateTime yearAgo = to.addYears( -1 ); - DatabaseCommand_NetworkCharts* yearCharts = new DatabaseCommand_NetworkCharts( yearAgo, to ); - yearCharts->setLimit( NETWORKCHARTS_NUM_TRACKS ); - connect( yearCharts, SIGNAL( done( QList ) ), SLOT( yearlyCharts( QList ) ) ); - Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( yearCharts ) ); + +void +NetworkActivityWidget::showWeekCharts() +{ + d_func()->activeView = WeekChart; + if ( !d_func()->weeklyChartsModel.isNull() ) + { + d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->weeklyChartsModel ); + d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); + } + else + { + fetchWeekCharts(); + } } +void +NetworkActivityWidget::showMonthCharts() +{ + d_func()->activeView = MonthChart; + if ( !d_func()->monthlyChartsModel.isNull() ) + { + d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->monthlyChartsModel ); + d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); + } + else + { + fetchMonthCharts(); + } +} void -NetworkActivityWidget::checkDone() +NetworkActivityWidget::showYearCharts() +{ + d_func()->activeView = YearChart; + if ( !d_func()->yearlyChartsModel.isNull() ) + { + d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->yearlyChartsModel ); + d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); + } + else + { + fetchYearCharts(); + } +} + +void NetworkActivityWidget::showOverallCharts() { - if ( !d_func()->weeklyChartsModel.isNull() && !d_func()->yearlyChartsModel.isNull() && !d_func()->monthlyChartsModel.isNull() ) + d_func()->activeView = OverallChart; + if ( !d_func()->overallChartsModel.isNull() ) + { + d_func()->ui->tracksViewLeft->proxyModel()->setStyle( PlayableProxyModel::Large ); + d_func()->ui->tracksViewLeft->setPlaylistModel( d_func()->overallChartsModel ); + d_func()->ui->tracksViewLeft->proxyModel()->sort( -1 ); + } + else { - // All charts are loaded, do the remaining work UI work. - - // Build up breadcrumb - QStandardItem* rootItem = d_func()->crumbModelLeft->invisibleRootItem(); - QStandardItem* chartItem = new QStandardItem( tr( "Charts" ) ); - rootItem->appendRow( chartItem ); - QStandardItem* weekItem = new QStandardItem( tr( "Last Week" ) ); - weekItem->setData( NETWORKCHARTS_WEEK_CHARTS, Breadcrumb::ChartIdRole ); - chartItem->appendRow( weekItem ); - QStandardItem* monthItem = new QStandardItem( tr( "Last Month" ) ); - monthItem->setData( NETWORKCHARTS_MONTH_CHARTS, Breadcrumb::ChartIdRole ); - chartItem->appendRow( monthItem ); - QStandardItem* yearItem = new QStandardItem( tr( "Last Year" ) ); - yearItem->setData( NETWORKCHARTS_YEAR_CHARTS, Breadcrumb::ChartIdRole ); - chartItem->appendRow( yearItem ); - d_func()->sortedProxy->setSourceModel( d_func()->crumbModelLeft ); - d_func()->sortedProxy->sort( 0, Qt::AscendingOrder ); - d_func()->ui->breadCrumbLeft->setModel( d_func()->sortedProxy ); - - d_func()->spinner->fadeOut(); - d_func()->ui->breadCrumbLeft->setVisible( true ); + fetchOverallCharts(); } } diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.h b/src/libtomahawk/widgets/NetworkActivityWidget.h index 35cb50b1fb..cf8513ea94 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.h +++ b/src/libtomahawk/widgets/NetworkActivityWidget.h @@ -36,6 +36,7 @@ class NetworkActivityWidget : public QWidget, public Tomahawk::ViewPage { Q_OBJECT public: + enum ViewType { WeekChart = 1, MonthChart = 2, YearChart = 3, OverallChart = 4 }; NetworkActivityWidget(QWidget* parent = 0); ~NetworkActivityWidget(); @@ -49,20 +50,26 @@ class NetworkActivityWidget : public QWidget, public Tomahawk::ViewPage virtual bool isBeingPlayed() const; virtual bool jumpToCurrentTrack(); - - void fetchData(); signals: private slots: void weeklyCharts( const QList& ); void monthlyCharts( const QList& ); void yearlyCharts( const QList& ); + void overallCharts( const QList& ); void leftCrumbIndexChanged( const QModelIndex& ); private: - void actualFetchData(); - void checkDone(); + void fetchWeekCharts(); + void fetchMonthCharts(); + void fetchYearCharts(); + void fetchOverallCharts(); + + void showWeekCharts(); + void showMonthCharts(); + void showYearCharts(); + void showOverallCharts(); Q_DECLARE_PRIVATE( NetworkActivityWidget ) NetworkActivityWidgetPrivate* d_ptr; diff --git a/src/libtomahawk/widgets/NetworkActivityWidget_p.h b/src/libtomahawk/widgets/NetworkActivityWidget_p.h index 063d265d5d..302cbd0dc6 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget_p.h +++ b/src/libtomahawk/widgets/NetworkActivityWidget_p.h @@ -37,13 +37,14 @@ class NetworkActivityWidgetPrivate private: QSharedPointer ui; Tomahawk::playlistinterface_ptr playlistInterface; - AnimatedSpinner* spinner; QStandardItemModel* crumbModelLeft; QSortFilterProxyModel* sortedProxy; QPointer weeklyChartsModel; QPointer monthlyChartsModel; QPointer yearlyChartsModel; + QPointer overallChartsModel; + NetworkActivityWidget::ViewType activeView; }; #endif // NETWORKACTIVITYWIDGET_P_H From a2b20fa49e77d9bb2d867b882d50fb4acb40adb8 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 01:02:45 +0200 Subject: [PATCH 280/565] Remove unused defines --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index a51c0bfb54..72b7778586 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -32,9 +32,6 @@ #include #define NETWORKCHARTS_NUM_TRACKS 100 -#define NETWORKCHARTS_WEEK_CHARTS "week" -#define NETWORKCHARTS_MONTH_CHARTS "month" -#define NETWORKCHARTS_YEAR_CHARTS "year" using namespace Tomahawk; From 7597dd55549350c3915f8ba49d0213a251b3d5e2 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sun, 9 Jun 2013 02:16:48 +0200 Subject: [PATCH 281/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_bg.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_bn_IN.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_ca.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_ca@valencia.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_cs.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_da.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_de.ts | 235 ++++++++++++++++++---------------- lang/tomahawk_el.ts | 235 ++++++++++++++++++---------------- lang/tomahawk_en.ts | 237 +++++++++++++++++++---------------- lang/tomahawk_es.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_fi.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_fr.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_gl.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_hi_IN.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_hu.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_id.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_it.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_ja.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_lt.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_pl.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_pt_BR.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_ru.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_sv.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_tr.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_zh_CN.ts | 233 ++++++++++++++++++---------------- lang/tomahawk_zh_TW.ts | 233 ++++++++++++++++++---------------- 27 files changed, 3460 insertions(+), 2839 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 87b446c1a8..1f1fa9a8c7 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -308,38 +308,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits الأكثر شهرة - + Related Artists الفنانين ذات الذوق القريب - + Albums ألبومات - + Sorry, we could not find any albums for this artist! نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! - + Sorry, we could not find any related artists! نعتذر، لم نستطيع إيجاد فنانين! - + Sorry, we could not find any top hits for this artist! نعتذر، لم نستطيع إيجاد أغاني مشهورة جدا لهذا الفنان! - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ connect and stream from you? فشل في إرسال معلومات الانهيار.
+ + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. هذه المجموعة فارغة حاليا. - + This playlist is currently empty. Add some tracks to it and enjoy the music! هذه القائمة فارغة حاليا. أضف لها بعض الأغاني واستمتع للموسيقى! @@ -731,6 +764,39 @@ Password تسجيل الدخول + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1035,27 +1101,32 @@ Password NetworkActivityWidget - + Charts الرسوم البيانية - + Last Week الأسبوع الماضي - + Last Month الشهر الماضي - + Last Year العام الماضي - + + Overall + + + + Network Activity نشاط الشبكة @@ -1159,42 +1230,42 @@ Password الدقة - + Perfect match تطابق تام - + Very good match تطابق جيد جدا - + Good match تطابق جيد - + Vague match تطابق مبهم - + Bad match تطابق سيء - + Very bad match تطابق سيء للغاية - + Not available غير متوفر - + Searching... قيد البحث... @@ -1499,17 +1570,17 @@ Password QueueView - + Open Queue إفتح قائمة الإنتظار - + Open Queue - %n item(s) إفتح قائمة الإنتظار - %n بندإفتح قائمة الإنتظار - بند %nإفتح قائمة الإنتظار - بندين %nإفتح قائمة الإنتظار - %n بنودإفتح قائمة الإنتظار - %n بنودإفتح قائمة الإنتظار - %n بنود - + Close Queue أغلق قائمة الإنتظار @@ -1940,56 +2011,36 @@ Password SourceItem - - + + Latest Additions أحدث الإضافات - + Recently Played تم الاستماع لها مؤخرا - + SuperCollection سوبر كولكشن - + Latest additions to your collection آخر إضافات على مجموعتك - + Latest additions to %1's collection آخر إضافات على مجموعة %1 - + Sorry, we could not find any recent additions! نعتذر، لم نستطيع إيجاد إضافة جديدة! - - - Recently Played Tracks - الأغاني التي إستمعت إليها مؤخرا - - - - Your recently played tracks - الأغاني التي إستمعت إليها مؤخرا - - - - %1's recently played tracks - الأغاني التي سمعها مؤخرا %1 - - - - Sorry, we could not find any recent plays! - نعتذر، لم نستطيع إيجاد أغاني مسموعة مؤخرا! - SourceTreeView @@ -2212,7 +2263,7 @@ Password StatsGauge - + out of %1 @@ -3588,42 +3639,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and و - + You أنت - + you أنت - + and و - + %n other(s) %n آخر%n آخر%n آخرين%n آخرين%n آخرين%n آخرين - + %n people %n شخصشخصشخصين%n أشخاص%n شخص%n شخص - + loved this track أحب هذه الأغنية - + sent you this track %1 %1 أرسل لك هذه الأغنية @@ -3889,42 +3940,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks أغاني قريبة - + Sorry, but we could not find similar tracks for this song! نعتذر، لم نستطيع إيجاد أغاني قريبة من هذه الأغنية! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). لقد استمعت إلى هذه الأغنية %n مرة.لقد استمعت إلى هذه الأغنية مرة %n.لقد استمعت إلى هذه الأغنية مرتين %n.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات. - + You've never listened to this track before. لم تستمع لهذه الأغنية من قبل. - + You first listened to it on %1. استمعت إليها أولاً في %1. - + You've listened to %1 %n time(s). لقد استمعت إلى %1 %n مرة.لقد استمعت إلى %1 مرة %n.لقد استمعت إلى %1 مرتين %n.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات. - + You've never listened to %1 before. لم تستمع إلى %1 من قبل. @@ -3932,7 +3988,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. عذراً، ترشيحك "%1" لم يطابق أي نتائج. @@ -4106,39 +4162,6 @@ You can re-send a sync message at any time simply by sending another tweet using لا إقتراحات للإستماع هنا. - - WelcomeWidget - - - Recent Additions - آخر الإضافات - - - - Newest Stations & Playlists - أجدد الإذاعات و قوائم الأغاني - - - - Recently Played Tracks - الأغاني التي إستمعت إليها مؤخرا - - - - Recently played tracks - الأغاني التي إستمعت إليها مؤخرا - - - - No recently created playlists in your network. - لا قوائم أغاني جديدة أنشئت مؤخرا على شبكتك. - - - - Welcome to Tomahawk - مرحبا بكم في توماهوك - - WhatsHotWidget diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 38ea21d10c..57bd009f38 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Най-известни изпълнения - + Related Artists Изпълнители с подобно звучене - + Albums Албуми - + Sorry, we could not find any albums for this artist! Съжалявам, но не откривам нито един албум за този изпълнител! - + Sorry, we could not find any related artists! Съжалявам, но не откривам нито един подобен на този изпълнтел! - + Sorry, we could not find any top hits for this artist! Съжалявам, но не откривам нито една хитова песен на този изпълнител! - - CHART # + + # IN YOUR CHARTS @@ -518,6 +518,39 @@ Tomahawk създаде доклад относно това и изпращай Изпращането на краш-данни е неуспешно. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -676,12 +709,12 @@ Tomahawk създаде доклад относно това и изпращай FlexibleView - + This playlist is currently empty. Този списък е празен, в момента. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Този списък е празен в момента. Добави няколко песни и се наслади на музиката. @@ -736,6 +769,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1040,27 +1106,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1164,42 +1235,42 @@ Password Съвпадение - + Perfect match Абсолютно - + Very good match Много добро - + Good match Добро - + Vague match Горе-долу - + Bad match Лошо - + Very bad match Много лошо - + Not available Няма съвпадение - + Searching... @@ -1506,17 +1577,17 @@ Password QueueView - + Open Queue Отвори опашката - + Open Queue - %n item(s) Отвори опашка - %n елементОтвори опашка - %n елемента - + Close Queue Затвори опашката @@ -1950,57 +2021,37 @@ Password SourceItem - - + + Latest Additions Последно добавени - + Recently Played Наскоро изпълнени песни - + SuperCollection Обща колекция /Сборен изглед от локалните и наличните в колекциите на приятелите ти изпълнения/ - + Latest additions to your collection Последно добавени към колекцията - + Latest additions to %1's collection Последно добавени в колекцията на %1 - + Sorry, we could not find any recent additions! Съжаляваме, но не откриваме наскоро-добавени позиции! - - - Recently Played Tracks - Наскоро изпълнени песни - - - - Your recently played tracks - Наскоро изпълнени песни от теб - - - - %1's recently played tracks - Наскоро изпълнените песни от %1 - - - - Sorry, we could not find any recent plays! - Съжалявам, но не откривам нито една наскоро изпълнена песен! - SourceTreeView @@ -2223,7 +2274,7 @@ Password StatsGauge - + out of %1 @@ -3601,42 +3652,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3904,42 +3955,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Подобни песни - + Sorry, but we could not find similar tracks for this song! Съжалявам, но не откривам нито една подобна на тази песен! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Ти си слушал тази песен %n път(и)Ти си слушал тази песен %n път(и) - + You've never listened to this track before. Никога не си слушал тази песен преди - + You first listened to it on %1. Първоначално си я слушал на %1 - + You've listened to %1 %n time(s). Слушал си %1 път(и)Слушал си %1 %n път(и) - + You've never listened to %1 before. Никога не си слушал %1 преди @@ -3947,7 +4003,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Съжалявам, твоят филтър %1 не върна никакъв резултат. @@ -4122,39 +4178,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - Нови попълнения - - - - Newest Stations & Playlists - Най-нови станции и списъци - - - - Recently Played Tracks - Наскоро изпълнени - - - - Recently played tracks - Наскоро изпълнени - - - - No recently created playlists in your network. - Не откривам наскоро създадени списъци в твоята мрежа - - - - Welcome to Tomahawk - Здравей! - - WhatsHotWidget diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 90122f9cad..844ee4237f 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1495,17 +1566,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1936,56 +2007,36 @@ Password SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - - - - - Your recently played tracks - - - - - %1's recently played tracks - - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2208,7 +2259,7 @@ Password StatsGauge - + out of %1 @@ -3574,42 +3625,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3874,42 +3925,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3917,7 +3973,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4086,39 +4142,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - - - - - Newest Stations & Playlists - - - - - Recently Played Tracks - - - - - Recently played tracks - - - - - No recently created playlists in your network. - - - - - Welcome to Tomahawk - - - WhatsHotWidget diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index e5d65d121f..881419ae7f 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'aquest artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'aquest artista - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? S'ha produït un error en enviar la informació sobre l'error. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... @@ -1496,17 +1567,17 @@ Password QueueView - + Open Queue Obre la cua - + Open Queue - %n item(s) Obre la cua - %n elementObre la cua - %n elements - + Close Queue Tanca la cua @@ -1940,56 +2011,36 @@ i emissores basades en els vostres gusts musicals. SourceItem - - + + Latest Additions Darreres novetats - + Recently Played Reproduïdes recentment - + SuperCollection Supercol·lecció - + Latest additions to your collection Darreres novetats a la col·lecció - + Latest additions to %1's collection Darreres novetats a la col·lecció %1 - + Sorry, we could not find any recent additions! No s'ha trobat cap novetat recent - - - Recently Played Tracks - Cançons reproduïdes recentment - - - - Your recently played tracks - Les cançons que heu reproduït recentment - - - - %1's recently played tracks - Cançons reproduïdes recentment per %1 - - - - Sorry, we could not find any recent plays! - No s'ha trobat cap cançó reproduïda recentment - SourceTreeView @@ -2212,7 +2263,7 @@ i emissores basades en els vostres gusts musicals. StatsGauge - + out of %1 @@ -3588,42 +3639,42 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3889,42 +3940,47 @@ introduïu el PIN aquí: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a aquesta cançó - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Heu escoltat aquesta cançó %n cop.Heu escoltat aquesta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai aquesta cançó abans. - + You first listened to it on %1. Vau escoltar aquesta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. @@ -3932,7 +3988,7 @@ introduïu el PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. @@ -4106,39 +4162,6 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant - - WelcomeWidget - - - Recent Additions - Darreres novetats - - - - Newest Stations & Playlists - Darreres Emissores i Llistes - - - - Recently Played Tracks - Cançons reproduïdes recentment - - - - Recently played tracks - Cançons reproduïdes recentment - - - - No recently created playlists in your network. - No hi ha cançons escoltades recentment a la xarxa. - - - - Welcome to Tomahawk - Us donem la benvinguda al Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index be67860ec6..91259e1c77 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'este artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'este artista - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? S'ha produït un error en enviar la informació sobre l'error. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... @@ -1496,17 +1567,17 @@ Password QueueView - + Open Queue Obri la cua - + Open Queue - %n item(s) Obri la cua - %n elementObri la cua - %n elements - + Close Queue Tanca la cua @@ -1940,56 +2011,36 @@ i emissores basades en els vostres gusts musicals. SourceItem - - + + Latest Additions Darreres novetats - + Recently Played Reproduïdes recentment - + SuperCollection Supercol·lecció - + Latest additions to your collection Darreres novetats a la col·lecció - + Latest additions to %1's collection Darreres novetats a la col·lecció %1 - + Sorry, we could not find any recent additions! No s'ha trobat cap novetat recent - - - Recently Played Tracks - Cançons reproduïdes recentment - - - - Your recently played tracks - Les cançons que heu reproduït recentment - - - - %1's recently played tracks - Cançons reproduïdes recentment per %1 - - - - Sorry, we could not find any recent plays! - No s'ha trobat cap cançó reproduïda recentment - SourceTreeView @@ -2212,7 +2263,7 @@ i emissores basades en els vostres gusts musicals. StatsGauge - + out of %1 @@ -3588,42 +3639,42 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3889,42 +3940,47 @@ introduïu el PIN ací: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a esta cançó - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Heu escoltat esta cançó %n cop.Heu escoltat esta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai esta cançó abans. - + You first listened to it on %1. Vau escoltar esta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. @@ -3932,7 +3988,7 @@ introduïu el PIN ací: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. @@ -4106,39 +4162,6 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant - - WelcomeWidget - - - Recent Additions - Darreres novetats - - - - Newest Stations & Playlists - Darreres Emissores i Llistes - - - - Recently Played Tracks - Cançons reproduïdes recentment - - - - Recently played tracks - Cançons reproduïdes recentment - - - - No recently created playlists in your network. - No hi ha cançons escoltades recentment a la xarxa. - - - - Welcome to Tomahawk - Vos donem la benvinguda al Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 5c3e0d816b..5343a309d0 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -308,38 +308,38 @@ se s vámi spojil? ArtistInfoWidget - + Top Hits Nejlepší písně - + Related Artists Podobní umělci - + Albums Alba - + Sorry, we could not find any albums for this artist! Promiňte, nepodařilo se najít žádná alba tohoto umělce! - + Sorry, we could not find any related artists! Promiňte, nepodařilo se najít žádné podobné umělce! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ se s vámi spojil? Odeslání zprávy o chybě se nezdařilo. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ se s vámi spojil? FlexibleView - + This playlist is currently empty. Tento seznam skladeb je nyní prázdný. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tento seznam skladeb je nyní prázdný. Přidejte do něj nějaké skladby a vychutnávejte hudbu! @@ -731,6 +764,39 @@ heslo Přihlášení + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1035,27 +1101,32 @@ heslo NetworkActivityWidget - + Charts Žebříčky - + Last Week Poslední týden - + Last Month Poslední měsíc - + Last Year Poslední rok - + + Overall + + + + Network Activity Činnost sítě @@ -1159,42 +1230,42 @@ heslo Přesnost - + Perfect match Přesná shoda - + Very good match Velmi dobrá shoda - + Good match Dobrá shoda - + Vague match Mlhavá shoda - + Bad match Špatná shoda - + Very bad match Velmi špatná shoda - + Not available Nedostupné - + Searching... Hledá se... @@ -1498,17 +1569,17 @@ heslo QueueView - + Open Queue Otevřít čekací řadu - + Open Queue - %n item(s) Otevřít čekací řadu - jedna píseňOtevřít čekací řadu - %n písněOtevřít čekací řadu - %n písní - + Close Queue Zavřít čekací řadu @@ -1939,56 +2010,36 @@ heslo SourceItem - - + + Latest Additions Nedávné přídavky - + Recently Played Nedávno poslouchané - + SuperCollection Supersbírka - + Latest additions to your collection Nejnovější písně ve vaší sbírce - + Latest additions to %1's collection Nejnovější písně ve sbírce %1's - + Sorry, we could not find any recent additions! Promiňte, ale nepodařilo se najít žádné nedávné přídavky! - - - Recently Played Tracks - Nedávno poslouchané skladby - - - - Your recently played tracks - Vaše nedávno poslouchané skladby - - - - %1's recently played tracks - %1's nedávno poslouchaných skladeb - - - - Sorry, we could not find any recent plays! - Promiňte, ale nepodařilo se najít žádné nedávno přehrávané skladby! - SourceTreeView @@ -2211,7 +2262,7 @@ heslo StatsGauge - + out of %1 @@ -3588,42 +3639,42 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Track - + and a - + You Vy - + you vy - + and a - + %n other(s) %n dalšímu%n dalším%n dalším - + %n people %n člověku%n lidem%n lidem - + loved this track se tato skladba líbí - + sent you this track %1 vám poslal tuto skladbu %1 @@ -3889,42 +3940,47 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackInfoWidget - + Similar Tracks Podobné skladby - + Sorry, but we could not find similar tracks for this song! Promiňte, ale nepodařilo se najít podobné skladby pro tuto píseň! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Tuto píseň jste si poslechl jednou.Tuto píseň jste si poslechl %n krát.Tuto píseň jste si poslechl %n krát. - + You've never listened to this track before. Tuto píseň jste si ještě nikdy neposlechl. - + You first listened to it on %1. Tuto píseň jste si poprvé poslechl %1. - + You've listened to %1 %n time(s). %1 jste si poslechl jednou.%1 jste si poslechl %n krát.%1 jste si poslechl %n krát. - + You've never listened to %1 before. %1 jste si předtím ještě nikdy neposlechl. @@ -3932,7 +3988,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackView - + Sorry, your filter '%1' did not match any results. Promiňte, vašemu filtru '%1' se nepodařilo najít žádné výsledky. @@ -4105,39 +4161,6 @@ Kdykoli můžete odeslat novou seřizovací zprávu. Žádné sledování návrhů. - - WelcomeWidget - - - Recent Additions - Nedávné přídavky - - - - Newest Stations & Playlists - Nejnovější stanice a seznamy skladeb - - - - Recently Played Tracks - Nedávno poslouchané skladby - - - - Recently played tracks - Nedávno poslouchané skladby - - - - No recently created playlists in your network. - Ve vaší síti nejsou žádné nedávno vytvořené seznamy skladeb. - - - - Welcome to Tomahawk - Vítejte v Tomahawku - - WhatsHotWidget diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 8a38d8a729..baa956d3a3 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Relaterede Kunstnere - + Albums Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? Mislykkede at sende crash info + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1496,17 +1567,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1937,56 +2008,36 @@ Password SourceItem - - + + Latest Additions Seneste Tilføjelser - + Recently Played Senest Afspillet - + SuperCollection SuperSamling - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - - - - - Your recently played tracks - - - - - %1's recently played tracks - - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2209,7 +2260,7 @@ Password StatsGauge - + out of %1 @@ -3576,42 +3627,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3876,42 +3927,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3919,7 +3975,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Beklager, din filter '%1' matchede ikke nogle resultater @@ -4088,39 +4144,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - Nyligt tilføjede - - - - Newest Stations & Playlists - Nyeste Stationer & Spillelister - - - - Recently Played Tracks - Nyligt Afspillede Numre - - - - Recently played tracks - - - - - No recently created playlists in your network. - Ingen nyligt oprettede spillelister i dit netværk - - - - Welcome to Tomahawk - Velkommen til Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index f329826cb8..1cbdef0b0c 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -308,38 +308,38 @@ erlauben sich mit dir zu verbinden? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Ähnliche Künstler - + Albums Alben - + Sorry, we could not find any albums for this artist! Sorry, wir konnten keine Alben für diesen Künstler finden! - + Sorry, we could not find any related artists! Sorry, wir konnten keine ähnlichen Künstler finden! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ erlauben sich mit dir zu verbinden? Übertragung des Fehlerberichts fehlgeschlagen. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ erlauben sich mit dir zu verbinden? FlexibleView - + This playlist is currently empty. Diese Playlist ist momentan leer. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Diese Playlist ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! @@ -731,6 +764,39 @@ Passwort Anmelden + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1035,27 +1101,32 @@ Passwort NetworkActivityWidget - + Charts Charts - + Last Week Letzte Woche - + Last Month Letzter Monat - + Last Year Letztes Jahr - + + Overall + + + + Network Activity Netzwerk Aktivität @@ -1159,42 +1230,42 @@ Passwort Treffsicherheit - + Perfect match Perfekt - + Very good match Sehr gut - + Good match Gut - + Vague match Vage - + Bad match Schlecht - + Very bad match Sehr schlecht - + Not available Nicht verfügbar - + Searching... Suche läuft... @@ -1498,17 +1569,17 @@ Passwort QueueView - + Open Queue Warteschlange öffnen - + Open Queue - %n item(s) Warteschlange öffnen - Ein LiedWarteschlange öffnen - %n Lieder - + Close Queue Warteschlange schließen @@ -1939,56 +2010,36 @@ Passwort SourceItem - - + + Latest Additions Kürzlich hinzugekommen - + Recently Played Kürzlich gehörte Lieder - + SuperCollection Supersammlung - + Latest additions to your collection Neueste Lieder in deiner Sammlung - + Latest additions to %1's collection Neueste Lieder in %1's Sammlung - + Sorry, we could not find any recent additions! Sorry, wir konnten keine Lieder finden die kürzlich hinzugefügt wurden! - - - Recently Played Tracks - Zuletzt gehörte Lieder - - - - Your recently played tracks - Deine zuletzt gehörten Lieder - - - - %1's recently played tracks - %1's zuletzt gehörte Lieder - - - - Sorry, we could not find any recent plays! - Sorry, wir konnten keine kürzlich gespielten Lieder finden! - SourceTreeView @@ -2211,9 +2262,9 @@ Passwort StatsGauge - + out of %1 - + aus %1 @@ -3583,42 +3634,42 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Track - + and und - + You Du - + you du - + and und - + %n other(s) %n andere%n andere - + %n people %n Personen%n Personen - + loved this track gefällt dieses Lied - + sent you this track %1 dieser track wurde gesendet %1 @@ -3884,42 +3935,47 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackInfoWidget - + Similar Tracks Ähnliche Lieder - + Sorry, but we could not find similar tracks for this song! Sorry, wir konnten keine ähnlichen Lieder finden! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Du hast dieses Lied einmal gehört.Du hast dieses Lied %n mal gehört. - + You've never listened to this track before. Du hast dieses Lied noch nie angehört. - + You first listened to it on %1. Du hast dieses Lied zum ersten mal am %1 gehört. - + You've listened to %1 %n time(s). Du hast %1 einmal angehört.Du hast %1 %n mal angehört. - + You've never listened to %1 before. Du hast %1 vorher noch nie gehört. @@ -3927,7 +3983,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackView - + Sorry, your filter '%1' did not match any results. Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. @@ -4100,39 +4156,6 @@ Du kannst jederzeit eine neue Sync-Nachricht abschicken. Es gibt keine Vorschläge hier. - - WelcomeWidget - - - Recent Additions - Kürzlich hinzugekommen - - - - Newest Stations & Playlists - Neueste Stationen & Playlisten - - - - Recently Played Tracks - Kürzlich gehörte Lieder - - - - Recently played tracks - Zuletzt gehörte Lieder - - - - No recently created playlists in your network. - Es gibt keine kürzlich erstellten Playlisten in deinem Netzwerk. - - - - Welcome to Tomahawk - Willkommen bei Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 53957bbdd9..bb7980c331 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Κορυφαία τραγούδια - + Related Artists Παρόμοιοι Καλλιτέχνες - + Albums Άλμπουμ - + Sorry, we could not find any albums for this artist! Συγνωμη, δεν βρεθηκαν αλμπουμ αυτου του καλλιτεχνη! - + Sorry, we could not find any related artists! Συγνωμη, δεν βρεθηκαν καλλιτεχνες! - + Sorry, we could not find any top hits for this artist! Συγνωμη, δεν βρεθηκαν κορυφαια τραγουδια αυτου του καλλιτεχνη! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? Αποτυχία αποστολής πληροφοριών κατάρρευσης. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Αυτη η λιστα αναπαραγωγης ειναι αδεια. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Αυτή η λίστα αναπαραγωγής είναι άδεια προς το παρόν. Προσθέστε μερικά κομμάτια σε αυτήν και απολαύστε την μουσική! @@ -730,6 +763,39 @@ Password Σύνδεση + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1034,27 +1100,32 @@ Password NetworkActivityWidget - + Charts Γραφήματα - + Last Week Τελευταία εβδομάδα - + Last Month Τελευταίος μήνας - + Last Year Τελευταίο ετος - + + Overall + + + + Network Activity Δραστηριότητα δικτύου @@ -1158,42 +1229,42 @@ Password Ακρίβεια - + Perfect match Τέλειο ταίριασμα - + Very good match Πολύ καλό ταίριασμα - + Good match Καλό ταίριασμα - + Vague match Ασαφές ταίριασμα - + Bad match Κακό ταίριασμα - + Very bad match Πολύ κακό ταίριασμα - + Not available Μη διαθέσιμο - + Searching... Αναζήτηση... @@ -1497,17 +1568,17 @@ Password QueueView - + Open Queue Άνοιγμα Σειράς - + Open Queue - %n item(s) Άνοιγμα Σειράς - %n αντικείμενοΆνοιγμα Σειράς - %n αντικείμενα - + Close Queue Κλείσιμο Σειράς @@ -1939,56 +2010,36 @@ Password SourceItem - - + + Latest Additions Τελευταίες Προσθήκες - + Recently Played Τελευταίες Αναπαραγωγές - + SuperCollection ΥπερΣυλλογή - + Latest additions to your collection Τελευταίες προσθήκες στην βιβλιοθήκη σας - + Latest additions to %1's collection Τελευταίες προσθήκες στην βιβλιοθήκη του %1 - + Sorry, we could not find any recent additions! Συγγνωμη, δεν βρεθηκαν προσφατες προσθηκες! - - - Recently Played Tracks - Τελευταίες Αναπαραγωγές Κομματιών - - - - Your recently played tracks - Οι τελευταίες σας αναπαραγωγές - - - - %1's recently played tracks - %1's Τελευταίες αναπαραγωγές τραγουδιων - - - - Sorry, we could not find any recent plays! - Συγγνωμη, δεν βρεθηκαν προσφατες αναπαραγωγες! - SourceTreeView @@ -2211,9 +2262,9 @@ Password StatsGauge - + out of %1 - + από %1 @@ -3589,42 +3640,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and και - + You Εσυ - + you εσυ - + and και - + %n other(s) %n άλλοι%n άλλοι - + %n people %n άτομα%n άτομα - + loved this track Αγαπημένο τραγούδι - + sent you this track %1 αυτο το τραγουδι σταλθηκε %1 @@ -3889,42 +3940,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Παρόμοια Κομμάτια - + Sorry, but we could not find similar tracks for this song! Συγγνωμη, δεν βρεθηκαν παρόμοια κομμάτια για αυτό το τραγούδι! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Έχετε ακούσει το κομμάτι %n φορά.Έχετε ακούσει το κομμάτι %n φορές. - + You've never listened to this track before. Δεν έχετε ακούσει αυτό το κομμάτι παλιότερα. - + You first listened to it on %1. Το ακούσατε για πρώτη φορά στις %1. - + You've listened to %1 %n time(s). Ακούσατε το %1 %n φορά.Ακούσατε το %1 %n φορές. - + You've never listened to %1 before. Δεν έχετε ακούσει το %1 ποτέ πριν. @@ -3932,7 +3988,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Συγγνώμη, το φίλτρο «%1» δεν αντιστοίχισε αποτελέσματα. @@ -4101,39 +4157,6 @@ You can re-send a sync message at any time simply by sending another tweet using Δεν υπάρχουν προτάσεις για ακρόαση εδώ. - - WelcomeWidget - - - Recent Additions - Τελευταίες Προσθήκες - - - - Newest Stations & Playlists - Τελευταίοι Σταθμοί & Λίστες Αναπαραγωγής - - - - Recently Played Tracks - Τελευταίες Αναπαραγωγές Κομματιών - - - - Recently played tracks - Τελευταίες αναπαραγωγές κομματιών - - - - No recently created playlists in your network. - Δεν δημιουργήθηκαν πρόσφατα λίστες αναπαραγωγής στο δίκτυό σας. - - - - Welcome to Tomahawk - Καλωσήρθατε στο Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index bb7f4e4db8..66ad23670c 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -308,39 +308,39 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Related Artists - + Albums Albums - + Sorry, we could not find any albums for this artist! Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! Sorry, we could not find any top hits for this artist! - - CHART # - CHART # + + # IN YOUR CHARTS + # IN YOUR CHARTS @@ -513,6 +513,39 @@ connect and stream from you? Failed to send crash info. + + Dashboard + + + Recently Played Tracks + Recently Played Tracks + + + + Recent Additions + Recent Additions + + + + Newest Stations & Playlists + Newest Stations & Playlists + + + + Recently played tracks + Recently played tracks + + + + No recently created playlists in your network. + No recently created playlists in your network. + + + + Welcome to Tomahawk + Welcome to Tomahawk + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -731,6 +764,39 @@ Password Login + + HistoryWidget + + + From: + From: + + + + To: + To: + + + + Recently Played Tracks + Recently Played Tracks + + + + Your recently played tracks + Your recently played tracks + + + + %1's recently played tracks + %1's recently played tracks + + + + Sorry, we could not find any recent plays! + Sorry, we could not find any recent plays! + + InboxItem @@ -1035,27 +1101,32 @@ Password NetworkActivityWidget - + Charts Charts - + Last Week Last Week - + Last Month Last Month - + Last Year Last Year - + + Overall + Overall + + + Network Activity Network Activity @@ -1159,42 +1230,42 @@ Password Accuracy - + Perfect match Perfect match - + Very good match Very good match - + Good match Good match - + Vague match Vague match - + Bad match Bad match - + Very bad match Very bad match - + Not available Not available - + Searching... Searching... @@ -1498,17 +1569,17 @@ Password QueueView - + Open Queue Open Queue - + Open Queue - %n item(s) Open Queue - %n itemOpen Queue - %n items - + Close Queue Close Queue @@ -1942,56 +2013,36 @@ Password SourceItem - - + + Latest Additions Latest Additions - + Recently Played Recently Played - + SuperCollection SuperCollection - + Latest additions to your collection Latest additions to your collection - + Latest additions to %1's collection Latest additions to %1's collection - + Sorry, we could not find any recent additions! Sorry, we could not find any recent additions! - - - Recently Played Tracks - Recently Played Tracks - - - - Your recently played tracks - Your recently played tracks - - - - %1's recently played tracks - %1's recently played tracks - - - - Sorry, we could not find any recent plays! - Sorry, we could not find any recent plays! - SourceTreeView @@ -2214,7 +2265,7 @@ Password StatsGauge - + out of %1 out of %1 @@ -3591,42 +3642,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and and - + You You - + you you - + and and - + %n other(s) %n other%n others - + %n people %n people%n people - + loved this track loved this track - + sent you this track %1 sent you this track %1 @@ -3892,42 +3943,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Similar Tracks - + Sorry, but we could not find similar tracks for this song! Sorry, but we could not find similar tracks for this song! - - PLAYS - PLAYS + + # PLAYS / ARTIST + # PLAYS / ARTIST + + + + # IN YOUR CHARTS + # IN YOUR CHARTS - + You've listened to this track %n time(s). You've listened to this track %n time.You've listened to this track %n times. - + You've never listened to this track before. You've never listened to this track before. - + You first listened to it on %1. You first listened to it on %1. - + You've listened to %1 %n time(s). You've listened to %1 %n time.You've listened to %1 %n times. - + You've never listened to %1 before. You've never listened to %1 before. @@ -3935,7 +3991,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Sorry, your filter '%1' did not match any results. @@ -4109,39 +4165,6 @@ You can re-send a sync message at any time simply by sending another tweet using No listening suggestions here. - - WelcomeWidget - - - Recent Additions - Recent Additions - - - - Newest Stations & Playlists - Newest Stations & Playlists - - - - Recently Played Tracks - Recently Played Tracks - - - - Recently played tracks - Recently played tracks - - - - No recently created playlists in your network. - No recently created playlists in your network. - - - - Welcome to Tomahawk - Welcome to Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 36092db24a..f5dcddd618 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -308,38 +308,38 @@ conectarse a usted y transmitir música? ArtistInfoWidget - + Top Hits Grandes éxitos - + Related Artists Artistas relacionados - + Albums Álbumes - + Sorry, we could not find any albums for this artist! No se encontraron álbumes de este artista - + Sorry, we could not find any related artists! No se encontraron artistas relacionados - + Sorry, we could not find any top hits for this artist! No se encontraron éxitos de este artista - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ conectarse a usted y transmitir música? Error al enviar el informe del fallo. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ conectarse a usted y transmitir música? FlexibleView - + This playlist is currently empty. Lista de reproducción vacía - + This playlist is currently empty. Add some tracks to it and enjoy the music! Lista de reproducción vacía. ¡Añada pistas y disfrute de la música! @@ -730,6 +763,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1034,27 +1100,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1158,42 +1229,42 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia muy buena - + Good match Coincidencia buena - + Vague match Coincidencia vaga - + Bad match Mala coincidencia - + Very bad match Muy mala coincidencia - + Not available No disponible - + Searching... @@ -1497,17 +1568,17 @@ Password QueueView - + Open Queue Abrir cola - + Open Queue - %n item(s) Abrir cola - %n pistaAbrir cola - %n pistas - + Close Queue Cerrar cola @@ -1941,56 +2012,36 @@ y estaciones basadas en sus gustos personales. SourceItem - - + + Latest Additions Añadidos recientemente - + Recently Played Reproducido recientemente - + SuperCollection Supercolección - + Latest additions to your collection Pistas añadidas recientemente - + Latest additions to %1's collection Pistas añadidas recientemente en la colección de %1 - + Sorry, we could not find any recent additions! No se encontraron pistas nuevas recientes - - - Recently Played Tracks - Pistas reproducidas recientemente - - - - Your recently played tracks - Mis pistas escuchadas recientemente - - - - %1's recently played tracks - Canciones escuchadas recientemente por %1 - - - - Sorry, we could not find any recent plays! - No hay reproducciones recientes - SourceTreeView @@ -2213,7 +2264,7 @@ y estaciones basadas en sus gustos personales. StatsGauge - + out of %1 @@ -3589,42 +3640,42 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3890,42 +3941,47 @@ introduzca su número PIN aquí: TrackInfoWidget - + Similar Tracks Pistas similares - + Sorry, but we could not find similar tracks for this song! No se han encontrado pistas similares - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Ha escuchado esta pista %n vez.Ha escuchado esta pista %n veces. - + You've never listened to this track before. Nunca ha escuchado esta pista antes. - + You first listened to it on %1. Escuchó esta pista por primera vez en %1. - + You've listened to %1 %n time(s). Ha escuchado %1 una vez.Ha escuchado %1 %n veces. - + You've never listened to %1 before. Nunca ha escuchado %1 antes. @@ -3933,7 +3989,7 @@ introduzca su número PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. Lo siento, tu filtro '%1' no ha encontrado resultados. @@ -4107,39 +4163,6 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en - - WelcomeWidget - - - Recent Additions - Añadidos recientemente - - - - Newest Stations & Playlists - Nuevas estaciones y listas de reproducción - - - - Recently Played Tracks - Pistas reproducidas recientemente - - - - Recently played tracks - Pistas reproducidas recientemente - - - - No recently created playlists in your network. - No hay listas de reproducción recientemente creadas en su red. - - - - Welcome to Tomahawk - Bienvenido a Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 60481ea7a6..3362534845 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -308,38 +308,38 @@ yhdistää ja toistaa sinulta virtaa? ArtistInfoWidget - + Top Hits Parhaat hitit - + Related Artists Samankaltaisia artisteja - + Albums Albumit - + Sorry, we could not find any albums for this artist! Valitettavasti emme löytänet yhtään tämän artistin albumia! - + Sorry, we could not find any related artists! Valitettavasti emme löytäneet yhtään samankaltaista artistia! - + Sorry, we could not find any top hits for this artist! Valitettavasti emme löytäneet yhtään tämän artistin parhaista hiteistä! - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ yhdistää ja toistaa sinulta virtaa? Kaatumistietojen lähettäminen epäonnistui. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ yhdistää ja toistaa sinulta virtaa? FlexibleView - + This playlist is currently empty. Tämä soittolista on parhaillaan tyhjä. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tämä soittolista on parhaillaan tyhjä. Lisää kappaleita ja nauti musiikista! @@ -731,6 +764,39 @@ salasana Kirjaudu + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1035,27 +1101,32 @@ salasana NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1159,42 +1230,42 @@ salasana Tarkkuus - + Perfect match Täysosuma - + Very good match Erittäin hyvä osuma - + Good match Hyvä osuma - + Vague match Epämääräinen osuma - + Bad match Kehno osuma - + Very bad match Erittäin kehno osuma - + Not available Ei saatavilla - + Searching... Haetaan... @@ -1498,17 +1569,17 @@ salasana QueueView - + Open Queue Avaa jono - + Open Queue - %n item(s) Avaa jono – %n kohdeAvaa jono – %n kohdetta - + Close Queue Sulje jono @@ -1944,56 +2015,36 @@ käyttäjäradion käyttöönottamiseksi SourceItem - - + + Latest Additions Viimeisimmät lisäykset - + Recently Played Viime aikoina kuunnellut - + SuperCollection Superkokoelma - + Latest additions to your collection Viimeisimmät lisäykset kokoelmaasi - + Latest additions to %1's collection Viimeisimmät lisäykset käyttäjän %1 kokoelmaan - + Sorry, we could not find any recent additions! Valitettavasti emme löytäneet yhtään viimeaikaisia lisäyksiä! - - - Recently Played Tracks - Viime aikoina kuunnellut kappaleet - - - - Your recently played tracks - Viime aikoina kuuntelemasi kappaleet - - - - %1's recently played tracks - Käyttäjän %1 viime aikoina kuuntelemat kappaleet - - - - Sorry, we could not find any recent plays! - Valitettavasti emme löytäneet yhtään viimeaikaisia soittoja! - SourceTreeView @@ -2217,7 +2268,7 @@ napsauttamalla hiiren oikealla. StatsGauge - + out of %1 @@ -3594,42 +3645,42 @@ kappaleen %2%4 %3. Tomahawk::Track - + and ja - + You Sinä - + you sinä - + and ja - + %n other(s) %n muu%n muuta - + %n people %n ihminen%n ihmistä - + loved this track tykkäsi tästä kappaleesta - + sent you this track %1 lähetti sinulle kappaleen %1 @@ -3895,42 +3946,47 @@ anna siellä näytetty PIN-koodi tähän: TrackInfoWidget - + Similar Tracks Samankaltaisia kappaleita - + Sorry, but we could not find similar tracks for this song! Valitettavasti emme löytäneet tälle kappaleelle samankaltaisia kappaleita! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Olet kuunnellut tätä kappaletta %n kerran.Olet kuunnellut tätä kappaletta %n kertaa. - + You've never listened to this track before. Et ole kuunnellut tätä kappaletta aiemmin. - + You first listened to it on %1. Kuuntelit sitä ensi kerran %1. - + You've listened to %1 %n time(s). Olet kuunnellut artistia %1 %n kerran.Olet kuunnellut artistia %1 %n kertaa. - + You've never listened to %1 before. Et ole kuunnellut artistia %1 aiemmin. @@ -3938,7 +3994,7 @@ anna siellä näytetty PIN-koodi tähän: TrackView - + Sorry, your filter '%1' did not match any results. Valitettavasti suodattimesi ”%1” ei tuottanut yhtään tuloksia. @@ -4112,39 +4168,6 @@ Voit lähettää synkronointiviestin uudelleen millä hetkellä hyvänsä lähet Ei kuunteluehdotuksia. - - WelcomeWidget - - - Recent Additions - Viimeisimmät lisäykset - - - - Newest Stations & Playlists - Uusimmat asemat ja soittolistat - - - - Recently Played Tracks - Viime aikoina kuunnellut kappaleet - - - - Recently played tracks - Viime aikoina kuunnellut kappaleet - - - - No recently created playlists in your network. - Verkossasi ei ole viime aikoina luotuja soittolistoja. - - - - Welcome to Tomahawk - Tervetuloa Tomahawkiin - - WhatsHotWidget diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index f23af3c85f..0465c2d375 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -308,38 +308,38 @@ de se connecter et streamer de vous? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artistes similaires - + Albums Albums - + Sorry, we could not find any albums for this artist! Désolé, on a pas pu trouver aucun album pour cet artiste! - + Sorry, we could not find any related artists! Désolé, on a rien trouvé par rapport a cet artite! - + Sorry, we could not find any top hits for this artist! Désolé, on a pas pu trouver aucun top hit pour cet artiste! - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ de se connecter et streamer de vous? Échec de l'envoi des informations de plantage. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ de se connecter et streamer de vous? FlexibleView - + This playlist is currently empty. Cette liste de lecture est vide. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Cette liste de lecture est vide. Ajoutez des morceaux et profitez de la musique ! @@ -730,6 +763,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1034,27 +1100,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1158,42 +1229,42 @@ Password Précision - + Perfect match Correspondance parfaite - + Very good match Très bonne correspondance - + Good match Bonne correspondance - + Vague match Vague correspondance - + Bad match Mauvaise correspondance - + Very bad match Très mauvaise correspondance - + Not available Indisponible - + Searching... Recherche... @@ -1497,17 +1568,17 @@ Password QueueView - + Open Queue Ouvrir la file d'attente - + Open Queue - %n item(s) Ouvrir la file d'attente - %n élémentOuvrir la file d'attente - %n éléments - + Close Queue Fermer la file d'attente @@ -1938,56 +2009,36 @@ Password SourceItem - - + + Latest Additions Derniers ajouts - + Recently Played Joués récemment - + SuperCollection SuperCollection - + Latest additions to your collection Derniers ajouts à votre collection - + Latest additions to %1's collection Derniers ajouts à la collection de %1 - + Sorry, we could not find any recent additions! Désolé, on a pas pu trouver des dernier ajouts! - - - Recently Played Tracks - Derniers titres joués - - - - Your recently played tracks - Les derniers titres que vous avez joués - - - - %1's recently played tracks - Derniers titres joués par %1 - - - - Sorry, we could not find any recent plays! - Désolé, aucune piste récemment jouée n'a pu être trouvée ! - SourceTreeView @@ -2210,7 +2261,7 @@ Password StatsGauge - + out of %1 @@ -3586,42 +3637,42 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Track - + and et - + You Votre - + you votre - + and et - + %n other(s) %n autre%n autres - + %n people %n personne%n personnes - + loved this track a aimé cette piste - + sent you this track %1 vous a envoyé cette piste %1 @@ -3887,42 +3938,47 @@ saisissez le numéro PIN ici : TrackInfoWidget - + Similar Tracks Piste similaire - + Sorry, but we could not find similar tracks for this song! Désolé, nous n'avons pu trouver aucune piste similaire pour cette chanson ! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Vous avez écouté cette piste %n fois.Vous avez écouté cette piste %n fois. - + You've never listened to this track before. Vous n'avez encore jamais écouté cette piste. - + You first listened to it on %1. Vous l'avez écouté pour la première fois le %1. - + You've listened to %1 %n time(s). Vous avez écouté %1 %n fois.Vous avez écouté %1 %n fois. - + You've never listened to %1 before. Vous n'avez encore jamais écouté %1. @@ -3930,7 +3986,7 @@ saisissez le numéro PIN ici : TrackView - + Sorry, your filter '%1' did not match any results. Désolé, votre filtre '%1' ne correspond à aucun résultat. @@ -4104,39 +4160,6 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env Aucune suggestion d'écoute. - - WelcomeWidget - - - Recent Additions - Derniers Ajouts - - - - Newest Stations & Playlists - Dernières stations et listes de lecture - - - - Recently Played Tracks - Joués récemment - - - - Recently played tracks - Dernières titres joués - - - - No recently created playlists in your network. - Pas de liste de lecture créée récemment sur votre réseau. - - - - Welcome to Tomahawk - Bienvenue dans Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index dc79af230c..06f95febcb 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Maiores éxitos - + Related Artists Artistas relacionados - + Albums Álbums - + Sorry, we could not find any albums for this artist! Non se atopou ningún álbum para este artista! - + Sorry, we could not find any related artists! Non se atopou ningún artista relacionado! - + Sorry, we could not find any top hits for this artist! Non se atopou ningún éxito deste artista! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? Fallou o envío do informe do erro. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Esta lista de reprodución está baleira. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodución está baleira. Engádelle algunhas pistas para gozar da música! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia moi boa - + Good match Boa coincidencia - + Vague match Parcialmente coincidente - + Bad match Mala coincidencia - + Very bad match Nada coincidentes - + Not available Non está dispoñíbel - + Searching... @@ -1496,17 +1567,17 @@ Password QueueView - + Open Queue Abrir a ringleira - + Open Queue - %n item(s) Abrir a ringleira - %n elementoAbrir a ringleira - %n elementos - + Close Queue Pechar a ringleira @@ -1937,56 +2008,36 @@ Password SourceItem - - + + Latest Additions Últimos engadidos - + Recently Played Escoitados recentemente - + SuperCollection Supercolección - + Latest additions to your collection Os últimos engadidos á túa colección - + Latest additions to %1's collection Os últimos engadidos a colección %1 - + Sorry, we could not find any recent additions! Non se atoparon novas pistas engadidas recentemente! - - - Recently Played Tracks - Pistas recentemente reproducidas - - - - Your recently played tracks - As pistas que soaron recentemente - - - - %1's recently played tracks - As pistas de %1 que soaron recentemente - - - - Sorry, we could not find any recent plays! - Non se escoitaron pistas recentemente! - SourceTreeView @@ -2211,7 +2262,7 @@ Password StatsGauge - + out of %1 @@ -3588,42 +3639,42 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3889,42 +3940,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Pistas parecidas - + Sorry, but we could not find similar tracks for this song! Non se atopan pistas parecidas a esta canción! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Escoitaches esta pista %n vece(s).Escoitaches esta pista %n vece(s). - + You've never listened to this track before. Nunca antes escoitaras esta pista. - + You first listened to it on %1. Escoitaches esta pista por primeira vez en %1. - + You've listened to %1 %n time(s). Escoitaches %1 %n veces.Escoitaches %1 %n veces. - + You've never listened to %1 before. Nunca antes escoitaras a %1. @@ -3932,7 +3988,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. O filtro «%1» non dá ningún resultado. @@ -4106,39 +4162,6 @@ Podes reenviar e sincronizar as mensaxes en calquera momento simplemente enviand - - WelcomeWidget - - - Recent Additions - Engadidos recentemente - - - - Newest Stations & Playlists - Novas estacións e listas de reprodución - - - - Recently Played Tracks - Pistas recentemente reproducidas - - - - Recently played tracks - Pistas recentemente reproducidas - - - - No recently created playlists in your network. - Non hai listas creadas recentemente na túa rede. - - - - Welcome to Tomahawk - Benvidos a Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index f97d9a9644..d6bcda2b62 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1495,17 +1566,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1936,56 +2007,36 @@ Password SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - - - - - Your recently played tracks - - - - - %1's recently played tracks - - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2208,7 +2259,7 @@ Password StatsGauge - + out of %1 @@ -3574,42 +3625,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3874,42 +3925,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3917,7 +3973,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4086,39 +4142,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - - - - - Newest Stations & Playlists - - - - - Recently Played Tracks - - - - - Recently played tracks - - - - - No recently created playlists in your network. - - - - - Welcome to Tomahawk - - - WhatsHotWidget diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index b5e3aa234a..a9ae4a5475 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Kapcsolódó előadók - + Albums Albumok - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1495,17 +1566,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1936,56 +2007,36 @@ Password SourceItem - - + + Latest Additions - + Recently Played Mostanában játszott - + SuperCollection Szuper kollekció - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - Monstanában játszott zeneszámok - - - - Your recently played tracks - - - - - %1's recently played tracks - - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2208,7 +2259,7 @@ Password StatsGauge - + out of %1 @@ -3574,42 +3625,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3874,42 +3925,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Hasonló zeneszámok - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3917,7 +3973,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4086,39 +4142,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - - - - - Newest Stations & Playlists - - - - - Recently Played Tracks - - - - - Recently played tracks - Mostanában játszott zeneszámok - - - - No recently created playlists in your network. - - - - - Welcome to Tomahawk - - - WhatsHotWidget diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index b70b8f18ab..d9afdc001b 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1495,17 +1566,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1936,56 +2007,36 @@ Password SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - - - - - Your recently played tracks - - - - - %1's recently played tracks - - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2208,7 +2259,7 @@ Password StatsGauge - + out of %1 @@ -3574,42 +3625,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3874,42 +3925,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3917,7 +3973,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4086,39 +4142,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - - - - - Newest Stations & Playlists - - - - - Recently Played Tracks - - - - - Recently played tracks - - - - - No recently created playlists in your network. - - - - - Welcome to Tomahawk - - - WhatsHotWidget diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 3ff4315115..652601230b 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artisti simili - + Albums Album - + Sorry, we could not find any albums for this artist! Ci dispiace, ma non abbiamo trovato nessun album di questo artista! - + Sorry, we could not find any related artists! Siamo spiacenti, non è stato possibile trovare artisti simili! - + Sorry, we could not find any top hits for this artist! Ci dispiace, ma non abbiamo trovato alcun risultato top per l'artista! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? Crash report non inviato. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Questa playlist al momento è vuota. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Questa playlist al momento è vuota. Aggiungi qualche traccia e goditi la musica @@ -730,6 +763,39 @@ temporanea Login + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1034,27 +1100,32 @@ temporanea NetworkActivityWidget - + Charts Classifiche - + Last Week Ultima settimana - + Last Month Ultimo mese - + Last Year Ultimo anno - + + Overall + + + + Network Activity Attività network @@ -1158,42 +1229,42 @@ temporanea Precisione - + Perfect match Abbinamento perfetto - + Very good match Abbinamento molto buono - + Good match Buon abbinamento - + Vague match Vaga corrispondenza - + Bad match Brutto abbinamento - + Very bad match Pessimo abbinamento - + Not available Non disponibile - + Searching... Sto cercando... @@ -1496,17 +1567,17 @@ temporanea QueueView - + Open Queue Espandi coda - + Open Queue - %n item(s) Espandi coda - un elementoEspandi coda - %n elementi - + Close Queue Collassa coda @@ -1937,56 +2008,36 @@ temporanea SourceItem - - + + Latest Additions Ultime aggiunte - + Recently Played Ascoltate di recente - + SuperCollection Supercollezione - + Latest additions to your collection Ultime aggiunte alla tua collezione - + Latest additions to %1's collection Ultime aggiunte alla collezione di %1 - + Sorry, we could not find any recent additions! Spiacente, non abbiamo trovato nessuna aggiunta recente! - - - Recently Played Tracks - Tracce ascoltate di recente - - - - Your recently played tracks - Tracce da te ascoltate di recente - - - - %1's recently played tracks - Tracce ascoltate di recente da %1 - - - - Sorry, we could not find any recent plays! - Spiacente, non è stato possibile trovare ascolti recenti! - SourceTreeView @@ -2209,7 +2260,7 @@ temporanea StatsGauge - + out of %1 @@ -3576,42 +3627,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and e - + You Tu - + you tu - + and e - + %n other(s) U%n altro%n altri - + %n people %n persona%n persone - + loved this track amato questa traccia - + sent you this track %1 ti ha spedito questa traccia %1 @@ -3876,42 +3927,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Tracce simili - + Sorry, but we could not find similar tracks for this song! Ci dispiace, ma non abbiamo trovato tracce simili per questa canzone! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Hai ascoltato questa traccia una volta.Hai ascoltato questa traccia %n volte. - + You've never listened to this track before. Non hai mai ascoltato questa traccia. - + You first listened to it on %1. L'hai ascoltata la prima volta su %1. - + You've listened to %1 %n time(s). Hai ascoltato %1 una volta.Hai ascoltato %1 %n volte. - + You've never listened to %1 before. Non hai mai ascoltato %1. @@ -3919,7 +3975,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Spiacente, il tuo filtro %1 non ha trovato nessun risultato. @@ -4088,39 +4144,6 @@ You can re-send a sync message at any time simply by sending another tweet using Nessun suggerimento musicale qui. - - WelcomeWidget - - - Recent Additions - Aggiunte recenti - - - - Newest Stations & Playlists - Nuove stazioni &playlist - - - - Recently Played Tracks - Tracce recentemente ascoltate - - - - Recently played tracks - Tracce riprodotte di recente - - - - No recently created playlists in your network. - Nessuna playlist creata di recente nel tuo network. - - - - Welcome to Tomahawk - Benvenuto in Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 662819c921..638b7288c5 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 大ヒット曲 - + Related Artists 似たアーティスト - + Albums アルバム - + Sorry, we could not find any albums for this artist! このアーティストのアルバムは見つかりませんでした。 - + Sorry, we could not find any related artists! 関連アーティストは見つかりませんでした。 - + Sorry, we could not find any top hits for this artist! このアーティストの大ヒット曲は見つかりませんでした。 - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ connect and stream from you? クラッシュ情報の送信に失敗しました。 + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. このプレイリストには何も入っていません。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! プレイリストには何も入っていません。トラックを追加して、音楽を楽しみましょう! @@ -730,6 +763,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1034,27 +1100,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1158,42 +1229,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1497,17 +1568,17 @@ Password QueueView - + Open Queue キューを表示 - + Open Queue - %n item(s) キューを表示 - %n項目 - + Close Queue キューを隠す @@ -1941,56 +2012,36 @@ Password SourceItem - - + + Latest Additions 最新追加した項目 - + Recently Played 最近聴いたトラック - + SuperCollection スーパーコレクション - + Latest additions to your collection コレクションの最新追加した項目 - + Latest additions to %1's collection %1のコレクションの最新追加した項目 - + Sorry, we could not find any recent additions! 最近追加した項目が見つかりませんでした。 - - - Recently Played Tracks - 最近再生したトラック - - - - Your recently played tracks - あなたの最近再生したトラック - - - - %1's recently played tracks - %1の最近再生したトラック - - - - Sorry, we could not find any recent plays! - 最近の再生した項目が見つかりませんでした。 - SourceTreeView @@ -2213,7 +2264,7 @@ Password StatsGauge - + out of %1 @@ -3586,42 +3637,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3887,42 +3938,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 似ているトラック - + Sorry, but we could not find similar tracks for this song! この曲に似ているトラックが見つかりませんでした。 - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). このトラックは%n回聴いています。 - + You've never listened to this track before. このトラックを一度も聴いていません。 - + You first listened to it on %1. 初めてこの曲を聴いたのは、%1です。 - + You've listened to %1 %n time(s). %1を%n回聴いています。 - + You've never listened to %1 before. %1を一度も聴いていません。 @@ -3930,7 +3986,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. %1に一致する結果は見つかりませんでした。 @@ -4104,39 +4160,6 @@ Twitterを使っている友達にTomahawkを接続したいなら、ツイー - - WelcomeWidget - - - Recent Additions - 最近追加した項目 - - - - Newest Stations & Playlists - 最新のステーション & プレイリスト - - - - Recently Played Tracks - 最近再生したトラック - - - - Recently played tracks - 最近再生したトラック - - - - No recently created playlists in your network. - ネットワークに最近作成したプレイリストはありません。 - - - - Welcome to Tomahawk - Tomahawkへようこそ - - WhatsHotWidget diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index ac19735c21..5d6c59acaf 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists Susiję atlikėjai - + Albums Albumai - + Sorry, we could not find any albums for this artist! Atsiprašome, neradome jokių šio atlikėjo albumų! - + Sorry, we could not find any related artists! Atsiprašome, neradome jokių susijusių atlikėjų! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? Nepavyko nusiųsti strigties informacijos. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1495,17 +1566,17 @@ Password QueueView - + Open Queue Atverti eilę - + Open Queue - %n item(s) Atverti eilę - %n elementasAtverti eilę - %n elementaiAtverti eilę - %n elementų - + Close Queue Užverti eilę @@ -1936,56 +2007,36 @@ Password SourceItem - - + + Latest Additions Neseniai pridėta - + Recently Played Neseniai grota - + SuperCollection Super Kolekcija - + Latest additions to your collection Neseniai pridėta prie Jūsų kolekcijos - + Latest additions to %1's collection Neseniai pridėta prie %1 kolekcijos - + Sorry, we could not find any recent additions! Atsiprašome, neradome nieko, kas pridėta neseniai! - - - Recently Played Tracks - Neseniai groti takeliai - - - - Your recently played tracks - Jūsų neseniai groti takeliai - - - - %1's recently played tracks - %1 neseniai groti takeliai - - - - Sorry, we could not find any recent plays! - Atsiprašome, neradome jokių neseniai grotų takelių! - SourceTreeView @@ -2208,7 +2259,7 @@ Password StatsGauge - + out of %1 @@ -3574,42 +3625,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3874,42 +3925,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Panašūs takeliai - + Sorry, but we could not find similar tracks for this song! Atsiprašome, neradome jokių į šią dainą panašių takelių! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Jūs klausėtės šio takelio %n kartą.Jūs klausėtės šio takelio %n kartus.Jūs klausėtės šio takelio %n kartų. - + You've never listened to this track before. Jūs niekad anksčiau nesiklausėte šio takelio. - + You first listened to it on %1. Pirmąkart klausėtės jo %1. - + You've listened to %1 %n time(s). Jūs klausėtės %1 %n kartą.Jūs klausėtės %1 %n kartus.Jūs klausėtės %1 %n kartų. - + You've never listened to %1 before. Jūs niekada anksčiau nesiklausėte %1. @@ -3917,7 +3973,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4086,39 +4142,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - Neseniai pridėta - - - - Newest Stations & Playlists - Naujausios stotys ir grojaraščiai - - - - Recently Played Tracks - Neseniai groti takeliai - - - - Recently played tracks - - - - - No recently created playlists in your network. - Jūsų tinkle nėra neseniai sukurtų grojaraščių. - - - - Welcome to Tomahawk - - - WhatsHotWidget diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 72a7d3306a..b9a1788481 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -308,38 +308,38 @@ połączyć się i strumieniować od ciebie? ArtistInfoWidget - + Top Hits Hity na Topie - + Related Artists Powiązani artyści - + Albums Albumy - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ połączyć się i strumieniować od ciebie? Nie udało sie wysłać informacji o awarii. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ połączyć się i strumieniować od ciebie? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -730,6 +763,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1034,27 +1100,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1158,42 +1229,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1497,17 +1568,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1938,56 +2009,36 @@ Password SourceItem - - + + Latest Additions Ostatnio Dodane - + Recently Played Ostatnio Odtworzone - + SuperCollection Superkolekcja - + Latest additions to your collection Ostatnio dodane do twojej kolekcji - + Latest additions to %1's collection Ostatnio dodane do kolekcji %1 - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - Ostatnio odtwarzane utwory - - - - Your recently played tracks - Twoje ostatnio odtwarzane utwory - - - - %1's recently played tracks - Utwory ostatnio odtwarzane przez %1 - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2210,7 +2261,7 @@ Password StatsGauge - + out of %1 @@ -3583,42 +3634,42 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3884,42 +3935,47 @@ wprowadź pokazany numer PIN tutaj: TrackInfoWidget - + Similar Tracks Podobne utwory - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Słuchałeś tego utworu %n raz.Słuchałeś tego utworu %n razy.Słuchałeś tego utworu %n razy. - + You've never listened to this track before. Nie słuchałeś wcześniej tego utworu. - + You first listened to it on %1. Pierwszy raz słuchałeś tego utworu %1. - + You've listened to %1 %n time(s). Słuchałeś %1 %n raz.Słuchałeś %1 %n razy.Słuchałeś %1 %n razy. - + You've never listened to %1 before. Nie słuchałeś wcześniej %1. @@ -3927,7 +3983,7 @@ wprowadź pokazany numer PIN tutaj: TrackView - + Sorry, your filter '%1' did not match any results. Przepraszamy, twój filtr '%1' nie dopasował żadnych wyników. @@ -4101,39 +4157,6 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl - - WelcomeWidget - - - Recent Additions - Ostatnio Dodane - - - - Newest Stations & Playlists - Najnowsze Stacje i Listy - - - - Recently Played Tracks - Ostatnio Odtworzone Utwory - - - - Recently played tracks - - - - - No recently created playlists in your network. - Brak ostatnio utworzonych list w twojej sieci. - - - - Welcome to Tomahawk - Witaj w Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index ec30b95c9d..915550a686 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -308,38 +308,38 @@ se conecte e faça o stream de você? ArtistInfoWidget - + Top Hits Mais Tocadas - + Related Artists Artistas Relacionados - + Albums Álbuns - + Sorry, we could not find any albums for this artist! Desculpe, mas não conseguimos encontrar outros álbuns para este artista! - + Sorry, we could not find any related artists! Desculpe, mas não conseguimos encontrar outros artistas relacionados a este! - + Sorry, we could not find any top hits for this artist! Desculpe, mas não conseguimos encontrar outras faixas mais tocadas deste artista! - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ se conecte e faça o stream de você? Falha ao enviar as informações de erro. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ se conecte e faça o stream de você? FlexibleView - + This playlist is currently empty. Esta lista de reprodução está vazia no momento. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodução está vazia no momento. Adicione algumas faixas a ela e aproveite a música! @@ -730,6 +763,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1034,27 +1100,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1158,42 +1229,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1497,17 +1568,17 @@ Password QueueView - + Open Queue Abrir Lista - + Open Queue - %n item(s) Abrir Fila - %n itemAbrir Fila - %n itens - + Close Queue Fechar Lista @@ -1938,56 +2009,36 @@ Password SourceItem - - + + Latest Additions Últimas Adições - + Recently Played Ouvidas Recentemente - + SuperCollection SuperColeção - + Latest additions to your collection Últimas adições à sua coleção - + Latest additions to %1's collection Últimas adições à coleção de %1 - + Sorry, we could not find any recent additions! Desculpe, não foi possível encontrar adições recentes! - - - Recently Played Tracks - Faixas Reproduzidas Recentemente - - - - Your recently played tracks - Suas faixas reproduzidas recentemente - - - - %1's recently played tracks - Faixas reproduzidas recentemente por %1 - - - - Sorry, we could not find any recent plays! - Desculpe, não foi possível encontrar playlists recentes! - SourceTreeView @@ -2210,7 +2261,7 @@ Password StatsGauge - + out of %1 @@ -3583,42 +3634,42 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3884,42 +3935,47 @@ colocar o número PIN mostrado aqui: TrackInfoWidget - + Similar Tracks Faixas Similares - + Sorry, but we could not find similar tracks for this song! Desculpe, mas não conseguimos encontrar faixas similares para esta música! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Você ouviu esta faixa %n vez.Você ouviu esta faixa %n vezes. - + You've never listened to this track before. Você nunca ouviu esta faixa antes. - + You first listened to it on %1. Você ouviu pela primeira vez em %1. - + You've listened to %1 %n time(s). Você ouviu %1 %n vez.Você ouviu %1 %n vezes. - + You've never listened to %1 before. Você nunca ouviu %1 antes. @@ -3927,7 +3983,7 @@ colocar o número PIN mostrado aqui: TrackView - + Sorry, your filter '%1' did not match any results. Desculpe, o seu filtro '%1' não encontreou nenhum resultado. @@ -4101,39 +4157,6 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment - - WelcomeWidget - - - Recent Additions - Mais recentes - - - - Newest Stations & Playlists - Estações e listas mais recentes - - - - Recently Played Tracks - Faixas reproduzidas recentemente - - - - Recently played tracks - Faixas reproduzidas recentemente - - - - No recently created playlists in your network. - Nenhuma lista de reprodução criada recentemente na sua rede. - - - - Welcome to Tomahawk - Bem-vindo ao Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index f8f8bfba30..5c78778321 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -311,38 +311,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Хиты - + Related Artists Похожие исполнители - + Albums Альбомы - + Sorry, we could not find any albums for this artist! К сожалению, мы не смогли найти никаких альбомов этого исполнителя! - + Sorry, we could not find any related artists! К сожалению, мы не смогли найти никаких исполнители! - + Sorry, we could not find any top hits for this artist! К сожалению, мы не смогли найти никаких хитов этого исполнителя! - - CHART # + + # IN YOUR CHARTS @@ -516,6 +516,39 @@ connect and stream from you? Невозможно отправить отчет о ошибке. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -674,12 +707,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Плейлист пуст - + This playlist is currently empty. Add some tracks to it and enjoy the music! Этот плейлист пуст. Добавьте какие-нибудь песни и наслаждайтесь музыкой! @@ -733,6 +766,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1037,27 +1103,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1161,42 +1232,42 @@ Password Совпадение - + Perfect match Превосходное - + Very good match Очень Хорошое - + Good match Хорошое - + Vague match Расплывчатое - + Bad match Плохое совпадение - + Very bad match Очень плохое совпадение - + Not available Недоступно - + Searching... @@ -1500,17 +1571,17 @@ Password QueueView - + Open Queue Показать Очередь - + Open Queue - %n item(s) Открыть %n в ОчередиОткрыть %n в ОчередиОткрыть %n в Очереди - + Close Queue Спрятать Очередь @@ -1944,56 +2015,36 @@ Password SourceItem - - + + Latest Additions Последние Добавленные - + Recently Played Последние Воспроизводимые - + SuperCollection Общая Коллекция - + Latest additions to your collection Новые Поступления в Коллекцию - + Latest additions to %1's collection Новые поступления в коллекции %1 - + Sorry, we could not find any recent additions! К сожалению, мы не смогли найти никаких последних добавлений! - - - Recently Played Tracks - Недавно Воспроизводимые - - - - Your recently played tracks - Ваши Недавно Воспроизводимые - - - - %1's recently played tracks - %1 последние проиграные треки - - - - Sorry, we could not find any recent plays! - К сожалению, мы не смогли найти никаких воспроизвидений треков! - SourceTreeView @@ -2216,7 +2267,7 @@ Password StatsGauge - + out of %1 @@ -3590,42 +3641,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3890,42 +3941,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Похожие Песни - + Sorry, but we could not find similar tracks for this song! Извините, но мы не смогли найти похожие на эту песни ! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Вы слушали эту песню %n раз.Вы слушали эту песню %n раз.Вы слушали эту песню %n раз. - + You've never listened to this track before. Вы никогда не слушали эту песню раньше. - + You first listened to it on %1. Первый раз слушали эту песню %1. - + You've listened to %1 %n time(s). Вы слушали %1 %n раз.Вы слушали %1 %n раза.Вы слушали %1 %n раз. - + You've never listened to %1 before. Вы никогда не слушали %1 до этого. @@ -3933,7 +3989,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Ваш поиск '%1' недал результатов. @@ -4107,39 +4163,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - Последние Добавленные Песни - - - - Newest Stations & Playlists - Последние Станции и Плейлисты - - - - Recently Played Tracks - Последние Воспроизводимые Песни - - - - Recently played tracks - Недавно воспроизводимые песни - - - - No recently created playlists in your network. - Нет списков, созданных в последнее время в вашей сети. - - - - Welcome to Tomahawk - Добро пожаловать в Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 6a04555fb4..526d7d353c 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -308,38 +308,38 @@ ansluta och strömma från dig? ArtistInfoWidget - + Top Hits Största hits - + Related Artists Relaterade artister - + Albums Album - + Sorry, we could not find any albums for this artist! Tyvärr! Det gick inte hitta några album av denna artisten! - + Sorry, we could not find any related artists! Tyvärr! Det gick inte hitta några relaterade artister! - + Sorry, we could not find any top hits for this artist! Tyvärr! Det gick inte hitta några tophits av denna artisten - - CHART # + + # IN YOUR CHARTS @@ -513,6 +513,39 @@ ansluta och strömma från dig? Misslyckades skicka kraschrapport. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -671,12 +704,12 @@ ansluta och strömma från dig? FlexibleView - + This playlist is currently empty. Spellistan är för närvarande tom. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Spellistan är för tillfället tom. Lägg till några låtar och avnjut musiken! @@ -731,6 +764,39 @@ Password Logga in + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1035,27 +1101,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1159,42 +1230,42 @@ Password Exakthet - + Perfect match Perfekt matchning - + Very good match Mycket bra matchning - + Good match Bra matchning - + Vague match Svag matchning - + Bad match Dålig matchning - + Very bad match Väldigt dålig matchning - + Not available Inte tillgänglig - + Searching... Söker... @@ -1498,17 +1569,17 @@ Password QueueView - + Open Queue Öppna kö - + Open Queue - %n item(s) Öppna kö - %n artiklarÖppna kö - %n artiklar - + Close Queue Stäng kö @@ -1941,56 +2012,36 @@ och radiostationer baserat på din personliga profil SourceItem - - + + Latest Additions Senast tillagda - + Recently Played Senast spelade spår - + SuperCollection Supersamling - + Latest additions to your collection Senaste tillägget till ditt samling. - + Latest additions to %1's collection Senaste tillägget till %1's samling. - + Sorry, we could not find any recent additions! Tyvärr! Det gick inte hitta några nya tillägg! - - - Recently Played Tracks - Senast spelade spår - - - - Your recently played tracks - Dina senast spelade spår - - - - %1's recently played tracks - %1's senast spelade spår - - - - Sorry, we could not find any recent plays! - Tyvärr! Det gick inte hitta några nyligen spelade spår - SourceTreeView @@ -2213,7 +2264,7 @@ och radiostationer baserat på din personliga profil StatsGauge - + out of %1 @@ -3589,42 +3640,42 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Track - + and och - + You Du - + you du - + and och - + %n other(s) %n annan%n andra - + %n people %n person%n personer - + loved this track älskade detta spåret - + sent you this track %1 skickade spåret '%1' till dig @@ -3890,42 +3941,47 @@ anger du PIN-koden här: TrackInfoWidget - + Similar Tracks Liknande spår - + Sorry, but we could not find similar tracks for this song! Tyvärr! Det gick inte hitta några spår som liknande denna låten! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). Du har lyssnat på detta spåret %n gånger.Du har lyssnat på detta spåret %n gånger - + You've never listened to this track before. Du har inte lyssnat på detta spåret tidigare. - + You first listened to it on %1. Du har lyssnat på det på %1. - + You've listened to %1 %n time(s). Du har lyssnat på %1 %n gång.Du har lyssnat på %1 %n gånger. - + You've never listened to %1 before. Du har inte lyssnat på %1 tidigare. @@ -3933,7 +3989,7 @@ anger du PIN-koden här: TrackView - + Sorry, your filter '%1' did not match any results. Tyvärr, ditt filter '%1' gav inga träffar @@ -4107,39 +4163,6 @@ Du kan skicka om ett synkat meddelande när som helst genom att skicka ett tweet Det finns inga rekommendationer här. - - WelcomeWidget - - - Recent Additions - Senast tillagda - - - - Newest Stations & Playlists - Nyaste stationer & spellistor - - - - Recently Played Tracks - Senaste spelade spår - - - - Recently played tracks - Nyligen spelade spår - - - - No recently created playlists in your network. - Inga skapade spellistor i ditt nätverk på sistone. - - - - Welcome to Tomahawk - Välkommen till Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index d99fce8ed6..b2c103fbc5 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits En Çok Dinlenenler - + Related Artists Benzer Sanatçılar - + Albums Albümler - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? Çökme bilgisi gönderimi başarısız. + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1495,17 +1566,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1936,56 +2007,36 @@ Password SourceItem - - + + Latest Additions - + Recently Played - + SuperCollection - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - - - - - Your recently played tracks - - - - - %1's recently played tracks - - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2208,7 +2259,7 @@ Password StatsGauge - + out of %1 @@ -3574,42 +3625,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3874,42 +3925,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3917,7 +3973,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4086,39 +4142,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - - - - - Newest Stations & Playlists - - - - - Recently Played Tracks - - - - - Recently played tracks - - - - - No recently created playlists in your network. - - - - - Welcome to Tomahawk - - - WhatsHotWidget diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 0579cf4543..5c71225d28 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 最热歌曲 - + Related Artists 相关艺人 - + Albums 专辑 - + Sorry, we could not find any albums for this artist! 抱歉,未找到该艺术家的其他专辑! - + Sorry, we could not find any related artists! 抱歉,没有找到相关的艺术家! - + Sorry, we could not find any top hits for this artist! 抱歉,没有找到该艺术家的任何人气歌曲! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? 发送崩溃信息失败。 + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. 当前播放列表为空。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password 准确度 - + Perfect match 完美匹配 - + Very good match 极高匹配 - + Good match 高匹配 - + Vague match 模糊匹配 - + Bad match 低匹配 - + Very bad match 极低匹配 - + Not available - + Searching... @@ -1496,17 +1567,17 @@ Password QueueView - + Open Queue 打开队列 - + Open Queue - %n item(s) 打开队列 - %n 项 - + Close Queue 隐藏队列 @@ -1939,56 +2010,36 @@ Password SourceItem - - + + Latest Additions 最近添加 - + Recently Played 最近播放 - + SuperCollection 超级收藏 - + Latest additions to your collection 最近加入收藏的歌曲 - + Latest additions to %1's collection 最新加入 %1 收藏的项目 - + Sorry, we could not find any recent additions! 抱歉,未找到任何最近添加的音乐! - - - Recently Played Tracks - 最近播放歌曲 - - - - Your recently played tracks - 你最近播放的歌曲 - - - - %1's recently played tracks - %1最近播放的歌曲 - - - - Sorry, we could not find any recent plays! - 抱歉,未找到任何最近播放的音乐! - SourceTreeView @@ -2211,7 +2262,7 @@ Password StatsGauge - + out of %1 @@ -3584,42 +3635,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3885,42 +3936,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 相似歌曲 - + Sorry, but we could not find similar tracks for this song! 抱歉,无法找到与这首歌类似的歌曲! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). 你已经收听过此歌曲 %n 遍。 - + You've never listened to this track before. 你之前从未听过此歌曲。 - + You first listened to it on %1. 你第一次听的是 %1. - + You've listened to %1 %n time(s). 你已经听过 %1 有 %n 遍了。 - + You've never listened to %1 before. 你之前从未听过 %1。 @@ -3928,7 +3984,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. 抱歉,未找到任何匹配 '%1' 的结果。 @@ -4102,39 +4158,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - 最近新增 - - - - Newest Stations & Playlists - 最近的电台和播放列表 - - - - Recently Played Tracks - 最近播放歌曲 - - - - Recently played tracks - 最近播放的歌曲 - - - - No recently created playlists in your network. - 在你的网络中最近未创建播放列表 - - - - Welcome to Tomahawk - 欢迎使用 Tomahawk - - WhatsHotWidget diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index b4e3f04265..6dc743fdf5 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -307,38 +307,38 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 流行精選 - + Related Artists 相關演出者 - + Albums 專輯 - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - - CHART # + + # IN YOUR CHARTS @@ -512,6 +512,39 @@ connect and stream from you? 無法發送故障信息。 + + Dashboard + + + Recently Played Tracks + + + + + Recent Additions + + + + + Newest Stations & Playlists + + + + + Recently played tracks + + + + + No recently created playlists in your network. + + + + + Welcome to Tomahawk + + + DatabaseCommand_AllAlbums @@ -670,12 +703,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -729,6 +762,39 @@ Password + + HistoryWidget + + + From: + + + + + To: + + + + + Recently Played Tracks + + + + + Your recently played tracks + + + + + %1's recently played tracks + + + + + Sorry, we could not find any recent plays! + + + InboxItem @@ -1033,27 +1099,32 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + + Overall + + + + Network Activity @@ -1157,42 +1228,42 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... @@ -1495,17 +1566,17 @@ Password QueueView - + Open Queue - + Open Queue - %n item(s) - + Close Queue @@ -1936,56 +2007,36 @@ Password SourceItem - - + + Latest Additions 最新加入 - + Recently Played 最近播放的 - + SuperCollection 超級收藏 - + Latest additions to your collection - + Latest additions to %1's collection - + Sorry, we could not find any recent additions! - - - Recently Played Tracks - - - - - Your recently played tracks - - - - - %1's recently played tracks - - - - - Sorry, we could not find any recent plays! - - SourceTreeView @@ -2208,7 +2259,7 @@ Password StatsGauge - + out of %1 @@ -3574,42 +3625,42 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Track - + and - + You - + you - + and - + %n other(s) - + %n people - + loved this track - + sent you this track %1 @@ -3874,42 +3925,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - - PLAYS + + # PLAYS / ARTIST + + + + + # IN YOUR CHARTS - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3917,7 +3973,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4086,39 +4142,6 @@ You can re-send a sync message at any time simply by sending another tweet using - - WelcomeWidget - - - Recent Additions - 最近新增 - - - - Newest Stations & Playlists - - - - - Recently Played Tracks - 最近播放的曲目 - - - - Recently played tracks - - - - - No recently created playlists in your network. - 沒有最近建立的播放清單在您的網路。 - - - - Welcome to Tomahawk - 歡迎到 Tomahawk - - WhatsHotWidget From 482b0288e7196d37d9cd01b5d1957c29181ea51e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 9 Jun 2013 03:33:24 +0200 Subject: [PATCH 282/565] * Fixed coding style in AudioEngine. --- src/libtomahawk/audio/AudioEngine.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index 193cb5fbd8..7f0ebb5b9a 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -1191,19 +1191,22 @@ AudioEngine::playlist() const } -result_ptr AudioEngine::currentTrack() const +result_ptr +AudioEngine::currentTrack() const { return d_func()->currentTrack; } -query_ptr AudioEngine::stopAfterTrack() const +query_ptr +AudioEngine::stopAfterTrack() const { return d_func()->stopAfterTrack; } void -AudioEngine::onVolumeChanged(qreal volume) { +AudioEngine::onVolumeChanged( qreal volume ) +{ emit volumeChanged( volume * 100 ); } From 0f82e54f2a68692e19726414006e71fdabee27b1 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 11:47:00 +0200 Subject: [PATCH 283/565] Reconnect if ACL decision took too long --- src/libtomahawk/network/Connection.cpp | 12 +++-- src/libtomahawk/network/Connection.h | 3 +- src/libtomahawk/network/ControlConnection.cpp | 12 +++++ src/libtomahawk/network/ControlConnection.h | 1 + src/libtomahawk/network/Servent.cpp | 45 +++++++++++++++++++ src/libtomahawk/network/Servent.h | 4 ++ src/libtomahawk/network/Servent_p.h | 2 + 7 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 47986cb93a..21cf176fbd 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -207,23 +207,27 @@ Connection::checkACL() } QString nodeid = property( "nodeid" ).toString(); - QString bareName = name().contains( '/' ) ? name().left( name().indexOf( "/" ) ) : name(); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking ACL for" << name(); connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ), this, SLOT( checkACLResult( QString, QString, ACLRegistry::ACL ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, nodeid ), Q_ARG( QString, bareName ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, nodeid ), Q_ARG( QString, bareName() ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); } +QString +Connection::bareName() const +{ + return name().contains( '/' ) ? name().left( name().indexOf( "/" ) ) : name(); +} + void Connection::checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ) { - QString bareName = name().contains( '/' ) ? name().left( name().indexOf( "/" ) ) : name(); if ( nodeid != property( "nodeid" ).toString() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( property( "nodeid" ).toString() ).arg( username ); return; } - if ( username != bareName ) + if ( username != bareName() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "username not our barename"; return; diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index 769e05a580..075ddefed0 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -92,6 +92,7 @@ Q_OBJECT const QHostAddress peerIpAddress() const { return m_peerIpAddress; } + QString bareName() const; signals: void ready(); void failed(); @@ -105,6 +106,7 @@ Q_OBJECT protected slots: virtual void handleMsg( msg_ptr msg ) = 0; + virtual void authCheckTimeout(); public slots: virtual void start( QTcpSocket* sock ); @@ -122,7 +124,6 @@ private slots: void doSetup(); void checkACL(); void checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ); - void authCheckTimeout(); void bytesWritten( qint64 ); void calcStats(); diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 120c4b715e..2a870aad7e 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -273,6 +273,18 @@ ControlConnection::handleMsg( msg_ptr msg ) tDebug() << id() << "Invalid msg:" << QString::fromLatin1( msg->payload() ); } +void +ControlConnection::authCheckTimeout() +{ + if ( m_ready ) + return; + + Servent::instance()->queueForAclResult( bareName(), m_peerInfos ); + + tDebug( LOGVERBOSE ) << "Closing connection, not authed in time."; + shutdown(); +} + void ControlConnection::onPingTimer() diff --git a/src/libtomahawk/network/ControlConnection.h b/src/libtomahawk/network/ControlConnection.h index a8bde2d158..f4bfb3c4fe 100644 --- a/src/libtomahawk/network/ControlConnection.h +++ b/src/libtomahawk/network/ControlConnection.h @@ -58,6 +58,7 @@ Q_OBJECT protected slots: virtual void handleMsg( msg_ptr msg ); + virtual void authCheckTimeout(); private slots: void dbSyncConnFinished( QObject* c ); diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index d4b106ce60..bdb77ee92c 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -210,6 +210,8 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) break; } + connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ), this, SLOT( checkACLResult( QString, QString, ACLRegistry::ACL ) ), Qt::QueuedConnection ); + return true; } @@ -421,6 +423,22 @@ Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) return sipInfos; } +void +Servent::queueForAclResult( const QString& username, const QSet& peerInfos ) +{ + if ( peerInfos.isEmpty() ) + { + // If all peerInfos disappeared, do not queue. + return; + } + + if ( !d_func()->queuedForACLResult.contains( username ) ) + { + d_func()->queuedForACLResult[username] = QMap >(); + } + d_func()->queuedForACLResult[username][ (*peerInfos.begin())->nodeId() ] = QSet( peerInfos ); +} + SipInfo Servent::getSipInfoForOldVersions( const QList& sipInfos ) { @@ -880,6 +898,33 @@ Servent::socketError( QAbstractSocket::SocketError e ) } } +void +Servent::checkACLResult( const QString& nodeid, const QString& username, ACLRegistry::ACL peerStatus ) +{ + + if ( !d_func()->queuedForACLResult.contains( username ) ) + { + return; + } + if ( !d_func()->queuedForACLResult.value( username ).contains( nodeid ) ) + { + return; + } + + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; + QSet peerInfos = d_func()->queuedForACLResult.value( username ).value( nodeid ); + if ( peerStatus == ACLRegistry::Stream ) + { + foreach ( Tomahawk::peerinfo_ptr peerInfo, peerInfos ) + { + registerPeer( peerInfo ); + } + + } + // We have a result, so remove from queue + d_func()->queuedForACLResult[username].remove( nodeid ); +} + void Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& theirdbid, const QString& key, const QString& theirkey ) { diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 5a455cc482..63f259ce30 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -36,6 +36,7 @@ #include "Typedefs.h" #include "Msg.h" +#include "AclRegistry.h" #include "DllMacro.h" @@ -134,6 +135,8 @@ public slots: bool isReady() const; QList getLocalSipInfos(const QString& nodeid, const QString &key); + + void queueForAclResult( const QString& username, const QSet& peerInfos ); signals: void dbSyncTriggered(); void streamStarted( StreamConnection* ); @@ -158,6 +161,7 @@ private slots: void deleteLazyOffer( const QString& key ); void readyRead(); void socketError( QAbstractSocket::SocketError e ); + void checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ); Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); diff --git a/src/libtomahawk/network/Servent_p.h b/src/libtomahawk/network/Servent_p.h index f0f06bd7f2..60ecc1231e 100644 --- a/src/libtomahawk/network/Servent_p.h +++ b/src/libtomahawk/network/Servent_p.h @@ -83,6 +83,8 @@ Q_OBJECT // currently active file transfers: QList< StreamConnection* > scsessions; QMutex ftsession_mut; + // username -> nodeid -> PeerInfos + QMap > > queuedForACLResult; QPointer< PortFwdThread > portfwd; }; From c3eca392f2ccd3b7f18aa4e9bdcf0e98fc7a11ec Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 17:32:37 +0200 Subject: [PATCH 284/565] Less strong refs to ConnectionManagers if they are not used --- src/libtomahawk/network/ConnectionManager.cpp | 96 ++++++++++++++----- src/libtomahawk/network/ConnectionManager.h | 7 ++ src/libtomahawk/network/ConnectionManager_p.h | 1 + 3 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index ebfeaeeec1..7dd45a6476 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -30,6 +30,43 @@ #include #include +/* Management of ConnectionManagers */ + +static QMutex* nodeMapMutex = new QMutex(); +static QMap< QString, QWeakPointer< ConnectionManager > > connectionManagers; +static QMap< QString, QSharedPointer< ConnectionManager > > activeConnectionManagers; + +QSharedPointer +ConnectionManager::getManagerForNodeId( const QString &nodeid ) +{ + QMutexLocker locker( nodeMapMutex ); + if ( connectionManagers.contains( nodeid ) && !connectionManagers.value( nodeid ).isNull() ) { + return connectionManagers.value( nodeid ).toStrongRef(); + } + + // There exists no connection for this nodeid + QSharedPointer< ConnectionManager > manager( new ConnectionManager( nodeid ) ); + manager->setWeakRef( manager.toWeakRef() ); + connectionManagers[nodeid] = manager->weakRef(); + return manager; +} + +void +ConnectionManager::setActive( bool active, const QString& nodeid, const QSharedPointer& manager ) +{ + QMutexLocker locker( nodeMapMutex ); + if ( active ) + { + activeConnectionManagers[ nodeid ] = manager; + } + else + { + activeConnectionManagers.remove( nodeid ); + } +} + +/* ConnectionManager Implementation */ + ConnectionManager::ConnectionManager( const QString &nodeid ) : d_ptr( new ConnectionManagerPrivate( this, nodeid ) ) { @@ -48,10 +85,22 @@ ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo ) QtConcurrent::run( this, &ConnectionManager::handleSipInfoPrivate, peerInfo ); } +QWeakPointer +ConnectionManager::weakRef() const +{ + return d_func()->ownRef; +} + +void +ConnectionManager::setWeakRef( QWeakPointer weakRef ) +{ + d_func()->ownRef = weakRef; +} + void ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo ) { - d_func()->mutex.lock(); + activate(); // Respect different behaviour before 0.7.100 peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Trying to connect to client with version " << peerInfo->versionString().split(' ').last() << TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.99" ); if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString().split(' ').last(), "0.7.100" ) < 0) @@ -69,7 +118,7 @@ ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo // We connected to the peer, so we are done here. } } - d_func()->mutex.unlock(); + deactivate(); return; } foreach ( SipInfo info, peerInfo->sipInfos() ) @@ -84,7 +133,7 @@ ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo return; } } - d_func()->mutex.unlock(); + deactivate(); } void @@ -93,7 +142,7 @@ ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool l // Lock, so that we will not attempt to do two parallell connects. if (lock) { - d_func()->mutex.lock(); + activate(); } // Check that we are not already connected to this peer ControlConnection* cconn = Servent::instance()->lookupControlConnection( peerInfo->nodeId() ); @@ -106,7 +155,7 @@ ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool l { d_func()->controlConnection = QPointer(cconn); } - d_func()->mutex.unlock(); + deactivate(); return; // TODO: Keep the peerInfo in mind for reconnecting // FIXME: Do we need this for reconnecting if the connection drops? @@ -151,7 +200,7 @@ void ConnectionManager::tryConnect() // Clean up. d_func()->currentPeerInfo.clear(); delete d_func()->controlConnection.data(); - d_func()->mutex.unlock(); + deactivate(); return; } @@ -215,6 +264,20 @@ ConnectionManager::socketError( QAbstractSocket::SocketError error ) tryConnect(); } +void +ConnectionManager::activate() +{ + d_func()->mutex.lock(); + setActive( true, d_func()->nodeid, weakRef().toStrongRef() ); +} + +void +ConnectionManager::deactivate() +{ + setActive( false, d_func()->nodeid, weakRef().toStrongRef() ); + d_func()->mutex.unlock(); +} + void ConnectionManager::socketConnected() @@ -227,7 +290,7 @@ ConnectionManager::socketConnected() handoverSocket( sock ); d_func()->currentPeerInfo.clear(); - d_func()->mutex.unlock(); + deactivate(); } void @@ -249,23 +312,4 @@ ConnectionManager::handoverSocket( QTcpSocketExtra* sock ) // ControlConntection is now connected, now it can be destroyed if the PeerInfos disappear d_func()->controlConnection->setShutdownOnEmptyPeerInfos( true ); d_func()->currentPeerInfo.clear(); - d_func()->mutex.unlock(); -} - - -static QMutex* nodeMapMutex = new QMutex(); -static QMap< QString, QSharedPointer< ConnectionManager > > connectionManagers; - -QSharedPointer -ConnectionManager::getManagerForNodeId( const QString &nodeid ) -{ - QMutexLocker locker( nodeMapMutex ); - if ( connectionManagers.contains( nodeid ) && !connectionManagers.value( nodeid ).isNull() ) { - return connectionManagers.value( nodeid ); - } - - // There exists no connection for this nodeid - QSharedPointer< ConnectionManager > manager( new ConnectionManager( nodeid ) ); - connectionManagers[nodeid] = manager; - return manager; } diff --git a/src/libtomahawk/network/ConnectionManager.h b/src/libtomahawk/network/ConnectionManager.h index 0335cf3b4f..fc5b6525aa 100644 --- a/src/libtomahawk/network/ConnectionManager.h +++ b/src/libtomahawk/network/ConnectionManager.h @@ -35,11 +35,16 @@ class DLLEXPORT ConnectionManager : public QObject public: static QSharedPointer getManagerForNodeId( const QString& nodeid ); + static void setActive( bool active, const QString& nodeid, const QSharedPointer& manager ); + ConnectionManager( const QString& nodeid ); ~ConnectionManager(); void handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ); + QWeakPointer< ConnectionManager > weakRef() const; + void setWeakRef( QWeakPointer< ConnectionManager > weakRef ); + private slots: void socketConnected(); void socketError( QAbstractSocket::SocketError error ); @@ -48,6 +53,8 @@ private slots: Q_DECLARE_PRIVATE( ConnectionManager ) ConnectionManagerPrivate* d_ptr; + void activate(); + void deactivate(); void connectToPeer(const Tomahawk::peerinfo_ptr& peerInfo , bool lock); void handleSipInfoPrivate( const Tomahawk::peerinfo_ptr& peerInfo ); diff --git a/src/libtomahawk/network/ConnectionManager_p.h b/src/libtomahawk/network/ConnectionManager_p.h index 7033965735..5c2241c0bb 100644 --- a/src/libtomahawk/network/ConnectionManager_p.h +++ b/src/libtomahawk/network/ConnectionManager_p.h @@ -43,6 +43,7 @@ Q_OBJECT QPointer controlConnection; QList sipCandidates; QMutex mutex; + QWeakPointer< ConnectionManager > ownRef; }; #endif // CONNECTIONMANAGER_P_H From d3f32f73c9591942b69ec84ce7bd8bc7bb3337d5 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 19:24:41 +0200 Subject: [PATCH 285/565] Proxy the QtConncurrent call to a static function to preserve a strong ref --- src/libtomahawk/network/ConnectionManager.cpp | 8 +++++++- src/libtomahawk/network/ConnectionManager.h | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 7dd45a6476..9409eccadf 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -82,7 +82,7 @@ void ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo ) { // Start handling in a separate thread so that we do not block the event loop. - QtConcurrent::run( this, &ConnectionManager::handleSipInfoPrivate, peerInfo ); + QtConcurrent::run( &ConnectionManager::handleSipInfoPrivateS, peerInfo, weakRef().toStrongRef() ); } QWeakPointer @@ -264,6 +264,12 @@ ConnectionManager::socketError( QAbstractSocket::SocketError error ) tryConnect(); } +void +ConnectionManager::handleSipInfoPrivateS( const Tomahawk::peerinfo_ptr &peerInfo, const QSharedPointer &connectionManager ) +{ + connectionManager->handleSipInfoPrivate( peerInfo ); +} + void ConnectionManager::activate() { diff --git a/src/libtomahawk/network/ConnectionManager.h b/src/libtomahawk/network/ConnectionManager.h index fc5b6525aa..eca5a6102f 100644 --- a/src/libtomahawk/network/ConnectionManager.h +++ b/src/libtomahawk/network/ConnectionManager.h @@ -53,6 +53,9 @@ private slots: Q_DECLARE_PRIVATE( ConnectionManager ) ConnectionManagerPrivate* d_ptr; + // Proxy to hand over a strong reference to the connectionManager + static void handleSipInfoPrivateS( const Tomahawk::peerinfo_ptr& peerInfo, const QSharedPointer& connectionManager ); + void activate(); void deactivate(); void connectToPeer(const Tomahawk::peerinfo_ptr& peerInfo , bool lock); From 6216ab1a7953a81ab368a8b71c5c328afb105789 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 19:46:31 +0200 Subject: [PATCH 286/565] No need for a pointer here --- src/libtomahawk/network/ConnectionManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 9409eccadf..7a0d9aade3 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -32,14 +32,14 @@ /* Management of ConnectionManagers */ -static QMutex* nodeMapMutex = new QMutex(); +static QMutex nodeMapMutex; static QMap< QString, QWeakPointer< ConnectionManager > > connectionManagers; static QMap< QString, QSharedPointer< ConnectionManager > > activeConnectionManagers; QSharedPointer ConnectionManager::getManagerForNodeId( const QString &nodeid ) { - QMutexLocker locker( nodeMapMutex ); + QMutexLocker locker( &nodeMapMutex ); if ( connectionManagers.contains( nodeid ) && !connectionManagers.value( nodeid ).isNull() ) { return connectionManagers.value( nodeid ).toStrongRef(); } @@ -54,7 +54,7 @@ ConnectionManager::getManagerForNodeId( const QString &nodeid ) void ConnectionManager::setActive( bool active, const QString& nodeid, const QSharedPointer& manager ) { - QMutexLocker locker( nodeMapMutex ); + QMutexLocker locker( &nodeMapMutex ); if ( active ) { activeConnectionManagers[ nodeid ] = manager; From d52333893f392533b07ad09957500f037251cb55 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 23:37:10 +0200 Subject: [PATCH 287/565] Self cleaning peerInfo cache, fixes TWK-1396 --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/sip/PeerInfo.cpp | 13 +++---- src/libtomahawk/sip/PeerInfo.h | 3 +- src/libtomahawk/sip/WeakPeerHash.cpp | 57 ++++++++++++++++++++++++++++ src/libtomahawk/sip/WeakPeerHash.h | 47 +++++++++++++++++++++++ src/libtomahawk/sip/WeakPeerHash_p.h | 39 +++++++++++++++++++ 6 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 src/libtomahawk/sip/WeakPeerHash.cpp create mode 100644 src/libtomahawk/sip/WeakPeerHash.h create mode 100644 src/libtomahawk/sip/WeakPeerHash_p.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 9159091dbc..9c16c6a477 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -341,6 +341,7 @@ list(APPEND libSources sip/SipInfo.cpp sip/PeerInfo.cpp sip/SipStatusMessage.cpp + sip/WeakPeerHash.cpp utils/TomahawkUtils.cpp utils/Logger.cpp diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 405174e4d2..f122ba2aec 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -20,6 +20,7 @@ #include "PeerInfo_p.h" #include "SipPlugin.h" +#include "WeakPeerHash.h" #include "utils/TomahawkCache.h" #include "utils/TomahawkUtilsGui.h" #include "network/ControlConnection.h" @@ -32,7 +33,7 @@ namespace Tomahawk { -QHash< QString, peerinfo_wptr > PeerInfo::s_peersByCacheKey = QHash< QString, peerinfo_wptr >(); +WeakPeerHash PeerInfo::s_peersByCacheKey = WeakPeerHash(); QHash< SipPlugin*, peerinfo_ptr > PeerInfo::s_selfPeersBySipPlugin = QHash< SipPlugin*, peerinfo_ptr >(); @@ -79,9 +80,9 @@ Tomahawk::peerinfo_ptr PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options ) { const QString key = peerCacheKey( parent, id ); - if ( s_peersByCacheKey.contains( key ) && !s_peersByCacheKey.value( key ).isNull() ) + if ( s_peersByCacheKey.hash().contains( key ) && !s_peersByCacheKey.hash().value( key ).isNull() ) { - return s_peersByCacheKey.value( key ).toStrongRef(); + return s_peersByCacheKey.hash().value( key ).toStrongRef(); } // if AutoCreate isn't enabled nothing to do here @@ -92,7 +93,7 @@ PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options ) peerinfo_ptr peerInfo( new PeerInfo( parent, id ) ); peerInfo->setWeakRef( peerInfo.toWeakRef() ); - s_peersByCacheKey.insert( key, peerInfo.toWeakRef() ); + s_peersByCacheKey.insert( key, peerInfo ); return peerInfo; } @@ -102,7 +103,7 @@ QList< Tomahawk::peerinfo_ptr > PeerInfo::getAll() { QList< Tomahawk::peerinfo_ptr > strongRefs; - foreach ( Tomahawk::peerinfo_wptr wptr, s_peersByCacheKey.values() ) + foreach ( Tomahawk::peerinfo_wptr wptr, s_peersByCacheKey.hash().values() ) { if ( !wptr.isNull() ) strongRefs << wptr.toStrongRef(); @@ -122,8 +123,6 @@ PeerInfo::PeerInfo( SipPlugin* parent, const QString& id ) PeerInfo::~PeerInfo() { -// tDebug() << Q_FUNC_INFO; - s_peersByCacheKey.remove( s_peersByCacheKey.key( weakRef() ) ); delete m_avatar; delete m_fancyAvatar; delete d_ptr; diff --git a/src/libtomahawk/sip/PeerInfo.h b/src/libtomahawk/sip/PeerInfo.h index 12ffbf703f..44be6847db 100644 --- a/src/libtomahawk/sip/PeerInfo.h +++ b/src/libtomahawk/sip/PeerInfo.h @@ -30,6 +30,7 @@ class ControlConnection; class SipPlugin; class SipInfo; +class WeakPeerHash; namespace Tomahawk { @@ -129,7 +130,7 @@ Q_OBJECT Q_DECLARE_PRIVATE( Tomahawk::PeerInfo ) Tomahawk::PeerInfoPrivate* d_ptr; - static QHash< QString, peerinfo_wptr > s_peersByCacheKey; + static WeakPeerHash s_peersByCacheKey; static QHash< SipPlugin*, peerinfo_ptr > s_selfPeersBySipPlugin; mutable QPixmap* m_avatar; diff --git a/src/libtomahawk/sip/WeakPeerHash.cpp b/src/libtomahawk/sip/WeakPeerHash.cpp new file mode 100644 index 0000000000..a6e4518455 --- /dev/null +++ b/src/libtomahawk/sip/WeakPeerHash.cpp @@ -0,0 +1,57 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "WeakPeerHash_p.h" + +#include "PeerInfo.h" + +#define WEAKPEERHASH_KEY "WeakPeerHashKey" + +WeakPeerHash::WeakPeerHash(QObject *parent) + : QObject(parent) + , d_ptr( new WeakPeerHashPrivate( this ) ) +{ +} + +WeakPeerHash::WeakPeerHash( const WeakPeerHash &hash ) + : QObject( hash.parent() ) + , d_ptr( new WeakPeerHashPrivate( this ) ) +{ + d_func()->hash = hash.hash(); +} + +void +WeakPeerHash::insert(const QString &key, const Tomahawk::peerinfo_ptr &value) +{ + value->setProperty( WEAKPEERHASH_KEY, key ); + connect( value.data(), SIGNAL( destroyed( QObject* ) ), SLOT( remove( QObject* ) ) ); + d_func()->hash.insert( key, value.toWeakRef() ); +} + +const QHash& +WeakPeerHash::hash() +{ + return d_func()->hash; +} + +void +WeakPeerHash::remove( QObject *value ) +{ + const QString key = value->property( WEAKPEERHASH_KEY ).toString(); + d_func()->hash.remove( key ); +} diff --git a/src/libtomahawk/sip/WeakPeerHash.h b/src/libtomahawk/sip/WeakPeerHash.h new file mode 100644 index 0000000000..f9f724f237 --- /dev/null +++ b/src/libtomahawk/sip/WeakPeerHash.h @@ -0,0 +1,47 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef WEAKPEERHASH_H +#define WEAKPEERHASH_H + +#include "Typedefs.h" + +#include + +class WeakPeerHashPrivate; + +class WeakPeerHash : public QObject +{ + Q_OBJECT +public: + WeakPeerHash( QObject *parent = 0 ); + WeakPeerHash( const WeakPeerHash& hash ); + void insert( const QString& key, const Tomahawk::peerinfo_ptr& value ); + const QHash< QString, Tomahawk::peerinfo_wptr>& hash(); + +signals: + +private slots: + void remove( QObject* value ); +private: + Q_DECLARE_PRIVATE( WeakPeerHash ) + WeakPeerHashPrivate* d_ptr; + +}; + +#endif // WEAKPEERHASH_H diff --git a/src/libtomahawk/sip/WeakPeerHash_p.h b/src/libtomahawk/sip/WeakPeerHash_p.h new file mode 100644 index 0000000000..ee4e80e07a --- /dev/null +++ b/src/libtomahawk/sip/WeakPeerHash_p.h @@ -0,0 +1,39 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef WEAKPEERHASH_P_H +#define WEAKPEERHASH_P_H + +#include "WeakPeerHash.h" + +class WeakPeerHashPrivate +{ +public: + WeakPeerHashPrivate( WeakPeerHash* q ) + : q_ptr ( q ) + { + } + WeakPeerHash* q_ptr; + Q_DECLARE_PUBLIC ( WeakPeerHash ) + +private: + QHash< QString, Tomahawk::peerinfo_wptr > hash; +}; + + +#endif // WEAKPEERHASH_P_H From ce10b501a828fb49be2ead31662c8caaad2d8338 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 9 Jun 2013 23:42:34 +0200 Subject: [PATCH 288/565] Copy internal hash instead of trying to make a non-valid assignment --- src/libtomahawk/sip/WeakPeerHash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/sip/WeakPeerHash.cpp b/src/libtomahawk/sip/WeakPeerHash.cpp index a6e4518455..7f1dcae3a1 100644 --- a/src/libtomahawk/sip/WeakPeerHash.cpp +++ b/src/libtomahawk/sip/WeakPeerHash.cpp @@ -32,7 +32,7 @@ WeakPeerHash::WeakPeerHash( const WeakPeerHash &hash ) : QObject( hash.parent() ) , d_ptr( new WeakPeerHashPrivate( this ) ) { - d_func()->hash = hash.hash(); + d_func()->hash = hash.d_func()->hash; } void From 9f9d01618e4ed3435d3c6737c5d2270885d17380 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Mon, 10 Jun 2013 02:16:42 +0200 Subject: [PATCH 289/565] Automatic merge of Transifex translations --- lang/tomahawk_bg.ts | 176 ++++++++++++++++++++++---------------------- lang/tomahawk_cs.ts | 34 ++++----- lang/tomahawk_de.ts | 32 ++++---- lang/tomahawk_el.ts | 32 ++++---- 4 files changed, 138 insertions(+), 136 deletions(-) diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 57bd009f38..007e4c0318 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -124,7 +124,7 @@ connect and stream from you? &Load Station - + &Зареди станция @@ -134,7 +134,7 @@ connect and stream from you? &Rename Station - + &Преименувай станция @@ -267,7 +267,7 @@ connect and stream from you? Tracklist - Списък на песни за изпълняване + Списък за изпълняване @@ -339,7 +339,7 @@ connect and stream from you? # IN YOUR CHARTS - + # В ТВОИТЕ КЛАСАЦИИ @@ -523,32 +523,32 @@ Tomahawk създаде доклад относно това и изпращай Recently Played Tracks - + Наскоро изпълнени Recent Additions - + Нови попълнения Newest Stations & Playlists - + Най-нови станции и списъци Recently played tracks - + Наскоро изпълнени No recently created playlists in your network. - + Няма наскоро създавани списъци в твоята мрежа Welcome to Tomahawk - + Здравей! @@ -703,7 +703,7 @@ Tomahawk създаде доклад относно това и изпращай Filter... - + Филтър... @@ -725,48 +725,49 @@ Tomahawk създаде доклад относно това и изпращай Form - + Форма Connect to your Hatchet account - + Свържи се към твоя Hatchet акаунт One-time Password - + Еднократна +Парола Username - + Потребителско име Hatchet username - + Hatchet потребителско име Password: - + Парола: Hatchet password - + Hatchet парола (Only if configured) - + (Само ако е настроен) Login - + Влизане @@ -774,32 +775,32 @@ Password From: - + От: To: - + До: Recently Played Tracks - + Наскоро изпълнени Your recently played tracks - + Наскоро изпълнени песни от теб %1's recently played tracks - + Наскоро изпълнените песни от %1 Sorry, we could not find any recent plays! - + Съжалявам, но не откривам скорошни списъци @@ -807,7 +808,7 @@ Password Inbox - + Входяща кутия @@ -815,12 +816,12 @@ Password Sent %1 by %2 to %3. - + %1 ти изпрати %2 от %3. %1 sent you %2 by %3. - + %1 ти изпрати %2 от %3. @@ -846,7 +847,7 @@ Password Script Resolver Warning: API call %1 returned data synchronously. - + Предупреждение на скриптът за извличане: Заявката към %1 върна данни синхронно. @@ -1108,32 +1109,32 @@ Password Charts - + Класации Last Week - + Миналата седмица Last Month - + Миналия месец Last Year - + Миналата година Overall - + Изцяло Network Activity - + Мрежова активност @@ -1272,7 +1273,7 @@ Password Searching... - + Търсене... @@ -1565,12 +1566,12 @@ Password No query - + Няма опашка Parameter count mismatch - + Параметърът 'брой' не съответства @@ -1626,7 +1627,7 @@ Password Reload Collection - + Презареди @@ -1639,17 +1640,17 @@ Password SSL Error - + SSL грешка You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Ти пожела Tomahawk да се свърже сигурно към <b>%1</b>, но ние не можем да потвърдим, че връзката е сигурна:<br><br><b>%2</b><br><br>Искаш ли да се довериш на тази връзка? Trust certificate - + Удостовери този сертификат @@ -1728,12 +1729,12 @@ Password Tomahawk Resolvers (*.axe *.js);;All files (*) - + Модули за извличане на Tomahawk (*.axe *.js);; Всички (*) Resolver installation from file %1 failed. - + Инсталирането на извличащ модул от файл %1 не бе успешно. @@ -1770,7 +1771,7 @@ Password Active (your host needs to be directly reachable) - + Активен (твоята машина трябва да е достъпна от интернет) @@ -1970,7 +1971,7 @@ Password Drop to send tracks - + Пусни при изпратените @@ -2172,12 +2173,12 @@ Password Network Activity - + Мрежова активност Cloud - + Облак @@ -2276,7 +2277,7 @@ Password out of %1 - + от %1 @@ -2400,12 +2401,12 @@ Password Login - + Влизане Logged in as: %1 - + Влязъл като :%1 @@ -2413,7 +2414,7 @@ Password Connect to your Hatchet account - + Свържи се към твоя Hatchet акаунт @@ -2490,27 +2491,27 @@ Password Resolver installation error: cannot open bundle. - + Инсталирането на извличащ модул бе неуспешно: Не мога да отворя файла съдържащ данните. Resolver installation error: incomplete bundle. - + Инсталирането на извличащ модул бе неуспешно: Непълен комплект данни. Resolver installation error: bad metadata in bundle. - + Инсталирането на извличащ модул бе неуспешно: Лоши мета-данни в комплекта. Resolver installation error: platform mismatch. - + Инсталирането на извличащ модул бе неуспешно: Неправилна платформа. Resolver installation error: Tomahawk %1 or newer is required. - + Инсталирането на извличащ модул бе неуспешно: Необходима е минимум версия %1 на Tomahawk %1. @@ -2603,7 +2604,7 @@ Password Send tweets from Tomahawk. - + Изпрати туитове от Tomahawk. @@ -2819,12 +2820,12 @@ username@jabber.org Add to &Playlist - + Добави към &Списък Send to &Friend - + Изпрати към &Приятел @@ -2864,17 +2865,17 @@ username@jabber.org Mark as &Listened - + Маркирай като &Слушана &Remove Items - + &Премахни позиции &Remove Item - + &Премахни позиция @@ -3369,7 +3370,7 @@ Try tweaking the filters for a new set of songs to play. where genre is %1 - + където жанра е %1 @@ -3414,45 +3415,46 @@ Try tweaking the filters for a new set of songs to play. on 'on' is followed by an album name - + на %1%4 %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing, %4 is the preposition used to link track and artist ('by' in english) - + %1%4 %2%3. by preposition to link track and artist - + от %1 sent you %2%4 %3. %1 is a nickname, %2 is a title, %3 is an artist, %4 is the preposition used to link track and artist ('by' in english) - + %1 ти изпрати +%2%4 %3. %1 sent you "%2" by %3. %1 is a nickname, %2 is a title, %3 is an artist - + %1 ти изпрати %2 от %3. on "%1" %1 is an album name - + на "%1" "%1" by %2%3. %1 is a title, %2 is an artist and %3 is replaced by either the previous message or nothing - + "%1" от %2%3. @@ -3578,7 +3580,7 @@ Try tweaking the filters for a new set of songs to play. %1 Collection Name of a collection based on a resolver, e.g. Subsonic Collection - + %1 Колекция @@ -3654,42 +3656,42 @@ Try tweaking the filters for a new set of songs to play. and - + и You - + Ти you - + ти and - + и %n other(s) - + %n друг%n други %n people - + %n човек%n човека loved this track - + Хареса тази песен sent you this track %1 - + ти изпрати тази песен %1 @@ -3967,12 +3969,12 @@ enter the displayed PIN number here: # PLAYS / ARTIST - + # ИЗПЪЛНЕНИЯ / АРТИСТ # IN YOUR CHARTS - + # В ТВОИТЕ КЛАСАЦИИ @@ -4057,7 +4059,7 @@ enter the displayed PIN number here: The Twitter plugin allows you to post messages to your Twitter account. - + Този модул за Twitter ще ти позволи да публикуваш съобщения от твоя профил. @@ -4133,12 +4135,12 @@ You can re-send a sync message at any time simply by sending another tweet using Inbox - + Входяща кутия Listening suggestions from your friends - + Предложения за слушане от приятелите ти @@ -4175,7 +4177,7 @@ You can re-send a sync message at any time simply by sending another tweet using No listening suggestions here. - + Няма предложения. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 5343a309d0..3bbc7d427c 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -340,7 +340,7 @@ se s vámi spojil? # IN YOUR CHARTS - + # VE VAŠICH ŽEBŘÍČCÍCH @@ -518,32 +518,32 @@ se s vámi spojil? Recently Played Tracks - + Nedávno poslouchané skladby Recent Additions - + Nedávné přídavky Newest Stations & Playlists - + Nejnovější stanice a seznamy skladeb Recently played tracks - + Nedávno poslouchané skladby No recently created playlists in your network. - + Ve vaší síti nejsou žádné nedávno vytvořené seznamy skladeb. Welcome to Tomahawk - + Vítejte v Tomahawku @@ -769,32 +769,32 @@ heslo From: - + Od: To: - + Do: Recently Played Tracks - + Nedávno poslouchané skladby Your recently played tracks - + Vaše nedávno poslouchané skladby %1's recently played tracks - + %1's nedávno poslouchaných skladeb Sorry, we could not find any recent plays! - + Promiňte, ale nepodařilo se najít žádné nedávno poslouchané skladby! @@ -1123,7 +1123,7 @@ heslo Overall - + Celkový @@ -2264,7 +2264,7 @@ heslo out of %1 - + kromě %1 @@ -3952,12 +3952,12 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: # PLAYS / ARTIST - + # HRAJE / UMĚLEC # IN YOUR CHARTS - + # VE VAŠICH ŽEBŘÍČCÍCH diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 1cbdef0b0c..091e72416e 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -340,7 +340,7 @@ erlauben sich mit dir zu verbinden? # IN YOUR CHARTS - + # IN DEINEN CHARTS @@ -518,32 +518,32 @@ erlauben sich mit dir zu verbinden? Recently Played Tracks - + Kürzlich gehörte Lieder Recent Additions - + Kürzlich hinzugekommen Newest Stations & Playlists - + Neueste Stationen & Wiedergabelisten Recently played tracks - + Zuletzt gehörte Lieder No recently created playlists in your network. - + Es gibt keine kürzlich erstellten Wiedergabelisten in deinem Netzwerk. Welcome to Tomahawk - + Willkommen bei Tomahawk @@ -769,32 +769,32 @@ Passwort From: - + Aus: To: - + Zu: Recently Played Tracks - + Kürzlich gehörte Lieder Your recently played tracks - + Deine zuletzt gehörten Lieder %1's recently played tracks - + %1's zuletzt gehörte Lieder Sorry, we could not find any recent plays! - + Es konnten keine zuletzt gehörten Lieder gefunden werden! @@ -1123,7 +1123,7 @@ Passwort Overall - + Allgemein @@ -3947,12 +3947,12 @@ Tomahawk auf Twitter's Website authentifiziert hast: # PLAYS / ARTIST - + # WIEDERGEGEBEN/ KÜNSTLER # IN YOUR CHARTS - + # IN DEINEN CHARTS diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index bb7980c331..f21041d453 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -339,7 +339,7 @@ connect and stream from you? # IN YOUR CHARTS - + # ΣΤΑ ΔΙΚΑ ΣΑΣ CHART @@ -517,32 +517,32 @@ connect and stream from you? Recently Played Tracks - + Πρόσφατες αναπαραγωγές Recent Additions - + Τελευταίες Προσθήκες Newest Stations & Playlists - + Τελευταίοι Σταθμοί & Λίστες Αναπαραγωγής Recently played tracks - + Τελευταίες αναπαραγωγές κομματιών No recently created playlists in your network. - + Δεν δημιουργηθηκαν πρόσφατα λίστες αναπαραγωγής στο δίκτυό σας. Welcome to Tomahawk - + Καλως ήρθατε στο Tomahawk @@ -768,32 +768,32 @@ Password From: - + Από: To: - + Σε: Recently Played Tracks - + Πρόσφατες αναπαραγωγές Your recently played tracks - + Οι τελευταίες σας αναπαραγωγές %1's recently played tracks - + %1's τελευταίες αναπαραγωγές τραγουδιων Sorry, we could not find any recent plays! - + Συγγνωμη, δεν βρεθηκαν προσφατες αναπαραγωγες! @@ -1122,7 +1122,7 @@ Password Overall - + Συνολικά @@ -3952,12 +3952,12 @@ enter the displayed PIN number here: # PLAYS / ARTIST - + # ΑΝΑΠΑΡΑΓΩΓΕΣ / ΚΑΛΛΙΤΕΧΝΗΣ # IN YOUR CHARTS - + # ΣΤΑ ΔΙΚΑ ΣΑΣ CHARTS From cc9687c407e3286d5643e49538aece2f8403c390 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 06:29:45 +0200 Subject: [PATCH 290/565] * Fixed secondary text's color in PlaylistDelegate & PlaylistLargeItemDelegate. --- src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp | 2 +- src/libtomahawk/widgets/Dashboard.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp index 0e0363b316..d2ef227d0d 100644 --- a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp @@ -255,7 +255,7 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& drawRichText( painter, opt, leftRect.adjusted( 0, boldFontMetrics.height() + 1, 0, 0 ), Qt::AlignTop, textDoc ); if ( !( option.state & QStyle::State_Selected || item->isPlaying() ) ) - painter->setPen( Qt::gray ); + painter->setPen( opt.palette.text().color().darker() ); textDoc.setHtml( lowerText ); textDoc.setDocumentMargin( 0 ); diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index 1a69def7c8..b0fd778cf7 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -371,8 +371,7 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, QColor c = painter->pen().color(); if ( !( option.state & QStyle::State_Selected && option.state & QStyle::State_Active ) ) { - //FIXME const color - painter->setPen( QColor( Qt::gray ).darker() ); + painter->setPen( opt.palette.text().color().darker() ); } QRect rectText = option.rect.adjusted( option.fontMetrics.height() * 4.5, boldFontMetrics.height() + 6, -leftEdge - 10, -8 ); @@ -397,7 +396,7 @@ PlaylistWidget::PlaylistWidget( QWidget* parent ) : QListView( parent ) { m_overlay = new OverlayWidget( this ); - /* LoadingSpinner* spinner = */ new LoadingSpinner( this ); + new LoadingSpinner( this ); } From 27879a814edbfbff68d86ae9446ed65107924ea3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 06:30:47 +0200 Subject: [PATCH 291/565] * Moved some page-impl out of the headers. --- .../widgets/infowidgets/AlbumInfoWidget.cpp | 10 ++++++++++ src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h | 2 +- .../widgets/infowidgets/ArtistInfoWidget.cpp | 11 +++++++++++ .../widgets/infowidgets/ArtistInfoWidget.h | 2 +- .../widgets/infowidgets/TrackInfoWidget.cpp | 11 +++++++++++ src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h | 2 +- .../widgets/infowidgets/TrackInfoWidget.ui | 2 +- 7 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index f6fb36a1ca..ccd971a329 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -269,3 +269,13 @@ AlbumInfoWidget::changeEvent( QEvent* e ) break; } } + + +QPixmap +AlbumInfoWidget::pixmap() const +{ + if ( m_pixmap.isNull() ) + return Tomahawk::ViewPage::pixmap(); + else + return m_pixmap; +} diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h index 7909bfe8f1..699556fab1 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h @@ -64,7 +64,7 @@ Q_OBJECT virtual QString title() const { return m_title; } virtual QString description() const { return m_description; } virtual QString longDescription() const { return m_longDescription; } - virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } + virtual QPixmap pixmap() const; virtual bool isTemporaryPage() const { return true; } virtual bool showInfoBar() const { return false; } diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 953cf2515e..95f4059697 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -108,6 +108,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* l->addWidget( m_playStatsGauge ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); ui->statsWidget->setLayout( l ); + TomahawkUtils::unmarginLayout( l ); m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Original, QSize( 48, 48 ) ); ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Grid, ui->cover->size() ) ); @@ -379,3 +380,13 @@ ArtistInfoWidget::changeEvent( QEvent* e ) break; } } + + +QPixmap +ArtistInfoWidget::pixmap() const +{ + if ( m_pixmap.isNull() ) + return Tomahawk::ViewPage::pixmap(); + else + return m_pixmap; +} diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h index 23b55677cf..73976fa23c 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h @@ -75,7 +75,7 @@ Q_OBJECT virtual QString title() const { return m_title; } virtual QString description() const { return m_description; } virtual QString longDescription() const { return m_longDescription; } - virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } + virtual QPixmap pixmap() const; virtual bool isTemporaryPage() const { return true; } virtual bool showInfoBar() const { return false; } diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 1edad069ea..bec5c0ca04 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -112,6 +112,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); ui->statsWidget->setLayout( l ); ui->statsLabel->setVisible( false ); + TomahawkUtils::unmarginLayout( l ); QVBoxLayout* layout = new QVBoxLayout(); layout->addWidget( m_scrollArea ); @@ -309,3 +310,13 @@ TrackInfoWidget::changeEvent( QEvent* e ) break; } } + + +QPixmap +TrackInfoWidget::pixmap() const +{ + if ( m_pixmap.isNull() ) + return Tomahawk::ViewPage::pixmap(); + else + return m_pixmap; +} diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h index 12138bcde8..b14b986066 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.h @@ -64,7 +64,7 @@ Q_OBJECT virtual QString title() const { return m_title; } virtual QString description() const { return QString(); } virtual QString longDescription() const { return QString(); } - virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; } + virtual QPixmap pixmap() const; virtual bool isBeingPlayed() const; virtual bool isTemporaryPage() const { return true; } diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index 7305c7b237..d6453bbefe 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -149,7 +149,7 @@ - 450 + 440 240 From f5dc808d4c6cdd02e4fa1a757c38343a1d5498ec Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 10:35:10 +0200 Subject: [PATCH 292/565] * Use TrackView's setPlaylistItemDelegate across Tomahawk. --- src/libtomahawk/ViewManager.cpp | 5 +++-- src/libtomahawk/playlist/FlexibleView.cpp | 2 +- src/libtomahawk/widgets/HistoryWidget.cpp | 2 +- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 2 +- src/libtomahawk/widgets/WhatsHotWidget.cpp | 2 +- src/tomahawk/sourcetree/items/LovedTracksItem.cpp | 2 +- src/tomahawk/sourcetree/items/SourceItem.cpp | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 83a76fdfff..84e9d375d0 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -467,7 +467,7 @@ ViewManager::showRecentPlaysPage() raModel->setDescription( tr( "Recently played tracks from all your friends" ) ); PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::RecentlyPlayed, pv->trackView(), pv->trackView()->proxyModel() ); - pv->trackView()->setItemDelegate( del ); + pv->trackView()->setPlaylistItemDelegate( del ); pv->setPlayableModel( raModel ); pv->setEmptyTip( tr( "Sorry, we could not find any recent plays!" ) ); @@ -493,7 +493,7 @@ ViewManager::showInboxPage() new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::Inbox, inboxView, inboxView->proxyModel() ); - inboxView->setItemDelegate( delegate ); + inboxView->setPlaylistItemDelegate( delegate ); inboxView->proxyModel()->setStyle( PlayableProxyModel::Large ); inboxView->setPlayableModel( m_inboxModel ); @@ -510,6 +510,7 @@ ViewManager::showInboxPage() return show( m_inboxWidget ); } + ViewPage *ViewManager::showNetworkActivityPage() { if ( !m_networkActivityWidget ) diff --git a/src/libtomahawk/playlist/FlexibleView.cpp b/src/libtomahawk/playlist/FlexibleView.cpp index 76622c4153..a3d73815c9 100644 --- a/src/libtomahawk/playlist/FlexibleView.cpp +++ b/src/libtomahawk/playlist/FlexibleView.cpp @@ -55,7 +55,7 @@ FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) m_detailedView->setColumnHidden( PlayableModel::Composer, true ); // Hide composer column per default PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::LovedTracks, m_trackView, m_trackView->proxyModel() ); - m_trackView->setItemDelegate( del ); + m_trackView->setPlaylistItemDelegate( del ); m_trackView->proxyModel()->setStyle( PlayableProxyModel::Large ); m_stack = new QStackedWidget(); diff --git a/src/libtomahawk/widgets/HistoryWidget.cpp b/src/libtomahawk/widgets/HistoryWidget.cpp index ac7bdc905e..fcaaf1490f 100644 --- a/src/libtomahawk/widgets/HistoryWidget.cpp +++ b/src/libtomahawk/widgets/HistoryWidget.cpp @@ -83,7 +83,7 @@ HistoryWidget::HistoryWidget( const source_ptr& source, QWidget* parent ) m_model->setDescription( tr( "%1's recently played tracks" ).arg( source->friendlyName() ) ); PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::RecentlyPlayed, trackView(), trackView()->proxyModel() ); - trackView()->setItemDelegate( del ); + trackView()->setPlaylistItemDelegate( del ); setPlayableModel( m_model ); setEmptyTip( tr( "Sorry, we could not find any recent plays!" ) ); diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 72b7778586..99d0de3f37 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -58,7 +58,7 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) d_func()->ui->tracksViewLeft->setHeaderHidden( true ); d_func()->ui->tracksViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( d_func()->ui->tracksViewLeft, d_func()->ui->tracksViewLeft->proxyModel() ); - d_func()->ui->tracksViewLeft->setItemDelegate( del ); + d_func()->ui->tracksViewLeft->setPlaylistItemDelegate( del ); d_func()->ui->tracksViewLeft->setUniformRowHeights( false ); d_func()->playlistInterface = d_func()->ui->tracksViewLeft->playlistInterface(); diff --git a/src/libtomahawk/widgets/WhatsHotWidget.cpp b/src/libtomahawk/widgets/WhatsHotWidget.cpp index e351d7a966..c22b2d35ec 100644 --- a/src/libtomahawk/widgets/WhatsHotWidget.cpp +++ b/src/libtomahawk/widgets/WhatsHotWidget.cpp @@ -82,7 +82,7 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) ui->tracksViewLeft->setHeaderHidden( true ); ui->tracksViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); PlaylistChartItemDelegate* del = new PlaylistChartItemDelegate( ui->tracksViewLeft, ui->tracksViewLeft->proxyModel() ); - ui->tracksViewLeft->setItemDelegate( del ); + ui->tracksViewLeft->setPlaylistItemDelegate( del ); ui->tracksViewLeft->setUniformRowHeights( false ); TreeProxyModel* artistsProxy = new TreeProxyModel( ui->artistsViewLeft ); diff --git a/src/tomahawk/sourcetree/items/LovedTracksItem.cpp b/src/tomahawk/sourcetree/items/LovedTracksItem.cpp index 1cc662a52b..fb45cdda80 100644 --- a/src/tomahawk/sourcetree/items/LovedTracksItem.cpp +++ b/src/tomahawk/sourcetree/items/LovedTracksItem.cpp @@ -79,7 +79,7 @@ LovedTracksItem::activate() raModel->setTitle( text() ); PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::LovedTracks, pv->trackView(), pv->trackView()->proxyModel() ); - pv->trackView()->setItemDelegate( del ); + pv->trackView()->setPlaylistItemDelegate( del ); pv->setEmptyTip( tr( "Sorry, we could not find any loved tracks!" ) ); if ( !par ) diff --git a/src/tomahawk/sourcetree/items/SourceItem.cpp b/src/tomahawk/sourcetree/items/SourceItem.cpp index 4c48f68313..4aca89cf1c 100644 --- a/src/tomahawk/sourcetree/items/SourceItem.cpp +++ b/src/tomahawk/sourcetree/items/SourceItem.cpp @@ -611,7 +611,7 @@ SourceItem::latestAdditionsClicked() raModel->setDescription( tr( "Latest additions to %1's collection" ).arg( m_source->friendlyName() ) ); PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::LatestAdditions, pv->trackView(), pv->trackView()->proxyModel() ); - pv->trackView()->setItemDelegate( del ); + pv->trackView()->setPlaylistItemDelegate( del ); pv->setPlayableModel( raModel ); pv->trackView()->sortByColumn( PlayableModel::Age, Qt::DescendingOrder ); From d6bb90fc25727d44d15455694ed0f0b73f29896e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 10:36:07 +0200 Subject: [PATCH 293/565] * Handle source-icon and info-icon painting in the base delegate class. --- .../playlist/PlaylistItemDelegate.cpp | 129 ++++++++++++++++-- .../playlist/PlaylistItemDelegate.h | 28 +++- 2 files changed, 143 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp index a6c5c69525..d146eec0f0 100644 --- a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "Query.h" #include "Result.h" @@ -37,6 +38,8 @@ #include "ViewHeader.h" #include "ViewManager.h" +#include "utils/PixmapDelegateFader.h" +#include "utils/Closure.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" @@ -45,16 +48,42 @@ using namespace Tomahawk; PlaylistItemDelegate::PlaylistItemDelegate( TrackView* parent, PlayableProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) + , m_smallBoldFontMetrics( QFontMetrics( parent->font() ) ) + , m_bigBoldFontMetrics( QFontMetrics( parent->font() ) ) , m_view( parent ) , m_model( proxy ) { - connect( this, SIGNAL( updateIndex( QModelIndex ) ), parent, SLOT( update( QModelIndex ) ) ); - m_topOption = QTextOption( Qt::AlignTop ); m_topOption.setWrapMode( QTextOption::NoWrap ); - m_bottomOption = QTextOption( Qt::AlignBottom ); m_bottomOption.setWrapMode( QTextOption::NoWrap ); + + m_centerOption = QTextOption( Qt::AlignVCenter ); + m_centerOption.setWrapMode( QTextOption::NoWrap ); + m_centerRightOption = QTextOption( Qt::AlignVCenter | Qt::AlignRight ); + m_centerRightOption.setWrapMode( QTextOption::NoWrap ); + + m_bigBoldFont = parent->font(); + m_bigBoldFont.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); + m_bigBoldFont.setWeight( 99 ); + + m_boldFont = parent->font(); + m_boldFont.setBold( true ); + + m_smallBoldFont = parent->font(); + m_smallBoldFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); + m_smallBoldFont.setBold( true ); + m_smallBoldFont.setWeight( 60 ); + + m_smallFont = parent->font(); + m_smallFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); + + m_bigBoldFontMetrics = QFontMetrics( m_bigBoldFont ); + m_smallBoldFontMetrics = QFontMetrics( m_smallBoldFont ); + + connect( this, SIGNAL( updateIndex( QModelIndex ) ), parent, SLOT( update( QModelIndex ) ) ); + connect( proxy, SIGNAL( modelReset() ), SLOT( modelChanged() ) ); + connect( parent, SIGNAL( modelChanged() ), SLOT( modelChanged() ) ); } @@ -187,11 +216,8 @@ PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& painter->drawPixmap( ir, pixmap ); - QFont boldFont = opt.font; - boldFont.setBold( true ); - r.adjust( ir.width() + 12, 0, -12, 0 ); - painter->setFont( boldFont ); + painter->setFont( m_boldFont ); QString text = painter->fontMetrics().elidedText( upperText, Qt::ElideRight, r.width() ); painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, m_topOption ); @@ -226,9 +252,7 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt { opt.rect.setWidth( opt.rect.width() - opt.rect.height() - 2 ); const QRect arrowRect( opt.rect.x() + opt.rect.width(), opt.rect.y() + 1, opt.rect.height() - 2, opt.rect.height() - 2 ); - painter->drawPixmap( arrowRect, TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() ) ); - - m_infoButtonRects[ index ] = arrowRect; + drawInfoButton( painter, arrowRect, index, 0.9 ); } painter->save(); @@ -285,6 +309,58 @@ PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewIt } +QRect +PlaylistItemDelegate::drawInfoButton( QPainter* painter, const QRect& rect, const QModelIndex& index, float height ) const +{ + const int iconSize = rect.height() * height; + QRect pixmapRect = QRect( ( rect.height() - iconSize ) / 2 + rect.left(), rect.center().y() - iconSize / 2, iconSize, iconSize ); + + painter->drawPixmap( pixmapRect, TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, pixmapRect.size() ) ); + m_infoButtonRects[ index ] = pixmapRect; + + return rect.adjusted( rect.height(), 0, 0, 0 ); +} + + +QRect +PlaylistItemDelegate::drawCover( QPainter* painter, const QRect& rect, PlayableItem* item, const QModelIndex& index ) const +{ + QRect pixmapRect = rect; + pixmapRect.setWidth( pixmapRect.height() ); + + if ( !m_pixmaps.contains( index ) ) + { + m_pixmaps.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->query(), pixmapRect.size(), TomahawkUtils::RoundedCorners, false ) ) ); + _detail::Closure* closure = NewClosure( m_pixmaps[ index ], SIGNAL( repaintRequest() ), const_cast(this), SLOT( doUpdateIndex( const QPersistentModelIndex& ) ), QPersistentModelIndex( index ) ); + closure->setAutoDelete( false ); + } + + const QPixmap pixmap = m_pixmaps[ index ]->currentPixmap(); + painter->drawPixmap( pixmapRect, pixmap ); + + return rect.adjusted( pixmapRect.width(), 0, 0, 0 ); +} + + +QRect +PlaylistItemDelegate::drawSourceIcon( QPainter* painter, const QRect& rect, PlayableItem* item, float height ) const +{ + if ( item->query()->numResults() == 0 ) + return rect; + + const int sourceIconSize = rect.height() * height; + const QPixmap sourceIcon = item->query()->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ); + if ( sourceIcon.isNull() ) + return rect; + + painter->setOpacity( 0.8 ); + painter->drawPixmap( QRect( rect.right() - sourceIconSize, rect.center().y() - sourceIconSize / 2, sourceIcon.width(), sourceIcon.height() ), sourceIcon ); + painter->setOpacity( 1.0 ); + + return rect.adjusted( 0, 0, -( sourceIcon.width() + 8 ), 0 ); +} + + bool PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) { @@ -314,9 +390,11 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con if ( m_hoveringOver != index ) { - PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); - item->requestRepaint(); + QPersistentModelIndex ti = m_hoveringOver; m_hoveringOver = index; + + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( ti ) ); + item->requestRepaint(); emit updateIndex( m_hoveringOver ); } @@ -379,6 +457,33 @@ PlaylistItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, con void PlaylistItemDelegate::resetHoverIndex() { + if ( !m_model ) + return; + + QPersistentModelIndex idx = m_hoveringOver; + m_hoveringOver = QModelIndex(); m_infoButtonRects.clear(); + + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( idx ) ); + if ( item ) + item->requestRepaint(); + + emit updateIndex( idx ); + m_view->repaint(); +} + + +void +PlaylistItemDelegate::modelChanged() +{ + m_pixmaps.clear(); +} + + +void +PlaylistItemDelegate::doUpdateIndex( const QPersistentModelIndex& index ) +{ + if ( index.isValid() ) + emit updateIndex( index ); } diff --git a/src/libtomahawk/playlist/PlaylistItemDelegate.h b/src/libtomahawk/playlist/PlaylistItemDelegate.h index eaf7ab997d..b0ad2efd15 100644 --- a/src/libtomahawk/playlist/PlaylistItemDelegate.h +++ b/src/libtomahawk/playlist/PlaylistItemDelegate.h @@ -29,6 +29,10 @@ class PlayableItem; class PlayableProxyModel; class TrackView; +namespace Tomahawk { + class PixmapDelegateFader; +} + class DLLEXPORT PlaylistItemDelegate : public QStyledItemDelegate { Q_OBJECT @@ -46,6 +50,9 @@ public slots: signals: void updateIndex( const QModelIndex& idx ); +private slots: + void doUpdateIndex( const QPersistentModelIndex& index ); + protected: void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ) const; @@ -53,15 +60,32 @@ public slots: bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); QPersistentModelIndex hoveringOver() const { return m_hoveringOver; } - void setInfoButtonRect( const QPersistentModelIndex& index, const QRect& rect ) const { m_infoButtonRects[ index ] = rect; } -private: + QRect drawInfoButton( QPainter* painter, const QRect& rect, const QModelIndex& index, float height ) const; + QRect drawSourceIcon( QPainter* painter, const QRect& rect, PlayableItem* item, float height ) const; + QRect drawCover( QPainter* painter, const QRect& rect, PlayableItem* item, const QModelIndex& index ) const; + void paintDetailed( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; void paintShort( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index, bool useAvatars = false ) const; QTextOption m_topOption; QTextOption m_bottomOption; + QTextOption m_centerOption; + QTextOption m_centerRightOption; + QFont m_smallFont; + QFont m_smallBoldFont; + QFont m_boldFont; + QFont m_bigBoldFont; + + QFontMetrics m_smallBoldFontMetrics; + QFontMetrics m_bigBoldFontMetrics; + +protected slots: + virtual void modelChanged(); + +private: + mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps; mutable QHash< QPersistentModelIndex, QRect > m_infoButtonRects; QPersistentModelIndex m_hoveringOver; From 1a882032476eb9210f5b9f8f312d8bc6f6e18214 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 10:36:44 +0200 Subject: [PATCH 294/565] * Views handle leaveEvents now and inform the delegate to update itself when required. --- src/libtomahawk/playlist/GridView.cpp | 9 +++++++++ src/libtomahawk/playlist/GridView.h | 1 + src/libtomahawk/playlist/TrackView.cpp | 18 ++++++++++++++---- src/libtomahawk/playlist/TrackView.h | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/GridView.cpp b/src/libtomahawk/playlist/GridView.cpp index 7e0fda64b3..913fb5f6d6 100644 --- a/src/libtomahawk/playlist/GridView.cpp +++ b/src/libtomahawk/playlist/GridView.cpp @@ -195,6 +195,15 @@ GridView::scrollContentsBy( int dx, int dy ) } +void +GridView::leaveEvent( QEvent* event ) +{ + QListView::leaveEvent( event ); + + m_delegate->resetHoverIndex(); +} + + void GridView::paintEvent( QPaintEvent* event ) { diff --git a/src/libtomahawk/playlist/GridView.h b/src/libtomahawk/playlist/GridView.h index 2419dce25c..8b5de2c83d 100644 --- a/src/libtomahawk/playlist/GridView.h +++ b/src/libtomahawk/playlist/GridView.h @@ -89,6 +89,7 @@ public slots: virtual void startDrag( Qt::DropActions supportedActions ); virtual void scrollContentsBy( int dx, int dy ); + void leaveEvent( QEvent* event ); void paintEvent( QPaintEvent* event ); void resizeEvent( QResizeEvent* event ); void wheelEvent( QWheelEvent* ); diff --git a/src/libtomahawk/playlist/TrackView.cpp b/src/libtomahawk/playlist/TrackView.cpp index e9ca9a1205..18a07adb2f 100644 --- a/src/libtomahawk/playlist/TrackView.cpp +++ b/src/libtomahawk/playlist/TrackView.cpp @@ -166,8 +166,7 @@ TrackView::setProxyModel( PlayableProxyModel* model ) connect( m_proxyModel, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( verifySize() ) ); m_delegate = new PlaylistItemDelegate( this, m_proxyModel ); - setItemDelegate( m_delegate ); - + QTreeView::setItemDelegate( m_delegate ); QTreeView::setModel( m_proxyModel ); } @@ -184,8 +183,11 @@ TrackView::setModel( QAbstractItemModel* model ) void TrackView::setPlaylistItemDelegate( PlaylistItemDelegate* delegate ) { + if ( m_delegate ) + delete m_delegate; + m_delegate = delegate; - setItemDelegate( delegate ); + QTreeView::setItemDelegate( delegate ); verifySize(); } @@ -511,7 +513,6 @@ TrackView::dragMoveEvent( QDragMoveEvent* event ) void TrackView::dragLeaveEvent( QDragLeaveEvent* event ) { - tDebug() << Q_FUNC_INFO; QTreeView::dragLeaveEvent( event ); m_dragging = false; @@ -550,6 +551,15 @@ TrackView::dropEvent( QDropEvent* event ) } +void +TrackView::leaveEvent( QEvent* event ) +{ + QTreeView::leaveEvent( event ); + + m_delegate->resetHoverIndex(); +} + + void TrackView::paintEvent( QPaintEvent* event ) { diff --git a/src/libtomahawk/playlist/TrackView.h b/src/libtomahawk/playlist/TrackView.h index 2281c79999..0398c0dfa6 100644 --- a/src/libtomahawk/playlist/TrackView.h +++ b/src/libtomahawk/playlist/TrackView.h @@ -113,6 +113,7 @@ public slots: virtual void dragMoveEvent( QDragMoveEvent* event ); virtual void dropEvent( QDropEvent* event ); + virtual void leaveEvent( QEvent* event ); virtual void paintEvent( QPaintEvent* event ); virtual void keyPressEvent( QKeyEvent* event ); virtual void wheelEvent( QWheelEvent* event ); From 241a3a075b28030b2dd3e35ca3168b7b7ca3e8ec Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 10:37:46 +0200 Subject: [PATCH 295/565] * Delegates use the base-class impls for painting / handling. --- .../playlist/AlbumItemDelegate.cpp | 77 +++---------- src/libtomahawk/playlist/AlbumItemDelegate.h | 9 +- src/libtomahawk/playlist/GridItemDelegate.cpp | 14 +++ src/libtomahawk/playlist/GridItemDelegate.h | 3 + .../playlist/PlaylistLargeItemDelegate.cpp | 104 ++++-------------- .../playlist/PlaylistLargeItemDelegate.h | 15 +-- 6 files changed, 61 insertions(+), 161 deletions(-) diff --git a/src/libtomahawk/playlist/AlbumItemDelegate.cpp b/src/libtomahawk/playlist/AlbumItemDelegate.cpp index f938be75fb..746f952264 100644 --- a/src/libtomahawk/playlist/AlbumItemDelegate.cpp +++ b/src/libtomahawk/playlist/AlbumItemDelegate.cpp @@ -48,14 +48,6 @@ AlbumItemDelegate::AlbumItemDelegate( TrackView* parent, PlayableProxyModel* pro , m_view( parent ) , m_model( proxy ) { - m_centerOption = QTextOption( Qt::AlignVCenter ); - m_centerOption.setWrapMode( QTextOption::NoWrap ); - - m_centerRightOption = QTextOption( Qt::AlignVCenter | Qt::AlignRight ); - m_centerRightOption.setWrapMode( QTextOption::NoWrap ); - - connect( proxy, SIGNAL( modelReset() ), this, SLOT( modelChanged() ) ); - connect( parent, SIGNAL( modelChanged() ), this, SLOT( modelChanged() ) ); } @@ -82,11 +74,10 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, opt.text.clear(); qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); - if ( m_view->header()->visualIndex( index.column() ) > 0 ) return; - const track_ptr track = item->query()->track(); + const track_ptr& track = item->query()->track(); QString lowerText; painter->save(); @@ -102,73 +93,41 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, r.adjust( pixHeight, 0, 0, 0 ); } - QFont boldFont = opt.font; - boldFont.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); - boldFont.setWeight( 99 ); - QFontMetrics boldFontMetrics( boldFont ); - - QFont smallBoldFont = opt.font; - smallBoldFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); - smallBoldFont.setBold( true ); - smallBoldFont.setWeight( 60 ); - QFontMetrics smallBoldFontMetrics( smallBoldFont ); - - painter->setFont( boldFont ); + painter->setFont( m_bigBoldFont ); painter->setPen( option.palette.text().color().lighter( 450 ) ); + QRect leftRect = r; QRect figureRect = r.adjusted( 4, 0, 0, 0 ); figureRect.setWidth( QFontMetrics( painter->font() ).width( "888" ) ); - painter->drawText( figureRect, QString::number( index.row() + 1 ), QTextOption( Qt::AlignCenter ) ); - - r.adjust( figureRect.width() + 12, 0, 0, 0 ); - QRect rightRect = r.adjusted( r.width() - smallBoldFontMetrics.width( TomahawkUtils::timeToString( track->duration() ) ), 0, 0, 0 ); - QRect leftRect = r.adjusted( 0, 0, -( rightRect.width() + 8 ), 0 ); - - const int sourceIconSize = r.height(); if ( hoveringOver() == index && index.column() == 0 ) { - const QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, QSize( sourceIconSize, sourceIconSize ) ); - QRect arrowRect = QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, infoIcon.width(), infoIcon.height() ); - painter->drawPixmap( arrowRect, infoIcon ); - - setInfoButtonRect( index, arrowRect ); - rightRect.moveLeft( rightRect.left() - infoIcon.width() - 8 ); - leftRect.adjust( 0, 0, -( infoIcon.width() + 8 ), 0 ); + drawInfoButton( painter, figureRect.adjusted( 1, 0, 0, 0 ), index, 1.0 ); } - else if ( item->query()->numResults() && !item->query()->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ).isNull() ) + else + { + painter->drawText( figureRect, QString::number( index.row() + 1 ), QTextOption( Qt::AlignCenter ) ); + } + + leftRect = r; + leftRect.adjust( figureRect.width() + 12, 0, 0, 0 ); + QRect rightRect = r.adjusted( r.width() - m_smallBoldFontMetrics.width( TomahawkUtils::timeToString( track->duration() ) ), 0, 0, 0 ); { - const QPixmap sourceIcon = item->query()->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ); - painter->setOpacity( 0.8 ); - painter->drawPixmap( QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, sourceIcon.width(), sourceIcon.height() ), sourceIcon ); - painter->setOpacity( 1.0 ); - rightRect.moveLeft( rightRect.left() - sourceIcon.width() - 8 ); + const QRect leftRectBefore = leftRect; + leftRect = drawSourceIcon( painter, leftRect, item, 1.0 ); + rightRect.moveLeft( rightRect.left() - ( leftRectBefore.width() - leftRect.width() ) ); + leftRect.setWidth( leftRect.width() - rightRect.width() ); } - QString text = painter->fontMetrics().elidedText( track->track(), Qt::ElideRight, leftRect.width() ); + const QString text = painter->fontMetrics().elidedText( track->track(), Qt::ElideRight, leftRect.width() ); painter->setPen( opt.palette.text().color() ); painter->drawText( leftRect, text, m_centerOption ); if ( track->duration() > 0 ) { - painter->setPen( opt.palette.text().color() ); - painter->setFont( smallBoldFont ); + painter->setFont( m_smallBoldFont ); painter->drawText( rightRect, TomahawkUtils::timeToString( track->duration() ), m_centerRightOption ); } } painter->restore(); } - - -void -AlbumItemDelegate::doUpdateIndex( const QPersistentModelIndex& idx ) -{ - if ( idx.isValid() ) - emit updateIndex( idx ); -} - - -void -AlbumItemDelegate::modelChanged() -{ -} diff --git a/src/libtomahawk/playlist/AlbumItemDelegate.h b/src/libtomahawk/playlist/AlbumItemDelegate.h index 040a20099d..dccdbc5b8c 100644 --- a/src/libtomahawk/playlist/AlbumItemDelegate.h +++ b/src/libtomahawk/playlist/AlbumItemDelegate.h @@ -43,18 +43,11 @@ Q_OBJECT AlbumItemDelegate( TrackView* parent = 0, PlayableProxyModel* proxy = 0 ); virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; - + protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; -private slots: - void doUpdateIndex( const QPersistentModelIndex& idx ); - void modelChanged(); - private: - QTextOption m_centerOption; - QTextOption m_centerRightOption; - TrackView* m_view; PlayableProxyModel* m_model; }; diff --git a/src/libtomahawk/playlist/GridItemDelegate.cpp b/src/libtomahawk/playlist/GridItemDelegate.cpp index c25c368b21..544db79350 100644 --- a/src/libtomahawk/playlist/GridItemDelegate.cpp +++ b/src/libtomahawk/playlist/GridItemDelegate.cpp @@ -543,6 +543,20 @@ GridItemDelegate::onCurrentIndexChanged() } +void +GridItemDelegate::resetHoverIndex() +{ + foreach ( ImageButton* button, m_playButton ) + button->deleteLater(); + m_playButton.clear(); + + QModelIndex idx = m_hoveringOver; + m_hoveringOver = QPersistentModelIndex(); + m_hoverIndex = QPersistentModelIndex(); + doUpdateIndex( idx ); +} + + void GridItemDelegate::createPauseButton( const QPersistentModelIndex& index ) { diff --git a/src/libtomahawk/playlist/GridItemDelegate.h b/src/libtomahawk/playlist/GridItemDelegate.h index 6b48e3a191..002daa589a 100644 --- a/src/libtomahawk/playlist/GridItemDelegate.h +++ b/src/libtomahawk/playlist/GridItemDelegate.h @@ -48,6 +48,9 @@ Q_OBJECT QSize itemSize() const { return m_itemSize; } void setItemSize( const QSize& size ) { m_itemSize = size; } +public slots: + void resetHoverIndex(); + protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; diff --git a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp index d2ef227d0d..e6b36b079b 100644 --- a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp @@ -37,8 +37,6 @@ #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" -#include -#include using namespace Tomahawk; @@ -49,17 +47,6 @@ PlaylistLargeItemDelegate::PlaylistLargeItemDelegate( DisplayMode mode, TrackVie , m_model( proxy ) , m_mode( mode ) { - m_topOption = QTextOption( Qt::AlignTop ); - m_topOption.setWrapMode( QTextOption::NoWrap ); - - m_centerRightOption = QTextOption( Qt::AlignVCenter | Qt::AlignRight ); - m_centerRightOption.setWrapMode( QTextOption::NoWrap ); - - m_bottomOption = QTextOption( Qt::AlignBottom ); - m_bottomOption.setWrapMode( QTextOption::NoWrap ); - - connect( proxy, SIGNAL( modelReset() ), SLOT( modelChanged() ) ); - connect( parent, SIGNAL( modelChanged() ), SLOT( modelChanged() ) ); } @@ -91,7 +78,6 @@ PlaylistLargeItemDelegate::drawRichText( QPainter* painter, const QStyleOptionVi y += ( rect.height() - height ) / 2; QAbstractTextDocumentLayout::PaintContext context; - context.palette.setColor( QPalette::Text, painter->pen().color() ); painter->save(); @@ -111,10 +97,10 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& prepareStyleOption( &opt, index, item ); bool isUnlistened = true; - if( m_mode == Inbox ) + if ( m_mode == Inbox ) { QList< Tomahawk::SocialAction > socialActions = item->query()->queryTrack()->allSocialActions(); - foreach( const Tomahawk::SocialAction& sa, socialActions ) + foreach ( const Tomahawk::SocialAction& sa, socialActions ) { if ( sa.action.toString() == "Inbox" && sa.value.toBool() == false ) { @@ -132,8 +118,8 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& const track_ptr track = item->query()->track(); QString lowerText; - QSize avatarSize( 32, 32 ); + if ( m_mode == RecentlyPlayed && item->playbackLog().source ) { QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->playbackLog().timestamp ), true ); @@ -147,7 +133,6 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& if ( m_mode == LatestAdditions && item->query()->numResults() ) { QString playtime = TomahawkUtils::ageToString( QDateTime::fromTime_t( item->query()->results().first()->modificationTime() ), true ); - lowerText = QString( tr( "added %1", "e.g. added 3 hours ago" ) ).arg( playtime ); } @@ -170,78 +155,43 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& if ( item->isPlaying() ) { painter->drawPixmap( npr, TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker, TomahawkUtils::Original, npr.size() ) ); - r.adjust( pixHeight + 2*pixMargin, 0, 0, 0 ); + r.adjust( pixHeight + 2 * pixMargin, 0, 0, 0 ); } else { npr = npr.adjusted( 0, npr.height() / 4, -npr.width() / 2, -npr.height() / 4 ); painter->drawPixmap( npr, TomahawkUtils::defaultPixmap( TomahawkUtils::InboxNewItem, TomahawkUtils::Original, npr.size() ) ); - r.adjust( npr.width() + 2*pixMargin, 0, 0, 0 ); + r.adjust( npr.width() + 2 * pixMargin, 0, 0, 0 ); } } painter->setPen( opt.palette.text().color() ); - QRect pixmapRect = r.adjusted( 6, 0, -option.rect.width() + option.rect.height() - 6 + r.left(), 0 ); - QRect avatarRect = r.adjusted( option.rect.width() - r.left() - 12 - avatarSize.width(), ( option.rect.height() - avatarSize.height() ) / 2 - 5, 0, 0 ); - avatarRect.setSize( avatarSize ); - - if ( !m_pixmaps.contains( index ) ) - { - m_pixmaps.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->query(), pixmapRect.size(), TomahawkUtils::RoundedCorners, false ) ) ); - _detail::Closure* closure = NewClosure( m_pixmaps[ index ], SIGNAL( repaintRequest() ), const_cast(this), SLOT( doUpdateIndex( const QPersistentModelIndex& ) ), QPersistentModelIndex( index ) ); - closure->setAutoDelete( false ); - } - - const QPixmap pixmap = m_pixmaps[ index ]->currentPixmap(); - painter->drawPixmap( pixmapRect, pixmap ); - - QFont boldFont = opt.font; - boldFont.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); - boldFont.setWeight( 99 ); - QFontMetrics boldFontMetrics( boldFont ); - - QFont smallBoldFont = opt.font; - smallBoldFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); - smallBoldFont.setBold( true ); - smallBoldFont.setWeight( 60 ); - QFontMetrics smallBoldFontMetrics( smallBoldFont ); - - QFont smallFont = opt.font; - smallFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); - - r.adjust( pixmapRect.width() + 12, 1, - 16, 0 ); - QRect rightRect = r.adjusted( r.width() - smallBoldFontMetrics.width( TomahawkUtils::timeToString( track->duration() ) ), 0, 0, 0 ); - QRect leftRect = r.adjusted( 0, 0, -( rightRect.width() + 8 ), 0 ); - - const int sourceIconSize = avatarRect.width() - 6; + r.adjust( 4, 0, -16, 0 ); + QRect leftRect; if ( hoveringOver() == index && !index.data().toString().isEmpty() && index.column() == 0 ) { - const QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, QSize( sourceIconSize, sourceIconSize ) ); - QRect arrowRect = QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, infoIcon.width(), infoIcon.height() ); - painter->drawPixmap( arrowRect, infoIcon ); - - setInfoButtonRect( index, arrowRect ); - rightRect.moveLeft( rightRect.left() - infoIcon.width() - 8 ); - leftRect.adjust( 0, 0, -( infoIcon.width() + 8 ), 0 ); + leftRect = drawInfoButton( painter, r, index, 0.9 ); } - else if ( item->query()->numResults() && !item->query()->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ).isNull() ) + else { - const QPixmap sourceIcon = item->query()->results().first()->sourceIcon( TomahawkUtils::RoundedCorners, QSize( sourceIconSize, sourceIconSize ) ); - painter->setOpacity( 0.8 ); - painter->drawPixmap( QRect( rightRect.right() - sourceIconSize, r.center().y() - sourceIconSize / 2, sourceIcon.width(), sourceIcon.height() ), sourceIcon ); - painter->setOpacity( 1.0 ); + leftRect = drawCover( painter, r, item, index ); + } - rightRect.moveLeft( rightRect.left() - sourceIcon.width() - 8 ); - leftRect.adjust( 0, 0, -( sourceIcon.width() + 8 ), 0 ); + leftRect.setX( leftRect.left() + 8 ); + QRect rightRect = r.adjusted( r.width() - m_smallBoldFontMetrics.width( TomahawkUtils::timeToString( track->duration() ) ), 0, 0, 0 ); + { + const QRect leftRectBefore = leftRect; + leftRect = drawSourceIcon( painter, leftRect, item, 0.5 ); + rightRect.moveLeft( rightRect.left() - ( leftRectBefore.width() - leftRect.width() ) ); } - painter->setFont( boldFont ); - QString text = painter->fontMetrics().elidedText( track->track(), Qt::ElideRight, leftRect.width() ); + painter->setFont( m_bigBoldFont ); + const QString text = painter->fontMetrics().elidedText( track->track(), Qt::ElideRight, leftRect.width() ); painter->drawText( leftRect, text, m_topOption ); - painter->setFont( smallFont ); + painter->setFont( m_smallFont ); QTextDocument textDoc; if ( track->album().isEmpty() ) textDoc.setHtml( tr( "by %1", "e.g. by SomeArtist" ).arg( track->artist() ) ); @@ -252,7 +202,7 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& textDoc.setDefaultTextOption( m_topOption ); if ( textDoc.idealWidth() <= leftRect.width() ) - drawRichText( painter, opt, leftRect.adjusted( 0, boldFontMetrics.height() + 1, 0, 0 ), Qt::AlignTop, textDoc ); + drawRichText( painter, opt, leftRect.adjusted( 0, m_bigBoldFontMetrics.height() + 1, 0, 0 ), Qt::AlignTop, textDoc ); if ( !( option.state & QStyle::State_Selected || item->isPlaying() ) ) painter->setPen( opt.palette.text().color().darker() ); @@ -270,7 +220,7 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& if ( track->duration() > 0 ) { painter->setPen( opt.palette.text().color() ); - painter->setFont( smallBoldFont ); + painter->setFont( m_smallBoldFont ); painter->drawText( rightRect, TomahawkUtils::timeToString( track->duration() ), m_centerRightOption ); } } @@ -278,16 +228,8 @@ PlaylistLargeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& } -void -PlaylistLargeItemDelegate::doUpdateIndex( const QPersistentModelIndex& idx ) -{ - if ( idx.isValid() ) - emit updateIndex( idx ); -} - - void PlaylistLargeItemDelegate::modelChanged() { - m_pixmaps.clear(); + PlaylistItemDelegate::modelChanged(); } diff --git a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h index 2812225c85..d67f047a7d 100644 --- a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h +++ b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.h @@ -27,10 +27,6 @@ #include "DllMacro.h" #include "Typedefs.h" -namespace Tomahawk { -class PixmapDelegateFader; -} - class PlayableItem; class PlayableProxyModel; class TrackView; @@ -50,19 +46,12 @@ Q_OBJECT protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; -private slots: - void doUpdateIndex( const QPersistentModelIndex& idx ); - void modelChanged(); +protected slots: + virtual void modelChanged(); private: void drawRichText( QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, int flags, QTextDocument& text ) const; - QTextOption m_topOption; - QTextOption m_centerRightOption; - QTextOption m_bottomOption; - - mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps; - TrackView* m_view; PlayableProxyModel* m_model; DisplayMode m_mode; From 0d2dade6da577f11766e3a9991c7baf83cc74ceb Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 10:38:06 +0200 Subject: [PATCH 296/565] * Don't print out cache-value in log. --- src/libtomahawk/utils/TomahawkCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/utils/TomahawkCache.cpp b/src/libtomahawk/utils/TomahawkCache.cpp index 07a1f6707a..d0c679121c 100644 --- a/src/libtomahawk/utils/TomahawkCache.cpp +++ b/src/libtomahawk/utils/TomahawkCache.cpp @@ -125,7 +125,7 @@ Cache::putData( const QString& identifier, qint64 maxAge, const QString& key, co addClient( identifier ); QSettings cached_settings( cacheDir, QSettings::IniFormat ); cached_settings.setValue( key, QVariant::fromValue( CacheData( QDateTime::currentMSecsSinceEpoch() + maxAge, value ) ) ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Storing from client" << identifier << maxAge << key << value; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Storing from client" << identifier << maxAge << key; } From ca307d69226b4b8abff7a50bbb9b7f4f6e7fe04a Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 10:43:05 +0200 Subject: [PATCH 297/565] Do not kill used ControlConnection * Debug spam ++ --- src/libtomahawk/Source.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 035288f015..8e7fd96c4e 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -95,11 +95,13 @@ Source::setControlConnection( ControlConnection* cc ) if ( !m_cc.isNull() && m_cc->isReady() && m_cc->isRunning() ) { const QString& nodeid = Database::instance()->impl()->dbid(); + peerInfoDebug( (*cc->peerInfos().begin()) ) << Q_FUNC_INFO << "Comparing" << cc->id() << "and" << nodeid << "to detect duplicate connection, outbound:" << cc->outbound(); if ( cc->id() < nodeid && m_cc->outbound() ) { - m_cc = cc; // This ControlConnection is not needed anymore, get rid of it! m_cc->deleteLater(); + // Use new ControlConnection + m_cc = cc; return true; } else From ca025e41b08438f5792d8d9de9280e8825553c4d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 10:53:26 +0200 Subject: [PATCH 298/565] * Style fixes. --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 9 ++++++++- src/libtomahawk/widgets/WhatsHotWidget.cpp | 7 ++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 99d0de3f37..74aac66a12 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -163,6 +163,7 @@ NetworkActivityWidget::yearlyCharts( const QList& tracks ) } } + void NetworkActivityWidget::overallCharts( const QList& tracks ) { @@ -218,6 +219,7 @@ NetworkActivityWidget::fetchYearCharts() Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( yearCharts ) ); } + void NetworkActivityWidget::fetchOverallCharts() { @@ -239,6 +241,7 @@ NetworkActivityWidget::fetchWeekCharts() Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( weekCharts ) ); } + void NetworkActivityWidget::fetchMonthCharts() { @@ -267,6 +270,7 @@ NetworkActivityWidget::showWeekCharts() } } + void NetworkActivityWidget::showMonthCharts() { @@ -283,6 +287,7 @@ NetworkActivityWidget::showMonthCharts() } } + void NetworkActivityWidget::showYearCharts() { @@ -299,7 +304,9 @@ NetworkActivityWidget::showYearCharts() } } -void NetworkActivityWidget::showOverallCharts() + +void +NetworkActivityWidget::showOverallCharts() { d_func()->activeView = OverallChart; if ( !d_func()->overallChartsModel.isNull() ) diff --git a/src/libtomahawk/widgets/WhatsHotWidget.cpp b/src/libtomahawk/widgets/WhatsHotWidget.cpp index c22b2d35ec..419003ea40 100644 --- a/src/libtomahawk/widgets/WhatsHotWidget.cpp +++ b/src/libtomahawk/widgets/WhatsHotWidget.cpp @@ -313,10 +313,10 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat } } + void -WhatsHotWidget::setViewData(const QVariantMap &data) +WhatsHotWidget::setViewData( const QVariantMap& data ) { - QStandardItem* rootItem = m_crumbModelLeft->invisibleRootItem(); QVariantMap returnedData = data; @@ -367,10 +367,11 @@ WhatsHotWidget::setViewData(const QVariantMap &data) } } + void WhatsHotWidget::infoSystemFinished( QString target ) { - if( m_loading ) + if ( m_loading ) { if ( target != s_whatsHotIdentifier ) { From d14afb5a8f5212eed2651e3fb2b7b13ea256250e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 10 Jun 2013 10:56:07 +0200 Subject: [PATCH 299/565] * Tweak timeouts for the lazy-lists drop-down. --- src/tomahawk/sourcetree/AnimationHelper.cpp | 8 ++++---- src/tomahawk/sourcetree/SourceDelegate.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tomahawk/sourcetree/AnimationHelper.cpp b/src/tomahawk/sourcetree/AnimationHelper.cpp index 1955e26b4a..d11a3f648a 100644 --- a/src/tomahawk/sourcetree/AnimationHelper.cpp +++ b/src/tomahawk/sourcetree/AnimationHelper.cpp @@ -28,12 +28,12 @@ AnimationHelper::AnimationHelper( const QModelIndex& index, QObject *parent ) , m_expandAnimation( 0 ) { m_expandTimer.setSingleShot( true ); - m_expandTimer.setInterval( 600 ); - connect( &m_expandTimer, SIGNAL( timeout() ), SLOT(expandTimeout() ) ); + m_expandTimer.setInterval( 1000 ); + connect( &m_expandTimer, SIGNAL( timeout() ), SLOT( expandTimeout() ) ); m_collapseTimer.setSingleShot( true ); - m_collapseTimer.setInterval( 600 ); - connect( &m_collapseTimer, SIGNAL( timeout() ), SLOT(collapseTimeout() ) ); + m_collapseTimer.setInterval( 0 ); + connect( &m_collapseTimer, SIGNAL( timeout() ), SLOT( collapseTimeout() ) ); } diff --git a/src/tomahawk/sourcetree/SourceDelegate.cpp b/src/tomahawk/sourcetree/SourceDelegate.cpp index f8840758de..86a95bf2aa 100644 --- a/src/tomahawk/sourcetree/SourceDelegate.cpp +++ b/src/tomahawk/sourcetree/SourceDelegate.cpp @@ -103,7 +103,7 @@ SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& int dropTypes = dropTypeCount( item ); QSize originalSize = QSize( option.rect.width(), option.fontMetrics.height() * 1.4 ); QSize targetSize = originalSize + QSize( 0, dropTypes == 0 ? 0 : 38 + option.fontMetrics.height() * 1.4 ); - m_expandedMap.value( index )->initialize( originalSize, targetSize, 300 ); + m_expandedMap.value( index )->initialize( originalSize, targetSize, 600 ); m_expandedMap.value( index )->expand(); } QMetaObject::invokeMethod( m_parent, "update", Qt::QueuedConnection, Q_ARG( QModelIndex, index ) ); @@ -1005,7 +1005,7 @@ SourceDelegate::hovered( const QModelIndex& index, const QMimeData* mimeData ) { foreach ( AnimationHelper* helper, m_expandedMap ) { - helper->collapse(); + helper->collapse( true ); } return; } @@ -1014,7 +1014,7 @@ SourceDelegate::hovered( const QModelIndex& index, const QMimeData* mimeData ) { foreach ( AnimationHelper* helper, m_expandedMap ) { - helper->collapse(); + helper->collapse( true ); } m_newDropHoverIndex = index; From 2555ab7766efd21e93b2ee4fd0852ab2c1bc2eb3 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 11:40:33 +0200 Subject: [PATCH 300/565] Better debug spam on column restore --- src/libtomahawk/playlist/ViewHeader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/ViewHeader.cpp b/src/libtomahawk/playlist/ViewHeader.cpp index 02054709d5..2bcc4da256 100644 --- a/src/libtomahawk/playlist/ViewHeader.cpp +++ b/src/libtomahawk/playlist/ViewHeader.cpp @@ -81,18 +81,17 @@ ViewHeader::checkState() disconnect( this, SIGNAL( sectionResized( int, int, int ) ), this, SLOT( onSectionsChanged() ) ); QByteArray state; - tDebug( LOGVERBOSE ) << "Restoring columns state for view:" << m_guid; - if ( !m_guid.isEmpty() ) state = TomahawkSettings::instance()->playlistColumnSizes( m_guid ); if ( !state.isEmpty() ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Restoring columns state for view:" << m_guid; restoreState( state ); } else { - tDebug( LOGVERBOSE ) << "Giving columns initial weighting:" << m_columnWeights; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Giving columns of view" << m_guid << "initial weighting:" << m_columnWeights << "for" << count() << "columns"; for ( int i = 0; i < count() - 1; i++ ) { if ( isSectionHidden( i ) ) From 251ed6e55515cc85410f3cb05030f1ed5604353f Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 14:10:17 +0200 Subject: [PATCH 301/565] Documentation++, make ConnectionManager CTOR private --- src/libtomahawk/network/ConnectionManager.h | 43 ++++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/ConnectionManager.h b/src/libtomahawk/network/ConnectionManager.h index eca5a6102f..ce95e4016b 100644 --- a/src/libtomahawk/network/ConnectionManager.h +++ b/src/libtomahawk/network/ConnectionManager.h @@ -29,17 +29,31 @@ class ConnectionManagerPrivate; class QTcpSocketExtra; +/** + * Handle all (outgoing) connection attempts to a specific nodeid. + */ class DLLEXPORT ConnectionManager : public QObject { Q_OBJECT public: + /** + * Get the ConnectionManager responsible for the given nodeid, if there is none yet, create a new instance. + */ static QSharedPointer getManagerForNodeId( const QString& nodeid ); + + /** + * Activate/Deactivate the ConnectionManager for a specific nodeid. + * + * A strong reference is held for every active ConnectionManagers so that they are not automatically deleted while in operation. + */ static void setActive( bool active, const QString& nodeid, const QSharedPointer& manager ); - ConnectionManager( const QString& nodeid ); ~ConnectionManager(); + /** + * Receive incoming SipInfos and start a new thread to connect to this peer. + */ void handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ); QWeakPointer< ConnectionManager > weakRef() const; @@ -53,12 +67,37 @@ private slots: Q_DECLARE_PRIVATE( ConnectionManager ) ConnectionManagerPrivate* d_ptr; - // Proxy to hand over a strong reference to the connectionManager + /** + * Create a new ConnectionManager. + * + * This should only be done internally so that we do not have more than one ConnectionManager for a nodeid. + */ + ConnectionManager( const QString& nodeid ); + + /** + * Proxy handleSipInfoPrivate to hand over a strong reference to the connectionManager + * so that the refcount is >0 while transferring the context of operation to another thread. + */ static void handleSipInfoPrivateS( const Tomahawk::peerinfo_ptr& peerInfo, const QSharedPointer& connectionManager ); + /** + * Acquire the object lock and register this ConnectionManager as active. + */ void activate(); + + /** + * Release the object lock and register this ConnectionManager as inactive. + */ void deactivate(); + + /** + * Try to connect to a peer with a given SipInfo. + */ void connectToPeer(const Tomahawk::peerinfo_ptr& peerInfo , bool lock); + + /** + * Look for existing connections and try to connect if there is none. + */ void handleSipInfoPrivate( const Tomahawk::peerinfo_ptr& peerInfo ); /** From c248ebb9e0809876accb601dd79ca558e08b4d17 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 14:40:33 +0200 Subject: [PATCH 302/565] Speed up connecting by sorting the SipInfos --- src/libtomahawk/network/ConnectionManager.cpp | 64 ++++++++++++++++++- src/libtomahawk/network/Servent.cpp | 14 ++++ src/libtomahawk/network/Servent.h | 7 ++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 7a0d9aade3..f430b45a14 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -164,7 +164,69 @@ ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool l // If we are not connected, try to connect d_func()->currentPeerInfo = peerInfo; peerInfoDebug( peerInfo ) << "No existing connection found, trying to connect."; - d_func()->sipCandidates.append( peerInfo->sipInfos() ); + // Sort SipInfos + QList< SipInfo > anyOther; + QList< SipInfo > publicIPv4; + QList< SipInfo > publicIPv6; + QList< SipInfo > privateIPv4; + QList< SipInfo > privateIPv6; + foreach ( SipInfo sipInfo, peerInfo->sipInfos() ) + { + if ( !sipInfo.isVisible() ) + { + continue; + } + + QHostAddress ha; + if ( ha.setAddress( sipInfo.host() ) ) + { + if ( Servent::isValidExternalIP( ha ) ) + { + if ( ha.protocol() == QAbstractSocket::IPv6Protocol ) + { + publicIPv6.append( sipInfo ); + } + else + { + publicIPv4.append( sipInfo ); + } + } + else + { + if ( ha.protocol() == QAbstractSocket::IPv6Protocol ) + { + privateIPv6.append( sipInfo ); + } + else + { + privateIPv4.append( sipInfo ); + } + } + } + else + { + anyOther.append( sipInfo ); + } + + } + if ( Servent::instance()->ipv6ConnectivityLikely() && !publicIPv6.isEmpty() ) + { + // Prefer IPv6 over IPv4 + d_func()->sipCandidates.append( anyOther ); + d_func()->sipCandidates.append( publicIPv6 ); + d_func()->sipCandidates.append( publicIPv4 ); + d_func()->sipCandidates.append( privateIPv6 ); + d_func()->sipCandidates.append( privateIPv4 ); + } + else + { + // First try all IPv4 before trying IPv6 + d_func()->sipCandidates.append( anyOther ); + d_func()->sipCandidates.append( publicIPv4 ); + d_func()->sipCandidates.append( privateIPv4 ); + d_func()->sipCandidates.append( publicIPv6 ); + d_func()->sipCandidates.append( privateIPv6 ); + } QVariantMap m; m["conntype"] = "accept-offer"; diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index bdb77ee92c..2602112793 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -953,6 +953,20 @@ Servent::visibleExternally() const return (!d_func()->externalHostname.isNull()) || (d_func()->externalAddresses.length() > 0); } +bool +Servent::ipv6ConnectivityLikely() const +{ + foreach ( QHostAddress ha, d_func()->externalAddresses ) + { + if ( ha.protocol() == QAbstractSocket::IPv6Protocol && Servent::isValidExternalIP( ha ) ) + { + return true; + } + } + + return false; +} + int Servent::port() const { diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 63f259ce30..76ccbc4228 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -99,6 +99,13 @@ public slots: bool visibleExternally() const; + /** + * Is the probality that this host supports IPv6 high? + * + * Though we cannot fully test for IPv6 connectivity, some guesses based on non-localhost addresses are done. + */ + bool ipv6ConnectivityLikely() const; + /** * The port this Peer listens directly (per default) */ From 0a4a2810272bbfeecae58ce7be8ffdd426e84dcc Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 15:42:15 +0200 Subject: [PATCH 303/565] Move all sockets to the Servent thread. --- src/libtomahawk/network/ConnectionManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index f430b45a14..0180dfee05 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -309,7 +309,7 @@ void ConnectionManager::tryConnect() peerInfoDebug( d_func()->currentPeerInfo ) << Q_FUNC_INFO << "Connecting socket to " << info.host() << ":" << info.port(); sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite ); - sock->moveToThread( thread() ); + sock->moveToThread( Servent::instance()->thread() ); } void From aafab2e01661da765ec58d9a6a548d1138478459 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 15:44:01 +0200 Subject: [PATCH 304/565] Group peers by (Account, nodeid) in the Diagnostics Dialog, add nodeid info --- src/tomahawk/DiagnosticsDialog.cpp | 40 +++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index 71505bd5c9..ead2446a39 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -166,19 +166,41 @@ DiagnosticsDialog::accountLog( Tomahawk::Accounts::Account* account ) .arg( stateString ) ); - foreach( const Tomahawk::peerinfo_ptr& peerInfo, account->sipPlugin()->peersOnline() ) + QMap< QString, QList< Tomahawk::peerinfo_ptr > > nodes; + foreach ( const Tomahawk::peerinfo_ptr& peerInfo, account->sipPlugin()->peersOnline() ) { - accountInfo.append( QString( " %1: " ).arg( peerInfo->id() ) ); - foreach ( SipInfo info, peerInfo->sipInfos() ) + if ( !nodes.contains( peerInfo->nodeId() ) ) { - if ( info.isValid() ) - accountInfo.append( QString( "[%1]:%2; " ).arg( info.host() ).arg( info.port() ) ); - else - accountInfo.append( "SipInfo invalid; " ); + nodes[peerInfo->nodeId()] = QList< Tomahawk::peerinfo_ptr >(); + } + nodes[peerInfo->nodeId()].append( peerInfo); + } + foreach ( const QString& nodeid, nodes.keys() ) + { + accountInfo.append( QString( " " ) ); + QStringList peerIds; + foreach ( const Tomahawk::peerinfo_ptr& peerInfo, nodes.value( nodeid ) ) + { + peerIds << peerInfo->id(); + } + accountInfo.append( peerIds.join( QChar( ',' ) ) ); + accountInfo.append( QString( ": %1 @ ").arg( nodeid ) ); + QStringList sipInfos; + foreach ( const Tomahawk::peerinfo_ptr& peerInfo, nodes.value( nodeid ) ) + { + foreach ( SipInfo info, peerInfo->sipInfos() ) + { + if ( info.isValid() ) + sipInfos << QString( "[%1]:%2" ).arg( info.host() ).arg( info.port() ) ; + else + sipInfos << QString( "SipInfo invalid" ); + } } - if ( ( ( peerInfo->sipInfos().length() == 1 ) && ( !peerInfo->sipInfos().first().isVisible() ) ) || ( peerInfo->sipInfos().isEmpty() ) ) + sipInfos.removeDuplicates(); + accountInfo.append( sipInfos.join( QChar( ';' ) ) ); + if ( ( ( nodes.value( nodeid ).first()->sipInfos().length() == 1 ) && ( !nodes.value( nodeid ).first()->sipInfos().first().isVisible() ) ) || ( nodes.value( nodeid ).first()->sipInfos().isEmpty() ) ) accountInfo.append( "(outbound connections only) "); - accountInfo.append( QString( " (%1)\n" ).arg( peerInfo->versionString() ) ); + accountInfo.append( QString( " (%1)\n" ).arg( nodes.value( nodeid ).first()->versionString() ) ); } accountInfo.append( "\n" ); From 18d488b0f16ba64866753d9f322e98ca1eac9fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 10 Jun 2013 14:46:58 +0200 Subject: [PATCH 305/565] Stylefix --- src/libtomahawk/utils/M3uLoader.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/utils/M3uLoader.cpp b/src/libtomahawk/utils/M3uLoader.cpp index 7176c9f9a0..033a0b037a 100644 --- a/src/libtomahawk/utils/M3uLoader.cpp +++ b/src/libtomahawk/utils/M3uLoader.cpp @@ -77,9 +77,11 @@ M3uLoader::getTags( const QFileInfo& info ) TagLib::FileRef f( encodedName ); if( f.isNull() ) return; + TagLib::Tag *tag = f.tag(); if( !tag ) return; + QString artist = TStringToQString( tag->artist() ).trimmed(); QString album = TStringToQString( tag->album() ).trimmed(); QString track = TStringToQString( tag->title() ).trimmed(); @@ -103,6 +105,7 @@ M3uLoader::getTags( const QFileInfo& info ) } } + void M3uLoader::parseLine( const QString& line, const QFile& file ) { @@ -114,7 +117,7 @@ M3uLoader::parseLine( const QString& line, const QFile& file ) } else { - QUrl fileUrl = QUrl::fromUserInput( QString( QFileInfo(file).canonicalPath() + "/" + line.simplified() ) ); + QUrl fileUrl = QUrl::fromUserInput( QString( QFileInfo( file ).canonicalPath() + "/" + line.simplified() ) ); QFileInfo tmpFile( fileUrl.toLocalFile() ); if ( tmpFile.exists() ) { @@ -123,6 +126,7 @@ M3uLoader::parseLine( const QString& line, const QFile& file ) } } + void M3uLoader::parseM3u( const QString& fileLink ) { @@ -145,7 +149,7 @@ M3uLoader::parseM3u( const QString& fileLink ) QString line = stream.readLine().trimmed(); /// Fallback solution for, (drums) itunes! - singleLine.append(line); + singleLine.append( line ); /// If anyone wants to take on the regex for parsing EXT, go ahead /// But the notion that users does not tag by a common rule. that seems hard @@ -161,7 +165,7 @@ M3uLoader::parseM3u( const QString& fileLink ) { if ( !singleLine.isEmpty() ) { - QStringList m3uList = singleLine.split("\r"); + QStringList m3uList = singleLine.split( "\r" ); foreach( const QString& line, m3uList ) parseLine( line, file ); } @@ -190,6 +194,7 @@ M3uLoader::parseM3u( const QString& fileLink ) m_tracks.clear(); } + void M3uLoader::playlistCreated() { From 587568e0269a611cc68b478c5893ea17c3d7d704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 10 Jun 2013 14:53:37 +0200 Subject: [PATCH 306/565] TWK-1377: Space Encoding on Imported M3Us --- src/libtomahawk/utils/M3uLoader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libtomahawk/utils/M3uLoader.cpp b/src/libtomahawk/utils/M3uLoader.cpp index 033a0b037a..ad3c4717a3 100644 --- a/src/libtomahawk/utils/M3uLoader.cpp +++ b/src/libtomahawk/utils/M3uLoader.cpp @@ -139,8 +139,6 @@ M3uLoader::parseM3u( const QString& fileLink ) return; } - m_title = fileInfo.baseName(); - QTextStream stream( &file ); QString singleLine; @@ -179,6 +177,7 @@ M3uLoader::parseM3u( const QString& fileLink ) if ( m_createNewPlaylist ) { + m_title = QUrl::fromPercentEncoding( fileInfo.baseName().toUtf8() ); m_playlist = Playlist::create( SourceList::instance()->getLocal(), uuid(), m_title, From 980f56dbcf7bc39a07f52eb5da090ba580819863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 10 Jun 2013 16:12:18 +0200 Subject: [PATCH 307/565] TWK-1389: Utilize private sessions, if private --- .../accounts/spotify/SpotifyAccount.cpp | 26 +++++++++++++++++-- .../accounts/spotify/SpotifyAccount.h | 3 +++ .../accounts/spotify/SpotifyAccountConfig.cpp | 13 +++++++++- .../accounts/spotify/SpotifyAccountConfig.h | 4 ++- .../accounts/spotify/SpotifyAccountConfig.ui | 18 ++++++++++--- 5 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/accounts/spotify/SpotifyAccount.cpp b/src/libtomahawk/accounts/spotify/SpotifyAccount.cpp index d8a976a302..e37f79ec3f 100644 --- a/src/libtomahawk/accounts/spotify/SpotifyAccount.cpp +++ b/src/libtomahawk/accounts/spotify/SpotifyAccount.cpp @@ -32,6 +32,7 @@ #include "SpotifyInfoPlugin.h" #include "infosystem/InfoSystem.h" #include "utils/Logger.h" +#include "TomahawkSettings.h" #ifndef ENABLE_HEADLESS #include "jobview/JobStatusView.h" @@ -226,7 +227,7 @@ SpotifyAccount::hookupResolver() connect( m_spotifyResolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) ); connect( m_spotifyResolver.data(), SIGNAL( customMessage( QString,QVariantMap ) ), this, SLOT( resolverMessage( QString, QVariantMap ) ) ); - + connect( ActionCollection::instance(), SIGNAL( privacyModeChanged() ), SLOT( privateModeChanged() ) ); // Always get logged in status QVariantMap msg; msg[ "_msgtype" ] = "getCredentials"; @@ -433,6 +434,17 @@ SpotifyAccount::starTrack(const QString &artist, const QString &title, const boo } +void +SpotifyAccount::privateModeChanged() +{ + qDebug() << Q_FUNC_INFO << "Sending privateMode"; + QVariantMap msg; + msg[ "_msgtype" ] = "setPrivacyMode"; + msg[ "private" ] = ( m_configWidget.data()->persitentPrivacy() || TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::PublicListening ); + sendMessage( msg ); +} + + bool SpotifyAccount::loggedIn() const { @@ -1123,6 +1135,7 @@ SpotifyAccount::configurationWidget() m_configWidget = QPointer< SpotifyAccountConfig >( new SpotifyAccountConfig( this ) ); connect( m_configWidget.data(), SIGNAL( login( QString,QString ) ), this, SLOT( login( QString,QString ) ) ); connect( m_configWidget.data(), SIGNAL( logout() ), this, SLOT( logout() ) ); + connect( m_configWidget.data(), SIGNAL( updatePrivacy( bool ) ), this, SLOT( privateModeChanged() ) ); m_configWidget.data()->setPlaylists( m_allSpotifyPlaylists.values() ); } @@ -1178,6 +1191,8 @@ SpotifyAccount::saveConfig() QVariantHash config = configuration(); config[ "deleteOnUnsync" ] = m_configWidget.data()->deleteOnUnsync(); config[ "loveSync" ] = m_configWidget.data()->loveSync(); + config[ "persitentPrivacy" ] = m_configWidget.data()->persitentPrivacy(); + setConfiguration( config ); m_configWidget.data()->saveSettings(); @@ -1214,7 +1229,7 @@ SpotifyAccount::login( const QString& username, const QString& password ) msg[ "_msgtype" ] = "login"; msg[ "username" ] = username; msg[ "password" ] = password; - + msg[ "privateSession" ] = ( m_configWidget.data()->persitentPrivacy() || TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::PublicListening ); msg[ "highQuality" ] = m_configWidget.data()->highQuality(); m_spotifyResolver.data()->sendMessage( msg ); @@ -1465,6 +1480,13 @@ SpotifyAccount::loveSync() const } +bool +SpotifyAccount::persitentPrivacy() const +{ + return configuration().value( "persitentPrivacy", false ).toBool(); +} + + void SpotifyAccount::stopPlaylistSync( SpotifyPlaylistInfo* playlist, bool forceDontDelete ) { diff --git a/src/libtomahawk/accounts/spotify/SpotifyAccount.h b/src/libtomahawk/accounts/spotify/SpotifyAccount.h index 372cc9f8b9..c8f231d32b 100644 --- a/src/libtomahawk/accounts/spotify/SpotifyAccount.h +++ b/src/libtomahawk/accounts/spotify/SpotifyAccount.h @@ -112,6 +112,8 @@ class DLLEXPORT SpotifyAccount : public CustomAtticaAccount bool deleteOnUnsync() const; bool loveSync() const; + bool persitentPrivacy() const; + void starTrack( const QString& artist, const QString& title, const bool starred ); void setManualResolverPath( const QString& resolverPath ); @@ -131,6 +133,7 @@ private slots: void resolverInstalled( const QString& resolverId ); void resolverMessage( const QString& msgType, const QVariantMap& msg ); + void privateModeChanged(); void login( const QString& username, const QString& password ); void logout(); diff --git a/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.cpp b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.cpp index e834de8d08..45d27b15a0 100644 --- a/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.cpp +++ b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.cpp @@ -49,7 +49,9 @@ SpotifyAccountConfig::SpotifyAccountConfig( SpotifyAccount *account ) m_ui->loginButton->setDefault( true ); connect( m_ui->loginButton, SIGNAL( clicked( bool ) ), this, SLOT( doLogin() ) ); - connect( m_ui->loveSync, SIGNAL( toggled(bool) ), this, SLOT( showStarredPlaylist(bool) ) ); + connect( m_ui->loveSync, SIGNAL( toggled( bool ) ), this, SLOT( showStarredPlaylist( bool ) ) ); + connect( m_ui->persitentPrivacy, SIGNAL( toggled( bool ) ), this, SIGNAL( updatePrivacy( bool ) ) ); + connect( m_ui->usernameEdit, SIGNAL( textEdited( QString ) ), this, SLOT( resetLoginButton() ) ); connect( m_ui->passwordEdit, SIGNAL( textEdited( QString ) ), this, SLOT( resetLoginButton() ) ); connect( m_ui->selectAllCheckbox, SIGNAL( stateChanged( int ) ), this, SLOT( selectAllPlaylists() ) ); @@ -78,6 +80,7 @@ SpotifyAccountConfig::loadFromConfig() m_ui->streamingCheckbox->setChecked( m_account->credentials().value( "highQuality" ).toBool() ); m_ui->deleteOnUnsync->setChecked( m_account->deleteOnUnsync() ); m_ui->loveSync->setChecked( m_account->loveSync() ); + m_ui->persitentPrivacy->setChecked( m_account->persitentPrivacy() ); if ( m_account->loggedIn() ) { @@ -149,6 +152,13 @@ SpotifyAccountConfig::loveSync() const } +bool +SpotifyAccountConfig::persitentPrivacy() const +{ + return m_ui->persitentPrivacy->isChecked(); +} + + void SpotifyAccountConfig::setPlaylists( const QList& playlists ) { @@ -223,6 +233,7 @@ SpotifyAccountConfig::loginResponse( bool success, const QString& msg, const QSt } + void SpotifyAccountConfig::showStarredPlaylist( bool hide ) { diff --git a/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.h b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.h index 6224108bba..81aa36a640 100644 --- a/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.h +++ b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.h @@ -53,6 +53,7 @@ class SpotifyAccountConfig : public AccountConfigWidget bool highQuality() const; bool deleteOnUnsync() const; bool loveSync() const; + bool persitentPrivacy() const; void setPlaylists( const QList< SpotifyPlaylistInfo* >& playlists ); @@ -66,6 +67,7 @@ class SpotifyAccountConfig : public AccountConfigWidget signals: void login( const QString& username, const QString& pw ); void logout(); + void updatePrivacy( bool ); protected: void showEvent( QShowEvent* event ); @@ -74,7 +76,7 @@ private slots: void doLogin(); void resetLoginButton(); void selectAllPlaylists(); - void showStarredPlaylist(bool); + void showStarredPlaylist( bool ); private: void showLoggedIn(); diff --git a/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.ui b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.ui index 8eb3e55f5b..b269e2d44d 100644 --- a/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.ui +++ b/src/libtomahawk/accounts/spotify/SpotifyAccountConfig.ui @@ -6,8 +6,8 @@ 0 0 - 375 - 487 + 406 + 534 @@ -34,7 +34,7 @@ - :/data/images/spotify-logo.png + :/data/images/spotify-logo.png true @@ -178,10 +178,20 @@ + + + + Use this to force Spotify to never announce listening data to Social Networks + + + Always run in Private Mode + + + - + From d800323afe18ad65f1a2ddfeef69677f9bb50947 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 17:36:22 +0200 Subject: [PATCH 308/565] Do not try to access NULL-pointers instead search the whole hash for holes --- src/libtomahawk/sip/WeakPeerHash.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/sip/WeakPeerHash.cpp b/src/libtomahawk/sip/WeakPeerHash.cpp index 7f1dcae3a1..81b698fd8e 100644 --- a/src/libtomahawk/sip/WeakPeerHash.cpp +++ b/src/libtomahawk/sip/WeakPeerHash.cpp @@ -52,6 +52,20 @@ WeakPeerHash::hash() void WeakPeerHash::remove( QObject *value ) { - const QString key = value->property( WEAKPEERHASH_KEY ).toString(); - d_func()->hash.remove( key ); + if ( value ) + { + const QString key = value->property( WEAKPEERHASH_KEY ).toString(); + d_func()->hash.remove( key ); + } + else + { + // Scan for null-Pointers + foreach ( QString key, d_func()->hash.keys() ) + { + if ( d_func()->hash.value( key ).isNull() ) + { + d_func()->hash.remove( key ); + } + } + } } From b3dc65997ca6986419f3dde2cd4b1089bc47738e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 10 Jun 2013 17:36:35 +0200 Subject: [PATCH 309/565] Do not squash Peers with no nodeid --- src/tomahawk/DiagnosticsDialog.cpp | 61 ++++++++++++++++++------------ src/tomahawk/DiagnosticsDialog.h | 1 + 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index ead2446a39..df41ee28e6 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -169,6 +169,12 @@ DiagnosticsDialog::accountLog( Tomahawk::Accounts::Account* account ) QMap< QString, QList< Tomahawk::peerinfo_ptr > > nodes; foreach ( const Tomahawk::peerinfo_ptr& peerInfo, account->sipPlugin()->peersOnline() ) { + if ( peerInfo->nodeId().isEmpty() ) + { + QList< Tomahawk::peerinfo_ptr> infos; + infos.append( peerInfo ); + accountInfo.append( peerLog( peerInfo->nodeId(), infos ) ); + } if ( !nodes.contains( peerInfo->nodeId() ) ) { nodes[peerInfo->nodeId()] = QList< Tomahawk::peerinfo_ptr >(); @@ -177,32 +183,39 @@ DiagnosticsDialog::accountLog( Tomahawk::Accounts::Account* account ) } foreach ( const QString& nodeid, nodes.keys() ) { - accountInfo.append( QString( " " ) ); - QStringList peerIds; - foreach ( const Tomahawk::peerinfo_ptr& peerInfo, nodes.value( nodeid ) ) - { - peerIds << peerInfo->id(); - } - accountInfo.append( peerIds.join( QChar( ',' ) ) ); - accountInfo.append( QString( ": %1 @ ").arg( nodeid ) ); - QStringList sipInfos; - foreach ( const Tomahawk::peerinfo_ptr& peerInfo, nodes.value( nodeid ) ) - { - foreach ( SipInfo info, peerInfo->sipInfos() ) - { - if ( info.isValid() ) - sipInfos << QString( "[%1]:%2" ).arg( info.host() ).arg( info.port() ) ; - else - sipInfos << QString( "SipInfo invalid" ); - } - } - sipInfos.removeDuplicates(); - accountInfo.append( sipInfos.join( QChar( ';' ) ) ); - if ( ( ( nodes.value( nodeid ).first()->sipInfos().length() == 1 ) && ( !nodes.value( nodeid ).first()->sipInfos().first().isVisible() ) ) || ( nodes.value( nodeid ).first()->sipInfos().isEmpty() ) ) - accountInfo.append( "(outbound connections only) "); - accountInfo.append( QString( " (%1)\n" ).arg( nodes.value( nodeid ).first()->versionString() ) ); + accountInfo.append( peerLog( nodeid, nodes.value( nodeid ) ) ); } accountInfo.append( "\n" ); return accountInfo; } + +QString +DiagnosticsDialog::peerLog( const QString& nodeid, const QList &peerInfos ) +{ + QString peerLine( " " ); + QStringList peerIds; + foreach ( const Tomahawk::peerinfo_ptr& peerInfo, peerInfos ) + { + peerIds << peerInfo->id(); + } + peerLine.append( peerIds.join( QChar( ',' ) ) ); + peerLine.append( QString( ": %1 @ ").arg( nodeid ) ); + QStringList sipInfos; + foreach ( const Tomahawk::peerinfo_ptr& peerInfo, peerInfos ) + { + foreach ( SipInfo info, peerInfo->sipInfos() ) + { + if ( info.isValid() ) + sipInfos << QString( "[%1]:%2" ).arg( info.host() ).arg( info.port() ) ; + else + sipInfos << QString( "SipInfo invalid" ); + } + } + sipInfos.removeDuplicates(); + peerLine.append( sipInfos.join( QChar( ';' ) ) ); + if ( ( ( peerInfos.first()->sipInfos().length() == 1 ) && ( !peerInfos.first()->sipInfos().first().isVisible() ) ) || ( peerInfos.first()->sipInfos().isEmpty() ) ) + peerLine.append( "(outbound connections only) "); + peerLine.append( QString( " (%1)\n" ).arg( peerInfos.first()->versionString() ) ); + return peerLine; +} diff --git a/src/tomahawk/DiagnosticsDialog.h b/src/tomahawk/DiagnosticsDialog.h index 4fa29dd6ea..b881653135 100644 --- a/src/tomahawk/DiagnosticsDialog.h +++ b/src/tomahawk/DiagnosticsDialog.h @@ -51,6 +51,7 @@ private slots: private: Ui::DiagnosticsDialog* ui; + QString peerLog( const QString& nodeid, const QList& peerInfos ); }; #endif // DIAGNOSTICSDIALOG_H From ddb66a5cfe78d20615e8d2a591c2ce6ea4c72957 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Tue, 11 Jun 2013 02:17:08 +0200 Subject: [PATCH 310/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 84 ++++++++++++---------- lang/tomahawk_bg.ts | 84 ++++++++++++---------- lang/tomahawk_bn_IN.ts | 84 ++++++++++++---------- lang/tomahawk_ca.ts | 84 ++++++++++++---------- lang/tomahawk_ca@valencia.ts | 84 ++++++++++++---------- lang/tomahawk_cs.ts | 84 ++++++++++++---------- lang/tomahawk_da.ts | 84 ++++++++++++---------- lang/tomahawk_de.ts | 84 ++++++++++++---------- lang/tomahawk_el.ts | 84 ++++++++++++---------- lang/tomahawk_en.ts | 84 ++++++++++++---------- lang/tomahawk_es.ts | 84 ++++++++++++---------- lang/tomahawk_fi.ts | 84 ++++++++++++---------- lang/tomahawk_fr.ts | 84 ++++++++++++---------- lang/tomahawk_gl.ts | 84 ++++++++++++---------- lang/tomahawk_hi_IN.ts | 84 ++++++++++++---------- lang/tomahawk_hu.ts | 84 ++++++++++++---------- lang/tomahawk_id.ts | 84 ++++++++++++---------- lang/tomahawk_it.ts | 84 ++++++++++++---------- lang/tomahawk_ja.ts | 84 ++++++++++++---------- lang/tomahawk_lt.ts | 84 ++++++++++++---------- lang/tomahawk_pl.ts | 84 ++++++++++++---------- lang/tomahawk_pt_BR.ts | 84 ++++++++++++---------- lang/tomahawk_ru.ts | 84 ++++++++++++---------- lang/tomahawk_sv.ts | 130 +++++++++++++++++++---------------- lang/tomahawk_tr.ts | 84 ++++++++++++---------- lang/tomahawk_zh_CN.ts | 84 ++++++++++++---------- lang/tomahawk_zh_TW.ts | 84 ++++++++++++---------- 27 files changed, 1292 insertions(+), 1022 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 1f1fa9a8c7..fea389f826 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1279,12 +1279,12 @@ Password PlaylistItemDelegate - + played %1 by you سمعت %1 - + played %1 by %2 %2 سمع %1 @@ -1292,31 +1292,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you سمعت %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 سمع %1 - + added %1 e.g. added 3 hours ago أضيفت %1 - + by <b>%1</b> e.g. by SomeArtist للفنان <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum للفنان <b>%1</b> في البوم <b>%2</b> @@ -2231,6 +2231,16 @@ Password High Quality Streams جودة عالية في الأغاني المحملة + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2502,47 +2512,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify مزامنة مع سبوتيفي (Spotify) - + Re-enable syncing with Spotify إعادة تمكين المزامنة مع سبوتيفي (Spotify) - + Create local copy إنشاء نسخة محلية - + Subscribe to playlist changes الاشتراك في تغييرات قائمة الأغاني - + Re-enable playlist subscription إعادة تمكين الإشتراك في تغييرات قائمة الأغاني - + Stop subscribing to changes توقيف الإشتراك في التغييرات - + Enable Spotify collaborations تمكين التعاون الخاص بسبوتيفي (Collaborations Spotify) - + Disable Spotify collaborations تعطيل التعاون الخاص بسبوتيفي (Collaborations Spotify) - + Stop syncing with Spotify أوقف المزامنة مع سبوتيفي (Spotify) @@ -2550,28 +2560,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... جاري تسجيل الدخول... - + Failed: %1 فشل: %1 - + Logged in as %1 مسجل تحت اسم %1 - + Log Out تسجيل الخروج - - + + Log In تسجيل الدخول @@ -3587,43 +3597,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل @@ -3960,27 +3970,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). لقد استمعت إلى هذه الأغنية %n مرة.لقد استمعت إلى هذه الأغنية مرة %n.لقد استمعت إلى هذه الأغنية مرتين %n.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات. - + You've never listened to this track before. لم تستمع لهذه الأغنية من قبل. - + You first listened to it on %1. استمعت إليها أولاً في %1. - + You've listened to %1 %n time(s). لقد استمعت إلى %1 %n مرة.لقد استمعت إلى %1 مرة %n.لقد استمعت إلى %1 مرتين %n.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات. - + You've never listened to %1 before. لم تستمع إلى %1 من قبل. @@ -3988,7 +3998,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. عذراً، ترشيحك "%1" لم يطابق أي نتائج. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 007e4c0318..0c2778fd2e 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1285,12 +1285,12 @@ Password PlaylistItemDelegate - + played %1 by you изпълнена %1 от мен - + played %1 by %2 изпълнена %1 от %2 @@ -1298,31 +1298,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you Изпълнена %1 от теб - + played %1 by %2 e.g. played 3 hours ago by SomeSource Изпълнена %1 от %2 - + added %1 e.g. added 3 hours ago Добавена %1 - + by <b>%1</b> e.g. by SomeArtist от <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum от <b>%1</b> на <b>%2</b> @@ -2243,6 +2243,16 @@ Password High Quality Streams Високо качество + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2517,47 +2527,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Синхронизирай с Spotify - + Re-enable syncing with Spotify Включи отново синхронизирането с Spotify - + Create local copy Създай локално копие - + Subscribe to playlist changes Получавай актуализации за промяната на списъка - + Re-enable playlist subscription Поднови получаването на промените на списъка - + Stop subscribing to changes Спри получаването на промени в списъка - + Enable Spotify collaborations Активирай връзката с Spotify - + Disable Spotify collaborations Деактивирай връзката с Spotify - + Stop syncing with Spotify Спри синхронизацията с Spotify @@ -2565,28 +2575,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Влизам... - + Failed: %1 Неуспех: %1 - + Logged in as %1 Регистриран като %1 - + Log Out Изход - - + + Log In Влез @@ -3602,43 +3612,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 песни) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия @@ -3977,27 +3987,27 @@ enter the displayed PIN number here: # В ТВОИТЕ КЛАСАЦИИ - + You've listened to this track %n time(s). Ти си слушал тази песен %n път(и)Ти си слушал тази песен %n път(и) - + You've never listened to this track before. Никога не си слушал тази песен преди - + You first listened to it on %1. Първоначално си я слушал на %1 - + You've listened to %1 %n time(s). Слушал си %1 път(и)Слушал си %1 %n път(и) - + You've never listened to %1 before. Никога не си слушал %1 преди @@ -4005,7 +4015,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Съжалявам, твоят филтър %1 не върна никакъв резултат. diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 844ee4237f..6f8a400eee 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2227,6 +2227,16 @@ Password High Quality Streams + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2498,47 +2508,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + Re-enable syncing with Spotify - + Create local copy - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify @@ -2546,28 +2556,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Failed: %1 - + Logged in as %1 - + Log Out - - + + Log In @@ -3573,43 +3583,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3945,27 +3955,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3973,7 +3983,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 881419ae7f..6ef45b9570 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you reproduït %1 - + played %1 by %2 reproduït %1 per %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduït %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduït %1 per %2 - + added %1 e.g. added 3 hours ago afegeix %1 - + by <b>%1</b> e.g. by SomeArtist per <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum per <b>%1</b> el <b>%2</b> @@ -2231,6 +2231,16 @@ i emissores basades en els vostres gusts musicals. High Quality Streams Fluxos d'Alta Qualitat + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2502,47 +2512,47 @@ i emissores basades en els vostres gusts musicals. Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sincronitza amb Spotify - + Re-enable syncing with Spotify Torna a habilitar la sincronització amb l'Spotify - + Create local copy Crea una còpia local - + Subscribe to playlist changes Subscriu-me als canvis de la llista de reproducció - + Re-enable playlist subscription Torna a habilitar la subscripció a la llista de reproducció - + Stop subscribing to changes Atura la subscripció als canvis - + Enable Spotify collaborations Habilita les col·laboracions amb l'Spotify - + Disable Spotify collaborations Inhabilita les col·laboracions amb l'Spotify - + Stop syncing with Spotify Atura la sincronització amb Spotify @@ -2550,28 +2560,28 @@ i emissores basades en els vostres gusts musicals. Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Iniciant sessió... - + Failed: %1 Error: %1 - + Logged in as %1 Connectat com a %1 - + Log Out Tanca la sessió - - + + Log In Incia Sessió @@ -3587,43 +3597,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia @@ -3960,27 +3970,27 @@ introduïu el PIN aquí: - + You've listened to this track %n time(s). Heu escoltat aquesta cançó %n cop.Heu escoltat aquesta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai aquesta cançó abans. - + You first listened to it on %1. Vau escoltar aquesta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. @@ -3988,7 +3998,7 @@ introduïu el PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 91259e1c77..e975fe6609 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you reproduït %1 - + played %1 by %2 reproduït %1 per %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduït %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduït %1 per %2 - + added %1 e.g. added 3 hours ago afig %1 - + by <b>%1</b> e.g. by SomeArtist per <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum per <b>%1</b> el <b>%2</b> @@ -2231,6 +2231,16 @@ i emissores basades en els vostres gusts musicals. High Quality Streams Fluxos d'Alta Qualitat + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2502,47 +2512,47 @@ i emissores basades en els vostres gusts musicals. Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sincronitza amb Spotify - + Re-enable syncing with Spotify Torna a habilitar la sincronització amb l'Spotify - + Create local copy Crea una còpia local - + Subscribe to playlist changes Subscriu-me als canvis de la llista de reproducció - + Re-enable playlist subscription Torna a habilitar la subscripció a la llista de reproducció - + Stop subscribing to changes Atura la subscripció als canvis - + Enable Spotify collaborations Habilita les col·laboracions amb l'Spotify - + Disable Spotify collaborations Inhabilita les col·laboracions amb l'Spotify - + Stop syncing with Spotify Atura la sincronització amb Spotify @@ -2550,28 +2560,28 @@ i emissores basades en els vostres gusts musicals. Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Iniciant sessió... - + Failed: %1 Error: %1 - + Logged in as %1 Connectat com a %1 - + Log Out Tanca la sessió - - + + Log In Incia Sessió @@ -3587,43 +3597,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia @@ -3960,27 +3970,27 @@ introduïu el PIN ací: - + You've listened to this track %n time(s). Heu escoltat esta cançó %n cop.Heu escoltat esta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai esta cançó abans. - + You first listened to it on %1. Vau escoltar esta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. @@ -3988,7 +3998,7 @@ introduïu el PIN ací: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 3bbc7d427c..9b01f0e18e 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -1279,12 +1279,12 @@ heslo PlaylistItemDelegate - + played %1 by you vyslechnuto %1 vámi - + played %1 by %2 vyslechnuto %1 %2 @@ -1292,31 +1292,31 @@ heslo PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you vyslechnuto %1 vámi - + played %1 by %2 e.g. played 3 hours ago by SomeSource vyslechnuto %1 %2 - + added %1 e.g. added 3 hours ago přidáno %1 - + by <b>%1</b> e.g. by SomeArtist od <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum od <b>%1</b> na <b>%2</b> @@ -2230,6 +2230,16 @@ heslo High Quality Streams Vysoce kvalitní proudy + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2501,47 +2511,47 @@ heslo Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Seřídit se Spotify - + Re-enable syncing with Spotify Zapnout znovu seřizování se Spotify - + Create local copy Vytvořit místní kopii - + Subscribe to playlist changes Odebírat změny seznamu skladeb - + Re-enable playlist subscription Zapnout znovu odběr seznamu skladeb - + Stop subscribing to changes Zastavit odběr změn - + Enable Spotify collaborations Zapnout spolupráci se Spotify - + Disable Spotify collaborations Vypnout spolupráci se Spotify - + Stop syncing with Spotify Zastavit seřizování se Spotify @@ -2549,28 +2559,28 @@ heslo Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Přihlašuje se... - + Failed: %1 Nepodařilo se: %1 - + Logged in as %1 Přihlášen jako %1 - + Log Out Odhlásit se - - + + Log In Přihlásit se @@ -3587,43 +3597,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený @@ -3960,27 +3970,27 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: # VE VAŠICH ŽEBŘÍČCÍCH - + You've listened to this track %n time(s). Tuto píseň jste si poslechl jednou.Tuto píseň jste si poslechl %n krát.Tuto píseň jste si poslechl %n krát. - + You've never listened to this track before. Tuto píseň jste si ještě nikdy neposlechl. - + You first listened to it on %1. Tuto píseň jste si poprvé poslechl %1. - + You've listened to %1 %n time(s). %1 jste si poslechl jednou.%1 jste si poslechl %n krát.%1 jste si poslechl %n krát. - + You've never listened to %1 before. %1 jste si předtím ještě nikdy neposlechl. @@ -3988,7 +3998,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackView - + Sorry, your filter '%1' did not match any results. Promiňte, vašemu filtru '%1' se nepodařilo najít žádné výsledky. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index baa956d3a3..7df085fdaf 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you afspillede %1 af dig - + played %1 by %2 afspillede %1 af %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2228,6 +2228,16 @@ Password High Quality Streams + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2499,47 +2509,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + Re-enable syncing with Spotify - + Create local copy - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify Stop med at synkronisere med Spotify @@ -2547,28 +2557,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Logger ind... - + Failed: %1 Fejlede: %1 - + Logged in as %1 - + Log Out - - + + Log In Log ind @@ -3575,43 +3585,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline @@ -3947,27 +3957,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3975,7 +3985,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Beklager, din filter '%1' matchede ikke nogle resultater diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 091e72416e..ba8b63282c 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1279,12 +1279,12 @@ Passwort PlaylistItemDelegate - + played %1 by you angehört %1 von dir - + played %1 by %2 angehört %1 von %2 @@ -1292,31 +1292,31 @@ Passwort PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you angehört %1 von dir - + played %1 by %2 e.g. played 3 hours ago by SomeSource angehört %1 von %2 - + added %1 e.g. added 3 hours ago hinzugefügt %1 - + by <b>%1</b> e.g. by SomeArtist von <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum von <b>%1</b> auf <b>%2</b> @@ -2230,6 +2230,16 @@ Passwort High Quality Streams Hohe Stream Qualität + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2501,47 +2511,47 @@ Passwort Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Mit Spotify synchronisieren - + Re-enable syncing with Spotify Synchronisierung mit Spotify re-aktivieren - + Create local copy Lokale Kopie erstellen - + Subscribe to playlist changes Playlist Änderungen abonnieren - + Re-enable playlist subscription Playlist Abonnement re-aktivieren - + Stop subscribing to changes Abonnement stoppen - + Enable Spotify collaborations Spotify Kollaboration aktivieren - + Disable Spotify collaborations Spotify Kollaboration deaktivieren - + Stop syncing with Spotify Synchronisation beenden @@ -2549,28 +2559,28 @@ Passwort Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Anmelden... - + Failed: %1 Fehler: %1 - + Logged in as %1 Angemeldet als %1 - + Log Out Abmelden - - + + Log In Anmelden @@ -3582,43 +3592,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline @@ -3955,27 +3965,27 @@ Tomahawk auf Twitter's Website authentifiziert hast: # IN DEINEN CHARTS - + You've listened to this track %n time(s). Du hast dieses Lied einmal gehört.Du hast dieses Lied %n mal gehört. - + You've never listened to this track before. Du hast dieses Lied noch nie angehört. - + You first listened to it on %1. Du hast dieses Lied zum ersten mal am %1 gehört. - + You've listened to %1 %n time(s). Du hast %1 einmal angehört.Du hast %1 %n mal angehört. - + You've never listened to %1 before. Du hast %1 vorher noch nie gehört. @@ -3983,7 +3993,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackView - + Sorry, your filter '%1' did not match any results. Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index f21041d453..913b72aa5a 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1278,12 +1278,12 @@ Password PlaylistItemDelegate - + played %1 by you παίχθηκε %1 από εσάς - + played %1 by %2 παίχθηκε %1 από τον/την %2 @@ -1291,31 +1291,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you αναπαράχθηκε το %1 από εσάς - + played %1 by %2 e.g. played 3 hours ago by SomeSource αναπαράχθηκε το%1 από %2 - + added %1 e.g. added 3 hours ago προστέθηκε %1 - + by <b>%1</b> e.g. by SomeArtist από <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum από <b>%1</b> σε <b>%2</b> @@ -2230,6 +2230,16 @@ Password High Quality Streams Streams υψηλης ποιοτητας + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2502,47 +2512,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Συγχρονισμός με Spotify - + Re-enable syncing with Spotify Επαναλειτουργια συγχρονισμου με το Spotify - + Create local copy Δημιουργία τοπικού αντιγράφου - + Subscribe to playlist changes Εγγραφή σε μεταβολές λίστας αναπαραγωγής - + Re-enable playlist subscription Επανενεργοποίηση εγγραφής στην λίστα αναπαραγωγής - + Stop subscribing to changes Διακοπή εγγραφής σε μεταβολές - + Enable Spotify collaborations Ενεργοποιηση των Spotify συνεργασειων - + Disable Spotify collaborations Απενεργοποιηση των Spotify συνεργασειων - + Stop syncing with Spotify Διακοπή συγχρονισμού με Spotify @@ -2550,28 +2560,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Σύνδεση... - + Failed: %1 Αποτυχία: %1 - + Logged in as %1 Σύνδεση ως %1 - + Log Out Αποσύνδεση - - + + Log In Σύνδεση @@ -3588,43 +3598,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης @@ -3960,27 +3970,27 @@ enter the displayed PIN number here: # ΣΤΑ ΔΙΚΑ ΣΑΣ CHARTS - + You've listened to this track %n time(s). Έχετε ακούσει το κομμάτι %n φορά.Έχετε ακούσει το κομμάτι %n φορές. - + You've never listened to this track before. Δεν έχετε ακούσει αυτό το κομμάτι παλιότερα. - + You first listened to it on %1. Το ακούσατε για πρώτη φορά στις %1. - + You've listened to %1 %n time(s). Ακούσατε το %1 %n φορά.Ακούσατε το %1 %n φορές. - + You've never listened to %1 before. Δεν έχετε ακούσει το %1 ποτέ πριν. @@ -3988,7 +3998,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Συγγνώμη, το φίλτρο «%1» δεν αντιστοίχισε αποτελέσματα. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 66ad23670c..b8ad95dffb 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1279,12 +1279,12 @@ Password PlaylistItemDelegate - + played %1 by you played %1 by you - + played %1 by %2 played %1 by %2 @@ -1292,31 +1292,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you played %1 by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource played %1 by %2 - + added %1 e.g. added 3 hours ago added %1 - + by <b>%1</b> e.g. by SomeArtist by <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum by <b>%1</b> on <b>%2</b> @@ -2233,6 +2233,16 @@ Password High Quality Streams High Quality Streams + + + Use this to force Spotify to never announce listening data to Social Networks + Use this to force Spotify to never announce listening data to Social Networks + + + + Always run in Private Mode + Always run in Private Mode + Spotify playlists to keep in sync: @@ -2504,47 +2514,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sync with Spotify - + Re-enable syncing with Spotify Re-enable syncing with Spotify - + Create local copy Create local copy - + Subscribe to playlist changes Subscribe to playlist changes - + Re-enable playlist subscription Re-enable playlist subscription - + Stop subscribing to changes Stop subscribing to changes - + Enable Spotify collaborations Enable Spotify collaborations - + Disable Spotify collaborations Disable Spotify collaborations - + Stop syncing with Spotify Stop syncing with Spotify @@ -2552,28 +2562,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Logging in... - + Failed: %1 Failed: %1 - + Logged in as %1 Logged in as %1 - + Log Out Log Out - - + + Log In Log In @@ -3590,43 +3600,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline @@ -3963,27 +3973,27 @@ enter the displayed PIN number here: # IN YOUR CHARTS - + You've listened to this track %n time(s). You've listened to this track %n time.You've listened to this track %n times. - + You've never listened to this track before. You've never listened to this track before. - + You first listened to it on %1. You first listened to it on %1. - + You've listened to %1 %n time(s). You've listened to %1 %n time.You've listened to %1 %n times. - + You've never listened to %1 before. You've never listened to %1 before. @@ -3991,7 +4001,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index f5dcddd618..c6c3216d36 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1278,12 +1278,12 @@ Password PlaylistItemDelegate - + played %1 by you %1 reproducido por usted - + played %1 by %2 %1 reproducido por %2 @@ -1291,31 +1291,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproducido %1 por usted - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproducido %1 de %2 - + added %1 e.g. added 3 hours ago añadido %1 - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum por <b>%1</b> en <b>%2</b> @@ -2232,6 +2232,16 @@ y estaciones basadas en sus gustos personales. High Quality Streams Streams de alta calidad + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2503,47 +2513,47 @@ y estaciones basadas en sus gustos personales. Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sincronizar con Spotify - + Re-enable syncing with Spotify Volver a activar la sincronización con Spotify - + Create local copy Crear copia local - + Subscribe to playlist changes Suscribirse a la lista de reproducción - + Re-enable playlist subscription Activar suscripción a la lista de reproducción - + Stop subscribing to changes Dejar de suscribirse - + Enable Spotify collaborations Activar colaboraciones de Spotify - + Disable Spotify collaborations Desactivar colaboraciones de Spotify - + Stop syncing with Spotify Detener la sincronización con Spotify @@ -2551,28 +2561,28 @@ y estaciones basadas en sus gustos personales. Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Iniciando sesión… - + Failed: %1 Fallo: %1 - + Logged in as %1 Sesión iniciada como %1 - + Log Out Salir - - + + Log In Iniciar sesión @@ -3588,43 +3598,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado @@ -3961,27 +3971,27 @@ introduzca su número PIN aquí: - + You've listened to this track %n time(s). Ha escuchado esta pista %n vez.Ha escuchado esta pista %n veces. - + You've never listened to this track before. Nunca ha escuchado esta pista antes. - + You first listened to it on %1. Escuchó esta pista por primera vez en %1. - + You've listened to %1 %n time(s). Ha escuchado %1 una vez.Ha escuchado %1 %n veces. - + You've never listened to %1 before. Nunca ha escuchado %1 antes. @@ -3989,7 +3999,7 @@ introduzca su número PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. Lo siento, tu filtro '%1' no ha encontrado resultados. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 3362534845..cd41f9351c 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1279,12 +1279,12 @@ salasana PlaylistItemDelegate - + played %1 by you kuuntelit %1 - + played %1 by %2 %2 kuunteli %1 @@ -1292,31 +1292,31 @@ salasana PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you kuuntelit %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 kuunteli %1 - + added %1 e.g. added 3 hours ago lisätty %1 - + by <b>%1</b> e.g. by SomeArtist artistilta <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum artistilta <b>%1</b> albumilla <b>%2</b> @@ -2236,6 +2236,16 @@ napsauttamalla hiiren oikealla. High Quality Streams Laadukkaat virrat + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2507,47 +2517,47 @@ napsauttamalla hiiren oikealla. Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Synkronoi Spotifyn kanssa - + Re-enable syncing with Spotify Ota Spotifyn kanssa synkronointi käyttöön - + Create local copy Luo paikallinen kopio - + Subscribe to playlist changes Tilaa soittolistojen muutokset - + Re-enable playlist subscription Tilaa soittolistojen muutokset uudelleen - + Stop subscribing to changes Lopeta muutosten tilaus - + Enable Spotify collaborations Käytä Spotify-yhteistöitä - + Disable Spotify collaborations Poista Spotify-yhteistyöt käytöstä - + Stop syncing with Spotify Lopeta Spotifyn kanssa synkronointi @@ -2555,28 +2565,28 @@ napsauttamalla hiiren oikealla. Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Kirjaudutaan... - + Failed: %1 Epäonnistui: %1 - + Logged in as %1 Kirjauduttu käyttäjänä %1 - + Log Out Kirjaudu ulos - - + + Log In Kirjaudu @@ -3593,43 +3603,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa @@ -3966,27 +3976,27 @@ anna siellä näytetty PIN-koodi tähän: - + You've listened to this track %n time(s). Olet kuunnellut tätä kappaletta %n kerran.Olet kuunnellut tätä kappaletta %n kertaa. - + You've never listened to this track before. Et ole kuunnellut tätä kappaletta aiemmin. - + You first listened to it on %1. Kuuntelit sitä ensi kerran %1. - + You've listened to %1 %n time(s). Olet kuunnellut artistia %1 %n kerran.Olet kuunnellut artistia %1 %n kertaa. - + You've never listened to %1 before. Et ole kuunnellut artistia %1 aiemmin. @@ -3994,7 +4004,7 @@ anna siellä näytetty PIN-koodi tähän: TrackView - + Sorry, your filter '%1' did not match any results. Valitettavasti suodattimesi ”%1” ei tuottanut yhtään tuloksia. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 0465c2d375..b80edab2bc 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1278,12 +1278,12 @@ Password PlaylistItemDelegate - + played %1 by you joué %1 par vous - + played %1 by %2 joué %1 par %2 @@ -1291,31 +1291,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you joué %1 par vous - + played %1 by %2 e.g. played 3 hours ago by SomeSource joué %1 par %2 - + added %1 e.g. added 3 hours ago ajouté %1 - + by <b>%1</b> e.g. by SomeArtist par <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum par <b>%1</b> sur <b>%2</b> @@ -2229,6 +2229,16 @@ Password High Quality Streams Streaming haute qualité + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2500,47 +2510,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Synchroniser avec Spotify - + Re-enable syncing with Spotify Réactiver la synchronisation avec Spotify - + Create local copy Créer une copie localement - + Subscribe to playlist changes S'abonner aux modifications de la liste de lecture - + Re-enable playlist subscription Réactiver l'abonnement à la liste de lecture - + Stop subscribing to changes Stopper l'abonnement aux modifications - + Enable Spotify collaborations Activer les collaborations Spotify - + Disable Spotify collaborations Désactiver les collaborations Spotify - + Stop syncing with Spotify Stopper la synchronisation avec Spotify @@ -2548,28 +2558,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Connexion... - + Failed: %1 Echec : %1 - + Logged in as %1 Connecté sous %1 - + Log Out Déconnectez-vous - - + + Log In Connexion @@ -3585,43 +3595,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne @@ -3958,27 +3968,27 @@ saisissez le numéro PIN ici : - + You've listened to this track %n time(s). Vous avez écouté cette piste %n fois.Vous avez écouté cette piste %n fois. - + You've never listened to this track before. Vous n'avez encore jamais écouté cette piste. - + You first listened to it on %1. Vous l'avez écouté pour la première fois le %1. - + You've listened to %1 %n time(s). Vous avez écouté %1 %n fois.Vous avez écouté %1 %n fois. - + You've never listened to %1 before. Vous n'avez encore jamais écouté %1. @@ -3986,7 +3996,7 @@ saisissez le numéro PIN ici : TrackView - + Sorry, your filter '%1' did not match any results. Désolé, votre filtre '%1' ne correspond à aucun résultat. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 06f95febcb..f439aa2da0 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you escoitou %1 por coñecela por ti - + played %1 by %2 escoitou %1 por coñecela por %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduciu %3 horas atrás por ti - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduciu % por %2 - + added %1 e.g. added 3 hours ago engadiu %1 - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum by <b>%1</b> on <b>%2</b> @@ -2230,6 +2230,16 @@ Password High Quality Streams Transmisión de alta calidade + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2501,47 +2511,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sincronización con Spotify - + Re-enable syncing with Spotify Reactivar a sincronización con Spotify - + Create local copy Crear unha copia local - + Subscribe to playlist changes Subscribirse a cambios la lista de reprodución - + Re-enable playlist subscription Reactivar subscrición á lista de reprodución - + Stop subscribing to changes Parar de subscribirse a cambios - + Enable Spotify collaborations Activar as colaboracións con Spotify - + Disable Spotify collaborations Desactivar as colaboracións con Spotify - + Stop syncing with Spotify Deter a sincronización con Spotify @@ -2549,28 +2559,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Rexistrándose... - + Failed: %1 Fallou: %1 - + Logged in as %1 Accedeuse como %1 - + Log Out Saír - - + + Log In Iniciar sesión @@ -3587,43 +3597,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado @@ -3960,27 +3970,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Escoitaches esta pista %n vece(s).Escoitaches esta pista %n vece(s). - + You've never listened to this track before. Nunca antes escoitaras esta pista. - + You first listened to it on %1. Escoitaches esta pista por primeira vez en %1. - + You've listened to %1 %n time(s). Escoitaches %1 %n veces.Escoitaches %1 %n veces. - + You've never listened to %1 before. Nunca antes escoitaras a %1. @@ -3988,7 +3998,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. O filtro «%1» non dá ningún resultado. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index d6bcda2b62..e303aeb13d 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2227,6 +2227,16 @@ Password High Quality Streams + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2498,47 +2508,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + Re-enable syncing with Spotify - + Create local copy - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify @@ -2546,28 +2556,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Failed: %1 - + Logged in as %1 - + Log Out - - + + Log In @@ -3573,43 +3583,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3945,27 +3955,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3973,7 +3983,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index a9ae4a5475..da32acf95d 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2227,6 +2227,16 @@ Password High Quality Streams Magas minőségű streamek + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2498,47 +2508,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Szinkronizálás a Spotify-val - + Re-enable syncing with Spotify - + Create local copy Helyi másolat létrehozása - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify @@ -2546,28 +2556,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Belépés... - + Failed: %1 - + Logged in as %1 Bejelentkezve mint %1 - + Log Out Kijelentkezés - - + + Log In Belépés @@ -3573,43 +3583,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető @@ -3945,27 +3955,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3973,7 +3983,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index d9afdc001b..3d8a75c981 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2227,6 +2227,16 @@ Password High Quality Streams + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2498,47 +2508,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + Re-enable syncing with Spotify - + Create local copy - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify @@ -2546,28 +2556,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Failed: %1 - + Logged in as %1 - + Log Out - - + + Log In @@ -3573,43 +3583,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3945,27 +3955,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3973,7 +3983,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 652601230b..ea2be3f20b 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1278,12 +1278,12 @@ temporanea PlaylistItemDelegate - + played %1 by you ascoltata %1 da te - + played %1 by %2 ascoltata %1 da %2 @@ -1291,31 +1291,31 @@ temporanea PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you riprodotta %1 da te - + played %1 by %2 e.g. played 3 hours ago by SomeSource riprotta %1 da %2 - + added %1 e.g. added 3 hours ago aggiunta %1 - + by <b>%1</b> e.g. by SomeArtist da <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum da <b>%1</b> su <b>%2</b> @@ -2228,6 +2228,16 @@ temporanea High Quality Streams Stream di alta qualità + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2499,47 +2509,47 @@ temporanea Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sincronizzati con Spotify - + Re-enable syncing with Spotify Riabilita sincronizzazione con Spotify - + Create local copy Crea copia locale - + Subscribe to playlist changes Sottoscriviti ai cambiamenti della playlist - + Re-enable playlist subscription Riabilita sottoscrizione alla playlist - + Stop subscribing to changes Blocca sottoscrizione ai cambiamenti - + Enable Spotify collaborations Abilita collaborazione Spotify - + Disable Spotify collaborations Disabilita collaborazione Spotify - + Stop syncing with Spotify Ferma la sincronizzazione con Spotify @@ -2547,28 +2557,28 @@ temporanea Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Accedendo... - + Failed: %1 Fallito: %1 - + Logged in as %1 Collegato come %1 - + Log Out Logout - - + + Log In Login @@ -3575,43 +3585,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso @@ -3947,27 +3957,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Hai ascoltato questa traccia una volta.Hai ascoltato questa traccia %n volte. - + You've never listened to this track before. Non hai mai ascoltato questa traccia. - + You first listened to it on %1. L'hai ascoltata la prima volta su %1. - + You've listened to %1 %n time(s). Hai ascoltato %1 una volta.Hai ascoltato %1 %n volte. - + You've never listened to %1 before. Non hai mai ascoltato %1. @@ -3975,7 +3985,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Spiacente, il tuo filtro %1 non ha trovato nessun risultato. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 638b7288c5..f6ce31fb62 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1278,12 +1278,12 @@ Password PlaylistItemDelegate - + played %1 by you %1を再生しました。 - + played %1 by %2 %2が%1を再生しました。 @@ -1291,31 +1291,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you %1を再生しました。 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2が%1を再生しました。 - + added %1 e.g. added 3 hours ago %1を追加しました - + by <b>%1</b> e.g. by SomeArtist <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum <b>%1</b>の<b>%2</b> @@ -2232,6 +2232,16 @@ Password High Quality Streams 高音質ストリーム + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2503,47 +2513,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Spotifyと同期する - + Re-enable syncing with Spotify Spotifyとの同期を再び有効にする - + Create local copy ローカルのコピーを作成 - + Subscribe to playlist changes プレイリストの変更フィードに登録する - + Re-enable playlist subscription 再びプレイリストのフィードに登録する - + Stop subscribing to changes 変更フィードの登録を解除 - + Enable Spotify collaborations Spotifyのコラボレーションを有効にする - + Disable Spotify collaborations Spotifyのコラボレーションを無効にする - + Stop syncing with Spotify Spotifyとの同期を解除 @@ -2551,28 +2561,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... ログイン中... - + Failed: %1 失敗しました: %1 - + Logged in as %1 %1としてログイン済み - + Log Out ログアウト - - + + Log In ログイン @@ -3585,43 +3595,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン @@ -3958,27 +3968,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). このトラックは%n回聴いています。 - + You've never listened to this track before. このトラックを一度も聴いていません。 - + You first listened to it on %1. 初めてこの曲を聴いたのは、%1です。 - + You've listened to %1 %n time(s). %1を%n回聴いています。 - + You've never listened to %1 before. %1を一度も聴いていません。 @@ -3986,7 +3996,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. %1に一致する結果は見つかりませんでした。 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 5d6c59acaf..950e28bea8 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2227,6 +2227,16 @@ Password High Quality Streams Aukštos kokybės srautai + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2498,47 +2508,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sinchronizuoti su Spotify - + Re-enable syncing with Spotify Iš naujo įjungti sinchronizavimą su Spotify - + Create local copy Sukurti vietinę kopiją - + Subscribe to playlist changes Sekti grojaraščio pokyčius - + Re-enable playlist subscription Sekti grojarašio atnaujinimus iš naujo - + Stop subscribing to changes Nebesekti grojaraščio pokyčių - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify Stabdyti sinchronizavimą su Spotify @@ -2546,28 +2556,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Prisijungiama... - + Failed: %1 Nepavyko: %1 - + Logged in as %1 Prisijungta kaip %1 - + Log Out Atsijungti - - + + Log In Prisijungti @@ -3573,43 +3583,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs @@ -3945,27 +3955,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Jūs klausėtės šio takelio %n kartą.Jūs klausėtės šio takelio %n kartus.Jūs klausėtės šio takelio %n kartų. - + You've never listened to this track before. Jūs niekad anksčiau nesiklausėte šio takelio. - + You first listened to it on %1. Pirmąkart klausėtės jo %1. - + You've listened to %1 %n time(s). Jūs klausėtės %1 %n kartą.Jūs klausėtės %1 %n kartus.Jūs klausėtės %1 %n kartų. - + You've never listened to %1 before. Jūs niekada anksčiau nesiklausėte %1. @@ -3973,7 +3983,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index b9a1788481..77c18c33f7 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1278,12 +1278,12 @@ Password PlaylistItemDelegate - + played %1 by you odtworzone %1 przez ciebie - + played %1 by %2 odtworzone %1 przez %2 @@ -1291,31 +1291,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2229,6 +2229,16 @@ Password High Quality Streams Strumieniowanie w wysokiej jakości + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2500,47 +2510,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Synchronizuj ze Spotify - + Re-enable syncing with Spotify Włącz ponownie synchronizację ze Spotify - + Create local copy - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify Przestań synchronizować ze Spotify @@ -2548,28 +2558,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Loguję się... - + Failed: %1 Nie udało się: %1 - + Logged in as %1 - + Log Out - - + + Log In Zaloguj się @@ -3582,43 +3592,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline @@ -3955,27 +3965,27 @@ wprowadź pokazany numer PIN tutaj: - + You've listened to this track %n time(s). Słuchałeś tego utworu %n raz.Słuchałeś tego utworu %n razy.Słuchałeś tego utworu %n razy. - + You've never listened to this track before. Nie słuchałeś wcześniej tego utworu. - + You first listened to it on %1. Pierwszy raz słuchałeś tego utworu %1. - + You've listened to %1 %n time(s). Słuchałeś %1 %n raz.Słuchałeś %1 %n razy.Słuchałeś %1 %n razy. - + You've never listened to %1 before. Nie słuchałeś wcześniej %1. @@ -3983,7 +3993,7 @@ wprowadź pokazany numer PIN tutaj: TrackView - + Sorry, your filter '%1' did not match any results. Przepraszamy, twój filtr '%1' nie dopasował żadnych wyników. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 915550a686..6f9f22d487 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1278,12 +1278,12 @@ Password PlaylistItemDelegate - + played %1 by you tocou %1 por você - + played %1 by %2 tocou %1 por %2 @@ -1291,31 +1291,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum por <b>%1</b> em <b>%2</b> @@ -2229,6 +2229,16 @@ Password High Quality Streams Streams de Alta Qualidade + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2500,47 +2510,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Sincronizar com o Spotify - + Re-enable syncing with Spotify Reativar sincronização com Spotify - + Create local copy Criar cópia local - + Subscribe to playlist changes Assinar para alterações na lista de reprodução - + Re-enable playlist subscription Re-habilitar assinatura da lista de reprodução - + Stop subscribing to changes Parar a assinatura de alterações - + Enable Spotify collaborations Habilitar colaborações Spotify - + Disable Spotify collaborations Desabilitar colaborações Spotify - + Stop syncing with Spotify Parar a sincronização com o Spotify @@ -2548,28 +2558,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Logando... - + Failed: %1 Falha: %1 - + Logged in as %1 Conectado como %1 - + Log Out Sair - - + + Log In Log In @@ -3582,43 +3592,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline @@ -3955,27 +3965,27 @@ colocar o número PIN mostrado aqui: - + You've listened to this track %n time(s). Você ouviu esta faixa %n vez.Você ouviu esta faixa %n vezes. - + You've never listened to this track before. Você nunca ouviu esta faixa antes. - + You first listened to it on %1. Você ouviu pela primeira vez em %1. - + You've listened to %1 %n time(s). Você ouviu %1 %n vez.Você ouviu %1 %n vezes. - + You've never listened to %1 before. Você nunca ouviu %1 antes. @@ -3983,7 +3993,7 @@ colocar o número PIN mostrado aqui: TrackView - + Sorry, your filter '%1' did not match any results. Desculpe, o seu filtro '%1' não encontreou nenhum resultado. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 5c78778321..1856ffacd4 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1281,12 +1281,12 @@ Password PlaylistItemDelegate - + played %1 by you Воспроизводилась %1 мной - + played %1 by %2 Песня %1 воспроизводилась %2 @@ -1294,31 +1294,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you Воспроизводилась %1 вами - + played %1 by %2 e.g. played 3 hours ago by SomeSource Воспроизводилась %1 %2 - + added %1 e.g. added 3 hours ago Добавлена %1 - + by <b>%1</b> e.g. by SomeArtist <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum <b>%1</b> на <b>%2</b> @@ -2235,6 +2235,16 @@ Password High Quality Streams Поток высокого качества + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2506,47 +2516,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Синхронизировать с Spotify - + Re-enable syncing with Spotify Повторно включить синхронизацию с Spotify - + Create local copy Создать локальную копию - + Subscribe to playlist changes Следить за изменением плейлиста - + Re-enable playlist subscription Повторно включить подписку плейлиста - + Stop subscribing to changes Прекратить следить за изменением плейлиста - + Enable Spotify collaborations Включить сотрудничество с Spotify - + Disable Spotify collaborations Выключить сотрудничество с Spotify - + Stop syncing with Spotify Прекратить синхронизацию с Spotify @@ -2554,28 +2564,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Вхожу... - + Failed: %1 Ошибка: %1 - + Logged in as %1 Вошли как %1 - + Log Out Выйти - - + + Log In Войти @@ -3589,43 +3599,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети @@ -3961,27 +3971,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Вы слушали эту песню %n раз.Вы слушали эту песню %n раз.Вы слушали эту песню %n раз. - + You've never listened to this track before. Вы никогда не слушали эту песню раньше. - + You first listened to it on %1. Первый раз слушали эту песню %1. - + You've listened to %1 %n time(s). Вы слушали %1 %n раз.Вы слушали %1 %n раза.Вы слушали %1 %n раз. - + You've never listened to %1 before. Вы никогда не слушали %1 до этого. @@ -3989,7 +3999,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Ваш поиск '%1' недал результатов. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 526d7d353c..aee8c68317 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -340,7 +340,7 @@ ansluta och strömma från dig? # IN YOUR CHARTS - + # I DINA LISTOR @@ -518,32 +518,32 @@ ansluta och strömma från dig? Recently Played Tracks - + Nyligen uppspelade spår Recent Additions - + Nyligen tillagda Newest Stations & Playlists - + Nyaste Stationer och Spellistor Recently played tracks - + Nyligen uppspelade spår No recently created playlists in your network. - + Det finns inga nyligen skapade spellistor på ditt nätverk Welcome to Tomahawk - + Välkommen till Tomahawk @@ -769,32 +769,32 @@ Password From: - + Från: To: - + Till: Recently Played Tracks - + Nyligen uppspelade spår Your recently played tracks - + Dina nyligen uppspelade spår %1's recently played tracks - + %1's nyligen uppspelade spår Sorry, we could not find any recent plays! - + Tyvärr kunde vi inte hitta några nyligen uppspelningar @@ -1103,32 +1103,32 @@ Password Charts - + Listor Last Week - + Förra veckan Last Month - + Förra månaden Last Year - + Förra året Overall - + Generell Network Activity - + Nätverksaktivitet @@ -1279,12 +1279,12 @@ Password PlaylistItemDelegate - + played %1 by you %1 spelades av dig - + played %1 by %2 %1 spelades av %2 @@ -1292,31 +1292,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you spelade %1 av dig - + played %1 by %2 e.g. played 3 hours ago by SomeSource Spelade %1 av %2 - + added %1 e.g. added 3 hours ago La till %1 - + by <b>%1</b> e.g. by SomeArtist av <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum av <b>%1</b> på <b>%2</b> @@ -2162,7 +2162,7 @@ och radiostationer baserat på din personliga profil Network Activity - + Nätverksaktivitet @@ -2232,6 +2232,16 @@ och radiostationer baserat på din personliga profil High Quality Streams Högkvalitetsström + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2266,7 +2276,7 @@ och radiostationer baserat på din personliga profil out of %1 - + ur %1 @@ -2503,47 +2513,47 @@ och radiostationer baserat på din personliga profil Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify Synkronisera med Spotify - + Re-enable syncing with Spotify Återaktivera syncronisering mot Spotify - + Create local copy Skapa lokal kopia - + Subscribe to playlist changes Prenummerera på spellist-ändringar - + Re-enable playlist subscription Återaktivera spellisteprenummeration - + Stop subscribing to changes Sluta prenummerera på ändringar - + Enable Spotify collaborations Aktivera Spotify-samverkare - + Disable Spotify collaborations Avaktivera Spotify-samverkare - + Stop syncing with Spotify Sluta syncronisera med Spotify @@ -2551,28 +2561,28 @@ och radiostationer baserat på din personliga profil Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... Loggar in... - + Failed: %1 Misslyckade: %1 - + Logged in as %1 Inloggad som %1 - + Log Out Logga ut - - + + Log In Logga in @@ -3588,43 +3598,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline @@ -3953,35 +3963,35 @@ anger du PIN-koden här: # PLAYS / ARTIST - + # UPPSPELNINGAR / ARTIST # IN YOUR CHARTS - + # I DINA LISTOR - + You've listened to this track %n time(s). Du har lyssnat på detta spåret %n gånger.Du har lyssnat på detta spåret %n gånger - + You've never listened to this track before. Du har inte lyssnat på detta spåret tidigare. - + You first listened to it on %1. Du har lyssnat på det på %1. - + You've listened to %1 %n time(s). Du har lyssnat på %1 %n gång.Du har lyssnat på %1 %n gånger. - + You've never listened to %1 before. Du har inte lyssnat på %1 tidigare. @@ -3989,7 +3999,7 @@ anger du PIN-koden här: TrackView - + Sorry, your filter '%1' did not match any results. Tyvärr, ditt filter '%1' gav inga träffar diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index b2c103fbc5..36eab372ae 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2227,6 +2227,16 @@ Password High Quality Streams + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2498,47 +2508,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + Re-enable syncing with Spotify - + Create local copy - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify @@ -2546,28 +2556,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... - + Failed: %1 - + Logged in as %1 - + Log Out - - + + Log In @@ -3573,43 +3583,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3945,27 +3955,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3973,7 +3983,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 5c71225d28..d3ffbf74ab 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you %1 - + played %1 by %2 已播放 %2 的 %1 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you 你播放于 %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 播放于 %1 - + added %1 e.g. added 3 hours ago 添加于 %1 - + by <b>%1</b> e.g. by SomeArtist 来自 <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum 来自 <b>%1</b> 的专辑 <b>%2</b> @@ -2230,6 +2230,16 @@ Password High Quality Streams 高质量音频流 + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2501,47 +2511,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify 与 Spotify 同步 - + Re-enable syncing with Spotify 重新启用 Spotify 同步 - + Create local copy 创建本地拷贝 - + Subscribe to playlist changes 订阅播放列表改动 - + Re-enable playlist subscription 重新启用播放列表订阅 - + Stop subscribing to changes 停止订阅改动 - + Enable Spotify collaborations 启用 Spotify 碰撞 - + Disable Spotify collaborations 禁用 Spotify 碰撞 - + Stop syncing with Spotify 停止与 Spotify 同步 @@ -2549,28 +2559,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... 登录中... - + Failed: %1 失败:%1 - + Logged in as %1 作为 %1 登录 - + Log Out 登出 - - + + Log In 登录 @@ -3583,43 +3593,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 @@ -3956,27 +3966,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). 你已经收听过此歌曲 %n 遍。 - + You've never listened to this track before. 你之前从未听过此歌曲。 - + You first listened to it on %1. 你第一次听的是 %1. - + You've listened to %1 %n time(s). 你已经听过 %1 有 %n 遍了。 - + You've never listened to %1 before. 你之前从未听过 %1。 @@ -3984,7 +3994,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. 抱歉,未找到任何匹配 '%1' 的结果。 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 6dc743fdf5..ad1c4d2e5d 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1277,12 +1277,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1290,31 +1290,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -2227,6 +2227,16 @@ Password High Quality Streams 高品質的串流 + + + Use this to force Spotify to never announce listening data to Social Networks + + + + + Always run in Private Mode + + Spotify playlists to keep in sync: @@ -2498,47 +2508,47 @@ Password Tomahawk::Accounts::SpotifyAccount - + Sync with Spotify - + Re-enable syncing with Spotify - + Create local copy - + Subscribe to playlist changes - + Re-enable playlist subscription - + Stop subscribing to changes - + Enable Spotify collaborations - + Disable Spotify collaborations - + Stop syncing with Spotify @@ -2546,28 +2556,28 @@ Password Tomahawk::Accounts::SpotifyAccountConfig - + Logging in... 登錄中... - + Failed: %1 失敗:%1 - + Logged in as %1 - + Log Out - - + + Log In 登錄 @@ -3573,43 +3583,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3945,27 +3955,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. @@ -3973,7 +3983,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. From b3b47398bc8ab2ab7d73b1537bb5d43da1c09a9d Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Wed, 12 Jun 2013 02:16:52 +0200 Subject: [PATCH 311/565] Automatic merge of Transifex translations --- lang/tomahawk_cs.ts | 4 ++-- lang/tomahawk_de.ts | 4 ++-- lang/tomahawk_el.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 9b01f0e18e..7034b731c4 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -2233,12 +2233,12 @@ heslo Use this to force Spotify to never announce listening data to Social Networks - + Toto použít, aby bylo vynuceno, že Spotify nikdy neoznámí údaje o poslouchané hudbě společenským sítím. Always run in Private Mode - + Vždy spouštět v soukromém režimu diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index ba8b63282c..4038b715c8 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -2233,12 +2233,12 @@ Passwort Use this to force Spotify to never announce listening data to Social Networks - + Verwenden Sie dieses um Spotify zu zwingen, Spotify dass es Daten auf Soziale Netzwerke weitergibt Always run in Private Mode - + Immer im privaten Modus ausführen diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 913b72aa5a..a4388d8578 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -2233,12 +2233,12 @@ Password Use this to force Spotify to never announce listening data to Social Networks - + Χρησιμοποιήστε αυτο για να αναγκάσει το Spotify να μην ανακοινώνει την ακρόαση των δεδομένων στα κοινωνικά δίκτυα Always run in Private Mode - + Πάντα εκτέλεση με ιδιωτικό τρόπο From 81a406d1b5d41a9b543b4643d598f5d72add4ffd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:04:03 +0200 Subject: [PATCH 312/565] * Style fixes for Servent. --- src/libtomahawk/network/Servent.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 2602112793..96ae4a7ebe 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -252,6 +252,7 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const return _key; } + bool Servent::isValidExternalIP( const QHostAddress& addr ) { @@ -309,12 +310,14 @@ Servent::isValidExternalIP( const QHostAddress& addr ) return !addr.isNull(); } + void Servent::registerOffer( const QString& key, Connection* conn ) { d_func()->offers[key] = QPointer(conn); } + void Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, const QString &nodeid, const int timeout ) { @@ -326,6 +329,7 @@ Servent::registerLazyOffer(const QString &key, const peerinfo_ptr &peerInfo, con timer->start(); } + void Servent::deleteLazyOffer( const QString& key ) { @@ -373,6 +377,7 @@ Servent::lookupControlConnection( const SipInfo& sipInfo ) return NULL; } + ControlConnection* Servent::lookupControlConnection( const QString& nodeid ) { @@ -423,6 +428,7 @@ Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) return sipInfos; } + void Servent::queueForAclResult( const QString& username, const QSet& peerInfos ) { @@ -439,6 +445,7 @@ Servent::queueForAclResult( const QString& username, const QSet& p d_func()->queuedForACLResult[username][ (*peerInfos.begin())->nodeId() ] = QSet( peerInfos ); } + SipInfo Servent::getSipInfoForOldVersions( const QList& sipInfos ) { @@ -457,6 +464,7 @@ Servent::getSipInfoForOldVersions( const QList& sipInfos ) return info; } + void Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) { @@ -534,7 +542,8 @@ Servent::onSipInfoChanged() } -void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) +void +Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) { // We do not have received the initial SipInfo for this client yet, so wait for it. // Each client will have at least one non-visible SipInfo @@ -544,6 +553,7 @@ void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) ConnectionManager::getManagerForNodeId( peerInfo->nodeId() )->handleSipInfo( peerInfo ); } + void Servent::incomingConnection( int sd ) { @@ -784,6 +794,7 @@ Servent::socketConnected() handoverSocket( conn, sock ); } + // transfers ownership of socket to the connection and inits the connection void Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) @@ -804,6 +815,7 @@ Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) conn->start( sock ); } + void Servent::cleanupSocket( QTcpSocketExtra *sock ) { @@ -820,6 +832,7 @@ Servent::cleanupSocket( QTcpSocketExtra *sock ) sock->deleteLater(); } + void Servent::initiateConnection( const SipInfo& sipInfo, Connection* conn ) { @@ -868,6 +881,7 @@ Servent::initiateConnection( const SipInfo& sipInfo, Connection* conn ) sock->moveToThread( thread() ); } + void Servent::socketError( QAbstractSocket::SocketError e ) { @@ -898,6 +912,7 @@ Servent::socketError( QAbstractSocket::SocketError e ) } } + void Servent::checkACLResult( const QString& nodeid, const QString& username, ACLRegistry::ACL peerStatus ) { @@ -925,6 +940,7 @@ Servent::checkACLResult( const QString& nodeid, const QString& username, ACLRegi d_func()->queuedForACLResult[username].remove( nodeid ); } + void Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& theirdbid, const QString& key, const QString& theirkey ) { @@ -947,12 +963,14 @@ Servent::reverseOfferRequest( ControlConnection* orig_conn, const QString& their createParallelConnection( orig_conn, new_conn, theirkey ); } + bool Servent::visibleExternally() const { return (!d_func()->externalHostname.isNull()) || (d_func()->externalAddresses.length() > 0); } + bool Servent::ipv6ConnectivityLikely() const { @@ -967,30 +985,35 @@ Servent::ipv6ConnectivityLikely() const return false; } + int Servent::port() const { return d_func()->port; } + QList Servent::addresses() const { return d_func()->externalAddresses; } + QString Servent::additionalAddress() const { return d_func()->externalHostname; } + int Servent::additionalPort() const { return d_func()->externalPort; } + // return the appropriate connection for a given offer key, or NULL if invalid Connection* Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer ) @@ -1219,12 +1242,14 @@ Servent::connectedToSession( const QString& session ) return false; } + unsigned int Servent::numConnectedPeers() const { return d_func()->controlconnections.length(); } + QList Servent::streams() const { @@ -1310,6 +1335,7 @@ Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result, callback( sp ); } + bool Servent::isReady() const { From 2d6b8de76b791a25e651e675720dd064f963d9ff Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:04:41 +0200 Subject: [PATCH 313/565] * Caution for upcoming changes. --- src/libtomahawk/playlist/FlexibleHeader.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/FlexibleHeader.cpp b/src/libtomahawk/playlist/FlexibleHeader.cpp index a539e071c0..78c576e9fe 100644 --- a/src/libtomahawk/playlist/FlexibleHeader.cpp +++ b/src/libtomahawk/playlist/FlexibleHeader.cpp @@ -83,9 +83,12 @@ FlexibleHeader::FlexibleHeader( FlexibleView* parent ) TomahawkUtils::unmarginLayout( outerModeLayout ); TomahawkUtils::unmarginLayout( modeLayout ); - NewClosure( m_radioNormal, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Flat )->setAutoDelete( false ); - NewClosure( m_radioDetailed, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Detailed )->setAutoDelete( false ); - NewClosure( m_radioCloud, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Grid )->setAutoDelete( false ); + if ( parent ) + { + NewClosure( m_radioNormal, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Flat )->setAutoDelete( false ); + NewClosure( m_radioDetailed, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Detailed )->setAutoDelete( false ); + NewClosure( m_radioCloud, SIGNAL( clicked() ), const_cast< FlexibleView* >( parent ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Grid )->setAutoDelete( false ); + } } From db56757bc8274e2c5fba2c78ed3d342a45d23b5c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:05:09 +0200 Subject: [PATCH 314/565] * PlayableItem::forceUpdate() forces a repaint by emitting dataChanged(). --- src/libtomahawk/playlist/PlayableItem.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/playlist/PlayableItem.h b/src/libtomahawk/playlist/PlayableItem.h index 41f769740f..a22c948fb0 100644 --- a/src/libtomahawk/playlist/PlayableItem.h +++ b/src/libtomahawk/playlist/PlayableItem.h @@ -56,6 +56,7 @@ Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY dataChanged) void setPlaybackLog( const Tomahawk::PlaybackLog& log ); PlayableItem* parent() const { return m_parent; } + void forceUpdate() { emit dataChanged(); } bool isPlaying() const { return m_isPlaying; } void setIsPlaying( bool b ) { m_isPlaying = b; emit dataChanged(); } From fc4e42befae660a8fa167d4e1932be7233f6feeb Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:05:52 +0200 Subject: [PATCH 315/565] * Added custom UserRoles to access model's data. --- src/libtomahawk/playlist/PlayableModel.cpp | 47 +++++++++------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index a1365e49bb..670ad88136 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -294,6 +294,26 @@ PlayableModel::data( const QModelIndex& index, int role ) const if ( !entry ) return QVariant(); + if ( role == PlayableProxyModel::TypeRole ) + { + if ( entry->result() ) + { + return Tomahawk::TypeResult; + } + else if ( entry->query() ) + { + return Tomahawk::TypeQuery; + } + else if ( entry->artist() ) + { + return Tomahawk::TypeArtist; + } + else if ( entry->album() ) + { + return Tomahawk::TypeAlbum; + } + } + int column = index.column(); if ( role < CoverIDRole && role >= Qt::UserRole ) { @@ -310,33 +330,6 @@ PlayableModel::data( const QModelIndex& index, int role ) const break; } - case IsPlayingRole: - { - return entry->isPlaying(); - break; - } - - case PlayableProxyModel::TypeRole: - { - if ( entry->result() ) - { - return Tomahawk::TypeResult; - } - else if ( entry->query() ) - { - return Tomahawk::TypeQuery; - } - else if ( entry->artist() ) - { - return Tomahawk::TypeArtist; - } - else if ( entry->album() ) - { - return Tomahawk::TypeAlbum; - } - break; - } - default: { if ( !entry->query().isNull() ) From 88dfad3298d6597bec4b042121d06c134af95628 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:06:06 +0200 Subject: [PATCH 316/565] * Style fix. --- src/libtomahawk/playlist/PlayableProxyModel.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libtomahawk/playlist/PlayableProxyModel.cpp b/src/libtomahawk/playlist/PlayableProxyModel.cpp index 821ada7e93..d0f048d692 100644 --- a/src/libtomahawk/playlist/PlayableProxyModel.cpp +++ b/src/libtomahawk/playlist/PlayableProxyModel.cpp @@ -531,9 +531,7 @@ QVariant PlayableProxyModel::data( const QModelIndex& index, int role ) const { if ( role == StyleRole ) - { return m_style; - } if ( !sourceModel() ) return QVariant(); From a2406e859589c1906f55da7f51f0993d55e7b2a4 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:06:46 +0200 Subject: [PATCH 317/565] * Disconnect from previous query correctly. --- .../widgets/infowidgets/TrackInfoWidget.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index bec5c0ca04..8b2a81f3dd 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -175,12 +175,6 @@ TrackInfoWidget::jumpToCurrentTrack() void TrackInfoWidget::load( const query_ptr& query ) { - m_query = query; - m_artist = Artist::get( m_query->track()->artist() ); - m_title = QString( "%1 - %2" ).arg( query->track()->artist() ).arg( query->track()->track() ); - ui->trackLabel->setText( m_query->track()->track() ); - ui->artistLabel->setArtist( m_query->track()->artistPtr() ); - if ( !m_query.isNull() ) { disconnect( m_query->track().data(), SIGNAL( lyricsLoaded() ), this, SLOT( onLyricsLoaded() ) ); @@ -191,6 +185,12 @@ TrackInfoWidget::load( const query_ptr& query ) disconnect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), this, SLOT( onSimilarArtistsLoaded() ) ); } + m_query = query; + m_artist = Artist::get( m_query->track()->artist() ); + m_title = QString( "%1 - %2" ).arg( query->track()->artist() ).arg( query->track()->track() ); + ui->trackLabel->setText( m_query->track()->track() ); + ui->artistLabel->setArtist( m_query->track()->artistPtr() ); + connect( m_artist.data(), SIGNAL( similarArtistsLoaded() ), SLOT( onSimilarArtistsLoaded() ) ); connect( m_artist.data(), SIGNAL( statsLoaded() ), SLOT( onStatsLoaded() ) ); connect( m_query->track().data(), SIGNAL( lyricsLoaded() ), SLOT( onLyricsLoaded() ) ); From 812ca58674b9bfb51b7c52e454cffee6ff576d56 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:15:16 +0200 Subject: [PATCH 318/565] * Added ColumnItemDelegate. --- .../playlist/ColumnItemDelegate.cpp | 349 ++++++++++++++++++ src/libtomahawk/playlist/ColumnItemDelegate.h | 62 ++++ 2 files changed, 411 insertions(+) create mode 100644 src/libtomahawk/playlist/ColumnItemDelegate.cpp create mode 100644 src/libtomahawk/playlist/ColumnItemDelegate.h diff --git a/src/libtomahawk/playlist/ColumnItemDelegate.cpp b/src/libtomahawk/playlist/ColumnItemDelegate.cpp new file mode 100644 index 0000000000..60dcd7acc7 --- /dev/null +++ b/src/libtomahawk/playlist/ColumnItemDelegate.cpp @@ -0,0 +1,349 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "ColumnItemDelegate.h" + +#include +#include +#include +#include +#include + +#include "Query.h" +#include "Result.h" + +#include "utils/TomahawkStyle.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" +#include "utils/Closure.h" +#include "utils/PixmapDelegateFader.h" + +#include "PlayableItem.h" +#include "TreeProxyModel.h" +#include "ColumnView.h" +#include "ViewManager.h" +#include "Typedefs.h" + + +ColumnItemDelegate::ColumnItemDelegate( ColumnView* parent, TreeProxyModel* proxy ) + : QStyledItemDelegate( (QObject*)parent ) + , m_view( parent ) + , m_model( proxy ) +{ +} + + +QSize +ColumnItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QSize size; + + if ( index.isValid() ) + { + Tomahawk::ModelTypes type = (Tomahawk::ModelTypes)index.data( PlayableProxyModel::TypeRole ).toInt(); + switch ( type ) + { + case Tomahawk::TypeAlbum: + { + size.setHeight( option.fontMetrics.height() * 5 ); + return size; + } + + case Tomahawk::TypeQuery: + case Tomahawk::TypeResult: + { + size.setHeight( option.fontMetrics.height() * 1.6 ); + return size; + } + + default: + break; + } + } + + // artist per default + size.setHeight( option.fontMetrics.height() * 3 ); + return size; +} + + +void +ColumnItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item ) + return; + + QTextOption textOption( Qt::AlignVCenter | (Qt::Alignment)index.data( Qt::TextAlignmentRole ).toUInt() ); + textOption.setWrapMode( QTextOption::NoWrap ); + + QString text; + if ( !item->artist().isNull() ) + { + text = item->artist()->name(); + } + else if ( !item->album().isNull() ) + { + text = item->album()->name(); + } + else if ( !item->result().isNull() || !item->query().isNull() ) + { + float opacity = item->result().isNull() ? 0.0 : item->result()->score(); + opacity = qMax( (float)0.3, opacity ); + QColor textColor = TomahawkUtils::alphaBlend( option.palette.color( QPalette::Foreground ), option.palette.color( QPalette::Background ), opacity ); + + { + QStyleOptionViewItemV4 o = option; + initStyleOption( &o, QModelIndex() ); + + painter->save(); + o.palette.setColor( QPalette::Text, textColor ); + + if ( m_view->currentIndex() == index ) + o.state |= QStyle::State_Selected; + else + o.state &= ~QStyle::State_Selected; + + if ( o.state & QStyle::State_Selected && o.state & QStyle::State_Active ) + { + o.palette.setColor( QPalette::Text, o.palette.color( QPalette::HighlightedText ) ); + } + + if ( item->isPlaying() ) + { + textColor = TomahawkStyle::NOW_PLAYING_ITEM_TEXT; + o.palette.setColor( QPalette::Highlight, TomahawkStyle::NOW_PLAYING_ITEM ); + o.palette.setColor( QPalette::Text, TomahawkStyle::NOW_PLAYING_ITEM_TEXT ); + o.state |= QStyle::State_Selected; + } + + int oldX = 0; +// if ( m_view->header()->visualIndex( index.column() ) == 0 ) + { + oldX = o.rect.x(); + o.rect.setX( 0 ); + } + qApp->style()->drawControl( QStyle::CE_ItemViewItem, &o, painter ); + if ( oldX > 0 ) + o.rect.setX( oldX ); + +/* if ( m_hoveringOver == index && !index.data().toString().isEmpty() && index.column() == 0 ) + { + o.rect.setWidth( o.rect.width() - o.rect.height() ); + QRect arrowRect( o.rect.x() + o.rect.width(), o.rect.y() + 1, o.rect.height() - 2, o.rect.height() - 2 ); + + QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() ); + painter->drawPixmap( arrowRect, infoIcon ); + + m_infoButtonRects[ index ] = arrowRect; + }*/ + + { + QRect r = o.rect.adjusted( 3, 0, 0, 0 ); + + // Paint Now Playing Speaker Icon + if ( item->isPlaying() ) + { + const int pixMargin = 1; + const int pixHeight = r.height() - pixMargin * 2; + QRect npr = r.adjusted( pixMargin, pixMargin, pixHeight - r.width() + pixMargin, -pixMargin ); + painter->drawPixmap( npr, TomahawkUtils::defaultPixmap( TomahawkUtils::NowPlayingSpeaker, TomahawkUtils::Original, npr.size() ) ); + r.adjust( pixHeight + 6, 0, 0, 0 ); + } + + painter->setPen( o.palette.text().color() ); + + QString text = index.data().toString(); + if ( item->query()->track()->albumpos() > 0 ) + { + text = QString( "%1. %2" ) + .arg( index.data( PlayableModel::AlbumPosRole ).toString() ) + .arg( index.data().toString() ); + } + + text = painter->fontMetrics().elidedText( text, Qt::ElideRight, r.width() - 3 ); + painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, textOption ); + } + painter->restore(); + } + + return; + } + else + return; + + if ( text.trimmed().isEmpty() ) + text = tr( "Unknown" ); + + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, QModelIndex() ); + + const QModelIndex curIndex = m_view->currentIndex(); + if ( curIndex == index || curIndex.parent() == index || curIndex.parent().parent() == index ) + opt.state |= QStyle::State_Selected; + else + opt.state &= ~QStyle::State_Selected; + + qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); + + if ( opt.state & QStyle::State_Selected ) + { + opt.palette.setColor( QPalette::Text, opt.palette.color( QPalette::HighlightedText ) ); + } + + QRect arrowRect( m_view->viewport()->width() - option.rect.height(), option.rect.y() + 1, option.rect.height() - 2, option.rect.height() - 2 ); + if ( m_hoveringOver.row() == index.row() && m_hoveringOver.parent() == index.parent() ) + { + QPixmap infoIcon = TomahawkUtils::defaultPixmap( TomahawkUtils::InfoIcon, TomahawkUtils::Original, arrowRect.size() ); + painter->drawPixmap( arrowRect, infoIcon ); + + m_infoButtonRects[ index ] = arrowRect; + } + + if ( index.column() > 0 ) + return; + + painter->save(); + painter->setRenderHint( QPainter::Antialiasing ); + painter->setPen( opt.palette.color( QPalette::Text ) ); + + QRect r = option.rect.adjusted( 4, 4, -option.rect.width() + option.rect.height() - 4, -4 ); +// painter->drawPixmap( r, QPixmap( RESPATH "images/cover-shadow.png" ) ); + + if ( !m_pixmaps.contains( index ) ) + { + if ( !item->album().isNull() ) + { + m_pixmaps.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->album(), r.size(), TomahawkUtils::Original, false ) ) ); + _detail::Closure* closure = NewClosure( m_pixmaps[ index ], SIGNAL( repaintRequest() ), const_cast(this), SLOT( doUpdateIndex( const QPersistentModelIndex& ) ), QPersistentModelIndex( index ) ); + closure->setAutoDelete( false ); + } + else if ( !item->artist().isNull() ) + { + m_pixmaps.insert( index, QSharedPointer< Tomahawk::PixmapDelegateFader >( new Tomahawk::PixmapDelegateFader( item->artist(), r.size(), TomahawkUtils::Original, false ) ) ); + _detail::Closure* closure = NewClosure( m_pixmaps[ index ], SIGNAL( repaintRequest() ), const_cast(this), SLOT( doUpdateIndex( const QPersistentModelIndex& ) ), QPersistentModelIndex( index ) ); + closure->setAutoDelete( false ); + } + } + + const QPixmap cover = m_pixmaps[ index ]->currentPixmap(); + painter->drawPixmap( r, cover ); + + r = option.rect.adjusted( option.rect.height(), 6, -4, -option.rect.height() + 22 ); + text = painter->fontMetrics().elidedText( text, Qt::ElideRight, r.width() ); + painter->drawText( r, text, textOption ); + + painter->restore(); +} + + +void +ColumnItemDelegate::doUpdateIndex( const QPersistentModelIndex& index ) +{ + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item ) + return; + + item->forceUpdate(); +} + + +bool +ColumnItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) +{ + Q_UNUSED( model ); + Q_UNUSED( option ); + + if ( event->type() != QEvent::MouseButtonRelease && + event->type() != QEvent::MouseMove && + event->type() != QEvent::MouseButtonPress && + event->type() != QEvent::Leave ) + return false; + + bool hoveringInfo = false; + if ( m_infoButtonRects.contains( index ) ) + { + const QRect infoRect = m_infoButtonRects[ index ]; + const QMouseEvent* ev = static_cast< QMouseEvent* >( event ); + hoveringInfo = infoRect.contains( ev->pos() ); + } + + if ( event->type() == QEvent::MouseMove ) + { + if ( hoveringInfo ) + m_view->setCursor( Qt::PointingHandCursor ); + else + m_view->setCursor( Qt::ArrowCursor ); + + if ( m_hoveringOver != index ) + { + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + item->requestRepaint(); + m_hoveringOver = index; + doUpdateIndex( m_hoveringOver ); + } + + event->accept(); + return true; + } + + // reset mouse cursor. we switch to a pointing hand cursor when hovering an info button + m_view->setCursor( Qt::ArrowCursor ); + + if ( hoveringInfo ) + { + if ( event->type() == QEvent::MouseButtonRelease ) + { + PlayableItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) ); + if ( !item ) + return false; + + if ( item->query() ) + { + ViewManager::instance()->show( item->query()->track()->toQuery() ); + } + else if ( item->artist() ) + { + ViewManager::instance()->show( item->artist() ); + } + else if ( item->album() ) + { + ViewManager::instance()->show( item->album() ); + } + + event->accept(); + return true; + } + else if ( event->type() == QEvent::MouseButtonPress ) + { + // Stop the whole item from having a down click action as we just want the info button to be clicked + event->accept(); + return true; + } + } + + return false; +} + + +void +ColumnItemDelegate::resetHoverIndex() +{ + m_hoveringOver = QModelIndex(); + m_infoButtonRects.clear(); +} diff --git a/src/libtomahawk/playlist/ColumnItemDelegate.h b/src/libtomahawk/playlist/ColumnItemDelegate.h new file mode 100644 index 0000000000..c22bad870a --- /dev/null +++ b/src/libtomahawk/playlist/ColumnItemDelegate.h @@ -0,0 +1,62 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef COLUMNITEMDELEGATE_H +#define COLUMNITEMDELEGATE_H + +#include + +#include "DllMacro.h" +#include "Typedefs.h" + +namespace Tomahawk { +class PixmapDelegateFader; +} + +class ColumnView; +class TreeProxyModel; + +class DLLEXPORT ColumnItemDelegate : public QStyledItemDelegate +{ +Q_OBJECT + +public: + ColumnItemDelegate( ColumnView* parent, TreeProxyModel* proxy ); + + virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + +public slots: + void resetHoverIndex(); + +protected: + void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); + +private slots: + void doUpdateIndex( const QPersistentModelIndex& index ); + +private: + ColumnView* m_view; + TreeProxyModel* m_model; + + mutable QHash< QPersistentModelIndex, QSharedPointer< Tomahawk::PixmapDelegateFader > > m_pixmaps; + mutable QHash< QPersistentModelIndex, QRect > m_infoButtonRects; + QPersistentModelIndex m_hoveringOver; +}; + +#endif // COLUMNITEMDELEGATE_H From 7f64f25e595c8840b9e4bee092abd5d76ad3d420 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:15:48 +0200 Subject: [PATCH 319/565] * Added ColumnView. --- src/libtomahawk/playlist/ColumnView.cpp | 484 ++++++++++++++++++++++++ src/libtomahawk/playlist/ColumnView.h | 119 ++++++ 2 files changed, 603 insertions(+) create mode 100644 src/libtomahawk/playlist/ColumnView.cpp create mode 100644 src/libtomahawk/playlist/ColumnView.h diff --git a/src/libtomahawk/playlist/ColumnView.cpp b/src/libtomahawk/playlist/ColumnView.cpp new file mode 100644 index 0000000000..07818d32b7 --- /dev/null +++ b/src/libtomahawk/playlist/ColumnView.cpp @@ -0,0 +1,484 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "ColumnView.h" + +#include "audio/AudioEngine.h" +#include "context/ContextWidget.h" +#include "utils/AnimatedSpinner.h" +#include "widgets/OverlayWidget.h" + +#include "ContextMenu.h" +#include "TomahawkSettings.h" +#include "ViewHeader.h" +#include "ColumnItemDelegate.h" +#include "ColumnViewPreviewWidget.h" +#include "TreeModel.h" +#include "PlayableItem.h" +#include "Source.h" +#include "ViewManager.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +#include +#include +#include +#include +#include +#include +#include + +#define SCROLL_TIMEOUT 280 + +using namespace Tomahawk; + + +ColumnView::ColumnView( QWidget* parent ) + : QColumnView( parent ) + , m_header( new ViewHeader( this ) ) + , m_overlay( new OverlayWidget( this ) ) + , m_model( 0 ) + , m_proxyModel( 0 ) + , m_delegate( 0 ) + , m_loadingSpinner( new LoadingSpinner( this ) ) + , m_previewWidget( new ColumnViewPreviewWidget( this ) ) + , m_updateContextView( true ) + , m_contextMenu( new ContextMenu( this ) ) +{ + setFrameShape( QFrame::NoFrame ); + setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + setContentsMargins( 0, 0, 0, 0 ); + setMouseTracking( true ); + setAlternatingRowColors( true ); + setDragEnabled( true ); + setDropIndicatorShown( false ); + setDragDropOverwriteMode( false ); + setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + setSelectionMode( QAbstractItemView::SingleSelection ); + setSelectionBehavior( QAbstractItemView::SelectRows ); + setContextMenuPolicy( Qt::CustomContextMenu ); + setProxyModel( new TreeProxyModel( this ) ); + setPreviewWidget( m_previewWidget ); + + m_timer.setInterval( SCROLL_TIMEOUT ); + connect( verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ), SLOT( onViewChanged() ) ); + connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), SLOT( onViewChanged() ) ); + connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) ); + + connect( this, SIGNAL( updatePreviewWidget( QModelIndex ) ), SLOT( onUpdatePreviewWidget( QModelIndex ) ) ); + connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) ); + connect( this, SIGNAL( customContextMenuRequested( QPoint ) ), SLOT( onCustomContextMenu( QPoint ) ) ); + connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) ); +} + + +ColumnView::~ColumnView() +{ + tDebug() << Q_FUNC_INFO; +} + + +void +ColumnView::setProxyModel( TreeProxyModel* model ) +{ + m_proxyModel = model; + m_delegate = new ColumnItemDelegate( this, m_proxyModel ); + setItemDelegate( m_delegate ); + + QColumnView::setModel( m_proxyModel ); +} + + +void +ColumnView::setModel( QAbstractItemModel* model ) +{ + Q_UNUSED( model ); + tDebug() << "Explicitly use setPlaylistModel instead"; + Q_ASSERT( false ); +} + + +void +ColumnView::setTreeModel( TreeModel* model ) +{ + m_model = model; + + if ( m_proxyModel ) + { + m_proxyModel->setSourcePlayableModel( model ); + m_proxyModel->sort( 0 ); + } + + connect( m_proxyModel, SIGNAL( filteringStarted() ), SLOT( onFilteringStarted() ) ); + connect( m_proxyModel, SIGNAL( filteringFinished() ), m_loadingSpinner, SLOT( fadeOut() ) ); + + connect( m_proxyModel, SIGNAL( filteringFinished() ), SLOT( onFilterChangeFinished() ) ); + connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) ); + + guid(); // this will set the guid on the header + + m_header->setDefaultColumnWeights( m_proxyModel->columnWeights() ); + if ( m_proxyModel->style() == PlayableProxyModel::Large ) + { + setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + } + else + { + setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); + } + + connect( model, SIGNAL( changed() ), this, SIGNAL( modelChanged() ) ); + emit modelChanged(); + +/* setColumnHidden( PlayableModel::Score, true ); // Hide score column per default + setColumnHidden( PlayableModel::Origin, true ); // Hide origin column per default + setColumnHidden( PlayableModel::Composer, true ); //Hide composer column per default + + setGuid( QString( "columnview/%1" ).arg( model->columnCount() ) ); + sortByColumn( PlayableModel::Artist, Qt::AscendingOrder );*/ + + QList< int > widths; + widths << 320; + setColumnWidths( widths ); +} + + +void +ColumnView::setEmptyTip( const QString& tip ) +{ + m_emptyTip = tip; + m_overlay->setText( tip ); +} + + +bool +ColumnView::setFilter( const QString& filter ) +{ + proxyModel()->setFilter( filter ); + return true; +} + + +void +ColumnView::onViewChanged() +{ + if ( m_timer.isActive() ) + m_timer.stop(); + + m_timer.start(); +} + + +void +ColumnView::onScrollTimeout() +{ + if ( m_timer.isActive() ) + m_timer.stop(); + + QModelIndex left = indexAt( viewport()->rect().topLeft() ); + while ( left.isValid() && left.parent().isValid() ) + left = left.parent(); + + QModelIndex right = indexAt( viewport()->rect().bottomLeft() ); + while ( right.isValid() && right.parent().isValid() ) + right = right.parent(); + + int max = m_proxyModel->playlistInterface()->trackCount(); + if ( right.isValid() ) + max = right.row() + 1; + + if ( !max ) + return; + + for ( int i = left.row(); i < max; i++ ) + { + m_model->getCover( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) ); + } +} + + +void +ColumnView::currentChanged( const QModelIndex& current, const QModelIndex& previous ) +{ + QColumnView::currentChanged( current, previous ); + + if ( !m_updateContextView ) + return; + + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) ); + if ( item ) + { + if ( !item->result().isNull() ) + ViewManager::instance()->context()->setQuery( item->result()->toQuery() ); + else if ( !item->artist().isNull() ) + ViewManager::instance()->context()->setArtist( item->artist() ); + else if ( !item->album().isNull() ) + ViewManager::instance()->context()->setAlbum( item->album() ); + else if ( !item->query().isNull() ) + ViewManager::instance()->context()->setQuery( item->query() ); + } +} + + +void +ColumnView::onItemActivated( const QModelIndex& index ) +{ + PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); + if ( item ) + { + if ( !item->result().isNull() && item->result()->isOnline() ) + { + AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->result() ); + } + else if ( !item->query().isNull() ) + { + AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->query() ); + } + } +} + + +void +ColumnView::keyPressEvent( QKeyEvent* event ) +{ + QColumnView::keyPressEvent( event ); + + if ( !model() ) + return; + + if ( event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return ) + { + onItemActivated( currentIndex() ); + } +} + + +void +ColumnView::resizeEvent( QResizeEvent* event ) +{ + QColumnView::resizeEvent( event ); + m_header->checkState(); + + if ( !model() ) + return; + + if ( model()->columnCount( QModelIndex() ) == 1 ) + { + m_header->resizeSection( 0, event->size().width() ); + } +} + + +void +ColumnView::wheelEvent( QWheelEvent* event ) +{ + QColumnView::wheelEvent( event ); + + m_delegate->resetHoverIndex(); + repaint(); +} + + +void +ColumnView::onFilterChangeFinished() +{ + if ( selectedIndexes().count() ) + scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter ); + + if ( !proxyModel()->filter().isEmpty() && !proxyModel()->playlistInterface()->trackCount() && model()->trackCount() ) + { + m_overlay->setText( tr( "Sorry, your filter '%1' did not match any results." ).arg( proxyModel()->filter() ) ); + m_overlay->show(); + } + else + { + if ( model()->trackCount() ) + { + m_overlay->hide(); + } + else + { + m_overlay->setText( m_emptyTip ); + m_overlay->show(); + } + } +} + + +void +ColumnView::onFilteringStarted() +{ + m_overlay->hide(); + m_loadingSpinner->fadeIn(); +} + + +void +ColumnView::startDrag( Qt::DropActions supportedActions ) +{ + QList pindexes; + QModelIndexList indexes; + foreach( const QModelIndex& idx, selectedIndexes() ) + { + if ( ( m_proxyModel->flags( idx ) & Qt::ItemIsDragEnabled ) ) + { + indexes << idx; + pindexes << idx; + } + } + + if ( indexes.count() == 0 ) + return; + + tDebug( LOGVERBOSE ) << "Dragging" << indexes.count() << "indexes"; + QMimeData* data = m_proxyModel->mimeData( indexes ); + if ( !data ) + return; + + QDrag* drag = new QDrag( this ); + drag->setMimeData( data ); + + QPixmap p; + if ( data->hasFormat( "application/tomahawk.metadata.artist" ) ) + p = TomahawkUtils::createDragPixmap( TomahawkUtils::MediaTypeArtist, indexes.count() ); + else if ( data->hasFormat( "application/tomahawk.metadata.album" ) ) + p = TomahawkUtils::createDragPixmap( TomahawkUtils::MediaTypeAlbum, indexes.count() ); + else + p = TomahawkUtils::createDragPixmap( TomahawkUtils::MediaTypeTrack, indexes.count() ); + + drag->setPixmap( p ); + drag->setHotSpot( QPoint( -20, -20 ) ); + + drag->exec( supportedActions, Qt::CopyAction ); +} + + +void +ColumnView::onCustomContextMenu( const QPoint& pos ) +{ + m_contextMenu->clear(); + + QModelIndex idx = indexAt( pos ); + idx = idx.sibling( idx.row(), 0 ); + m_contextMenuIndex = idx; + + if ( !idx.isValid() ) + return; + + QList queries; + QList artists; + QList albums; + + QModelIndexList indexes = selectedIndexes(); + if ( !indexes.contains( idx ) ) + { + indexes.clear(); + indexes << idx; + } + + foreach ( const QModelIndex& index, indexes ) + { + if ( index.column() || indexes.contains( index.parent() ) ) + continue; + + PlayableItem* item = m_proxyModel->itemFromIndex( m_proxyModel->mapToSource( index ) ); + + if ( item && !item->result().isNull() ) + queries << item->result()->toQuery(); + else if ( item && !item->query().isNull() ) + queries << item->query(); + if ( item && !item->artist().isNull() ) + artists << item->artist(); + if ( item && !item->album().isNull() ) + albums << item->album(); + } + + m_contextMenu->setQueries( queries ); + m_contextMenu->setArtists( artists ); + m_contextMenu->setAlbums( albums ); + m_contextMenu->setPlaylistInterface( proxyModel()->playlistInterface() ); + + m_contextMenu->exec( viewport()->mapToGlobal( pos ) ); +} + + +void +ColumnView::onMenuTriggered( int action ) +{ + switch ( action ) + { + case ContextMenu::ActionPlay: + onItemActivated( m_contextMenuIndex ); + break; + + default: + break; + } +} + + +bool +ColumnView::jumpToCurrentTrack() +{ + if ( !m_proxyModel || !m_proxyModel->sourceModel() ) + return false; + + scrollTo( m_proxyModel->currentIndex(), QAbstractItemView::PositionAtCenter ); + return true; +} + + +QString +ColumnView::guid() const +{ + if ( m_guid.isEmpty() ) + { + m_guid = QString( "columnview/%1" ).arg( m_model->columnCount( QModelIndex() ) ); + m_header->setGuid( m_guid ); + } + + return m_guid; +} + + +void +ColumnView::onUpdatePreviewWidget( const QModelIndex& index ) +{ + PlayableItem* item = m_proxyModel->itemFromIndex( m_proxyModel->mapToSource( index ) ); + if ( !item || !item->result() ) + { + QList< int > widths = columnWidths(); + QList< int > finalWidths; + foreach ( int w, widths ) + { + finalWidths << qMax( 320, w ); + } + setColumnWidths( finalWidths ); + + return; + } + + m_previewWidget->setQuery( item->result()->toQuery() ); + + QList< int > widths = columnWidths(); + const int previewWidth = viewport()->width() - widths.at( 0 ) - widths.at( 1 ) - widths.at( 2 ); + widths.removeLast(); + widths << qMax( previewWidth, m_previewWidget->minimumSize().width() + 32 ); + setColumnWidths( widths ); +} diff --git a/src/libtomahawk/playlist/ColumnView.h b/src/libtomahawk/playlist/ColumnView.h new file mode 100644 index 0000000000..d2bcd847d0 --- /dev/null +++ b/src/libtomahawk/playlist/ColumnView.h @@ -0,0 +1,119 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Jeff Mitchell + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef COLUMNVIEW_H +#define COLUMNVIEW_H + +#include +#include +#include + +#include "TreeProxyModel.h" +#include "ViewPage.h" + +#include "PlaylistInterface.h" + +#include "DllMacro.h" + +namespace Tomahawk +{ + class ContextMenu; +}; + +class ViewHeader; +class AnimatedSpinner; +class OverlayWidget; +class TreeModel; +class ColumnItemDelegate; +class ColumnViewPreviewWidget; + +class DLLEXPORT ColumnView : public QColumnView +{ +Q_OBJECT + +public: + explicit ColumnView( QWidget* parent = 0 ); + ~ColumnView(); + + virtual QString guid() const; + virtual void setGuid( const QString& guid ) { m_guid = guid; } + + void setProxyModel( TreeProxyModel* model ); + + TreeModel* model() const { return m_model; } + TreeProxyModel* proxyModel() const { return m_proxyModel; } + OverlayWidget* overlay() const { return m_overlay; } + + void setModel( QAbstractItemModel* model ); + void setTreeModel( TreeModel* model ); + + virtual bool setFilter( const QString& filter ); + void setEmptyTip( const QString& tip ); + + virtual bool jumpToCurrentTrack(); + + bool updatesContextView() const { return m_updateContextView; } + void setUpdatesContextView( bool b ) { m_updateContextView = b; } + +public slots: + void onItemActivated( const QModelIndex& index ); + +signals: + void modelChanged(); + +protected: + virtual void startDrag( Qt::DropActions supportedActions ); + virtual void resizeEvent( QResizeEvent* event ); + + virtual void keyPressEvent( QKeyEvent* event ); + virtual void wheelEvent( QWheelEvent* event ); + +protected slots: + virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous ); + virtual void onUpdatePreviewWidget( const QModelIndex& index ); + +private slots: + void onFilterChangeFinished(); + void onFilteringStarted(); + void onViewChanged(); + void onScrollTimeout(); + + void onCustomContextMenu( const QPoint& pos ); + void onMenuTriggered( int action ); + +private: + ViewHeader* m_header; + OverlayWidget* m_overlay; + TreeModel* m_model; + TreeProxyModel* m_proxyModel; + ColumnItemDelegate* m_delegate; + AnimatedSpinner* m_loadingSpinner; + ColumnViewPreviewWidget* m_previewWidget; + + bool m_updateContextView; + + QModelIndex m_contextMenuIndex; + Tomahawk::ContextMenu* m_contextMenu; + + QString m_emptyTip; + QTimer m_timer; + mutable QString m_guid; +}; + +#endif // COLUMNVIEW_H From 95e6efd26dc82203925bb0c15d021223afc63762 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:16:05 +0200 Subject: [PATCH 320/565] * Added ColumnViewPreviewWidget. --- .../playlist/ColumnViewPreviewWidget.cpp | 108 ++++++++++ .../playlist/ColumnViewPreviewWidget.h | 58 ++++++ .../playlist/ColumnViewPreviewWidget.ui | 195 ++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp create mode 100644 src/libtomahawk/playlist/ColumnViewPreviewWidget.h create mode 100644 src/libtomahawk/playlist/ColumnViewPreviewWidget.ui diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp new file mode 100644 index 0000000000..f84249a753 --- /dev/null +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp @@ -0,0 +1,108 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "ColumnViewPreviewWidget.h" +#include "ui_ColumnViewPreviewWidget.h" + +#include +#include + +#include "ColumnView.h" +#include "utils/Logger.h" +#include "Source.h" +#include "utils/TomahawkUtilsGui.h" + +using namespace Tomahawk; + + +ColumnViewPreviewWidget::ColumnViewPreviewWidget( ColumnView* parent ) + : QWidget( parent ) + , ui( new Ui::ColumnViewPreviewWidget ) +{ + setVisible( false ); + ui->setupUi( this ); + + ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Grid, ui->cover->size() ) ); + ui->cover->setShowText( false ); + + ui->artistLabel->setContentsMargins( 6, 2, 6, 2 ); + ui->artistLabel->setElideMode( Qt::ElideMiddle ); + ui->artistLabel->setType( QueryLabel::Artist ); + connect( ui->artistLabel, SIGNAL( clickedArtist() ), SLOT( onArtistClicked() ) ); +} + + +ColumnViewPreviewWidget::~ColumnViewPreviewWidget() +{ + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; +} + + +void +ColumnViewPreviewWidget::changeEvent( QEvent* e ) +{ + QWidget::changeEvent( e ); + switch ( e->type() ) + { + case QEvent::LanguageChange: + ui->retranslateUi( this ); + break; + + default: + break; + } +} + + +void +ColumnViewPreviewWidget::setQuery( const Tomahawk::query_ptr& query ) +{ + if ( !m_query.isNull() ) + { + disconnect( m_query->track().data(), SIGNAL( updated() ), this, SLOT( onCoverUpdated() ) ); + } + + m_query = query; + connect( m_query->track().data(), SIGNAL( updated() ), SLOT( onCoverUpdated() ) ); + + onCoverUpdated(); + ui->cover->setQuery( query ); + + ui->trackLabel->setText( query->track()->track() ); + ui->artistLabel->setArtist( query->track()->artistPtr() ); + + setVisible( true ); +} + + +void +ColumnViewPreviewWidget::onCoverUpdated() +{ + if ( m_query->track()->cover( QSize( 0, 0 ) ).isNull() ) + return; + + ui->cover->setPixmap( TomahawkUtils::createRoundedImage( m_query->track()->cover( ui->cover->size() ), QSize( 0, 0 ) ) ); +} + + +QSize +ColumnViewPreviewWidget::minimumSize() const +{ + int minWidth = qMax( ui->trackLabel->sizeHint().width() + 32, ui->artistLabel->sizeHint().width() + 32 ); + return QSize( qMax( minWidth, 348 ), minimumHeight() ); +} diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.h b/src/libtomahawk/playlist/ColumnViewPreviewWidget.h new file mode 100644 index 0000000000..f78cda3aab --- /dev/null +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.h @@ -0,0 +1,58 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef COLUMNVIEWPREVIEWWIDGET_H +#define COLUMNVIEWPREVIEWWIDGET_H + +#include + +#include "Query.h" +#include "DllMacro.h" + +class ColumnView; + +namespace Ui +{ + class ColumnViewPreviewWidget; +} + +class DLLEXPORT ColumnViewPreviewWidget : public QWidget +{ +Q_OBJECT + +public: + explicit ColumnViewPreviewWidget( ColumnView* parent ); + ~ColumnViewPreviewWidget(); + + QSize minimumSize() const; + +public slots: + void setQuery( const Tomahawk::query_ptr& query ); + +protected: + void changeEvent( QEvent* e ); + +private slots: + void onCoverUpdated(); + +private: + Ui::ColumnViewPreviewWidget* ui; + Tomahawk::query_ptr m_query; +}; + +#endif // COLUMNVIEWPREVIEWWIDGET_H diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui b/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui new file mode 100644 index 0000000000..cd67319614 --- /dev/null +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui @@ -0,0 +1,195 @@ + + + ColumnViewPreviewWidget + + + + 0 + 0 + 378 + 422 + + + + + 0 + 0 + + + + + 348 + 378 + + + + InfoBar + + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 8 + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 260 + 260 + + + + Cover + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 16 + + + + + + + + + 0 + 0 + + + + + 18 + 75 + true + + + + TrackName + + + Qt::AlignCenter + + + 4 + + + + + + + + 0 + 0 + + + + + 14 + + + + ArtistName + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 17 + 70 + + + + + + + + + PlayableCover + QLabel +
widgets/PlayableCover.h
+
+ + QueryLabel + QLabel +
widgets/QueryLabel.h
+
+
+ + +
From d590fa4faabcbef21aa9725291e06b8732a88936 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:16:41 +0200 Subject: [PATCH 321/565] * Added FlexibleTreeView. --- src/libtomahawk/playlist/FlexibleTreeView.cpp | 309 ++++++++++++++++++ src/libtomahawk/playlist/FlexibleTreeView.h | 102 ++++++ 2 files changed, 411 insertions(+) create mode 100644 src/libtomahawk/playlist/FlexibleTreeView.cpp create mode 100644 src/libtomahawk/playlist/FlexibleTreeView.h diff --git a/src/libtomahawk/playlist/FlexibleTreeView.cpp b/src/libtomahawk/playlist/FlexibleTreeView.cpp new file mode 100644 index 0000000000..501acd0113 --- /dev/null +++ b/src/libtomahawk/playlist/FlexibleTreeView.cpp @@ -0,0 +1,309 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "FlexibleTreeView.h" + +#include +#include +#include + +#include "playlist/FlexibleHeader.h" +#include "playlist/TreeModel.h" +#include "playlist/ColumnView.h" +#include "playlist/TrackView.h" +#include "playlist/GridView.h" +#include "playlist/PlaylistLargeItemDelegate.h" +#include "PlayableProxyModelPlaylistInterface.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" + +using namespace Tomahawk; + + +FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) + : QWidget( parent ) + , m_header( new FlexibleHeader( 0 ) ) + , m_columnView( new ColumnView() ) +// , m_gridView( new GridView() ) +// , m_trackView( new TrackView() ) + , m_model( 0 ) + , m_temporary( false ) +{ + qRegisterMetaType< FlexibleTreeViewMode >( "FlexibleTreeViewMode" ); + +// m_trackView->setPlaylistInterface( m_playlistInterface ); +// m_columnView->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); +// m_gridView->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); + +/* m_columnView->setColumnHidden( PlayableModel::Age, true ); // Hide age column per default + m_columnView->setColumnHidden( PlayableModel::Filesize, true ); // Hide filesize column per default + m_columnView->setColumnHidden( PlayableModel::Composer, true ); // Hide composer column per default*/ + +/* PlaylistLargeItemDelegate* del = new PlaylistLargeItemDelegate( PlaylistLargeItemDelegate::LovedTracks, m_trackView, m_trackView->proxyModel() ); + m_trackView->setPlaylistItemDelegate( del ); + m_trackView->proxyModel()->setStyle( PlayableProxyModel::Large );*/ + + m_stack = new QStackedWidget(); + setLayout( new QVBoxLayout() ); + TomahawkUtils::unmarginLayout( layout() ); + + layout()->addWidget( m_header ); + if ( extraHeader ) + layout()->addWidget( extraHeader ); + layout()->addWidget( m_stack ); + + m_stack->addWidget( m_columnView ); +/* m_stack->addWidget( m_gridView ); + m_stack->addWidget( m_trackView );*/ + + setCurrentMode( Columns ); + + connect( m_header, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) ); +} + + +FlexibleTreeView::~FlexibleTreeView() +{ + tDebug() << Q_FUNC_INFO; +} + + +void +FlexibleTreeView::setGuid( const QString& guid ) +{ +// m_trackView->setGuid( guid ); + m_columnView->setGuid( guid ); +} + + +void +FlexibleTreeView::setTrackView( TrackView* view ) +{ + if ( m_trackView ) + { + m_stack->removeWidget( m_trackView ); + delete m_trackView; + } + +// view->setPlaylistInterface( m_playlistInterface ); + + m_trackView = view; + m_stack->addWidget( view ); +} + + +void +FlexibleTreeView::setColumnView( ColumnView* view ) +{ + if ( m_columnView ) + { + m_stack->removeWidget( m_columnView ); + delete m_columnView; + } + + connect( view, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection ); + +// view->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); + + m_columnView = view; + m_stack->addWidget( view ); +} + + +void +FlexibleTreeView::setGridView( GridView* view ) +{ + if ( m_gridView ) + { + m_stack->removeWidget( m_gridView ); + delete m_gridView; + } + + view->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); + + m_gridView = view; + m_stack->addWidget( view ); +} + + +void +FlexibleTreeView::setTreeModel( TreeModel* model ) +{ + if ( m_model ) + { + disconnect( m_model, SIGNAL( changed() ), this, SLOT( onModelChanged() ) ); + delete m_model; + } + + m_model = model; + +// m_trackView->setPlayableModel( model ); + m_columnView->setTreeModel( model ); +// m_gridView->setPlayableModel( model ); + +/* m_trackView->setSortingEnabled( false ); + m_trackView->sortByColumn( -1 ); + m_trackView->proxyModel()->sort( -1 ); + m_columnView->proxyModel()->sort( -1 ); + m_gridView->proxyModel()->sort( -1 );*/ + + connect( model, SIGNAL( changed() ), SLOT( onModelChanged() ), Qt::UniqueConnection ); + onModelChanged(); +} + + +void +FlexibleTreeView::setCurrentMode( FlexibleTreeViewMode mode ) +{ + m_mode = mode; + + switch ( mode ) + { + case Flat: + { + tDebug() << "m_trackView:" << m_trackView << m_stack->indexOf( m_trackView ); + m_stack->setCurrentWidget( m_trackView ); + break; + } + + case Columns: + { + m_stack->setCurrentWidget( m_columnView ); + break; + } + + case Albums: + { + m_stack->setCurrentWidget( m_gridView ); + break; + } + } + + emit modeChanged( mode ); +} + + +Tomahawk::playlistinterface_ptr +FlexibleTreeView::playlistInterface() const +{ + return Tomahawk::playlistinterface_ptr(); + return m_trackView->proxyModel()->playlistInterface(); +} + + +QString +FlexibleTreeView::title() const +{ + return m_model->title(); +} + + +QString +FlexibleTreeView::description() const +{ + return m_model->description(); +} + + +QPixmap +FlexibleTreeView::pixmap() const +{ + return m_pixmap; +} + + +bool +FlexibleTreeView::jumpToCurrentTrack() +{ + tDebug() << Q_FUNC_INFO; + + bool b = false; + + // note: the order of comparison is important here, if we'd write "b || foo" then foo will not be executed if b is already true! + b = m_trackView->jumpToCurrentTrack() || b; + b = m_columnView->jumpToCurrentTrack() || b; + b = m_gridView->jumpToCurrentTrack() || b; + + return b; +} + + +bool +FlexibleTreeView::setFilter( const QString& pattern ) +{ + ViewPage::setFilter( pattern ); + + m_columnView->setFilter( pattern ); +/* m_gridView->setFilter( pattern ); + m_trackView->setFilter( pattern );*/ + + return true; +} + + +void +FlexibleTreeView::setEmptyTip( const QString& tip ) +{ + m_columnView->setEmptyTip( tip ); +/* m_gridView->setEmptyTip( tip ); + m_trackView->setEmptyTip( tip );*/ +} + + +void +FlexibleTreeView::setPixmap( const QPixmap& pixmap ) +{ + m_pixmap = pixmap; + m_header->setPixmap( pixmap ); +} + + +void +FlexibleTreeView::onModelChanged() +{ + setPixmap( m_model->icon() ); + m_header->setCaption( m_model->title() ); + m_header->setDescription( m_model->description() ); + + if ( m_model->isReadOnly() ) + setEmptyTip( tr( "This playlist is currently empty." ) ); + else + setEmptyTip( tr( "This playlist is currently empty. Add some tracks to it and enjoy the music!" ) ); +} + + +void +FlexibleTreeView::onWidgetDestroyed( QWidget* widget ) +{ + Q_UNUSED( widget ); + emit destroyed( this ); +} + + +bool +FlexibleTreeView::isTemporaryPage() const +{ + return m_temporary; +} + + +void +FlexibleTreeView::setTemporaryPage( bool b ) +{ + m_temporary = b; +} diff --git a/src/libtomahawk/playlist/FlexibleTreeView.h b/src/libtomahawk/playlist/FlexibleTreeView.h new file mode 100644 index 0000000000..a88a53047a --- /dev/null +++ b/src/libtomahawk/playlist/FlexibleTreeView.h @@ -0,0 +1,102 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Christian Muehlhaeuser + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef FLEXIBLETREEVIEW_H +#define FLEXIBLETREEVIEW_H + +#include "ViewPage.h" +#include "PlaylistInterface.h" +#include "DllMacro.h" + +class QStackedWidget; + +class GridView; +class TrackView; +class ColumnView; +class TreeModel; +class PlaylistModel; +class FlexibleHeader; + +class DLLEXPORT FlexibleTreeView : public QWidget, public Tomahawk::ViewPage +{ +Q_OBJECT + +public: + enum FlexibleTreeViewMode + { Columns = 0, Albums = 1, Flat = 2 }; + + explicit FlexibleTreeView( QWidget* parent = 0, QWidget* extraHeader = 0 ); + ~FlexibleTreeView(); + + virtual QWidget* widget() { return this; } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const; + + virtual QString title() const; + virtual QString description() const; + virtual QPixmap pixmap() const; + + virtual bool showInfoBar() const { return false; } + virtual bool jumpToCurrentTrack(); + virtual bool isTemporaryPage() const; + void setTemporaryPage( bool b ); + + ColumnView* columnView() const { return m_columnView; } + GridView* gridView() const { return m_gridView; } + TrackView* trackView() const { return m_trackView; } + + void setGuid( const QString& guid ); + + void setColumnView( ColumnView* view ); + void setGridView( GridView* view ); + void setTrackView( TrackView* view ); + + void setTreeModel( TreeModel* model ); + + void setPixmap( const QPixmap& pixmap ); + void setEmptyTip( const QString& tip ); + +public slots: + void setCurrentMode( FlexibleTreeViewMode mode ); + virtual bool setFilter( const QString& pattern ); + +signals: + void modeChanged( FlexibleTreeViewMode mode ); + void destroyed( QWidget* widget ); + +private slots: + void onModelChanged(); + void onWidgetDestroyed( QWidget* widget ); + +private: + FlexibleHeader* m_header; + QPixmap m_pixmap; + + ColumnView* m_columnView; + GridView* m_gridView; + TrackView* m_trackView; + + TreeModel* m_model; + QStackedWidget* m_stack; + + FlexibleTreeViewMode m_mode; + bool m_temporary; +}; + +Q_DECLARE_METATYPE( FlexibleTreeView::FlexibleTreeViewMode ); + +#endif // FLEXIBLETREEVIEW_H From 53bdd2de6bd9e5d9fa528ff4466c9e904450687c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:16:53 +0200 Subject: [PATCH 322/565] * Updated CMakeLists.txt. --- src/libtomahawk/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 9c16c6a477..ca1e058aa3 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -47,10 +47,13 @@ set( libGuiSources playlist/InboxView.cpp playlist/FlexibleHeader.cpp playlist/FlexibleView.cpp + playlist/FlexibleTreeView.cpp playlist/TreeModel.cpp playlist/TreeProxyModel.cpp playlist/TreeProxyModelPlaylistInterface.cpp playlist/TreeItemDelegate.cpp + playlist/ColumnItemDelegate.cpp + playlist/ColumnViewPreviewWidget.cpp playlist/PlaylistModel.cpp playlist/PlaylistView.cpp playlist/PlaylistItemDelegate.cpp @@ -64,6 +67,7 @@ set( libGuiSources playlist/GridItemDelegate.cpp playlist/GridView.cpp playlist/TreeView.cpp + playlist/ColumnView.cpp playlist/TreeWidget.cpp playlist/ViewHeader.cpp playlist/LovedTracksModel.cpp @@ -380,6 +384,7 @@ set( libUI ${libUI} widgets/infowidgets/AlbumInfoWidget.ui widgets/infowidgets/TrackInfoWidget.ui playlist/QueueView.ui + playlist/ColumnViewPreviewWidget.ui filemetadata/MetadataEditor.ui context/ContextWidget.ui infobar/InfoBar.ui From d098c1cba78322da9f92d434ca5f2143131f66b9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:21:04 +0200 Subject: [PATCH 323/565] * Use FlexibleTreeViews for showing collections. --- src/libtomahawk/ViewManager.cpp | 25 +++++++++++++++---------- src/libtomahawk/ViewManager.h | 3 ++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 84e9d375d0..0fd93eed3b 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -26,11 +26,13 @@ #include "infobar/InfoBar.h" #include "playlist/FlexibleView.h" +#include "playlist/FlexibleTreeView.h" #include "playlist/TreeModel.h" #include "playlist/PlaylistModel.h" #include "playlist/PlaylistView.h" #include "playlist/PlayableProxyModel.h" #include "playlist/PlayableModel.h" +#include "playlist/ColumnView.h" #include "playlist/TreeView.h" #include "playlist/TreeWidget.h" #include "playlist/GridView.h" @@ -311,28 +313,31 @@ ViewManager::show( const Tomahawk::collection_ptr& collection ) { m_currentCollection = collection; - TreeWidget* widget; - if ( !m_treeWidgets.contains( collection ) || m_treeWidgets.value( collection ).isNull() ) + FlexibleTreeView* view; + if ( !m_collectionViews.contains( collection ) || m_collectionViews.value( collection ).isNull() ) { - widget = new TreeWidget(); - widget->view()->proxyModel()->setStyle( PlayableProxyModel::Collection ); + view = new FlexibleTreeView(); + + view->columnView()->proxyModel()->setStyle( PlayableProxyModel::Collection ); TreeModel* model = new TreeModel(); - widget->view()->setTreeModel( model ); + + view->setTreeModel( model ); if ( !collection.isNull() ) - widget->view()->setEmptyTip( collection->emptyText() ); + view->setEmptyTip( collection->emptyText() ); model->addCollection( collection ); + setPage( view ); - m_treeWidgets.insert( collection, widget ); + m_collectionViews.insert( collection, view ); } else { - widget = m_treeWidgets.value( collection ).data(); + view = m_collectionViews.value( collection ).data(); } - setPage( widget ); - return widget; + setPage( view ); + return view; } diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 093eec3e9f..34a04a847f 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -41,6 +41,7 @@ class TreeWidget; class CollectionModel; class ContextWidget; class FlexibleView; +class FlexibleTreeView; class PlaylistModel; class PlaylistView; class TrackProxyModel; @@ -202,7 +203,7 @@ private slots: QList< Tomahawk::collection_ptr > m_superCollections; QHash< Tomahawk::dynplaylist_ptr, QPointer > m_dynamicWidgets; - QHash< Tomahawk::collection_ptr, QPointer > m_treeWidgets; + QHash< Tomahawk::collection_ptr, QPointer > m_collectionViews; QHash< Tomahawk::artist_ptr, QPointer > m_artistViews; QHash< Tomahawk::album_ptr, QPointer > m_albumViews; QHash< Tomahawk::query_ptr, QPointer > m_trackViews; From 447c6c02174095a5ec407959aadcb3e01c6dc2d3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 06:21:19 +0200 Subject: [PATCH 324/565] * Reset the model before applying a filter. --- src/libtomahawk/playlist/TreeProxyModel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/playlist/TreeProxyModel.cpp b/src/libtomahawk/playlist/TreeProxyModel.cpp index 4827a8377a..0f12bdb69d 100644 --- a/src/libtomahawk/playlist/TreeProxyModel.cpp +++ b/src/libtomahawk/playlist/TreeProxyModel.cpp @@ -101,6 +101,7 @@ TreeProxyModel::onModelReset() void TreeProxyModel::setFilter( const QString& pattern ) { + reset(); emit filteringStarted(); m_filter = pattern; From d2f8bc2784d5294bfffd9e0ed815ae08d23ede76 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 07:35:10 +0200 Subject: [PATCH 325/565] * Removed header from ColumnView. --- src/libtomahawk/playlist/ColumnView.cpp | 16 ++-------------- src/libtomahawk/playlist/ColumnView.h | 1 - 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/libtomahawk/playlist/ColumnView.cpp b/src/libtomahawk/playlist/ColumnView.cpp index 07818d32b7..d3f42ddb7f 100644 --- a/src/libtomahawk/playlist/ColumnView.cpp +++ b/src/libtomahawk/playlist/ColumnView.cpp @@ -50,7 +50,6 @@ using namespace Tomahawk; ColumnView::ColumnView( QWidget* parent ) : QColumnView( parent ) - , m_header( new ViewHeader( this ) ) , m_overlay( new OverlayWidget( this ) ) , m_model( 0 ) , m_proxyModel( 0 ) @@ -133,7 +132,6 @@ ColumnView::setTreeModel( TreeModel* model ) guid(); // this will set the guid on the header - m_header->setDefaultColumnWeights( m_proxyModel->columnWeights() ); if ( m_proxyModel->style() == PlayableProxyModel::Large ) { setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); @@ -154,7 +152,7 @@ ColumnView::setTreeModel( TreeModel* model ) sortByColumn( PlayableModel::Artist, Qt::AscendingOrder );*/ QList< int > widths; - widths << 320; + widths << m_previewWidget->minimumSize().width() + 32; setColumnWidths( widths ); } @@ -273,15 +271,6 @@ void ColumnView::resizeEvent( QResizeEvent* event ) { QColumnView::resizeEvent( event ); - m_header->checkState(); - - if ( !model() ) - return; - - if ( model()->columnCount( QModelIndex() ) == 1 ) - { - m_header->resizeSection( 0, event->size().width() ); - } } @@ -450,7 +439,6 @@ ColumnView::guid() const if ( m_guid.isEmpty() ) { m_guid = QString( "columnview/%1" ).arg( m_model->columnCount( QModelIndex() ) ); - m_header->setGuid( m_guid ); } return m_guid; @@ -467,7 +455,7 @@ ColumnView::onUpdatePreviewWidget( const QModelIndex& index ) QList< int > finalWidths; foreach ( int w, widths ) { - finalWidths << qMax( 320, w ); + finalWidths << qMax( m_previewWidget->minimumSize().width() + 32, w ); } setColumnWidths( finalWidths ); diff --git a/src/libtomahawk/playlist/ColumnView.h b/src/libtomahawk/playlist/ColumnView.h index d2bcd847d0..c2e6965c79 100644 --- a/src/libtomahawk/playlist/ColumnView.h +++ b/src/libtomahawk/playlist/ColumnView.h @@ -98,7 +98,6 @@ private slots: void onMenuTriggered( int action ); private: - ViewHeader* m_header; OverlayWidget* m_overlay; TreeModel* m_model; TreeProxyModel* m_proxyModel; From e8a48aafe47c6a217f1c74c55a0d4268170de864 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 07:35:29 +0200 Subject: [PATCH 326/565] * Show more info in the preview-widget. --- .../playlist/ColumnViewPreviewWidget.cpp | 28 ++- .../playlist/ColumnViewPreviewWidget.ui | 184 +++++++++++++++++- 2 files changed, 204 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp index f84249a753..381c8fbe47 100644 --- a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp @@ -83,10 +83,36 @@ ColumnViewPreviewWidget::setQuery( const Tomahawk::query_ptr& query ) onCoverUpdated(); ui->cover->setQuery( query ); + setVisible( true ); + ui->trackLabel->setText( query->track()->track() ); ui->artistLabel->setArtist( query->track()->artistPtr() ); + ui->composerValue->setText( query->track()->composer() ); - setVisible( true ); + ui->composerValue->setVisible( !query->track()->composerPtr().isNull() ); + ui->composerLabel->setVisible( !query->track()->composerPtr().isNull() ); + + if ( query->numResults() ) + { + ui->yearValue->setText( QString::number( query->track()->year() ) ); + ui->bitrateValue->setText( QString::number( query->results().first()->bitrate() ) ); + ui->durationValue->setText( TomahawkUtils::timeToString( query->track()->duration() ) ); + ui->ageValue->setText( TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) ) ); + + ui->yearValue->setVisible( query->track()->year() > 0 ); + ui->yearLabel->setVisible( query->track()->year() > 0 ); + } + else + { + ui->yearLabel->setVisible( false ); + ui->yearValue->setVisible( false ); + ui->bitrateLabel->setVisible( false ); + ui->bitrateValue->setVisible( false ); + ui->durationLabel->setVisible( false ); + ui->durationValue->setVisible( false ); + ui->ageLabel->setVisible( false ); + ui->ageValue->setVisible( false ); + } } diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui b/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui index cd67319614..e41423dd8b 100644 --- a/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui @@ -6,8 +6,8 @@ 0 0 - 378 - 422 + 348 + 500 @@ -19,16 +19,13 @@ 348 - 378 + 500 InfoBar - - 0 - @@ -163,6 +160,179 @@ + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 16 + + + + + + + + 4 + + + + + + 12 + + + + Composer + + + + + + + + 12 + + + + Age + + + + + + + + 12 + + + + Year + + + + + + + + 12 + + + + Duration + + + + + + + + 12 + + + + Bitrate + + + + + + + false + + + + 12 + + + + Composer: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 12 + + + + Duration: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 12 + + + + Bitrate: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 12 + + + + Year: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 12 + + + + Age: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + @@ -171,7 +341,7 @@ 17 - 70 + 15 From 5c2cebb423509568f0ae3da198f65ce311ade10a Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 07:39:45 +0200 Subject: [PATCH 327/565] * Don't show empty labels in preview-widget. --- src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp index 381c8fbe47..73efc48579 100644 --- a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp @@ -101,6 +101,12 @@ ColumnViewPreviewWidget::setQuery( const Tomahawk::query_ptr& query ) ui->yearValue->setVisible( query->track()->year() > 0 ); ui->yearLabel->setVisible( query->track()->year() > 0 ); + ui->bitrateLabel->setVisible( query->results().first()->bitrate() > 0 ); + ui->bitrateValue->setVisible( query->results().first()->bitrate() > 0 ); + ui->durationLabel->setVisible( query->track()->duration() > 0 ); + ui->durationValue->setVisible( query->track()->duration() > 0 ); + ui->ageLabel->setVisible( query->results().first()->modificationTime() > 0 ); + ui->ageValue->setVisible( query->results().first()->modificationTime() > 0 ); } else { From 7b62c3ed141f8b46c079f217eaece6ec7bbed044 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 09:02:57 +0200 Subject: [PATCH 328/565] * A few ColumnItemDelegate fixes. --- src/libtomahawk/playlist/ColumnItemDelegate.cpp | 9 +++++++-- src/libtomahawk/playlist/ColumnViewPreviewWidget.ui | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/ColumnItemDelegate.cpp b/src/libtomahawk/playlist/ColumnItemDelegate.cpp index 60dcd7acc7..0a54379be8 100644 --- a/src/libtomahawk/playlist/ColumnItemDelegate.cpp +++ b/src/libtomahawk/playlist/ColumnItemDelegate.cpp @@ -222,7 +222,7 @@ ColumnItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option painter->setRenderHint( QPainter::Antialiasing ); painter->setPen( opt.palette.color( QPalette::Text ) ); - QRect r = option.rect.adjusted( 4, 4, -option.rect.width() + option.rect.height() - 4, -4 ); + QRect r = option.rect.adjusted( 8, 2, -option.rect.width() + option.rect.height() + 4, -2 ); // painter->drawPixmap( r, QPixmap( RESPATH "images/cover-shadow.png" ) ); if ( !m_pixmaps.contains( index ) ) @@ -244,7 +244,12 @@ ColumnItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option const QPixmap cover = m_pixmaps[ index ]->currentPixmap(); painter->drawPixmap( r, cover ); - r = option.rect.adjusted( option.rect.height(), 6, -4, -option.rect.height() + 22 ); + QFont font = painter->font(); +// font.setWeight( 58 ); + font.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); + painter->setFont( font ); + + r = option.rect.adjusted( option.rect.height() + 16, 6, -4, -6 ); text = painter->fontMetrics().elidedText( text, Qt::ElideRight, r.width() ); painter->drawText( r, text, textOption ); diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui b/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui index e41423dd8b..6e578499a9 100644 --- a/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.ui @@ -6,7 +6,7 @@ 0 0 - 348 + 300 500 @@ -18,7 +18,7 @@ - 348 + 300 500 From 8ac833bf974a789f48e5437457bc603cf3652385 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 09:15:42 +0200 Subject: [PATCH 329/565] * Double-clicking in ColumnView opens the corresponding page. --- src/libtomahawk/playlist/ColumnView.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/ColumnView.cpp b/src/libtomahawk/playlist/ColumnView.cpp index d3f42ddb7f..5349138a49 100644 --- a/src/libtomahawk/playlist/ColumnView.cpp +++ b/src/libtomahawk/playlist/ColumnView.cpp @@ -240,7 +240,15 @@ ColumnView::onItemActivated( const QModelIndex& index ) PlayableItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( index ) ); if ( item ) { - if ( !item->result().isNull() && item->result()->isOnline() ) + if ( !item->artist().isNull() ) + { + ViewManager::instance()->show( item->artist() ); + } + else if ( !item->album().isNull() ) + { + ViewManager::instance()->show( item->album() ); + } + else if ( !item->result().isNull() && item->result()->isOnline() ) { AudioEngine::instance()->playItem( m_proxyModel->playlistInterface(), item->result() ); } From 4ea22ba05426d9e01b21becf14de29f659d75036 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 09:23:14 +0200 Subject: [PATCH 330/565] * Show a default track cover until we retrieved the proper, new one. --- src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp index 73efc48579..f3b38ad5c7 100644 --- a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp @@ -37,9 +37,7 @@ ColumnViewPreviewWidget::ColumnViewPreviewWidget( ColumnView* parent ) setVisible( false ); ui->setupUi( this ); - ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Grid, ui->cover->size() ) ); ui->cover->setShowText( false ); - ui->artistLabel->setContentsMargins( 6, 2, 6, 2 ); ui->artistLabel->setElideMode( Qt::ElideMiddle ); ui->artistLabel->setType( QueryLabel::Artist ); @@ -126,7 +124,10 @@ void ColumnViewPreviewWidget::onCoverUpdated() { if ( m_query->track()->cover( QSize( 0, 0 ) ).isNull() ) + { + ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Grid, ui->cover->size() ) ); return; + } ui->cover->setPixmap( TomahawkUtils::createRoundedImage( m_query->track()->cover( ui->cover->size() ), QSize( 0, 0 ) ) ); } From bcd33932d0427da5357e87c2114647eb8be053ad Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:27:19 +0200 Subject: [PATCH 331/565] * Added ModeHeader widget. --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/playlist/ModeHeader.cpp | 120 ++++++++++++++++++++++++ src/libtomahawk/playlist/ModeHeader.h | 54 +++++++++++ 3 files changed, 175 insertions(+) create mode 100644 src/libtomahawk/playlist/ModeHeader.cpp create mode 100644 src/libtomahawk/playlist/ModeHeader.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index ca1e058aa3..82d01f6172 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -46,6 +46,7 @@ set( libGuiSources playlist/InboxModel.cpp playlist/InboxView.cpp playlist/FlexibleHeader.cpp + playlist/ModeHeader.cpp playlist/FlexibleView.cpp playlist/FlexibleTreeView.cpp playlist/TreeModel.cpp diff --git a/src/libtomahawk/playlist/ModeHeader.cpp b/src/libtomahawk/playlist/ModeHeader.cpp new file mode 100644 index 0000000000..08f204f43b --- /dev/null +++ b/src/libtomahawk/playlist/ModeHeader.cpp @@ -0,0 +1,120 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2013, Christian Muehlhaeuser + * Copyright 2012-2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "ModeHeader.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "playlist/FlexibleView.h" +#include "ViewManager.h" +#include "utils/TomahawkStyle.h" +#include "utils/Closure.h" +#include "utils/TomahawkUtilsGui.h" +#include "utils/Logger.h" +#include "widgets/QueryLabel.h" +#include "Source.h" + +using namespace Tomahawk; + + +ModeHeader::ModeHeader( QWidget* parent ) + : QWidget( parent ) + , m_parent( parent ) +{ + QFile f( RESPATH "stylesheets/topbar-radiobuttons.css" ); + f.open( QFile::ReadOnly ); + QString css = QString::fromLatin1( f.readAll() ); + f.close(); + + QHBoxLayout* outerModeLayout = new QHBoxLayout; + outerModeLayout->addStretch(); + + QWidget* modeWidget = new QWidget( this ); + QHBoxLayout* modeLayout = new QHBoxLayout; + modeWidget->setLayout( modeLayout ); + modeWidget->setStyleSheet( css ); + + m_radioNormal = new QRadioButton( modeWidget ); + m_radioDetailed = new QRadioButton( modeWidget ); + m_radioCloud = new QRadioButton( modeWidget ); + //for the CSS: + m_radioNormal->setObjectName( "radioNormal" ); + m_radioCloud->setObjectName( "radioCloud" ); + + m_radioNormal->setFocusPolicy( Qt::NoFocus ); + m_radioDetailed->setFocusPolicy( Qt::NoFocus ); + m_radioCloud->setFocusPolicy( Qt::NoFocus ); + + modeLayout->addWidget( m_radioNormal ); + modeLayout->addWidget( m_radioDetailed ); + modeLayout->addWidget( m_radioCloud ); + + modeWidget->setFixedSize( 87, 30 ); + + m_radioNormal->setChecked( true ); + + outerModeLayout->addWidget( modeWidget ); + outerModeLayout->addStretch(); + + setLayout( outerModeLayout ); + + TomahawkUtils::unmarginLayout( outerModeLayout ); + TomahawkUtils::unmarginLayout( modeLayout ); + outerModeLayout->setContentsMargins( 2, 2, 2, 2 ); + + QPalette pal = palette(); + pal.setColor( QPalette::Foreground, Qt::white ); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND.lighter() ); + + setAutoFillBackground( true ); + setPalette( pal ); + + connect( m_radioNormal, SIGNAL( clicked() ), SIGNAL( flatClicked() ) ); + connect( m_radioDetailed, SIGNAL( clicked() ), SIGNAL( detailedClicked() ) ); + connect( m_radioCloud, SIGNAL( clicked() ), SIGNAL( gridClicked() ) ); +} + + +ModeHeader::~ModeHeader() +{ +} + + +void +ModeHeader::changeEvent( QEvent* e ) +{ + QWidget::changeEvent( e ); + switch ( e->type() ) + { + case QEvent::LanguageChange: +// ui->retranslateUi( this ); + break; + + default: + break; + } +} + diff --git a/src/libtomahawk/playlist/ModeHeader.h b/src/libtomahawk/playlist/ModeHeader.h new file mode 100644 index 0000000000..47b9d346cc --- /dev/null +++ b/src/libtomahawk/playlist/ModeHeader.h @@ -0,0 +1,54 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2013, Christian Muehlhaeuser + * Copyright 2012-2013, Teo Mrnjavac + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef MODEHEADER_H +#define MODEHEADER_H + +#include + +#include "DllMacro.h" + +class QRadioButton; + +class DLLEXPORT ModeHeader : public QWidget +{ + Q_OBJECT + +public: + ModeHeader( QWidget* parent ); + ~ModeHeader(); + +protected: + void changeEvent( QEvent* e ); + +signals: + void detailedClicked(); + void flatClicked(); + void gridClicked(); + +private: + QWidget* m_parent; + + QRadioButton* m_radioCloud; + QRadioButton* m_radioDetailed; + QRadioButton* m_radioNormal; + +}; + +#endif From 0f45146adaf41dd2c0194f741941846632b7ed87 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:27:48 +0200 Subject: [PATCH 332/565] * Compact BasicHeader. --- src/libtomahawk/widgets/BasicHeader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/widgets/BasicHeader.cpp b/src/libtomahawk/widgets/BasicHeader.cpp index f689acafb1..00933f5f35 100644 --- a/src/libtomahawk/widgets/BasicHeader.cpp +++ b/src/libtomahawk/widgets/BasicHeader.cpp @@ -40,9 +40,9 @@ BasicHeader::BasicHeader( QWidget* parent ) setLayout( m_mainLayout ); m_imageLabel = new QLabel( this ); - m_imageLabel->setFixedSize( 64, 64 ); + m_imageLabel->setFixedSize( 48, 48 ); m_mainLayout->addWidget( m_imageLabel ); - m_mainLayout->addSpacing( 16 ); + m_mainLayout->addSpacing( 8 ); m_verticalLayout = new QVBoxLayout; m_mainLayout->addLayout( m_verticalLayout ); @@ -53,7 +53,7 @@ BasicHeader::BasicHeader( QWidget* parent ) m_verticalLayout->addWidget( m_descriptionLabel ); m_verticalLayout->addStretch(); - m_mainLayout->addSpacing( 16 ); + m_mainLayout->addSpacing( 8 ); m_mainLayout->setStretchFactor( m_verticalLayout, 2 ); QPalette pal = palette(); @@ -89,7 +89,7 @@ BasicHeader::BasicHeader( QWidget* parent ) TomahawkUtils::unmarginLayout( layout() ); layout()->setContentsMargins( 8, 4, 8, 4 ); setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - setFixedHeight( 80 ); + setFixedHeight( 56 ); setAutoFillBackground( true ); setPalette( pal ); From 3ef99fbb32c632d613084e303b84c068e180c455 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:28:12 +0200 Subject: [PATCH 333/565] * FlexibleHeader needs to set its own size. --- src/libtomahawk/playlist/FlexibleHeader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/playlist/FlexibleHeader.cpp b/src/libtomahawk/playlist/FlexibleHeader.cpp index 78c576e9fe..6bfdc87ace 100644 --- a/src/libtomahawk/playlist/FlexibleHeader.cpp +++ b/src/libtomahawk/playlist/FlexibleHeader.cpp @@ -43,6 +43,8 @@ FlexibleHeader::FlexibleHeader( FlexibleView* parent ) : FilterHeader( parent ) , m_parent( parent ) { + setFixedHeight( 80 ); + QFile f( RESPATH "stylesheets/topbar-radiobuttons.css" ); f.open( QFile::ReadOnly ); QString css = QString::fromLatin1( f.readAll() ); From da8fd58aaa220d28d85046cf7d1fae2856129b66 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:28:59 +0200 Subject: [PATCH 334/565] * Re-enable TreeView for collections. --- src/libtomahawk/playlist/FlexibleTreeView.cpp | 79 +++++++++++++------ src/libtomahawk/playlist/FlexibleTreeView.h | 13 +-- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/libtomahawk/playlist/FlexibleTreeView.cpp b/src/libtomahawk/playlist/FlexibleTreeView.cpp index 501acd0113..74db192a2a 100644 --- a/src/libtomahawk/playlist/FlexibleTreeView.cpp +++ b/src/libtomahawk/playlist/FlexibleTreeView.cpp @@ -22,14 +22,18 @@ #include #include -#include "playlist/FlexibleHeader.h" +#include "widgets/FilterHeader.h" #include "playlist/TreeModel.h" #include "playlist/ColumnView.h" #include "playlist/TrackView.h" +#include "playlist/TreeView.h" #include "playlist/GridView.h" +#include "playlist/ModeHeader.h" #include "playlist/PlaylistLargeItemDelegate.h" #include "PlayableProxyModelPlaylistInterface.h" +#include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" +#include "utils/Closure.h" #include "utils/Logger.h" using namespace Tomahawk; @@ -37,15 +41,18 @@ using namespace Tomahawk; FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) : QWidget( parent ) - , m_header( new FlexibleHeader( 0 ) ) + , m_header( new FilterHeader( 0 ) ) + , m_modeHeader( new ModeHeader( this ) ) , m_columnView( new ColumnView() ) -// , m_gridView( new GridView() ) -// , m_trackView( new TrackView() ) + , m_treeView( new TreeView() ) + , m_trackView( 0 ) , m_model( 0 ) , m_temporary( false ) { qRegisterMetaType< FlexibleTreeViewMode >( "FlexibleTreeViewMode" ); + m_treeView->proxyModel()->setStyle( PlayableProxyModel::Collection ); + // m_trackView->setPlaylistInterface( m_playlistInterface ); // m_columnView->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); // m_gridView->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); @@ -62,18 +69,45 @@ FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) setLayout( new QVBoxLayout() ); TomahawkUtils::unmarginLayout( layout() ); + QFrame* lineAbove = new QFrame( this ); + lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineAbove->setFrameShape( QFrame::HLine ); + lineAbove->setMaximumHeight( 1 ); + QFrame* lineAbove2 = new QFrame( this ); + lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + lineAbove2->setFrameShape( QFrame::HLine ); + lineAbove2->setMaximumHeight( 1 ); + QFrame* lineBelow = new QFrame( this ); + lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineBelow->setFrameShape( QFrame::HLine ); + lineBelow->setMaximumHeight( 1 ); + QFrame* lineBelow2 = new QFrame( this ); + lineBelow2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + lineBelow2->setFrameShape( QFrame::HLine ); + lineBelow2->setMaximumHeight( 1 ); + layout()->addWidget( m_header ); + layout()->addWidget( lineAbove ); + layout()->addWidget( lineAbove2 ); + layout()->addWidget( m_modeHeader ); if ( extraHeader ) layout()->addWidget( extraHeader ); + layout()->addWidget( lineBelow ); + layout()->addWidget( lineBelow2 ); layout()->addWidget( m_stack ); m_stack->addWidget( m_columnView ); -/* m_stack->addWidget( m_gridView ); + m_stack->addWidget( m_treeView ); + /* m_stack->addWidget( m_gridView ); m_stack->addWidget( m_trackView );*/ setCurrentMode( Columns ); connect( m_header, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) ); + + NewClosure( m_modeHeader, SIGNAL( flatClicked() ), const_cast< FlexibleTreeView* >( this ), SLOT( setCurrentMode( FlexibleTreeViewMode ) ), FlexibleTreeView::Columns )->setAutoDelete( false ); + NewClosure( m_modeHeader, SIGNAL( detailedClicked() ), const_cast< FlexibleTreeView* >( this ), SLOT( setCurrentMode( FlexibleTreeViewMode ) ), FlexibleTreeView::Flat )->setAutoDelete( false ); + NewClosure( m_modeHeader, SIGNAL( gridClicked() ), const_cast< FlexibleTreeView* >( this ), SLOT( setCurrentMode( FlexibleTreeViewMode ) ), FlexibleTreeView::Albums )->setAutoDelete( false ); } @@ -86,7 +120,7 @@ FlexibleTreeView::~FlexibleTreeView() void FlexibleTreeView::setGuid( const QString& guid ) { -// m_trackView->setGuid( guid ); + m_treeView->setGuid( guid ); m_columnView->setGuid( guid ); } @@ -126,17 +160,17 @@ FlexibleTreeView::setColumnView( ColumnView* view ) void -FlexibleTreeView::setGridView( GridView* view ) +FlexibleTreeView::setTreeView( TreeView* view ) { - if ( m_gridView ) + if ( m_treeView ) { - m_stack->removeWidget( m_gridView ); - delete m_gridView; + m_stack->removeWidget( m_treeView ); + delete m_treeView; } - view->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); +// view->setPlaylistInterface( m_columnView->proxyModel()->playlistInterface() ); - m_gridView = view; + m_treeView = view; m_stack->addWidget( view ); } @@ -154,7 +188,8 @@ FlexibleTreeView::setTreeModel( TreeModel* model ) // m_trackView->setPlayableModel( model ); m_columnView->setTreeModel( model ); -// m_gridView->setPlayableModel( model ); + m_treeView->setTreeModel( model ); + // m_gridView->setPlayableModel( model ); /* m_trackView->setSortingEnabled( false ); m_trackView->sortByColumn( -1 ); @@ -176,8 +211,7 @@ FlexibleTreeView::setCurrentMode( FlexibleTreeViewMode mode ) { case Flat: { - tDebug() << "m_trackView:" << m_trackView << m_stack->indexOf( m_trackView ); - m_stack->setCurrentWidget( m_trackView ); + m_stack->setCurrentWidget( m_treeView ); break; } @@ -189,7 +223,7 @@ FlexibleTreeView::setCurrentMode( FlexibleTreeViewMode mode ) case Albums: { - m_stack->setCurrentWidget( m_gridView ); +// m_stack->setCurrentWidget( m_gridView ); break; } } @@ -201,8 +235,7 @@ FlexibleTreeView::setCurrentMode( FlexibleTreeViewMode mode ) Tomahawk::playlistinterface_ptr FlexibleTreeView::playlistInterface() const { - return Tomahawk::playlistinterface_ptr(); - return m_trackView->proxyModel()->playlistInterface(); + return m_columnView->proxyModel()->playlistInterface(); } @@ -235,9 +268,9 @@ FlexibleTreeView::jumpToCurrentTrack() bool b = false; // note: the order of comparison is important here, if we'd write "b || foo" then foo will not be executed if b is already true! - b = m_trackView->jumpToCurrentTrack() || b; b = m_columnView->jumpToCurrentTrack() || b; - b = m_gridView->jumpToCurrentTrack() || b; +// b = m_trackView->jumpToCurrentTrack() || b; + b = m_treeView->jumpToCurrentTrack() || b; return b; } @@ -249,7 +282,8 @@ FlexibleTreeView::setFilter( const QString& pattern ) ViewPage::setFilter( pattern ); m_columnView->setFilter( pattern ); -/* m_gridView->setFilter( pattern ); + m_treeView->proxyModel()->setFilter( pattern ); + /* m_gridView->setFilter( pattern ); m_trackView->setFilter( pattern );*/ return true; @@ -260,7 +294,8 @@ void FlexibleTreeView::setEmptyTip( const QString& tip ) { m_columnView->setEmptyTip( tip ); -/* m_gridView->setEmptyTip( tip ); + m_treeView->setEmptyTip( tip ); + /* m_gridView->setEmptyTip( tip ); m_trackView->setEmptyTip( tip );*/ } diff --git a/src/libtomahawk/playlist/FlexibleTreeView.h b/src/libtomahawk/playlist/FlexibleTreeView.h index a88a53047a..1e1ec9471e 100644 --- a/src/libtomahawk/playlist/FlexibleTreeView.h +++ b/src/libtomahawk/playlist/FlexibleTreeView.h @@ -27,10 +27,12 @@ class QStackedWidget; class GridView; class TrackView; +class TreeView; class ColumnView; class TreeModel; +class ModeHeader; class PlaylistModel; -class FlexibleHeader; +class FilterHeader; class DLLEXPORT FlexibleTreeView : public QWidget, public Tomahawk::ViewPage { @@ -56,13 +58,13 @@ Q_OBJECT void setTemporaryPage( bool b ); ColumnView* columnView() const { return m_columnView; } - GridView* gridView() const { return m_gridView; } + TreeView* treeView() const { return m_treeView; } TrackView* trackView() const { return m_trackView; } void setGuid( const QString& guid ); void setColumnView( ColumnView* view ); - void setGridView( GridView* view ); + void setTreeView( TreeView* view ); void setTrackView( TrackView* view ); void setTreeModel( TreeModel* model ); @@ -83,11 +85,12 @@ private slots: void onWidgetDestroyed( QWidget* widget ); private: - FlexibleHeader* m_header; + FilterHeader* m_header; + ModeHeader* m_modeHeader; QPixmap m_pixmap; ColumnView* m_columnView; - GridView* m_gridView; + TreeView* m_treeView; TrackView* m_trackView; TreeModel* m_model; From a0b7c6e504a0936ed513a8e46f3cbfb1d8ddb567 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:35:40 +0200 Subject: [PATCH 335/565] * Init FilterHeader with parent. --- src/libtomahawk/playlist/FlexibleTreeView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/FlexibleTreeView.cpp b/src/libtomahawk/playlist/FlexibleTreeView.cpp index 74db192a2a..f5e32a3de1 100644 --- a/src/libtomahawk/playlist/FlexibleTreeView.cpp +++ b/src/libtomahawk/playlist/FlexibleTreeView.cpp @@ -41,7 +41,7 @@ using namespace Tomahawk; FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) : QWidget( parent ) - , m_header( new FilterHeader( 0 ) ) + , m_header( new FilterHeader( this ) ) , m_modeHeader( new ModeHeader( this ) ) , m_columnView( new ColumnView() ) , m_treeView( new TreeView() ) From ff007d8595575cb7905af0fa1bc62ffea52c0675 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:36:07 +0200 Subject: [PATCH 336/565] * Make FlexibleView use new header style - to be consolidated. --- src/libtomahawk/playlist/FlexibleView.cpp | 34 +++++++++++++++++++++-- src/libtomahawk/playlist/FlexibleView.h | 6 ++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/FlexibleView.cpp b/src/libtomahawk/playlist/FlexibleView.cpp index a3d73815c9..ad91c5d967 100644 --- a/src/libtomahawk/playlist/FlexibleView.cpp +++ b/src/libtomahawk/playlist/FlexibleView.cpp @@ -22,14 +22,17 @@ #include #include -#include "playlist/FlexibleHeader.h" +#include "widgets/FilterHeader.h" +#include "playlist/ModeHeader.h" #include "playlist/PlayableModel.h" #include "playlist/PlaylistModel.h" #include "playlist/TrackView.h" #include "playlist/GridView.h" #include "playlist/PlaylistLargeItemDelegate.h" #include "PlayableProxyModelPlaylistInterface.h" +#include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" +#include "utils/Closure.h" #include "utils/Logger.h" using namespace Tomahawk; @@ -37,7 +40,8 @@ using namespace Tomahawk; FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) : QWidget( parent ) - , m_header( new FlexibleHeader( this ) ) + , m_header( new FilterHeader( this ) ) + , m_modeHeader( new ModeHeader( this ) ) , m_trackView( new TrackView() ) , m_detailedView( new TrackView() ) , m_gridView( new GridView() ) @@ -62,9 +66,31 @@ FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) setLayout( new QVBoxLayout() ); TomahawkUtils::unmarginLayout( layout() ); + QFrame* lineAbove = new QFrame( this ); + lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineAbove->setFrameShape( QFrame::HLine ); + lineAbove->setMaximumHeight( 1 ); + QFrame* lineAbove2 = new QFrame( this ); + lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + lineAbove2->setFrameShape( QFrame::HLine ); + lineAbove2->setMaximumHeight( 1 ); + QFrame* lineBelow = new QFrame( this ); + lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineBelow->setFrameShape( QFrame::HLine ); + lineBelow->setMaximumHeight( 1 ); + QFrame* lineBelow2 = new QFrame( this ); + lineBelow2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + lineBelow2->setFrameShape( QFrame::HLine ); + lineBelow2->setMaximumHeight( 1 ); + layout()->addWidget( m_header ); + layout()->addWidget( lineAbove ); + layout()->addWidget( lineAbove2 ); + layout()->addWidget( m_modeHeader ); if ( extraHeader ) layout()->addWidget( extraHeader ); + layout()->addWidget( lineBelow ); + layout()->addWidget( lineBelow2 ); layout()->addWidget( m_stack ); m_stack->addWidget( m_trackView ); @@ -74,6 +100,10 @@ FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) setCurrentMode( Flat ); connect( m_header, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) ); + + NewClosure( m_modeHeader, SIGNAL( flatClicked() ), const_cast< FlexibleView* >( this ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Flat )->setAutoDelete( false ); + NewClosure( m_modeHeader, SIGNAL( detailedClicked() ), const_cast< FlexibleView* >( this ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Detailed )->setAutoDelete( false ); + NewClosure( m_modeHeader, SIGNAL( gridClicked() ), const_cast< FlexibleView* >( this ), SLOT( setCurrentMode( FlexibleViewMode ) ), FlexibleView::Grid )->setAutoDelete( false ); } diff --git a/src/libtomahawk/playlist/FlexibleView.h b/src/libtomahawk/playlist/FlexibleView.h index 9a4abaf4e7..bc367265b5 100644 --- a/src/libtomahawk/playlist/FlexibleView.h +++ b/src/libtomahawk/playlist/FlexibleView.h @@ -29,7 +29,8 @@ class GridView; class TrackView; class PlayableModel; class PlaylistModel; -class FlexibleHeader; +class FilterHeader; +class ModeHeader; class DLLEXPORT FlexibleView : public QWidget, public Tomahawk::ViewPage { @@ -83,7 +84,8 @@ private slots: void onWidgetDestroyed( QWidget* widget ); private: - FlexibleHeader* m_header; + FilterHeader* m_header; + ModeHeader* m_modeHeader; QPixmap m_pixmap; TrackView* m_trackView; From a883c7135f5763ccf95bd8354dc8e8dd7b8d0653 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:36:24 +0200 Subject: [PATCH 337/565] * Fixed HistoryWidget's extra header. --- src/libtomahawk/widgets/HistoryWidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/HistoryWidget.cpp b/src/libtomahawk/widgets/HistoryWidget.cpp index fcaaf1490f..3841b887a7 100644 --- a/src/libtomahawk/widgets/HistoryWidget.cpp +++ b/src/libtomahawk/widgets/HistoryWidget.cpp @@ -51,14 +51,14 @@ HistoryWidget::HistoryWidget( const source_ptr& source, QWidget* parent ) m_calendarTo->setDisplayFormat( "yyyy MMMM dd" ); // setting an empty style-sheet prevents the QDateEdits from adopting their parent's QPalette - QString calSheet = QString( "QDateEdit { }" ).arg( TomahawkStyle::PAGE_BACKGROUND.name() ); + QString calSheet = QString( "QDateEdit { }" ); m_calendarFrom->setStyleSheet( calSheet ); m_calendarTo->setStyleSheet( calSheet ); QPalette pal = m_header->palette(); pal.setColor( QPalette::Foreground, Qt::white ); pal.setColor( QPalette::Text, Qt::white ); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND.lighter() ); m_header->setPalette( pal ); m_header->setAutoFillBackground( true ); From b2ac6acb6ecc0ff3620265550e2f5fe6e1b10361 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 11:43:11 +0200 Subject: [PATCH 338/565] * Show 'kbps' next to the bitrate. --- src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp index f3b38ad5c7..c0e0a2e4b1 100644 --- a/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp +++ b/src/libtomahawk/playlist/ColumnViewPreviewWidget.cpp @@ -93,7 +93,7 @@ ColumnViewPreviewWidget::setQuery( const Tomahawk::query_ptr& query ) if ( query->numResults() ) { ui->yearValue->setText( QString::number( query->track()->year() ) ); - ui->bitrateValue->setText( QString::number( query->results().first()->bitrate() ) ); + ui->bitrateValue->setText( tr( "%1 kbps" ).arg( query->results().first()->bitrate() ) ); ui->durationValue->setText( TomahawkUtils::timeToString( query->track()->duration() ) ); ui->ageValue->setText( TomahawkUtils::ageToString( QDateTime::fromTime_t( query->results().first()->modificationTime() ) ) ); From cc62a458f5098450c46a024618585fb19d6d0a2c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 12:58:49 +0200 Subject: [PATCH 339/565] * Draw divider below BasicHeader. --- src/libtomahawk/playlist/FlexibleTreeView.cpp | 10 ------- src/libtomahawk/playlist/FlexibleView.cpp | 10 ------- src/libtomahawk/widgets/BasicHeader.cpp | 26 +++++++++++++++---- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/libtomahawk/playlist/FlexibleTreeView.cpp b/src/libtomahawk/playlist/FlexibleTreeView.cpp index f5e32a3de1..58163ea4f2 100644 --- a/src/libtomahawk/playlist/FlexibleTreeView.cpp +++ b/src/libtomahawk/playlist/FlexibleTreeView.cpp @@ -69,14 +69,6 @@ FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) setLayout( new QVBoxLayout() ); TomahawkUtils::unmarginLayout( layout() ); - QFrame* lineAbove = new QFrame( this ); - lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); - lineAbove->setFrameShape( QFrame::HLine ); - lineAbove->setMaximumHeight( 1 ); - QFrame* lineAbove2 = new QFrame( this ); - lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); - lineAbove2->setFrameShape( QFrame::HLine ); - lineAbove2->setMaximumHeight( 1 ); QFrame* lineBelow = new QFrame( this ); lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); lineBelow->setFrameShape( QFrame::HLine ); @@ -87,8 +79,6 @@ FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) lineBelow2->setMaximumHeight( 1 ); layout()->addWidget( m_header ); - layout()->addWidget( lineAbove ); - layout()->addWidget( lineAbove2 ); layout()->addWidget( m_modeHeader ); if ( extraHeader ) layout()->addWidget( extraHeader ); diff --git a/src/libtomahawk/playlist/FlexibleView.cpp b/src/libtomahawk/playlist/FlexibleView.cpp index ad91c5d967..0be8861bdc 100644 --- a/src/libtomahawk/playlist/FlexibleView.cpp +++ b/src/libtomahawk/playlist/FlexibleView.cpp @@ -66,14 +66,6 @@ FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) setLayout( new QVBoxLayout() ); TomahawkUtils::unmarginLayout( layout() ); - QFrame* lineAbove = new QFrame( this ); - lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); - lineAbove->setFrameShape( QFrame::HLine ); - lineAbove->setMaximumHeight( 1 ); - QFrame* lineAbove2 = new QFrame( this ); - lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); - lineAbove2->setFrameShape( QFrame::HLine ); - lineAbove2->setMaximumHeight( 1 ); QFrame* lineBelow = new QFrame( this ); lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); lineBelow->setFrameShape( QFrame::HLine ); @@ -84,8 +76,6 @@ FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) lineBelow2->setMaximumHeight( 1 ); layout()->addWidget( m_header ); - layout()->addWidget( lineAbove ); - layout()->addWidget( lineAbove2 ); layout()->addWidget( m_modeHeader ); if ( extraHeader ) layout()->addWidget( extraHeader ); diff --git a/src/libtomahawk/widgets/BasicHeader.cpp b/src/libtomahawk/widgets/BasicHeader.cpp index 00933f5f35..3868306d37 100644 --- a/src/libtomahawk/widgets/BasicHeader.cpp +++ b/src/libtomahawk/widgets/BasicHeader.cpp @@ -36,8 +36,11 @@ using namespace Tomahawk; BasicHeader::BasicHeader( QWidget* parent ) : QWidget( parent ) { + QLayout* l = new QVBoxLayout; + TomahawkUtils::unmarginLayout( l ); + setLayout( l ); + m_mainLayout = new QHBoxLayout; - setLayout( m_mainLayout ); m_imageLabel = new QLabel( this ); m_imageLabel->setFixedSize( 48, 48 ); @@ -58,7 +61,7 @@ BasicHeader::BasicHeader( QWidget* parent ) QPalette pal = palette(); pal.setColor( QPalette::Foreground, Qt::white ); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_LOWER ); m_captionLabel->setPalette( pal ); m_descriptionLabel->setPalette( pal ); @@ -86,10 +89,23 @@ BasicHeader::BasicHeader( QWidget* parent ) m_captionLabel->setGraphicsEffect( effect );*/ // m_descriptionLabel->setGraphicsEffect( effect ); - TomahawkUtils::unmarginLayout( layout() ); - layout()->setContentsMargins( 8, 4, 8, 4 ); + QFrame* lineAbove = new QFrame( this ); + lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineAbove->setFrameShape( QFrame::HLine ); + lineAbove->setMaximumHeight( 1 ); + QFrame* lineAbove2 = new QFrame( this ); + lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + lineAbove2->setFrameShape( QFrame::HLine ); + lineAbove2->setMaximumHeight( 1 ); + + l->addItem( m_mainLayout ); + l->addWidget( lineAbove ); + l->addWidget( lineAbove2 ); + + TomahawkUtils::unmarginLayout( m_mainLayout ); + m_mainLayout->setContentsMargins( 8, 4, 8, 4 ); setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); - setFixedHeight( 56 ); + setFixedHeight( 58 ); setAutoFillBackground( true ); setPalette( pal ); From 9cc6c1fb354dc9da0c09edaef6c80ac3b7c5be83 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 12:59:09 +0200 Subject: [PATCH 340/565] * Tweak page and header colors. --- src/libtomahawk/utils/TomahawkStyle.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index ee7c81ad31..f159d5365a 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -54,11 +54,11 @@ namespace TomahawkStyle static const QColor SELECTION_FOREGROUND = QColor( "#ffffff" ); static const QColor HEADER_UPPER = QColor( "#25292c" ); - static const QColor HEADER_LOWER = QColor( "#707070" ); + static const QColor HEADER_LOWER = QColor( "#1e1e1e" ); static const QColor HEADER_TEXT = QColor( "#eaeaea" ); static const QColor HEADER_HIGHLIGHT = QColor( "#333" ); - static const QColor PAGE_BACKGROUND = QColor( "#1e1e1e" ); + static const QColor PAGE_BACKGROUND = QColor( "#1e1e1e" ).lighter(); static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); From 2b6da8280586f24002d4c92b0dbfba6c53d8a518 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 13:00:02 +0200 Subject: [PATCH 341/565] * Give Dashboard a header. --- src/libtomahawk/widgets/Dashboard.cpp | 10 +++++++++- src/libtomahawk/widgets/Dashboard.h | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index b0fd778cf7..7aeb3c2f1a 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -33,9 +33,11 @@ #include "playlist/RecentlyPlayedModel.h" #include "playlist/dynamic/GeneratorInterface.h" #include "widgets/OverlayWidget.h" +#include "widgets/BasicHeader.h" +#include "utils/ImageRegistry.h" #include "utils/AnimatedSpinner.h" #include "utils/TomahawkStyle.h" -#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include @@ -51,10 +53,15 @@ using namespace Tomahawk; Dashboard::Dashboard( QWidget* parent ) : QWidget( parent ) , ui( new Ui::Dashboard ) + , m_header( new BasicHeader( this ) ) { QWidget* widget = new QWidget; ui->setupUi( widget ); + m_header->setPixmap( ImageRegistry::instance()->pixmap( RESPATH "images/dashboard.svg", QSize( 0, 0 ) ) ); + m_header->setCaption( tr( "Dashboard" ) ); + m_header->setDescription( tr( "An overview of your recent activity" ) ); + RecentPlaylistsModel* model = new RecentPlaylistsModel( HISTORY_PLAYLIST_ITEMS, this ); QPalette trackViewPal = ui->tracksView->palette(); @@ -102,6 +109,7 @@ Dashboard::Dashboard( QWidget* parent ) area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget( m_header ); layout->addWidget( area ); setLayout( layout ); TomahawkUtils::unmarginLayout( layout ); diff --git a/src/libtomahawk/widgets/Dashboard.h b/src/libtomahawk/widgets/Dashboard.h index 3083b6f1a2..69824cc8b3 100644 --- a/src/libtomahawk/widgets/Dashboard.h +++ b/src/libtomahawk/widgets/Dashboard.h @@ -37,6 +37,7 @@ class AlbumModel; class RecentlyPlayedModel; class OverlayWidget; +class BasicHeader; namespace Ui { @@ -116,6 +117,7 @@ private slots: private: Ui::Dashboard *ui; + BasicHeader* m_header; RecentlyPlayedModel* m_tracksModel; AlbumModel* m_recentAlbumsModel; Tomahawk::playlistinterface_ptr m_playlistInterface; From f4bd8959e174a867f1f834f4907f67f0f7de06ee Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 13:12:44 +0200 Subject: [PATCH 342/565] * Make hover-glow subtler. --- src/libtomahawk/playlist/GridItemDelegate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/GridItemDelegate.cpp b/src/libtomahawk/playlist/GridItemDelegate.cpp index 544db79350..06e5e66b26 100644 --- a/src/libtomahawk/playlist/GridItemDelegate.cpp +++ b/src/libtomahawk/playlist/GridItemDelegate.cpp @@ -146,11 +146,11 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, if ( m_hoverFaders.contains( index ) ) { const qreal pct = ( m_hoverFaders[ index ]->currentFrame() / 100.0 ); - opacity = 0.35 - pct * 0.35; + opacity = 0.15 - pct * 0.15; } else if ( m_hoverIndex == index ) { - opacity = 0.35; + opacity = 0.15; } if ( opacity > -1.0 ) From a95276d96018587f5cf654385e2a156fde30b025 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 13:35:11 +0200 Subject: [PATCH 343/565] * Auto-resize PlaylistWidget. --- src/libtomahawk/widgets/Dashboard.cpp | 20 ++++++++++++++++++-- src/libtomahawk/widgets/Dashboard.h | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index 7aeb3c2f1a..e74b37e2a3 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -43,7 +43,6 @@ #include #include - #define HISTORY_PLAYLIST_ITEMS 10 #define HISTORY_TRACK_ITEMS 15 @@ -243,7 +242,7 @@ void Dashboard::onPlaylistActivated( const QModelIndex& item ) { Tomahawk::playlist_ptr pl = item.data( RecentlyPlayedPlaylistsModel::PlaylistRole ).value< Tomahawk::playlist_ptr >(); - if( Tomahawk::dynplaylist_ptr dynplaylist = pl.dynamicCast< Tomahawk::DynamicPlaylist >() ) + if ( Tomahawk::dynplaylist_ptr dynplaylist = pl.dynamicCast< Tomahawk::DynamicPlaylist >() ) ViewManager::instance()->show( dynplaylist ); else ViewManager::instance()->show( pl ); @@ -412,5 +411,22 @@ void PlaylistWidget::setModel( QAbstractItemModel* model ) { QListView::setModel( model ); + + connect( model, SIGNAL( modelReset() ), SLOT( verifySize() ) ); + connect( model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( verifySize() ) ); + connect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), SLOT( verifySize() ) ); + emit modelChanged(); + verifySize(); } + + +void +PlaylistWidget::verifySize() +{ + if ( !model() ) + return; + + if ( model()->rowCount() > 0 ) + setFixedHeight( model()->rowCount() * itemDelegate()->sizeHint( QStyleOptionViewItem(), model()->index( 0, 0 ) ).height() + frameWidth() * 2 ); +} \ No newline at end of file diff --git a/src/libtomahawk/widgets/Dashboard.h b/src/libtomahawk/widgets/Dashboard.h index 69824cc8b3..f8372b2afa 100644 --- a/src/libtomahawk/widgets/Dashboard.h +++ b/src/libtomahawk/widgets/Dashboard.h @@ -73,6 +73,9 @@ Q_OBJECT signals: void modelChanged(); +private slots: + void verifySize(); + private: OverlayWidget* m_overlay; }; From 8dc56abbf646d9df69caf5848813a4ea7a628cfd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Wed, 12 Jun 2013 13:35:33 +0200 Subject: [PATCH 344/565] * Fixed RecentPlaylistsModel not respecting the set limit. --- src/libtomahawk/widgets/RecentPlaylistsModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/RecentPlaylistsModel.cpp b/src/libtomahawk/widgets/RecentPlaylistsModel.cpp index 1589510fa7..3da07ce994 100644 --- a/src/libtomahawk/widgets/RecentPlaylistsModel.cpp +++ b/src/libtomahawk/widgets/RecentPlaylistsModel.cpp @@ -67,7 +67,7 @@ RecentPlaylistsModel::onRefresh() emit loadingStarted(); DatabaseCommand_LoadAllSortedPlaylists* cmd = new DatabaseCommand_LoadAllSortedPlaylists( source_ptr() ); - cmd->setLimit( 15 ); + cmd->setLimit( m_maxPlaylists ); cmd->setSortOrder( DatabaseCommand_LoadAllPlaylists::ModificationTime ); cmd->setSortAscDesc( DatabaseCommand_LoadAllPlaylists::Descending ); connect( cmd, SIGNAL( done( QList ) ), this, SLOT( playlistsLoaded( QList ) ) ); From 81d2278857ab92f35bdf977760a29703a2a69f26 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Thu, 13 Jun 2013 02:17:08 +0200 Subject: [PATCH 345/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_bg.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_bn_IN.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_ca.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_ca@valencia.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_cs.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_da.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_de.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_el.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_en.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_es.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_fi.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_fr.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_gl.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_hi_IN.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_hu.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_id.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_it.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_ja.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_lt.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_pl.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_pt_BR.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_ru.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_sv.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_tr.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_zh_CN.ts | 145 +++++++++++++++++++++++++++++------ lang/tomahawk_zh_TW.ts | 145 +++++++++++++++++++++++++++++------ 27 files changed, 3267 insertions(+), 648 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index fea389f826..a74ef9c0ad 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -445,6 +445,80 @@ connect and stream from you? مسح
+ + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -701,15 +785,28 @@ connect and stream from you? مرشح...
+ + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. هذه المجموعة فارغة حاليا. - + This playlist is currently empty. Add some tracks to it and enjoy the music! هذه القائمة فارغة حاليا. أضف لها بعض الأغاني واستمتع للموسيقى! @@ -1230,48 +1327,48 @@ Password الدقة - + Perfect match تطابق تام - + Very good match تطابق جيد جدا - + Good match تطابق جيد - + Vague match تطابق مبهم - + Bad match تطابق سيء - + Very bad match تطابق سيء للغاية - + Not available غير متوفر - + Searching... قيد البحث... - + Name إسم @@ -4126,48 +4223,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox البريد الوارد - + Listening suggestions from your friends إقتراحات للإستماع من قبل اصدقاءك - - + + This playlist is empty! هذه المجموعة فارغة! - + SuperCollection سوبر كولكشن - + Combined libraries of all your online friends مكتبات مجمعة لكل اصحابك المتصلين - + Recently Played Tracks الأغاني التي إستمعت إليها مؤخرا - + Recently played tracks from all your friends جميع الأغاني التي استمع إليها أصدقائك مؤخرا - + Sorry, we could not find any recent plays! نعتذر، لم نستطيع إيجاد أغاني مسموعة مؤخرا! - + No listening suggestions here. لا إقتراحات للإستماع هنا. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 0c2778fd2e..c37e849664 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -444,6 +444,80 @@ connect and stream from you? Изчисти + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -536,17 +610,27 @@ Tomahawk създаде доклад относно това и изпращай Най-нови станции и списъци - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks Наскоро изпълнени - + No recently created playlists in your network. Няма наскоро създавани списъци в твоята мрежа - + Welcome to Tomahawk Здравей! @@ -706,15 +790,28 @@ Tomahawk създаде доклад относно това и изпращай Филтър... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Този списък е празен, в момента. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Този списък е празен в момента. Добави няколко песни и се наслади на музиката. @@ -1236,48 +1333,48 @@ Password Съвпадение - + Perfect match Абсолютно - + Very good match Много добро - + Good match Добро - + Vague match Горе-долу - + Bad match Лошо - + Very bad match Много лошо - + Not available Няма съвпадение - + Searching... Търсене... - + Name Име @@ -4143,49 +4240,49 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Входяща кутия - + Listening suggestions from your friends Предложения за слушане от приятелите ти - - + + This playlist is empty! Списъка е празен! - + SuperCollection Обща колекция /Сборен изглед от локалните и наличните в колекциите на приятелите ти изпълнения/ - + Combined libraries of all your online friends Обща колекция с всичките ми приятели на линия - + Recently Played Tracks Наскоро изпълени песни - + Recently played tracks from all your friends Наскоро изпълнени песни от всичките ти приятели - + Sorry, we could not find any recent plays! Съжалявам, но не откривам скорошни списъци - + No listening suggestions here. Няма предложения. diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 6f8a400eee..f0621fdf3d 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -444,6 +444,80 @@ connect and stream from you? + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -4106,48 +4203,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 6ef45b9570..65cd41514f 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -444,6 +444,80 @@ connect and stream from you? Neteja + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -1228,48 +1325,48 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... - + Name Nom @@ -4126,48 +4223,48 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! La llista de reproducció és buida - + SuperCollection SuperCol·lecció - + Combined libraries of all your online friends Biblioteques combinades de tots els amis en línia - + Recently Played Tracks Cançons Escoltades Recentment - + Recently played tracks from all your friends Cançons escoltades recentment pels amics - + Sorry, we could not find any recent plays! No s'ha trobat cap reproducció recent - + No listening suggestions here. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index e975fe6609..192812926c 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -444,6 +444,80 @@ connect and stream from you? Neteja + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -1228,48 +1325,48 @@ Password Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... - + Name Nom @@ -4126,48 +4223,48 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! La llista de reproducció és buida - + SuperCollection SuperCol·lecció - + Combined libraries of all your online friends Biblioteques combinades de tots els amis en línia - + Recently Played Tracks Cançons Escoltades Recentment - + Recently played tracks from all your friends Cançons escoltades recentment pels amics - + Sorry, we could not find any recent plays! No s'ha trobat cap reproducció recent - + No listening suggestions here. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 7034b731c4..b35a3959e3 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -445,6 +445,80 @@ se s vámi spojil? Vyprázdnit + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ se s vámi spojil? Nejnovější stanice a seznamy skladeb - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks Nedávno poslouchané skladby - + No recently created playlists in your network. Ve vaší síti nejsou žádné nedávno vytvořené seznamy skladeb. - + Welcome to Tomahawk Vítejte v Tomahawku @@ -701,15 +785,28 @@ se s vámi spojil? Filtr... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Tento seznam skladeb je nyní prázdný. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tento seznam skladeb je nyní prázdný. Přidejte do něj nějaké skladby a vychutnávejte hudbu! @@ -1230,48 +1327,48 @@ heslo Přesnost - + Perfect match Přesná shoda - + Very good match Velmi dobrá shoda - + Good match Dobrá shoda - + Vague match Mlhavá shoda - + Bad match Špatná shoda - + Very bad match Velmi špatná shoda - + Not available Nedostupné - + Searching... Hledá se... - + Name Název @@ -4125,48 +4222,48 @@ Kdykoli můžete odeslat novou seřizovací zprávu. ViewManager - + Inbox Doručené - + Listening suggestions from your friends Sledování návrhů od vašich přátel - - + + This playlist is empty! Tento seznam skladeb je prázdný! - + SuperCollection Supersbírka - + Combined libraries of all your online friends Spojená sbírka všech vašich přátel - + Recently Played Tracks Nedávno poslouchané skladby - + Recently played tracks from all your friends Naposledy poslouchané skladby všech vašich přátel - + Sorry, we could not find any recent plays! Promiňte, ale nepodařilo se najít žádné nedávno poslouchané skladby! - + No listening suggestions here. Žádné sledování návrhů. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 7df085fdaf..7e66cac375 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -444,6 +444,80 @@ connect and stream from you? Ryd + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -4108,48 +4205,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection SuperSamling - + Combined libraries of all your online friends Alle dine venners kombineret bibliotek - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 4038b715c8..e8ed4bdaff 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -445,6 +445,80 @@ erlauben sich mit dir zu verbinden? Leeren + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ erlauben sich mit dir zu verbinden? Neueste Stationen & Wiedergabelisten - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks Zuletzt gehörte Lieder - + No recently created playlists in your network. Es gibt keine kürzlich erstellten Wiedergabelisten in deinem Netzwerk. - + Welcome to Tomahawk Willkommen bei Tomahawk @@ -701,15 +785,28 @@ erlauben sich mit dir zu verbinden? Filter... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Diese Playlist ist momentan leer. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Diese Playlist ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! @@ -1230,48 +1327,48 @@ Passwort Treffsicherheit - + Perfect match Perfekt - + Very good match Sehr gut - + Good match Gut - + Vague match Vage - + Bad match Schlecht - + Very bad match Sehr schlecht - + Not available Nicht verfügbar - + Searching... Suche läuft... - + Name Name @@ -4120,48 +4217,48 @@ Du kannst jederzeit eine neue Sync-Nachricht abschicken. ViewManager - + Inbox Posteingang - + Listening suggestions from your friends Hören sie sich die Vorschläge ihrer Freunde an - - + + This playlist is empty! Diese Playlist ist leer! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Kombinierte Sammlung all deiner Freunde - + Recently Played Tracks Zuletzt gehörte Lieder - + Recently played tracks from all your friends Zuletzt gehörte Lieder all deiner Freunde - + Sorry, we could not find any recent plays! Es konnten keine zuletzt gehörten Songs gefunden werden! - + No listening suggestions here. Es gibt keine Vorschläge hier. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index a4388d8578..e33db86275 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -444,6 +444,80 @@ connect and stream from you? Καθαρισμός + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? Τελευταίοι Σταθμοί & Λίστες Αναπαραγωγής - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks Τελευταίες αναπαραγωγές κομματιών - + No recently created playlists in your network. Δεν δημιουργηθηκαν πρόσφατα λίστες αναπαραγωγής στο δίκτυό σας. - + Welcome to Tomahawk Καλως ήρθατε στο Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? Φίλτρο... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Αυτη η λιστα αναπαραγωγης ειναι αδεια. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Αυτή η λίστα αναπαραγωγής είναι άδεια προς το παρόν. Προσθέστε μερικά κομμάτια σε αυτήν και απολαύστε την μουσική! @@ -1229,48 +1326,48 @@ Password Ακρίβεια - + Perfect match Τέλειο ταίριασμα - + Very good match Πολύ καλό ταίριασμα - + Good match Καλό ταίριασμα - + Vague match Ασαφές ταίριασμα - + Bad match Κακό ταίριασμα - + Very bad match Πολύ κακό ταίριασμα - + Not available Μη διαθέσιμο - + Searching... Αναζήτηση... - + Name Όνομα @@ -4121,48 +4218,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Εισερχόμενα - + Listening suggestions from your friends Προτάσεις προς ακρόαση από τους φίλους σας - - + + This playlist is empty! Αυτη η λιστα αναπαραγωγης ειναι αδεια. - + SuperCollection ΥπερΣυλλογή - + Combined libraries of all your online friends Συνδυασμένες βιβλιοθήκες όλων των online φίλων σας - + Recently Played Tracks Τελευταίες Αναπαραγωγές Κομματιών - + Recently played tracks from all your friends Τελευταίες αναπαραγωγές από όλους τους φίλους σας - + Sorry, we could not find any recent plays! Συγγνωμη, δεν βρεθηκαν προσφατες αναπαραγωγες! - + No listening suggestions here. Δεν υπάρχουν προτάσεις για ακρόαση εδώ. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index b8ad95dffb..d27861af5a 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -445,6 +445,80 @@ connect and stream from you? Clear + + ColumnItemDelegate + + + Unknown + Unknown + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + Sorry, your filter '%1' did not match any results. + + + + ColumnViewPreviewWidget + + + Composer + Composer + + + + Age + Age + + + + Year + Year + + + + Duration + Duration + + + + Bitrate + Bitrate + + + + Composer: + Composer: + + + + Duration: + Duration: + + + + Bitrate: + Bitrate: + + + + Year: + Year: + + + + Age: + Age: + + + + %1 kbps + %1 kbps + + ContextWidget @@ -531,17 +605,27 @@ connect and stream from you? Newest Stations & Playlists - + + Dashboard + Dashboard + + + + An overview of your recent activity + An overview of your recent activity + + + Recently played tracks Recently played tracks - + No recently created playlists in your network. No recently created playlists in your network. - + Welcome to Tomahawk Welcome to Tomahawk @@ -701,15 +785,28 @@ connect and stream from you? Filter... + + FlexibleTreeView + + + This playlist is currently empty. + This playlist is currently empty. + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + This playlist is currently empty. Add some tracks to it and enjoy the music! + + FlexibleView - + This playlist is currently empty. This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1230,48 +1327,48 @@ Password Accuracy - + Perfect match Perfect match - + Very good match Very good match - + Good match Good match - + Vague match Vague match - + Bad match Bad match - + Very bad match Very bad match - + Not available Not available - + Searching... Searching... - + Name Name @@ -4129,48 +4226,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Inbox - + Listening suggestions from your friends Listening suggestions from your friends - - + + This playlist is empty! This playlist is empty! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Combined libraries of all your online friends - + Recently Played Tracks Recently Played Tracks - + Recently played tracks from all your friends Recently played tracks from all your friends - + Sorry, we could not find any recent plays! Sorry, we could not find any recent plays! - + No listening suggestions here. No listening suggestions here. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index c6c3216d36..0f1e4e63f9 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -445,6 +445,80 @@ conectarse a usted y transmitir música? Limpiar + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ conectarse a usted y transmitir música? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -701,15 +785,28 @@ conectarse a usted y transmitir música? Filtrar… + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Lista de reproducción vacía - + This playlist is currently empty. Add some tracks to it and enjoy the music! Lista de reproducción vacía. ¡Añada pistas y disfrute de la música! @@ -1229,48 +1326,48 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia muy buena - + Good match Coincidencia buena - + Vague match Coincidencia vaga - + Bad match Mala coincidencia - + Very bad match Muy mala coincidencia - + Not available No disponible - + Searching... - + Name Título @@ -4127,48 +4224,48 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Lista de reproducción vacía - + SuperCollection Supercolección - + Combined libraries of all your online friends Colecciones combinadas de todos sus amigos - + Recently Played Tracks Pistas reproducidas recientemente - + Recently played tracks from all your friends Temas escuchados recientemente por mis amigos - + Sorry, we could not find any recent plays! No hay reproducciones recientes - + No listening suggestions here. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index cd41f9351c..6cba2b55c1 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -445,6 +445,80 @@ yhdistää ja toistaa sinulta virtaa? Tyhjennä + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ yhdistää ja toistaa sinulta virtaa? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -701,15 +785,28 @@ yhdistää ja toistaa sinulta virtaa? Suodata... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Tämä soittolista on parhaillaan tyhjä. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tämä soittolista on parhaillaan tyhjä. Lisää kappaleita ja nauti musiikista! @@ -1230,48 +1327,48 @@ salasana Tarkkuus - + Perfect match Täysosuma - + Very good match Erittäin hyvä osuma - + Good match Hyvä osuma - + Vague match Epämääräinen osuma - + Bad match Kehno osuma - + Very bad match Erittäin kehno osuma - + Not available Ei saatavilla - + Searching... Haetaan... - + Name Nimi @@ -4132,48 +4229,48 @@ Voit lähettää synkronointiviestin uudelleen millä hetkellä hyvänsä lähet ViewManager - + Inbox Saapuneet - + Listening suggestions from your friends Kaveriesi lähettämät kuunteluehdotukset - - + + This playlist is empty! Tämä soittolista on tyhjä! - + SuperCollection Superkokoelma - + Combined libraries of all your online friends Kaikkien verkkokaveriesi yhdistetyt kirjastot - + Recently Played Tracks Viime aikoina kuunnellut kappaleet - + Recently played tracks from all your friends Kaikkien kaveriesi viime aikoina kuuntelemat kappaleet - + Sorry, we could not find any recent plays! Valitettavasti emme löytäneet yhtään viimeaikaisia soittoja! - + No listening suggestions here. Ei kuunteluehdotuksia. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index b80edab2bc..435c77c838 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -445,6 +445,80 @@ de se connecter et streamer de vous? Vider + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ de se connecter et streamer de vous? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -701,15 +785,28 @@ de se connecter et streamer de vous? Filtrer... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Cette liste de lecture est vide. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Cette liste de lecture est vide. Ajoutez des morceaux et profitez de la musique ! @@ -1229,48 +1326,48 @@ Password Précision - + Perfect match Correspondance parfaite - + Very good match Très bonne correspondance - + Good match Bonne correspondance - + Vague match Vague correspondance - + Bad match Mauvaise correspondance - + Very bad match Très mauvaise correspondance - + Not available Indisponible - + Searching... Recherche... - + Name Nom @@ -4124,48 +4221,48 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env ViewManager - + Inbox Boîte de réception - + Listening suggestions from your friends Suggestions d'écoute de vos amis - - + + This playlist is empty! Cette liste de lecture est vide! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Collections regroupant toutes celles de vos amis en ligne - + Recently Played Tracks Derniers titres joués - + Recently played tracks from all your friends Derniers titres joués par vos amis - + Sorry, we could not find any recent plays! Désolé, aucune piste récemment jouée n'a pu être trouvée ! - + No listening suggestions here. Aucune suggestion d'écoute. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index f439aa2da0..1cf1b9bb5b 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -444,6 +444,80 @@ connect and stream from you? Limpar + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? Filtro... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Esta lista de reprodución está baleira. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodución está baleira. Engádelle algunhas pistas para gozar da música! @@ -1228,48 +1325,48 @@ Password Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia moi boa - + Good match Boa coincidencia - + Vague match Parcialmente coincidente - + Bad match Mala coincidencia - + Very bad match Nada coincidentes - + Not available Non está dispoñíbel - + Searching... - + Name Nome @@ -4126,48 +4223,48 @@ Podes reenviar e sincronizar as mensaxes en calquera momento simplemente enviand ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Esta lista de reprodución está baleira! - + SuperCollection Supercolección - + Combined libraries of all your online friends Bibliotecas combinadas de todas as túas amizades - + Recently Played Tracks Pistas recentemente reproducidas - + Recently played tracks from all your friends Pistas escoitadas recentemente polas túas amizades - + Sorry, we could not find any recent plays! Non se atoparan reproducións recentes! - + No listening suggestions here. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index e303aeb13d..5a15fa1cd8 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -444,6 +444,80 @@ connect and stream from you? मिटा दो + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -4106,48 +4203,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index da32acf95d..82ea682a32 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -444,6 +444,80 @@ connect and stream from you? Törlés + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name Név @@ -4106,48 +4203,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Ez a lejátszólista üres! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks Mostanában játszott zeneszámok - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 3d8a75c981..08d1e215ac 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -444,6 +444,80 @@ connect and stream from you? + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -4106,48 +4203,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index ea2be3f20b..7f02f064d5 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -444,6 +444,80 @@ connect and stream from you? Cancella + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? Filtra... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Questa playlist al momento è vuota. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Questa playlist al momento è vuota. Aggiungi qualche traccia e goditi la musica @@ -1229,48 +1326,48 @@ temporanea Precisione - + Perfect match Abbinamento perfetto - + Very good match Abbinamento molto buono - + Good match Buon abbinamento - + Vague match Vaga corrispondenza - + Bad match Brutto abbinamento - + Very bad match Pessimo abbinamento - + Not available Non disponibile - + Searching... Sto cercando... - + Name Nome @@ -4108,48 +4205,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Inbox - + Listening suggestions from your friends Suggerimenti per l'ascolto dai tuoi amici. - - + + This playlist is empty! Questa playlist è vuota! - + SuperCollection Supercollezione - + Combined libraries of all your online friends Collezioni combinate di tutti i tuoi amici online - + Recently Played Tracks Tracce ascoltate di recente - + Recently played tracks from all your friends Tracce ascoltate di recente dai tuoi amici - + Sorry, we could not find any recent plays! Spiacente, non sono riuscito a trovare nessuna riproduzione recente! - + No listening suggestions here. Nessun suggerimento musicale qui. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index f6ce31fb62..df2a85999a 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -444,6 +444,80 @@ connect and stream from you? クリアー + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -701,15 +785,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. このプレイリストには何も入っていません。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! プレイリストには何も入っていません。トラックを追加して、音楽を楽しみましょう! @@ -1229,48 +1326,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name 名前 @@ -4124,48 +4221,48 @@ Twitterを使っている友達にTomahawkを接続したいなら、ツイー ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! このプレイリストには何も入っていません。 - + SuperCollection スーパーコレクション - + Combined libraries of all your online friends オンラインの友達全員のライブラリ - + Recently Played Tracks 最近再生したトラック - + Recently played tracks from all your friends 友達の最近再生したトラック - + Sorry, we could not find any recent plays! 最近の再生した項目が見つかりませんでした。 - + No listening suggestions here. diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 950e28bea8..87f373b08f 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -444,6 +444,80 @@ connect and stream from you? Valyti + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name Vardas @@ -4106,48 +4203,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection Super kolekcija - + Combined libraries of all your online friends Jungtinė visų Jūsų prisijungusių draugų kolekcija - + Recently Played Tracks Neseniai groti takeliai - + Recently played tracks from all your friends Visų Jūsų draugų neseniai groti takeliai - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 77c18c33f7..c1c3361811 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -445,6 +445,80 @@ połączyć się i strumieniować od ciebie? Wyczyść + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ połączyć się i strumieniować od ciebie? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -701,15 +785,28 @@ połączyć się i strumieniować od ciebie? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1229,48 +1326,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -4121,48 +4218,48 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection SuperKolekcja - + Combined libraries of all your online friends Połączone biblioteki wszystkich twoich znajomych online - + Recently Played Tracks Ostatnio odtwarzane utwory - + Recently played tracks from all your friends Utwory ostatnio odtwarzane przez twoich znajomych - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 6f9f22d487..9cd3169f4d 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -445,6 +445,80 @@ se conecte e faça o stream de você? Limpar + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ se conecte e faça o stream de você? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -701,15 +785,28 @@ se conecte e faça o stream de você? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Esta lista de reprodução está vazia no momento. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodução está vazia no momento. Adicione algumas faixas a ela e aproveite a música! @@ -1229,48 +1326,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name Nome @@ -4121,48 +4218,48 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Essa lista de reprodução está vazia! - + SuperCollection SuperColeção - + Combined libraries of all your online friends Bibliotecas combinadas de todos os seus amigos online - + Recently Played Tracks Faixas Reproduzidas Recentemente - + Recently played tracks from all your friends Faixas reproduzidas recentemente por todos os seus amigos - + Sorry, we could not find any recent plays! Desculpe, não foi possível encontrar reproduções recentes! - + No listening suggestions here. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 1856ffacd4..7460e2dda9 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -448,6 +448,80 @@ connect and stream from you? Очистить + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -534,17 +608,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -704,15 +788,28 @@ connect and stream from you? Фильтр... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Плейлист пуст - + This playlist is currently empty. Add some tracks to it and enjoy the music! Этот плейлист пуст. Добавьте какие-нибудь песни и наслаждайтесь музыкой! @@ -1232,48 +1329,48 @@ Password Совпадение - + Perfect match Превосходное - + Very good match Очень Хорошое - + Good match Хорошое - + Vague match Расплывчатое - + Bad match Плохое совпадение - + Very bad match Очень плохое совпадение - + Not available Недоступно - + Searching... - + Name Имя @@ -4127,48 +4224,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Плейлист пуст. - + SuperCollection Общая Коллекция - + Combined libraries of all your online friends Комбинированная библиотека всех ваших друзей онлайн - + Recently Played Tracks Последние Воспроизводимые Песни - + Recently played tracks from all your friends Последние воспроизводимые песни все ваших друзей - + Sorry, we could not find any recent plays! К сожалению, мы не смогли найти никаких воспроизвидений песен! - + No listening suggestions here. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index aee8c68317..8f54aed69b 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -445,6 +445,80 @@ ansluta och strömma från dig? Töm + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -531,17 +605,27 @@ ansluta och strömma från dig? Nyaste Stationer och Spellistor - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks Nyligen uppspelade spår - + No recently created playlists in your network. Det finns inga nyligen skapade spellistor på ditt nätverk - + Welcome to Tomahawk Välkommen till Tomahawk @@ -701,15 +785,28 @@ ansluta och strömma från dig? Filter... + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. Spellistan är för närvarande tom. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Spellistan är för tillfället tom. Lägg till några låtar och avnjut musiken! @@ -1230,48 +1327,48 @@ Password Exakthet - + Perfect match Perfekt matchning - + Very good match Mycket bra matchning - + Good match Bra matchning - + Vague match Svag matchning - + Bad match Dålig matchning - + Very bad match Väldigt dålig matchning - + Not available Inte tillgänglig - + Searching... Söker... - + Name Namn @@ -4127,48 +4224,48 @@ Du kan skicka om ett synkat meddelande när som helst genom att skicka ett tweet ViewManager - + Inbox Inkorg - + Listening suggestions from your friends Lyssnar-rekommendationer från dina vänner - - + + This playlist is empty! Spellistan är tom! - + SuperCollection Supersamling - + Combined libraries of all your online friends Kombinerat bibliotek av alla dina vänner online - + Recently Played Tracks Senast spelade spår - + Recently played tracks from all your friends Alla dina vänners senast spelade spår - + Sorry, we could not find any recent plays! Tyvärr! Det gick inte hitta några nyligen spelade spår - + No listening suggestions here. Det finns inga rekommendationer här. diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 36eab372ae..844bdaef80 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -444,6 +444,80 @@ connect and stream from you? Temizle + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -4106,48 +4203,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index d3ffbf74ab..636b6a87a1 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -444,6 +444,80 @@ connect and stream from you? 清除 + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. 当前播放列表为空。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password 准确度 - + Perfect match 完美匹配 - + Very good match 极高匹配 - + Good match 高匹配 - + Vague match 模糊匹配 - + Bad match 低匹配 - + Very bad match 极低匹配 - + Not available - + Searching... - + Name 名字 @@ -4122,48 +4219,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! 当前播放列表为空! - + SuperCollection 超级收藏 - + Combined libraries of all your online friends 当前在线的朋友的音乐库集合 - + Recently Played Tracks 最近播放歌曲 - + Recently played tracks from all your friends 所有朋友最近播放的歌曲 - + Sorry, we could not find any recent plays! 对不起,找不到任何最近播放项目! - + No listening suggestions here. diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index ad1c4d2e5d..9f38551b0a 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -444,6 +444,80 @@ connect and stream from you? 清除 + + ColumnItemDelegate + + + Unknown + + + + + ColumnView + + + Sorry, your filter '%1' did not match any results. + + + + + ColumnViewPreviewWidget + + + Composer + + + + + Age + + + + + Year + + + + + Duration + + + + + Bitrate + + + + + Composer: + + + + + Duration: + + + + + Bitrate: + + + + + Year: + + + + + Age: + + + + + %1 kbps + + + ContextWidget @@ -530,17 +604,27 @@ connect and stream from you? - + + Dashboard + + + + + An overview of your recent activity + + + + Recently played tracks - + No recently created playlists in your network. - + Welcome to Tomahawk @@ -700,15 +784,28 @@ connect and stream from you? + + FlexibleTreeView + + + This playlist is currently empty. + + + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + + + FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1228,48 +1325,48 @@ Password - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - + Name @@ -4106,48 +4203,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection 超級收藏 - + Combined libraries of all your online friends 聯合您所有線上朋友的音樂庫 - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. From 022efd5503bbe9cc893e19276daac621392e1440 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 09:04:41 +0200 Subject: [PATCH 346/565] * Moved stylesheet methods into TomahawkStyle. --- data/images/widget-border.png | Bin 1092 -> 0 bytes src/libtomahawk/utils/TomahawkStyle.cpp | 35 +++++++++++++++++++++ src/libtomahawk/utils/TomahawkStyle.h | 9 ++++++ src/libtomahawk/utils/TomahawkUtilsGui.cpp | 25 --------------- src/libtomahawk/utils/TomahawkUtilsGui.h | 2 -- 5 files changed, 44 insertions(+), 27 deletions(-) delete mode 100644 data/images/widget-border.png diff --git a/data/images/widget-border.png b/data/images/widget-border.png deleted file mode 100644 index 227e0f70a1baaff725aca070491a766a1d257107..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1092 zcmeAS@N?(olHy`uVBq!ia0vp^4}jQ#gAGVB8&>l(FtBiYx;TbZFutAMop;57$8~oA zSC(qP&+dQ!mn$j;$S2;pHa)@DXm&~URw4CC*XM0KK2NUb<~IZ1%ip`ZyLYGUpJi(@ zd)Cq7io#!OUdQ!zMGJi0YrHjW-?<(0R=-}+z4u;h_12r)pSyoGl{bDJ7yJF$oTB{m zds0eIRqK3RwOMQPto5~SKezs}k?t{?cm4X_U46Ii#eF*w^Xra({P*hD{A+(~x>;IN zllg6JWXJn`H-k4pGSe$A;-hP(+M^DN>=fl~LJx%-^KRA{!)C$jJVdUsw zU=rbC;`qU-;9y^@=H$R4upmId;ebB_Ne}IO}w-i=XVB!eS;81v=-oWtp z%uEhOL5Bt>M+QcFMwS=p=Uhgu#v9<82b`tnKHJYKtoWf`aKbgc+vjDwKZKu^n^l`* zW_tTm&RY4ecBT6-?pgb@c>n!t^PgY&`q6Lh=i*ntb6<*YUAWQztX^<~dG5#GrJpzc z{H3$E!29>{dHuTr`vd>Yko#)4_^Y9@yX^^m;TxY0>6QPSZMp3JiZtE*(@sb0T>bLR zx%_&fV2f1#>{g}svu#aJFW=Wux;I4pxA)fS^J~6FpMJXMOLW#}pRdm^Exz}9?dyA| zH!WYj`~0mvYj}$q5 z*QFQl|FvfJ`}2=aoX>u?v*Yv0Szk))x2$`<$?i{7-W2({XDzc!@7795zUwmW~c?(9>m>Lus@zEYka?`3*D`uumLdFzYAemsB9 Zudh}rJ}GFqGq7}E@O1TaS?83{1OQWU #include #include +#include +#include void @@ -157,3 +159,36 @@ TomahawkStyle::drawArrow( QStyle::PrimitiveElement element, QPainter* p, const Q int yOffset = r.y() + ( r.height() - size ) / 2; p->drawPixmap( xOffset, yOffset, pixmap ); } + + +void +TomahawkStyle::stylePageFrame( QFrame* frame ) +{ + frame->setStyleSheet( QString( "QFrame#%1 { background-color: %2; border: 1px solid white; border-radius: 3px; }" ) + .arg( frame->objectName() ) + .arg( TomahawkStyle::PAGE_ITEM_BACKGROUND.name() ) ); +} + + +void +TomahawkStyle::styleScrollBar( QScrollBar* scrollBar ) +{ + scrollBar->setStyleSheet( + "QScrollBar:horizontal { background-color: transparent; }" + "QScrollBar::handle:horizontal { border-height: 9px; margin-bottom: 6px;" + "border-image: url(" RESPATH "images/scrollbar-horizontal-handle.png) 3 3 3 3 stretch stretch;" + "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" + "QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { width: 0px; height: 0px; background: none; }" + "QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; height: 0px; background: none; }" + "QScrollBar:left-arrow:horizontal, QScrollBar::right-arrow:horizontal {" + "border: 0px; width: 0px; height: 0px; background: none; background-color: transparent; }" + + "QScrollBar:vertical { background-color: transparent; }" + "QScrollBar::handle:vertical { border-width: 9px; margin-right: 6px;" + "border-image: url(" RESPATH "images/scrollbar-vertical-handle.png) 3 3 3 3 stretch stretch;" + "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" + "QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { width: 0px; height: 0px; background: none; }" + "QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { width: 0px; height: 0px; background: none; }" + "QScrollBar:up-arrow:vertical, QScrollBar::down-arrow:vertical {" + "border: 0px; width: 0px; height: 0px; background: none; background-color: transparent; }" ); +} diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index f159d5365a..6016a6e97f 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -26,6 +26,9 @@ #include #include +class QFrame; +class QScrollBar; + namespace TomahawkStyle { /** @@ -41,6 +44,9 @@ namespace TomahawkStyle */ DLLEXPORT void drawArrow( QStyle::PrimitiveElement, QPainter* painter, const QStyleOption* opt ); + DLLEXPORT void stylePageFrame( QFrame* frame ); + DLLEXPORT void styleScrollBar( QScrollBar* scrollBar ); + static const QColor BORDER_LINE = QColor( "#8c8c8c" ); static const QColor POPUP_BACKGROUND = QColor( "#ffffff" ); static const QColor POPUP_OSX_BACKGROUND = QColor( "#D6E3F1" ); @@ -58,6 +64,9 @@ namespace TomahawkStyle static const QColor HEADER_TEXT = QColor( "#eaeaea" ); static const QColor HEADER_HIGHLIGHT = QColor( "#333" ); + static const QColor PAGE_TEXT = Qt::gray; + static const QColor PAGE_ITEM_BACKGROUND = QColor( "#1e1e1e" ).lighter( 290 ); + static const QColor PAGE_FOREGROUND = QColor( "#ffffff" ); static const QColor PAGE_BACKGROUND = QColor( "#1e1e1e" ).lighter(); static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index f09970ad22..708479940e 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -818,30 +817,6 @@ drawRoundedButton( QPainter* painter, const QRect& btnRect, const QColor& color, } -void -styleScrollBar( QScrollBar* scrollBar ) -{ - scrollBar->setStyleSheet( - "QScrollBar:horizontal { background-color: transparent; }" - "QScrollBar::handle:horizontal { border-height: 9px; margin-bottom: 6px;" - "border-image: url(" RESPATH "images/scrollbar-horizontal-handle.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" - "QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { width: 0px; height: 0px; background: none; }" - "QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; height: 0px; background: none; }" - "QScrollBar:left-arrow:horizontal, QScrollBar::right-arrow:horizontal {" - "border: 0px; width: 0px; height: 0px; background: none; background-color: transparent; }" - - "QScrollBar:vertical { background-color: transparent; }" - "QScrollBar::handle:vertical { border-width: 9px; margin-right: 6px;" - "border-image: url(" RESPATH "images/scrollbar-vertical-handle.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" - "QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { width: 0px; height: 0px; background: none; }" - "QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { width: 0px; height: 0px; background: none; }" - "QScrollBar:up-arrow:vertical, QScrollBar::down-arrow:vertical {" - "border: 0px; width: 0px; height: 0px; background: none; background-color: transparent; }" ); -} - - QPixmap createTiledPixmap( int width, int height, const QImage& inputTile ) { diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.h b/src/libtomahawk/utils/TomahawkUtilsGui.h index 1c3d872dac..109edc67db 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.h +++ b/src/libtomahawk/utils/TomahawkUtilsGui.h @@ -39,7 +39,6 @@ class QPixmap; class QLayout; class QPalette; class QRect; -class QScrollBar; namespace TomahawkUtils { @@ -68,7 +67,6 @@ namespace TomahawkUtils DLLEXPORT void prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, PlayableItem* item ); DLLEXPORT void drawRoundedButton( QPainter* painter, const QRect& btnRect, const QColor& color, const QColor &gradient1bottom = QColor(), const QColor& gradient2top = QColor(), const QColor& gradient2bottom = QColor() ); - DLLEXPORT void styleScrollBar( QScrollBar* scrollBar ); DLLEXPORT QPixmap defaultPixmap( ImageType type, ImageMode mode = TomahawkUtils::Original, const QSize& size = QSize( 0, 0 ) ); DLLEXPORT QPixmap createTiledPixmap( int width, int height, const QImage& src ); From 815ec7cbe2aee97ad69152eaeaa3bdf0b08ce80e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 09:06:43 +0200 Subject: [PATCH 347/565] * Use TomahawkStyle's methods to apply stylesheets. --- src/libtomahawk/widgets/Dashboard.cpp | 32 +++++++------------ .../widgets/infowidgets/AlbumInfoWidget.cpp | 24 ++++++-------- .../widgets/infowidgets/ArtistInfoWidget.cpp | 29 ++++++----------- .../widgets/infowidgets/TrackInfoWidget.cpp | 11 +++---- 4 files changed, 35 insertions(+), 61 deletions(-) diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index e74b37e2a3..b024965c16 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -64,8 +64,8 @@ Dashboard::Dashboard( QWidget* parent ) RecentPlaylistsModel* model = new RecentPlaylistsModel( HISTORY_PLAYLIST_ITEMS, this ); QPalette trackViewPal = ui->tracksView->palette(); - trackViewPal.setColor( QPalette::Foreground, Qt::white ); - trackViewPal.setColor( QPalette::Text, Qt::white ); + trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); @@ -101,6 +101,7 @@ Dashboard::Dashboard( QWidget* parent ) area->setWidget( widget ); QPalette pal = palette(); + // background: qradialgradient(cx: 0.5, cy: -1.8, fx: 0.5, fy: 0, radius: 2, stop: 0 %1, stop: 1 %2); pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); area->setPalette( pal ); area->setAutoFillBackground( true ); @@ -113,8 +114,8 @@ Dashboard::Dashboard( QWidget* parent ) setLayout( layout ); TomahawkUtils::unmarginLayout( layout ); - TomahawkUtils::styleScrollBar( ui->playlistWidget->verticalScrollBar() ); - TomahawkUtils::styleScrollBar( ui->additionsView->verticalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->playlistWidget->verticalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->additionsView->verticalScrollBar() ); QFont f; f.setBold( true ); @@ -122,30 +123,21 @@ Dashboard::Dashboard( QWidget* parent ) ui->tracksView->setMinimumWidth( fm.width( tr( "Recently played tracks" ) ) * 2 ); QPalette p = ui->label->palette(); - p.setColor( QPalette::Foreground, Qt::white ); - p.setColor( QPalette::Text, Qt::gray ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TEXT ); ui->label->setPalette( p ); ui->label_2->setPalette( p ); ui->label_3->setPalette( p ); ui->playlistWidget->setStyleSheet( "QListView { background-color: transparent; }" ); - ui->playlistFrame->setStyleSheet( "QFrame#playlistFrame { background-color: transparent; }" - "QFrame#playlistFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->playlistFrame ); ui->additionsView->setStyleSheet( "QListView { background-color: transparent; }" ); - ui->additionsFrame->setStyleSheet( "QFrame#additionsFrame { background-color: transparent; }" - "QFrame#additionsFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); - - ui->tracksView->setStyleSheet( "QTreeView#tracksView { background-color: transparent; }" ); - ui->trackFrame->setStyleSheet( "QFrame#trackFrame { background-color: transparent; }" - "QFrame#trackFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->additionsFrame ); + + ui->tracksView->setStyleSheet( "QTreeView { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->trackFrame ); MetaPlaylistInterface* mpl = new MetaPlaylistInterface(); mpl->addChildInterface( ui->tracksView->playlistInterface() ); diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index ccd971a329..36d969932c 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -59,8 +59,8 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par m_tracksModel->setMode( Mixed ); QPalette trackViewPal = ui->tracks->palette(); - trackViewPal.setColor( QPalette::Foreground, Qt::white ); - trackViewPal.setColor( QPalette::Text, Qt::white ); + trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); @@ -91,11 +91,11 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par ui->biography->setFrameShape( QFrame::NoFrame ); ui->biography->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - TomahawkUtils::styleScrollBar( ui->biography->verticalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); QPalette p = ui->biography->palette(); - p.setColor( QPalette::Foreground, Qt::white ); - p.setColor( QPalette::Text, Qt::white ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TEXT ); ui->biography->setPalette( p ); ui->label->setPalette( p ); @@ -118,22 +118,16 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par setLayout( layout ); TomahawkUtils::unmarginLayout( layout ); - TomahawkUtils::styleScrollBar( ui->tracks->horizontalScrollBar() ); - TomahawkUtils::styleScrollBar( ui->albums->verticalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->tracks->horizontalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); ui->albums->setStyleSheet( "QListView { background-color: transparent; }" ); - ui->albumFrame->setStyleSheet( "QFrame#albumFrame { background-color: transparent; }" - "QFrame#albumFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->albumFrame ); ui->tracks->setStyleSheet( "QTreeView#tracks { background-color: transparent; }" ); - ui->trackFrame->setStyleSheet( "QFrame#trackFrame { background-color: transparent; }" - "QFrame#trackFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->trackFrame ); MetaPlaylistInterface* mpl = new MetaPlaylistInterface(); mpl->addChildInterface( ui->tracks->playlistInterface() ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 95f4059697..71e5b3aea1 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -90,8 +90,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->albums->proxyModel()->setHideDupeItems( true ); QPalette trackViewPal = ui->topHits->palette(); - trackViewPal.setColor( QPalette::Foreground, Qt::white ); - trackViewPal.setColor( QPalette::Text, Qt::white ); + trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); ui->topHits->setPalette( trackViewPal ); @@ -121,11 +121,11 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->biography->setFrameShape( QFrame::NoFrame ); ui->biography->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->biography->setFont( f ); - TomahawkUtils::styleScrollBar( ui->biography->verticalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); QPalette p = ui->biography->palette(); - p.setColor( QPalette::Foreground, Qt::white ); - p.setColor( QPalette::Text, Qt::gray ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TEXT ); ui->biography->setPalette( p ); ui->artistLabel->setPalette( p ); @@ -150,29 +150,20 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* setLayout( layout ); TomahawkUtils::unmarginLayout( layout ); - TomahawkUtils::styleScrollBar( ui->albums->horizontalScrollBar() ); - TomahawkUtils::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->albums->horizontalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); ui->biography->document()->setDefaultStyleSheet( "a { text-decoration: none; font-weight: bold; color: #ffffff; }" ); ui->albums->setStyleSheet( "QListView { background-color: transparent; }" ); - ui->albumFrame->setStyleSheet( "QFrame#albumFrame { background-color: transparent; }" - "QFrame#albumFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->albumFrame ); ui->relatedArtists->setStyleSheet( "QListView { background-color: transparent; }" ); - ui->artistFrame->setStyleSheet( "QFrame#artistFrame { background-color: transparent; }" - "QFrame#artistFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->artistFrame ); ui->topHits->setStyleSheet( "QTreeView#topHits { background-color: transparent; }" ); - ui->trackFrame->setStyleSheet( "QFrame#trackFrame { background-color: transparent; }" - "QFrame#trackFrame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->trackFrame ); connect( ui->biography, SIGNAL( anchorClicked( QUrl ) ), SLOT( onBiographyLinkClicked( QUrl ) ) ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 8b2a81f3dd..c3170958eb 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -59,7 +59,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par ui->similarTracksView->setAutoResize( true ); ui->similarTracksView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); // TomahawkUtils::styleScrollBar( ui->similarTracksView->verticalScrollBar() ); - TomahawkUtils::styleScrollBar( ui->lyricsView->verticalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->lyricsView->verticalScrollBar() ); // ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; } QListView::item { background-color: transparent; }" ); @@ -69,8 +69,8 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par ui->statsLabel->setFont( f ); QPalette p = ui->lyricsView->palette(); - p.setColor( QPalette::Foreground, Qt::white ); - p.setColor( QPalette::Text, Qt::white ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); ui->lyricsView->setPalette( p ); ui->label->setPalette( p ); @@ -120,10 +120,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par TomahawkUtils::unmarginLayout( layout ); ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; }" ); - ui->frame->setStyleSheet( "QFrame#frame { background-color: transparent; }" - "QFrame#frame { " - "border-image: url(" RESPATH "images/widget-border.png) 3 3 3 3 stretch stretch;" - "border-top: 3px transparent; border-bottom: 3px transparent; border-right: 3px transparent; border-left: 3px transparent; }" ); + TomahawkStyle::stylePageFrame( ui->frame ); load( query ); } From 7723cade58552f52251b4b8875e75b1e9807385c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 09:07:01 +0200 Subject: [PATCH 348/565] * Removed obsolete png. --- resources.qrc | 1 - 1 file changed, 1 deletion(-) diff --git a/resources.qrc b/resources.qrc index 67594db1fb..dc33976479 100644 --- a/resources.qrc +++ b/resources.qrc @@ -172,7 +172,6 @@ data/images/delete.svg data/images/ok.svg data/images/tweet.svg - data/images/widget-border.png data/images/refresh.svg data/images/inbox.svg data/images/new-inbox.svg From a59971a74a5f9fa72815da6d77d63713b4efb575 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 10:18:36 +0200 Subject: [PATCH 349/565] * Emit a signal when the currentTrackPlaylist changes in AudioEngine. --- src/libtomahawk/audio/AudioEngine.cpp | 25 +++++++++++++++++++++---- src/libtomahawk/audio/AudioEngine.h | 2 ++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index 7f0ebb5b9a..1a10737120 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -688,7 +688,7 @@ AudioEngine::loadPreviousTrack() if ( d->playlist.data()->previousResult() ) { result = d->playlist.data()->setSiblingResult( -1 ); - d->currentTrackPlaylist = d->playlist; + setCurrentTrackPlaylist( d->playlist ); } if ( !result.isNull() ) @@ -731,7 +731,7 @@ AudioEngine::loadNextTrack() if ( d->playlist.data()->nextResult() ) { result = d->playlist.data()->setSiblingResult( 1 ); - d->currentTrackPlaylist = d->playlist; + setCurrentTrackPlaylist( d->playlist ); } } @@ -763,9 +763,13 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk: setPlaylist( playlist ); if ( playlist.isNull() && !fromQuery.isNull() ) - d->currentTrackPlaylist = playlistinterface_ptr( new SingleTrackPlaylistInterface( fromQuery ) ); + { + setCurrentTrackPlaylist( playlistinterface_ptr( new SingleTrackPlaylistInterface( fromQuery ) ) ); + } else - d->currentTrackPlaylist = playlist; + { + setCurrentTrackPlaylist( playlist ); + } if ( !result.isNull() ) { @@ -1210,3 +1214,16 @@ AudioEngine::onVolumeChanged( qreal volume ) { emit volumeChanged( volume * 100 ); } + + +void +AudioEngine::setCurrentTrackPlaylist( const playlistinterface_ptr& playlist ) +{ + Q_D( AudioEngine ); + + if ( d->currentTrackPlaylist != playlist ) + { + d->currentTrackPlaylist = playlist; + emit currentTrackPlaylistChanged( d->currentTrackPlaylist ); + } +} diff --git a/src/libtomahawk/audio/AudioEngine.h b/src/libtomahawk/audio/AudioEngine.h index 0e052f0a76..0544fe1208 100644 --- a/src/libtomahawk/audio/AudioEngine.h +++ b/src/libtomahawk/audio/AudioEngine.h @@ -117,6 +117,7 @@ public slots: void timerPercentage( unsigned int percentage ); void playlistChanged( Tomahawk::playlistinterface_ptr playlist ); + void currentTrackPlaylistChanged( Tomahawk::playlistinterface_ptr playlist ); void error( AudioEngine::AudioErrorCode errorCode ); @@ -143,6 +144,7 @@ private slots: void checkStateQueue(); void queueState( AudioState state ); void setState( AudioState state ); + void setCurrentTrackPlaylist( const Tomahawk::playlistinterface_ptr& playlist ); Q_DECLARE_PRIVATE( AudioEngine ); AudioEnginePrivate* d_ptr; From 63ee2cbd5928b7eb4e5766623cf5e1feddff4f4f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 10:23:19 +0200 Subject: [PATCH 350/565] * Implemented Flexible(Tree)View's isBeingPlayed(). --- src/libtomahawk/playlist/FlexibleTreeView.cpp | 19 +++++++++++++++++++ src/libtomahawk/playlist/FlexibleTreeView.h | 1 + src/libtomahawk/playlist/FlexibleView.cpp | 17 +++++++++++++++++ src/libtomahawk/playlist/FlexibleView.h | 1 + 4 files changed, 38 insertions(+) diff --git a/src/libtomahawk/playlist/FlexibleTreeView.cpp b/src/libtomahawk/playlist/FlexibleTreeView.cpp index 58163ea4f2..da0dc0c43b 100644 --- a/src/libtomahawk/playlist/FlexibleTreeView.cpp +++ b/src/libtomahawk/playlist/FlexibleTreeView.cpp @@ -22,6 +22,7 @@ #include #include +#include "audio/AudioEngine.h" #include "widgets/FilterHeader.h" #include "playlist/TreeModel.h" #include "playlist/ColumnView.h" @@ -53,6 +54,8 @@ FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) m_treeView->proxyModel()->setStyle( PlayableProxyModel::Collection ); + m_treeView->proxyModel()->setPlaylistInterface( m_columnView->proxyModel()->playlistInterface() ); + // m_trackView->setPlaylistInterface( m_playlistInterface ); // m_columnView->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); // m_gridView->setPlaylistInterface( m_trackView->proxyModel()->playlistInterface() ); @@ -332,3 +335,19 @@ FlexibleTreeView::setTemporaryPage( bool b ) { m_temporary = b; } + + +bool +FlexibleTreeView::isBeingPlayed() const +{ + if ( !playlistInterface() ) + return false; + + if ( playlistInterface() == AudioEngine::instance()->currentTrackPlaylist() ) + return true; + + if ( playlistInterface()->hasChildInterface( AudioEngine::instance()->currentTrackPlaylist() ) ) + return true; + + return false; +} diff --git a/src/libtomahawk/playlist/FlexibleTreeView.h b/src/libtomahawk/playlist/FlexibleTreeView.h index 1e1ec9471e..2c671ae015 100644 --- a/src/libtomahawk/playlist/FlexibleTreeView.h +++ b/src/libtomahawk/playlist/FlexibleTreeView.h @@ -55,6 +55,7 @@ Q_OBJECT virtual bool showInfoBar() const { return false; } virtual bool jumpToCurrentTrack(); virtual bool isTemporaryPage() const; + virtual bool isBeingPlayed() const; void setTemporaryPage( bool b ); ColumnView* columnView() const { return m_columnView; } diff --git a/src/libtomahawk/playlist/FlexibleView.cpp b/src/libtomahawk/playlist/FlexibleView.cpp index 0be8861bdc..24c7f8fc2a 100644 --- a/src/libtomahawk/playlist/FlexibleView.cpp +++ b/src/libtomahawk/playlist/FlexibleView.cpp @@ -22,6 +22,7 @@ #include #include +#include "audio/AudioEngine.h" #include "widgets/FilterHeader.h" #include "playlist/ModeHeader.h" #include "playlist/PlayableModel.h" @@ -338,3 +339,19 @@ FlexibleView::setTemporaryPage( bool b ) { m_temporary = b; } + + +bool +FlexibleView::isBeingPlayed() const +{ + if ( !playlistInterface() ) + return false; + + if ( playlistInterface() == AudioEngine::instance()->currentTrackPlaylist() ) + return true; + + if ( playlistInterface()->hasChildInterface( AudioEngine::instance()->currentTrackPlaylist() ) ) + return true; + + return false; +} diff --git a/src/libtomahawk/playlist/FlexibleView.h b/src/libtomahawk/playlist/FlexibleView.h index bc367265b5..ff78a03c9e 100644 --- a/src/libtomahawk/playlist/FlexibleView.h +++ b/src/libtomahawk/playlist/FlexibleView.h @@ -53,6 +53,7 @@ Q_OBJECT virtual bool showInfoBar() const { return false; } virtual bool jumpToCurrentTrack(); virtual bool isTemporaryPage() const; + virtual bool isBeingPlayed() const; void setTemporaryPage( bool b ); TrackView* trackView() const { return m_trackView; } From 0cf881359cfcf18a27a3dd6450ceabe7c1019dc7 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 10:23:40 +0200 Subject: [PATCH 351/565] * Style fixes in SourceTreeView. --- src/tomahawk/sourcetree/SourceTreeView.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tomahawk/sourcetree/SourceTreeView.cpp b/src/tomahawk/sourcetree/SourceTreeView.cpp index 7ce9973d6a..a2712d1a60 100644 --- a/src/tomahawk/sourcetree/SourceTreeView.cpp +++ b/src/tomahawk/sourcetree/SourceTreeView.cpp @@ -29,7 +29,6 @@ #include "SourceDelegate.h" #include "sourcetree/items/PlaylistItems.h" #include "sourcetree/items/SourceItem.h" -#include "audio/AudioEngine.h" #include "SourcePlaylistInterface.h" #include "TomahawkSettings.h" #include "GlobalActionManager.h" @@ -870,10 +869,11 @@ SourceTreeView::drawRow( QPainter* painter, const QStyleOptionViewItem& option, QTreeView::drawRow( painter, option, index ); } + void -SourceTreeView::drawBranches( QPainter *painter, const QRect &rect, const QModelIndex &index ) const +SourceTreeView::drawBranches( QPainter* painter, const QRect& rect, const QModelIndex& index ) const { - if( !QString( qApp->style()->metaObject()->className() ).toLower().contains( "qtcurve" ) ) + if ( !QString( qApp->style()->metaObject()->className() ).toLower().contains( "qtcurve" ) ) QTreeView::drawBranches( painter, rect, index ); } @@ -891,7 +891,7 @@ SourceTreeView::itemFromIndex( const QModelIndex& index ) const void -SourceTreeView::update( const QModelIndex &index ) +SourceTreeView::update( const QModelIndex& index ) { dataChanged( index, index ); } From 555d3587d74c8372adaf813a16fef604c0727f24 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 10:24:31 +0200 Subject: [PATCH 352/565] * Style fixes in SourcesModel. --- src/tomahawk/sourcetree/SourcesModel.cpp | 51 ++++++++++++------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index b54a446faa..f6a6549fb3 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -178,7 +178,7 @@ SourcesModel::columnCount( const QModelIndex& ) const int SourcesModel::rowCount( const QModelIndex& parent ) const { - if( !parent.isValid() ) + if ( !parent.isValid() ) { return m_rootItem->children().count(); } @@ -190,14 +190,14 @@ SourcesModel::rowCount( const QModelIndex& parent ) const QModelIndex SourcesModel::parent( const QModelIndex& child ) const { - if( !child.isValid() ) + if ( !child.isValid() ) { return QModelIndex(); } SourceTreeItem* node = itemFromIndex( child ); SourceTreeItem* parent = node->parent(); - if( parent == m_rootItem ) + if ( parent == m_rootItem ) return QModelIndex(); return createIndex( rowForItem( parent ), 0, parent ); @@ -207,10 +207,10 @@ SourcesModel::parent( const QModelIndex& child ) const QModelIndex SourcesModel::index( int row, int column, const QModelIndex& parent ) const { - if( row < 0 || column < 0 ) + if ( row < 0 || column < 0 ) return QModelIndex(); - if( hasIndex( row, column, parent ) ) + if ( hasIndex( row, column, parent ) ) { SourceTreeItem *parentNode = itemFromIndex( parent ); SourceTreeItem *childNode = parentNode->children().at( row ); @@ -250,11 +250,11 @@ SourcesModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int ro { SourceTreeItem* item = 0; // qDebug() << "Got mime data dropped:" << row << column << parent << itemFromIndex( parent )->text(); - if( row == -1 && column == -1 ) + if ( row == -1 && column == -1 ) item = itemFromIndex( parent ); - else if( column == 0 ) + else if ( column == 0 ) item = itemFromIndex( index( row, column, parent ) ); - else if( column == -1 ) // column is -1, that means the drop is happening "below" the indices. that means we actually want the one before it + else if ( column == -1 ) // column is -1, that means the drop is happening "below" the indices. that means we actually want the one before it item = itemFromIndex( index( row - 1, 0, parent ) ); Q_ASSERT( item ); @@ -471,17 +471,17 @@ SourcesModel::viewPageActivated( Tomahawk::ViewPage* page ) SourceTreeItem* SourcesModel::activatePlaylistPage( ViewPage* p, SourceTreeItem* i ) { - if( !i ) + if ( !i ) return 0; - if( qobject_cast< PlaylistItem* >( i ) && + if ( qobject_cast< PlaylistItem* >( i ) && qobject_cast< PlaylistItem* >( i )->activateCurrent() ) return i; SourceTreeItem* ret = 0; - for( int k = 0; k < i->children().size(); k++ ) + for ( int k = 0; k < i->children().size(); k++ ) { - if( SourceTreeItem* retItem = activatePlaylistPage( p, i->children().at( k ) ) ) + if ( SourceTreeItem* retItem = activatePlaylistPage( p, i->children().at( k ) ) ) ret = retItem; } @@ -494,7 +494,7 @@ SourcesModel::loadSources() { QList sources = SourceList::instance()->sources(); - foreach( const source_ptr& source, sources ) + foreach ( const source_ptr& source, sources ) appendItem( source ); } @@ -502,7 +502,7 @@ SourcesModel::loadSources() void SourcesModel::onSourcesAdded( const QList& sources ) { - foreach( const source_ptr& source, sources ) + foreach ( const source_ptr& source, sources ) appendItem( source ); } @@ -577,11 +577,11 @@ SourcesModel::itemUpdated() Q_ASSERT( qobject_cast< SourceTreeItem* >( sender() ) ); SourceTreeItem* item = qobject_cast< SourceTreeItem* >( sender() ); - if( !item ) + if ( !item ) return; QModelIndex idx = indexFromItem( item ); - if( idx.isValid() ) + if ( idx.isValid() ) emit dataChanged( idx, idx ); } @@ -592,7 +592,7 @@ SourcesModel::onItemRowsAddedBegin( int first, int last ) Q_ASSERT( qobject_cast< SourceTreeItem* >( sender() ) ); SourceTreeItem* item = qobject_cast< SourceTreeItem* >( sender() ); - if( !item ) + if ( !item ) return; QModelIndex idx = indexFromItem( item ); @@ -615,7 +615,7 @@ SourcesModel::onItemRowsRemovedBegin( int first, int last ) Q_ASSERT( qobject_cast< SourceTreeItem* >( sender() ) ); SourceTreeItem* item = qobject_cast< SourceTreeItem* >( sender() ); - if( !item ) + if ( !item ) return; QModelIndex idx = indexFromItem( item ); @@ -643,7 +643,7 @@ SourcesModel::linkSourceItemToPage( SourceTreeItem* item, ViewPage* p ) if ( QObject* obj = dynamic_cast< QObject* >( p ) ) { - if( obj->metaObject()->indexOfSignal( "destroyed(QWidget*)" ) > -1 ) + if ( obj->metaObject()->indexOfSignal( "destroyed(QWidget*)" ) > -1 ) connect( obj, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection ); } m_viewPageDelayedCacheItem = 0; @@ -662,7 +662,7 @@ void SourcesModel::removeSourceItemLink( SourceTreeItem* item ) { QList< ViewPage* > pages = m_sourceTreeLinks.keys( item ); - foreach( ViewPage* p, pages ) + foreach ( ViewPage* p, pages ) m_sourceTreeLinks.remove( p ); } @@ -670,7 +670,7 @@ SourcesModel::removeSourceItemLink( SourceTreeItem* item ) SourceTreeItem* SourcesModel::itemFromIndex( const QModelIndex& idx ) const { - if( !idx.isValid() ) + if ( !idx.isValid() ) return m_rootItem; Q_ASSERT( idx.internalPointer() ); @@ -682,7 +682,7 @@ SourcesModel::itemFromIndex( const QModelIndex& idx ) const QModelIndex SourcesModel::indexFromItem( SourceTreeItem* item ) const { - if( !item || !item->parent() ) // should never happen.. + if ( !item || !item->parent() ) // should never happen.. return QModelIndex(); // reconstructs a modelindex from a sourcetreeitem that is somewhere in the tree @@ -703,9 +703,9 @@ SourcesModel::indexFromItem( SourceTreeItem* item ) const **/ QList< int > childIndexList; SourceTreeItem* curItem = item; - while( curItem != m_rootItem ) { + while ( curItem != m_rootItem ) { int row = rowForItem( curItem ); - if( row < 0 ) // something went wrong, bail + if ( row < 0 ) // something went wrong, bail return QModelIndex(); childIndexList << row; @@ -715,7 +715,8 @@ SourcesModel::indexFromItem( SourceTreeItem* item ) const // qDebug() << "build child index list:" << childIndexList; // now rebuild the qmodelindex we need QModelIndex idx; - for( int i = childIndexList.size() - 1; i >= 0 ; i-- ) { + for ( int i = childIndexList.size() - 1; i >= 0 ; i-- ) + { idx = index( childIndexList[ i ], 0, idx ); } // qDebug() << "Got index from item:" << idx << idx.data( Qt::DisplayRole ).toString(); From d8311c0e4197255ea27dfc273e6638abf2262cb9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 10:25:04 +0200 Subject: [PATCH 353/565] * Style fixes in GenericPageItems. --- src/tomahawk/sourcetree/items/GenericPageItems.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tomahawk/sourcetree/items/GenericPageItems.cpp b/src/tomahawk/sourcetree/items/GenericPageItems.cpp index e57f243d9c..a080acdc8f 100644 --- a/src/tomahawk/sourcetree/items/GenericPageItems.cpp +++ b/src/tomahawk/sourcetree/items/GenericPageItems.cpp @@ -35,7 +35,7 @@ GenericPageItem::GenericPageItem( SourcesModel* model, SourceTreeItem* parent, c , m_show( show ) , m_get( get ) { - if( ViewPage* p = m_get() ) + if ( ViewPage* p = m_get() ) model->linkSourceItemToPage( this, p ); } @@ -76,12 +76,13 @@ GenericPageItem::willAcceptDrag(const QMimeData* data) const void -GenericPageItem::setText( const QString &text ) +GenericPageItem::setText( const QString& text ) { m_text = text; emit updated(); } + bool GenericPageItem::isBeingPlayed() const { @@ -90,10 +91,10 @@ GenericPageItem::isBeingPlayed() const if ( m_get()->isBeingPlayed() ) return true; - if ( !m_get()->playlistInterface().isNull() && m_get()->playlistInterface() == AudioEngine::instance()->currentTrackPlaylist() ) + if ( m_get()->playlistInterface() && m_get()->playlistInterface() == AudioEngine::instance()->currentTrackPlaylist() ) return true; - if ( !m_get()->playlistInterface().isNull() && m_get()->playlistInterface()->hasChildInterface( AudioEngine::instance()->currentTrackPlaylist() ) ) + if ( m_get()->playlistInterface() && m_get()->playlistInterface()->hasChildInterface( AudioEngine::instance()->currentTrackPlaylist() ) ) return true; } @@ -109,7 +110,7 @@ GenericPageItem::peerSortValue() const void -GenericPageItem::setSortValue(int value) +GenericPageItem::setSortValue( int value ) { m_sortValue = value; } From bd4e19fa32c4d28f4577935e6c0320b79c8bf1de Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 10:25:28 +0200 Subject: [PATCH 354/565] * Implemented LovedTracksItem's isBeingPlayed(). --- src/tomahawk/sourcetree/items/LovedTracksItem.cpp | 10 +++++++++- src/tomahawk/sourcetree/items/LovedTracksItem.h | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tomahawk/sourcetree/items/LovedTracksItem.cpp b/src/tomahawk/sourcetree/items/LovedTracksItem.cpp index fb45cdda80..a41f3b54e2 100644 --- a/src/tomahawk/sourcetree/items/LovedTracksItem.cpp +++ b/src/tomahawk/sourcetree/items/LovedTracksItem.cpp @@ -30,6 +30,7 @@ #include "playlist/PlaylistLargeItemDelegate.h" #include "utils/ImageRegistry.h" +#include "utils/Logger.h" using namespace Tomahawk; @@ -97,7 +98,7 @@ LovedTracksItem::activate() pv->setGuid( QString( "lovedtracks/%1" ).arg( par->source()->nodeId() ) ); } - pv->setPlayableModel( raModel ); + pv->setPlaylistModel( raModel ); raModel->setSource( !par ? source_ptr() : par->source() ); m_lovedTracksPage = pv; @@ -167,3 +168,10 @@ LovedTracksItem::loveDroppedTracks( QList< Tomahawk::query_ptr > qrys ) foreach( Tomahawk::query_ptr qry, qrys ) qry->track()->setLoved( true ); } + + +bool +LovedTracksItem::isBeingPlayed() const +{ + return m_lovedTracksPage && m_lovedTracksPage->isBeingPlayed(); +} diff --git a/src/tomahawk/sourcetree/items/LovedTracksItem.h b/src/tomahawk/sourcetree/items/LovedTracksItem.h index fb71e8106f..8136c90aee 100644 --- a/src/tomahawk/sourcetree/items/LovedTracksItem.h +++ b/src/tomahawk/sourcetree/items/LovedTracksItem.h @@ -44,6 +44,8 @@ class LovedTracksItem : public SourceTreeItem void setSortValue( int value ); + virtual bool isBeingPlayed() const; + private slots: void loveDroppedTracks( QList< Tomahawk::query_ptr > qrys ); From af5fe4b965a9e53ebd280b3751f0b326f7607a60 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 10:25:59 +0200 Subject: [PATCH 355/565] * When a track starts playing, check if we need to update the sidebar's speaker-icon. --- src/tomahawk/sourcetree/items/SourceTreeItem.cpp | 11 +++++++++++ src/tomahawk/sourcetree/items/SourceTreeItem.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/tomahawk/sourcetree/items/SourceTreeItem.cpp b/src/tomahawk/sourcetree/items/SourceTreeItem.cpp index 2225c6b09c..f042b561a0 100644 --- a/src/tomahawk/sourcetree/items/SourceTreeItem.cpp +++ b/src/tomahawk/sourcetree/items/SourceTreeItem.cpp @@ -18,6 +18,7 @@ #include "SourceTreeItem.h" +#include "audio/AudioEngine.h" #include "utils/Logger.h" using namespace Tomahawk; @@ -30,6 +31,8 @@ SourceTreeItem::SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, Sou , m_model( model ) , m_peerSortValue( peerSortValue ) { + connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), SLOT( checkPlayingStatus() ) ); + connect( this, SIGNAL( beginChildRowsAdded( int, int ) ), m_model, SLOT( onItemRowsAddedBegin( int, int ) ) ); connect( this, SIGNAL( beginChildRowsRemoved( int, int ) ), m_model, SLOT( onItemRowsRemovedBegin( int, int ) ) ); connect( this, SIGNAL( childRowsAdded() ), m_model, SLOT( onItemRowsAddedDone() ) ); @@ -56,6 +59,14 @@ SourceTreeItem::~SourceTreeItem() } +void +SourceTreeItem::checkPlayingStatus() +{ + if ( isBeingPlayed() ) + emit updated(); +} + + SourcesModel::RowType SourceTreeItem::type() const { diff --git a/src/tomahawk/sourcetree/items/SourceTreeItem.h b/src/tomahawk/sourcetree/items/SourceTreeItem.h index ea7dbe8805..91a9c9e540 100644 --- a/src/tomahawk/sourcetree/items/SourceTreeItem.h +++ b/src/tomahawk/sourcetree/items/SourceTreeItem.h @@ -98,6 +98,9 @@ public slots: void setRowType( SourcesModel::RowType t ); void setParentItem( SourceTreeItem* item ); +private slots: + void checkPlayingStatus(); + private: SourcesModel::RowType m_type; From 1e89558c782fd8c8489422c58d3368dcd8fea23b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 11:40:39 +0200 Subject: [PATCH 356/565] * Make boxes entirely transparent. --- src/libtomahawk/utils/TomahawkStyle.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.cpp b/src/libtomahawk/utils/TomahawkStyle.cpp index 8c304a96e4..f70a7cafde 100644 --- a/src/libtomahawk/utils/TomahawkStyle.cpp +++ b/src/libtomahawk/utils/TomahawkStyle.cpp @@ -164,9 +164,8 @@ TomahawkStyle::drawArrow( QStyle::PrimitiveElement element, QPainter* p, const Q void TomahawkStyle::stylePageFrame( QFrame* frame ) { - frame->setStyleSheet( QString( "QFrame#%1 { background-color: %2; border: 1px solid white; border-radius: 3px; }" ) - .arg( frame->objectName() ) - .arg( TomahawkStyle::PAGE_ITEM_BACKGROUND.name() ) ); + frame->setStyleSheet( QString( "QFrame#%1 { background-color: transparent; border: 0px solid white; border-radius: 0px; }" ) + .arg( frame->objectName() ) ); } From 082d86a127bcce1afe1142b45d0499edfd51acf9 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 11:40:51 +0200 Subject: [PATCH 357/565] * Style fixes. --- src/libtomahawk/widgets/BasicHeader.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/widgets/BasicHeader.cpp b/src/libtomahawk/widgets/BasicHeader.cpp index 3868306d37..2133f00d58 100644 --- a/src/libtomahawk/widgets/BasicHeader.cpp +++ b/src/libtomahawk/widgets/BasicHeader.cpp @@ -93,14 +93,14 @@ BasicHeader::BasicHeader( QWidget* parent ) lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); lineAbove->setFrameShape( QFrame::HLine ); lineAbove->setMaximumHeight( 1 ); - QFrame* lineAbove2 = new QFrame( this ); - lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); - lineAbove2->setFrameShape( QFrame::HLine ); - lineAbove2->setMaximumHeight( 1 ); + QFrame* lineBelow = new QFrame( this ); + lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + lineBelow->setFrameShape( QFrame::HLine ); + lineBelow->setMaximumHeight( 1 ); l->addItem( m_mainLayout ); l->addWidget( lineAbove ); - l->addWidget( lineAbove2 ); + l->addWidget( lineBelow ); TomahawkUtils::unmarginLayout( m_mainLayout ); m_mainLayout->setContentsMargins( 8, 4, 8, 4 ); From e27396be63f41de82f5f9c084f3c813982ab766a Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 11:41:19 +0200 Subject: [PATCH 358/565] * Further tweaks to artist-page layout. --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 28 +- .../widgets/infowidgets/ArtistInfoWidget.ui | 425 +++++++++++------- 2 files changed, 278 insertions(+), 175 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 71e5b3aea1..4b5e04812b 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -57,10 +57,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* artist->loadStats(); connect( artist.data(), SIGNAL( statsLoaded() ), SLOT( onArtistStatsLoaded() ) ); - m_albumsModel = new PlayableModel( ui->albums ); - ui->albums->setPlayableModel( m_albumsModel ); - ui->albums->setEmptyTip( tr( "Sorry, we could not find any albums for this artist!" ) ); - m_relatedModel = new PlayableModel( ui->relatedArtists ); ui->relatedArtists->setPlayableModel( m_relatedModel ); ui->relatedArtists->proxyModel()->sort( -1 ); @@ -82,13 +78,23 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->relatedArtists->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ ui->relatedArtists->delegate()->setItemSize( QSize( 170, 170 ) ); - ui->albums->setAutoFitItems( false ); - ui->albums->setWrapping( false ); + ui->albums->setAutoResize( true ); ui->albums->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); +/* ui->albums->setWrapping( false ); + ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ ui->albums->delegate()->setItemSize( QSize( 170, 170 ) ); ui->albums->proxyModel()->setHideDupeItems( true ); + m_albumsModel = new PlayableModel( ui->albums ); + ui->albums->setPlayableModel( m_albumsModel ); + ui->albums->proxyModel()->sort( -1 ); + ui->albums->setEmptyTip( tr( "Sorry, we could not find any albums for this artist!" ) ); + + ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + ui->lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + ui->lineBelow2->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + QPalette trackViewPal = ui->topHits->palette(); trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); @@ -139,18 +145,22 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* area->setWidget( widget ); QPalette pal = palette(); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_LOWER ); area->setPalette( pal ); area->setAutoFillBackground( true ); area->setFrameShape( QFrame::NoFrame ); area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + ui->widget->setPalette( pal ); + ui->widget->setAutoFillBackground( true ); + QVBoxLayout* layout = new QVBoxLayout(); layout->addWidget( area ); setLayout( layout ); TomahawkUtils::unmarginLayout( layout ); - TomahawkStyle::styleScrollBar( ui->albums->horizontalScrollBar() ); + TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); TomahawkStyle::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 829ca723e9..2e2ca533cd 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -7,18 +7,18 @@ 0 0 965 - 1179 + 822 Form - + - 16 + 0 - 12 + 0 @@ -26,7 +26,7 @@ 16 - 0 + 12 @@ -130,207 +130,305 @@ - - - 0 + + + + 16777215 + 1 + - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 8 - - - 4 - - - 8 - - - 8 - - - - - - 18 - 75 - true - - - - Top Hits - - - 0 - - - - - - - - 0 - 0 - - - - true - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - + + QFrame::Sunken + + + Qt::Horizontal + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 16 + + + 0 + + + 16 + + + - 8 + 12 - 4 + 0 - 8 + 12 - 4 + 0 - - - - Arial - 18 - 75 - true - - - - Related Artists + + + QFrame::StyledPanel - - 0 + + QFrame::Raised + + + 4 + + + 8 + + + 4 + + + 8 + + + 8 + + + + + + 18 + 75 + true + + + + Top Hits + + + 0 + + + + + + + + 0 + 0 + + + + true + + + + - - - - 0 - 0 - + + + QFrame::StyledPanel - - - 0 - 190 - + + QFrame::Raised + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + Arial + 18 + 75 + true + + + + Related Artists + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 190 + + + + + - - - + + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + - - - QFrame::StyledPanel + + + + 16777215 + 1 + - - QFrame::Raised + + Qt::Horizontal - + + + + + - 4 + 0 - 8 + 0 - 4 + 16 - 8 + 0 - 4 + 16 - - - - Arial - 18 - 75 - true - + + + QFrame::StyledPanel - - Albums - - - 0 - - - - - - - - 0 - 0 - - - - - 0 - 190 - + + QFrame::Raised + + + 4 + + + 28 + + + 4 + + + 8 + + + 4 + + + + + + Arial + 18 + 75 + true + + + + Albums + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 190 + + + + + - - - - Qt::Vertical - - - - 20 - 0 - - - - + + PlaylistView + QTreeView +
playlist/PlaylistView.h
+
GridView QListView @@ -341,11 +439,6 @@ QLabel
widgets/PlayableCover.h
- - PlaylistView - QTreeView -
playlist/PlaylistView.h
-
From df5687740341afd75d3dad53ae95ccb87aad5b22 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 14:26:31 +0200 Subject: [PATCH 359/565] * Added example ttf font. --- data/fonts/OFL.txt | 92 +++++++++++++++++++ data/fonts/TitilliumWeb-Black.ttf | Bin 0 -> 49356 bytes data/fonts/TitilliumWeb-Bold.ttf | Bin 0 -> 59908 bytes data/fonts/TitilliumWeb-BoldItalic.ttf | Bin 0 -> 69796 bytes data/fonts/TitilliumWeb-ExtraLight.ttf | Bin 0 -> 63124 bytes data/fonts/TitilliumWeb-ExtraLightItalic.ttf | Bin 0 -> 67788 bytes data/fonts/TitilliumWeb-Italic.ttf | Bin 0 -> 72416 bytes data/fonts/TitilliumWeb-Light.ttf | Bin 0 -> 64032 bytes data/fonts/TitilliumWeb-LightItalic.ttf | Bin 0 -> 71720 bytes data/fonts/TitilliumWeb-Regular.ttf | Bin 0 -> 63752 bytes data/fonts/TitilliumWeb-SemiBold.ttf | Bin 0 -> 63044 bytes data/fonts/TitilliumWeb-SemiBoldItalic.ttf | Bin 0 -> 71812 bytes resources.qrc | 1 + 13 files changed, 93 insertions(+) create mode 100644 data/fonts/OFL.txt create mode 100644 data/fonts/TitilliumWeb-Black.ttf create mode 100644 data/fonts/TitilliumWeb-Bold.ttf create mode 100644 data/fonts/TitilliumWeb-BoldItalic.ttf create mode 100644 data/fonts/TitilliumWeb-ExtraLight.ttf create mode 100644 data/fonts/TitilliumWeb-ExtraLightItalic.ttf create mode 100644 data/fonts/TitilliumWeb-Italic.ttf create mode 100644 data/fonts/TitilliumWeb-Light.ttf create mode 100644 data/fonts/TitilliumWeb-LightItalic.ttf create mode 100644 data/fonts/TitilliumWeb-Regular.ttf create mode 100644 data/fonts/TitilliumWeb-SemiBold.ttf create mode 100644 data/fonts/TitilliumWeb-SemiBoldItalic.ttf diff --git a/data/fonts/OFL.txt b/data/fonts/OFL.txt new file mode 100644 index 0000000000..723d4560b9 --- /dev/null +++ b/data/fonts/OFL.txt @@ -0,0 +1,92 @@ +Copyright (c) 2009-2011 by Accademia di Belle Arti di Urbino and students of MA course of Visual design. Some rights reserved. +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/data/fonts/TitilliumWeb-Black.ttf b/data/fonts/TitilliumWeb-Black.ttf new file mode 100644 index 0000000000000000000000000000000000000000..fc5c4b50116f71be2fbf86d8940539f341d4b044 GIT binary patch literal 49356 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 BpLG4FW7jsT$jJ| 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 bq=SlC)4(YGPxnA`LJ!ox!30F8@!vlIC*Rj^ literal 0 HcmV?d00001 diff --git a/data/fonts/TitilliumWeb-Bold.ttf b/data/fonts/TitilliumWeb-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0af0fe7d27e63fce1fd40c217a4fe9c50ffdb170 GIT binary patch literal 59908 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 literal 0 HcmV?d00001 diff --git a/data/fonts/TitilliumWeb-BoldItalic.ttf b/data/fonts/TitilliumWeb-BoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..77425eaab62d2591e114074577a866315082ca9d GIT binary patch literal 69796 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 BvF!i= literal 0 HcmV?d00001 diff --git a/data/fonts/TitilliumWeb-ExtraLight.ttf b/data/fonts/TitilliumWeb-ExtraLight.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2b506ef2270789e04d0dafcd7b530470072a134f GIT binary patch literal 63124 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
  • 7WSIrzx*Q8!N7|pB@jCtall45~ z%+H==CQD|qWClx~Gs!b1dA?*lTMExuo&IdeN!gBoqrgKO$nzt4b|lY@p!Hm$LkF7AC8j7u`wrrYLM*^RY*C06IEXI_u>=SGNG{P~F43Wbe#AjP;-DXK z(2qEXKMILI3W+~hu>s2H)2r0EM2Go03bX_EBD*2GBW+j+@dD9d9?@YQ(P19ZVII+89?@YQaZG_`6hL(7 zCOUKz9lBuv2UeI%bm+hmbBS#V^(AaiB+p6YIfeXYa@|#6HbD18hYpy)L7(Oz#&Hqf zxb&kOe~dD&We)anu6u&*C%NZS9DkZTpJDr1>?4=xa3|5>PSp=zXVyRsK%Nf<0%i}? zVB`=m3=9WJU<4rgQlpT`$kE8LjJGbJZyCpU>O!KTi;T~)+)6c-bJO4jYs6j7`#^$f z2KX(w0x&AXqQg((%CTs95?6^u2eD=k)?AJ?=VQ%IEV&3PEm40V&o$ry@K^94cnCZU z9s#UHP>+GNq3gSYsl2npck1mt*ziSbaHGUyjw6WA)`&eK}TNj@6fA^+jqc_x>E&e3V~; zwU=Yfy_l7M&=>&kp*1u z0)@a0ia1_Oxl548kfq2nWI3_|S&6JdRwKR08l(>y0715Ek;jpB$P>tVWCQXf@?Xdw zkp?`c=-)I_o+7qD%9F%sr1dmWo;8=_btQORIbK(S*OlXSC3s!AZc93OqSziOPZiHW z%9F*OJXf~S@Ag9WM)qN*p)cwCA^Rf-AkRk*L=Hj@Mh-y^MGiv_XN;7DcZ@JT#}kX_ z13t(5!m}17cwRaEz<#{WL;w2&p68?w{sHfE(hvWD2RiXUnaL=}`(%{yIUZO;CMStBQ7n0}PJKXn{{JRb}MgV-O8 z90Fu+dl+&!NCG1O+Jb%Duul=}ZkWhTJ=`#no4(ae-|B&h+%S;`CUVoaxV3qz*rs_%L8M1U@Q-e<$`?>*dWb!a5PP^_f%ig8cqqh#heAwvD8z&wm_f$Z9@x+W zLu`f(kHQk0VZ)=W6lSEZ+M1nZFhCI=UXJ&^kLOn4xe`Sm!gKfIx#h&mWiaO< znA3rm6~S*s@LLi5Rs_El!EZ(In~XX3V~s^vVi8tYgoev$z4zgn_i2UW@Y{ZBzn|Kd zQ}c3aT~3XQsBIB7dmnx~4!<3T-;Ps@B1*rXlJD0GcygNxmVi4s{vz{qFT;K7DbHe} zry8QC8piTIB784ARu6a8n>Av61^iSGH#JbZT)3&88ak+X}=Sm3Q@wkYx2;J)=nu;DPDZRkG1RxS&Kvh z;gu<}N=R0w$Q({FzEOv7)ZrU-_(mPRQHO8T;Tv`IJ`MCf4fH+@^ga#rJ`MCf4fH+@ z^ga#rJ`MCf4Oq+`dX)yOCcKKI4u7h{pXy-j2H3g*rfz_x8(`=L*tr2_Zh(awVBiMW zw*lsDfOQ*S+y>aT0ZS@|WgB4F2H3R$W@TkOFwnLJEkHDA3)+KofUNwGRUfkALsonA zG(Tk6eIs0qvi-^j0-!*(TSI$&)bIn$Oi=QN&Srkd+|W8D)-aS4&+ zjXdpIfDB(N7TT(DZy*-B0Sn!Lb$)?W%4$`ytnFCV0jz2ZR@Jn+574?>Xx+WE?iN~iufBm*DmQ`@un0D0tYw@@`2N2Phn1B z&3YmLd5SMj@a5@!1-)rGJ!v_;Xa(bha(c!Jdd3RokhU|2v>ltP!{+L+xjN>PzGj{- zpY;y1{-m9`w#{0DGL3a5(}6tsoP`XpM_G<8WlhQkj>)=|jYwIW@+LTSeab&M_CELk z$Xb<;ke`5^K-R771>b-ia1c1aVUQ2bwx%VPH7)T#*0!`cWqr#f>|YJ$f$&sU8+ zJxIedpJOHO^MLg)JmcL4v%baZF&R6TVcqfz0~g-%F;9G}I5RSIrZdk}W0<#z;VF7w z#uLJ$W5GB$cRao6MMP?hSKvY?Tse3^Oniv(PJsHb zdV=2lFh>d~Z#4CY2NOt7OvOp^Dz66uv8k?@F->Yu#`QN?m|09woPJL>{O9(&MLo zvGFx!-v#!7ec+T|Mo{JeWe!m00A&skGX;1?(-%wVN4qk^NUY%}*6uM z56%Id;P7)n7tjs#0sX)LFc1s|L&0#Kri=ii%qNnE;gQ2=Esyx50_*W%JwB{QR>2>} zf_(6bk6#D%h5Ps&5*l@)(I6TPqR}84b)wNA8V#b+AQ}y#(I6TPqEROr4Wdye8V#aR zFB~vZIqctnPrq2 zM)|FR6HPkNq!Ud#(WJ~I$}D0KO?uI!7fpK6q!&$k(WDnmdeNj8O?uI!7fs63IawPg z>*Abf(2E9xXfTKdgJ{sjyJ`BuRC$R{m49CNk_mzasCziDyJJ zfpN|hBEf;YLnf6~P|NvV%DZ4PSpRV=-^+OO%N;z``!m0A{SCiy{fJbb=C5Q1#ev@RzqVdC1N$TIYYi`Istx(tesI}KTKcgX>SM%d%y7JUxTpPOYzjrO8fyR$f zMN1bOc}A|)hH}DSroXB7jY-W7ViaYp=ZXWyd!iZR6XO-WmBt41TgJ7A7%^CBosriP zqwry+gXkzP@=Wt>(^@J|Ic92}_(ZNPLWd1T86`g+ZnJReNDFE_RukUhYA>+zYSf&x z8M&U8mt19diP#!H#*4-}V~6pDv5nZ+{1``!e;8YhO^lwEv7K{!j5lbF*W@Jp+w60? z69gNS6 z4~b{m^nhi85wTa-3AL9WGUq|x(EuZ49Td7(4( z@{-8`UxbhTdr~Zt8m#B~5>|Uge2j8xS8lZtDVq5gr4!wm?PNS=-u)XXKP@12Yb;+!pyQ0A#mD6hY!M&q`U{I|Pt|d#Wa52p^V;&TErl;f>o1xu?{9APhM!2D z4`Ty`#cktnadZ80k(Gy~ z9V4HT)fz5FFQ%P`^FN#XpWio}Gxksi7Zh>daLY&}5c=Sm&3ZH0BmIlu7_;gTAIe_| z@DV8^dSJF=iXH z&};y&r1eh!G>-;IY5E4&HmjNB4pv#$hih=kwWajhZ!@o@n)D2o_ytvaqBx`?!d7xwM_EWsdk*^`f_Ipmn59 z(ynIw(v*m@oTj6Qv+u~Z_%eSFNna{_ar%dGcf`l^u?FL1^jj_7^h-Wg#2^+$sTqfv zU*Y?Rh)wdtT8|rtLwphHVMA>tT4^wH;2j_Lr@qaZLh|D31ED@FRQnLOZ!q)z*0f%i z6+xV-^o<@OC$);!Uof*D{xog%=2wcn!-r3i9nE*sL>{!>Q7Qj#T4HePJ(aQ*N1~?p zRVwCD6p?wm$V+A3mpNQ{dnNx4g_*1Fd?jwT@$JRAfsOC2RQzf{neVP_mceN|=bd&W z@<5l!oyKk6ugt8J^_7y#H_EI*95s-yHL^%#bdg9MOOHdQl7c zDljng9ZbCjn>k1>0{;6L}VC(g?Y-y;&c*m*gpVoc@uk z!;tprOlm8)!PsS2Ms$6#PU~;e)$;Ds|NT!lo=5z5Ki$y0_+gV@G;N(rE1i3m+jVJH z-%e;b3dq-zI<#l)%Q5RcvI-C7-xJfSk)K$060wa$q*#0SBki3CVnH{w*MSq~fR3hx zhf|$N#-Hh6yJl0|+k50$@7AE`n3E4Azp z`gSCVyg4_v5&V~L@^6B8j)g>Ib41Zjk&v>S4f?F-x^hhuBJVBRk>^^_{*jRSwL~KV zekFTG#Gotrzn-G)?u}5*>MMCl9;big>M*2@qz$dyI%1az_-I?K)B0;&Ew&oZ%@$CS)_g`eVG%g^wew;8-^_#NJ!{2ud73(fha zrOY?=RIGMm)@h&2Ix+XetdsJZvraMQTh3z5c_k^Gl=1?ujC{Xb4>ZY#_sg|FNWXmQ zev8zUEW7HsnD-5;uFEn~mr*ZCDe0saj0#;TEYSvw=|P=`@oP4@pQc8ZoMdu{=6$~A zzK#va)**Qz`Lt0jt#=hu5ArwPiOjo@IrDopQZM7J%lwakrZ)`pKH>lJG42$N1=ADr zrs6wzYtp0oS$^~O5B;&~!P|bnM3++I^MSCM)I@sy7E&&@Z|57uJCHLuA~^|bDBcqN zC#88q@=|JalfDV&mb_!QI!0g1U&%}CD1lkQrneCPo%a%(?uq gPi{qeahM78Szf#vy_R#kQ<->3oPxqKL7v# literal 0 HcmV?d00001 diff --git a/data/fonts/TitilliumWeb-Italic.ttf b/data/fonts/TitilliumWeb-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..42f2c10f1862e47cebe8e03113a67924c8a2df23 GIT binary patch literal 72416 zcmcG%34Bvk7C-*(du`LEX__`^lkRQOCf&ERP4|7@X(_bqOW6^SO*UB+P*4yMH&8%8 zM0C_aTt;yjx1Y{9?&C5#xQxz>GmZ=H4t@E5&wWXowkbNl-{)U=P13w~@44rmbMCok zy8;sgAq2mC1YK=+Qv*IRfknQ9SO5C@@ojUi?a38{2T$U2bVF@jJ^K!K3J*Su&+!eN z9o>sevFGvm6G52uRYP}Ab;U+|v%pMm3W6whbZ6x*`1n_4$mLKtN*;kYv&4&JcjReiv{8OmGkG!?7zS=%kX?OzAu=MKlnFF z_u%t*d`_Hy$;veasmE``=N|<@6~B1t?3q`MpPeoU8o)2@yJY5?WnzfXh40tk{`e&` zFPZb?J2$>22**AY1V7udr7Ko`5wz!kAe{0Sguok@EuXV&zTdUm@cgT|UjYOWo>NDA=L=XjkT*?oj^AdRje%IssCwwLg{esc; z9e-gjP`miufnMl`o&?_UyAl`uNbIj{kKil#i#x?$=a)hYy$a*lG$EE@gaW=A{Gm4J zS^m@0GO-<>;=g~!1;Zt5Ydp|NU$Sq-GkA$de;?3i{G|=Q-pBR0P{N9YzlzyH1+MA1 z4vOoA7U8IHK=_+5PWYSiXLg%#FaCb1@UZh+Hcpr&oD?$IF(DOyn~UooxN2}U;%dWH zk82sOeYmP|wF`F(S;C7>8SfY3T8HZSvVke;kg9iL8(kQA~He4 z{v(`XUkX-c5F*$q;Vd(7I98(1SkaG}*tbF-o~>gy2vhj`mxakJUr1nY3ZeLWweT`s z&b!#-czs)_msCPEJCExN-1m(TjQ72)6rX=1*jc%7DL&6)&kNUx?+H7ABR$?PU`Nq^ zk-p!0 zg-^w77KnT2F_W+h*G|#QboiVmW(a$QQ^GvS$O`fIKd>HQFZ&Sl+9fO&7vP#HY+yGD zMvimB`-ls;ji7npb3pUpx@ZnOFLAceH8=+}51v=Huo<80_&m^DxGtIpF-d3@ql7As zXTtl4OZ<;e#%{zs&>VPN${e`o0yO0-bPUb`&4b4UJe_4RY1isq4UIr3GS zBrKHTgkF*O479GiI{)VW444Z!{g3tfXV7W6FrWC7T@PIR1^rYDQ;9Zcetz+_8~5Wr z&eOzK#M3mNZr&#TA|Cg61sz1X=a=Z3XiHoTTzmms5S;f2xqxRj;3@#WY{qqzJuG}C zd?=j5`+Rmcc;{~6J@EjGKwA^seUE${q$8pOqC-#Eh}ODWBxKG?HWJ%e=EfDFQj0qJ&m~$@p_*PtdUDr{(-r?~% z3-7yd1>h2Kh2rwX6@@Dimj)Nfihl7ij3rx`jcW_89$Z^-k(}OuYdNl&xQ^ml>$)z* z>n2=VKy!Bqw>#f(uN|U<^{5v3e^`iP&kAPtl8}unnY{~Gc0(S120ngAaImX|QjC8E z#=in`X(`s40^wt!SNH_58iaVJ7knTWC*ks8|6;Z5VHV1M%Y4|CEC%mYxT08z@FC`) zSojd1O~O`O58+ykYbCCAxYpvj8rOc;dm2xIv|E^t&k?vfa0TL$aOrUoej;%Zeu8jm zSSM)nCsvB_e2W!Pa$V6_P4NRtk@&l4K)DBhn1Qw07i;N8;U1R59ITu*vW;vXbBa;o z{}KNxJr%nx_U_pGW1owCJNBE{pW+Sik@2zd=6Gv-c6@PsZTzzMwedUR?@2HxSk0o@ z&m3$HHAk9b&1vRF^9=Kxq(6NB#P`oQo#-iEI3nE3@>mh8V$JB`XVD}+FTR5wHV^i2 zKK93WeY`0?IzFEFP{ey!8NYd04}Nc^Y_jtoliK=Iv;bMbXGZ2@(&h$F#m%oA5?yj@`3fer`4~h|EOROqxk>t$4^vK+g(jgH`d6#a25H_QbV3K~61s))LXR*(mRVVW=<)HoC4*(Pikt`>F*`-Gc>+n~GME*ul?5RMBcpsSq{?t%t(k8rQ>8{t0T ze&MumMtA_S=waa@;W6P+;jHjD>xXvNFU$c?&lN5M)h!hkv5mqi=p#FXt00lKLjvz) z*9h~#d7HtXSFkpAm9SV?kG}5|ekW`eW(!N$Hdf58W?LZbHn4fn6SoLkg&SEA)3PaS zDx1k>v1x3&@CdtBc#7HCB6c}jz!uVMusOmSHiOM(^MxygYlJI=YlR)cZb-{Lked62 z8*t0@!a?Cy=%nXa9c#h*+yJg?0`IqiGwKC2>fCm}&n{}r{bRtqj0XmOW|gd+En(NP zQ|t-$2lfv8gq;^7#dL8zbnBDiTarOam6l1Tq$i};rN2u*`)GVheNOp2@AIRt*|*Vm zhVL5RlfKXTzV7=kKYzbSzhb}fejEG_`Mv7*qbgZds+yr%soJkPt@?xN@2VgD{rpY- z>Hfw3js6?_Py4?gpbaPu=nps(a4z7yTBlA`7pga?kE>r${~VYaSRJ@L@W#L=0$&gO zO5?AI)f8%$Yc^{h)%*qjz6y#D8XvSX=uFV_+GuUIcE0wM_66-b!Mfns;L_mU;B~?G z2A>W7yUtH%&=u;Mb!&7t>YmqK2x$%35OOr+g^=^1si6}>_lKUtdL_894J~A`1HgZzr%E&#DZ$(8$)kiIlIudm*S{H4N?vLId{cQA)F_|%~F;~Q# zhR_~iI$@%!SRj=zwQo-jXQXTnnnUzl^vxC`)&th3tZ!LAw`py;wocn>+qJd> zwkK>KCi^5?lPi-aBri$6BKg+j)5)(Vf1aXCsZQxn*`M-g%IB$}skNyqQ;(#cOEaZ) zrEO1pDm^^CK7DTbjp=97|CyoAsLoiI@leM3%<{~gna^hlS=m|3vi4-1&3Zpuo!ywd zGy6>TCppnMlX8yce35I;os@fX?yI>M>{ffTeX)I?{UQ5X_Mh`2^Q!aa=G~n4etv2G z;`}!Yq6=mf++6T#!M_{^ht*N&81IqcWQ2BY4 zwW_{qR@H{8qg9VoJ$QG2%b54G>tUZ~U6#nu(p_1CSdySnb7x)7>@7L0%A3euhm`?sRN&W6@c?9RoFF7wR2B!n z9CiopM05O7SyXoVLM&gBUYmXp*Am?lx-x4s4`$S6@K2}l#TojkJYA{0oIXubLlUMt zuNJ3DTFCP>$ZR3W;Ab)0(33TbIl_(lOqQQlV9zz8x9~Wovf5O!OqGWhdv1Yb2sb(` zWHP_jn9~=Yw131&g|7TcD=W7 zy|`?pZ=h5h;Kw9WdC9D0Z0Rch0LkI+`=7|N8jB_LyDrueZwdXd0WkUs2c18NMdEW< zqk^#trC?nVm|5i)X0Yd$13rlvnFJui3@R-%+pKxMLvI}g#ypEz%anVWEjuj09F<_W zpUuyeZ%H*T>Glt8nHQ#JjpOlVewf%a!_$>4!1v25qK zh=lrv#8Ah7-ER_hPPU~q(`~?Ii}QPNz4!#yOEa)3Fo5BwG8Q=O_^q-5vDUl-hZP8w zatjcl73 zx6ZTOT+%vk{OZh0SC0GbniPBXl(_@z^0Qt_n9)2(tZ!P8W^OA2)qt)JfUd$6y3&JM z-mlJUP{<13jdN~g>%`B=9u*2K#xV;4R#3qJ=*!s}sUa*Yp{~H#&{4^DKHI!`O7z69 zpKm?XZeD+wXofj|cJ5>r@jN|Gz-KXgI0ot$%q#_t6T_bWe8l4n+T9BFx%d<`4bVHV zqOv(`4x_`Uva`>xOwLYS6uWp~?4sn{lq;C?^5Bgf$sKK}?HhHQXiS$11~C`=`#^A1 zzCGU_W)HK3S@JJ^yza>->)x$>=9${(o+J3ab#8(Nu#|Yph_UCC&FbFr6`r3Ze8v{C zO!_;(vqBnMm;G zTzof6JPdeX^P<@X`C)p?nb8b%=oIQ?2VQ8}^JIVho~e$>QH5(7FR_bllTPjnX2J~ia(}AMw8p}$H?K~c85JF} z_`u?oTY|61pXbQ!ni!>;As)VZi$h=Hn7<(;z`w{ep`ttLv;;hb1 zUD5fGQ|_O*>+;F{g%yiS8|oU2^3O>3TxNk<0*s4ph)b^!~Z@p~q%BAa9Zp)7e z_LW~XsMU#^C(Iq^$iukyIltlK!YZM6B&e8;Io{E+-BI%8^=y;B*jc-L;5DOYP9#}f z?fg#M1USQ>gTpe)a*6F?nL#UA%sK}b)sQTZ&K8m_05=R0+D7o^$y%O|kVX;aN<{dtqc!ws9}o5Ct;iz;&ySahQ)*V?mX;FkWGHeF~( zb)T3xz9=p=B)BPWGAK_J+JUofz+%KMfYUx?uf?oTjRUg-w3Ran61su`TbLr1AG`ac zI_j~j>Z^>AjdyH_xvctxPsmNvFD;r98@#G+fyPHX{KxF##~;@D)`Toumbf=OV&*pa zhhCF^Y1Vb)8^X|M7JB}O{SKJm*s`l6aO!$+>dm-V^U|gAL*QA_DTDvNb>BzpF1Sf0 z&gx#%jr+EI^%eUgZUgQY3PEBZ`v>tGboQ{M{3J0@{tfGvr?cDHEw>%%_*3;CX{_sD zF-T?WT>a)Jg!$rH$L^Oq+5IfPuI_hSh%oF#g2Y0ly$qJ`i`&@0(BAFx^b5DO|FQZ{ z9cXKpz}SyiBaEDq2gPOqutjK6*qH%d_G4v#U4VZB^9!8RnLnqX#^~2ueTRHQL|dd- z(lD_tKiifNQ&v||lU=LzDT^4WS-Fy6o#=E(Z;P*Cx7EZc#0WChidfT$V{DL4j!-P; zA}9l7>>y&X^2rG^Bsf%lbPE*l=@sd?vF-$PYP;y8eA_le=SU9(j3Or8A&9z-n%oZ_BSM{^7NO`FWL0)ULWT ztBQm(#s*)CXb{g}_OSv$c<3!QaExS^Osww)ZL6O=(8mg&{6r<58EF0OZ^dQuRtDFE zgk7Z|F2{2k>}g{-R)BY6?=+ZOpgs{lORiTdd#Ss-p?LhxOj!6YKI1bc| z&l1m6?(L6Am~hDz;u-ncfhq0XEa%I~wdvqYq9u|cEBM+k_$tItB16T62}gDX0|(^% zzAO2f-s)CUNZZc&@iirtt(8XKKK1jb54HD)`j!TFSM9vDwp!y`0r3tV!>?-rcOW=k zNU-SbB$HHHF%;95hH>W>u(B_YoSQx~FRJy@+u}-VuHST9$xQLgti{)?ubf@RB6jVl z8hDdDNQ}3&l(!|=xu~>5aZFIPA7)qR9>uf1f{?-pcy_mZ>`3b#I5N%*kdaMk zUm|$0J_1%f+Og;@%pzF;_Lg*3dF7GTy@B$hotf%Ac<3Rqd7xEXHgK0XkvE{s@r!t# zxC77Ac$J5*p0D54a^%XzhU@U$L2(YATQG1F`V@ubn71RCHx2191>w0|4q45R=82@u zK$(GJ{rk$jIkinSJ2LA!*#|whzCE}5z{0oJS0xvIxU8ivspy}1=Qq#%;`3dvxM+3- zU{J$?#P=);)%w}^q6w;H^KL3=J@S{nu3am7CTHc}(EQJTif6`kZ#!1Id3H{=e3rws zl*1GPdlRV11q8`Tu18xvp=9+-I5a)u43XJQ8J43%psG1cSDmj33EFMit=cvOuM5sq zfKx-frL3fSLY77EJIGwWpWL^op|!2ycve&RHn!`QgKYITDY$#=V00Ps`wn0{mZ zl}o$YBgF-wEe*+uj>@WKn+$%Xp*dvs6h z9&uT1?Erog4G|ySh5LYg2e=LdZI@_ehTz9LzsEOsbc8N#TN=4`-YbEN+m@*JOla<4 zAIWjc7wySOWpBty$qFq#i2hC}{XtK&NFXM$uT6gFP(YiM0V-nx3=kj+<`}~7iSOLCSHQaiwB-3c)?5J^>`jM3tT<#$*n+b%!OYH+_5_s>`Q;<^^(AGA=%BRY1(Bv<;eR-HT}oIT|S ze9o1%rrO!;JgJ*PgmXK)Q~Wo_J;bJj>yu}KFZ<19A-@;@T{RF>g?rB8#@*uMBxjPK zm$2RKTRZ&y#RvVdACa9`v1_s4!rak*g!W^TPs%j zyM(jir&IGLfZqE+7iK171q6i}K@P;&aZK_H77H9&NpFRmHbCRAVDE0uU(->x(Auh1 z`!s2Nd~>V?f2p4{-fFHL7Zs_#q5F>Yp(^>Y!q!M>LRf%3E`t3|J8#G0)T)F8pjW6v zKSAQlupkpB@C6HeOMC$GfWjf64^lh0($21KDYmA!rKgXdl5n)SDyPWwcKwuQy)`Z? zx;fm){?6*Il2|N{qA(e7UjW?UB;N{L;JQ?1GGjn5@xxj~`k?AWp0%MR z!mqFEa7Fy0n*H1N*#leWPHJ19H7`r&R}ypcHJ21ruj^~D%}OgOJp!C0IL+);@g3S9 zDmbA{3lXgfYYD7I<&14GTQ%C`dRvHkyKd{8vZ|YlbdEq%i};S(r&v{3m0BgYofG#| zr_JpKC51Y}#C*VNhIPMyFhW>`hFHPyizvvUvhbaf%})_|R_u@n_Dp8O81lF*e6D-O zR$a6Uz`TQ4X{Bt6vScJ z5D9jvTx7e8OT%Dv46Vs&(5VAjTTZkz1jP0*xpw}93qN&PTbe>Nf%1dm%ks;C2?dxR zw8=8K?F1Z4t0?Uss#mnEtPF1nW8caeamu7g*k|1348wh-BLyk`P zW!@InVxVW1HsqIP=81RWNiGA zDS!K=NYB*cR{@@J=`!^vn(q)_9zfKR(jL@cnePi}yQPd?#ka1;aAUa$<#7l*w2|eg zVh`2Ew3&=;%`2Khjd5k!g%L*IfP~(p;u1q~OR>H>x-KkC($ZUYX-i>rw0wqrJ2BRn z9v2;}j?Jmvbyc=>!Len=j61{X7Z`2 zhh`t&^QK&M*Q)Ho`&P{&o(p!GMJ?J1QFZ`|tuTv$D+neSY=E$EZcb#^UQro4Yh|G! zzxhOy9)zNc?u>{mty3Vh#+;z)*i&$_5_YNl12XP<*$h zv)-rH$5H4vFR;8g6w4U;yx(aSepcj>F~lBnfAcx%o(r_ygwAupxsk0BzaV(@1c${z zt3;T=NDz=Fj~9YT`EULBV(_rN`V&zKK&C7OpoFeb05QavJp(&U9e7+wGGpO^RS-)Q zUtf)W?BS@JlJY}B)a3;+H|P`XA?k`}%8eRLd48E$QwG8j1s`Xj@D=c`*q0!#u(mlK zbNOtTR!(Jak<*5c{h;%H*oEtf(+e^gx0n#!8dcD2u-^?Qb|4A+pVBaW`0EVD zsPeRG`CLe4XcaS8+n3A?@&J%GolNotaQYZk;Rf1rS(Sy{ zz^-1LS)W%Q6N6uwi!}3r>q0#x5cZ8_pvd1UajEf4N zb7^bJb;X8wi(&18vMQ6j&ze(V3P?*`8RQTh+Oj;o$zP*3^)^omFU%^-PSE&NYnz%( zv5qcVsmYYryhh_IuZ@lg&<3fx@?yi)@I;7+fn3b~E57KWK@z06&WDwUuQQZ~i;Jh? z2gsC!!@}+2z3@w@;1h%5od|7=%)tUFUr@S?xu8Tdr$ZyOZ+y%0Z@lr78*Y$KUw(Gu z#>Zw^t&*+I7-?>hl2R)3@@_x$kK6E{UHa6vZBK1)Qs?_+EXq;kXn z{(7vQ*dVezi^>54q4ho7sDjndkNj>R$O^GQ#f?40<7`UuO8@3XVOpPfKYfrjHlS=u zNT5$(zI~#fxGyF;HAvTz<}Zp>k*e(AaBZ4lr^Y8oqm7KvS#+r>l3%$0RS`ixHNH|3 zHh2U>C!)l@X18FE1bvGI(;lZnd2z6>!>{Z$?A98U7j{eA#2IxrSa&Ng;HUM%D$&L! zko`rU0H;yHdUj8}e1J{(=r*+NGYp^6o>dC7gpdve8-yi`h1qlAwBsn< z{~GYQapS(CC51)d(T?(*(h_^Q8=pUHe{wqzD*tB@%S!J{)2vu@!`!(59x<((L_hIS z(&b3Tf2w8rU~yAP3C4;2-*M3jTLR6kAh>4M>Y7>B*WSLVz5QTrcV1pMJKl!(o7x^8 zZ?|{n5su~wRxueiX!4Z`dJiv|Dj_Uk-r4#mpR7O2elJ(Bq}q)eYd39rtd?-g5U+TS z-6F+f--dld80Hm>3Ddo!z)Dli8oH*pZqmN4T~Z$48*ZGasgUA}+v-Y-3n%25^&#pK zET~M_$=+m#&_+By7|1c9Z2aIzBAq-9R&6L)yghp^yRF=)_K8@Oo~sKjoT3(!s)MSu zaRH&Gsp|5yUh(hcHvh25u$lg1p(-W7FRCEYqEi(@@j=rtnT-dJd``H-Cc_9ifX#P& z@w95dkOn;uiz&$%ZvUvDeJ8095N)LELA7{gnBv(J>uA2SIgf?86tW=!yD4m@aAmT+Tc)!DZQV^3H(hdb<+$d0_HNJa%7LxohJmdJ z)PWGE=jRFE2`?k=2k_DwF)y+$^1D^5_;-ko&Or37FX>R+ZOymJ4y;+gJYh`5j`m3U zzO*QHP*OyS+E;I|g(U}NI0Azv`igcbQLmkX4FE&z^Er086i)U7lC2hp>JOKdwpEsP zim%qoFPzUJ+SrSDqkF)^!HO3R;*C7!u8pUWE5Br}g+^VKUMz|Jq2tPy`|CRc6I1l| z#AJhbeUeq36x-Q2D>A;L?%ULkurynT9vU3AU4e*g=H|_$3Yr&{ukylT6{eJ1mDkSU zyeSEbq5n<=2P%Yow#o z3MX7RL{`Niw~ec<`O?Y3wm?(E+S(fMNx;DUBiafnnpSn;7O!nhk&b~sv|PqH?4+;{ zYcA_}%8a^#8S+gdT8!AabKJNK{}|Dv3%=FTZO}I(_^#3n-EWxA>Djy(YMiGr=7xwh z)~Gpw`fQz4G4Rodo)QZag9=I=W=(#{h4)AH=fiQ^FI|J4X=T6;gyaM12cG?~p0IM` zrtos(z#m7no=M8Xh1W;6tIS!R)CjzUlfDki4pwqn-LZ&s9~%s^b_oU^c$2XrknxVq z)%XO}P7e?E35r%cZ|!V5TyN>3&Zy1}WCmk_c$>}|lA9Z$OAX014a{=S>`GN=I1@8f z21q=}4e-0PReVGlPXYxvVY`z=mRbBmw|X`u<;6)&tWOLLmY*qNKEd*Ti&;sq{P1JH zn}jwWX%xSz`>qkrzN^?;$iZdK4DN@6!1E?g$$F)z%_CfMX-vhEM@rhz8@##eI(!2> zD+h6cFM}e9Kc3N)6-QNrwc^*a85 z>GtAzWM+tcqEDs$W4wKCUH9(Mba8>#F674)8u8XmDshCnpS$>+#3CY5fU9(b>=(ws zoLDz%hUAG}5$Q2i1AX4{>Ejyj4oB z*o-Vl%6}pLAOittghKd!d*t@)qeruEKaz9oDEs5lW7)U6J`=9$oV&%Br5GN4VS`?- z7)H5yL9yqe2^cxJ3KGX$c(bVvXTZ(enL9pfaz@Xs3p%oUQ`vj*8{mW1TH-guAFVx> zp8eV9yQ0^JZO@-})%ne)%@N{RBizvoi(+hga!%W0z9#8^~ z6n*DN_ozb6nckeF?siXNB}Tj6ysadoMtNMH0|k%vx{5Q1fZc5(j}kc--rH1U*{Ei> zDMag~<@s)!Rq&wD3OP32^35AI`iM#lb>VXVD3mA^((fH7r-1M+Tt?HD(B^<01sf%@ z^{_q+ymrt#%ft7HX1&Z&0v+UsU8QB)&)WntZ3i9>Z>eTwS=*jWv{pM+vRiWz| zE{~iezO!awW_Xk}q9`MK+7hjANl;@+bb=$TDc7E1sjO-r_)!d5(#K^V_rbzHOMD6i zVaU7a#S1HeOfTw*t%P&;qTX0BjXb}^f7EXw_RGJjUy$rY`z^<2`=b4_1GE!&_iGkE zhTaiHHf{GRd@<+q*9Y?_v}E@!yqFJKY`N*C2@?kHzPKyeHSSu>H;p^z7x1kgCBQF= zh4Eq20>d(crSc@xWVJpu*th87IJqY~KCm!9J2tQon?kZTDYWn+m)p@i?$phV7efzI zgDuw)TfTfz3~@W*z|Mvg=qciYQauxOnDM z(-)$ike~`l4l#yY6j7o%IwjiQKc_r8L|>(gj0}y`8lnQRsU&(TbAB&{z@L?htg=c- z2iJbpY$LhA?T@13{8geTjWFXHVaZDJ^TnNH)+WD$Us6F{J{D^~75m+!#-`|?Iro~= zuS}_k(D?@S?&=OMZS08AEH7T7EnBcl(gw?~sO8t~<;$gz#_Fz6Nz4tDddk}kdwcw( z3SEKTk{MHItgALk=`kh#QeAL!^Huq|4l%8`%&M(y@beF92);hm=G)#r9`a`jY&{gO zkO54S{P9!5gWR?r+M968v*JS-j2g74z8$qOS+%lk!pOje^5NT(&?^ztFf7c$0vz(# z1IQqXJuv@b^Z_a4V@4n}KmvJ-LSWBx!NdC!b&xy1vM)%UG5fj{Qpamw`CQm2eTiJw z#85=COV)@mq$ws1*S>FfaK-zIw7Gh$7z;(x$kEM&?^|3Aq$M@sH#k(DF zGx5nWJ60I@zur1l;uBJMoDcRv(ES5Rr=j?SXv_%38Yn?O;2GuK6Wms++$)hzLiZK` zqx@cOI32uqJ;&kjJ=6Gu`4d~S`}*;eOZGlUPja4wq-PslI2f^KniuX;A^t)SDkIvj zJSYksi09A87|c9kL9s*f@MPMZBh?6kg%nGx&4DB%_@Ug}b$hOu7nr&4(y6HlqI&O~ z_$I_BOkW;7F*`k`*Rdd~YsR$3ozr~Tf@|0lDcMimYfmZE>Q?Oy2?#KR_Lnwi7V3j) z^f9%W`Ay#?E*hr}=yl;p%)~lj;+g^6cdp$}n}t?wb^2X*lriMG*T|68G<$YG%x3N^Kjj@M9d z5r;V;n6YQ@j*HQ|t`Q?(QDM_GhQW0@P3v3aWk6ZOCPLxsV9>OJFYM{P_daMJ7#@Ez z!{w~=(osI`t-v!8w8>ne&J{9L5H3oSq_LjEK%+y*Y_B8I9Qpjmb@FOmEAe_?X~@iwsNJ zX9vx@M30b|#fD4M&K#PpU8P@_JVA+eXA0~kDgWoP9B@VWex2ZKzow+ z2n`kwB24raron~LK&zYTJ$x*L2c`YbUG`}2ZIj6I8Ua#5x~P|i#c2wO4z^D;3Twci z2I@7)#I+hs@Hz;W&grEynor3!4n?1XCw-7MypQNdw`lypYiBO?&3k8}5=#Pp=5c33 z(IkW>kBE{TihxJB3!+LC_ZaXI#y1ip`(i`x2wo&dr^IsJx4BG*BDkw2jIC@ zWIMEcbS_H*i9D@-aO1*f<9}4G%=Reok@|l5LkynZzg9R z&)Es5{d@vXOry{k9>eFb!IcWvPMA>j2;mvHYPmdU9)=^)Zgg9jiZZ>)B zr2Jw^35F?y@y;(*n0sRVJJl5E?6I3NHFB85^+Tc07%SxfZG!ECdxM5$!BG0!pgvE0 zIz}c?6ig)EvVo*1Zr(&6hGb@oXu&y3uTQk*>$yuQ9qMpGNQfxIGL(zO^9<+p*F;W>foPj)#mRLO5E7xk|3 z4CT3=>M?`+|5sm7jc%kbD0J7weL*DSw_r?>wBH<(@a~Eb6q+(-o1=60owOu zVV$LMVCDIR3=aK>m3GWA?H^oaqYOS?$A)d>(Bi821(!-UyXOR=#dA)MLT1UAj2Ke+ zq)8W$M(Osy^07b$KlH2T#6I?%+yoKk73^{@!;qhd^_p@}p$WhuH>d?9ctDNCn)NGc zKuMDP{&48SVvAOBRUp1S&NYUJI>@WZ&PJY3lSGy^zGgwcfRhP14~o5qR2?z{LDNN$ z3pz%cJkPFS#r1X8gr@fR3RiB8Vvte!$Jhyo`8?C+k&2ue-`49kE#`?f6UZhbiKeP> z0)@TJ^KRU8anj{ucS$Xefr(2LX(eY2ACq*Fgw=)K5px3i`k2oNY+1iVUy6VpldRrS zHvW*IX0q5z#Gc$#Isg_#AEzO4#J)-c1YXYdy+xxFKtW|voJCW-CGjxiG}s9_Pys9 z4h-#odeGMetcM2l1{)yHn@S)zAP+F6VHgt}3G0G8qnn~xHGXx3wykJxl%Hs9TA9hJ zqvR*p53F_#e(K}^Rw%!~eptO4ekK|N(T>thk&e*h|voXbXN1o<)WBZXq>Q-b4LYK{tKUqu-s- zOZmOrp90mBdV)5h59Ur19+~eR^PAj`1B=#3y%OV1_0T5|Q%56BHm-3Vg#IYOqN|K^ zg!HApvpgj$hvB_p;u1MlpE>h*Z3}4Rv=dMQS`-B*yI~GGkE=B0_@}>AQPLy+cA(YJ zWFnP?cwBV8k7qNX_vt`={ApJ_jPf{{FD(sKR=^QoL2c{|YYl@fq$`enA#05Fydf!> zZ+;bA&*FM0aLwg5&Ab-+r;{+?(m5$sj?*T}zf+DCA32TGksj_*e~!@~Ft+ z<~EPQ9Q_K5Vg3f|bIcj!E1s9T0(wONRVX7mLi&`FmrGHA)TNS|o8mGafO=fnxi=5m z4B8=YZv<~j)P|8ns^n4e<5S2`W*S!Yqv>_3UofKb2jmIbp*&yS4xyC`C$a-jw(sPL z^)muJGk)byUq?H`@_rTh7SCg9FnJUM%1fUluu@oyR=KK(3`IZgq50*x!hhw@c=-J& zV|YFuqO4*50H*~3W979;B+5ACV@&|k5?-M}+Fk9_@PKYKE;>KP{YoA(+NS$y;6=E* zAMNuSDJxllJC);*;KuG-fm=*<)H+HAtDj&)trU|Og!_JuFQO&du@f!bk0<26P`)$m zzgv0MGu4Y0*jco%-jZc&?9n+XuK^dDMe>Bx` znUjVMTCBydx#$ZX7`q$;U}DntbDmy=ZW8p`1v{GIHgyBQec5hL`agGUR#n zE8eU)KY)MG9`@g$J;3TqtBNPACQR504^Uk*JQ7Z{q2zOu|7Qp?1W>rVq~#)vfhzA= z-Twl8d(G^=@Q>F?*-&WYMd{FpeG~T(@ZvS%ZOXoBK=uGtI>a|kvp|qT;3{i(4*1^4 zvnIX6>QHt|-oZ^*wn|K7(wgZl6t=On6z!8OBphq!#eK_UFByS-nV7m7hS7l5oSf6Q3*nf}V{T;Dpj~L&vVaxaEaULJG zm@9F-pW}QG(m}|7!)1#v_zV|*(#m~J+ z{P~ys+$7})k`W_hCfi+U@@=-0XQ5Qz6dFQPN9euh00szXd>raX>aOZYB5%* z(=v|DfB3Yz<+CR7d}~09zi3lkb>s96<-lSu`m6(Q#iJg1(NL@yyj`#jxdKgcd4yOr zQb?2hY>4?J>rhxMLn54`z;`f8>>tzWTcR~{#w`mkUE5|&4T)RzS$Nr+>wJPjzpTQVLba%1trRHhhL@tkN`PX!N~t%Bh!(HSZ}FT;Spf!NaS=6}juiE!l(vqM)PL5obzuUjUcaIMa*6_0FhZ)6)D9 z&!RBFO0k#~Q$~OlG`v7evAcB4gsBq1a?$@KJSU6jL*Z07<=rUpRxIxlg<})OsF^3- zPKu!Xf*S*)E9X(a-~&*&MgQV+)}h_lSa+!rGct|#sSZ7hJyMD&=qIiMu51Tyoe zei%sw2K}DjDV6;~13mK$Cn{zB;NqY;o{u@E+B@mq+lQ@gSS221TaVm{*GJB-zG7H; z9;(=rAr70_?vO^CyIhZjs`prc(*r>40xowUvt4sZN+gD)7aLu^j>GN>g)ZUxy1P!H z;%_M8YYJ8rJnvv$D+q~2wDbFempVdxC-~bD#e|r!9B?f{yV&uN)rS}B14xvTcPodc z^w!V=wXL-`>24||gvtZ;^1IAd&tQ-syiSG8JPy2K-bU0k8Zw(;cu8y8 zj{S!_e(=$Y#t%wp)@%Hrn#XT>fmYnXw;>*L7Q}rL!*(H{*wI;JuY2r3=8l>ukKM=0 zvF9lndk-T;y^c{0Yotfn>)4tHHy{+Vo2ms05SH{53qF~i(NU66YfW}x(M z%2eRl-(E&8CYxs(gME2Y_%Tr*;owQ&@_$F+K!}P8vGK(4utu{g)R>J_@B!m!Y`9}8 zKLI=@Xz{3hr$mR0NF6aHV`#>^wbaW>=x|Ls2`6V;aJ0c)4d-%?{$>*t}WSs&HK0@JK>){ z1=$J8^DS@~-Oh3fr{mS-$v}q1+Jow?EGgWlBBny|*!PT|&TGmJ4s@B1CLmPuH#T#7 zaYaG9)KKTDGMg|sWX9$0Ec8t^;NX4shz+Pq1eu5#tSO1YaKkf*+TT!0tr83q>3CML8-eN#!bF%g!h|sQlolO14rs zDW9`Oma|2>JJ2rKptRI3@Z~lVQCVA;qk2Sfxz`4*(8FrXDY5aujVLHNl>{r<99^&u zrd8?`C{Y;KUHH}o-KgA3B_7vh7x#!8R$BB39j+V%$rU}!qn|nLE3oY3G^yjz@QfFn zz~ajCOm$_-Pags&gTmg|jsOSaf)*$$^*vm+RGbQ-VpRD7Re4z_b~*Cak+pfb+4=|1 z`nIV5pbNOXuoe=aGY}ccojHq8*U5rK#4fbrB~fK3Yy!v9{h$UKni{t zXwi>+7_K-z9=gTx!)7bGth{4*WkJ4Mm0ueyFUW*QXOOs1yqB!1Mqbbw>wv46ER`AK zreLxzYuU`0cy+Kfwmf>yJl%XvY{xE}t}Z$)BQ)T)kXz>(yT9=hE2X9EM5BFWZ$PzA zZD(SmJYi-B`J5@nd!7$`&#)3j@{AAfRY+)Ktin=MLba(bR*9qtQ~fs9A>WHuBe?(? zIEO{ZRglD&$559+ucfGS3%uW@z#QmkC~nYOuI^Y16YKN;#9zMEBfxl6LFL?0Ym_8Z zO>MexyGOL~s6xx4*6WG|(L?qbX)A0DR38RfmbZ#3JUI9wRaNX@ypfevZdYsyK1vQI zcumQ{ga_sSH4BsMTK_MZmeE}YuDhgtwszs-@FktIwev0sD<y{c;raf?IcF=PDYFpo)H>>Vj zm1I0|U!c71u2q(ZqxbuZuKX_8t6=w|n)VXURtJY)+@|ve+2@`cW2fHqy0mrdUAidb z!Up&WY!<)5Rz=Y$?9YQ0Ya7Fdj1$!+`2~Vv?Ctr$%ZlG=HpWK#bIK{=Joxs-zGyXrQcdO_rI%h zbW zlWL>gJ5tef>zP#R-_@RKxUR{rKtA5f!^;@+OuUCilrkQKjcQC$TaY9pVfzUy!oa!F zu#t27fw5rw9CM8JM(DuQ{0Ie@1#zuxI9!VNL2}_usd*L-=s=@(Tq7ZT(_QrJ%h6Es zs#EJmf{D>8y84S8PP9D&Qep?{Y6P^OyVE;I)Px?a&8pN@m?(|se!QGttgA3O$jMhx z$S<0bHE*+7_fdQ= z9~>*EKGP*uKOVxsz&0-l_Dt)>3&Gh0IF%gaOu*?iwRtr3t_d9JFs&OsXYaUnTB9Ar zcMpN7Oq6>cNVOG_d(UOa;I69&(u0i~+H!HJF?`46&Ko6}5d><-9IN>5lrk|dU`pFO zk}&DikcWnHFx`FMiaA5{{^$`8?f(X`X&xnO0Jwl)@(f`4B?9a+~&hyT7!Yw$x8sE{mFRA!$ z`v(~3wRq+={tRNA;EuH<;rcH<8AMH=`QQU;KoqF$HwW)S3)BkrkVW_YS-Fk;r_Qx3 z8|P&6_(e!8=m;vykGRKytCwzL!bazVr~z;eWM0v0-J~7;-41KK)S0;nj63 z*&S`-6KC6)9L&DrIBCQ)Ry>0!MI7{sr|9BO($lKP(hr9mcHE}45PHaQDD+T9%h^`; znbOQ>t?Y3HNB7}W?Yr33ga-%e#NYSxLKZ83Oy5bArz$Sy=WZ&ex>5VCD+{Of9l>=N z*HZoiJJ>DGAEeS@XTere{IA|sy_V7}f9-j&jsgdH?RY^OR9^8sDE6o51Va+eiY;M_ z?D7p6<|X6(L)z$^*cN_HtRc|fvegom5SoDf@4tusWQiuJ-nE_A)OA(6#*vxB>su@MMeSW?R_?1n zY_d^RF|5!oZeq^4&VR6p3Z0^UgmFAok&U40u%NSBLEpDa@sRyI#)~S`;P@OWsDnMW zCt6rTUtnZT>FA~8BKQi|`5&s>mXN(`;co^Lhvr)1AF*)rx+zo*(sOUIlX#B2LwRNd=YgbgC?_vU>T(n!KfR(jHoM?4 z?KV?Pi$UWXy(lv+SfAIck;Ja3V87Ev4Z)`1WW!>$`?953HQ7WEVPkhZ{Q4#lf5^t2)`(-4vn>f{h#1h^(Pu{_Z8nMZ~Hd*1K0?` z1A76}Nx)=+^%Hh(J*~F94n36v;vg^LYqJ;$ogHskFFrjCw%z~ zNtb2u(Khr=4`MMD|%ketssk zCP{THBGJz;T&;-%u!JXq=^$W=qI&Q8T%J=%fint@INK=GKy~jBt;W--{JtsItF$<6 zAX~5T4Qk4t7-(#%O|k`rq^Fw0Bh`MEEUhR8Hy}9OCt8)5r>kB?Cbogshf zR_2ef&|YddZ|xt>TKz+uMRe)w+|Q!%9PMJqGcJWGV2J2Jlz_kBg^xeEk2yQ_Fst zrK=p!ez`OIAOm3# z=D*0fLy3~C^N~Dlx}PpJI4kwK{+c?Yu6gG?Gt+40&$R(eua&=BUC|Kk+oOIWraZf- zY{8Zg-{RmYZDr|Y<&m3%e9B0!iNbrVMSNFWjk!X!ENa~F)4Fj|43vlv3+j#7!rptK z+HrG3O?^XNO#aKRwVFF}lyOAcRk_^rsRS!$6udEmGhHSi^keXDb-xCZB}Lud01 zvCu!9J8TJIpS;{w$G&YiCXKsrpERzv_QHKYH|}4C`|EKA5aa>rw_yzb@R}dMma(_w z^-ORIY#UCo!Eu7(*R|pD!w&g)MBN;=oK51-ZxG(a85uF)9-Ob95VnEc{cbIt1-sn2 zMm!+}5Y(7OavTBdr!UUsL|HMU0Ec*lWw9+awY{RPDWR$%Eip(+YEAU(ALn1` zlUpg)T{_uMs`M?R9Hq5F4{)>xdlU;$KI)VqoP{#-+@S}eZnpAsha53C>Mi*=cPM|< zUzoPsqzS}PM3%s^g5M20mI$z~b$%kIh+(9M1OL$PK>Pf%IV||R_LJJO;%&$5Z+%-` zTDW?DE{>!=Xu7g)W!=rD&2=jQ^QA(#Xk=ew&4Uu;I_a8Wf<>eJTh<{@VkhWgC+03` zy|H$$oP!o{S)=aTq|*1n)8_A;L1&)~Vrxh;EZZ){z|AMz2l2w#eI><-X4 z*%AOTLp&ySIMCmW7VO#Wl+D9_=vSXS86f}XWWelM{&hy{>{&_1B-T_@Q&oj4B4TSq z1aww9UypKyKzBrQN*2#h`VMX1l)N3>`-c1}=>K7&T?u_-fxna307j1$ zuUtgvDjC!Bl>^dq%X!gA>$ui!r^l6VU3`L>dY9x(txk>e56=1V)a35lk1jjLVva8^ z$cxV_jVq2XLGByQ^TMyI0UPuP=yupisTRD^x}kHa#R9KGfph$f6{^We22v=meFvfp$relM(iQC!V%|I_c=|h^s*_-L;Y$^jRgq_5dlFgxN%oB=&=jy zn1Ru`nS=qeN=cQ$(JqK~gOH*4N)?0&&NifVjH0;NlD(;>DTzJRkp;8&wH&;rvep#d z6g$4DGTeWrtt3tz%pN_aF%`~itVkMHfAB4xZ<+Sz(x`wm(@bMR7)>AI$iHI$!dSQt z57)AdIo5`<=_dKva+acE_t(vqr&-uGxG#BqQZW+qqlXPGh31IVcA9TUJ*e~qr9KyR z4J{N|p$9$CfeN&Su_fbsH#A?ib^Mg=6>G!;b$waIFHVdM3s30HwX=c3%uCGiW#jsb zvRCEB7gSG`D=tk6RTpO#uuVy^S*Ad3S~4O%fgcC-<{yC{p0z*%&Vio!u7*<`p9VU& zlq>6Wq*$1tt!ek1(B78a`BAg?wA^wn@bf`Tb9lJlEL%xzKyagW;<&_1Tgu~XUG=xU zq4PbDI*Jk2nc;C^aj|fWQlF(x8E4}@ro>-4E^?ko>G1PJl;iCAc_LeepC>}6*}KmZ zVZsU)AkJeg#MiW@t>8ABmTJxq_|9jt$9LRjT#JnWpeUguG^MN*RoJrwwgV-}PrZ~%#RKh>YJ1AQ98vJ&d@ zO${BD?Buh}*R@4Y{QCLUL+$4EhY3Hl4>-xvBprVqc+bKPo){PTT?LMNvo#WbzQ9P& ztDkM&JZ0qb@y_GyUaY-Df7k-kd1?Q4ogFL%xXvE-ao)l90Y=yU!P% zC(1JFJ|i@*5rx(#&CbJFDA0qt##fkrgc?Y>l!wzOnubSNSU}LFrJk&v3=ww;TY{k zV$fU7cWC|I7}MxOcIsn3K3u0>Y456QR7T93t!?qd_;>AhU#+n_^-8;vW3K1=GQ)Ln zpI(jW*r|{CxMQzg-qF2IZ6pmRY5Z=V{+1*4>0_=xOsC%DjBCj2I?5w)eEWWVxP8A~ zT@%%@Cr%`izlZaVJZ3iZFdchUd#Ikh=x!tB-bA_o@R-?k9sBmtV`du;*SR;#zFGZE z-A@_J_SWj(zZ)4d%Wdu8$5PgHl=b6C|Nc*pndSd}|6Z;8;~jkQ6>S4~x|uxvxiPbX zNFTpf=Yz(~`XxFBjY)}7Fa1s@pC_gmx5YdXdn`Te_Rolw_ulasWqBZC58kD~+ci#> zo~$``s2)Fk#Io_LPa1mGwGWNDWZdeZ^X~iLFFk(S>%9Du{(o7vwC~9YF;5(|xz`gD zK796$#RKl_@voE4*|7JH-!8lL>H)u<@|QE!=iSaa?V5X>ICPA5nQ>oiVeClOOU)Ax zGjC>(DdC&RcNG`N``R4x5nNEAix=C*5;ypZ??K4D5N#akHQP=*MwA)rlKZmi^Y)>6pA!6c%j)sdR7{uke;Yl06l$exeHgnrma!XieB7Kf zAmfJ`AOGTz)Kze3=2AQZ`%x96MUO=;JZLPkGshgmwGTZOxuYX(9dax(mi$W^&Ob61 z$(zH>vB=J|R;@a<}PUKC+B4Hy;IBKnDu5# znQZ0oO_sKj|7UX*Cs)bh*KEF*6s=#*CygAwP31b0b~<~K`%Ipi=+qW*)LQfCwlYbo z+2(QS$Sc`eAx~JGnYHfk*%_-==Q@X_4R?+kGiLJ0abw1gbuRzAb9!1@>WcKW8L7?{ z8O|B$Yu2PYr)TG8$lj9d=3XNYBYwl{w0}C~IxH)4WoSGdn#eJ^RY^6W#ne8 zS(CATt@DC(cIR=N6gPtk(dOl*uE{`-r3|8>vDy^IfY0L81#BIhsFmc%_KA!gHD=7% zDND|}V8N8uYjLTOk;D&9CX%;yd1*aY@)6zBS^Q{9;bhc@B>?o(1n0>A! zlX_)p#+uaSc<5CbxvQP2&N@*U$gux}ir*baIk35-A|F`k`F@45?7U^hkwdN9h@ zi;=dY8Tsl>kE<`OX@AE3j%C!>sSRMhY7jFZ$1~?P6rLW=difEUz(}fmw8;eHaE9@? z#K}0%B)Iz&9OzW-H0^YrYBUX1&Vcb}X|qwwT`ZuLK3uy1Rh@&P&%SX)kC`Yqx3pv>UYh zw0g~_-KjmOZPd2YZhE!Hxz8w|`LzaZi}s54wf2?vDyEjE-GlLei^1*GzR`AT-)Y}# zyR-^i=0|Oh_HXSq%*VqUvVS4=`x$LjX*H~?yd3ko0zz1WOZ*EX`x}NQM6(|AyAs2# z)UMX9p@sS{?Z34R+WXp*%r#uA{e#(qYOPlLP{pb^)k!&s6%w=-?KhREl6Wh6vSu(J zkfOTq#10+_qPnXds;BD3G}qDU7}Z<#QGHcEtx;=I{ncO8vFbSGR0GsNHAoFs$MZ&v zp=y{Ku1-)Rw2=0zI#G>Oqts|MMvYbDSZ6a{P0-w038V58)g(1p@s?S2sya>aQhPN` zO;OVnT0rRp-ZOr@&jDoy)Htx)M|rCOzxY316pYPHHxm#Zt(8nsqssw|?U zziE55Vr{?X((<(;El(@d9-)VyqjJ@Hb)~vWU9JAkNWp)p4eGzuwd#L(r^s8{yV`r& z7uws}JK7HIdTpC_i}sTCrS_@znf9^zhq{h;Ki{BkR5z)c)h*0l+^TL07+Gg#3?E|$*-KRFI`_%*LLG@4dka}1> zq8?R`smIk5>R;+fwT0(+JguH#GU++>JZrvQR4=KQ)hp^%^_qHJy`kPzZ>hJ{JL+Ba zo_b$>pgvR|sgKns>QnWZ+NwTR+thaTh1#LMR9~r`>TC6l`c{1xzdkc#%$Vt8;-{}o zP0P;8j8C3l}pxt&+HZO5IlJT*IR zHUAv*h)Y(aJ0v_=ksd$KE=h)6l6h81GOVhbXXiP?uDUbZQtW(r+jjCcHg8s2#@w>+nod#LmuK6`P*9 zDt@6|rYyTm3#~F`nIdK3>h+ncQnS~uU6Z;#Hzg~2&vBl0-E8Z+=S5vAIXh~{ah`SE zY)dS%&dM=G=S6Mx(mAJXJ7IBJ`U)b#)P!7n!?D;pJ=YX*i$z1ZqM;?0hSpmeT4I-G zyjLcP?*318tCDCO`Ss%R@zr@z$dRvnhwB@YxRZ-g)MQ#7RZQF6N zRmN*fk#upZDJEUhwmDsLVI7d#x*0!xw)s_jYPu;qFU(n!nzP#4&T89EUKCZq$vM$G zi=%gl1Y1qtalxwWR2=;(OI%bS}ji5INMNGDX!$Vt2^vL%EucJ}lcmYgxhk~1e* za@Iu0MOH$8H-+`>oJqEvoP14sc2>^%b?Mm|S=q@erK`}olW}EwWT$gZ#?_JSlpJ^{ zvvn_Bx+;;qXDvW_D|)^TQiSes^6tOKPc%&l&JTc4hjOBY~8)>WCK(o)wYrDv|d zTCB~rkf?7qov*X20)73q}J z`nj9E-+Dlbe32gcxr@2i`eUML9Fk6=>A2>W>8i4o9I-XoNwQqlJi>HW*-8-Ckd1E9 z4k}-yw7QachFRZKceYvI=7!X_Y}oZJn^t{WTTRFN`n}amy!$eWkwQ zf1J5zotZ>jy+DhZHD~@}t;?F!+)Uzf`CTJ*Vt@PSw!N6Nbvf&_E?1;yXUaM?^BVGB z`T!2%eaQ`LEzNUd)Q|-~v6n>8pcDTU@zwt(%D;}->;^MFznK&^5;5OKe0~S<_+7+W z_YhBSB0`s#>j5J4hls`=Ar5z6eTp!e$h*DdKpm`xPv2{E#b!TGfuBtmR^ijmmeRzKDam34mh?R#D zCyyXL9!+d4k@3ky#8ZfcrxFLxB+5O92=_ue@{5l9KGf9NeWkY5q?l`BzKFRdZeYAW zAtrG{;=|yl;S82Hm5$Dr|p&L4F3poiIV4BG14 zGAK0IIe5akt8dg~uK=myL` zx%&8U_fNabFmthmoS1*Tc3OXZ&zUgp5VZGC z!>5BBr=2j!(f!lYR)AN5zx$_y#+&e;etM4+23_6bl0gp(PQT;Sl|3$*dOE*xd>eXf z8MHNmE*qw9?zwBQ)56qkz2XMXX@@yetEc}kcwMiZgEt+F8Lr-OLvHJP&d^HH)Ue(b zW;&##l6$#|nHsv0BO{Qy7~mYV)jD%%<;*esT|V=U#0@iln0d!cKRT@3{_F%iG|?zxP4ws5AR|l< zb^?9VzVz6R*4{8Y^f(96x9mml@@V?FN2z;_dbP=DQuiAb>H#C99%TO^6F)UBLcDj4WJi1bDNRkG^ay zULK2|$KvU+^u=T8hsWaYvD!w`xDRXw_k#z(gWw_XFzGx39tDqq$4UEB(%T9?=iKdJ z2l$F(zXsoe??FB&1jV2P_%ytnl`e^(Ge`l)87}3d)B}(Mk%PeTU?><4Mu3rEG_^9u z2&l2f_iCKsQzs$0KTw?prjpJ~&Y#6`3pj2e$DM;b7kM6X5t1=&bs^_aAL?S{QsiaE z9<|)qL+y7qf~@0i)Q$&3j2dkyau^s6PGElo@ zyn#i&Nm<_Fx3|GN;9c+@_yBxJc|QUlgHOPxl=(BRyA}C4dDzA=+mT-&cObt+enozF zBELp{gZvix9rAnRF63^G+k^Z!@(1LP$e)luBY#2uirkCLL+(TFN4k*toL>lvK?yj( z_hsaz9H}EKkd>6F3R#V;LDnMckRGHLS&#IC0NV|m6GS#5n~))7Gct^9LH>p`uxO2S zehL|bj77#FJ0Ts&c%w!oVA+YtBxGk~GBO3(1=*G3x^Y~0WDjIdWH01V$fH@4cMP&O zvJbK^vLCWP(g_9v@xsAImFbB?km8NQkm8XiAjK%bHw3mW=jcPcu zM%`yLtIgnk!)tS8wfZN&Kg78YQ-^Z5MT0gC3tI**#kcorBaK>EGN6$c*s+1~BYYf!u-o61fw6 z1HJ>hz#i}e_zC<1_JV!D1E zUi;8q5bb?O%hrgtJVdgSX|<*pI!AapqL%xx_Y)B&GEmW^4&SKRp)~9*k6QPj9h{|AFsR+ z{1wK&j`XevH-H<#P2gs53)n~+_kqpee((Tz5Ih8)B&{vrDeyFS20RO%1J8pOz>DA| z@G^LXk@eRY!+)I-dW#8tSb_&j@L&lZEWv{%c(4QyOc;U*Loi_oCJbQ_d$0%(7O@A5 z@L&;pum}(A=f^5Mu%92x@W6h4tiuEQ`LPfW7UID|JXnYa3-Mqf9xTLzg?L~%zg7SW zK@lhhZcqYBK_&A9)#SC7UXG7?_M>5DEMO)dtmK1{d@zz9tMb4|ek{uaBl)o|4~*o; z!aT5%4<_=#LOvKM1p9}gj}TVq!3y_ag&wTXgPm1jIeu880n4eua!O!{YV58G3u?rIs$q!$EHT-r#@^zv zw@$5lvFycB+QInFNNjB)ZNOxs9?mGlLYlCUCM={03u(eany`>2EJUKBLbM~kDt_8T zO-Xc9fgM-iSB3agA*B!>65kPi6MrehR|=WAN#u-V^qXS*%BuPUM2r&474Y<(-SAi) z9wM<^C99h9;Iv9D3(t822D}!JcoO*(<$VT^{17jiOSBj!S`3q-pZK$$6vbP_O9GUq zl9W9}i9vj?fpQgdl{&8C!c&4=B}|+VCe8?RwE$NurrdtY?Wf#+%I&Aze#-5q+;AWfV|5MQVr90t+V7!luB$uQ2oV8WH&G^vB2I6u#dm6eJmJET@N9rCxDT}MdOg;!O2($wgvO~U_L+0 z=Y#qD=ra(-e14nxd^Yn*T-ng7@1>kCI{z!zSo5Sz>E#p za{!B(h!%&S#X-b)KJ2>+Z619Hc5Hwh{jg&L?C6Ib8(>F2?AQQ1`rEPNQSgElqczzv zT9X~4F^kCYg8?=GTQA5DM}CxXdf3jT2_HhCKIbmUZyo5uB~a}9~R z#J|M1#IF|f`x1^jpWiP4;$PxhmyixNVfxrI;~Ue*(vYh_2I*YRd5_|6kAWvS{wa8G z2bSC&uW7_v{CG+jPZ3KKPiclxn$dV)G@gRSlc>d`sl^0p(LwF;<^WQ7%BaPIyc{8^ zOrx&b&g{dP(t5^XD=nOz$XRjrSzgZawVovm7Up`=`|i(KNt_jDpOwT}o$Ry1oCRB& zzBi9FrPurlv)on=Pv8vE^#pP`m|TwF%p|7P0X^!!V+()$BnnRCoPUto${*<4El!^5TZS z-hq4GhD+XqN8TVFkHem3VNVmVr<350kGNuAIN<}h;0L0lpYi>V@xg!NgI|%0CdQ$~ z6NsV6e*<|JyB0q>mMe)}ON8H<6k|y-j?^5~Km%vikbe)e?TKh#^550?8f)2ywR}x# zduUs#Xj`f%6V{FO4573qfRSi?9CAF6agEdPi&>Oq4wwth0OBiW8l|)`l|oxUIDm`+NdI5@{#L)A{vvEWo;Yg) zzIHNlB61Rvm>I7xp#5><`31B;ZrYz(H4SO?`*r&LVcMVu+Mov7pa$9?H;hm~8|0=9 za%2CE*ncC;un%U~M;p`xJA6$W1d}3Hfed1;%VCj6Iqxy>B)>fcM?S;#KQ!uSi{zOL ziD=S_`{J?sy;ys#X}!V&OX0LEtnw9BI=#lRud{0E4QjWNby3x9*8+0{huW6flbW-x zQe|Jo!`dNq0^W^v^?_`zJ{8OSr?tn-kI*}o7Txd=AC7_Ytrxt7R( zBj?-)HiP>CJr``f8e6Z%)~m4fDr~(9Td%^_tFZMdY`qFw*RgdSTi3C59b4Ba(OHy8 zTG>&Q$d8@t*tw3K>)5%Do$J`Sj-A&LQ`Heu)e%$G5mWiGb3Zm-g?(3H+f~?hHMXr| z+tt{%j%`CEDwvKJ<*tU*s>)3WRw(Z5XtFdh_wq1>Fd$H|m zY}*ST#!}m-P}{?)?J?MO5_VjT9qZU}HFm6H$JN-ejvZHH$2vAyM;ug#P1a$Pb=YJb zHd#j;*qIMItHQ=~Y)r?-{MeWu8!N!Z3QQXl`^qp5 zU{fw^idddjoAw@Ca#53sj8a%F!N=H95q9K5i+R}5e%88m!j`Jg#BQ|kIa=6(78bB_ zEf!65MH6Gu#C}-kd$h3~E$pPmcTw9qHMfJ>*=Dy6r%@XxQXAv2y|1Z}WJU(V)W|k! zV<$E7HMUrdExM_R-PFYQ)PRhQ7vqUPQ>sczRe>keVWZM7ER*$uW{V+Kek?gThMY*R zXCznZ%ax?p(;Lff=4i$@85Q@?A{<~;Jd9NrYD1C3z;JK^`y-Gif>D5WUn2r#R6LC3 z7cwdy#`+5x6%W&T)M|_b60OAH4S9G&9^R0LH{?-wO{^j8Mhj9*3sQ_{gM5Re&|N5G@tG4MF$cmnw(dE5e?0#Acyz_Z{v z@H}_{ya-+bF9WOPk+xcDz6Kj8#s>26$UHnU4_nB?BlGacJUr5cN4oGx7xv-8BTKN6 z5?Y=Ecx4_f&jCC$kCx{E-kFDY7UG?GcxNHrnTK~4;+=VTXCdC1$LMz$56xrrJB*j+ zG5Q_G-trjz4&$wPjDCml*gQtR!+0$-w@4Qk^Tet_=9}^Pewr>;OelfOo-r-~;d}>2C#AToktBqOctoh3&XVEK#gbEKsaZEH7-vLlO%~ z97GIc$2DO)t_j<5P1ufW!t_9PqhUY2klkq6kCr`X*n@UGXx4*PJ!sTJ52XN&*3wJa zibgBxscg05k}$oK-Du2D52XOD)zV8TKy$V9R0_~uEm{kswJ<%FttP`>j9f|@m%+-* z!M*f3HleWxX+M~cpl2%m4(2)Otw{gSO>d=y_`Q_)y@vR`gdSZbZHbI8F?YjJI!DQv zcmqfN%8~ofmBh^ToKemh`Lr%x&MGp~4sk|+Go%+(%h6RF)yPqm@JoQB>WTMdRPh!1 zt*_C7yv~SxCB4>aTB%xo^8;xWB#+Xgl024@Rv~GXaAoNuNj}{&LP~n&EbLFACF@EA z)eUy&j_kozrIi>-n_-S8@;mc9Fntia3&Qk4Y%hrI1!47|8S`%-w?|@adD{Jx9J>WP z1)c`afM>yT;Cb)@coDn=UIwqQ;`}w3)neB&Y%+*V2C>N?HW|bwgV?0Z9}0s8Z3Ye6 z3>t(zgD__h)(pa!LD(_~QwFikAhsFAHiOt^5Zeq=hY}ecMIBBc4sWI&d%{>j*eVE9 z1!1Wmj1dc4#1b3Je3;CI$vl{_MbKu8pqj=ubz(C@5ZemE2(hq1Eb&Ml@kpLp#{5be zSOu(Dw^qg#MGM%#vGn~W(+itQjh{`9q)+Q%<$=udEMqJsP3wx@noTV?qGcIlkn!I_ z+rG-sawBJznOe4N%Y8(vi8rn0NOmQoI0^g>B<0EE#~N$!ag`8P@k`rBo&vT7Uv2!1 z?wnEfnbOWatq*0dm;Hj5RpDj7kUuHE4^NX({3^zE zJ|P#4FuZtO9W0-R*9mLXkoSDNPey`$c%O^}`{;E9@IaaOk~y!Sou^{Fv6|j@1KxO~ z5&k^9@kk@OKD^O~H~R2KAKvJrtTMal!vlSIpbro9;ekFpkToM*%NnWm+atApVi+GD z=)(hjc%Tmt^x=V#ky#%e=)(hjc%Tmt^wFCL5Gx1F(O4OYW#knP^x=U%JkW;+$~;q$ zI3P$25TyMNs^g4LXdOPG1!#u-^I-lwyt4uCY`{Ak@J{J9$Y`mbw%n(tff<}PlXF<9 zL2J-VOVEtR%1Ei7*1(6y`e+UE7{AKHW2^AkDz(h`1&{p&kNpLYt)_R{fZlzk57wbc zuc^t&XkiKrZ83K~8WHa(qUPl2dPc;%;iasJv2`6n$1)!zw&}C=>$UakMZeAH*K6w6 z8Y%UmTQ@Cs!SCqKi|)MW&WrB6=*~+Hs?nVn-FeZS7u|W$ofqACO})wJx}`5K`tqVL zFZ%LE>C210yy(k|zP#wmi@uuCS2Ox*rpMQeuDs~Vi>|!r%G;`&v9z=&0nvk{1Fu>C zQs?J$+y&r5e!m!8Lag;ykbyT}OKnO^*3jxH;vG}4Ldzp$q*J`GL@d+PhWNd-%QA{6 z37NOW>ft0;>m9L=UKl9#9cIpdxxe zMf8A*=m8ZG-?{%p56Ddq$W0H(O%KRT56Ddq2rFvs@#NDc=hG(V(VU#Exaej%Bb#5muK^dtC%u-FStI*w&3_xQK1tc!$gG@s!y;o+7))Q%jG>3(FPLx8>P7ytwB6>MR^l*yQiC`2MLj-E|Y|8AOO&L9#GI};;uwfBi zm=7Bk;feXg$Zi)W%kJ5f**%*wbuYhd z;=KFlHEkw+Y)n1CdGxZ0mCMyb{Pr;WG4yP{qi6FSZCwzrtD%QejMvp@!@zKG0{bJ7 zCxTI6G#CTM0>(e^x*EnFit)M{#vc6e=Pr6gZv6Z!+RHug=qF}dIg2)bHdZ-@HhwPK zXCP%f;!N7;vuIQ2gR{W`un?RB&IRXj+#=*+&RK#ypZ4bhxcWk@RmOoXW_*X4GkRBr zcxaHeHHdcx@y;OL8N@q-cxMpr4C0-|cxMgc6E%!a)G$6#!}vrE;}bQEPt-6zQN#E| z4dW9vj87EfoyB-(G2U5>cNXKF#f(q5>7}{prMc;)3Fm*p_(Tn}5_NEYIUZY%$Cl%< z<#;T2_JQZX^OWNS@FI8#ybNC9oL9kX;B_G5pl>4I;bPWiTRo$bgkkUNlHBEKTfJ304j4S|NA5!IChvPV?%&8CkUt`S z;`^VGzaW1_?nUMy_aXNqUC4aSFQDEFkwwU2q#Id+EJYsRxH8I8j?|GA$Vy}tvKm=~ ztVPz*zIkY;yzJMr?*{?48<0U{BeDq@LN+7A$QI;pNQ2l`W6VtL?$6SBa@LS$S%mP9M_HGx+7U}fT!0mwo!+t`|Yfemq@P zRqVps3yE)b;qis^eq~&+j2^HX&o84F?8f`c=n1=FfHHc+Zdjm9jYN(@j%NML7{(aJ z66-L2M;rW!85>O?{*m}-B2r=`q86AT2s4z!3_+No9A+qn84kb?d~G15@mWDav7rAWU%prZ_-*{t3NlSqbwAz3D=F)4S+R@1lnzQ7~h- zw8%PZWF|7FI+;2?g-GdC7+%H=A4hsPQr2r6$6A;HU^Kn(u`tj{U?%$u*gprH2N*}> zIxg;=n2cq~jPx-0##${yec7>fiG3YE5kxuH+6P}$;Kyb7aT$JGh98&V$7T3&8Gc+w zKK5bnW!QQdc3vj&C@YI%$x{h=Dj`pP^0bdU?W0~RsM8ASvx2%TqaMr1X9@ZAlTSbS z^iw}&LsN)bWTo*%)ZS9~ zpoN)<-^j}VxUU&jb;GJUtg6GRI;^V0syeKy!>T&0s#6QqFssgWb(mF$S#_9Ihgo%) zRfk!1n6(UzdJ#6Ys+HSa@e)pX4i69Rfkn|SXGBrb;_i}syeKy z!>Z-5s?3WBtLm_7DXgl)s-h=1tXfKG%3;+5X54cf-(3%G05^i0z|G(mu#xj_1-F6Q z!5!dEa2L26+ym|fn*i+*tg6GRI;^V0syeKy!>VPlY8k9r2CIsOx5279tg8PGtCqv6 z<*;fwtXdAMmcyz#tg6GRI;^V0syeKy!>Z-5YB{W04y%^Ks^ze1IjmX^tCqv6<*;fw zta<=eEr(SPU}bKbRoynLx@}g~VbyY2Rfko}VO1SgEr(TgShXBhl{p$=Ro!M)nQsqREJ4* zm{f;Jb(mC#Np+Z1he^v}QXM8Ohe>sqv>YbYVbXG#REJ4*m{f;Jb(mC#Np+aC3??mw zNy}i;Qq>397ugTlA9)<}?oN&yfMh%lCM|YZagGtL_ z(lVH|98cd5la}J`+hEcIc>Ffg<0tUDc)i3|;`vsL<+hpBZ8ND3la|AzWiV+etne{R zs>2NXVbW6AVLwb-3PbFNNlR@eb;G2*O9NcUwJzeC7bBNqEB}ktGjPSkRz^&wCq#>+ z(HhaOs-(A634c_;4?cWQUAs|3LlyK>c~BI}i2dfiYzrc?ET| zlX{UmJ>)JA?(pEd3pw^;u#8qS4Xgs!QkUGXpq_$FpCLcXDV>k_(nsqeBVvBD56Jpc z7%*g(Y#eetU^bglmcVc!N?JroW&O;Ll(2#lmcp@NVnw+_BLqX$z)F`y6V2c%7M^1ZZ6 zj8kFZA*?%uWrwip5EdQ6nnP$n$CB&O!jD*T4Vw6owmyV)wwUeJEVf~BEVCXh=vZbw zn$WS#dNlDPmRN%}e#8=Mm{s-CuGcej@1T%UARDsnmU zTE0Ju+@+9S3fB?_VO26K^ae8KEbmF2M!j7|?w287;K)PIEtsRkq#Q@8ab~K2$M2f) zyJk|X!ta`47B9@=g;`|0R8}a<9at?yB`riHEkq?PL?kUlBrW)*L?kUlBrQZFEkqkLviS7dDdB+Ph&R85b8{mAMRSRkN)1uvRw9YOw>b)8X%+x|Q>9 z1Gj@az@6YOa5uOI+zU2=$GO%M$hMWJjWAjxjMhl2+d_-mLLa7umbQgfwuKh9h1Ru& zmb8Udw1pP5h1Ro$ma~OcvxR;~i`iNRkU`J{nn4RN@I3`$K_}1^bO$|wtQp3NFKUmhqjepp>yk@v6kb zc#p*-vf?ic19YaOzrTWf84-ROC6_h0vVKF>x&N_wy)>;eWhggwM~lecpIvX;!z_Cz zQb;mWk+Sus7LJ24dQ%f!DZP`@cRfsbEw;FpRJwyiG$GHoiqujcQtCxY-AO5ll=_fT z(vebvo#MzxC+YwV+I8K&Q?m@NJ&&ySWfy+z!iO1=<2%yC zcYnoq_tGYBqfOpUo4lPinKidq(6z>gDtc|Qv<1=+53vTW6FKh0bHC!aA31@Xw4b*t z;QJ!r2BkpewWH>lm6LXGAehCu5ik6`IcHi9?9!ThJ9vgp?uL;Wx+-cTpEfpu=XZ%0 zT2Dfd_NymtOoi5sv4rkOSvwo?%vo^NY>u6SoD0qX=C~i<%?D?L1z;ht=2g6M7q;om ztkuAy6ItggUY%fi^;*_*ZGbWU#cxlN))w#-cpAt$_GiI!;Caw-zU39pc@;?9XRS{^ zK+o{d^Dm!rz0WvqD{>phd;z`$J3;%o7%#m%FFm|N&BcVx@i7@2lX0;!(z8}1$Dy$} zX0_vp8{&u?WF2uFaYGz&LmY8K9C3r(Efq)4tNr|q6W$#N#_-!Xw0tVRpAM#hS$t1D zs&m02u$b>6b2+dhcM2vjm$QqxoL$5t@k9*?v?~?FB=JNK3A8U2v@aDZgY|8fQ^zxC zDXg{F-Kh1*dh9u@#6JVby6p3ik+s=tX`N+#_VauvYqVcP$~x_rk%wBV{TAQ94c-B= zZu>pt2Vf_VHQe7JcY&Y4FJLd&2VCHH)_0pDLJ6$?PXe;my9+XUz4tWs&j%NP$lC8q z7&ZGV$e_i26!{onJ-FI|Ra>6XafVxLw{4bNe5l$y@`$rt;;S4fE%>4D%Isve)JM82 zGlBY?jup!qj6V3J7Y-Ofx<_MC@{EHXFnA(8#U8AJAA}yQx%)7ErWX7t7JidfZU!lI zw^NX(B*?vovO2LJDWp(aEm;05aQJJadARE&QU>e3Q)$(?_mp!xsgC!b9uFHwM*d2P zFl&e+W!;p_%gQ*`0V2psBE(Yquch=~OXw}TzP zM_Sfh5gwS?11rlozyYGJN}{exqOMA!u2P~d>Ep}Yi>0u$^xF@>E1mey*f6YHOI?L& z4}PXSaKUtijP`{Y=?kD?;bbeit>Bk3`V5`$iX<*HNprR|6&erYhH1zG8^i>uYc(z_^e5l70nZ$4=ilTJN( zt5?flhcvJX+;7)Yq`Xohsg39onRhLj8DrIPp?*yJ0l0dkH>?dU^o~7xR;0aT}GAmV@dnzhgV}ca+jox3dx9&SkQhfXg{_g zxt@Y9@D=QD7f%I}5oBeK1ZUIF3enG!CtXz2*NT49MK%4c!#(Mun*NqN>4JM!kk*qf zQbJ-AWvVACodcYXpx^S3aX&`HXsXTvr-GoBnoVfnRal*Wg?5J=o3f{{}yTpTVyn59|l| z{9Xu(K?yj(cjcf0_OC+LfI8p>0T2XDpc%9PgBceEVnHVmPdrIWg=gmDnfdgx{bVZU8reo50QB7O;`?Zsok&!0q4;a3{D6+zsvl_kvB}K91cC?gtNm2f;(&Vc7c- z@F;i;SSv;XXtWyttj0g9@y{YOT7*W6&}b1F4WQ8g8V#V)02&RT(Eu6^pwS{UT7*W6 z&}b1F4WQ8g8jVMz0W=ywqX9I^+ArdT05S-gKr?6o2BX3X#DY#Bo)ri@kqNC9q17U^ z8bGT7v?_N(b<}D-K3b2D*5jk~_-H*oT91!5DOoWg>%fT;@Xu=ev&hzJ6IyLTtMXKg zYI6mI^c4o6Rhd(h)wT6#Bm_6hefK)7DZZwo86C~&XhugfJRKR_0v-pSgY94kAg&+> zGP|i`S2}DFf-OR@h1i%*Ze*^YZMKtnG+G56+miUctyM^*RrroM(B5WWNu!lap>^m= zgyx{1Cu>RvnPYvk=-UkEiOXrM<5|n!Tzb1%+!uQ_fAe@w@&HAvmdb9o2zT;44_#qDYSCh*MN4|$&3 z4*n);F+F>cQ!P2J>mA&eGEmbDX4j3N@q^J|6dOG6gl)f3YrxUQ&&E&2kA{cuYG|#G z_`w=lKcvLGli@b7Db8(?ANbCctp^>nL7Ohem4YB~Mu!irtNk&mB|nk9juO4Mez<0k zqjhpG&jvC-d=oT6N6HKBto<=sh-j#v)_u;j_DP}jsK~F;vVHnSGdT{Mf83$bmY#J+ z$NlJZ+O|osvDKk251$5YPwR(z@S&3eW4}>}hA0;$^za?eMnBSr$W$}l!Y1|@ zZ&G^=Sjh(l>rUB|zgoU4W>&@gz(R-kSXUrk65Z~PK7bs^9V_;S95zdxAL?UiP|8Ez zS>ecemE=CyQ4_Q_hyB3U_J^}Yw~ewZDPXBQFG}haUGWz=?qEszqjG4xVZ35|W9&4( zrgv(7$koT}eZ_dWhWsAS`p9_QcsDwwwtU#$B zP@0O;GSIK7`qb%mfDl27^7>y5g)oMZ35r_J3d}#Urf&YY!wZEEhu`mkx2ogmm5c?!>M4b6w zGvw)4Z66u+mOqo?7w{ zM{~ciW;|f-(?f!TqHRp)+U%PxfdhN5G2Rsqq;`lRBn70z0AI3w5Ahj2XFSW~{7B9y zvE7dFO)U1&Y-~5L*=pI=k)~HZDjJh?t?wfL8TFTMO)KZxKCbu+<7CuF;RmU)xwd(8Zb9y}e+mmdG`e#GlJ7Y*@$Kcg>)`9#M%ZHL(H%RAIko$*E6_kU)~ ziaEvSD9ta_c_Vd#emmwK?FI2hvsGztUmR=JlAT5#=LfJ1>iM8L^_$FNGQ~l$f~hBo zHqgkwO^cA8c?ZUfl&p;pBi|quW3`|2rkcp7ZEGMS!So3Z`RT>HT>Fpp&K&V@nGv$y zn`0pK{cvJAI*^tb{n4?~Y;avOj;)Gjj4bfZv9hEaEaJ2m= zC6GVOEWrRuA%7YeZvTy-TqDdBBo%?;{Ak25qZ>o5#FA!5#8FSt=)`p_^hdUbgZ+5w z)0*R#dH&rlfK3N6wBw>gQ;Vwo)5`UG}BVJQ(Y={lkY3n8RGIPrLFSg_dK0{8>`jd)B^uXT8@-!$UQ!LOibRBNigh+DPH6M6ATg-*MEt z)OQL<0R7Nj4}R|n2 z>d;E97k1ehAMJ{DTK}!H#a5FT?fSp}$er2$*-tXs6!z>yvPt~OI9+$Xlh&vcj5C&3HlPQef5{cU}D-qjhTa3*hM8^AsC7qbRZ-pe-D9ML<;9K{>Y zv*91)UiOC=x1Fy&$-L@$Jc;r>*2>C=-fBklj?=zl1aGp&Q`ec#FOzXS&0~)1In1{% zB$(rOk~_)eB+iU}-^HQ6fvuFgZl=elc@jCD=0AmZy+T7e;Q)cc)Pf(l_7H#=F`0 z<#KJDTEml^c&}*N8_#&Z*?;&ut|Tiz;VIsnwt@Ak_o|0^veawpecu22k@^B%N{NR7 zVKpgO^L2hSHRqowu7@BJds)69U|`w z>CZ~9e?`3&?FrtKX1*_t-^Fv1dE4JK?NV(SW4Tvr|ByGtaaLdQDDQX6CC#XJyd5S_ JWQNrE{6F%KSm^)& literal 0 HcmV?d00001 diff --git a/data/fonts/TitilliumWeb-Light.ttf b/data/fonts/TitilliumWeb-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ca67971246382f5f58d8db2989fdf713fa82ea06 GIT binary patch literal 64032 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 z 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 Bm%;!5 literal 0 HcmV?d00001 diff --git a/data/fonts/TitilliumWeb-Regular.ttf b/data/fonts/TitilliumWeb-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6da821935dc29725c445ee8b469a2242c508c481 GIT binary patch literal 63752 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 D2bv~K literal 0 HcmV?d00001 diff --git a/data/fonts/TitilliumWeb-SemiBold.ttf b/data/fonts/TitilliumWeb-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..dfdcdbea9036f5674d399b1775cecc5388864947 GIT binary patch literal 63044 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�}!<`hKJXN3S{)C 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�!)sHV?w&}UTBXE^9H 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 d_wuzfuIf%1data/images/outbox.svg data/images/inbox-512x512.png data/images/network-activity.svg + data/fonts/TitilliumWeb-Bold.ttf From 7a4e4410a16b196dca9c07f13851bb769bb85f4c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 14:27:04 +0200 Subject: [PATCH 360/565] * TomahawkStyle::loadFonts() auto-loads all fonts in the data/fonts/ resource. --- src/libtomahawk/utils/TomahawkStyle.cpp | 17 +++++++++++++++++ src/libtomahawk/utils/TomahawkStyle.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/src/libtomahawk/utils/TomahawkStyle.cpp b/src/libtomahawk/utils/TomahawkStyle.cpp index f70a7cafde..5f325704b1 100644 --- a/src/libtomahawk/utils/TomahawkStyle.cpp +++ b/src/libtomahawk/utils/TomahawkStyle.cpp @@ -19,12 +19,16 @@ #include "TomahawkStyle.h" +#include "utils/Logger.h" + +#include #include #include #include #include #include #include +#include void @@ -191,3 +195,16 @@ TomahawkStyle::styleScrollBar( QScrollBar* scrollBar ) "QScrollBar:up-arrow:vertical, QScrollBar::down-arrow:vertical {" "border: 0px; width: 0px; height: 0px; background: none; background-color: transparent; }" ); } + + +void +TomahawkStyle::loadFonts() +{ + QDir dir( ":/data/fonts" ); + foreach ( const QString& fileName, dir.entryList() ) + { + tDebug() << "Trying to add font resource:" << fileName; + const int id = QFontDatabase::addApplicationFont( ":/data/fonts/" + fileName ); + tDebug() << "Added font:" << id << QFontDatabase::applicationFontFamilies( id ).first(); + } +} diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index 6016a6e97f..b19be89d19 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -31,6 +31,8 @@ class QScrollBar; namespace TomahawkStyle { + DLLEXPORT void loadFonts(); + /** * Draws a header background on a painter with the specified rectangle */ From 8c671568bb3e5729cc9c98f7ebe6b1b3d63a81bd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 14:27:25 +0200 Subject: [PATCH 361/565] * Load fonts on startup. --- src/tomahawk/TomahawkWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tomahawk/TomahawkWindow.cpp b/src/tomahawk/TomahawkWindow.cpp index ee7b473a08..d7ee9d8d26 100644 --- a/src/tomahawk/TomahawkWindow.cpp +++ b/src/tomahawk/TomahawkWindow.cpp @@ -42,6 +42,7 @@ #include "accounts/AccountManager.h" #include "sourcetree/SourceTreeView.h" #include "network/Servent.h" +#include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" #include "utils/ProxyStyle.h" #include "utils/WidgetDragFilter.h" @@ -112,6 +113,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) , m_settingsDialog( 0 ) , m_audioRetryCounter( 0 ) { + TomahawkStyle::loadFonts(); setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) ); ViewManager* vm = new ViewManager( this ); From d833f5fef20c49d5a3397923fba1483419fea5e0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 14:40:07 +0200 Subject: [PATCH 362/565] * Use Titillium Web for the labels on ArtistInfoWidget. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 4b5e04812b..48bf0039b9 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -139,6 +139,13 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->label_2->setPalette( p ); ui->label_3->setPalette( p ); + QFont font = ui->label->font(); + font.setBold( true ); + font.setFamily( "Titillium Web" ); + ui->label->setFont( font ); + ui->label_2->setFont( font ); + ui->label_3->setFont( font ); + QScrollArea* area = new QScrollArea(); area->setWidgetResizable( true ); area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); From ae8ddb2144d7ace01f5fd639c00307195b9ba471 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 13 Jun 2013 16:29:56 +0200 Subject: [PATCH 363/565] * Use TomahawkStyle::HEADER_TEXT for captions. --- src/libtomahawk/widgets/BasicHeader.cpp | 2 +- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/BasicHeader.cpp b/src/libtomahawk/widgets/BasicHeader.cpp index 2133f00d58..f4fb9bd687 100644 --- a/src/libtomahawk/widgets/BasicHeader.cpp +++ b/src/libtomahawk/widgets/BasicHeader.cpp @@ -60,7 +60,7 @@ BasicHeader::BasicHeader( QWidget* parent ) m_mainLayout->setStretchFactor( m_verticalLayout, 2 ); QPalette pal = palette(); - pal.setColor( QPalette::Foreground, Qt::white ); + pal.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_LOWER ); m_captionLabel->setPalette( pal ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 48bf0039b9..ffd1e2243b 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -130,7 +130,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); QPalette p = ui->biography->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); p.setColor( QPalette::Text, TomahawkStyle::PAGE_TEXT ); ui->biography->setPalette( p ); From d2f9d2d79ca4803eee26b6669b67b095e74fce89 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Thu, 13 Jun 2013 19:11:27 -0400 Subject: [PATCH 364/565] A bunch of styling to the Arist Page. WIP. --- data/fonts/FaunaOne-Regular.ttf | Bin 0 -> 30244 bytes data/images/album-placeholder-grid.svg | 45 +++++++++++++----- data/images/artist-placeholder-grid.svg | 16 ++++--- resources.qrc | 1 + src/libtomahawk/utils/TomahawkStyle.h | 12 ++--- .../widgets/infowidgets/ArtistInfoWidget.cpp | 9 ++-- 6 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 data/fonts/FaunaOne-Regular.ttf diff --git a/data/fonts/FaunaOne-Regular.ttf b/data/fonts/FaunaOne-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e6fec0abd84b8170ac3e0532720808c87e500ec9 GIT binary patch literal 30244 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 - + album-placeholder-grid Created with Sketch (http://www.bohemiancoding.com/sketch) - - - + + + - + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + \ No newline at end of file diff --git a/data/images/artist-placeholder-grid.svg b/data/images/artist-placeholder-grid.svg index 5f3f487b25..ac65319830 100644 --- a/data/images/artist-placeholder-grid.svg +++ b/data/images/artist-placeholder-grid.svg @@ -1,13 +1,17 @@ - artist-placeholder-grid + Slice 1 Created with Sketch (http://www.bohemiancoding.com/sketch) - - - - - + + + + + + + + + diff --git a/resources.qrc b/resources.qrc index a45534abd1..2a463a8c4d 100644 --- a/resources.qrc +++ b/resources.qrc @@ -185,5 +185,6 @@ data/images/inbox-512x512.png data/images/network-activity.svg data/fonts/TitilliumWeb-Bold.ttf + data/fonts/FaunaOne-Regular.ttf diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index b19be89d19..973850d71a 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -61,15 +61,15 @@ namespace TomahawkStyle static const QColor SELECTION_BACKGROUND = QColor( "#962c26" ); static const QColor SELECTION_FOREGROUND = QColor( "#ffffff" ); - static const QColor HEADER_UPPER = QColor( "#25292c" ); - static const QColor HEADER_LOWER = QColor( "#1e1e1e" ); - static const QColor HEADER_TEXT = QColor( "#eaeaea" ); + static const QColor HEADER_UPPER = QColor( "#292f34" ); + static const QColor HEADER_LOWER = QColor( "#292f34" ); + static const QColor HEADER_TEXT = QColor( "#ffffff" ); static const QColor HEADER_HIGHLIGHT = QColor( "#333" ); - static const QColor PAGE_TEXT = Qt::gray; + static const QColor PAGE_TEXT = QColor( "#ABCCE8" ); static const QColor PAGE_ITEM_BACKGROUND = QColor( "#1e1e1e" ).lighter( 290 ); - static const QColor PAGE_FOREGROUND = QColor( "#ffffff" ); - static const QColor PAGE_BACKGROUND = QColor( "#1e1e1e" ).lighter(); + static const QColor PAGE_FOREGROUND = QColor( "#292f34" ); + static const QColor PAGE_BACKGROUND = QColor( "#DBDBDB" ); static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index ffd1e2243b..3140dbf615 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -98,7 +98,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QPalette trackViewPal = ui->topHits->palette(); trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); - trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); + trackViewPal.setColor( QPalette::Highlight, QColor( "#292f34" ) ); trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); ui->topHits->setPalette( trackViewPal ); ui->topHits->setAlternatingRowColors( false ); @@ -121,7 +121,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->cover->setShowText( false ); QFont f = font(); - f.setPointSize( f.pointSize() + 3 ); + f.setPointSize( f.pointSize() + 1 ); + f.setFamily( "Fauna One" ); ui->biography->setOpenLinks( false ); ui->biography->setOpenExternalLinks( true ); ui->biography->setFrameShape( QFrame::NoFrame ); @@ -140,8 +141,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->label_3->setPalette( p ); QFont font = ui->label->font(); - font.setBold( true ); - font.setFamily( "Titillium Web" ); + font.setBold( false ); + font.setFamily( "Fauna One" ); ui->label->setFont( font ); ui->label_2->setFont( font ); ui->label_3->setFont( font ); From d302850d5172e29f5bd901fdbb4fecb88702f415 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Thu, 13 Jun 2013 19:35:11 -0400 Subject: [PATCH 365/565] Use a font that also has bold. WIP. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 3140dbf615..a5f3443bbc 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -121,8 +121,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->cover->setShowText( false ); QFont f = font(); - f.setPointSize( f.pointSize() + 1 ); - f.setFamily( "Fauna One" ); + f.setPointSize( f.pointSize() + 2 ); + f.setFamily( "Titillium Web" ); ui->biography->setOpenLinks( false ); ui->biography->setOpenExternalLinks( true ); ui->biography->setFrameShape( QFrame::NoFrame ); @@ -141,8 +141,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->label_3->setPalette( p ); QFont font = ui->label->font(); - font.setBold( false ); - font.setFamily( "Fauna One" ); + font.setBold( true ); + font.setFamily( "Titillium Web" ); ui->label->setFont( font ); ui->label_2->setFont( font ); ui->label_3->setFont( font ); From 0aade324ade26f87c046892ed7f18f49e9b90235 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 01:55:40 +0200 Subject: [PATCH 366/565] * Added regular version of Titillium as resource. --- resources.qrc | 1 + 1 file changed, 1 insertion(+) diff --git a/resources.qrc b/resources.qrc index 2a463a8c4d..c6f3ba2df4 100644 --- a/resources.qrc +++ b/resources.qrc @@ -185,6 +185,7 @@ data/images/inbox-512x512.png data/images/network-activity.svg data/fonts/TitilliumWeb-Bold.ttf + data/fonts/TitilliumWeb-Regular.ttf data/fonts/FaunaOne-Regular.ttf From 6166eac485064c121a71a3811aec26cc1ab625d4 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 01:56:05 +0200 Subject: [PATCH 367/565] * Cleaned up TomahawkStyle. --- src/libtomahawk/utils/TomahawkStyle.cpp | 4 ++-- src/libtomahawk/utils/TomahawkStyle.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.cpp b/src/libtomahawk/utils/TomahawkStyle.cpp index 5f325704b1..802649ded5 100644 --- a/src/libtomahawk/utils/TomahawkStyle.cpp +++ b/src/libtomahawk/utils/TomahawkStyle.cpp @@ -42,8 +42,8 @@ TomahawkStyle::horizontalHeader( QPainter* painter, const QRect& r ) painter->fillRect( lowerHalf, TomahawkStyle::headerLowerColor() );*/ QLinearGradient gradient( QPoint( 0, 0 ), QPoint( 0, 1 ) ); gradient.setCoordinateMode( QGradient::ObjectBoundingMode ); - gradient.setColorAt( 0.0, TomahawkStyle::HEADER_LOWER ); - gradient.setColorAt( 1.0, TomahawkStyle::HEADER_UPPER ); + gradient.setColorAt( 0.0, TomahawkStyle::HEADER_BACKGROUND ); + gradient.setColorAt( 1.0, TomahawkStyle::HEADER_BACKGROUND ); painter->setBrush( gradient ); painter->fillRect( r, gradient ); diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index 973850d71a..ef6e02468d 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -61,15 +61,20 @@ namespace TomahawkStyle static const QColor SELECTION_BACKGROUND = QColor( "#962c26" ); static const QColor SELECTION_FOREGROUND = QColor( "#ffffff" ); - static const QColor HEADER_UPPER = QColor( "#292f34" ); - static const QColor HEADER_LOWER = QColor( "#292f34" ); + static const QColor HEADER_BACKGROUND = QColor( "#292f34" ); static const QColor HEADER_TEXT = QColor( "#ffffff" ); + static const QColor HEADER_LINK = QColor( "#8dbf2d" ); static const QColor HEADER_HIGHLIGHT = QColor( "#333" ); + static const QColor TOGGLEBUTTON_BACKGROUND = QColor( "#292f34" ); + static const QColor TOGGLEBUTTON_TEXT = QColor( "#ffffff" ); + static const QColor TOGGLEBUTTON_HIGHLIGHT = QColor( "#333" ); + + static const QColor PAGE_CAPTION = QColor( "#292F34" ); static const QColor PAGE_TEXT = QColor( "#ABCCE8" ); - static const QColor PAGE_ITEM_BACKGROUND = QColor( "#1e1e1e" ).lighter( 290 ); static const QColor PAGE_FOREGROUND = QColor( "#292f34" ); static const QColor PAGE_BACKGROUND = QColor( "#DBDBDB" ); + static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); From aa5e0fcecbd45bece1daad572c924487188dbc1e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 01:56:43 +0200 Subject: [PATCH 368/565] * Got rid of obsolete TomahawkStyle colors. --- src/libtomahawk/playlist/FlexibleTreeView.cpp | 2 +- src/libtomahawk/playlist/FlexibleView.cpp | 2 +- src/libtomahawk/widgets/BasicHeader.cpp | 4 ++-- src/libtomahawk/widgets/ToggleButton.cpp | 10 +++------- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/playlist/FlexibleTreeView.cpp b/src/libtomahawk/playlist/FlexibleTreeView.cpp index da0dc0c43b..0204b89657 100644 --- a/src/libtomahawk/playlist/FlexibleTreeView.cpp +++ b/src/libtomahawk/playlist/FlexibleTreeView.cpp @@ -73,7 +73,7 @@ FlexibleTreeView::FlexibleTreeView( QWidget* parent, QWidget* extraHeader ) TomahawkUtils::unmarginLayout( layout() ); QFrame* lineBelow = new QFrame( this ); - lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); lineBelow->setFrameShape( QFrame::HLine ); lineBelow->setMaximumHeight( 1 ); QFrame* lineBelow2 = new QFrame( this ); diff --git a/src/libtomahawk/playlist/FlexibleView.cpp b/src/libtomahawk/playlist/FlexibleView.cpp index 24c7f8fc2a..50903e0044 100644 --- a/src/libtomahawk/playlist/FlexibleView.cpp +++ b/src/libtomahawk/playlist/FlexibleView.cpp @@ -68,7 +68,7 @@ FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) TomahawkUtils::unmarginLayout( layout() ); QFrame* lineBelow = new QFrame( this ); - lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); lineBelow->setFrameShape( QFrame::HLine ); lineBelow->setMaximumHeight( 1 ); QFrame* lineBelow2 = new QFrame( this ); diff --git a/src/libtomahawk/widgets/BasicHeader.cpp b/src/libtomahawk/widgets/BasicHeader.cpp index f4fb9bd687..be6c41a811 100644 --- a/src/libtomahawk/widgets/BasicHeader.cpp +++ b/src/libtomahawk/widgets/BasicHeader.cpp @@ -61,7 +61,7 @@ BasicHeader::BasicHeader( QWidget* parent ) QPalette pal = palette(); pal.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); - pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_LOWER ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); m_captionLabel->setPalette( pal ); m_descriptionLabel->setPalette( pal ); @@ -90,7 +90,7 @@ BasicHeader::BasicHeader( QWidget* parent ) // m_descriptionLabel->setGraphicsEffect( effect ); QFrame* lineAbove = new QFrame( this ); - lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); lineAbove->setFrameShape( QFrame::HLine ); lineAbove->setMaximumHeight( 1 ); QFrame* lineBelow = new QFrame( this ); diff --git a/src/libtomahawk/widgets/ToggleButton.cpp b/src/libtomahawk/widgets/ToggleButton.cpp index b75e7c6a14..6f68285fe0 100644 --- a/src/libtomahawk/widgets/ToggleButton.cpp +++ b/src/libtomahawk/widgets/ToggleButton.cpp @@ -89,15 +89,11 @@ ToggleButton::paintEvent( QPaintEvent* event ) if ( isChecked() ) { - p.setBrush( TomahawkStyle::HEADER_HIGHLIGHT ); - } - else if ( false ) - { - p.setBrush( TomahawkStyle::HEADER_LOWER ); + p.setBrush( TomahawkStyle::TOGGLEBUTTON_HIGHLIGHT ); } else { - p.setBrush( TomahawkStyle::HEADER_UPPER ); + p.setBrush( TomahawkStyle::TOGGLEBUTTON_BACKGROUND ); } p.drawRoundedRect( highlightRect, 4.0, 4.0 ); @@ -105,7 +101,7 @@ ToggleButton::paintEvent( QPaintEvent* event ) QTextOption to( Qt::AlignCenter ); r.adjust( 8, 0, -8, 0 ); - p.setBrush( TomahawkStyle::HEADER_TEXT ); + p.setBrush( TomahawkStyle::TOGGLEBUTTON_TEXT ); p.drawText( r, text(), to ); p.restore(); From 42640ef2596a873aca42a7664fa7b0032314dc66 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 01:57:40 +0200 Subject: [PATCH 369/565] * WIP: Artist page. --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 102 +++++++++++------- .../widgets/infowidgets/ArtistInfoWidget.ui | 2 +- 2 files changed, 67 insertions(+), 37 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index a5f3443bbc..c9259d5991 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -72,8 +72,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* AlbumItemDelegate* del = new AlbumItemDelegate( ui->topHits, ui->topHits->proxyModel() ); ui->topHits->setPlaylistItemDelegate( del ); -/* ui->relatedArtists->setAutoFitItems( true ); - ui->relatedArtists->setWrapping( false ); + ui->relatedArtists->setAutoFitItems( true ); +/* ui->relatedArtists->setWrapping( false ); ui->relatedArtists->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); ui->relatedArtists->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ ui->relatedArtists->delegate()->setItemSize( QSize( 170, 170 ) ); @@ -90,10 +90,10 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->albums->proxyModel()->sort( -1 ); ui->albums->setEmptyTip( tr( "Sorry, we could not find any albums for this artist!" ) ); - ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); ui->lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); - ui->lineBelow2->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_UPPER.name() ) ); + ui->lineBelow2->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); QPalette trackViewPal = ui->topHits->palette(); trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); @@ -120,32 +120,67 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Grid, ui->cover->size() ) ); ui->cover->setShowText( false ); - QFont f = font(); - f.setPointSize( f.pointSize() + 2 ); - f.setFamily( "Titillium Web" ); - ui->biography->setOpenLinks( false ); - ui->biography->setOpenExternalLinks( true ); - ui->biography->setFrameShape( QFrame::NoFrame ); - ui->biography->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->biography->setFont( f ); - TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); - - QPalette p = ui->biography->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); - p.setColor( QPalette::Text, TomahawkStyle::PAGE_TEXT ); - - ui->biography->setPalette( p ); - ui->artistLabel->setPalette( p ); - ui->label->setPalette( p ); - ui->label_2->setPalette( p ); - ui->label_3->setPalette( p ); - - QFont font = ui->label->font(); - font.setBold( true ); - font.setFamily( "Titillium Web" ); - ui->label->setFont( font ); - ui->label_2->setFont( font ); - ui->label_3->setFont( font ); + { + QFont f = font(); + f.setPointSize( f.pointSize() + 1 ); + f.setFamily( "Titillium Web" ); + + QPalette p = ui->biography->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_LINK ); + p.setColor( QPalette::Text, TomahawkStyle::HEADER_TEXT ); + + ui->biography->setFont( f ); + ui->biography->setPalette( p ); + ui->biography->setOpenLinks( false ); + ui->biography->setOpenExternalLinks( true ); + ui->biography->setFrameShape( QFrame::NoFrame ); + ui->biography->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); + ui->biography->document()->setDefaultStyleSheet( "a { text-decoration: none; font-weight: bold; color: #ffffff; }" ); + TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); + + connect( ui->biography, SIGNAL( anchorClicked( QUrl ) ), SLOT( onBiographyLinkClicked( QUrl ) ) ); + } + + { + QFont f = ui->biography->font(); + f.setPointSize( f.pointSize() + 2 ); + + QPalette p = ui->biography->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + + ui->artistLabel->setFont( f ); + ui->artistLabel->setPalette( p ); + } + + { + QFont f = ui->label->font(); + f.setBold( false ); + f.setFamily( "Fauna One" ); + f.setPointSize( f.pointSize() + 2 ); + + QPalette p = ui->biography->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); + + ui->label->setFont( f ); + ui->label_2->setFont( f ); + ui->label->setPalette( p ); + ui->label_2->setPalette( p ); + } + + { + QFont f = ui->albumLabel->font(); + f.setBold( false ); + f.setFamily( "Fauna One" ); + f.setPointSize( f.pointSize() + 2 ); + + QPalette p = ui->biography->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + + ui->albumLabel->setFont( f ); + ui->albumLabel->setPalette( p ); + } QScrollArea* area = new QScrollArea(); area->setWidgetResizable( true ); @@ -153,7 +188,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* area->setWidget( widget ); QPalette pal = palette(); - pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_LOWER ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); area->setPalette( pal ); area->setAutoFillBackground( true ); area->setFrameShape( QFrame::NoFrame ); @@ -171,9 +206,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); TomahawkStyle::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); - ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); - ui->biography->document()->setDefaultStyleSheet( "a { text-decoration: none; font-weight: bold; color: #ffffff; }" ); - ui->albums->setStyleSheet( "QListView { background-color: transparent; }" ); TomahawkStyle::stylePageFrame( ui->albumFrame ); @@ -183,8 +215,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->topHits->setStyleSheet( "QTreeView#topHits { background-color: transparent; }" ); TomahawkStyle::stylePageFrame( ui->trackFrame ); - connect( ui->biography, SIGNAL( anchorClicked( QUrl ) ), SLOT( onBiographyLinkClicked( QUrl ) ) ); - MetaPlaylistInterface* mpl = new MetaPlaylistInterface(); mpl->addChildInterface( ui->relatedArtists->playlistInterface() ); mpl->addChildInterface( ui->topHits->playlistInterface() ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 2e2ca533cd..96c59331d1 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -382,7 +382,7 @@ 4 - + Arial From 13c87e0accb153180e86f7308620a1ca23f56157 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 02:00:54 +0200 Subject: [PATCH 370/565] * Fixed link color in header. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index c9259d5991..973a30e5b6 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -126,7 +126,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* f.setFamily( "Titillium Web" ); QPalette p = ui->biography->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_LINK ); p.setColor( QPalette::Text, TomahawkStyle::HEADER_TEXT ); ui->biography->setFont( f ); @@ -137,7 +136,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->biography->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); - ui->biography->document()->setDefaultStyleSheet( "a { text-decoration: none; font-weight: bold; color: #ffffff; }" ); + ui->biography->document()->setDefaultStyleSheet( QString( "a { text-decoration: none; font-weight: bold; color: %1; }" ).arg( TomahawkStyle::HEADER_LINK.name() ) ); TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); connect( ui->biography, SIGNAL( anchorClicked( QUrl ) ), SLOT( onBiographyLinkClicked( QUrl ) ) ); From 865b7db1b873b8dfd6954de4af40caa69be76bee Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Fri, 14 Jun 2013 02:16:47 +0200 Subject: [PATCH 371/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 146 +++++++++++++++++------------------ lang/tomahawk_bg.ts | 146 +++++++++++++++++------------------ lang/tomahawk_bn_IN.ts | 146 +++++++++++++++++------------------ lang/tomahawk_ca.ts | 146 +++++++++++++++++------------------ lang/tomahawk_ca@valencia.ts | 146 +++++++++++++++++------------------ lang/tomahawk_cs.ts | 146 +++++++++++++++++------------------ lang/tomahawk_da.ts | 146 +++++++++++++++++------------------ lang/tomahawk_de.ts | 146 +++++++++++++++++------------------ lang/tomahawk_el.ts | 146 +++++++++++++++++------------------ lang/tomahawk_en.ts | 146 +++++++++++++++++------------------ lang/tomahawk_es.ts | 146 +++++++++++++++++------------------ lang/tomahawk_fi.ts | 146 +++++++++++++++++------------------ lang/tomahawk_fr.ts | 146 +++++++++++++++++------------------ lang/tomahawk_gl.ts | 146 +++++++++++++++++------------------ lang/tomahawk_hi_IN.ts | 146 +++++++++++++++++------------------ lang/tomahawk_hu.ts | 146 +++++++++++++++++------------------ lang/tomahawk_id.ts | 146 +++++++++++++++++------------------ lang/tomahawk_it.ts | 146 +++++++++++++++++------------------ lang/tomahawk_ja.ts | 146 +++++++++++++++++------------------ lang/tomahawk_lt.ts | 146 +++++++++++++++++------------------ lang/tomahawk_pl.ts | 146 +++++++++++++++++------------------ lang/tomahawk_pt_BR.ts | 146 +++++++++++++++++------------------ lang/tomahawk_ru.ts | 146 +++++++++++++++++------------------ lang/tomahawk_sv.ts | 146 +++++++++++++++++------------------ lang/tomahawk_tr.ts | 146 +++++++++++++++++------------------ lang/tomahawk_zh_CN.ts | 146 +++++++++++++++++------------------ lang/tomahawk_zh_TW.ts | 146 +++++++++++++++++------------------ 27 files changed, 1971 insertions(+), 1971 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index a74ef9c0ad..c1a11cf7ae 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -286,7 +286,7 @@ connect and stream from you? نعتذر، لم نستطيع إيجاد أغاني أخرى لهذا الألبوم! - + Other Albums by %1 ألبومات أخرى ل%1 @@ -308,37 +308,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits الأكثر شهرة - + Related Artists الفنانين ذات الذوق القريب - + Albums ألبومات - + Sorry, we could not find any albums for this artist! نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! - + Sorry, we could not find any related artists! نعتذر، لم نستطيع إيجاد فنانين! - + Sorry, we could not find any top hits for this artist! نعتذر، لم نستطيع إيجاد أغاني مشهورة جدا لهذا الفنان! - + # IN YOUR CHARTS @@ -384,17 +384,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 نعتذر، لم نستطيع إيجاد الأغنية '%1' ل%2 - + Sorry, Tomahawk couldn't find the artist '%1' نعتذر، لم نستطيع إيجاد الفنان '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 نعتذر، لم نستطيع إيجاد الألبوم '%1' ل%2 @@ -615,12 +615,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -788,12 +788,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. هذه المجموعة فارغة حاليا. - + This playlist is currently empty. Add some tracks to it and enjoy the music! هذه القائمة فارغة حاليا. أضف لها بعض الأغاني واستمتع للموسيقى! @@ -1057,32 +1057,32 @@ Password LovedTracksItem - + Top Loved Tracks أفضل الأغاني المحبوبة - + Sorry, we could not find any loved tracks! نعتذر، لم نستطيع إيجاد اي من الأغاني المحبوبة! - + The most loved tracks from all your friends أفضل الأغاني المحبوبة من جميع أصدقائك - + All of your loved tracks جميع أغانيك المحبوبة - + All of %1's loved tracks جميع أغاني "%1" المحبوبة - + Loved Tracks الأغاني المحبوبة @@ -2142,68 +2142,68 @@ Password SourceTreeView - + &Copy Link &نسخ الرابط - + &Delete %1 &أحذف %1 - + Add to my Playlists أضف إلى لوائحي للأغاني - + Add to my Automatic Playlists أضف إلى لوائحي الأوتوماتيكية للأغاني - + Add to my Stations أضف إلى إذاعاتي - + &Export Playlist &تصدير قائمة الأغاني - + playlist قائمة الأغاني - + automatic playlist قائمة أغاني أوتوماتيكية - + station إذاعة - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? هل ترغب في حذف %1 <b>"%2"</b>؟ - + Delete احذف - + Save XSPF حفظ XSPF - + Playlists (*.xspf) قوائم أغاني (*.xspf) @@ -3882,156 +3882,156 @@ enter the displayed PIN number here: توماهوك - + Back إلى الوراء - + Go back one page العودة صفحة واحدة إلى الوراء - + Forward تقدم - + Go forward one page تقدم صفحة واحدة - - + + Hide Menu Bar إخفي شريط القائمة - - + + Show Menu Bar أظهر شريط القائمة - + Search for any artist, album or song... ابحث عن أي ألبوم، فنان أو أغنية... - + &Main Menu ال&قائمة الرئيسية - + Exit Full Screen الخروج من وضع ملء الشاشة - + Enter Full Screen الدخول إلى وضع ملء الشاشة - + XSPF Error خطأ XSPF - + This is not a valid XSPF playlist. قائمة الأغاني XSPF هذه ليست صالحة. - + Failed to save tracks فشل في حفظ الأغاني - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. بعض الأغاني في قائمة الأغاني لا تحتوي على إسم الفنان أو إسم الأغنية. هذه الأغاني سوف تتجاهل. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. تأكد أن لديك خلفية فونون المناسبة والإضافات المطلوبة مثبتة. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. - + Station إذاعة - + Create New Station إنشاء قائمة أغاني جديدة - + Name: الاسم: - + Playlist قائمة الأغاني - + Automatic Playlist قائمة أغاني أوتوماتيكية - + Pause تعليق - + &Play &إستمع - + %1 by %2 track, artist name %1 من قبل %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 حق النشر ٢٠١٠ - ٢٠١٣ - + Thanks to: شكر لكل من: - + About Tomahawk عن توماهوك @@ -4067,27 +4067,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). لقد استمعت إلى هذه الأغنية %n مرة.لقد استمعت إلى هذه الأغنية مرة %n.لقد استمعت إلى هذه الأغنية مرتين %n.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات. - + You've never listened to this track before. لم تستمع لهذه الأغنية من قبل. - + You first listened to it on %1. استمعت إليها أولاً في %1. - + You've listened to %1 %n time(s). لقد استمعت إلى %1 %n مرة.لقد استمعت إلى %1 مرة %n.لقد استمعت إلى %1 مرتين %n.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات. - + You've never listened to %1 before. لم تستمع إلى %1 من قبل. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index c37e849664..724d889ed3 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -285,7 +285,7 @@ connect and stream from you? Съжалявам, но не откривам нито една песен за този изпълнител! - + Other Albums by %1 Други албуми от %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Най-известни изпълнения - + Related Artists Изпълнители с подобно звучене - + Albums Албуми - + Sorry, we could not find any albums for this artist! Съжалявам, но не откривам нито един албум за този изпълнител! - + Sorry, we could not find any related artists! Съжалявам, но не откривам нито един подобен на този изпълнтел! - + Sorry, we could not find any top hits for this artist! Съжалявам, но не откривам нито една хитова песен на този изпълнител! - + # IN YOUR CHARTS # В ТВОИТЕ КЛАСАЦИИ @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Съжалявам. Не успявам да открия изпълнение '%1' от '%2' - + Sorry, Tomahawk couldn't find the artist '%1' Съжалявам, но не откривам '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Съжалявам, но не откривам албум с име '%1' от '%2' @@ -620,12 +620,12 @@ Tomahawk създаде доклад относно това и изпращай - + Recently played tracks Наскоро изпълнени - + No recently created playlists in your network. Няма наскоро създавани списъци в твоята мрежа @@ -793,12 +793,12 @@ Tomahawk създаде доклад относно това и изпращай FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -806,12 +806,12 @@ Tomahawk създаде доклад относно това и изпращай FlexibleView - + This playlist is currently empty. Този списък е празен, в момента. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Този списък е празен в момента. Добави няколко песни и се наслади на музиката. @@ -1063,32 +1063,32 @@ Password LovedTracksItem - + Top Loved Tracks Най-харесвани изпълнения - + Sorry, we could not find any loved tracks! Съжалявам, не откривам песни, които са маркирани като любими. - + The most loved tracks from all your friends Най-харесваните изпълнения от всичките ти приятели - + All of your loved tracks Всички песни, които харесваш - + All of %1's loved tracks Всички песни, които %1 харесва - + Loved Tracks Любими изпълнения @@ -2154,68 +2154,68 @@ Password SourceTreeView - + &Copy Link &Копирай адреса - + &Delete %1 &Изтрий %1 - + Add to my Playlists Добави към моите списъци - + Add to my Automatic Playlists Добави към моите автоматично-генерирани списъци - + Add to my Stations Добави към станциите ми - + &Export Playlist &Изнеси списък - + playlist списък - + automatic playlist автоматично-генериран списък - + station станция - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Наистина ли желаеш да изтриеш %1 <b>''%2" </b> - + Delete Изтрий - + Save XSPF Запази XSPF - + Playlists (*.xspf) Списъци (*.xspf) @@ -3896,159 +3896,159 @@ enter the displayed PIN number here: Tomahawk - + Back Назад - + Go back one page Една страница назад - + Forward Напред - + Go forward one page Една страница напред - - + + Hide Menu Bar Скрий лентата с менюто - - + + Show Menu Bar Покажи лентата с менюто - + Search for any artist, album or song... Търси всеки изпълнител, албум или песен... - + &Main Menu &Основно меню - + Exit Full Screen Излез от режим на цял екран - + Enter Full Screen Превключи в режим на цял екран - + XSPF Error XSPF Грешка - + This is not a valid XSPF playlist. Това не е валиден XSPF списък - + Failed to save tracks Не успях да запазя избраните песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Някои от песните в този списък нямат изпълнител и заглавие. Те ще бъдат игнорирани. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Съжалявам. Има проблем с достъпа до твоето аудио-устройство или до избраната песен - тя ще бъде прескочена. Моля, увери се, че са инсталирани подходящ Phonon и приставки. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Съжалявам. Има проблем с достъпа до твоето аудио устройство или избраната песен. Тя ще бъде пропусната. - + Station Станция - + Create New Station Създай нова станция - + Name: Име: - + Playlist Списък - + Automatic Playlist Автоматично-генериран списък - + Pause Пауза - + &Play &Възпроизвеждане - + %1 by %2 track, artist name %1 от %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Всички права - запазени 2010 - 2013 - + Thanks to: Благодарности на: - + About Tomahawk Относно Tomahawk @@ -4084,27 +4084,27 @@ enter the displayed PIN number here: # В ТВОИТЕ КЛАСАЦИИ - + You've listened to this track %n time(s). Ти си слушал тази песен %n път(и)Ти си слушал тази песен %n път(и) - + You've never listened to this track before. Никога не си слушал тази песен преди - + You first listened to it on %1. Първоначално си я слушал на %1 - + You've listened to %1 %n time(s). Слушал си %1 път(и)Слушал си %1 %n път(и) - + You've never listened to %1 before. Никога не си слушал %1 преди diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index f0621fdf3d..9495ffeb03 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -285,7 +285,7 @@ connect and stream from you? - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -2138,68 +2138,68 @@ Password SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -3867,156 +3867,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4052,27 +4052,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 65cd41514f..849a84170e 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -285,7 +285,7 @@ connect and stream from you? No s'ha trobat cap altra cançó d'aquest àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'aquest artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'aquest artista - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 No s'ha trobat la cançó «%1» de «%2» - + Sorry, Tomahawk couldn't find the artist '%1' No s'ha trobat l'artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 No s'ha trobat l'àlbum «%1» de «%2» @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks Les cançons més estimades - + Sorry, we could not find any loved tracks! No s'ha trobat cap cançó estimada - + The most loved tracks from all your friends Les cançons més estimades per tots els vostres amics - + All of your loved tracks Totes les vostres cançons estimades - + All of %1's loved tracks Totes les cançons més estimades per %1 - + Loved Tracks Cançons preferides @@ -2142,68 +2142,68 @@ i emissores basades en els vostres gusts musicals. SourceTreeView - + &Copy Link &Copia l'Enllaç - + &Delete %1 &Esborra %1 - + Add to my Playlists Afegeix a les meves Llistes - + Add to my Automatic Playlists Afegeix a les meves Llistes Automàtiques - + Add to my Stations Afegeix a les meves Emissores - + &Export Playlist E&xporta la Llista de Reproducció - + playlist llista - + automatic playlist llista automàtica - + station emissora - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Voleu esborrar %1 <b>"%2"</b>? - + Delete Suprimeix - + Save XSPF Desa com XSPF - + Playlists (*.xspf) Llistes de reproducció (*.xspf) @@ -3882,156 +3882,156 @@ introduïu el PIN aquí: Tomahawk - + Back Enrere - + Go back one page Retrocedeix una pàgina - + Forward Endavant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Surt de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en desar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4067,27 +4067,27 @@ introduïu el PIN aquí: - + You've listened to this track %n time(s). Heu escoltat aquesta cançó %n cop.Heu escoltat aquesta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai aquesta cançó abans. - + You first listened to it on %1. Vau escoltar aquesta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 192812926c..a6c42a8fe1 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -285,7 +285,7 @@ connect and stream from you? No s'ha trobat cap altra cançó d'este àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'este artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'este artista - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 No s'ha trobat la cançó «%1» de «%2» - + Sorry, Tomahawk couldn't find the artist '%1' No s'ha trobat l'artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 No s'ha trobat l'àlbum «%1» de «%2» @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks Les cançons més estimades - + Sorry, we could not find any loved tracks! No s'ha trobat cap cançó estimada - + The most loved tracks from all your friends Les cançons més estimades per tots els vostres amics - + All of your loved tracks Totes les vostres cançons estimades - + All of %1's loved tracks Totes les cançons més estimades per %1 - + Loved Tracks Cançons preferides @@ -2142,68 +2142,68 @@ i emissores basades en els vostres gusts musicals. SourceTreeView - + &Copy Link &Copia l'Enllaç - + &Delete %1 &Esborra %1 - + Add to my Playlists Afig a les meues Llistes - + Add to my Automatic Playlists Afig a les meues Llistes Automàtiques - + Add to my Stations Afig a les meues Emissores - + &Export Playlist E&xporta la Llista de Reproducció - + playlist llista - + automatic playlist llista automàtica - + station emissora - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Voleu esborrar %1 <b>"%2"</b>? - + Delete Suprimeix - + Save XSPF Alça com XSPF - + Playlists (*.xspf) Llistes de reproducció (*.xspf) @@ -3882,156 +3882,156 @@ introduïu el PIN ací: Tomahawk - + Back Arrere - + Go back one page Retrocedeix una pàgina - + Forward Avant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Ix de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en alçar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4067,27 +4067,27 @@ introduïu el PIN ací: - + You've listened to this track %n time(s). Heu escoltat esta cançó %n cop.Heu escoltat esta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai esta cançó abans. - + You first listened to it on %1. Vau escoltar esta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index b35a3959e3..195ea47c27 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -286,7 +286,7 @@ se s vámi spojil? Promiňte, nepodařilo se najít žádné skladby pro toto album! - + Other Albums by %1 Jiná alba %1 @@ -308,37 +308,37 @@ se s vámi spojil? ArtistInfoWidget - + Top Hits Nejlepší písně - + Related Artists Podobní umělci - + Albums Alba - + Sorry, we could not find any albums for this artist! Promiňte, nepodařilo se najít žádná alba tohoto umělce! - + Sorry, we could not find any related artists! Promiňte, nepodařilo se najít žádné podobné umělce! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! - + # IN YOUR CHARTS # VE VAŠICH ŽEBŘÍČCÍCH @@ -384,17 +384,17 @@ se s vámi spojil? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Promiňte, Tomahawku se nepodařilo najít skladbu '%1' od %2 - + Sorry, Tomahawk couldn't find the artist '%1' Promiňte, Tomahawku se nepodařilo najít umělce '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Promiňte, Tomahawku se nepodařilo najít album '%1' od %2 @@ -615,12 +615,12 @@ se s vámi spojil? - + Recently played tracks Nedávno poslouchané skladby - + No recently created playlists in your network. Ve vaší síti nejsou žádné nedávno vytvořené seznamy skladeb. @@ -788,12 +788,12 @@ se s vámi spojil? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ se s vámi spojil? FlexibleView - + This playlist is currently empty. Tento seznam skladeb je nyní prázdný. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tento seznam skladeb je nyní prázdný. Přidejte do něj nějaké skladby a vychutnávejte hudbu! @@ -1057,32 +1057,32 @@ heslo LovedTracksItem - + Top Loved Tracks Nejoblíbenější skladby - + Sorry, we could not find any loved tracks! Promiňte, nepodařilo se najít žádné oblíbené skladby! - + The most loved tracks from all your friends Nejoblíbenější skladby všech vašich přátel - + All of your loved tracks Vaše nejmilejší skladby - + All of %1's loved tracks %1's nejmilejších skladeb - + Loved Tracks Nejmilejší skladby @@ -2141,68 +2141,68 @@ heslo SourceTreeView - + &Copy Link &Kopírovat odkaz - + &Delete %1 &Smazat %1 - + Add to my Playlists Přidat do mých seznamů skladeb - + Add to my Automatic Playlists Přidat do mých automatických seznamů skladeb - + Add to my Stations Přidat do mých stanic - + &Export Playlist &Vyvést seznam skladeb - + playlist Seznam skladeb - + automatic playlist Automatický seznam skladeb - + station Stanice - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Chcete smazat %1 <b>"%2"</b>? - + Delete Smazat - + Save XSPF Uložit XSPF - + Playlists (*.xspf) Seznamy skladeb (*.xspf) @@ -3882,156 +3882,156 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: Tomahawk - + Back Zpět - + Go back one page Jít o jednu stranu zpět - + Forward Vpřed - + Go forward one page Jít o jednu stranu vpřed - - + + Hide Menu Bar Skrýt pruh s hlavní nabídkou - - + + Show Menu Bar Ukázat pruh s hlavní nabídkou - + Search for any artist, album or song... Hledat umělce, album nebo píseň... - + &Main Menu Hlavní &nabídka - + Exit Full Screen Ukončit režim na celou obrazovku - + Enter Full Screen Vstoupit do režimu na celou obrazovku - + XSPF Error Chyba XSPF - + This is not a valid XSPF playlist. Toto není platný seznam skladeb XSPF. - + Failed to save tracks Nepodařilo se uložit skladby - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Některé skladby v seznamu skladeb neobsahují ani umělce ani název. Tyto budou přehlíženy. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. Ujistěte se, že máte nainstalováno vhodné jádro Phonona potřebné přídavné moduly. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. - + Station Stanice - + Create New Station Vytvořit novou stanici - + Name: Název: - + Playlist Seznam skladeb - + Automatic Playlist Automatický seznam skladeb - + Pause Pozastavit - + &Play &Přehrát - + %1 by %2 track, artist name %1 od %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorské právo 2010 - 2013 - + Thanks to: Poděkování: - + About Tomahawk O Tomahawku @@ -4067,27 +4067,27 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: # VE VAŠICH ŽEBŘÍČCÍCH - + You've listened to this track %n time(s). Tuto píseň jste si poslechl jednou.Tuto píseň jste si poslechl %n krát.Tuto píseň jste si poslechl %n krát. - + You've never listened to this track before. Tuto píseň jste si ještě nikdy neposlechl. - + You first listened to it on %1. Tuto píseň jste si poprvé poslechl %1. - + You've listened to %1 %n time(s). %1 jste si poslechl jednou.%1 jste si poslechl %n krát.%1 jste si poslechl %n krát. - + You've never listened to %1 before. %1 jste si předtím ještě nikdy neposlechl. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 7e66cac375..6098dbedd6 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -285,7 +285,7 @@ connect and stream from you? - + Other Albums by %1 Andre Albums af %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Relaterede Kunstnere - + Albums Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -2139,68 +2139,68 @@ Password SourceTreeView - + &Copy Link &Kopier Link - + &Delete %1 &Slet %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist &Eksporter Spilleliste - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF Gem XSPF - + Playlists (*.xspf) Spilleliste (*.xspf) @@ -3869,156 +3869,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF Fejl - + This is not a valid XSPF playlist. Dette er ikke en gyldig XSPF spilleliste - + Failed to save tracks Fejlede i at gemme numrene - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station Lav Ny Station - + Name: Navn: - + Playlist - + Automatic Playlist - + Pause Pause - + &Play - + %1 by %2 track, artist name %1 af %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4054,27 +4054,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index e8ed4bdaff..c45b9d2574 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -286,7 +286,7 @@ erlauben sich mit dir zu verbinden? Sorry, wir konnten keine anderen Lieder für dieses Album finden! - + Other Albums by %1 Andere Alben von %1 @@ -308,37 +308,37 @@ erlauben sich mit dir zu verbinden? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Ähnliche Künstler - + Albums Alben - + Sorry, we could not find any albums for this artist! Sorry, wir konnten keine Alben für diesen Künstler finden! - + Sorry, we could not find any related artists! Sorry, wir konnten keine ähnlichen Künstler finden! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! - + # IN YOUR CHARTS # IN DEINEN CHARTS @@ -384,17 +384,17 @@ erlauben sich mit dir zu verbinden? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Sorry, Tomahawk konnte '%1' von %2 nicht finden - + Sorry, Tomahawk couldn't find the artist '%1' Sorry, Tomahawk konnte den Künstler '%1' nicht finden - + Sorry, Tomahawk couldn't find the album '%1' by %2 Sorry, Tomahawk konnte das Album '%1' von %2 nicht finden @@ -615,12 +615,12 @@ erlauben sich mit dir zu verbinden? - + Recently played tracks Zuletzt gehörte Lieder - + No recently created playlists in your network. Es gibt keine kürzlich erstellten Wiedergabelisten in deinem Netzwerk. @@ -788,12 +788,12 @@ erlauben sich mit dir zu verbinden? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ erlauben sich mit dir zu verbinden? FlexibleView - + This playlist is currently empty. Diese Playlist ist momentan leer. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Diese Playlist ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! @@ -1057,32 +1057,32 @@ Passwort LovedTracksItem - + Top Loved Tracks Meist-geliebte Songs - + Sorry, we could not find any loved tracks! Sorry, wir konnten keine Lieblingslieder finden! - + The most loved tracks from all your friends Die meist-geliebten Songs all deiner Freunde - + All of your loved tracks Deine Lieblingslieder - + All of %1's loved tracks %1's Lieblingslieder - + Loved Tracks Lieblingslieder @@ -2141,68 +2141,68 @@ Passwort SourceTreeView - + &Copy Link &Kopiere Link - + &Delete %1 &Lösche %1 - + Add to my Playlists Zu meinen Playlisten hinzufügen - + Add to my Automatic Playlists Zu meinen Automatischen Playlisten hinzufügen - + Add to my Stations Zu meinen Stationen hinzufügen - + &Export Playlist Playlist &exportieren - + playlist Playlist - + automatic playlist Automatische Playlist - + station Station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Möchtest du die %1 <b>"%2"</b> löschen? - + Delete Löschen - + Save XSPF XSPF speichern - + Playlists (*.xspf) Playlisten (*.xspf) @@ -3877,156 +3877,156 @@ Tomahawk auf Twitter's Website authentifiziert hast: Tomahawk - + Back Zurück - + Go back one page Gehe eine Seite zurück - + Forward Vorwärts - + Go forward one page Gehe eine Seite vorwärts - - + + Hide Menu Bar Menüleiste ausblenden - - + + Show Menu Bar Menüleiste einblenden - + Search for any artist, album or song... Suche nach Künstler, Album oder Lied... - + &Main Menu Haupt&menü - + Exit Full Screen Vollbildmodus deaktivieren - + Enter Full Screen Vollbildmodus aktivieren - + XSPF Error XSPF-Fehler - + This is not a valid XSPF playlist. Dies ist keine gültige XSPF-Playlist. - + Failed to save tracks Konnte Stücke nicht abspeichern - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Einige Stücke in der Playlist enthalten weder Künstler noch Titel. Diese werden ignoriert. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. Vergewisser dich, dass ein geignetes Phonon-Backend mitsamt benötigten Plugins installiert ist. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. - + Station Station - + Create New Station Neue Station erstellen - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatische Playlist - + Pause Pause - + &Play Abs&pielen - + %1 by %2 track, artist name %1 von %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Danke an: - + About Tomahawk Über Tomahawk @@ -4062,27 +4062,27 @@ Tomahawk auf Twitter's Website authentifiziert hast: # IN DEINEN CHARTS - + You've listened to this track %n time(s). Du hast dieses Lied einmal gehört.Du hast dieses Lied %n mal gehört. - + You've never listened to this track before. Du hast dieses Lied noch nie angehört. - + You first listened to it on %1. Du hast dieses Lied zum ersten mal am %1 gehört. - + You've listened to %1 %n time(s). Du hast %1 einmal angehört.Du hast %1 %n mal angehört. - + You've never listened to %1 before. Du hast %1 vorher noch nie gehört. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index e33db86275..140f774c0c 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -285,7 +285,7 @@ connect and stream from you? Συγνωμη, δεν βρεθηκαν αλλα τραγουδια αυτου του αλμπουμ! - + Other Albums by %1 Άλλα Άλμπουμ από %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Κορυφαία τραγούδια - + Related Artists Παρόμοιοι Καλλιτέχνες - + Albums Άλμπουμ - + Sorry, we could not find any albums for this artist! Συγνωμη, δεν βρεθηκαν αλμπουμ αυτου του καλλιτεχνη! - + Sorry, we could not find any related artists! Συγνωμη, δεν βρεθηκαν καλλιτεχνες! - + Sorry, we could not find any top hits for this artist! Συγνωμη, δεν βρεθηκαν κορυφαια τραγουδια αυτου του καλλιτεχνη! - + # IN YOUR CHARTS # ΣΤΑ ΔΙΚΑ ΣΑΣ CHART @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Συγνωμη, το Tomahawk δεν βρηκε το τραγουδι '%1' απο %2 - + Sorry, Tomahawk couldn't find the artist '%1' Συγνωμη, το Tomahawk δεν μπορεσε να βρει τον καλλιτεχνη '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Συγνωμη, το Tomahawk δεν μπορεσε να βρει το αλμπουμ του '%1' απο %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks Τελευταίες αναπαραγωγές κομματιών - + No recently created playlists in your network. Δεν δημιουργηθηκαν πρόσφατα λίστες αναπαραγωγής στο δίκτυό σας. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Αυτη η λιστα αναπαραγωγης ειναι αδεια. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Αυτή η λίστα αναπαραγωγής είναι άδεια προς το παρόν. Προσθέστε μερικά κομμάτια σε αυτήν και απολαύστε την μουσική! @@ -1056,32 +1056,32 @@ Password LovedTracksItem - + Top Loved Tracks Κορυφαία Αγαπημένα Κομμάτια - + Sorry, we could not find any loved tracks! Συγνωμη, δεν βρεθηκε κανενα αγαπημενο τραγουδι! - + The most loved tracks from all your friends Τα πιο αγαπημένα κομμάτια από όλους τους φίλους σας - + All of your loved tracks Όλα τα αγαπημένα κομμάτια σας - + All of %1's loved tracks Όλα τα αγαπημένα κομμάτια του/της %1 - + Loved Tracks Αγαπημένα Κομμάτια @@ -2141,68 +2141,68 @@ Password SourceTreeView - + &Copy Link &Αντιγραφή Υπερσυνδέσμου - + &Delete %1 &Διαγραφή %1 - + Add to my Playlists Προσθηκη στις λιστες αναπαραγωγης - + Add to my Automatic Playlists Προσθηκη στις αυτοματες λιστες αναπαραγωγης σας - + Add to my Stations Προσθηκη στους σταθμους μου - + &Export Playlist &Εξαγωγή Λίστας Αναπαραγωγής - + playlist λίστας αναπαραγωγής - + automatic playlist αυτόματη λίστα αναπαραγωγής - + station σταθμός - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Θέλετε να διαγράψετε την %1 <b>"%2"</b>; - + Delete Διαγραφή - + Save XSPF Αποθήκευση αρχείου XSPF - + Playlists (*.xspf) Λίστες Αναπαραγωγής (*.xspf) @@ -3882,156 +3882,156 @@ enter the displayed PIN number here: Tomahawk - + Back Πίσω - + Go back one page Πήγαινε πίσω μία σελίδα - + Forward Μπροστά - + Go forward one page Πήγαινε μπροστά μία σελίδα - - + + Hide Menu Bar Απόκρυψη Γραμμής Μενού - - + + Show Menu Bar Εμφανιση του κεντρικου μενου - + Search for any artist, album or song... Αναζήτηση για οποιονδήποτε καλλιτέχνη, άλμπουμ ή τραγούδι... - + &Main Menu &Κεντρικο Μενου - + Exit Full Screen Έξοδος από Πλήρη Οθόνη - + Enter Full Screen Εισαγωγή σε Πλήρη Οθόνη - + XSPF Error Σφάλμα XSPF - + This is not a valid XSPF playlist. Αυτή δεν είναι μια έγκυρη λίστα αναπαραγωγής XSPF. - + Failed to save tracks Αποτυχία αποθήκευσης κομματιών. - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Μερικά κομμάτια στην λίστα αναπαραγωγής δεν περιέχουν έναν καλλιτέχνη ή έναν τίτλο. Θα αγνοηθούν. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. Σιγουρευτείτε ότι έχετε εγκαταστήσει ένα κατάλληλο Phonon backend και τα απαιτούμενα πρόσθετα. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. - + Station Σταθμός - + Create New Station Δημιουργία Νέου Σταθμού - + Name: Όνομα: - + Playlist Λίστας Αναπαραγωγής - + Automatic Playlist Αυτόματη Λίστα Αναπαραγωγής - + Pause Παύση - + &Play &Αναπαραγωγή - + %1 by %2 track, artist name %1 από %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Χάρη στους: - + About Tomahawk Σχετικά με το Tomahawk @@ -4067,27 +4067,27 @@ enter the displayed PIN number here: # ΣΤΑ ΔΙΚΑ ΣΑΣ CHARTS - + You've listened to this track %n time(s). Έχετε ακούσει το κομμάτι %n φορά.Έχετε ακούσει το κομμάτι %n φορές. - + You've never listened to this track before. Δεν έχετε ακούσει αυτό το κομμάτι παλιότερα. - + You first listened to it on %1. Το ακούσατε για πρώτη φορά στις %1. - + You've listened to %1 %n time(s). Ακούσατε το %1 %n φορά.Ακούσατε το %1 %n φορές. - + You've never listened to %1 before. Δεν έχετε ακούσει το %1 ποτέ πριν. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index d27861af5a..8d3f7c3c62 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -286,7 +286,7 @@ connect and stream from you? Sorry, we could not find any tracks for this album! - + Other Albums by %1 Other Albums by %1 @@ -308,37 +308,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Related Artists - + Albums Albums - + Sorry, we could not find any albums for this artist! Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS # IN YOUR CHARTS @@ -384,17 +384,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -615,12 +615,12 @@ connect and stream from you? An overview of your recent activity - + Recently played tracks Recently played tracks - + No recently created playlists in your network. No recently created playlists in your network. @@ -788,12 +788,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1057,32 +1057,32 @@ Password LovedTracksItem - + Top Loved Tracks Top Loved Tracks - + Sorry, we could not find any loved tracks! Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends The most loved tracks from all your friends - + All of your loved tracks All of your loved tracks - + All of %1's loved tracks All of %1's loved tracks - + Loved Tracks Loved Tracks @@ -2144,68 +2144,68 @@ Password SourceTreeView - + &Copy Link &Copy Link - + &Delete %1 &Delete %1 - + Add to my Playlists Add to my Playlists - + Add to my Automatic Playlists Add to my Automatic Playlists - + Add to my Stations Add to my Stations - + &Export Playlist &Export Playlist - + playlist playlist - + automatic playlist automatic playlist - + station station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Would you like to delete the %1 <b>"%2"</b>? - + Delete Delete - + Save XSPF Save XSPF - + Playlists (*.xspf) Playlists (*.xspf) @@ -3885,156 +3885,156 @@ enter the displayed PIN number here: Tomahawk - + Back Back - + Go back one page Go back one page - + Forward Forward - + Go forward one page Go forward one page - - + + Hide Menu Bar Hide Menu Bar - - + + Show Menu Bar Show Menu Bar - + Search for any artist, album or song... Search for any artist, album or song... - + &Main Menu &Main Menu - + Exit Full Screen Exit Full Screen - + Enter Full Screen Enter Full Screen - + XSPF Error XSPF Error - + This is not a valid XSPF playlist. This is not a valid XSPF playlist. - + Failed to save tracks Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Station - + Create New Station Create New Station - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatic Playlist - + Pause Pause - + &Play &Play - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk About Tomahawk @@ -4070,27 +4070,27 @@ enter the displayed PIN number here: # IN YOUR CHARTS - + You've listened to this track %n time(s). You've listened to this track %n time.You've listened to this track %n times. - + You've never listened to this track before. You've never listened to this track before. - + You first listened to it on %1. You first listened to it on %1. - + You've listened to %1 %n time(s). You've listened to %1 %n time.You've listened to %1 %n times. - + You've never listened to %1 before. You've never listened to %1 before. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 0f1e4e63f9..dafc78cff0 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -286,7 +286,7 @@ conectarse a usted y transmitir música? No se encontraron pistas de este álbum - + Other Albums by %1 Otros álbumes de %1 @@ -308,37 +308,37 @@ conectarse a usted y transmitir música? ArtistInfoWidget - + Top Hits Grandes éxitos - + Related Artists Artistas relacionados - + Albums Álbumes - + Sorry, we could not find any albums for this artist! No se encontraron álbumes de este artista - + Sorry, we could not find any related artists! No se encontraron artistas relacionados - + Sorry, we could not find any top hits for this artist! No se encontraron éxitos de este artista - + # IN YOUR CHARTS @@ -384,17 +384,17 @@ conectarse a usted y transmitir música? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawk no pudo encontrar la pista '%1' de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawk no pudo encontrar el artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawk no pudo encontrar el álbum '%1' de %2 @@ -615,12 +615,12 @@ conectarse a usted y transmitir música? - + Recently played tracks - + No recently created playlists in your network. @@ -788,12 +788,12 @@ conectarse a usted y transmitir música? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ conectarse a usted y transmitir música? FlexibleView - + This playlist is currently empty. Lista de reproducción vacía - + This playlist is currently empty. Add some tracks to it and enjoy the music! Lista de reproducción vacía. ¡Añada pistas y disfrute de la música! @@ -1056,32 +1056,32 @@ Password LovedTracksItem - + Top Loved Tracks Pistas favoritas - + Sorry, we could not find any loved tracks! No se encontraron pistas favoritas - + The most loved tracks from all your friends Las pistas favoritas de sus amigos - + All of your loved tracks Mis pistas favoritas - + All of %1's loved tracks Pistas favoritas de %1 - + Loved Tracks Pistas favoritas @@ -2143,68 +2143,68 @@ y estaciones basadas en sus gustos personales. SourceTreeView - + &Copy Link &Copiar enlace - + &Delete %1 &Eliminar %1 - + Add to my Playlists Añadir a mis listas de reproducción - + Add to my Automatic Playlists Añadir a mis listas de reproducción automáticas - + Add to my Stations Añadir a mis estaciones - + &Export Playlist &Exportar lista de reproducción - + playlist lista de reproducción - + automatic playlist lista de reproducción automática - + station estación - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? ¿Borrar lista de reproducción %1 <b>"%2"</b>? - + Delete Eliminar - + Save XSPF Guardar XSPF - + Playlists (*.xspf) Listas de reproducción (*.xspf) @@ -3883,156 +3883,156 @@ introduzca su número PIN aquí: Tomahawk - + Back Atrás - + Go back one page Ir una página hacia atrás - + Forward Adelante - + Go forward one page Ir una página hacia adelante - - + + Hide Menu Bar Ocultar barra de menús - - + + Show Menu Bar Mostrar barra de menús - + Search for any artist, album or song... Buscar un artista, álbum o pista… - + &Main Menu &Menú principal - + Exit Full Screen Salir de pantalla completa - + Enter Full Screen Modo a pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. Esta no es una lista de reproducción XSPF válida. - + Failed to save tracks Fallo al guardar pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunas pistas en la lista de reproducción no contienen artista ni título. Serán ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Se ha producido un error al acceder al dispostivo de audio o a la pista deseada. Asegúrese de que ha instalado un backend de Phonon adecuado y los plugins necesarios. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Se ha producido un error al acceder al dispostivo de audio o a la pista deseado y se va saltar. - + Station Estación - + Create New Station Crear estación nueva - + Name: Nombre: - + Playlist Lista de reproducción - + Automatic Playlist Lista de reproducción automática - + Pause Pausar - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gracias a: - + About Tomahawk Acerca de Tomahawk @@ -4068,27 +4068,27 @@ introduzca su número PIN aquí: - + You've listened to this track %n time(s). Ha escuchado esta pista %n vez.Ha escuchado esta pista %n veces. - + You've never listened to this track before. Nunca ha escuchado esta pista antes. - + You first listened to it on %1. Escuchó esta pista por primera vez en %1. - + You've listened to %1 %n time(s). Ha escuchado %1 una vez.Ha escuchado %1 %n veces. - + You've never listened to %1 before. Nunca ha escuchado %1 antes. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 6cba2b55c1..1919d8f4c6 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -286,7 +286,7 @@ yhdistää ja toistaa sinulta virtaa? Valitettavasti emme löytäneet mitään tämän albumin kappaleita! - + Other Albums by %1 Muita artistin %1 levyjä @@ -308,37 +308,37 @@ yhdistää ja toistaa sinulta virtaa? ArtistInfoWidget - + Top Hits Parhaat hitit - + Related Artists Samankaltaisia artisteja - + Albums Albumit - + Sorry, we could not find any albums for this artist! Valitettavasti emme löytänet yhtään tämän artistin albumia! - + Sorry, we could not find any related artists! Valitettavasti emme löytäneet yhtään samankaltaista artistia! - + Sorry, we could not find any top hits for this artist! Valitettavasti emme löytäneet yhtään tämän artistin parhaista hiteistä! - + # IN YOUR CHARTS @@ -384,17 +384,17 @@ yhdistää ja toistaa sinulta virtaa? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Valitettavasti Tomahawk ei löytänyt artistin %2 kappaletta ”%1” - + Sorry, Tomahawk couldn't find the artist '%1' Valitettavasti Tomahawk ei löytänyt artistia ”%1” - + Sorry, Tomahawk couldn't find the album '%1' by %2 Valitettavasti Tomahawk ei löytänyt artistin %2 albumia ”%1” @@ -615,12 +615,12 @@ yhdistää ja toistaa sinulta virtaa? - + Recently played tracks - + No recently created playlists in your network. @@ -788,12 +788,12 @@ yhdistää ja toistaa sinulta virtaa? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ yhdistää ja toistaa sinulta virtaa? FlexibleView - + This playlist is currently empty. Tämä soittolista on parhaillaan tyhjä. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tämä soittolista on parhaillaan tyhjä. Lisää kappaleita ja nauti musiikista! @@ -1057,32 +1057,32 @@ salasana LovedTracksItem - + Top Loved Tracks Tykätyimmät kappaleet - + Sorry, we could not find any loved tracks! Valitettavasti emme löytäneet yhtään tykättyä kappaletta! - + The most loved tracks from all your friends Kaikkien kaveriesi tykätyimmät kappaleet - + All of your loved tracks Kaikki tykkäämäsi kappaleet - + All of %1's loved tracks Kaikki käyttäjän %1 tykkäämät kappaleet - + Loved Tracks Tykätyt kappaleet @@ -2146,68 +2146,68 @@ käyttäjäradion käyttöönottamiseksi SourceTreeView - + &Copy Link &Kopioi linkki - + &Delete %1 &Poista %1 - + Add to my Playlists Lisää omiin soittolistoihin - + Add to my Automatic Playlists Lisää omiin automaattisiin soittolistoihin - + Add to my Stations Lisää omiin asemiin - + &Export Playlist &Vie soittolista - + playlist soittolistan - + automatic playlist automaattisen soittolistan - + station aseman - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Haluatko poistaa %1 <b>”%2”</b>? - + Delete Poista - + Save XSPF Tallenna XSPF - + Playlists (*.xspf) Soittolistat (*.xspf) @@ -3888,156 +3888,156 @@ anna siellä näytetty PIN-koodi tähän: Tomahawk - + Back Takaisin - + Go back one page Mene yksi sivu takaisin - + Forward Eteenpäin - + Go forward one page Mene yksi sivu eteenpäin - - + + Hide Menu Bar Piilota valikkorivi - - + + Show Menu Bar Näytä valikkorivi - + Search for any artist, album or song... Hae artistia, albumia tai kappaletta... - + &Main Menu &Päävalikko - + Exit Full Screen Poistu koko näytöstä - + Enter Full Screen Siirry koko näyttöön - + XSPF Error XSPF-virhe - + This is not a valid XSPF playlist. Tämä ei ole kelvollinen XSPF-soittolista. - + Failed to save tracks Kappaleiden tallentaminen epäonnistui - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Joillakin soittolistan kappaleilla ei ole artistia ja nimeä. Ne jätetään huomiotta. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. Varmista, että sopiva Phononin taustaosa ja vaaditut liitännäiset on asennettu. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. - + Station Asema - + Create New Station Luo uusi asema - + Name: Nimi: - + Playlist Soittolista - + Automatic Playlist Automaattinen soittolista - + Pause Tauko - + &Play &Soita - + %1 by %2 track, artist name %1 artistilta %2 - + %1 - %2 current track, some window title %1 – %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010–2013 - + Thanks to: Kiitokset: - + About Tomahawk Tietoa Tomahawkista @@ -4073,27 +4073,27 @@ anna siellä näytetty PIN-koodi tähän: - + You've listened to this track %n time(s). Olet kuunnellut tätä kappaletta %n kerran.Olet kuunnellut tätä kappaletta %n kertaa. - + You've never listened to this track before. Et ole kuunnellut tätä kappaletta aiemmin. - + You first listened to it on %1. Kuuntelit sitä ensi kerran %1. - + You've listened to %1 %n time(s). Olet kuunnellut artistia %1 %n kerran.Olet kuunnellut artistia %1 %n kertaa. - + You've never listened to %1 before. Et ole kuunnellut artistia %1 aiemmin. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 435c77c838..9c2dddf1da 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -286,7 +286,7 @@ de se connecter et streamer de vous? Désolé, nous n'avons pu trouver aucune piste pour cet album ! - + Other Albums by %1 Autres albums de %1 @@ -308,37 +308,37 @@ de se connecter et streamer de vous? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artistes similaires - + Albums Albums - + Sorry, we could not find any albums for this artist! Désolé, on a pas pu trouver aucun album pour cet artiste! - + Sorry, we could not find any related artists! Désolé, on a rien trouvé par rapport a cet artite! - + Sorry, we could not find any top hits for this artist! Désolé, on a pas pu trouver aucun top hit pour cet artiste! - + # IN YOUR CHARTS @@ -384,17 +384,17 @@ de se connecter et streamer de vous? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Désolé, on a pas pu trouver la piste '%1' pour %2 - + Sorry, Tomahawk couldn't find the artist '%1' Désolé, on a pas pu trouver l'artiste '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Désolé, on a pas pu trouver l'album '%1' pour %2 @@ -615,12 +615,12 @@ de se connecter et streamer de vous? - + Recently played tracks - + No recently created playlists in your network. @@ -788,12 +788,12 @@ de se connecter et streamer de vous? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ de se connecter et streamer de vous? FlexibleView - + This playlist is currently empty. Cette liste de lecture est vide. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Cette liste de lecture est vide. Ajoutez des morceaux et profitez de la musique ! @@ -1056,32 +1056,32 @@ Password LovedTracksItem - + Top Loved Tracks Top pistes favoris - + Sorry, we could not find any loved tracks! Désolé, nous n'avons pu trouver aucune piste favorite ! - + The most loved tracks from all your friends Les titres favoris de vos amis - + All of your loved tracks Tous vos titres favoris - + All of %1's loved tracks Tous les titres favoris de %1 - + Loved Tracks Titres favoris @@ -2140,68 +2140,68 @@ Password SourceTreeView - + &Copy Link &Copier le lien - + &Delete %1 &Supprimer %1 - + Add to my Playlists Ajoute a mes listes de lecture - + Add to my Automatic Playlists Ajoute a mes listes de lecture automatique - + Add to my Stations Ajouter à mes stations - + &Export Playlist &Exporter la liste de lecture - + playlist liste de lecture - + automatic playlist liste de lecture automatique - + station station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Voulez-vous supprimer la %1 <b>"%2"</b>? - + Delete Supprimer - + Save XSPF Enregistrer XSPF - + Playlists (*.xspf) Listes de lecture (*.xspf) @@ -3880,156 +3880,156 @@ saisissez le numéro PIN ici : Tomahawk - + Back Retour - + Go back one page Reculer d'une page - + Forward Avancer - + Go forward one page Avancer d'une page - - + + Hide Menu Bar Masquer la barre de menu - - + + Show Menu Bar Afficher la barre de menu - + Search for any artist, album or song... Chercher un artiste, un album, ou un morceau... - + &Main Menu &Menu Principal - + Exit Full Screen Quitter le mode plein écran - + Enter Full Screen Activer le mode plein écran - + XSPF Error Erreur XSPF - + This is not a valid XSPF playlist. Ceci n'est pas une liste de lecture XSPF valide. - + Failed to save tracks Échec de la sauvegarde des pistes - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Certaines pistes dans la liste de lecture ne contiennent pas d'artiste ou de titre. Elles seront ignorées. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sautée. Vérifiez que vous avez un backend Phonon et les plugins requis installés. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours , celle-ci va être sautée. - + Station Station - + Create New Station Créer une nouvelle station - + Name: Nom : - + Playlist Liste de lecture - + Automatic Playlist Liste de lecture automatique - + Pause Pause - + &Play &Lire - + %1 by %2 track, artist name %1 par %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Droit d'auteur 2010 - 2013 - + Thanks to: Merci a: - + About Tomahawk A propos de Tomahawk @@ -4065,27 +4065,27 @@ saisissez le numéro PIN ici : - + You've listened to this track %n time(s). Vous avez écouté cette piste %n fois.Vous avez écouté cette piste %n fois. - + You've never listened to this track before. Vous n'avez encore jamais écouté cette piste. - + You first listened to it on %1. Vous l'avez écouté pour la première fois le %1. - + You've listened to %1 %n time(s). Vous avez écouté %1 %n fois.Vous avez écouté %1 %n fois. - + You've never listened to %1 before. Vous n'avez encore jamais écouté %1. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 1cf1b9bb5b..e506643a6e 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -285,7 +285,7 @@ connect and stream from you? Non se puido atopar ningunha outra pista para este álbum! - + Other Albums by %1 Outros álbums de %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Maiores éxitos - + Related Artists Artistas relacionados - + Albums Álbums - + Sorry, we could not find any albums for this artist! Non se atopou ningún álbum para este artista! - + Sorry, we could not find any related artists! Non se atopou ningún artista relacionado! - + Sorry, we could not find any top hits for this artist! Non se atopou ningún éxito deste artista! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawk non atopa a pista «%1» de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawk non atopa o artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawk non atopa o álbum «%1» de %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Esta lista de reprodución está baleira. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodución está baleira. Engádelle algunhas pistas para gozar da música! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks As pistas que máis me gustan - + Sorry, we could not find any loved tracks! Non se atopan pistas que che gustaran! - + The most loved tracks from all your friends As pistas que máis lle gustaron aos teus amigos - + All of your loved tracks Todas as pistas que che gustaron - + All of %1's loved tracks Todas as pistas de %1 que che gustaron - + Loved Tracks Pistas favoritas @@ -2139,69 +2139,69 @@ Password SourceTreeView - + &Copy Link Copiar a &ligazón - + &Delete %1 &Borrar %1 - + Add to my Playlists Engadir ás miñas listas de reprodución - + Add to my Automatic Playlists Engadir ás miñas listas reprodución automática - + Add to my Stations Engadir á miña emisora - + &Export Playlist &Exportar a lista de reprodución - + playlist Lista de reprodución - + automatic playlist lista de reprodución automática - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Queres eliminar a %1 que se chama <b>«%2»</b>? - + Delete Borrar - + Save XSPF Gardar XSPF - + Playlists (*.xspf) Listas de reprodución (*.xspf) @@ -3881,157 +3881,157 @@ enter the displayed PIN number here: Tomahawk - + Back Atrás - + Go back one page Ir unha páxina atrás - + Forward Adiante - + Go forward one page Ir unha páxina adiante - - + + Hide Menu Bar Agochar a barra de menú - - + + Show Menu Bar Mostrar a barra de menú - + Search for any artist, album or song... Buscar a calquera artista, álbum ou canción... - + &Main Menu Menú &principal - + Exit Full Screen Saír da pantalla ao completo - + Enter Full Screen Entrar na pantalla ao completo - + XSPF Error Erro XSPF - + This is not a valid XSPF playlist. Esta non é unha lista de XSPF válida. - + Failed to save tracks Fallou o gardado de pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunhas pistas na lista de reprodución non indican nin artista nin o título. Ignoraranse. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. Asegúrate de ter o motor Phonon e os engadidos necesarios instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. - + Station - + Create New Station Crear unha nova emisión - + Name: Nome: - + Playlist Lista de reprodución - + Automatic Playlist Lista de reprodución automática - + Pause Pausa - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1.- %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecementos: - + About Tomahawk Acerca de Tomahawk @@ -4067,27 +4067,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Escoitaches esta pista %n vece(s).Escoitaches esta pista %n vece(s). - + You've never listened to this track before. Nunca antes escoitaras esta pista. - + You first listened to it on %1. Escoitaches esta pista por primeira vez en %1. - + You've listened to %1 %n time(s). Escoitaches %1 %n veces.Escoitaches %1 %n veces. - + You've never listened to %1 before. Nunca antes escoitaras a %1. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 5a15fa1cd8..6939aeb7b6 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -285,7 +285,7 @@ connect and stream from you? - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -2138,68 +2138,68 @@ Password SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -3867,156 +3867,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4052,27 +4052,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 82ea682a32..9b614cdf39 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -285,7 +285,7 @@ connect and stream from you? - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Kapcsolódó előadók - + Albums Albumok - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -2138,68 +2138,68 @@ Password SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist lejátszólista - + automatic playlist automatikus lejátszólista - + station rádióállomás - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete Törlés - + Save XSPF XSPF mentése - + Playlists (*.xspf) Lejátszólisták (*.xspf) @@ -3867,156 +3867,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF hiba - + This is not a valid XSPF playlist. Nem érvényes XSPF lejátszólista. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Rádióállomás - + Create New Station - + Name: - + Playlist Lejátszólista - + Automatic Playlist Automatikus lejátszólista - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk Tomahawkról @@ -4052,27 +4052,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 08d1e215ac..67d569f3db 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -285,7 +285,7 @@ connect and stream from you? - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -2138,68 +2138,68 @@ Password SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -3867,156 +3867,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4052,27 +4052,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 7f02f064d5..ff60340ec1 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -285,7 +285,7 @@ connect and stream from you? Ci dispiace, ma non abbiamo trovato nessuna traccia di questo album! - + Other Albums by %1 Altri album di %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artisti simili - + Albums Album - + Sorry, we could not find any albums for this artist! Ci dispiace, ma non abbiamo trovato nessun album di questo artista! - + Sorry, we could not find any related artists! Siamo spiacenti, non è stato possibile trovare artisti simili! - + Sorry, we could not find any top hits for this artist! Ci dispiace, ma non abbiamo trovato alcun risultato top per l'artista! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Spiacente, Tomahawk non ha trovato la traccia '%1' di %2 - + Sorry, Tomahawk couldn't find the artist '%1' Spiacente, Tomahawk non ha trovato l'artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Spiacente, Tomahawk non ha trovato l'album '%1' di '%2' @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Questa playlist al momento è vuota. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Questa playlist al momento è vuota. Aggiungi qualche traccia e goditi la musica @@ -1056,32 +1056,32 @@ temporanea LovedTracksItem - + Top Loved Tracks Top tracce preferite - + Sorry, we could not find any loved tracks! Spiacente, non siamo riusciti a trovare nessuna traccia amata - + The most loved tracks from all your friends Le migliori tracce preferite dai tuoi amici - + All of your loved tracks Tutte le tue tracce preferite - + All of %1's loved tracks Tutte le tracce preferite di %1 - + Loved Tracks Tracce preferite @@ -2139,68 +2139,68 @@ temporanea SourceTreeView - + &Copy Link &Copia link - + &Delete %1 &Cancella %1 - + Add to my Playlists Aggiungi alle mie playlist - + Add to my Automatic Playlists Aggiungi alle mie playlist automatiche - + Add to my Stations Aggiungi alle mie stazioni - + &Export Playlist &Esporta playlist - + playlist playlist - + automatic playlist playlist automatica - + station stazione - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Vuoi cancellare %1 <b>"%2"</b>? - + Delete Cancella - + Save XSPF Salva XSPF - + Playlists (*.xspf) Playlist (*.xspf) @@ -3869,156 +3869,156 @@ enter the displayed PIN number here: Tomahawk - + Back Indietro - + Go back one page Vai indietro di una pagina - + Forward Avanti - + Go forward one page Vai avanti di una pagina - - + + Hide Menu Bar Nascondi barra menu - - + + Show Menu Bar Mostra barra menu - + Search for any artist, album or song... Cerca qualunque artista, album o canzone... - + &Main Menu &Menu principale - + Exit Full Screen Esci da schermo intero - + Enter Full Screen Modalità schermo intero - + XSPF Error Errore XSPF - + This is not a valid XSPF playlist. Questa non è una valida playlist XSPF. - + Failed to save tracks Errore nel salvare le tracce - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Alcune tracce nella playlist non contengono l'artista e il titolo. Verrano ignorate. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. Assicurati di avere le giuste librerie Phonon e i plugin necessari installati. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. - + Station Stazione - + Create New Station Crea una nuova stazione - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist automatica - + Pause Pausa - + &Play Ri&produci - + %1 by %2 track, artist name %1 di %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Grazie a: - + About Tomahawk Info su Tomahawk @@ -4054,27 +4054,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Hai ascoltato questa traccia una volta.Hai ascoltato questa traccia %n volte. - + You've never listened to this track before. Non hai mai ascoltato questa traccia. - + You first listened to it on %1. L'hai ascoltata la prima volta su %1. - + You've listened to %1 %n time(s). Hai ascoltato %1 una volta.Hai ascoltato %1 %n volte. - + You've never listened to %1 before. Non hai mai ascoltato %1. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index df2a85999a..e95722a65b 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -285,7 +285,7 @@ connect and stream from you? このアルバムの曲は見つかりませんでした。 - + Other Albums by %1 %1の他のアルバム @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 大ヒット曲 - + Related Artists 似たアーティスト - + Albums アルバム - + Sorry, we could not find any albums for this artist! このアーティストのアルバムは見つかりませんでした。 - + Sorry, we could not find any related artists! 関連アーティストは見つかりませんでした。 - + Sorry, we could not find any top hits for this artist! このアーティストの大ヒット曲は見つかりませんでした。 - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawkは%2の%1を見つかりませんでした。 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawkは'%1'と言うアーティストを見つかりませんでした。 - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawkは%2の%1を見つかりませんでした。 @@ -615,12 +615,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -788,12 +788,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. このプレイリストには何も入っていません。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! プレイリストには何も入っていません。トラックを追加して、音楽を楽しみましょう! @@ -1056,32 +1056,32 @@ Password LovedTracksItem - + Top Loved Tracks 最もLove トラック - + Sorry, we could not find any loved tracks! Love トラックが見つかりませんでした。 - + The most loved tracks from all your friends 友達の最もLove トラック - + All of your loved tracks 自分のLove トラック - + All of %1's loved tracks %1さんのLove トラック - + Loved Tracks Love トラック @@ -2143,68 +2143,68 @@ Password SourceTreeView - + &Copy Link リンクをコピー - + &Delete %1 %1を削除 - + Add to my Playlists プレイリストに追加する - + Add to my Automatic Playlists 自動プレイリストに追加する - + Add to my Stations ステーションに追加する - + &Export Playlist プレイリストを書き出し - + playlist プレイリスト - + automatic playlist 自動プレイリスト - + station ステーション - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? <b>"%2"</b>と言う%1を削除しますか? - + Delete 削除 - + Save XSPF XSPFを保存する - + Playlists (*.xspf) プレイリスト (*.xspf) @@ -3880,156 +3880,156 @@ enter the displayed PIN number here: Tomahawk - + Back プレイリスト - + Go back one page 前のページ - + Forward 次へ - + Go forward one page 次のページ - - + + Hide Menu Bar メニューバーを隠す - - + + Show Menu Bar メニューバーを表示 - + Search for any artist, album or song... アーティスト、又はアルバムや曲で検索して下さい - + &Main Menu メインメニュー - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPFエラー - + This is not a valid XSPF playlist. このプレイリストは有利なXSPFプレイリストではありません。 - + Failed to save tracks トラックの保存に失敗しました。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. プレイリストにアーティストもタイトルの無いトラックが見つかりました。この項目は無視されます。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。適しているPhononのバックエンドを確認の上、必須プラグインのインストールを確認して下さい。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。 - + Station ステーション - + Create New Station 新規ステーションを作成 - + Name: 名前: - + Playlist プレイリスト - + Automatic Playlist 自動プレイリスト - + Pause 一時停止 - + &Play 再生 - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk Tomahawkについて @@ -4065,27 +4065,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). このトラックは%n回聴いています。 - + You've never listened to this track before. このトラックを一度も聴いていません。 - + You first listened to it on %1. 初めてこの曲を聴いたのは、%1です。 - + You've listened to %1 %n time(s). %1を%n回聴いています。 - + You've never listened to %1 before. %1を一度も聴いていません。 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 87f373b08f..8363879a5f 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -285,7 +285,7 @@ connect and stream from you? Atsiprašome, neradome jokių takelių iš šio albumo! - + Other Albums by %1 Kiti %1 albumai @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists Susiję atlikėjai - + Albums Albumai - + Sorry, we could not find any albums for this artist! Atsiprašome, neradome jokių šio atlikėjo albumų! - + Sorry, we could not find any related artists! Atsiprašome, neradome jokių susijusių atlikėjų! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Atsiprašome, Tomahawk nepavyko rasti takelio '%1', atliekamo %2 - + Sorry, Tomahawk couldn't find the artist '%1' Atsiprašome, Tomahawk nepavyko rasti atlikėjo '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Atsiprašome, Tomahawk nepavyko rasti %2 atliekamo albumo '%1' @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks Populiariausios mylimos dainos - + Sorry, we could not find any loved tracks! Deja, neradome jokių mylimų dainų! - + The most loved tracks from all your friends Visų Jūsų draugų labiausiai mylimos dainos - + All of your loved tracks Visos Jūsų mylimos dainos - + All of %1's loved tracks Visos %1 mylimos dainos - + Loved Tracks Mylimos dainos @@ -2138,68 +2138,68 @@ Password SourceTreeView - + &Copy Link &Kopijuoti nuorodą - + &Delete %1 Paša&linti %1 - + Add to my Playlists Pridėti prie mano grojaraščių - + Add to my Automatic Playlists Pridėti prie mano automatinių grojaraščių - + Add to my Stations Pridėti prie mano stočių - + &Export Playlist - + playlist grojaraštis - + automatic playlist automatinis grojaraštis - + station stotis - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Ar norite pašalinti %1 <b>"%2"</b>? - + Delete Pašalinti - + Save XSPF Išsaugoti XSPF - + Playlists (*.xspf) Grojaraščiai (*.xspf) @@ -3867,156 +3867,156 @@ enter the displayed PIN number here: Tomahawk - + Back Atgal - + Go back one page Grįžti vienu puslapiu atgal - + Forward Pirmyn - + Go forward one page Eiti vienu puslapiu pirmyn - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF klaida - + This is not a valid XSPF playlist. - + Failed to save tracks Nepavyko išsaugoti takelių - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Stotis - + Create New Station Sukurti naują stotį - + Name: Pavadinimas: - + Playlist Grojaraštis - + Automatic Playlist Automatinis grojaraštis - + Pause Pristabdyti - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorinės teisės 2010 - 2013 - + Thanks to: Dėkojame: - + About Tomahawk Apie Tomahawk @@ -4052,27 +4052,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Jūs klausėtės šio takelio %n kartą.Jūs klausėtės šio takelio %n kartus.Jūs klausėtės šio takelio %n kartų. - + You've never listened to this track before. Jūs niekad anksčiau nesiklausėte šio takelio. - + You first listened to it on %1. Pirmąkart klausėtės jo %1. - + You've listened to %1 %n time(s). Jūs klausėtės %1 %n kartą.Jūs klausėtės %1 %n kartus.Jūs klausėtės %1 %n kartų. - + You've never listened to %1 before. Jūs niekada anksčiau nesiklausėte %1. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index c1c3361811..df3588d85b 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -286,7 +286,7 @@ połączyć się i strumieniować od ciebie? - + Other Albums by %1 Inne albumy %1 @@ -308,37 +308,37 @@ połączyć się i strumieniować od ciebie? ArtistInfoWidget - + Top Hits Hity na Topie - + Related Artists Powiązani artyści - + Albums Albumy - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -384,17 +384,17 @@ połączyć się i strumieniować od ciebie? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Przepraszamy, Tomahawk nie mógł znaleźć utworu '%1' wykonawcy %2 - + Sorry, Tomahawk couldn't find the artist '%1' Przepraszamy, Tomahawk nie mógł znaleźć wykonawcy '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Przepraszamy, Tomahawk nie mógł znaleźć albumu '%1' wykonawcy %2 @@ -615,12 +615,12 @@ połączyć się i strumieniować od ciebie? - + Recently played tracks - + No recently created playlists in your network. @@ -788,12 +788,12 @@ połączyć się i strumieniować od ciebie? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ połączyć się i strumieniować od ciebie? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1056,32 +1056,32 @@ Password LovedTracksItem - + Top Loved Tracks Najczęściej lubiane utwory - + Sorry, we could not find any loved tracks! Przepraszamy, nie mogliśmy znaleźć żadnych lubianych utworów! - + The most loved tracks from all your friends Najczęściej lubiane utwory wszystkich twoich znajomych - + All of your loved tracks Wszystkie twoje lubiane utwory - + All of %1's loved tracks Wszystkie utwory lubiane przez %1 - + Loved Tracks Lubiane @@ -2140,68 +2140,68 @@ Password SourceTreeView - + &Copy Link &Kopiuj Link - + &Delete %1 &Usuń %1 - + Add to my Playlists Dodaj do moich List odtwarzania - + Add to my Automatic Playlists Dodaj do moich Automatycznych list odtwarzania - + Add to my Stations Dodaj do moich Stacji - + &Export Playlist &Eksportuj Listę - + playlist lista odtwarzania - + automatic playlist automatyczna lista odtwarzania - + station stacja - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF Zapisz XSPF - + Playlists (*.xspf) Listy (*.xspf) @@ -3877,156 +3877,156 @@ wprowadź pokazany numer PIN tutaj: Tomahawk - + Back Wstecz - + Go back one page Cofnij o jedną stronę - + Forward Naprzód - + Go forward one page Przejdź naprzód o jedną stronę - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error Błąd XSPF - + This is not a valid XSPF playlist. To nie jest poprawna lista XSPF. - + Failed to save tracks Nie udało się zapisać utworów - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Niektóre utwory na liście nie zawierają artysty i tytułu. Zostaną one zignorowane. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Przepraszamy, wystąpił problem z połączeniem z twoim urządzeniem audio lub z żądanym utworem, zostanie on pominięty. - + Station - + Create New Station Utwórz Nową Stację - + Name: Nazwa: - + Playlist - + Automatic Playlist - + Pause Pauza - + &Play - + %1 by %2 track, artist name %1 wykonawcy %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Podziękowania dla: - + About Tomahawk O Tomahawku @@ -4062,27 +4062,27 @@ wprowadź pokazany numer PIN tutaj: - + You've listened to this track %n time(s). Słuchałeś tego utworu %n raz.Słuchałeś tego utworu %n razy.Słuchałeś tego utworu %n razy. - + You've never listened to this track before. Nie słuchałeś wcześniej tego utworu. - + You first listened to it on %1. Pierwszy raz słuchałeś tego utworu %1. - + You've listened to %1 %n time(s). Słuchałeś %1 %n raz.Słuchałeś %1 %n razy.Słuchałeś %1 %n razy. - + You've never listened to %1 before. Nie słuchałeś wcześniej %1. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 9cd3169f4d..94b46cba17 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -286,7 +286,7 @@ se conecte e faça o stream de você? Desculpe, mas não conseguimos encontrar outras faixas para este álbum! - + Other Albums by %1 Outros álbuns de %1 @@ -308,37 +308,37 @@ se conecte e faça o stream de você? ArtistInfoWidget - + Top Hits Mais Tocadas - + Related Artists Artistas Relacionados - + Albums Álbuns - + Sorry, we could not find any albums for this artist! Desculpe, mas não conseguimos encontrar outros álbuns para este artista! - + Sorry, we could not find any related artists! Desculpe, mas não conseguimos encontrar outros artistas relacionados a este! - + Sorry, we could not find any top hits for this artist! Desculpe, mas não conseguimos encontrar outras faixas mais tocadas deste artista! - + # IN YOUR CHARTS @@ -384,17 +384,17 @@ se conecte e faça o stream de você? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Desculpe, o Tomahawk não encontrou a faixa '%1' de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Desculpe, o Tomahawk não encontrou o artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Desculpe, o Tomahawk não encontrou o álbum '%1' de %2 @@ -615,12 +615,12 @@ se conecte e faça o stream de você? - + Recently played tracks - + No recently created playlists in your network. @@ -788,12 +788,12 @@ se conecte e faça o stream de você? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ se conecte e faça o stream de você? FlexibleView - + This playlist is currently empty. Esta lista de reprodução está vazia no momento. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodução está vazia no momento. Adicione algumas faixas a ela e aproveite a música! @@ -1056,32 +1056,32 @@ Password LovedTracksItem - + Top Loved Tracks Faixas mais favoritas - + Sorry, we could not find any loved tracks! Desculpe, não encontramos nenhuma faixa favorita! - + The most loved tracks from all your friends As faixas mais favoritas de todos os seus amigos - + All of your loved tracks Todas as suas faixas favoritas - + All of %1's loved tracks Todas as faixas favoritas do %1 - + Loved Tracks Faixas favoritas @@ -2140,68 +2140,68 @@ Password SourceTreeView - + &Copy Link &Copiar link - + &Delete %1 &Excluir %1 - + Add to my Playlists Adicionar às minhas Playlists - + Add to my Automatic Playlists Adicionar às minhas Playlists Automáticas - + Add to my Stations Adicionar às minhas Estações - + &Export Playlist &Exportar Playlist - + playlist playlist - + automatic playlist playlist automática - + station estação - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Gostaria de deletar a %1 <b>"%2"</b>? - + Delete Excluir - + Save XSPF Salvar XSPF - + Playlists (*.xspf) Playlists (*.xspf) @@ -3877,156 +3877,156 @@ colocar o número PIN mostrado aqui: Tomahawk - + Back Voltar - + Go back one page Voltar uma página - + Forward Avançar - + Go forward one page Avançar uma página - - + + Hide Menu Bar Esconder barra de menu - - + + Show Menu Bar Mostrar barra de menu - + Search for any artist, album or song... Pesquisar por qualquer artista, álbum ou música... - + &Main Menu &Menu principal - + Exit Full Screen - + Enter Full Screen - + XSPF Error Erro de XSPF - + This is not a valid XSPF playlist. Esta não é uma lista de reprodução XSPF válida. - + Failed to save tracks Falha ao salvar faixas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algumas faixas da lista de reprodução não contem artista e título. Estas serão ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. Certifique-se de ter um backend do Phonon adequado e os plugins necessários instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. - + Station Estação - + Create New Station Criar uma nova estação - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist Automática - + Pause PIN do Twitter - + &Play Re&produzir - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecimentos: - + About Tomahawk Sobre o Tomahawk @@ -4062,27 +4062,27 @@ colocar o número PIN mostrado aqui: - + You've listened to this track %n time(s). Você ouviu esta faixa %n vez.Você ouviu esta faixa %n vezes. - + You've never listened to this track before. Você nunca ouviu esta faixa antes. - + You first listened to it on %1. Você ouviu pela primeira vez em %1. - + You've listened to %1 %n time(s). Você ouviu %1 %n vez.Você ouviu %1 %n vezes. - + You've never listened to %1 before. Você nunca ouviu %1 antes. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 7460e2dda9..dd502321b8 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -289,7 +289,7 @@ connect and stream from you? К сожалению, мы не смогли найти никаких треков для этого альбома! - + Other Albums by %1 Другие альбомы %1 @@ -311,37 +311,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Хиты - + Related Artists Похожие исполнители - + Albums Альбомы - + Sorry, we could not find any albums for this artist! К сожалению, мы не смогли найти никаких альбомов этого исполнителя! - + Sorry, we could not find any related artists! К сожалению, мы не смогли найти никаких исполнители! - + Sorry, we could not find any top hits for this artist! К сожалению, мы не смогли найти никаких хитов этого исполнителя! - + # IN YOUR CHARTS @@ -387,17 +387,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 К сожалению, Tomahawk не смог найти песню '%1' %2 - + Sorry, Tomahawk couldn't find the artist '%1' К сожалению, Tomahawk не смог найти исполнителя '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 К сожалению, Tomahawk не смог найти альбом '%1' %2 @@ -618,12 +618,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -791,12 +791,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -804,12 +804,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Плейлист пуст - + This playlist is currently empty. Add some tracks to it and enjoy the music! Этот плейлист пуст. Добавьте какие-нибудь песни и наслаждайтесь музыкой! @@ -1059,32 +1059,32 @@ Password LovedTracksItem - + Top Loved Tracks Топ Любимых Песен - + Sorry, we could not find any loved tracks! К сожалению, мы не смогли найти никаких любимых песен! - + The most loved tracks from all your friends Любимые Песни Вас и Ваших Друзей - + All of your loved tracks Ваши любимые песни - + All of %1's loved tracks Любимые Песни %1 - + Loved Tracks Любимые Песни @@ -2146,68 +2146,68 @@ Password SourceTreeView - + &Copy Link &Скопировать Cсылку - + &Delete %1 &Удалить %1 - + Add to my Playlists Добавить к Моим Плейлистам - + Add to my Automatic Playlists Добавить к Моим Автоматическим Плейлистам - + Add to my Stations Добавить к Моим Станциям - + &Export Playlist &Экспорт Плейлиста - + playlist плейлист - + automatic playlist автоматический плейлист - + station станция - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Вы хотите удалить %1 <b>"%2"</b>? - + Delete Удалить - + Save XSPF Сохранить XSPF - + Playlists (*.xspf) Плейлисты (*.xspf) @@ -3883,156 +3883,156 @@ enter the displayed PIN number here: Tomahawk - + Back Назад - + Go back one page Перейти на предыдущую страницу - + Forward Вперед - + Go forward one page Перейдите на следующую страницу - - + + Hide Menu Bar Спрятать Строку Меню - - + + Show Menu Bar Показать Строку Меню - + Search for any artist, album or song... Поиск любого исполнителя, альбома или песни ... - + &Main Menu &Главное меню - + Exit Full Screen Выход из полноэкранного режима - + Enter Full Screen Переход в полноэкранный режим - + XSPF Error Ошибка XSPF - + This is not a valid XSPF playlist. Это не является допустимым XSPF плейлистом. - + Failed to save tracks Не удалось сохранить песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Некоторые песни в плейлисте не содержат исполнителя и название. Они будут проигнорированы. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. Убедитесь, что у вас есть подходящий Phonon backend и необходимые плагины установлены. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. - + Station Станция - + Create New Station Создать Новую Станцию - + Name: Имя: - + Playlist Плейлист - + Automatic Playlist Автоматический Плейлист - + Pause Пауза - + &Play &Играть - + %1 by %2 track, artist name %1 %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Авторское право 2010 - 2013 - + Thanks to: Благодарность - + About Tomahawk О Tomahawk @@ -4068,27 +4068,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). Вы слушали эту песню %n раз.Вы слушали эту песню %n раз.Вы слушали эту песню %n раз. - + You've never listened to this track before. Вы никогда не слушали эту песню раньше. - + You first listened to it on %1. Первый раз слушали эту песню %1. - + You've listened to %1 %n time(s). Вы слушали %1 %n раз.Вы слушали %1 %n раза.Вы слушали %1 %n раз. - + You've never listened to %1 before. Вы никогда не слушали %1 до этого. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 8f54aed69b..306901b2b5 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -286,7 +286,7 @@ ansluta och strömma från dig? Tyvärr! Det gick inte hitta några spår från detta albumet! - + Other Albums by %1 Andra album av %1 @@ -308,37 +308,37 @@ ansluta och strömma från dig? ArtistInfoWidget - + Top Hits Största hits - + Related Artists Relaterade artister - + Albums Album - + Sorry, we could not find any albums for this artist! Tyvärr! Det gick inte hitta några album av denna artisten! - + Sorry, we could not find any related artists! Tyvärr! Det gick inte hitta några relaterade artister! - + Sorry, we could not find any top hits for this artist! Tyvärr! Det gick inte hitta några tophits av denna artisten - + # IN YOUR CHARTS # I DINA LISTOR @@ -384,17 +384,17 @@ ansluta och strömma från dig? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tyvärr! Tomahawk kunde inte hitta spåret '%1' av %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tyvärr! Tomahawk kunde inte hitta artisten '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tyvärr! Tomahawk kunde inte hitta albumet '%1' av %2 @@ -615,12 +615,12 @@ ansluta och strömma från dig? - + Recently played tracks Nyligen uppspelade spår - + No recently created playlists in your network. Det finns inga nyligen skapade spellistor på ditt nätverk @@ -788,12 +788,12 @@ ansluta och strömma från dig? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -801,12 +801,12 @@ ansluta och strömma från dig? FlexibleView - + This playlist is currently empty. Spellistan är för närvarande tom. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Spellistan är för tillfället tom. Lägg till några låtar och avnjut musiken! @@ -1057,32 +1057,32 @@ Password LovedTracksItem - + Top Loved Tracks Mest älskade spår - + Sorry, we could not find any loved tracks! Tyvärr! Vi kunde inte hitta några älskade spår! - + The most loved tracks from all your friends Dina vänners mest älskade spår - + All of your loved tracks Alla dina älskade spår - + All of %1's loved tracks Alla %1's älskade spår - + Loved Tracks Älskade spår @@ -2143,68 +2143,68 @@ och radiostationer baserat på din personliga profil SourceTreeView - + &Copy Link &Kopiera länk - + &Delete %1 &Ta bort %1 - + Add to my Playlists Lägg till i mina spellistor - + Add to my Automatic Playlists Lägg till i mina automatiska spellistor - + Add to my Stations Lägg till i mina stationer - + &Export Playlist &Exportera spellista - + playlist spellista - + automatic playlist automatisk spellista - + station station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? Vill du ta bort %1 <b>"%2"</b>? - + Delete Ta bort - + Save XSPF Spara XSPF - + Playlists (*.xspf) Spellistor (*.xspf) @@ -3883,156 +3883,156 @@ anger du PIN-koden här: Tomahawk - + Back Tillbaka - + Go back one page Gå tillbaks en sida - + Forward Framåt - + Go forward one page Gå framåt en sida - - + + Hide Menu Bar Göm Menyrad - - + + Show Menu Bar Visa Menyrad - + Search for any artist, album or song... Sök efter valfri artist, album eller låt... - + &Main Menu &Huvudmeny - + Exit Full Screen Gå ur fullskärmsläge - + Enter Full Screen Fullskärmsläge - + XSPF Error XSPF-fel - + This is not a valid XSPF playlist. Detta är inte en giltig XSPF-spellista. - + Failed to save tracks Misslyckades med att spara spår - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Några spår i spellistan innehåller inte någon artist och titel. De kommer att ignoreras. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Tyvärr! Det uppstod ett problem med kontakten till ditt ljudkort eller det önskade spåret. Nuvarande spår kommer att hoppas över. Kontrollera att du har en lämplig Phonon-backend och alla nödvändiga plugins installerade - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Tyvärr blev det problem att hitta din ljudenhet eller den valda låten! Nuvarande låt kommer att hoppas över - + Station Station - + Create New Station Skapa ny station - + Name: Namn: - + Playlist Spellista - + Automatic Playlist Automatisk spellista - + Pause Paus - + &Play &Spela - + %1 by %2 track, artist name %1 av %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Tack till: - + About Tomahawk Om Tomahawk @@ -4068,27 +4068,27 @@ anger du PIN-koden här: # I DINA LISTOR - + You've listened to this track %n time(s). Du har lyssnat på detta spåret %n gånger.Du har lyssnat på detta spåret %n gånger - + You've never listened to this track before. Du har inte lyssnat på detta spåret tidigare. - + You first listened to it on %1. Du har lyssnat på det på %1. - + You've listened to %1 %n time(s). Du har lyssnat på %1 %n gång.Du har lyssnat på %1 %n gånger. - + You've never listened to %1 before. Du har inte lyssnat på %1 tidigare. diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 844bdaef80..988084b3b5 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -285,7 +285,7 @@ connect and stream from you? - + Other Albums by %1 Diğer %1 Albümleri @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits En Çok Dinlenenler - + Related Artists Benzer Sanatçılar - + Albums Albümler - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -2138,68 +2138,68 @@ Password SourceTreeView - + &Copy Link - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF - + Playlists (*.xspf) @@ -3867,156 +3867,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4052,27 +4052,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 636b6a87a1..fc4a0e19b4 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -285,7 +285,7 @@ connect and stream from you? 抱歉,没有找到这张专辑的其他歌曲! - + Other Albums by %1 %1 的其他专辑 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 最热歌曲 - + Related Artists 相关艺人 - + Albums 专辑 - + Sorry, we could not find any albums for this artist! 抱歉,未找到该艺术家的其他专辑! - + Sorry, we could not find any related artists! 抱歉,没有找到相关的艺术家! - + Sorry, we could not find any top hits for this artist! 抱歉,没有找到该艺术家的任何人气歌曲! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 抱歉,Tomahawk 未找到 %2 的歌曲 '%1' - + Sorry, Tomahawk couldn't find the artist '%1' 抱歉,Tomahawk 无法找到艺术家 '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 抱歉,Tomahawk 无法找到 %2 的专辑 '%1' @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. 当前播放列表为空。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks 最多喜爱的歌曲 - + Sorry, we could not find any loved tracks! 抱歉!未找到任何喜爱歌曲。 - + The most loved tracks from all your friends 从所有朋友中获取的最多喜爱歌曲 - + All of your loved tracks 我所有的喜爱歌曲 - + All of %1's loved tracks %1 所有被喜欢的歌曲 - + Loved Tracks 喜爱歌曲 @@ -2141,68 +2141,68 @@ Password SourceTreeView - + &Copy Link 复制链接 - + &Delete %1 删除%1 - + Add to my Playlists 添加到我的播放列表 - + Add to my Automatic Playlists 添加到我的自动播放列表 - + Add to my Stations 添加到我的电台 - + &Export Playlist 导出播放列表 - + playlist 播放列表 - + automatic playlist 自动播放列表 - + station 电台 - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? 要删除 %1 <b>"%2"</b> 吗? - + Delete 删除 - + Save XSPF 保存 XSPF - + Playlists (*.xspf) 播放列表 (*.xspf) @@ -3878,156 +3878,156 @@ enter the displayed PIN number here: Tomahawk - + Back 后退 - + Go back one page 转向上一页 - + Forward 下一个 - + Go forward one page 转向下一页 - - + + Hide Menu Bar 隐藏菜单栏 - - + + Show Menu Bar 显示菜单栏 - + Search for any artist, album or song... 搜索任意艺人,专辑,或歌曲... - + &Main Menu 主菜单 - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 错误 - + This is not a valid XSPF playlist. 这不是一个合法的 XSPF 播放列表。 - + Failed to save tracks 保存歌曲失败。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. 播放列表中的一些歌曲缺失艺术家和标题,它们将被忽略。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. 抱歉,访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。请确认你正在使用合适的 Phonon 后端并安装了必要的插件。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. 抱歉,在访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。 - + Station 电台 - + Create New Station 创建新电台 - + Name: 名字: - + Playlist 播放列表 - + Automatic Playlist 自动播放列表 - + Pause 暂停 - + &Play 播放 - + %1 by %2 track, artist name %2 的 %1 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 版权所有 2010 - 2013 - + Thanks to: 感谢: - + About Tomahawk 关于 Tomahawk @@ -4063,27 +4063,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). 你已经收听过此歌曲 %n 遍。 - + You've never listened to this track before. 你之前从未听过此歌曲。 - + You first listened to it on %1. 你第一次听的是 %1. - + You've listened to %1 %n time(s). 你已经听过 %1 有 %n 遍了。 - + You've never listened to %1 before. 你之前从未听过 %1。 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 9f38551b0a..e789c98c59 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -285,7 +285,7 @@ connect and stream from you? - + Other Albums by %1 列出所有其他專輯,依 %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 流行精選 - + Related Artists 相關演出者 - + Albums 專輯 - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + # IN YOUR CHARTS @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -614,12 +614,12 @@ connect and stream from you? - + Recently played tracks - + No recently created playlists in your network. @@ -787,12 +787,12 @@ connect and stream from you? FlexibleTreeView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -800,12 +800,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -1055,32 +1055,32 @@ Password LovedTracksItem - + Top Loved Tracks - + Sorry, we could not find any loved tracks! - + The most loved tracks from all your friends - + All of your loved tracks - + All of %1's loved tracks - + Loved Tracks @@ -2138,68 +2138,68 @@ Password SourceTreeView - + &Copy Link 複製鏈接 - + &Delete %1 - + Add to my Playlists - + Add to my Automatic Playlists - + Add to my Stations - + &Export Playlist 匯出播放清單 - + playlist - + automatic playlist - + station - + Would you like to delete the %1 <b>"%2"</b>? e.g. Would you like to delete the playlist named Foobar? - + Delete - + Save XSPF 儲存 XSPF - + Playlists (*.xspf) 播放清單(*.xspf) @@ -3867,156 +3867,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 錯誤 - + This is not a valid XSPF playlist. - + Failed to save tracks 無法儲存曲目 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: 名稱: - + Playlist - + Automatic Playlist - + Pause 暫停 - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4052,27 +4052,27 @@ enter the displayed PIN number here: - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. From 0492bbded4692875c0b7ae7083d53dc20c6622b5 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 02:19:47 +0200 Subject: [PATCH 372/565] * A few more tweaks to artist page. --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 12 +++++------- .../widgets/infowidgets/ArtistInfoWidget.ui | 10 ++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 973a30e5b6..cfc71b8453 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -121,7 +121,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->cover->setShowText( false ); { - QFont f = font(); + QFont f = ui->biography->font(); f.setPointSize( f.pointSize() + 1 ); f.setFamily( "Titillium Web" ); @@ -143,10 +143,10 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* } { - QFont f = ui->biography->font(); - f.setPointSize( f.pointSize() + 2 ); + QFont f = ui->artistLabel->font(); + f.setFamily( "Titillium Web" ); - QPalette p = ui->biography->palette(); + QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); ui->artistLabel->setFont( f ); @@ -157,9 +157,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QFont f = ui->label->font(); f.setBold( false ); f.setFamily( "Fauna One" ); - f.setPointSize( f.pointSize() + 2 ); - QPalette p = ui->biography->palette(); + QPalette p = ui->label->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); ui->label->setFont( f ); @@ -172,7 +171,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QFont f = ui->albumLabel->font(); f.setBold( false ); f.setFamily( "Fauna One" ); - f.setPointSize( f.pointSize() + 2 ); QPalette p = ui->biography->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 96c59331d1..7a800570f1 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -77,7 +77,7 @@ - 18 + 20 75 true @@ -218,7 +218,7 @@ - 18 + 20 75 true @@ -275,8 +275,7 @@ - Arial - 18 + 20 75 true @@ -385,8 +384,7 @@ - Arial - 18 + 20 75 true From 2066214aef2b8573a13babc2b675e7148e127c6e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 10:35:22 +0200 Subject: [PATCH 373/565] Do not rely on the object to be removed to be deleted after the callback --- src/libtomahawk/sip/WeakPeerHash.cpp | 30 ++++++++-------------------- src/libtomahawk/sip/WeakPeerHash.h | 2 +- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/libtomahawk/sip/WeakPeerHash.cpp b/src/libtomahawk/sip/WeakPeerHash.cpp index 81b698fd8e..52a04e8a76 100644 --- a/src/libtomahawk/sip/WeakPeerHash.cpp +++ b/src/libtomahawk/sip/WeakPeerHash.cpp @@ -19,11 +19,12 @@ #include "WeakPeerHash_p.h" #include "PeerInfo.h" +#include "utils/Closure.h" #define WEAKPEERHASH_KEY "WeakPeerHashKey" -WeakPeerHash::WeakPeerHash(QObject *parent) - : QObject(parent) +WeakPeerHash::WeakPeerHash( QObject *parent ) + : QObject( parent ) , d_ptr( new WeakPeerHashPrivate( this ) ) { } @@ -36,10 +37,10 @@ WeakPeerHash::WeakPeerHash( const WeakPeerHash &hash ) } void -WeakPeerHash::insert(const QString &key, const Tomahawk::peerinfo_ptr &value) +WeakPeerHash::insert( const QString &key, const Tomahawk::peerinfo_ptr &value ) { - value->setProperty( WEAKPEERHASH_KEY, key ); - connect( value.data(), SIGNAL( destroyed( QObject* ) ), SLOT( remove( QObject* ) ) ); + _detail::Closure* cl = NewClosure( value, SIGNAL( destroyed( QObject* ) ), this, SLOT( remove( QString ) ), key ); + cl->setAutoDelete( true ); d_func()->hash.insert( key, value.toWeakRef() ); } @@ -50,22 +51,7 @@ WeakPeerHash::hash() } void -WeakPeerHash::remove( QObject *value ) +WeakPeerHash::remove( const QString& key ) { - if ( value ) - { - const QString key = value->property( WEAKPEERHASH_KEY ).toString(); - d_func()->hash.remove( key ); - } - else - { - // Scan for null-Pointers - foreach ( QString key, d_func()->hash.keys() ) - { - if ( d_func()->hash.value( key ).isNull() ) - { - d_func()->hash.remove( key ); - } - } - } + d_func()->hash.remove( key ); } diff --git a/src/libtomahawk/sip/WeakPeerHash.h b/src/libtomahawk/sip/WeakPeerHash.h index f9f724f237..b165d8eb39 100644 --- a/src/libtomahawk/sip/WeakPeerHash.h +++ b/src/libtomahawk/sip/WeakPeerHash.h @@ -37,7 +37,7 @@ class WeakPeerHash : public QObject signals: private slots: - void remove( QObject* value ); + void remove( const QString& key ); private: Q_DECLARE_PRIVATE( WeakPeerHash ) WeakPeerHashPrivate* d_ptr; From 1b9b706fce3672ca3499113a5040bec30560932d Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 11:45:17 +0200 Subject: [PATCH 374/565] Keep a local strong reference to prevent deleting of the current object --- src/libtomahawk/network/ConnectionManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 0180dfee05..3ff264514c 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -342,8 +342,9 @@ ConnectionManager::activate() void ConnectionManager::deactivate() { - setActive( false, d_func()->nodeid, weakRef().toStrongRef() ); - d_func()->mutex.unlock(); + QSharedPointer strongRef = weakRef().toStrongRef(); + setActive( false, d_func()->nodeid, strongRef ); + strongRef->d_func()->mutex.unlock(); } From 2781fafdf4a440c1310f9d1d4f969e5b1066261f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 11:50:09 +0200 Subject: [PATCH 375/565] * Added new color roles to TomahawkStyle. --- src/libtomahawk/utils/TomahawkStyle.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index ef6e02468d..7a35229cc9 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -61,6 +61,11 @@ namespace TomahawkStyle static const QColor SELECTION_BACKGROUND = QColor( "#962c26" ); static const QColor SELECTION_FOREGROUND = QColor( "#ffffff" ); + static const QColor HEADER_GAUGE_HIGHLIGHT = QColor( "#8dbf2d" ); + static const QColor HEADER_GAUGE_BACKGROUND = QColor( "#252020" ); + static const QColor HEADER_GAUGE_TEXT = QColor( "#ffffff" ); + static const QColor HEADER_GAUGE_LABEL_BACKGROUND = QColor( "#3e3e3e" ); + static const QColor HEADER_BACKGROUND = QColor( "#292f34" ); static const QColor HEADER_TEXT = QColor( "#ffffff" ); static const QColor HEADER_LINK = QColor( "#8dbf2d" ); @@ -75,6 +80,10 @@ namespace TomahawkStyle static const QColor PAGE_FOREGROUND = QColor( "#292f34" ); static const QColor PAGE_BACKGROUND = QColor( "#DBDBDB" ); + static const QColor PAGE_TRACKLIST_TRACK_SOLVED = QColor( "#292F34" ); + static const QColor PAGE_TRACKLIST_TRACK_UNRESOLVED = QColor( "#8597A6" ); + static const QColor PAGE_TRACKLIST_NUMBER = QColor( "#8DBF2D" ).darker( 400 ); + static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); From 99eae3c54274a3c7dd70d4c164ab4ca9e6ad7ff3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 11:50:34 +0200 Subject: [PATCH 376/565] * Fixed TomahawkUtils::prepareStyleOption(). --- src/libtomahawk/utils/TomahawkUtilsGui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index 708479940e..a08a4deb7c 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -748,10 +748,11 @@ prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, Pl option->backgroundBrush = TomahawkStyle::NOW_PLAYING_ITEM; option->palette.setColor( QPalette::Highlight, TomahawkStyle::NOW_PLAYING_ITEM.lighter() ); option->palette.setColor( QPalette::Text, TomahawkStyle::NOW_PLAYING_ITEM_TEXT ); - + option->palette.setColor( QPalette::Foreground, TomahawkStyle::NOW_PLAYING_ITEM_TEXT ); } else if ( option->state & QStyle::State_Selected ) { + option->palette.setColor( QPalette::Foreground, option->palette.color( QPalette::HighlightedText ) ); option->palette.setColor( QPalette::Text, option->palette.color( QPalette::HighlightedText ) ); } else From d875b8dccf8962563a4c89df4894991de31526f7 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 11:50:53 +0200 Subject: [PATCH 377/565] * StatsGauge retrieves color palette from TomahawkStyle. --- src/libtomahawk/widgets/StatsGauge.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index a32be994ae..d5d51cb84b 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -54,23 +54,23 @@ StatsGauge::paintEvent( QPaintEvent* event ) QSize gaugeSize = m_sizeHint - QSize( 0, 40 ); - QPen pen( TomahawkStyle::NOW_PLAYING_ITEM.lighter() ); + QPen pen( TomahawkStyle::HEADER_GAUGE_HIGHLIGHT ); pen.setWidth( 16 ); p.setPen( pen ); int fullCircle = 16 * 360; p.drawArc( QRect( 12, 12, gaugeSize.width() - 24, gaugeSize.height() - 24 ), - 4*360, (int)( -1.0 * (float)fullCircle * ( invertedAppearance() ? ( 1.0 - m_percentage ) : m_percentage ) ) ); + 4 * 360, (int)( -1.0 * (float)fullCircle * ( invertedAppearance() ? ( 1.0 - m_percentage ) : m_percentage ) ) ); - pen = QPen( TomahawkStyle::NOW_PLAYING_ITEM.darker() ); + pen = QPen( TomahawkStyle::HEADER_GAUGE_HIGHLIGHT.darker() ); pen.setWidth( 6 ); p.setPen( pen ); - QBrush brush( QColor( "#252020" ) ); + QBrush brush( TomahawkStyle::HEADER_GAUGE_BACKGROUND ); p.setBrush( brush ); p.drawEllipse( QRect( 28, 28, gaugeSize.width() - 56, gaugeSize.height() - 56 ) ); - pen = QPen( Qt::white ); + pen = QPen( TomahawkStyle::HEADER_GAUGE_TEXT ); p.setPen( pen ); QFont font = p.font(); font.setWeight( QFont::Black ); @@ -84,7 +84,7 @@ StatsGauge::paintEvent( QPaintEvent* event ) QRect textRect( 0, gaugeSize.height() / 2 - 14, gaugeSize.width(), 62 ); p.drawText( textRect, Qt::AlignCenter, value() > 0 ? QString::number( value() ) : "-" ); - pen = QPen( QColor( "#8b8b8b" ) ); + pen = QPen( TomahawkStyle::HEADER_GAUGE_TEXT.darker() ); p.setPen( pen ); font = p.font(); font.setWeight( QFont::Black ); @@ -96,14 +96,14 @@ StatsGauge::paintEvent( QPaintEvent* event ) if ( !m_text.isEmpty() ) { - pen = QPen( Qt::white ); + pen = QPen( TomahawkStyle::HEADER_GAUGE_TEXT ); p.setPen( pen ); font = p.font(); font.setWeight( QFont::DemiBold ); font.setPixelSize( 16 ); p.setFont( font ); - QColor figColor( "#3e3e3e" ); + QColor figColor( TomahawkStyle::HEADER_GAUGE_LABEL_BACKGROUND ); p.setBrush( figColor ); QFontMetrics fm( font ); From 0e5e4e96e53f9d00dfe689e8e7b3f4a64970b60b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 11:51:31 +0200 Subject: [PATCH 378/565] * Use new color roles on Artist page. --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index cfc71b8453..a0b963c0ff 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -95,15 +95,19 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); ui->lineBelow2->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); - QPalette trackViewPal = ui->topHits->palette(); - trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); - trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); - trackViewPal.setColor( QPalette::Highlight, QColor( "#292f34" ) ); - trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); - ui->topHits->setPalette( trackViewPal ); - ui->topHits->setAlternatingRowColors( false ); - ui->topHits->setFrameShape( QFrame::NoFrame ); - ui->topHits->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + { + QPalette p = ui->topHits->palette(); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TRACKLIST_TRACK_SOLVED ); + p.setColor( QPalette::BrightText, TomahawkStyle::PAGE_TRACKLIST_TRACK_UNRESOLVED ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_TRACKLIST_NUMBER ); + p.setColor( QPalette::Highlight, QColor( "#292f34" ) ); + p.setColor( QPalette::HighlightedText, Qt::white ); + + ui->topHits->setPalette( p ); + ui->topHits->setAlternatingRowColors( false ); + ui->topHits->setFrameShape( QFrame::NoFrame ); + ui->topHits->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + } QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); From 6e84706d3e781c06ae25213b70b3f27c6f86828b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 11:52:17 +0200 Subject: [PATCH 379/565] * Use QPalette::Foreground for drawing AlbumItemDelegate's position figure. --- src/libtomahawk/playlist/AlbumItemDelegate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/AlbumItemDelegate.cpp b/src/libtomahawk/playlist/AlbumItemDelegate.cpp index 746f952264..fa86e6d083 100644 --- a/src/libtomahawk/playlist/AlbumItemDelegate.cpp +++ b/src/libtomahawk/playlist/AlbumItemDelegate.cpp @@ -94,7 +94,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, } painter->setFont( m_bigBoldFont ); - painter->setPen( option.palette.text().color().lighter( 450 ) ); + painter->setPen( opt.palette.foreground().color() ); QRect leftRect = r; QRect figureRect = r.adjusted( 4, 0, 0, 0 ); From 82705f63f79391522ea8d74055d170e0d30be419 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 11:57:26 +0200 Subject: [PATCH 380/565] * Moved highlight color roles into TomahawkStyle. --- src/libtomahawk/utils/TomahawkStyle.h | 2 ++ src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index 7a35229cc9..c10982c360 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -83,6 +83,8 @@ namespace TomahawkStyle static const QColor PAGE_TRACKLIST_TRACK_SOLVED = QColor( "#292F34" ); static const QColor PAGE_TRACKLIST_TRACK_UNRESOLVED = QColor( "#8597A6" ); static const QColor PAGE_TRACKLIST_NUMBER = QColor( "#8DBF2D" ).darker( 400 ); + static const QColor PAGE_TRACKLIST_HIGHLIGHT = QColor( "#292f34" ); + static const QColor PAGE_TRACKLIST_HIGHLIGHT_TEXT = QColor( "#ffffff" ); static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index a0b963c0ff..f0e4c493d1 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -100,8 +100,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* p.setColor( QPalette::Text, TomahawkStyle::PAGE_TRACKLIST_TRACK_SOLVED ); p.setColor( QPalette::BrightText, TomahawkStyle::PAGE_TRACKLIST_TRACK_UNRESOLVED ); p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_TRACKLIST_NUMBER ); - p.setColor( QPalette::Highlight, QColor( "#292f34" ) ); - p.setColor( QPalette::HighlightedText, Qt::white ); + p.setColor( QPalette::Highlight, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT ); + p.setColor( QPalette::HighlightedText, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT_TEXT ); ui->topHits->setPalette( p ); ui->topHits->setAlternatingRowColors( false ); From ea7a21518d8a9fb563160ac0e8cd836b742c27d5 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 12:23:04 +0200 Subject: [PATCH 381/565] Only invoke Closure if receiver still exists --- src/libtomahawk/utils/Closure.cpp | 39 ++++++++++++++++++++----------- src/libtomahawk/utils/Closure.h | 3 ++- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/libtomahawk/utils/Closure.cpp b/src/libtomahawk/utils/Closure.cpp index e77321d5d9..e1d7be6b0f 100644 --- a/src/libtomahawk/utils/Closure.cpp +++ b/src/libtomahawk/utils/Closure.cpp @@ -80,20 +80,31 @@ void Closure::Connect(QObject* sender, const char* signal) { Q_UNUSED(success); } -void Closure::Invoked() { - if (callback_) { - callback_(); - } else { - slot_.invoke( - parent() ? parent() : outOfThreadReceiver_, - val0_ ? val0_->arg() : QGenericArgument(), - val1_ ? val1_->arg() : QGenericArgument(), - val2_ ? val2_->arg() : QGenericArgument(), - val3_ ? val3_->arg() : QGenericArgument()); - } - - if ( autoDelete_ ) - deleteLater(); +void +Closure::Invoked() { + if ( callback_ ) + { + callback_(); + } + else + { + // Only invoke the closure if the receiver still exists + // Hint: If parent was destroyed, this closure would also be destroyed + if ( parent() || !outOfThreadReceiver_.isNull() ) + { + slot_.invoke( + parent() ? parent() : outOfThreadReceiver_.data(), + val0_ ? val0_->arg() : QGenericArgument(), + val1_ ? val1_->arg() : QGenericArgument(), + val2_ ? val2_->arg() : QGenericArgument(), + val3_ ? val3_->arg() : QGenericArgument()); + } + } + + if ( autoDelete_ ) + { + deleteLater(); + } } void Closure::Cleanup() { diff --git a/src/libtomahawk/utils/Closure.h b/src/libtomahawk/utils/Closure.h index fa89257d51..6008f62ed9 100644 --- a/src/libtomahawk/utils/Closure.h +++ b/src/libtomahawk/utils/Closure.h @@ -31,6 +31,7 @@ using std::tr1::function; #include #include +#include #include #include @@ -95,7 +96,7 @@ class DLLEXPORT Closure : public QObject, boost::noncopyable { QMetaMethod slot_; function callback_; bool autoDelete_; - QObject* outOfThreadReceiver_; + QPointer outOfThreadReceiver_; boost::scoped_ptr val0_; boost::scoped_ptr val1_; From 3df5ab3a411ffda5f0154625b91746cd41425335 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 12:49:02 +0200 Subject: [PATCH 382/565] * Got rid of cover shadow / gradient. --- src/libtomahawk/playlist/GridItemDelegate.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/playlist/GridItemDelegate.cpp b/src/libtomahawk/playlist/GridItemDelegate.cpp index 06e5e66b26..f4447f20e3 100644 --- a/src/libtomahawk/playlist/GridItemDelegate.cpp +++ b/src/libtomahawk/playlist/GridItemDelegate.cpp @@ -179,23 +179,13 @@ GridItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, int topHeight = QFontMetrics( boldFont ).boundingRect( top ).height(); int frameHeight = bottomHeight + topHeight + 10; - QColor c1; - c1.setRgb( 0, 0, 0 ); - c1.setAlphaF( 0.00 ); - QColor c2; - c2.setRgb( 0, 0, 0 ); - c2.setAlphaF( 0.88 ); - - QRect gradientRect = r.adjusted( 0, r.height() - frameHeight * 2, 0, 0 ); - QLinearGradient gradient( QPointF( 0, 0 ), QPointF( 0, 1 ) ); - gradient.setCoordinateMode( QGradient::ObjectBoundingMode ); - gradient.setColorAt( 0.0, c1 ); - gradient.setColorAt( 0.6, c2 ); - gradient.setColorAt( 1.0, c2 ); + QRect gradientRect = r.adjusted( 0, r.height() - frameHeight * 1.2, 0, 0 ); + QColor gradientColor = opt.palette.background().color(); + gradientColor.setAlphaF( 0.66 ); painter->save(); painter->setPen( Qt::transparent ); - painter->setBrush( gradient ); + painter->setBrush( gradientColor ); painter->drawRect( gradientRect ); painter->restore(); From ac5774a486ba355302276ad64ed85233863e359f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 12:49:34 +0200 Subject: [PATCH 383/565] * Set the proper background color for the albums grid on the Artist page. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index f0e4c493d1..0aeb74332f 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -207,7 +207,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); TomahawkStyle::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); - ui->albums->setStyleSheet( "QListView { background-color: transparent; }" ); + ui->albums->setStyleSheet( QString( "QListView { background-color: %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); TomahawkStyle::stylePageFrame( ui->albumFrame ); ui->relatedArtists->setStyleSheet( "QListView { background-color: transparent; }" ); From 2203c7afcb39f010aa59c7f8cde871fb41dd55ff Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 13:14:26 +0200 Subject: [PATCH 384/565] * Fixed Track page. --- .../widgets/infowidgets/TrackInfoWidget.cpp | 70 +++++++-- .../widgets/infowidgets/TrackInfoWidget.ui | 144 ++++++++++++------ 2 files changed, 151 insertions(+), 63 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index c3170958eb..24db980ef0 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -50,32 +50,71 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par connect( ui->artistLabel, SIGNAL( clickedArtist() ), SLOT( onArtistClicked() ) ); ui->statsLabel->setStyleSheet( "QLabel { background-image:url(); border: 2px solid #dddddd; background-color: #faf9f9; border-radius: 4px; padding: 12px; }" ); - ui->lyricsView->setStyleSheet( "QTextBrowser#lyricsView { background-color: transparent; }" ); + ui->statsLabel->setVisible( false ); + ui->lyricsView->setStyleSheet( "QTextBrowser#lyricsView { background-color: transparent; }" ); ui->lyricsView->setFrameShape( QFrame::NoFrame ); ui->lyricsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->lyricsView->setVisible( false ); // FIXME eventually ui->similarTracksView->setAutoResize( true ); ui->similarTracksView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + + ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); + ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + // TomahawkUtils::styleScrollBar( ui->similarTracksView->verticalScrollBar() ); TomahawkStyle::styleScrollBar( ui->lyricsView->verticalScrollBar() ); // ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; } QListView::item { background-color: transparent; }" ); - QFont f = ui->statsLabel->font(); - f.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); - f.setBold( true ); - ui->statsLabel->setFont( f ); + { + QFont f = ui->trackLabel->font(); + f.setFamily( "Titillium Web" ); + + QPalette p = ui->trackLabel->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); - QPalette p = ui->lyricsView->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); - p.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); + ui->trackLabel->setFont( f ); + ui->trackLabel->setPalette( p ); + } + + { + QFont f = ui->artistLabel->font(); + f.setFamily( "Titillium Web" ); + + QPalette p = ui->artistLabel->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); - ui->lyricsView->setPalette( p ); - ui->label->setPalette( p ); - ui->artistLabel->setPalette( p ); - ui->trackLabel->setPalette( p ); + ui->artistLabel->setFont( f ); + ui->artistLabel->setPalette( p ); + } + + { + QFont f = ui->label->font(); + f.setBold( false ); + f.setFamily( "Fauna One" ); + + QPalette p = ui->label->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); + + ui->label->setFont( f ); + ui->label->setPalette( p ); + } + + { + QFont f = ui->statsLabel->font(); + f.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); + f.setBold( true ); + ui->statsLabel->setFont( f ); + } + + { + QPalette p = ui->lyricsView->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); + ui->lyricsView->setPalette( p ); + } m_relatedTracksModel = new PlayableModel( ui->similarTracksView ); ui->similarTracksView->setPlayableModel( m_relatedTracksModel ); @@ -92,12 +131,16 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par m_scrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); QPalette pal = palette(); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); m_scrollArea->setPalette( pal ); m_scrollArea->setAutoFillBackground( true ); m_scrollArea->setFrameShape( QFrame::NoFrame ); m_scrollArea->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + ui->widget->setPalette( pal ); + ui->widget->setAutoFillBackground( true ); + QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); m_playStatsGauge->setText( tr( "# PLAYS / ARTIST" ) ); @@ -111,7 +154,6 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par l->addWidget( m_playStatsTotalGauge ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); ui->statsWidget->setLayout( l ); - ui->statsLabel->setVisible( false ); TomahawkUtils::unmarginLayout( l ); QVBoxLayout* layout = new QVBoxLayout(); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index d6453bbefe..404249f7d4 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -7,18 +7,18 @@ 0 0 965 - 771 + 832 Form - + - 16 + 0 - 12 + 0 @@ -26,7 +26,7 @@ 16 - 0 + 12 @@ -53,7 +53,7 @@ - 8 + 0 0 @@ -77,7 +77,7 @@ - 18 + 20 75 true @@ -100,7 +100,7 @@ - 14 + 16 @@ -163,6 +163,32 @@ + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + @@ -183,61 +209,81 @@ - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - + + - 4 + 0 - 8 + 0 - 4 + 16 - 8 + 0 - 8 + 16 - - - - Arial - 18 - 75 - true - + + + + 0 + 0 + - - Similar Tracks + + QFrame::StyledPanel - - 0 - - - - - - - - 0 - 0 - + + QFrame::Raised + + + 4 + + + 28 + + + 4 + + + 8 + + + 4 + + + + + + 20 + 75 + true + + + + Similar Tracks + + + 0 + + + + + + + + 0 + 0 + + + + + From 786a5f4cd5aca39a6b791b9588740ae3ab3533e2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 14:24:48 +0200 Subject: [PATCH 385/565] * Fixed ModeHeader's background color. --- src/libtomahawk/playlist/ModeHeader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/ModeHeader.cpp b/src/libtomahawk/playlist/ModeHeader.cpp index 08f204f43b..4ffabaac66 100644 --- a/src/libtomahawk/playlist/ModeHeader.cpp +++ b/src/libtomahawk/playlist/ModeHeader.cpp @@ -87,7 +87,7 @@ ModeHeader::ModeHeader( QWidget* parent ) QPalette pal = palette(); pal.setColor( QPalette::Foreground, Qt::white ); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND.lighter() ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND.lighter() ); setAutoFillBackground( true ); setPalette( pal ); From 60ad0a016f520cd2201eb19e7a5287338ab6dea1 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 09:52:52 -0400 Subject: [PATCH 386/565] More Artist page design. --- src/libtomahawk/utils/TomahawkStyle.h | 12 ++++++------ .../widgets/infowidgets/ArtistInfoWidget.cpp | 5 +++-- .../widgets/infowidgets/TrackInfoWidget.cpp | 5 +++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index c10982c360..0598d33212 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -61,14 +61,14 @@ namespace TomahawkStyle static const QColor SELECTION_BACKGROUND = QColor( "#962c26" ); static const QColor SELECTION_FOREGROUND = QColor( "#ffffff" ); - static const QColor HEADER_GAUGE_HIGHLIGHT = QColor( "#8dbf2d" ); - static const QColor HEADER_GAUGE_BACKGROUND = QColor( "#252020" ); - static const QColor HEADER_GAUGE_TEXT = QColor( "#ffffff" ); - static const QColor HEADER_GAUGE_LABEL_BACKGROUND = QColor( "#3e3e3e" ); + static const QColor HEADER_GAUGE_HIGHLIGHT = QColor( "#7DC4FF" ); + static const QColor HEADER_GAUGE_BACKGROUND = QColor( "#DBDBDB" ); + static const QColor HEADER_GAUGE_TEXT = QColor( "#292f34" ); + static const QColor HEADER_GAUGE_LABEL_BACKGROUND = QColor( "#DBDBDB" ); static const QColor HEADER_BACKGROUND = QColor( "#292f34" ); static const QColor HEADER_TEXT = QColor( "#ffffff" ); - static const QColor HEADER_LINK = QColor( "#8dbf2d" ); + static const QColor HEADER_LINK = QColor( "#7DC4FF" ); static const QColor HEADER_HIGHLIGHT = QColor( "#333" ); static const QColor TOGGLEBUTTON_BACKGROUND = QColor( "#292f34" ); @@ -81,7 +81,7 @@ namespace TomahawkStyle static const QColor PAGE_BACKGROUND = QColor( "#DBDBDB" ); static const QColor PAGE_TRACKLIST_TRACK_SOLVED = QColor( "#292F34" ); - static const QColor PAGE_TRACKLIST_TRACK_UNRESOLVED = QColor( "#8597A6" ); + static const QColor PAGE_TRACKLIST_TRACK_UNRESOLVED = QColor( "#8597A6" ).lighter( 200 ); static const QColor PAGE_TRACKLIST_NUMBER = QColor( "#8DBF2D" ).darker( 400 ); static const QColor PAGE_TRACKLIST_HIGHLIGHT = QColor( "#292f34" ); static const QColor PAGE_TRACKLIST_HIGHLIGHT_TEXT = QColor( "#ffffff" ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 0aeb74332f..d6dd1d61a4 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -126,7 +126,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->biography->font(); - f.setPointSize( f.pointSize() + 1 ); + f.setPointSize( f.pointSize() + 2 ); f.setFamily( "Titillium Web" ); QPalette p = ui->biography->palette(); @@ -148,7 +148,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->artistLabel->font(); - f.setFamily( "Titillium Web" ); + f.setPointSize( f.pointSize() + 12 ); + f.setFamily( "Fauna One" ); QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 24db980ef0..a182ca3124 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -70,7 +70,8 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->trackLabel->font(); - f.setFamily( "Titillium Web" ); + f.setFamily( "Fauna One" ); + f.setPointSize( TomahawkUtils::defaultFontSize() + 12 ); QPalette p = ui->trackLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); @@ -81,7 +82,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->artistLabel->font(); - f.setFamily( "Titillium Web" ); + f.setFamily( "Fauna One" ); QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); From e7eea28d8491a7fb092c9e827c4bfe735043b52f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 14 Jun 2013 16:04:44 +0200 Subject: [PATCH 387/565] * Fixed page fonts. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 3 +-- src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index d6dd1d61a4..2cac579fa8 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -148,8 +148,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->artistLabel->font(); - f.setPointSize( f.pointSize() + 12 ); - f.setFamily( "Fauna One" ); + f.setFamily( "Titillium Web" ); QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index a182ca3124..24db980ef0 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -70,8 +70,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->trackLabel->font(); - f.setFamily( "Fauna One" ); - f.setPointSize( TomahawkUtils::defaultFontSize() + 12 ); + f.setFamily( "Titillium Web" ); QPalette p = ui->trackLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); @@ -82,7 +81,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->artistLabel->font(); - f.setFamily( "Fauna One" ); + f.setFamily( "Titillium Web" ); QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); From 6bf5d70b5ca870686ad7abc8de3bdbc5ad2f33df Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 16:36:26 +0200 Subject: [PATCH 388/565] Use deleteLater so that peerinfo_ptr can be used in different threads. --- src/libtomahawk/sip/PeerInfo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index f122ba2aec..3855242022 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -58,7 +58,7 @@ PeerInfo::getSelf( SipPlugin* parent, PeerInfo::GetOptions options ) return peerinfo_ptr(); } - peerinfo_ptr selfPeer( new PeerInfo( parent, "local peerinfo don't use this id for anything" ) ); + peerinfo_ptr selfPeer( new PeerInfo( parent, "local peerinfo don't use this id for anything" ), &QObject::deleteLater ); selfPeer->setWeakRef( selfPeer.toWeakRef() ); selfPeer->setContactId( "localpeer" ); @@ -91,7 +91,7 @@ PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options ) return peerinfo_ptr(); } - peerinfo_ptr peerInfo( new PeerInfo( parent, id ) ); + peerinfo_ptr peerInfo( new PeerInfo( parent, id ), &QObject::deleteLater ); peerInfo->setWeakRef( peerInfo.toWeakRef() ); s_peersByCacheKey.insert( key, peerInfo ); From 776c9194457f45f0b30369256f9b75f59be07d3b Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 16:46:07 +0200 Subject: [PATCH 389/565] Correctly handle/display the different states of PeerInfos --- src/tomahawk/DiagnosticsDialog.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index df41ee28e6..2332422f0d 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -169,17 +169,24 @@ DiagnosticsDialog::accountLog( Tomahawk::Accounts::Account* account ) QMap< QString, QList< Tomahawk::peerinfo_ptr > > nodes; foreach ( const Tomahawk::peerinfo_ptr& peerInfo, account->sipPlugin()->peersOnline() ) { - if ( peerInfo->nodeId().isEmpty() ) + if ( peerInfo->sipInfos().isEmpty() ) + { + accountInfo.append( QString( " %1: waiting for SIP information\n" ).arg( peerInfo->id() ) ); + } + else if ( peerInfo->nodeId().isEmpty() ) { QList< Tomahawk::peerinfo_ptr> infos; infos.append( peerInfo ); accountInfo.append( peerLog( peerInfo->nodeId(), infos ) ); } - if ( !nodes.contains( peerInfo->nodeId() ) ) + else { - nodes[peerInfo->nodeId()] = QList< Tomahawk::peerinfo_ptr >(); + if ( !nodes.contains( peerInfo->nodeId() ) ) + { + nodes[peerInfo->nodeId()] = QList< Tomahawk::peerinfo_ptr >(); + } + nodes[peerInfo->nodeId()].append( peerInfo); } - nodes[peerInfo->nodeId()].append( peerInfo); } foreach ( const QString& nodeid, nodes.keys() ) { From 0be6ec1a4004698ff128cd4c0d1d19436d6f738b Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 17:55:29 +0200 Subject: [PATCH 390/565] Remove unneeded include --- src/libtomahawk/sip/SipInfo.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/sip/SipInfo.h b/src/libtomahawk/sip/SipInfo.h index fe337ea2d3..44138644d7 100644 --- a/src/libtomahawk/sip/SipInfo.h +++ b/src/libtomahawk/sip/SipInfo.h @@ -20,7 +20,6 @@ #define SIPINFO_H #include -#include #include "DllMacro.h" From 5fa5532fd2cd5e5b2bc8149c7b686570b39677b1 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 12:52:44 -0400 Subject: [PATCH 391/565] Unbreak OSX --- src/accounts/hatchet/account/HatchetAccount.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/accounts/hatchet/account/HatchetAccount.cpp b/src/accounts/hatchet/account/HatchetAccount.cpp index 5884d1a074..7cf840409e 100644 --- a/src/accounts/hatchet/account/HatchetAccount.cpp +++ b/src/accounts/hatchet/account/HatchetAccount.cpp @@ -17,6 +17,7 @@ */ #include "HatchetAccount.h" +#include #include "HatchetAccountConfig.h" #include "utils/Closure.h" From c66300aa67e524fdc333ab9633e5f5a5df3a886c Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 14:46:57 -0400 Subject: [PATCH 392/565] More artist & track design plus some updated placeholder icons --- data/images/album-placeholder-grid.svg | 28 ++++--------------- data/images/info.svg | 10 +++++-- data/images/track-placeholder-grid.svg | 14 +++++++--- src/libtomahawk/utils/TomahawkStyle.h | 5 ++-- .../widgets/infowidgets/ArtistInfoWidget.cpp | 6 ++-- .../widgets/infowidgets/TrackInfoWidget.cpp | 7 +++-- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/data/images/album-placeholder-grid.svg b/data/images/album-placeholder-grid.svg index d1120cd2b1..bb69070ee6 100644 --- a/data/images/album-placeholder-grid.svg +++ b/data/images/album-placeholder-grid.svg @@ -3,24 +3,8 @@ album-placeholder-grid Created with Sketch (http://www.bohemiancoding.com/sketch) - - - - - + - - - - - - - - - - - - @@ -33,12 +17,10 @@ - - - - - - + + + + \ No newline at end of file diff --git a/data/images/info.svg b/data/images/info.svg index b369d3c0af..569c931228 100644 --- a/data/images/info.svg +++ b/data/images/info.svg @@ -1,9 +1,13 @@ - + info Created with Sketch (http://www.bohemiancoding.com/sketch) - - + + + + + + \ No newline at end of file diff --git a/data/images/track-placeholder-grid.svg b/data/images/track-placeholder-grid.svg index d938fe3ac0..6669b63b10 100644 --- a/data/images/track-placeholder-grid.svg +++ b/data/images/track-placeholder-grid.svg @@ -1,9 +1,15 @@ - + track-placeholder-grid Created with Sketch (http://www.bohemiancoding.com/sketch) - - - + + + + + + + + + \ No newline at end of file diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index 0598d33212..915b924647 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -67,7 +67,8 @@ namespace TomahawkStyle static const QColor HEADER_GAUGE_LABEL_BACKGROUND = QColor( "#DBDBDB" ); static const QColor HEADER_BACKGROUND = QColor( "#292f34" ); - static const QColor HEADER_TEXT = QColor( "#ffffff" ); + static const QColor HEADER_LABEL = QColor( "#FFFFFF" ); + static const QColor HEADER_TEXT = QColor( "#DBDBDB" ); static const QColor HEADER_LINK = QColor( "#7DC4FF" ); static const QColor HEADER_HIGHLIGHT = QColor( "#333" ); @@ -81,7 +82,7 @@ namespace TomahawkStyle static const QColor PAGE_BACKGROUND = QColor( "#DBDBDB" ); static const QColor PAGE_TRACKLIST_TRACK_SOLVED = QColor( "#292F34" ); - static const QColor PAGE_TRACKLIST_TRACK_UNRESOLVED = QColor( "#8597A6" ).lighter( 200 ); + static const QColor PAGE_TRACKLIST_TRACK_UNRESOLVED = QColor( "#8597A6" ).lighter( 150 ); static const QColor PAGE_TRACKLIST_NUMBER = QColor( "#8DBF2D" ).darker( 400 ); static const QColor PAGE_TRACKLIST_HIGHLIGHT = QColor( "#292f34" ); static const QColor PAGE_TRACKLIST_HIGHLIGHT_TEXT = QColor( "#ffffff" ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 2cac579fa8..eafb25b29e 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -111,7 +111,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); - m_playStatsGauge->setText( tr( "# IN YOUR CHARTS" ) ); + m_playStatsGauge->setText( tr( "YOUR CHART RANK" ) ); m_playStatsGauge->setInvertedAppearance( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); @@ -149,9 +149,11 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->artistLabel->font(); f.setFamily( "Titillium Web" ); + f.setPointSize( TomahawkUtils::defaultFontSize() + 20 ); + f.setBold( true ); QPalette p = ui->artistLabel->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_LABEL ); ui->artistLabel->setFont( f ); ui->artistLabel->setPalette( p ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 24db980ef0..76b0bf8806 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -71,9 +71,11 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->trackLabel->font(); f.setFamily( "Titillium Web" ); + f.setPointSize( TomahawkUtils::defaultFontSize() + 20 ); + f.setBold( true ); QPalette p = ui->trackLabel->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_LABEL ); ui->trackLabel->setFont( f ); ui->trackLabel->setPalette( p ); @@ -82,6 +84,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->artistLabel->font(); f.setFamily( "Titillium Web" ); + f.setPointSize( TomahawkUtils::defaultFontSize() + 10 ); QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); @@ -145,7 +148,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par m_playStatsGauge = new StatsGauge( ui->statsWidget ); m_playStatsGauge->setText( tr( "# PLAYS / ARTIST" ) ); m_playStatsTotalGauge = new StatsGauge( ui->statsWidget ); - m_playStatsTotalGauge->setText( tr( "# IN YOUR CHARTS" ) ); + m_playStatsTotalGauge->setText( tr( "YOUR CHART RANK" ) ); m_playStatsTotalGauge->setInvertedAppearance( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); From 4926fcb239c2e4a45503a576d33674f45866655e Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 14:54:52 -0400 Subject: [PATCH 393/565] Pretty up the gauge a bit more --- src/libtomahawk/utils/TomahawkStyle.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index 915b924647..6488e64f8c 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -62,9 +62,9 @@ namespace TomahawkStyle static const QColor SELECTION_FOREGROUND = QColor( "#ffffff" ); static const QColor HEADER_GAUGE_HIGHLIGHT = QColor( "#7DC4FF" ); - static const QColor HEADER_GAUGE_BACKGROUND = QColor( "#DBDBDB" ); - static const QColor HEADER_GAUGE_TEXT = QColor( "#292f34" ); - static const QColor HEADER_GAUGE_LABEL_BACKGROUND = QColor( "#DBDBDB" ); + static const QColor HEADER_GAUGE_BACKGROUND = QColor( "#0B0A0A" ); + static const QColor HEADER_GAUGE_TEXT = QColor( "#FFFFFF" ); + static const QColor HEADER_GAUGE_LABEL_BACKGROUND = QColor( "#0B0A0A" ); static const QColor HEADER_BACKGROUND = QColor( "#292f34" ); static const QColor HEADER_LABEL = QColor( "#FFFFFF" ); From 088d7ed3aeff7c5dd15911647a539096166be9fd Mon Sep 17 00:00:00 2001 From: Thierry Goeckel Date: Fri, 14 Jun 2013 22:38:30 +0200 Subject: [PATCH 394/565] Don't sort alphabetically here. Makes sense as it is in the source code (week->month->year->overall). Now someone make week the default. :p --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 74aac66a12..65cef35b39 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -49,8 +49,6 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) d_func()->crumbModelLeft = new QStandardItemModel( this ); d_func()->sortedProxy = new QSortFilterProxyModel( this ); - d_func()->sortedProxy->setDynamicSortFilter( true ); - d_func()->sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); d_func()->ui->breadCrumbLeft->setRootIcon( TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ) ); connect( d_func()->ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged( QModelIndex ) ) ); @@ -74,14 +72,14 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) monthItem->setData( MonthChart, Breadcrumb::DefaultRole ); chartItem->appendRow( monthItem ); QStandardItem* yearItem = new QStandardItem( tr( "Last Year" ) ); - yearItem->setData( YearChart, Breadcrumb::DefaultRole ); + yearItem->setData( YearChart, Breadcrumb::DefaultRole); chartItem->appendRow( yearItem ); QStandardItem* overallItem = new QStandardItem( tr( "Overall" ) ); overallItem->setData( OverallChart, Breadcrumb::DefaultRole ); chartItem->appendRow( overallItem ); d_func()->sortedProxy->setSourceModel( d_func()->crumbModelLeft ); - d_func()->sortedProxy->sort( 0, Qt::AscendingOrder ); d_func()->ui->breadCrumbLeft->setModel( d_func()->sortedProxy ); + d_func()->ui->breadCrumbLeft->setVisible( true ); } From 508c7ffc8020789e9e4a8c1cdcc74772b2355174 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 16:38:51 -0400 Subject: [PATCH 395/565] Bump up bio font a couple of point sizes --- data/images/track-placeholder-grid.svg | 2 +- src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp | 1 + src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 4 ++-- src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/data/images/track-placeholder-grid.svg b/data/images/track-placeholder-grid.svg index 6669b63b10..2253294903 100644 --- a/data/images/track-placeholder-grid.svg +++ b/data/images/track-placeholder-grid.svg @@ -10,6 +10,6 @@ - + \ No newline at end of file diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index 36d969932c..6f22bbb87c 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -54,6 +54,7 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par m_albumsModel = new PlayableModel( ui->albums ); ui->albums->setPlayableModel( m_albumsModel ); ui->albums->setEmptyTip( tr( "Sorry, we could not find any other albums for this artist!" ) ); + m_tracksModel = new TreeModel( ui->tracks ); m_tracksModel->setMode( Mixed ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index eafb25b29e..9981aad83b 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -111,7 +111,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); - m_playStatsGauge->setText( tr( "YOUR CHART RANK" ) ); + m_playStatsGauge->setText( tr( "YOUR ARTIST RANK" ) ); m_playStatsGauge->setInvertedAppearance( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); @@ -126,7 +126,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->biography->font(); - f.setPointSize( f.pointSize() + 2 ); + f.setPointSize( f.pointSize() + 4 ); f.setFamily( "Titillium Web" ); QPalette p = ui->biography->palette(); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 76b0bf8806..6bf243fd54 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -148,7 +148,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par m_playStatsGauge = new StatsGauge( ui->statsWidget ); m_playStatsGauge->setText( tr( "# PLAYS / ARTIST" ) ); m_playStatsTotalGauge = new StatsGauge( ui->statsWidget ); - m_playStatsTotalGauge->setText( tr( "YOUR CHART RANK" ) ); + m_playStatsTotalGauge->setText( tr( "YOUR SONG RANK" ) ); m_playStatsTotalGauge->setInvertedAppearance( true ); l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); From 9b53ffc952a8c4723908054fcdd8eaa55eb04c2e Mon Sep 17 00:00:00 2001 From: Thierry Goeckel Date: Fri, 14 Jun 2013 22:42:49 +0200 Subject: [PATCH 396/565] Style fix. --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 65cef35b39..47753bed19 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -72,7 +72,7 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) monthItem->setData( MonthChart, Breadcrumb::DefaultRole ); chartItem->appendRow( monthItem ); QStandardItem* yearItem = new QStandardItem( tr( "Last Year" ) ); - yearItem->setData( YearChart, Breadcrumb::DefaultRole); + yearItem->setData( YearChart, Breadcrumb::DefaultRole ); chartItem->appendRow( yearItem ); QStandardItem* overallItem = new QStandardItem( tr( "Overall" ) ); overallItem->setData( OverallChart, Breadcrumb::DefaultRole ); From dd28657dbc8e1dd371367cc02e03656156797c77 Mon Sep 17 00:00:00 2001 From: Thierry Goeckel Date: Fri, 14 Jun 2013 22:43:29 +0200 Subject: [PATCH 397/565] Remove newline. --- src/libtomahawk/widgets/NetworkActivityWidget.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/widgets/NetworkActivityWidget.cpp b/src/libtomahawk/widgets/NetworkActivityWidget.cpp index 47753bed19..603290d2d2 100644 --- a/src/libtomahawk/widgets/NetworkActivityWidget.cpp +++ b/src/libtomahawk/widgets/NetworkActivityWidget.cpp @@ -79,7 +79,6 @@ NetworkActivityWidget::NetworkActivityWidget( QWidget* parent ) chartItem->appendRow( overallItem ); d_func()->sortedProxy->setSourceModel( d_func()->crumbModelLeft ); d_func()->ui->breadCrumbLeft->setModel( d_func()->sortedProxy ); - d_func()->ui->breadCrumbLeft->setVisible( true ); } From c014650628565453453bc3b7b26cb2b996986b67 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 17:00:04 -0400 Subject: [PATCH 398/565] Fix track number color --- src/libtomahawk/utils/TomahawkStyle.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index 6488e64f8c..fdb18c84d0 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -58,7 +58,7 @@ namespace TomahawkStyle static const QColor NOW_PLAYING_ITEM = QColor( "#962c26" ); static const QColor NOW_PLAYING_ITEM_TEXT = QColor( "#ffffff" ); - static const QColor SELECTION_BACKGROUND = QColor( "#962c26" ); + static const QColor SELECTION_BACKGROUND = QColor( "#7DC4FF" ); static const QColor SELECTION_FOREGROUND = QColor( "#ffffff" ); static const QColor HEADER_GAUGE_HIGHLIGHT = QColor( "#7DC4FF" ); @@ -83,7 +83,7 @@ namespace TomahawkStyle static const QColor PAGE_TRACKLIST_TRACK_SOLVED = QColor( "#292F34" ); static const QColor PAGE_TRACKLIST_TRACK_UNRESOLVED = QColor( "#8597A6" ).lighter( 150 ); - static const QColor PAGE_TRACKLIST_NUMBER = QColor( "#8DBF2D" ).darker( 400 ); + static const QColor PAGE_TRACKLIST_NUMBER = QColor( "#292f34" ); static const QColor PAGE_TRACKLIST_HIGHLIGHT = QColor( "#292f34" ); static const QColor PAGE_TRACKLIST_HIGHLIGHT_TEXT = QColor( "#ffffff" ); From 5b3c29730616648b5557181345402788c23430a0 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 17:23:09 -0400 Subject: [PATCH 399/565] Update all the Spotify icons to their new logo, except the one in the resolvers which is apparently the only one that matters. --- data/images/spotify-logo.png | Bin 54526 -> 381916 bytes data/images/spotify-logo.svg | 56 +++++--------- data/images/spotify-sourceicon.png | Bin 3450 -> 381916 bytes data/images/spotify-sourceicon.svg | 120 +++++++---------------------- 4 files changed, 50 insertions(+), 126 deletions(-) diff --git a/data/images/spotify-logo.png b/data/images/spotify-logo.png index 684bd22544196e3c45c3cae74fa80998b6df3121..f0703e5b2746597119a8078e156c9378e31440e5 100644 GIT binary patch literal 381916 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

    tECHMb9!1nke{`AFr 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%0AE7@;dbUDb>q-R;JV&2XcD- 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

    kv?OF9>HmV*{0BKl<7H2QpYZ0QO#eVOua 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 literal 54526 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 DC9cSe diff --git a/data/images/spotify-logo.svg b/data/images/spotify-logo.svg index de71bdbc25..e85d87c1ec 100644 --- a/data/images/spotify-logo.svg +++ b/data/images/spotify-logo.svg @@ -1,43 +1,29 @@ - - spotify-logo + + Slice 1 Created with Sketch (http://www.bohemiancoding.com/sketch) - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - - - - - - - - + + + + + + + + + + + diff --git a/data/images/spotify-sourceicon.png b/data/images/spotify-sourceicon.png index bd36eacf95514ca9b2451e632c3f8d0a5c1c6576..8d147f4fb5df7edaa47e2ad598dc864018508cc1 100644 GIT binary patch literal 381916 zcmYIvbySpZwDk-OJ;cymQc8nJmwVsZ1j`2{kE~_f7qsK z%8CHg-Ct(Y&v^6`JVzB>X8?eJ?Cu8yq@+=!AL4vg)lkA&z$T)=$NCniX8-^&15_2| z-?-22HF^BUHs!K$xj?5o5m|h&Lnz0;h24i;8)ysAfRQIc#1leNRif<0PMOFbzqc+tbHdfO+gl&HLr-FB`<(ZRM};P}6h7G!Dw(Rpk|tmS`IrHuThI8n zs`4+3zqlcKydX5mUz1~#A+c%`my)g9`M(Ex!>ITdy)I#NOBBMGEI}ksaao!k?4U$8 z{f3YPa?ApSy5{)I0F&Nk4w2TKK363Z%b3sl3@uA7d>Ddc)<}lQxy`uC>$T&h1v@7m zS{aM|npC?)NJf^UH!G`U0XrvSb9Yj#fvGNS)J{~rCMK+Y-#$?!rZq_)hsYkuEe-ePB zXel58jZh%Yo2;=JNHd2`*_aCE*wxv`*#O7Xd z9zwd6?_>Y&@#8o6^WAw!{DBbNKznKHg{1y-EB*-L+Mwu6F`0 zU@DE`0F~fl{3L$fii^Ls6gv6Eu`L_p)OCHsy5`d^m>D}1HwIU9m&$Q_mca27!BB1% z?jyURC}FCM05TjEP-ciuv01!En>e6~+KNvmoVv`<@S{K-k<(a?v;!@s6QSFo6u62O zR0HHv1Y1Iw3u0FS)l0t|>DRvY;)+Dt@C}+1SNd}P%O4H3=lEOyPH*i`-D#=N zfx%{W&Vq~AZb>R)5d%-0(yRYm3Gel_Qpj|`Jsn*A1^Be`J6yKGep9wkPhnx9yT4zv zovrtrk_REr^i8mh?MU@CQK-q!=6U?lR)%af@ z^6Ptv(6JJIgeJE_M~sjzAZH5sFDm42c9P&=7E)kZM{%;~dx?DcLTnWVxT_3q|`fIbtW_5JHg z3O5HY%vE;^2-Hlm@L{?BrtQJ~JAa9`1O2tI&E&fGT=`+29!Pm1nPp7{)^bw6`WtbWC;K{ zd1#~to6+)e3=#YZ&&-g1;nX}BM@N_x;LO)VL-J=>nC^8(Q~LZHf%A!XW%zB6nVmiq zP;cY{eqp}D?3A~_cQQ~W7a6kqQ%pp8@Ew3VNA)@h009$G+B!p%HM|5Ro9JFwo}>CD-W`Z+Ug09&}oYCJfHN<8_m#t^WPmJ2Yn8UbzJ4 zeR)SyaCk081IqE4$qTxiu81!3utrh$xY44b?9c9o>>)E-Sf7Idlq}svj%0GFM(5TG z7z)zn^EMxkF>o`Bx5fTAQ6@lDt1%)Z+u3?C3b*2GYB6a-W`${?z-h(1UyF3X1Y zTD7GHpRNi|iC`F%eiIyTI)Bj{<%W^x(Vvn$=w@m;#x0POT57X#u0?%e>jfBNy*iim za^JO*{W!%OqeQ0r;Y6Rxki4U@?Z>3hJ>&$j?Y{XR^r3TvB=res`WorQZS8UjA_`Cw zO9m~5Ey1VS&7pJE5d>2i?SoewL&gXh^~R)c6zZ>c$yzK_Oo60GjU$H|junZSC{|f4w-250Zb59$E@0vbM3JLX-V~5(QAuF!E5%%WFoU6s+cFK|Pt zO?QfX+Ql(h_oiL>tAD@PTl8dlvdiN-+(?@0LWewdkd->!B&ek!N>BbDKrYMv0ex}b zjW#jxvyf~O<_>g)`fURVkyX3_(>;rY4=1-T`wzpHTG;xgT3kP^I|X9gHIm%}Y`#Tz zi3c%M#&r0JiRM9nr9sTt4lEC_g@f|})%epKd-I;S!u5obHH_^He1MYY3XXwjIL(^LXnfNZ}VIfD~WyU1n!NIsnkfTXqOYfbeE z)L;%bZlKyhSWSJQA|JR%t$bqm|5WH%Q_v2po{I;sc@al088W))@GGr}3o~S7jNZ*= zk;wMCTUY;AM*DTa@apd!af4bUPw~81a6!#*tHq={$KX>BVO4q6&*UQylj6@^uItBr zmEQ)g6ELqfQbZO6?><{5=*P&%5SAmgp-Bx;rI(s!sCJkQo!R!87IBuX*4*zhtSD(y zwsm5=GVc1tsLj(ZzxV6T07Y5$E%|R91nr7>h4-E{Y zBNO=|Jl;n%D1EzxAGm8~t{)}UPTJ2UyM-BoRnANBG*i~O(7 z_xt}P{3G)aevG8UlyD+*rLxXy`GD+i8jp-0#~L@eJZknl1sm0WqVKPHvGS*i>gc5E zur2&)_M8Bfhy3u4wUZM6H(e4GJrtJ}>dg0gnha_~expwMZj3d}`gln09dja(RPSMF ze5_Gs@lTl)87G!3YU@|GkI99wpwl~AZAL4dZXat;~kzbkaE(K$rr!&VS~a1 z-|h=#V4WT7-y4*n(kkjg{#k|E!0r4XRU8o&?6deHF?2_5sAyJ3#=?E+0ClSn)%ipSXNs!5rc~J(27(RZisQ5CF zfR)7{dyp`%?nuzc3_#$m2w^8_E?VTo+3S?yqj}6q26kgIzn6F5(BjAO~vSzJ`oxpyW%U$-uEdui<9z7a2L zJ;fBP&fs5k@tEVE060lV{GYVEF?uQjPvch&VymE6o+vdZ5!QwBpt@(SZqKb8FK#Nt z{1EV3cLa`3oyTtnrN8AVjcUFQLes=%LmOrudLRXXqmnzTXBF#oMPb z5^r)>(pnPYSHD9qRdDFXE7xgY!!x9=2%vXUrV6&Gm@44Ue$*BK-xF4)bg~hz!x@j#pKIk@1 zZ{MxG?A*@I+r16ddiuD`yEkH)kS^@|mQ@EI>!}Q^F|lGH#N?5AjU^1~$+AG;O(e$< zpkZBI#D?LW-Xf0mx%`07IMGM}`Oy_AK_L3|6^nEoedFyyf6DU=nhYDk`>Km9z$9XuYQ&t3d#Ty`(^cyv2a7kfdyBV^_L4oMU5DvO z(@pM=68FFGAv9A!^O(kq^BI90Y*Q?5)ib;8<#gQCm_`-(sR3N{P#!Ft)^JnU;J5}C z^Fnx}wIIPmJhH($4($Sjzr=uVx_z!Wu%cT&0%fdFDUEBQ%yq|YVZ)lonDqOEj0Lq` z4D%NPu7mXL>AWRv2a*RpEz_QUk2B6yHM@Tc5Lrr3(#`BH`abd)rY&&^w1BZ$^~^ot zd=uCP#?-uvQ{A#x6?k7bWHnIL2*(s%)+kJQ=3!h#m}!z|?f5DH@MkqcjujV=nSh1U zn{k7Zr5;ex5E0TeDIlV~PadQ&xw3$1#>~=6sH;4)40VFwG zK%aMaF&C=Us#jLVW%>mC565XM6ppBeMI_@#DIFZL=p{ zer%0mI7L_^MPM>8rSJ#o%OMM2#?Sswwz&uTCVz8fmYiOh4Dg2hCY^5Dhw)3;J|eN& zzIx0sXo|WJH5*QtGF_U)IZ8E_7CPU#BZbs&-whtlQSV7yK62&?~j51;7WZFpQBHRc=bgC=*9IzFJ-^To%*uWI87+4=9%TpOozCnE-elhGgJ~iVlr&-^Qm)Y;;I~L^_bCM8#f|-rnW}*~lztbW?T{!>BRA{B2hT;lK zLt$E%e7ezQDD!Of#;@sC@wS88FECL%y}N%Zy>0O7IoHW1!C5jE6{cWbRsr)taL$qE z$ln`^kvaTL4~(-~s_jA@xuq?P%kRP`S7zf=nr*w1nkG~>-l5D-)5uvUGW;2{La2x4 zG?N3{mjoUkSHGc!v-Z5MDyntv_Ytc_$nPz=f^e*eZK&CWP3u*9c{CkN_{?{-%zLc0 z#5nIF{{AfO{~}K2s8VJ7D;u$#lhKtBOzeHW(^n9^_Ty`h)4^;-%#kZWdn{gKqIe@6 zEU8s1G_PI#mD7R)9kim;l)>o1^PorH+qEVA3rfJw_*)C2nn0iIEU3}q`GRj@IKTyI znJJz}4QiGNRKyofj8bOB#|fnl!NnE~hJjbv=D}Z9G8zTL@LcVsg97KGOiy?b5_? zf+2zB_$m)h6=~dUD95xcCSmb|Q)Vlc%b?r$(UaC?5*C^YMbBu?o}7lz0ebn;fpK#H0NJdV(C{h3)mGLJU5om);NGnsp~d_T@2E;-{L$~(Z2baHjy37EoM{-f$!me}hC_!gnvt(mAl^r<>v&;U)k|t~ z8%45`b`Prl$iK0J#~|xp_g~YW-Ja1V{yi`IjdhV<(vrQY5aQK5H;Ah$9X>O7X@c6r z6sz5Tk9HA+U2SLiHP$@qN1r8l?o{x-@BUD6T^aUyw=s5x@=g<|cpNkQLy@xv=n-w1 z2;=B*7!9wI)L6i$^1MZOT-m(=h zNMH#EGl#V46LmVutdXidtIhrxN`%13RZ7B7-RAMFpeJ+K99;Tj&80o zK0ns>@f31l9iV>$6!V|8v~4nzycm`V!kk+QYMP6pU#jy!DG0EhZ%ux;9h+>|v$`%XWi7=t7S5JY?0ZLdKO%&qL%s^ZybCUl2 z8^?9CcumU4T>9rqu$K;9wN7U30+=_`KU=pt>qKFib9MQNfVj z)xk%E$ks>r;G1-SF={}vh93TQ{`m%p$xF&o=yg=`Msp4ihPy(NmAeZmI$yr0{I4#% zF@DNrGGqnPn`iq7;Ef)EJx|faHJ9atrs;?uyfE^i=jUbWQA=dRxpU+4<-H+EiUKg8 zV1}Lj%Tim&ch!iE7V11BX41d7(;ei!&zvqAaMSj^SLT|279KvxkwgV{LyFoz&J>pY zXT2>;_gv25@;q<)X*b~Qv64#1aSD+1VH#|ubLs3&Q1*`fzq&Yy_?PcDMsjSFd?dj z@Z}{Ma_OsVeWt3L_a|FHZI3cnpb zRD4|#WYGFo`pEQm1r?JMXNgSICcjibLCp|Lf&?qqGCk>WXC3t-k0{YxK33MF2l)>| zA_8Z~nTI%=U<5Q@b=Q*QDU}e7$rzq<9mTDfsis#q-}S+-JYq-iFPmxzzw}*=bohc< zbAOusdX(n8q6|d)sf|7aAV(rrd|`i8Fa*oss`O^lJC0LtoTl2L8RzoA7w6!c_!qcq z%WFGTw134QGRjU3HeH$I`&Xm?H8f`5%dZ%uHc6JFe%vvqWpv7TRO>q`mYeW7t5qf= zchJvbsSqZEt4HcsLmv8+%pu87!WWDbRf9eqTBPG{^8IZn5D!yaK_o!RXjx;ZR^eo$ zsX?dZg)g7+#6&i18GJ^aOqQ(iwZ6DrU&{4?Ra)CT^L)GMpriDHfa+n9XJN0eUria? zX|r$8S?B8S%s(?zT2{t(d0XVTKmoG!zY>!F3sdo(tvt4ZGJ`)>Nof_6! zT>$4>PJ6`U?gixhI{E#3GGEZV#7~OXvKu3%z_-K2~Khu^)n4eBe^Dj@m ztjFdf2gJw!hM&=!knff#4I9@WM~e=t7pb>IUhLGgY+fuKz0v4#r7sJ9Gclz-DsKMT zj+@Bd54U9Y*!8wsm?_QY>YJ+_W8}b@R%-`oYT0D=RCuW8P0$1>NO|!3GWmT%x-Q8Qs2>=`kYM2f?&^PPVFSxn0|ns_2pi=hlr8SrRMVTWR1!200Lt2 zqLGkhgV_^YYI!UP=|jNd#9x)>wKgVe<~pQ^Dm8LA+}C)_d7ZDteP1r@e<(AU-LFDa z4v75;6zA9g#pT}2)L7s<91ZutTB8^OhX!?dpi*ut5Vw;ZCn>k%ew{ieBFf-w$4Pf} z#veR!>W~TPr zNn4}hm?ZNp>TKNUqKao#ryr*ojx$uB>%LaJBSe$N-g5V9OSfdq+qskp->(`%#|EDY z=I&1zWaLx_j05_6BAe`A?)lq%Io{{hURN9}8mvDrnC#x>3Mg0Y)vSmw$` zF(~7_`<9Kv(J?8idbQc`u-&HtNrXJlXm-BHKn579ZVdil58sHg+!$ughbIp&eZhua zk01Pq{dH%L#<)=HzP=}RlPjQ%*+x&b1hzS;tLA*|#{=w)rA#S_lE_5V#_3dg%dLht zs@)hHG9j;+3VK`CeaInb^4f9Lq&6~Tys>lzJaP+e`$YA)k68Y&mpe|>)c=>de_bJK zszvpGCZA{4^zbw5{pqxa)v$bCrhlQJJ{fs(!W~TJ4V9+9TY_ z&9H$^h~wXt`z}v&?mZY6cHqfyXNJmGENb4tsy{c|#J5eTWO>!ErHm>3TJKpbk6ceEC&cPytj-OgL%0 zJrO~tiHA?vH)9_R*yytxE#xJtTQ9{r(}dLwcy*5wQen} zvpx5;1Ly^!`QuRCQl7Ju#1#7lpCk5Lf8k*CFc~bOff$V!=Y^9eP?tK-*&J;w9Cr9G zIP@HBBk(i&?6(`${uOw}sK~!A5=2%Omc(+(bv4WE4aq)b5T3uySzCF3_BhPA&gGHs z!RiC|oB2e?l?`FX;;7!-CbGgpR*FtID>s|v@EquBo2u)PRrs!neaNpN7TAnL#gd?EaxCH8oNnuOUrtfyC!>*p$zQz)K8_(LA^=uj3oP}_^evfS^kOJ3mV%N~E8Oqs zea?mnD_W#>4jWyeju6hqXd|G7AQ6I1-bQW+)q&>qY`3ud)`4+7NS^99R{Qc?PIJ2} z0D!1|Kcs!}68w1{Jw!WV<$TbQHA^S=*3cMX-c40+35#+dW6QMKESyYjj;v0QnHy=` zCm?qzPZ*FLEaHUamT1b4EM-NRf~pN9R{e zS>Qe475W`6rg%U=q6?)dBtv+NC!)RdxkToovq#pR-g#n=%xS2%HvzdxLePMdeeQ{9wQi;9lCNSv+VJ( z$Wg?%Nu9!ls->GnBUskEnz4ABmwD!m}2>ebv3_`}bFnTnfMbL2NI1(eKLJ zr)~_rRI4zuhsEwS>-oslUIH zXL@8fg;8gie!l0y1jaq5RE+XGQ0$CP6%wE^0TGpkFB{8LYbQKU6dzT6u;x^`VhZ_d zXH*7FpL(Zs+u0+p@)P=kBiV^$q@l~CPh{~xdI5A{RBbpkRKgT}dqK%BF2Y07L1OVS zKxdj?o+lhDDNv8FWFGNuzGaQ!%F6h*uP6hNXQ;M;iU~-UajFh8IgP9HxNjSR_)Gr+ ze~=myh#z~Ekt-zrRCl;@QOSgLspV?@s;+;j6Ae@m`o|WJ%QpsOf$3K%us{NQt0x3a zEWgFh5}ZVJCO!Bu6i-b-+rFaBo|PCkT#7rs_~cP~icEgMZ+K}rN}QgDp>I1Y=g#L< zrI@3)o+9?k$<~ZXbRQn#6xv@^}@i?m_&c0uIvYgguBXeOl%$AX`r*d zVF7-Q%w24z^oS*2oh4o!SIDRkk8Xt2#GlK{l;Tw9QRI~~=GMSv$ZUqGuO6Q6wyqb| zv0ngMqMAyiw9y^hs5TxKU#?tQ9@vC!qvgHFBI3bayd?jCQpM7wD^3Ars zmA$OjHuu%!FTY4M3CCP*H-jSs9kKKLh9vd5-V_pz;bH_A_6a#ikeOutHtp|m`%VW8=_OB)hYsY2uH*oeJJL0p-?WKIZVwa|a}%V2Tg9T{G+hxR&Dvok|##chCC7)nbWKMn@^oXF(TqYojKsz z`#4`W&G&V~<+|q)rIb9Kq1@r+hbI!VZikuB#q(24gC(SJ+k{$ndW}eL?Xmz1hNq?R z@1|d|OJ1~IF_3wUl}>q$tnEk#B?gQzrqhXbDZVPlA6GwrchV5qqPckk+~;ax3BEkQ zN#z*drFaK3f)Vro(jHSIE34M3R3B)fmYozBj*=|wQk=I+xW1%gBbE1FJ&8Y%+(gmK z*4~O3=o>!M$gXBwFV+CaLV03lsER68KD_sT_v|N~Io6((P4J5nDtoo5CZN|bp!n>T z+;Y5r#2oL|Mqg8O{@SLRE4zBd~F+HlYS_^5Q~Fh9!nqXx2NhersaP zvoiHFWvDBP%gApE$I0ZPG)#_Covq9>wv+yuA~@uSS+=wDw&9G3&DTn~hdOC@79@>S z^S=Q(SRn8wJA;r#`VH#8&IER{O8n*5&4YN9F4vR%U78sv0`JRp{`c1xY9!+T?a=(p z0}$)G6sbv)r-e#qF$25ng;>uiEJArQFS*?>%|?ZAm8PulX-)|~m=SEfY=f~sS-Ga$ zT{PUgeJ36T?ppnE6qV@a+u!iUd{ASvyDsk4To*7*ae4j`nhN#oUV4+1%pqj<<{PyK z+l#KuvT?ILUI}-BH+Bnjz+pFDZGA5+A`t_UX9X-uSa3C|lk5lPALW~>67%IsV}|j z7f3DdH^MCicn9Bns7so5o31jNkC69(sjnCJ!G6fJZ%;1H;-G1a1u87#tGJ%_yCG-6?nZe1AKY=H&WLk+Bg`lOmV7gnc6fS zv$am0|NEI+9%1k*)W;$K5PO?}E54=i@X83mfH*_zg1j6^nc45_F z$Lbw6IpD6@Hm~vDqp1pt>;o5W3ieOdPam&q;gd7`%MDaA(bH^K^mHaKakmh|FUr{g z6*D$0r!x-=OQ8l`*;&e2*-C}fd;QNI#9X{mEcsUZs+w8yT1jTA1`rjY|8u88_+vih z@@e|A)7Olwfc*y()o$r>XXA7MT+G&;nmDk0oqdFzt%RnF_;QN$c<6^5ds(#Ja0KN)3^{ zXXRG>!i?#Ygz+(b`b}5!UxKZ-`UY>fGpQ)V9%gDj$r&;kMtz>J%7b4NKwRvW1?{&s zTUNnmuBSS6jN4c?^m$mYn&nU;^Yk~b6-m%?=GS){{HnhbwAC7i9jO-xav4OTY!$-I zZ?gD`brZe&sqoU-@_v~M(PQ;b`ZtT$oz(hl&Ql$IOKb_M?@S^MPR3-$ee%Q6QQ+70 z@|4=T<0^VxAgqliVEWM;dT=c9mo7qjs37_M3=IHv)_3t&&{dKv@OU-&mgB0N9}nsYJ_1>Cwya(TIToPV}s) z6S_B>=Q239m$RsYQ)S3>cLq9n!^}G+Y;O3%(fWu}HUD$2DJ^(4X9m4drt^5Z5n#%Z zKt4^{l@sRJ`3^mlZ? z?(22(T#+j1v-NksHn2?nf9M$kAvW1n2B83?gk9F>fPa4NCyIKrSOy1gr@T%6?bKgm zLSun~-nGWc5T*?9+0*2WweegHavS3!WIf%=Xe9D#N-~FXS(!gG+HJTZ;&7?TX33~$ zUcP2&3H!6{TqQXA?Dksuz1*WTFTc|oDhu79=O}mKKhanR{llzGW zEH&sPw~F*$71F)Y{&&~kUN}Xt<3?fsn$xq>^JcCE#A0O>*rSCQqBB%;Dnp_v=;x3R zM0CwY0LH>P(rGn5P6ZqUwdRnlMeH=&fEM4iHJB2g-mcNz0`&KF^=c;int!MRKa6AC z)DrydisEC!|C?hm?*RSs&ZMN-jYvVxL9}=yvP?lz)?(P0caHnurLhO}Fi{FQ$DA#| zxm^}c!z@B=DkfpiI>}aP*|3t`;qON29xDVfaJg}K|y6Em%DU%_5ahwh$Hlx z>lLKD;E(!eo{0Cr@I2(7bTdcF}b;*J)ScmL${J-&odBcZS=6TxA-#dPzgRhS2 zy8e}iYhALRDvP7`!)MgOGa*@?q-}STPd|$s+TLfEr-6CYep7JZ$;sHfYjuQ!F4Mt| zG+Yn?Nh%dq;n4lwIo&(s|GWO=fGnA5vg2^wz6ZZUWzh<12Z2^lshl#QX#WJ`3D!&g z^bVSAl7M=W50B^#aMN>s{~Z2=wb0$U6sV>36)-y7<8{XD;A7mf|M&w$ist6zSIq^F zT2z4^GpBjlLt3VD*&D*kgptw%6w%ICnFI{a(EWtb1e@p@dV)g5UhSZtlWBf&#f&s0 z@TWQ9>5Qo!HO{FRT0MJbGL7ep(Q9sghX~;egGxGOjk8GTAdE)%ZYj-H#l`=Q8pOCr z4SGNfK_OVjjOnCB^AZ&|SKI8~oAbuYAq|jDEgWG5HVkJBDjE>B$Z^|;Frf)@3{em; zdp_IN5Nioyo_YB|>vDF|qnc-bQIrP5Gl#e8r`viCW+gidU2x-7ULpAA<=5=uXJ`99 zU_ObBP5TNo#-E+bl>dR-j0G^Bu$KOvY!Rv6q{RSaiZHv%4U$L(KFth+^pk^}+*{r| z+u<1bZF(IaAv5Y5eLN5OA~w+k;Ynfny$g!Abe5kUkC4EAXNTXmf9+ZA(L_8%a~f^O zRW0Fjr79u$7BZYFT5>^pRsi1vo1mxJ@0wb5Ha6zMn^a5rLtGwYy3HZ~>Qq+#Np)Ym z9rDf=B=0RK4`e12_l}=eKxY|b1S(V6Q2FiK$ z9_*0&Hl$5CM|0p34xTzVBzp%&Kz`uiV$xe#G-WSe@M;74=bCrRdd_aHP4&4O5IUXC zm57oZg#O_!Dj$BhB{lBlgRMjmsm;gP=ROZdp_%9JX8Bki+xn6RH;2X&%jeE$6U50K zMbCIebqKRHdIJ(17+IiOkcHfVO3UWtpP!vP;wsL}cT&*~p?PQK^y<9OV$m~z_DTU_ z*(3;J&5l;oVZqrfv~(sJ?(Zy7E4i{d$e~~qRieYU5N2}ZW2G;(1poo>YE7kSTDKiCMM4(~ls(s{ zroyn!E_ztZTnLl#Tx}NvMfZI9kyEnBF5Vtw_(Bl-S4$J!Hg6*y>OPeU%rD_uJZLR0 zB)IE$5AWTvN?|vdHZM(1`9Kq_}1 zw`=v>##zk}`^D?jJ@DtyY>fKUtwn)+r17K&Q!wz&jHzc$HP@r;XahNi(nqP8HE^hb*Sm(Z)`mK7|XF<6Wl9Nr23FZfY9v4R~y($wXt-B ze{IMwT!Eiq6I2g_$-GgkeyzlO-dH7bNbX~|L!`jKlEZ&yJ&Mtc!GX#B0~=0aLEqhG z%FqrD74puF`Z8AeuLqb(X*^6|PO1oK@rgFm|qa z3_d9zf`89|W$&WA1s#2?N~3SQ&txT{6RXrp);uJ=5`8xk+uTY!uPX+tf0K{V_2}&< z{Y|+r&JsZL zomQnIApC)2tJ-8(TxzB}zly<{(^Z=%bCV%j2W2Ynnz}#9iKxs1yLgO!;NFLm?T=#g zH1idsa@Smbq>cG17OXCb=)J7ndi})1?(N3;#1-GZC!)kkFS=&wIIG6VGE6uf?7P=A zfTgsVHgES%(9g13H# z@T)!4?fV9MIk4^8+J4APa$k7i&_A_@&=|1A>_|=_jBPWv z*wW%D-j;*ulP^7rJ^aLhVx(UzV-TT5aKV&#YM4VgQm}w3w=2D^n?X<8xIL77ahPmE z-t6b(K@CfXqkllt7rgyXN38b+2GqJsvTi+pD=zbdXsT*a?+D`8b;51z=lVgD%qB!Z2`Az6)9{e;tN( z(vHYc1~$f%#2L0!+7v8awit8ciT;P2(1bUXZKDurO93>0#CNA|FX(~^F4(ff_nEY3 z9VXM45)h*iyi)kD&Qc0JQ46gPf_BUNDk1LdsGGeBtEQa`n*3HH|1PytWWUZCY6C&0 zXzIRT$kgih>2TO|b6lGACRj%$>In6NePztW;>JytyI+6p5Pdk6;A6sjFr?5B90b02 zMxCgIO)8LhD-D^aVm?_~lp^z6|I4&Glh_ibV}_f{QcW9hKy4Z!rp!#20G%emDAXM< zYR~Zs0G&ypN8peVoOn=WrLS-jAWQu4+h<_YfQ@wwl zZROT}%KqjJucvqMD#Y{+hGz-mb};-e2yd&I*F0}ZsTdk10TM}~VTCjv3|+3;;rKx+ zQZH0kLqx;Zk*|r$^Gr`E_l#mFrhqRYhr7yx=!U7n{y%4VTZ-QBEPUG+2hRBmF~obu z4RHLlUUs$1DFdS_mWk>WPg2sd3o@sS2$0Z;@405iP{fLp-*uu)%WMG6Ti_h+8}Z3Q zx2ohm)T|wHu)X=|v;hl5=bQ4>u$OdmTKw8gKeMs^XMsoFL~a4)8ZAo z2TEF>c+Kt-$x1ZDNJO)Z3|I1}Ia>b!JaihQSmc>ZV1A{|EuCbO>moP-^Jn&rJi7;kj9@m+gk}gub zGOe&?V}s5i`m}MmX>QYN#x*@g=+Uu(msEH5{*2mLiJAAcO%_Pba15h^azwQZY4A9( zDNiZ7<7yG`YjyU3Gkw=$?-S;sYBZcVFcRLx==>7m6DR#gk_lF++wdhX8V014PiuPv z5c3=Q#_}`>QQ^=%XLNijR(dYqH(QI#+>@ZVxxw>d=kMb=;8P_MIxHeIUwd2VC*Feg zF@Ko{=myO;GMi&R1YlBPRYN=5*Z=-DI~IN6i_Om`i#`2VkubMFZV%H9qShqzjXDj8 z@1JyC`%4jM+B))i--!x$HdXO`vH`GZVRH&U^N(NZe~ha(#_wAarWuv6!bV}69(E!h zP@^Npt7%WTE!$qrEBN@lmXe_!Fl&C_1|f8OYrbS{=7aUs$0w0txbEd(p3wC1#-Qkr zOH{<>(%uEkJf}vMh{gl;giQ9C+MeHw9v{Co1>kts%A%d}Q8lJ)G$zXfX23GWIwy7@QXh4|X1x`QWiD&r= zdM#)`YyN{MnLV5slff`t@Pl#tbqvPUEqLrg_wt*JXgY?c-v>>EsTppG2jfQ5MaB^C zzJz_j7x3>$z8I<>g%^j7-%OQ^oOwSedZc52T!?5IQzIRbY%0-?K8q#TvTOdSuATip z2(=I;Ae71Hk`bI9wMlF5{fK@wL@geV$X+slN&Kh+y|nvYH|T@u+8_7!(bOiu%8D{R zJsK+>h5L!!zbVTIYxmdfR#;87pXPnmx-{21zpiPGub9exgrLo^Fo2VzDUu8Vr4GA2 zt<`#+N3CuurvCo3_$O;zz4YNau!-nP0Vq|~;zdz(tMnY>{v=gk<&*$=)C;?{Chn2l z{;OO1_Y!aaxbb4@@-$dm2vVW&9w=?)KF}baWHlOn?#x?bvzDzC5o)*p=kR!aP)TW1 z${7o;>@VIz?{~Ix?l9%b1bQW`BmESFX-*;7$*GbU=0mJTGX@L zFf4&cf3bL%{MbZMTOAgkPz7#U*-eXYHTZwosUSs9U8o!=04s37>S?cQhR#1rTkfoDy=SdO{93oM^fr(9 z$^bk;o93)F!ME~O=56@;zVH-w1=F^O4nI4Ef8V&jn9+fLrJ@vc=RJ_`pHZo-o`#wUF_tWG` zW2=AH;FO@k7#l1SATB(0GZPbJh${`yu?0MoK!O2b) zYuG@QFecLl`fQmQoshlx^J??Q2t^7V(+`?fiRn!>hS7*xnbGXQGzX-Xp362T<&pi>_{Nh%?zy#RUj~1C%}XVJL%L$2jwhgWT+P~aJNSF zxaxuMG}>pVH2j~_p&d=WR$Jg|#ZVVG!AI(ehpp>Ne-Gi-7sXPR#MaJvjR zl3Yn(z@qD(PdFsCTxmHrT1!(saFWv5CO4k)BRF`ls{LX|n?A3Yzajmi=JnjAo_Wat za^m7VfM&Vgzql4#V)1E>Y$*$`d>M9Nb9KaVV)#2AwuB{0Yl`}#IukIqcZ&3Jv$mLIuw zG*J7Par{HPqR|-GaEvrrc|Ee4ee_lA7^6N)$&Y$zQE#NZJ6}e&YnHzXH`ZyJIB?(3 zqsc*x)l(MZ|IzeS0c|i{)3`jiyIW~-io3hC6e#WvMT@(;6nAT(6n7`MYmq>a;O-DS zF|_1TIZ99X%O{wr1}w%XCN*Q9wDVGx7Ir-BHBULS)Zc{2gX zamOw-#2eZpY4Xu#aFmlsct|BEykROWwjWIB=IaJ;l&kcG(bp|FiZb8!>NYI6{J6^> zBMLWK^*q0aO^0jp7SdeMXND7!U)`{UD-&Mk4o!LvHGbV*t%QE)$g$vOg?gh_U~$9j zgGqj}jOMtLlS||hZ1opmE$uDLx;AdF?3h|@(^%a}@o1VLaIM)hV8tX=$lWfNRJHCW zEG%jXv-b+CIi&w>%yU-8zh*`~RrXRK3T<*S!@01R9TIrKpNmm`!rB?h(%V1R-II^ISlI}J zTOpkrNiZ09Uv57l;2Wr4w#lFA97<+usI#r}(G-Qz$KPfF3jg-36P^AlIt{ti|0HwU zx$A+||6q@^H>P!(D=whU{J+nX)IW%#Rr?0%csS`rT>DyvoEN3c$Yl)gl}+sj2$siZ zuX6_7;WCKcvM9oICi#o6&rYjl{hFNuyqPF-vet^78)DPIt}3xS0^kTA?-AKP;jDn$ zGbS9>%zj*4z}_#Sh=nc?O5a|S z)M%Wb04pIG#KKUy5y1mPCU>_BibIQlx&7`iv|}q}6iC$$3Kc84)7q=S`A?^z-1}F; zKpXIZ7o|w}yTFQ5iQS0X(~kBE*K}e|n9IWbfgheOyh>L#0FJi9_M1LAC&FjUS61s} zSZWlm!iR1>5A75fsAAo7*P;Ogz5c;5ft-*?MEFA94#JgdaT!E-;>}E8)`+MX?!_LQ zfGYK5|Gb(UdFcaen)G2g?~#pXSF9d@Qj1aN{UdOjcDo%LdQUHk%TocL=m7x;p4C?F zcAR*e-HG4rnw0OnFHchlJiihQuN^4R7}?89IuWh`38`YWeE`G)#IIPp4%VVLf_4UN zwoRVd*US(mr3i5&`UZpLqLqgM^1bNa{z}mu{j@Q#VD1Upg#)YJ&*T+vQ6l=wf_-{i z-Wa$_#1e!?=ltic=iwNM{_*NeINO{GMwDpYEO{=^dv-&oOKpq&;1)vxfvc_kL#O7hb@;<-g!u z;`qHVQn`e07;-c32q;wp0mjx)RA-(whea?cHJ8Swx?QFd_^o7axQl|gUKklh~%q`pVtTv>Lm`fh0cF;DvB+I zwjXybkvdwOmdOiio2_GVrwur>YxF)8|LhewdXrQeTIm6qo4V-8<xW29qMb&$t*l1_hSe?)FiIwVIl(- z6YAq0TAH%;InVNYVcX01fSVs0I}z#5(p*@s+^Sgie1rP;a@n|#@l>+2pGeXA3qP-# z{s6(*p?y@`f@@XUJ+1fFy?{S+2?LAvm1dri5KL3~vO_9}nvy8&K@LFAFTobEZmagQ zHwNDoPuu3=Hl=X}%-is%`vm7X#EASLZCCzNt-wxMB^Y-6{@9a3M=OiWo)|XgS)v`ydcJbUwedQhwB6eh z^@%n(8}Jp^9?g}Q7Pi4ZYyXnbWUz3*Oh#f1ED@haETdeCwme=ve+Og{k0_rBKxgJ) z$F<+4JvObO3yHQXdy^xrEyIQ1N`xb9b(Wys^p?5co+&SrXmkHet8j!@k=Sa+>7$uo z6;boZA(98M9?~E!|9rPNd3C4D=q!f5YjsP)Gr1w|vzrhHG1NFa6aVh_uEK)~V@ZD& z6SyI#Vy_`k@~-RD@wSKE7Y1FeE9xmQ7>(X=pgFcKWu%9N!d}NZPojQ#1G63p;0OJOg^>xR(N?0b|NF~o^vV%o(>4Ty?)9big2d1#D8uS& zBdSFAH8@jz5sj?~m#>!$4y!Lpicz_7YSCW&a8}#aD8$xDlP7Q1tEh$4<=ZJQDLZ#> zwe;pGi;5c|LdP8`Wvkys8~WiC_0}V#T)PYRq?^wdTtZc2TN6+SEis^Bsn4K3Om(k` z{V1C8n`0+rg338}1e@KiaGXlO;J&=@tnI&=Iv(WPw||7aa<$ira8@asoaC8U+rx)+f!p-r%ef$&` z6Xj!s#Knmb%)U9NrE&%8y0u&TS?hr=NhC>Stca7YxMhl9Tawd?u6G@NI~Q{hMuagn zCG3o4s=`69U$(3lLu%qyKf5t$NG^+{u>G1O@ce%Iy<6!~FCb^Z?L5!@z8j*&imQ7reS7v`KW{^I$YX0bGnbX1ddgI1D^ zPy~^vksOD(0}GmKUoK6yhtdHNf*}=jI<#&OWQ^)!USw)40pJjHW)T}^ z0E)j~yPs%t-(SSTRPVnntXg6E-uVD!^y!M1Q-m!;+N_!hW^WlVQk60>7O?~c5%Q#4 zwxAJzW9k+;2K~YSa2oX7o-t9Xx$=7!c7D-jZ&izXdi}l z@al)53=%n>@J(&EP<1EJiICDpCd6b5N|J)VTC!4Rm4%UH52$^78oeuAx>K7j&whcz z9tB)r*VATsSQR_Yt5Y2h&lb4S()4Rop!{+Ru5d zjXRH<{`~$9=Yj|12Mg^q&WJysLgJ|WGHGI5ypCSJM;$%B8TP#F6jz{`pPym&`(qj9 z1mdb#O>K5SBNw-RRHBQzI`doZTC&uZz@Y+`-}~3(?djM+6+6gBNODkg*P5A6o-9PF zDO(F)VSybGlyt*q9-1Nn(Ks}d?iG)Y8E5l=zmt^xWL=($CRGW54KxfbHSp$y)M&mT zOy!%yj4tL0uu18Y#+YDFK9Op4pp(NJ;bDM8t_G@zyBy?R4Lu^l9ax1inidc=WCu_B z1pDJjXCuSov1m0`qkQQy6%oDhFEz6@MbWMLjqJqd;g{<#JkIbc6INGs({n|kBBnwT zANS)WC-(`jJss^Qw1-nBSpoZZ6ti7<#s~2`hH%zzyE4O+)^Yn6zt?=g7YlA!JMS|V zt-Ns`-RX_EW4wYLLc0mg)CtslYKF5~hC+jKgvX%vRZMxo`R zcceXgu_VM}>`3}VMuzEnpZ+fPP8!`KLb^EXR|qpb9@_~MHKnHKsrONE=Jdn36lkEj z(9iQ#mnKL|ooXY(kjFADY#(LRnrk#}lJ_mo5)Xb>0bB?I*`nFl|Ei7i+B<8mdqx@|Yg6XA@uMT(aRj zBvbe>+HV7jD4~4=oc@?SHxtH@4%Ane8PbkOZ?7*aCKW;2JVwQuZ(33dKOp@3&~M25 zlWRG=B=?PNVTe)t$?eD#xD|rB<_U`8dS`OLTw;|VFP&0UKo_AA?ml%LS31g3Q%4<8VcpTN(R_lVP$k=XX|&y*vJ$#LsT-1Qf|AE?}I9 zM_(|po0)V0jrlN{Z0rUSqfm|agTPK<;3HtDl%pp{DVP9ta3nEKvS~P5x>(ckU7Dos z8^YKM^26()8T*wC`UE++V8FT8VW^4EFoik*%F9A)h`W!1840hNh)E`iVvg2N7LGY+ zxj8AxXd(n{)?^6(NuKd3)%&|E!HO6jLibs#+QB_x!}JP|_nxHQcjnoZhA$2jk4@gy zolXhA2@dD&OGwS%rHM-1&$dbo+Z08vm;`1|8aAp7kKgGAF?_S0dBiE}oKENQR^g237EL;{;}L<*5lsJ=gl=8ODM%=Ws<4rMRN{u;CEtB)L#0Fg4m= zkRPVJx=*(SAjx@jw5f;)iAps(#(y_4`TuWXSptQWhz3J8=R^zK3aT@7;1TTg%8WfD z4p-7ODGY)qgKy|RBH^(@>ClVBlZL}IV-^|p*}z>`u~O1Td}u4K7=S0%Z~4JlrL0=S zY5%DexdcpJ0S@lG_zK-1KZ+T<=_yLblaEhSD{`)(0Pa#4_p`(c7Vw7@Ag69mbg1|$ zdLIJ?qZ_|G9$u;Zfh>A$+$}AaeRAr9k zJS|(WX&wFg1uI4W-S2$(cTq{l;4(R$N*-;l4jSfg*_RjPP=sY=cT>w>jW@oYP`Cg| zob^#cdt?uNdpD_1aZGBM%u8qAV32YSbRDr-V`FJ{I(S5le?vs42f4A=-#XvC4u88ICFh?#k_PySQe(q=(1N+8eWOOM_bTRGcZ>UTI}dJx z@0r*n=wtd2&fu9d*!_ zL?3U@3yaI%03alT8!kEyrF00&LOFxP;uh>p(`&qfT2X>tg{4FU8~LQPeJO&io52i6 zR(^(CoIFyVJ(V8go@s>k zEh_Ii_k7P)Cu$;U2b!F~y8)r&8~=f2;@Zb7fqfupV&?#SJX#?%k+z+Xq-}PZC#9E_ zG2rOQYbXbD$Q?W-suczlKs$qR`g`LT9+?QraC!v)9kv9Ee2o9WNvZGwV`#&l#|Bvp zc;vpGD6yCIe3Nv*K?|r)&;36}xqUJ`IW+x#j^40w*4-ip?5E)x*A1W`n|yCxiK@!z z)3arM`EOoN%w>VxSf@LT!*`VgegRtafssLmY&?p3FL&kJwmXJS()Jc{Im+Q};|`6o zs)p@l)ABgZKI=@m?@vyCJ9<EduvbR6d|oQ^LsVI&>dn8!)6?n zh~`OdiTG28;qcsiUv9i|9==4StCkyt8-B;&tT0hz46~?F zWqn-pMLwBRK3tW{AoH96*vvHWwBeu zNF9F{8XkaqfgmUd2if5sje-}J^1Hs4*yud$1RO7xi+kJ`hON+MhF0zPqxSnK71+Sj zbxDMXvzZa;wESqbjKe>iCPYl)N_ybhGkoHC_+y+akZ^PCq}*b)Ip~lVORK%S3M|mh z@8w9SrBR5G^vbuHr>-VAgh}CQy(iDtN>uesHp51Y#J%K8^5@uq#uP%nWlCV&hsk2A-CHhPG*#r zmv`3@j)4|Gc`TX0rO$_C%Ygb`S`^|+GmX@_S2tc({E6JE7&E!O!0f*WX>QT~L0>)Q zWy7`5oKyLR&Fj++l;m94eg1lJmVPn3Pc}kC9mSjn7XuE_?Z4ni)>V8xaq1|285b5i zd384QeM-*bM5Q8Ui~Yyr*Qu%Lr_d+!pzNg+{zr|~g0CAbpmHHjrr=hL2BH)9PiP*6 zLdc+U>F-HYCW-km)TxphbSm62cP3#MUl-7C7s(gLmp{FH#gmdpkVdh9Gkfdr%ewI< zUj~lY(&qyUB!wgZx!`fIu$L%PJc4c_lp6t;K%RMf*Etf-bUZV$%>_H_23{Bge>k302o0s(9X->@p0tD99SCq*@>-{zi6E#g zkj-;=G38H_v>8cuvuAY3m#m#AEtD+W$ia?@PYc!=lyt}hp5@4$b*;2u-WHoNxAdGh z&y}Z#Q2VK+;vL%F4IEz|R?bO8KILHRt>zt^WFJ;4pIRdIBhYc6yCYyE%k)liS;H8F z8*cbM?P>5hx9Hv#XYYw~(9w^e7fc~UI$YU1$tH|B=90gABky1F@BVn_YN8Cn!s$jB z*}BHHjBNP^+j0eu{m0U@w6kL~-4icEQ?)}7kN@;~5c?@ZZ%iN9cz7|e=YUj!^O6pv zMa|PUm*8oqu(U<^CHy+Q^=Wg@7>B$lV+XxP9sk-nF+g#yV&sW|SkP>btP12_r;n#t z>s1^2HlwBvd>k?6PQDvtAyx)|zczQ5^t=W&e#|Pvr!GEm04!N7%xVzvX2TD6_`65) z180u&^o|h?68<<=aVAw=;M+L~9==YvGi*-M$Wr6^f3G=dUydhDkTXhA@x!4Gfp0#B zS1@=-hZsgiJxsE_0_>vH4Lb6&UL-}BV7sNCUM4B&wkrEuK;C?m{D<1nR+{xQa#Pag zp~-MUKNVj$5a+0+kV2oQHy$6oHg4lg+(rUuq>Zh=;|61f3Z<)erl6_4DKGrmlT-Tq zxiwqzVxp&^y(bO60Ln8$H(Q=+*KJ(((}cs2A!^OGN*qB9S#WG4_w7;#97~fJ|9$IX zMJb|^dXaOxKmD+Vb??>ES;jM&l|qs3*OJPW?FAq^afoqxT=eJsi(+xvWJ+e)BH&C2 zU0dAcH;xb{f`|$7BOxBM-MlUIOn<6^n_bzXcWHjJNI`Dob<{4FTd?0vXG#O=(F}{86k{Xj%JJVrws@cyN^9 z2~-QhO4d&1>CN5`sUQ2|~b| zjsDLMqwCjLjV3||qm$|%jw23m8xglDNiK)Hk1c}WKyE{EJkr?#413UYy`zaXnZH-~ z3ZnH75$Q+cd1xb}6_MgRHo>Pje-1PXpGY1})}P+B9rUHrn(ZQ4noE9@u|-9oEvL-3 zgd>Y)CB5fDD7m=jK&HbLig%<}TH9Y0=(d0PomDN4A7TvcY;pJYM#vX9#J=aFbY+1( z!&1A5X~%A>#94qfdSJ&W7d>2d$pIb) z>ORuL|78KLfScJGFX*CMzss$cQR@sWi%G107lY?uRDfQ)>wh;MDSPgB zDX|PvD5cFNQn;yNFb$NTYEMX?iA8XhvI84We)h^+ICZ&Aw*~T-b$G}Bj9}_6+OXXVKi}*Kr8y;@__E zqiGraUsgbSzeHH8_FJ{iCYV5|*gN5R_W;Cn;eUI--Cg)N&<`nh{O|{v3`wY#9FLoa zG&Srq?P#y1jLYDFM}tiTlb{C+g8@1MPinHAWA+H0O0 zw5UTxkDqu5=lk1)S1L8S$u`!>7w_TfDvvHwE>-n&qB%0x8-_jIN<3*H7lCxoDL~t@5f=~B4R;293@%&B<+JrK8$`81Z)6uo*Tv7@vvItDibc9( zH?x?x1t0VLiFp=Uhg(zGK!rlz%if$WREqMXX z`vrQ?aANR9VXIDI9{%+Fmm&o&qh5U>8&_1i>bXdi7zf={Kx3n>wQ$Ew8-(5zrtf`@ zZ81ZLq($WwjX!?!L+L*m1#GlbAwizuy=nt(u&DSiPM%CJi{k>i^kPC zdS!*XcG+#4eK$>44(d>g5WHLG1T&Yk><%m=3FDYknA?A<;^thkrtHo=Moe8+Gzl%` zMEp||zNm>-n=i)UZ(pPvV~wZBj|cWy9g}n@(%rid+29AzJ-xbslR_uj2YoDhm!R*d z9W)O`@8dbApFQMmG~s;aNYL3L;>XhbfW4-3_f{>pCLEh_S5vw@Xc_^h7)_CXB?QJ8 z#QzZ~{-6R{*i)9$=<9mVR!S49-3QCeq8MB7#h-D%W#`9D=F!PaD!_ zvy>{jK>RG2Eh}q9b;F~)azG&gS$y;pzmfO9D%Q&4b{REXc$YTGYP(MRF*Vy}N38=> zS4V1_4-;$*+&8G}|h&vlck*fYY+O#ya<#o%Q?$MPUs}dWw}d z=xM1!7qVUW(SPoNe=wBx_B19XXOqUi``w=0>1`q6fy8>eM=quxK~9V@%kbC~jxBlV zgiJFX+AXfg$J!Y2rhqyc3-a1F4uSi!tN4}ah=)6tE?no%7w<@AO2oHsmx!Rc-}dBW znC+CwKf;R-oT5BuG7@-GZO?oA8+L@d=wa zE=qF(HuyPt$Gt75E{A7jf>0mV#P1mEJCg8%-h26Q84CvxqTlkb0-Xd_PP$a&iaHBK zjL0_5I|zdSE63TDg(-QGweLy6l~G@UhzFZ7#CLZ4?hd3g0$!8w`s3XunFcI4dQfKC zk8Li0#?`D82XHr_>fs*l>12Yv6_ox8n6^nc|2xMVykp^FXr5Es;E@12-=#A^3UulW zkTA&=O`L6DbwV#=%8lECK^rykWgYukRC(Wr$s6N8xXxE0m#N6=uha$Xr^Yw@3Fi-D zEZ&Rgk}JPZ&5uGmv#aL?f2dXBCn z;+>%lByLj47TK7e7}1DG4D)gk4$=P>qtZ({SZ%5wf;FJ%o>=f6ltrEDAoH51&7{4c z=L%@~&oM>!V4i5Y!5!8ENKTJPq%*9-H; zZ68UF-*nz2Ja+@zN@%Do2|b-D&Ue&0ShZ;?Sp1(BLZ#C%Ilhdzez`5Mck#OG9P(Lb zMaT`!G(DncRF=-Ns36#0?^!g3%-Ax7FTFNPpNstCjc7I|fmIP_j#)FL=SbTT@R$ef?!q;>Ain#xX1HxPAPJ7nE-@Tn+tp!`PB2C_X_6`-4bNZ)d@seYi-z@uU_)-K16q9SFTx zkD8q5o9e=Yw9{wOl98)fs34y{CK%T7Ev!#?hpupAlC0ROtVbAVRFG5d6Ok zVJ?>9i(l!>*%=@t&)Sp}mj~^d#~V+R3>RjV>~vLyO%DNvT*AAtMCY!*+SBm7Z!9*`Xzk*)6aS54fK}@SFsw&A8P7L3;+NbDKKmkXdUihIZPrs? z0ijyJ26M@I+i0uZ>l9Mr5hJl-8DF0kyRpr5Phb*eab`xDSB76<+*YT-Vj z9^&vH`NYCNm`kWh=n@d={NnQ!a3SVl%Ev8=z4nCzukXC!3!Q`I+fN-yk+(-)wGaX3 z(Q>8yVrt`UyZQ3lIYjlQnU4a*)#J`ioO=9vg(M%a;&Cx4M9)K6w)B8q2ubaSZ%az- zna+Oq$NYNBFa?+GU|6B~W?I{p-~;Tp@Q`|rlistY`Q%X`p`MLfhhrYxM!Og{EEG(N zwi~%xI~sI>D3Le=dPWac7MP!HAdIaY5T{hK{I>D#JNl1k_!J4B9@_@K#iwC9tl8){ z{V$j~1e91~uqw?6kwFWk#Myu>C&E7hRB+BA_(X0(}) zz!gy<(S~jE;uH*C1a2x^3KTv$!3P`R7njVm5?{Q z?uv{@vM@u%lxF9~FEUo@;hCFcnYPKj@ZVJHW6BvRptDzlAM8JpXB>%Q`|^JOvCc1` zSAs>x#bhKw^L*&0LGZZXv3TF|o1^D-Y@fs%|JjF6UAgLlZfB>@aa1}n;FS@>LfvP@ z(gRxtMSfa=+5K%omj+pu;I|KB~n&E@kME zsG(pdnugyCU|2xx9JNAG{_Uu{t^41@ooMl|u~Q(zxb~m>V0;%-->UK~&4>y|g&MG) zds!zS0R|QJoG*Z+1QA0d%RHpjQ0NiO;p3&cJz4YwPh`W3-*kTBn;9WBaGvEWc8x^( zHSvr*)L}R3&Kp{=Jn6n-*=&5$6_qS@Fy6GVyWf%sv6Q`dU_5(yqAo%bNL;^u!T(dI zl6YOFX=WRmSZnMo<3=DPDscqzvc61>L>0a#{kr|tm&KtlLG4$PWr<}Y$+NX!J~siy zs$2Kym+nDLyj8eS+epJ{;uTq|#<}^M`3IU-h>iaXMi5ImC^3%ib~9EKC=9FG+mv_n z|C(7!GXmWnx6eI+-f;a_CDTH1c`q-WyEE!N*f{(5VbRIiWcwayVPv1-rY9SyZTR90 z(So^t*2esV=~^o z|00VWe%n@S?EWAixqaGv6%*L0yRDUJYv)Vb@M;4#DMu+sC5+)+Xyrp(x31Fuw(eMA z{C=WPHwbizqPST0snN-z|8DJOMiNJau3sfz_9;LW`tBpKFZc8ocr_5``fZuw;`0E{ z`R7v_dIU@CexC^~zH%AMNLX>-6%6uZE$~~M+0IdTv1*ADJGCcQMW*}Jy2H0+ zmzB({$-dFptGZrXNudxWQ(9U}Ts+bOi%1NYH#l3BoX_fA*Umv#+c+=Dd2)3Tt{I|?QyQ_8OVo~sgmSj;)(rqsK5 z{E$NR5l3@A+(CQ?!w67N$7l|x%QEAHu(%ly4ngBi%b8%1$hViynFkc3CA8fODIetA6 z{TP#b3!a(a;Q$70W4Y~61;K@p@qE)k_1yBl!&3y;+=D^ir$hzD>KNt`doc@4in}4oh#$+pf@AZp0$F7^$ z^)jjLUKL9ZpsDBD>pNOPV=+h|H>xJSu4Z&Lb9To$=)iM)UuQbc0@ia^Zz_a=yp!K0 z9zN*P)xrc!s00sYPKTRn>V)at=9esDV0vr^KP=bN&)ZzxqoSsT*Vj)Uk6rwa@ANXtfsbWQq52j*^jA5u?xEfVchJ^b2m) z*kt7ueSYMaWRhl!l{u0x#nCo?Q6xbZvn_k>us!}s)JY!3V=S1mC0(+0vGWp@65HsN zzRD!`wN=9VnlHH+pzDbEg#OTkg*_GgMAQJ3;jNAerwG~LNFc?Ll-?V{TN;qP`@OV# zvRU}Buv;J;(j%@;ZiTa&z@A`ZoVju>+QK+X0yy$Ty!k+49Cly~aT7f*aqvV#rsKfK zpp+Ga+ag86ZJeaT6Kf;-Z^-k1wcdcjl;q1FvpKv#=Q*kZDL>rPeca7q4Zup*0fX~k z_9L04W$&nI?`TCgN_};uQINJ9>3=h-MPsJVvEC!f1YQ-nIc<7V4p(AV1}Djvz8${5 znN1qkS4D%3akb$Uz6t1alx)T#=)u0D}~XElSbfUg@FXLprodqOO+W{W;289E1u}` zxAr*NS$+#KN^4VuX?Dpgs#X0ZA>4xYrmngxW`$!IPull!fU{^H=3bh$j%Th9Tuxqn z_t=5|b|SW#}_eF^E02-#~h_C8@^YsRvpNjL$rII@f$U*Iy?jlceM#&Khq| z=u~75-Jzd6-*pYQOXH15*)~#r@GRUz?7!QB2AMFHxVTZyF?8l3b{~9u+ZOe}5$?E-l0RW1ZxC;hPc9<=ywUnd< zHp5D!%!ZEd&{ChOT2XB%#WMw_N)akQrW}S{!Os|52wjW{+)|oS#y82G z$Z3$rIEnGi%@q7cInl^9+0O^TG%Lp3;jcyR<(8 zJuVlTJDRmz%%q-i^6{wDsLSI(O}~4MuMqXuIX<#2ec}!d7Th`(Mn7C9W|-X5LR9tF zWmv`9#sS@6-$c>)vYtO|k4Un?*>KUDj;F|QjbIal4KQ2l^Y7 zFi|^`E+9rOuEGsaP7d>2lG__rj73yThKYe4@~*Ug^P$fR9KW#foVDETrkuSlPdabE zn|(Z*pWnJ?6Ivz2!r!T$U%qb2mjSOGz>n-%#8vW}8$kM4&r0QZ!A{X?)@dXayJ>VY z`=;EqW8A5jj&5sT_e%vIE;Y7f3Il97+P(z3gp$$=UTMoJ7r+_`r%3C+{RC@Pi5223 zGB)-b5l>Y&wv*gUXpbZLf2SrIBobh5fD2UNqLZ2pnUw=+_l+^bsmg90D>rm~~)H4Hp)KP88AS~kha0oXO^*`YBuFOLcq;5G5Lq>Fv| z!l=aa1D{jPFP2Rm!|!=dc%(izz9k>$ERe)2PwdPsIi&>7}Cw(tbyU@Hw~=a9ge1QS_WEp^}wy5O+6gyPU!i3 zD=F@HMumAa?eoXA7hhN^V<3R8Ithk0WdVVdZ^UP`iPAH7zkU2ew`vITC-oxiTw)qY6!mj)#tWd^Nc zwUoxZsd%RFBzL5WYI)U>b;jE4eHdj~1zJI){^ayLCctL8`FkP^b7yGIArN_Y``Bm6 zKeFF~pu#R6A&%sQDgX)RgAAA*%HSo8tup zxJoZ~e(DFoS#qWF45Y{EBGX|;vO09#rfLMhd!$S1UatYn4)s?uNJF&5f*a7jJ%>j6 zMrt1zmPGsxMCv$OQ#D_=W#)=s{Jg`5r506X6~7q8=9%#54VFxbUo>t+Ka^2vYDyQX z%k0G8jm198V(dHT`Du;cIrD`bnm^_v5$T5uxtq8Xon#@T(K#?%N2-VY3m*Nf)@UK( z{_+LWAow-s&VK%JlfHBp$v2heP;c6b&zNb>3H`PQRYMlZ0<)JF&CN1WDh<)pe~6)n zhz7E7(z8o6^G)j`qms7q2P8Xm5?Bu8?BQSHh`S8$msJ7Kp&;{z>fZUhz{Kj8CuIMv zOKeSbTGHLYh%k*ZZ|yq9p(&nzo&@#vakx97E_1o2k<6L~E!LI`z}ar3hVJEXCIZnXtaY15V)FYnqw$4Bsip^X?Af?gn#Nf7|+oK@)Lc2$^r&{#K3;cN*qR z5_h-#ul}))ko=?7tfZyd`$D#6+2g<21=jB5eeP{G!u5gt(?S?WcpO#bgqA6>agS2+k`4Khse?f`@c`7&#>28ys<2H@D640 zX9BVxPT*TyJB)QxTPuPUlG`WrqcUxeAo3vWoVGBL^Cw4wVcQa+*SzPY55D&U`qmy( zWfAGAq|-f)?-$vij?BNxoyZzI8+NeF+mg^!S)u$ZmzNJ*S#37Lvu z=RL4oZxU_Z|78Ji(mov|fhnk)KmRiR9T6VKtTSphN}Ecxi+f6n$ji$$8oai-TKn?B z|D@AIaDaOZ1K()U`bhn6!u+HEzZN_zQfQn0=NsQ96V&l9PZ)+Ck3wXd(LA2{HWTng z)I7iY7S`XH{K0tSdC9POgWW?Fv9r0~u=ub100n~rZ%#OUS9?U!Lf-tHG$_N=!Grat^)4=wdPm8b zd^1C6|6RX0s27T+DZFfU!mEFXPldtx@-D)^z^WP3EYSg$hSZk4$pezy7 z9sZKmof08#irvHO5-r~gMV~Ae&KB&a!53uFS@J&N=ha^xmBZLm)J4|#XUhDXCbAnp zm+v?KmphQM6P__wn+)H{&grR3+x0PKoaXTUtLy1{ACn>2%=R(vi(&v@lcO{zleb}OG;3l#83^}l9BoRBi&$u?pSj^#gO187% zS0(7;-N;U9CWnpZ&yCy!k*tl*j`Nik?muayO2*qi%ts*OtWHmZ;to@Ich;RRWq$JD z6;6EGa{Dl_5NY^@_qNK_?L^WXJqJ?shpue4IOq~{DeOm4JtPo^#SugiSfWqEK5O>m zpl*vr2TML<97IcAo4E7=gHDMazZSj#!p3gw7@SbhQAzLVOF63Se$sQD=PH)zo?-&V zL}wdDd#N1ON0v&#n49$T;Ggez)|Lc>TjUCAuFodmXbuw6wLJx51XB;bwmWL|+SnIv zG#}TMYPICAUPpfNr;4Sx7D(CU0JoQa2_p%ti@wU>X8}H*C8TB`iI8y-;lMzm5Bhv% zG7$8<%sC2pBrud*wn-lCAsm;+4Z&sgfgNdiF`9pR~)A?mC517JOCOI9Yd28|x zusQ4*&#Xz~dtouS{agm_0AqeHzNbJPw>ZSS#WEs-1~`_=G-RBKinh~3r*P)b8adWTEuH{K{U>kg00;xq%|F;V^>O=rOs<<~`VNGW6>#Vtx{*d2hDM~MyQFjI?uMabh#}tbzt;N&X05r;UH6`|&u{N5A|*nr z=P^PqMc_Yz&)K%dI`gQ%dYW&fVWi=V2f}CGL|uF1Vk<%P!4yDY+=U3EP5M zqq{yj= zop8IEm9|pdCcAv#Z91z@>t2tzmU_n)`1Y7)!zg%Ep?GMlw7++^%wC8n{ZaeSLk}I0 zdr^~SBoM8rFNjr;Lq28(%LC!?uQQhhFX_x$3ku}W5&K7$N zEN4+ORN3_V5>ZB(yb;!=Hx9nOWn3>L`H8`s^{n?wP|v**odlc$5Dom$6cx&Cf`&uk?5eVN;t66xt z$3wH0KN@>c`9eE0LO!Enfc>?98OGhO3*$&-v|g43<@jSj%$<#*GBlj{8Y6Oii}whB zA0S&w!ytHL>v|rq_O*w_ZiDTbJEr@)`n1Jsv+_!~Pf6x;_T7&^9IVwx?9>~katOk~ z0XWXD@Z?e*w?aCHQAUPDwt_?TkLl?D=5e-QLZ1dD3_Vwfs~j0GwRw@7;$ueMKR{UN z4R1V3mHHacAJzZ+!#&=Px_TQ)=x&zoxKy5Y^Ttp|C>;@{JNQnS6w0RuS`9 zSB9<3zwRE>GxPNLh2LF3b0X3zz_!rU$-cX&vpc;zee`&1|Ist}?chAaO5_w9$Z-tB z=c)H>L5Y_CYuJVLPE7NW8sy<4n#5zQ?U(qT2Cri-;^L2bWjRaN8udfevpAQp)(yf& zVEWDcVm-&~`<&I)9!c}@G4vnx-nzmi;1`BIK|GfJG6edyc%)gYQ-1H6GVH#fhiyi zEx@{I{C3XRHdV9~h`t$Fngh;TJV(gZNBQ}nI%bB$wI{EyI8&XU_3vW(x54j%^EPWe z#x3=<#vW_Eh-s7f=39Ni=(cWfKg0=0`3IXnbrfwlU3JvqRGZD!Fqd|S=XINb@18c} zG>m~WMgMT5$=?y8SbxNu_1IVUat~;gf){SYp*_P;`3c#P2$aCcJ{y*ML2Fv4Ntbk| zf<%AqV_$ypk#)Cet7E>1e~QJp+bA=WmC|b@zD*0@;Y}qKWtJDbqqIWm39H`QF>8yE z({`5de>cZ}m;rr9fFC^An!pRib;vTO+b;#_#Q5pKKs(m%@dkJ!q0@doJiyiRA_!Lm zL)`x7aJH>Y#g6Q;WR=4{7=3Bv>oOMD!gqlETqiy5#^xx$?Q>)uT2AU%~qF; z`pOw6out=6WF4uh9{vhG&xa2eViFuURt@+AfCIj`Pm(x&((7i)nQU*nYVHH?z1l>T zcSP*!kpjhualP-|C3%;@A5E{;L$?#kA1AEVAQ0~n`E&5OiRs~BJSRp7yWHpQyy@ON zq30u6_^CD=E=YnCio^P^s1&ax*Z>mg$$$GICI3mrG2+HyOS5>#3((D`Q-s%X_j?$(XK|+K(r@oHPttG z=?2OrO_u^OIo?rYYvWvA)dFAcC64tV*%t)%A)vK{c(3xL0^{xy`;z5a{X@3U(buoO zgv-UgnNLj)F-@Z%YVaE)_K-H$OHLmcF>P zYtb{^M_0|1GCsVZbAQD+mVR<8U~zUqu=?P7kqo&P@aAc!i6z&?O*%TZz9Zi=je00V za`DEb*Wne(TOPwGy-ux7N=A#d_~Cxs4eoaDQzou98fwcd`|g*y>Yn39Vx zMnN^px6`<y}nWx=;#Fi>Rh(MbgHfaB~NGZ|uC7QYQJql}kw zOVzm$F47^8$R}xPr^NiR8%tM2NqW|E6V2R)VSN2-*FhSR{N6Bl!2)F%fa6+_KNopB z{!aNHv17PN_aBhdjwSo41*@K)0jV!hNir#*G(HlyGYR?ZjIc3NgwhVizQszPXTZ4> zCQrAF7l4vVyPgIHp<+85BlFcUda-LqWp}qKPL>(b(|8X*4j{b-_Uue&Z5NDVcDr;n zYFnhfKTalVkI6~Jq^JdXT%wNSaGaz6d8cq2Y3rb>>zI z*6MvfG&Pn+-Ls=}Z<8B3G_dVIpuuf|>^c)(b`xejHju7~wcj&-8sS`JJ}vL?8}!aO z2R&>4uOIhoQ2MtwUYg5)9{rFy^|t+z^zyW#;lAK{#q|j*3BQ00Ex|-BAeB&VLI}r& z5o6=0m_2i3U5L0FEv+|pe4>THm-t83!$;L7jZb3TX%2H;Fdvl!t!Wf4z}g`@Iyym#`rh$M-?sz z8?ls~)k^{2OOa>waZ)D~T4q{yif9e*c8}=B(mzi=JP4&@=Cz5=-ZY03?v&7IBI+be$(yK@=mNUPcxMd%RzIzSECurbd)3ae9_o zfPk*dGJ3_Qu|ZHt-l^?){0*h%>7Ns11_*i_i&o6ZIU%lw&t(I)i(nM0yf2ucU9}4? zzWK3E?1(RUB|SGsUc{xzwtC(hKU&ND|4h2*GSxDTQ(5C6@+l&ZZldW)?nq>>;{{e< zH`{RSR@J-^yx3=QF?$w$SjnXI+yPt-L40$sfHW4Kzu!FRPN>EKv@fpM8+glL&(K}< zx%z&OR?oOETKCR%_vs{rkL#0IhGbBq1v#X=mPiG-JVm)bUQz|N747#^@i<*@N>i@79(|N66 zrmg<)RvRU(XJiwUy1lfzMX4@rDPGx=OL7^Cqf!-v(BO`0R#{$4xX18XMM<%qOGcfl z1nk_=%AtPlB!rBgHYoTozCc`D1H;G zbB7tk6!iY~AN?Z5Yv5s!vDuOt>2UOut7jH);K4^nhHXyM-E2WT{_XG|fO>ou|6g%} zf#|Nm@|Z318EQegRYP(DbhG1431zv(vC{RXxJEt`E! zuox#*(He1F3l-~+ZPVYAzFXm6K^`Z7zU57$00|y{wSK{h*XwvJkO@9B!acB@9Vn*f z7sg#7#|t}r5f`*p8y`W2yN!A~2G4hB%kVVq+tAlVu$jiXof1I|&xf=eg1;0;>T+mc z@DIh3N^6~AeoTLSQ$GUKYOT_A`?wm5L8e0nr2Dmn@T6r!e*U}Z)z@kBPc{Ga>{Cg= ze|&Lk2-*+{d#r`%Cr!ltt=-88`iynB}=UkQO+@xS4VWQ%x}-qG?qHsEP-8FAfb~ zW(rjLXvwb>C4ga+H-7oosOr$GR-1<8*`F9O9VK-gg#*_0WtG+HZCg6Od_PJ+WR7W^ z2((>(`|QmBsBG?jQJ+--aIy@2I6~jlBn7g2kJjB@|Lk+fg5=v|eZ2_Dueh%N>MibyULI7f1oIS#*( z01mTsb?~HfCN1$x)`Nl9#mU!zOL8RG$_2QJ^IAn8ZcAy~2>Ormb z;_cKJrCCeYUuR|;^*K!)s)M%cgBD<@ z!pdi{mg9?o!wfq$dsehlsuNcgnXOdkY6v#yi8I}4x=|eFV>5%Z=l7on z=MwY1_Ls@oJEeBQma0-mO9>DrMaC!HPpu9uN}f91Y~1sQk$EW4&Z9=MUVL&_E)@3G zuw+Y(Ej3@wPGlO?6lyhU9l0Q<*FB(TP79chI+^&By31;U8GrTB1RpN-VcQUdso$y) zQ0i3l8?T^S{{u&(-P+9rGrkwP6dN*iL6`pd$GT#10$_ClszvO_qwzMZ5~XbQv29)Ph#(vn zvSF)S+@lIA9Vo%24nU91>d18y#H+X}WQ0at>*G<~Pb*{F5pvb~)s&f9182-eiqwzG zH^bj&ffAeNSxPoO2$l~%!@Y_vs_&NX^I@h5q_&o1StG;|@ikP&aRwGu<~aNX3ot@f zf@apDNs^!xeItOGk)5IIwmGFFG_$!6 zUOTiF`^?B0;xVs8aY%J&eT*T#?ftXAQD!36$oKb?nJ*S>Bo6 zYgv-gOkzkspe;(88Iu^{K;$72QP_Zj2czis&3;$6>12%eZ{7u4oZ?DnH~*-J6t5rn z>}dS-=d->U(O#4)tyfx56A`8?ouEH;we65)f`(&Dh%F&*-pf5OqIT(NMGlUQ;fcE; z{_}B>(B905>yDM21%T5cWnza+8opxqwzd7e*@NMc&HdaqXzGMqToH>( zZP|PZDgF=K0H+NhqR4`$*B-4^)dxVp{brYB()Ts~0<`%3Wiip7S(YktlK0uc+6bRT zHl)I5tJeKkpkhOQ>Qk0r4P|w#AcZ!|E2^`^e-}uu1PY`7S_7fvgV5O@w)R_Qb@0LC zHEA^KNaqVI?FI%zE80=hVM_85Ob~rn_F8Xt-SNR?74&WV?)a;IXUuFt{KpL;RsYY1 zXv^*uI?r<(Tkj+ECf#&I;4jhO7aO$C^I)@-@5?b+2b^eHV_ih}80)m6&6tUGAnDk+ zp+w|$7@oiDGt=NBnnyI;B%R|TOvIWqs00^6=6po{Z9b}1*1Qpa94IO?WDOndg^4o7 zDt6-ijjq*gsZY7u7iDsbeiMMaRDEOmzY_!va?Jp0>&O+86VEoK|q&()V!sFHpQ1gl`3at?276{tskWq z(J1+ku71kg6R%VX8OF%Jn?=?~gUN#|Wmges`E|HH?H=|T{k`~XaZxItLtgGxvB&+< zkG#^~kTuU11Ey2j8BBlXf0-!pq}IQL{iBI<;70W2t2M=bshCs&NoVoc45!^7aOBZY_F~uC99$m=P1@7DdS`=u^Y6n7B=)ARlq1& z#G&5m{7mY0MlOaacX-xZp(jlC9=qVX@n?`B?rUPlbp|qNwSSJwd4Fl6Jqkxd*LCfi zHX9%OWT5lwm$5jVuUy6>iIwzFkSk?t{NI&ZMuO(N2uAwh5&h6j)C;NiiRhTPNxZUu zh82uscOyI8tsV2yqQqXKfqyaS_qd~_hX_u2;U*a?Ur4Vi$X(G2y!HpU?s#}*+sy1( z5NEsnv_E^hI;x7cET8-<$H*a5T>WcNO9iP+won{Co~qJpOmeBBPT}kJkQV6#3Qo{Y zgQ1PemcOm?6<&xD?@F-BH7&fgaNG)d|O+xJ-@Q@>7#s*m|zVEJ7NPUU-Nis3d2W(g(a4l<)Sw$;07C{(#4 z8b;1_a&Z%h$;cfAg&|zs7wzW)4d?No{-lblu>WQy)dmTy6JA7?SUPrHqU=Ae<#G@X1$ zKt?ug<#5l2M88%touDq_%7A16*LiK@*aCJ{A)swTrD#I%19!`u>+f~)-*DRfC>nRO zqVmO}QU_+P*rFZoJ8VJ6ekE!;HlY3oxw6B+7d-PIM0exPqi2-!raR!%b)$OeH=;c+ zml4kg=o(YrD}F2dm3Hv;MpoQuNL#b`-lBWQ+ShA?UGg(K<>c0uG%T;OK)WTAmaVSw zt+8E5N~Oqw;{zVFGJ!A4Wu)Y^Hs;Z)g=Mu0m(~2Mzqy3bXO>sH@BVG5Q9I%exmaT6 zj=t#kM!ljKD85(WPv!u3gn>iK2pv-wZjX(9w#eKm3!aQn*99Gpvs=|dZzrC!PG%p! zJX-5PztGP&l@9kJEU=L%srpPEt2Fk|A2*>3;Pdg&(p*TZ&DI#mKnG zGsQ4OaUygIE~tiY>9{jPizM%iHx^2a{vsoSkhYFwHu(-dzO|-}9+nXe!ul7Oy?ry@ME~GuA6w#2)Zo9*+x8{(l~h0l zZMM4w31Nq7v=+mxLax+TeNNu`r0o=K-JaI5B>7B9E&{IY7n`fzf@iCK8ZTkdId27w z7DJN7fBlH6e%h*X8@Tl!X*6RKnNL}obxw!(g<$Uf@DSv>a~*MNxjwF6ivgT}xBz=y za-+;~#SbeZScL2Om-Ag zf%huhw`c z-h@!*uxd95kDa+P!D7<4x%ruR1U2qaM{NIsV%Q8I?`VWt;uK6Mc#ocajQb_&kL6@5 z!5jtecNoZo1yi_IG_0FYqO7f!P)rtVusWz0C1wbP!T)^cl0AIOJa@AH*Ze8LQuoP{ zJ%!;?hFFh!qDJ7+GIF0)t#7T=c?#FPVm(g!8^H@QT$$01OoF8V&^`02*Im{mGoBhD z=?eyA9)pyeqamc1?bR;#znP?P5eEwPT#-TBDexAua<5N03VlVj6Big}(qTeI#_N|6 zI*ei>ttMg_-$_feRQZOfL!eU)7?1^*mflKhA&GvHA}<7@eq403-Ptch@Abmd`pIk! z@GbEG!c}$ccyADVVzu!XUfD(zpav;TzxF$8tMFl zH5~9a*Zis~ym!QQPj2R5eyObQs{hb3n=p|(4x+CAEv7}M_s8zkz!ww?wBjMqsjEqqnQFmaaUKas-TRQJ{-#clVH=x)@oSkIYG_)cr;s{FLYrr#+Ko8c4_1$Q}?(caXIxIQQ+&s3{~a zr?+i+KZZ!kxil3UVGBC8(D)zZ!=hy4Cn0NT}6Zr3jkTGV=DVKDrm zEnSu|jI90^<4}U8SrLr6;EHR9Sn`(wQGCcWHcu^IVcE#441UN}XkN8i6s=Ivu01M` zJC;W#y0q=y6B8E}t+)IfdG;;8@zYl4vIKq7YC*Y;R-Xj=c7iyu#Gy**fj#!wqC##F z{TQ$!6?utpELL$~c)F+;R5l!+sZ6UCb#IW!I(}p@)Md$~*TaqM{v#6PDiz;FNNYlf z+je%oiHyuL{)^Vh7hDCS-4sGcS}F4WMcaHq)br3_?V)^$pk|hTVnfBC<@{Rn&JO7y zYuPM(aZUg}ZWkgVFAq|vN)`B75c?f#Df5#Ze!Ojfn`=X)ahPUu@L~Xlq-~f+ZUM)b zQyF+poYbt5zVOTd%%J+jHe(8F@&H$tuTq^?$NLI&2}xTfoMhP1#5z}e_pSSEoG^BE z1$yVdNrduoQzD?w7>Fi_+HTR9v{AaICN{3vaeW)32fK5Y{Ht%6>~Sb4!NXqvH~rJs zxe6G94>pyz{VpEz2tTS1&a+4;Zfi;2rY`rL4eipu4{1ritb0^0>Cs=6Ao{aO2A;CD zzDL$3B}1t;Kfa2WP(Z=ogi+-Kc7~%7P^0L_x(Rt`iNGUbEBVV)U&hdvr<;Qs=z9xx z3(C!VSj2SD-%GCfeRWt9Er$2QTSy?18heiBha6~?TjWjRzWCT`&HEc8UB*E!=cT_) ziC z-&Cc!=eeD3FHm+P#W#LwHwiF7BEf9^PGD8F9O1#SUs5@9Unmu&(~um0UA;1j_2)mr zJz8gALRnh5SRl$Zz&-iaU)k9EAQ}r4Y#NZG{kFakYQ8*dULO8_#~o7ATX%G2V3W%g zhM`2$Uywg5Q?;qHna41{tJtMU7i11DgMFn8+|NHTPl9Yz7Wat0xVFq{-m|(&Fnvh) zaQ&JSu0B+&c40yZ0tVTFSp@2{UPUsi}l_D7=0N-PF~ZuA2C6T zV9cu9rrTpFIV{@W=A=?$t5NB?fL3Z7>!go+;A@{VHCYQ>eE&n4;Yzc3Sz3hWU)EQO zi@}pboX>kmKqh{F2o@F@k#k<`AGu-53bBSsAademrycW0tfw1ZabaJSRI;s>cgDQh zRpKq(p+l|RsavWvVL{I@%#!VKfUA#Z*FWufR$zj1gr7m54IrS_?jJV5wJ9g1W$r1A ze!;u- ziBI2pg2_N-aVyTiZe_|-)}smb-#&;rjuYY9F|aek=RH(m+IdwWrB^sJtPSgdQCl7bo}IXF%#4B zJoEy~#2mkn+_BV%am5l%qPN6fn|_ip(gGW8x$&jdaIK0`c9GtqaL$qjgOT=0+RniO zgY%w;N~z_`%g$tJU_(j(0H zS_$#rBJJ%}pvcN7<=d5H*lhn1~p zU%w>cS}N)nfZDV(2Pn0z)4D$6miho zw@}fn?_B2FVtvS73DPRcAYsP-F#0Zs8iqRh3CCF>M3!fcPO2kilbzSRpoNTGN-Y%S zXw5_C0k;i44&7vQP($F(Pv3og-t#gTsTTDJg4U|Ul{GIiEHMU@t%8)Ko>qar!cQ=R zM`Glilo5D-{?k&>gfDR2gBvG)R9G9ExVSt z4^Fcu6>|G|1w4!9rdib}_`ND9{Uzbv)?&H56Pbo|P{qM>sId6B|8@+4lT))5-L8P}FJ}{lX z7Q$!}hULcN3lp^`oNk-)r>mqINs*}!Dql-b?dhC9If0cgjzm-Zk$9%?1mr^zA zq4`^$b~ey*R%U10?UzgjI!gQI78g{~84CF>?Dr<9PY!yG7aB2e>@_+XxA@F<`RnG* zgXfr%oKZz#ye{TNIjU5s)BJ09>~n?k6*w% zLWCK8u&ga~_8Y9}7P+HZj7Vi?X+-0ST)N8hm(%O zGP1)rz1l6yd_X$3HwqKfq_&5UBrpj(%_39Rwj1zzPe#!jmebrz*X!|6y!-9Ys`>{* zrJbgD@SrX)dP=Eo9y>(3zvEBmtP$K${3H!xk+X4SuQ!8?O6q2hU#U@RlBsO{Zv`*$ zfmAihxsbURor=E?qT0-! zC+HI)Qnk=rf;cciKNAe+?fNpct*UrV3)C>-MyP54mcO6y` zs!Grdye}&B;BU_v7{8QEGW<6jytt+^j{QN2b2$c1ODjL~pyBxyZbRVT=IzI=iI%CV zIq{5}_N|HK@EPG~-iy<)7Zu%JT%Ma1WT=en*Z?IYBoaeF5IOV7agJoKcko>@^ zEXSiFVy$)KzF?z*H0!N{be60Uyt(0q6?#{Rb?kAOOIUod;M3_RjyDmODrm@=W9D&YrEDwN>5$q6oA;R zBuOuGxnHV((2-G_6j8%sWz$;d7}{v2PPD>}R$bV3CmSNgDSvu)47S3zQA|%`#djstG|_ z>u(L~CFG#@W22i2%Fz)K{MRs`s7I_0S-zumSgHFOX2EN@kU?k&P(V(AyZ>+K6mBNEs%$hfRyD>Hy)-VGDPcpzOV8XroY~1T}!79=j zF{dasO|CrOCEL`}vSONMwHQ8sS5|p5{uQ$}sXeg60SN@SPM*vy^v(Q4uS9L3;yb`L0l5P!D;gnd6oaUgv$m$n9 z>{FC5qg?3xHh*R}Qf-(PBzSZp6geQ<-B$CeM2kE3J9y3Evqp7Urfc0MI)!uOa;HPwT!h)i?CKgy&C?R=1e6!=?jt zU$50~i}Cu;ap#!ya6{%WFYGCe*w`XX6}aEKZ>{&J=%XieVEpJ~Q|sUgAT14gZ1dM8 zv((ii^g=^Ls|(DL%;=^bNP5{z!On+c!tFVqq+i7-R@Y?E`>~zDWp>%?vElmj^6#b? zr(=<8O>G2oa>4OQ$!{mZB`Z5i49zjLdQx%pWpUg25<)7vOG`pHUk zNLYbp^AJYCMHK)%>$*6t4@v^kGS4UWOWYhdsj*g41_5A%7Ix-aAu|E0jV*heo=KzdpT+IZ2D33_J?0 z@dBrO>DAYk_%nF6af6m+?uCXER9&mWkGebHynegkcZtl(K6`WhNO%lzD?#aw+z<}z z{F7NX%3_EcQl_b+uu_Z2s;-B5(cHYx$Dtc9&;l-zea^|i%1lS^h@)x{{g!ma=C%Hc zGkrRfQ%4Hndft4uKbo=YAIf4UKJ4P8>rv|-RcEoOCpr+rjwX;?`v#En?k7}7e!Y^q zK}7ph8$eJI{O57qhbBCX0w>@^duv)`@n*mGrplu857pwVpmO<$U8 zJY#Pf&kHC3fLvd2)OkJTQG!O@Xqe}^A&uw5Q*Ik8Zf156MCTsgHG@^NU@Csz3B zv>Kwp3w1(w4b<1lus*L3T#UTNF8dx~K$F^vn zJX|-aUt`x}*_*3&aQ2|(H9snBz)1!uV4nIpTNI~yphqkPJd}$*MFlEfh+VpO63d5Y@S4!L-nU}$<377IHH?#WqLcN#@;eQ&>d&a zG>=Yez||jZ{Pb;qm)qZz#;N6|K%J7rQHv)|A#&=@WP{qD>1lQOL*Mive~n-p^7=ky zcfLm_Zu6kO_ch-?=lbx&TkQQ9rJGAN7o!KLQG(CM$;3#bj)C^id4vS#J!#ywN>RT1Q{SWd zk{8?!1EUEi(XYxEU;4+rJa+_Xpy^=B{3|ltO=)!*l#O&0(SlT(JPcI)J^TU&o6FWi zvI{JNr?i%9EfCL#cn2VpRjAXVFfIb0yVu#Zs24J4p%OS*F!iZ}qi@|v`grk_Z?r{J z&}K0{-F-?n5V{kXV!1d25x1Q9E7msB)7UxwiFF{ZZOCdC_FRvg%jQXJOEhzCM?7Ly zKiV^|URdK~8MC-AkdVaM6StdHiCib9pyA;${j5>(u_ObH=;IoS95Wvl7ebP%*OLM_HM&+!OmOV2& zAuBXiPWcJPC|oqFj;*(Hr|Op0qUn5<#eOEGFw~8srC}ehOp!|gL@R8oMJlpu^ZMzzJWpJ@OxT_#IO%SLLWZR$B+WWOA zOI0-%aGa+szOEh|DxGiI?~$Tpn-bIh3jlp&A7jX)SN#) z?N5qeKdE^Enq`Rq??BT~6CCG2Q$vun`d5W*SL}_zzp8IJ(!NfNvu?oJAFXz19)~&B zg1DJ`cub2PTm}a&*sblI3Y(Y-n!HRYZ!W&=iqU!)wG}RF73d9b-FkK)AF7yZOIk!Z z^X#p=HQH5z1dV=|_A7jsCqQWT#CTnK%kar?hSOZqdh!Ht#p)O)CPGDiF4i?0l~=BL z1!PaX)c;v5FxZwPr_xcg+n@b`HAv}W#VZt zrJ60%`0|y{Rij|S7OXyB2EX4^#E{Loc55-gQBeZOG!EQO^)fy=cJ*H5(yK;erL*Du z4MlN?XvDVcR{DfunjgOL*2M+KpXVCso_U;S=^r8}Wf zEe)d{%x~4(pn+Su-h5r)5|c|_g56lZcz36B{g&FiEN#Y~6((Dnyx7vfkbS`NBPh+c z?VQ{Ws02(W{DuedoIaUIsoK3yXM?YBs2v#vFN!9QGqf#|d8bEm^%cGG(yDu;lwTB? z@czKi$~ENr^J!nqw+uQFO0&Y_U*7cYsgn4Sh~MgA&Y|E`;7D>sTS+wVlyX=P-TDJD zq4CIdWGQGqGV8(aW|8Z@5<`Oytyq9efQ$*RL`sOPcgfISjy19|BHc#g!z)qWhuMvr zvLX<`cXj?+YM+dVP>PSmYGgHt(dJc?yvZ1fk#x(J$6mq2*w$^VHW}C{3xuey8O)fe z@SQ!Y5o%amHprUdq2Zlc@EWSN+4g8`)P*_yzA&^^KVQo6aCik zaC#pc!^7U}PH@!B8Td-|X`eY~$*6NEf8kak zo)PeL#&SN=0S^p8SKW48u~C1sVmtX^(4(#2!bOPnsXBVp z+Pc+lg2dqDil#*luc?+JOXhn@ruHQiG`NpKa(Hj?N=j5n!-KALF4F81Q`bV0KlMMv zhJSso2A|mKCAE3f3C@E;$} zHGs_A2-xO=78uF3> zyuMd=?tjxF9qoU0WnD~2m|SwbH7@ATgG_#Vp4`NC4BjdbWrhN6D+2))rSd~obtP`# zXIs5vo<*ZnD(yjM1v*2h*QeqzUXEKROe7cxZ;49H@dUd? z^oTz+evU|~efUj)h5U)gck?^njDQ~PJ9vW2*Q7wiqy4R4UbkDlR@K?Ew&Sd2I@^=V zG$NUZz*fk6hbN=A;5&Pz@Co>`L=$53JuU0iC}&j=vxns;;3e zfpIqQmM~L8Q_9%k1rVY7bR^kyxvxy%ALr-7!P-J1rF{$1vZg#(lV zR7ATRBggX6ZY%3x`}@sJ$s8nT*vJ+}Z6YtUIrNi8=%xdDL`Ne#l<1K3J^d)GUGXOf z^SH@Z@U?-2t+D^t++U)`KnVY?JoI~WF(Hv9cKMpqE=}*M`v8H_{ROk+-NF+l*PYIX z`Bo}zRUBGI!qV|yo!o^LW_MQHm%+K`;)@(PC_LNqP6hWT*q6PF`yspD>jZ$uxr8D* zQAM->KT;g5+2@zV3}axnv019ca(qGUPb>l=y-Nsj=NHkNc(lmB_Vv+QSfv@OW#av@ zVOK@Di;mpZrJ?rg4%N;r4QWcZ6lGG?t!UR=RilHgu`)D?ay8XPw9K%8Yd=$BoQXV) zdfy^OXxEy;K9N}~A}M@q*KLu{Ogp>&=w$T7_@Z6moP_F^VFXWzc6OicDqTU-F=CdL zu!Jbl=k?z5W#4T3`G^p|RujJ$CFjq*098>^NbiYO1@b|N*0))evet}EpdT#6LtHvN zu#ew8*&3e|(0V9y`%W`pZIlGQ$E!xZ=LVNViDA4IGii|&xNp1gx9~!!HFNF8#iaP8 zkmj!(`?N{>F z&Jy*mv9-#(iogmkfi3U4f$EPE(|Oe(o@`rZQ2v{hPc@J^Y0R|1eUs(Kq7SqTjktxh zpRu=>_W9A3PUm_MXDv{~D(C%jg0rftih>6n^{_t=S?{H98QQYR-efq6MFxkQ-v`YG zeZx-h<@tl-5E#q-eqVk2QEeysIkfX{`umjH2u#yd^NkkeXP)&ZIEa87<}g#r8MYW% zBBM^CaP(G~mn`U2kfV$8{v44Dp`Jh=P~XRuR#wBm&Zw1mzK&_;=t#3BN1x z%O9HlNaFsI4Qhe1Z};ecF!dr@3 z-0FeiO)7U+xH-B12gc3D zyR5j-kd;$-Wo%h%l4rKEd#sdjCYu-k`ashs6S`u*2Vcs}*IO@b|GBzusw_w(fG-Hb zrr|Vy3_0g1ax^Od89iuZuJ=Rfqr9-Oh7{*%7EcGnAC^{T3iGS;m$zI+>KdL;8nf3T zOrE89Oh=<@7zWe#Y$#T%kU%BDn4tFSX<@Ik8paX45Z1jvs{Wm0C4J57W0|7#k25FU z_K)ypAME5U9BiD#9TbM$(k*H94gjUY1Q>XXt#*l#uxmC(tpak2KQpJ7@ze6r)=Rd_ zu1z_=`4VLp<}#=DoY+YR&oVuP+U)6(^4SPv@n&O8_tTYbTnO0J+L`0K?x!k6rCjw9 ztc#q*^iqIg7!Ldl#pA%?7d^1F>~}z{pGVT`MOyhmw4_%baL5AKE|^c@UdB;Q$$+Hz z6?6Q!PHuWj!DpDV75GQFrhBGpi;nQ~{oBRkyP}48qi1ZgVLF1Ibkg+Ij<%+IWKyMK zQD9HX=ELlyV){XT2CxL5y7MS2QRva};0N7FLDI%_^K1o-B8(_}stfMAE805WNINnG zuED#O#dH1dJA2KNNpA)ly?qMo9E!O?cRxBGo|`=E82zLnZDYQw?$sKvDE?U4n^8hI zq@iJX*NYGQJyxU=nH~iaR?p+0elutVxyMtF|FQL&)mqJD@PV2n*0GI;8>ylV$lvcs zJmFfk@9ge0jPhb&c3FS3`5yp9LAt&i=mtYL?f}h74MTyR-dw=ZdHiwh$6VUDcSVtgPdN7bJv!XZ~$l-G*!L)9U}iX zvBS?pS7M(Ny5StD{WlNoThq$xzfH2x#)ZAknZC~VI+Xx+mBV#^0_bVV+OSO=i|-nv zPlKV0y&1Eq>-fE8_W9Otb~{J|<){B; z5qI`(Gx}aD*5%<|Y5kZPswtk5i};Oue+cI;omMRVp+pHd1X+I1VCZ?TGZ+jVQKD_p zJ3uo)r3eoXHQTk%UA$l3de3WcH9ZejK=Cw~x&68>Sp{Ke@Y#=}w=yEcYEZSE0df35 zNFoN%95gO>KG`a{-2Fs}KR;;K&!}H>!YVDm0P;+-3~U;whofE{CL*wgf5uc38}1q_ zSWnVh(tH~s`GL4=QhWQtl`6SxJg|(oWjGa3v%1+bjFohZvB$*w{9Ta0q4ok_D!!)i zh~qvmtna$bep`6V5+k-+rxqGZ7q%@1F6NCDczXUc-hST?o+CEeH@=%MfRAHM{x-EdB@WYsJV-&fb@vd<=~q4MuLu1(&{ zV&SQi9J$NQI#(<8mI!f8o;%H-&1-mAj?-(iXKokGq+Z;q|s{ZkyquKLj75M z1HR|QKZj>u_X0w8(JN<^H5d#-#@7ae!D$1`{+IPO&(a~Jz^NnRvzH%~Q+K@@SFSt- zB7*Q02njf^yh_oWJU6T+N{Ux3CrBcV%lkcQ*GPw2pRV8Rx;Ax{=TlCe`>sJXZ433L zA60tan_cnIMT%SP?D9DYva0j0$@BhcP3kKE)mchiFa6uzxXb&y`x-Oi5J|~k&(@dz zIUVE`FPAYoR-(u8Uqbf=4x5`j2(arad|mzM)ioa8{>nskdNOu1hz^rRSf8ovGux71 z;GFwBt<_`b46HLeCFd8F=uoIyZGpkC)BC%@uilkl16RjE7u%I?qRrSU$8Q3MC4I}-zBqAH;y@pcpPYKM z4?b6nb?anhi0jI`Se;!y5U}d=-L;`pwRqz^-*CDf0LI?n!Du@|(BaRL(2>%0|*xqB3^OnUz>Csb8+qd;ls9k5Oh+Vo@L zcMmdY8HLA~vRvF!+$R5uh@e|ie6cbwB%orpm60Y{9ziA3gs10E;_Y|88n1lu=@i$& z0w;r^FTFGlbc3O3;{vHd0pk3>lz`*_q$f%BP>D`vWMe`Aedh9MdB@$a!6kVDEbWa# zla~EWc|3ymcp0m&W-9kIi+Jx2IF^p+kWD>BR@JfFJ; zrBH8O@5+(h`o3Db?D_muZ^t!vzL#^=_(^MXN>RoK<*d5XabNU}r=@3^YW1H)oc+FR zF1hpbN(aB9JI6c+$?y8}S8>R9*=skRC)EEZsq{O1$FqKLS@+y=t#3c}-zVmrA~$;3 z^s=z`o{+6yf@8;y;UB)}O?dY4=g$;K?*PsIUs5@#1V9B@|29hi8Vm!+-v+~;1aMjM z{4n#;i)wQ@Ne4JoqLbMsO(4*ZzKf3E@{xazC+8P7HbTR;u790_LfD18PDpk46t6&r zCeMeMx2di-_wZfo$qo1Sv}z`Xt{?ST?-;zK#er%4_sU2bC7!F=eNHWTzTe0W(4{w7 z*TMB^82dBWm^8*`sMp{7tZeX{dEOM8@njIVw(T>}6&lFETXi{!acyci6@zKa=6gP}o1_z=L!y!spn=XDvo1kUHmcSuzQaa+)?EAtmFbKQey*T#Bv z`b_Lsflfrh_P*c1UIvv~vQEo}6+PjB~*%xS4EXD zbEmm>^E0$|FLV7a=~=c~<)cK4vo25T?_aKO_c`^Y`slj?y-t4HD$t|p`^XjOo8zLW z6i#DQp!>w0Q>;KQ+xspR=qq$r1bor8&&NHt|LXDY{t;aHdO!)9s-2b?3=v{TOQ0JJ zO+NeLn$sbO4|2B>Me-cbgI}U+zx1!Y{L9j^Z3G^7%=dly$eOD>*M8;9~J)2d8)bif7 ztN7fHopJp<^37%kB+xB+9?$k0b`RZ>=)03ZFOn)g6dfT6^o{X3_$o0dU79P;sb&6j z-XyAB6)svM9GO0nwn=}Fm#P0ErD(@xTp#fRFyK? z%q)3M^pdyYnpeO02QEJLkhS$^+oc5h(2bea9E{rJ$L9O0DhT7-8K2K;O`xyquV=&7^(sHs zcym9UFxW?0EqQKeOQ`Tba?1lfkEbS5we6_<{Fn)J2Y+RlCWh29f8`rY@e=4|Y2rw0 z8z+I@{nNT|d#_e%kDoxVpMmaG=&nBB6cgy7`dF?;AKQbl1bRyXodtvhlHc@Pc@C0A zK_7|Yxi`EJ_kQA6Zg}J0(+i;Na-UVA8w^wS5DkVc24yTOM3h&aE8#;*GXQw$2XWo& z^5nT9MS^6pi4iG;=go-@5}V+O8?=z*d6$57(z>A2Tc19X=S=}m+{9l0+waiH^LiPn z8;7Ef&ph_nyYiBE4Q^b0Doc|;qJYeI1fa_=657xTC|pW`=*j2;*_B%XX_w@8SEBtH7I#Y4gZ zW`nz2y2|Es?d>48ugz9C*fJk`6DzqI*5i&U?SGL(cb|9Ki-5UmKBiEW>nnR4Vecx- zZ@I~4)oXkgjk8w~Q?9>uVE@g=WbtyhNF#x&X1A7@aPG;E;kqyXfeYXF7P^)IbG8hI zpt#Z~TnvUL|CtWU4MRcn(huUg?>heS^Jg!A6odqvQkF0$0SiNjL8Ks2lyA^hRi3vs z3E58ymu?C4z8Z#xIF^;X=Z?Xv-xn20>Z+zs0RfeSJ%2fnsk&;*qdV|YDZ^FGD?9Pt zSH36g+O?0&H;uWuS(Mu~-n!C!@h?n-bA!Te>L>^dsyG@R4(yt z@h(ufG}?F)B$^=23&1!f2wlZc$2*fh=jXT zZ5+*!66lc~W7qK%0n&P)6x0OOJ;Vh2CM`UghK8&^kvct3U6OA7s&ieUfO`l)cx@bw z*R5OUbD5y!O1+@Q&&}pg8|H6w45|udhD(KeXae2qeI;FUp*~Q`63ij;?P}l4ayV?# zSOTQeCCM$81UGcwy;p1>N0#Mg*%pfHpLLDJs(eS+&rbl~ebm2lTuW?r{bn((StlW@v`+VN;Nn^_I#yUG3 zs(8ssV1m8#0ajxIT|M+Pu6&0i(%WKHrTF?AtfxMC0p*SHuDqYJTk(uJNEy$2&Nt)l zfB8=`m6lqwlS?LK%|(so#b9vXpA3fm0;z)Xf1q<{wqYDBdXX;^?4-cu({H%?;N!m! zK#-DO18YkZOj;0Z3L=MScI~pinQe3M-LF0OyGBii5AG&&>kx>Nlf(l14 zgWxJ2KKlW@`+=K9^M0b5!BpD35suP=uiCuUOpYg>WV}0UE zOaHEVW&4XyrS#-Nj~%)FO}f2);Lh7d?XK$~F;l;`UZT#$l#}Omd4VEt;PU>(oTW=% ze5GeXNpSa>+wks(Zcb6O8ptcp_r9C1!Qf<5jKamR-zqR$ZW_7UUNj8NInM!=cb|Tv z-gD-5C<~4WL6IVL+`v{GlC=tjViTNdvYb0`Om=l4QvbeePA3vT z=K59FM6Grn3%#c96fRwC{<;kIBWqm~3+(mzR()=WiKPwX9%Q?3kE89GS~QBfy|2Qg`9nBIwRmWo`?#7ut$hXC zhryF?!v3j z2lX#|q*m!8DcClLV{D2M>Zf z%kkuP;;yuR>%OUjeRHLA>0__enreF@mvK?;_}ZVxvUYv@RUA$Ct28xkImR~aW!=}N zUy7veR=9s|`>l=RN;!w@079XfLXl!d03iXT30P)e1vHrP$H0~ zFc=I`_A7&7=WqyO0_f<75|HXmCvwhnJ;C>!eXrj3u{Y#SJA#!3Qiml?$+r0OKIP=O1UtlIRTssk!Ra?xuB+yru>Eow+(fE0`%RvQ zguuI%a4_s!7{ngdY~oKdvBXcGht@GFsQtzl_8?QUe}(_O=;^Zd>J|%3xmZYgmBZ;e zITE0UIyqSmjT_tMVHb*^hzN-$P*pHJ1JD^nmYnDXkGd)WIzvK=_dM_~anHG1HRo}$ z6GMZc(N5T17=xh*mKK~}_ndvN-uA%FNSOgiL6qddNzN(91cA10v`Nb^>;eaRSOQ%e zozitDDRvsG>MCd#zij0($_ez5Inb*DOjrJMe@C(#_j&y_O^sMHBvE~Tc`u<+Zu>Oj4P;$EM{l@6n5K);UE*REFhDa!4K&g#J%6N?W$`<(p4 zL@I0~)%tdudlqesuRbQ$)_u-@o%^xM?Vr2KfgY2?JDbN4Pt9G++Qz;9S>ma?K3v&Z zrEj+$C4FuqhQK@@t+2#qhj03$wepD4Hr7H>bvf1W5nO?}p zOpoDjz3@lyHP89>1W>y=Z!pxxo0dQ~?7o=uA%KHyyH_G2%>|zTQbOr(i5|WDK>F(+ z{(b<@1fd%R7!d&^U}*_}SXL4&qt6u8UvaH+IzmtCslx}jxRkzZjH}L|PvRyEu`4Uj zV^poSBx%4!8$s8`p-$tg(c!wL&>QpXo5xz4$CwG^m7qyO zQ#3x3W7I8%ap0F=k!X4lyD(z4L8^4p`PI*9wr?xVu^VjM%IB?pR$YQEB$2K}UVDT( z?o;dIa*vCb345tH?ru#03pLK~RI*nhva+7;{+s_|$4sERd8lrYM3sEBJ%8Q!>fZ0F zpFuH%a_JCCJ?u~0!Bkir)~%sv;6YYPJgd}Q-g?u;vLDxyUuZf=&1h*Ai7 zDXu1hV=3eN{^~E|*~eeN#hkn!l-y{X=mtYj{A@665r{#mC!wMQLgs(dA&3Fs1cG7VNFRxzNq4i7j=!o;z(&TV3DjDfM&WUYdTb1M+ zfhuToeqtoNb(A@q;#vi@-f^IBE-79qbwXxnv zlix36VtF%W3ggyJ^X!vE?z30bkoIs_^&=@!x)kd=OJ_(4_|1D>hjSNC3n)8CxDh(1 z#nl^P5X`AF>@_TbZrEuYW`2U+1WFE-0Hp*_ISCaI=oB;shymhrmmbvLxaU=PYJLH8 z?UvwOY*-$Sap&ewTfMQ{&6nK>8YJjBrs0HFkIN-c=u3=aHSk5`^8f%K07*naRMRS2 zEpg3jDl$o`gC^4VHK9BFK4Sr{Vb>uO(Y44na&aNA+fLEge`Tb(>C<=Ol^u8Ye9ocv z&SCt$;n&D+`}ZZ6`{iTA$`=$=E-6y5GC*?(XY z4kk3lcX*2Ac^8MWe}m;Wral{FwFNS5&QbR!Q2Vn%#o$7pW)s{geAn>8)F{6bto9qS z$?xi36O!mLx6!aI{)XoMhKwNB67f0u*=J&XW-&hUaA<>@dOHn$xbYMcv`f~pl7gpH zMeU{o(s?fX)kME>?NZH8x45O>3E&|46ffUu7vPgSSlH)E>M8;B86YG`RiI2@N--1Q zvRuM{`se{(@>aSIr22kEqIu5b)X{+@&<*?TU)rgtVfUpR1f_&X(*={8E?feA{k!P; zuXx@o&OdhPesHQtXM{w6lmYVWp|O6QwT+`}YDt|or#L3(xo?xWA(H350p;Rx>eexG z6H)K+IdU{^@>;vH7fPqK8Y=P24o-iQ9CjaP7zcW+`I88`Q}tbitf&fz>UhF|_GWAG z{Eu4O6rw{tbD($Uj_)U>5ZemdO(Vk;u*xAmlx|3Luf;cBYvVvCByOMW;ioP>$HDhe z8e;A1{=RzeQtHNm9VAE z^6^K11W#IHQG+2ot~MAZ4&epliSKc`rNv*pm-r&fY9bnx`z@ z$@7K7vLn2P@pB*I%23+qxlWIfdxweI~a;z~E65ZXmltIQCYp3hOSDuvb+c9p*o?#dtxSQ<9?Qyg9=l^SK zysopt0+j?rgj5NV0tqOibI}B8{?DVA9>5#F?PV8#=2vlDZuQK$GweMqfo|AosHz?= z{y<5QJV(YPqW}0GzTwi@%lAVhJAD<&E5}KpaBm{&dwq4%dv4FPBzEg1p5s0CT-I@G zZwnLXMn&F;0S-^t7OcONnyYoDnmlzHmt(SMwDENeD7X7L42G%8;~o^|wb;dKihzAs z`~LRwDIBT~$$`G>`%crLgkk^U;r<_Bd04WpAF2vYvcfjv6lpSt5QAlgL^A7>kb+5Z z?(vV~uYB_x&M!aLlIVv0#>}R`u+y6#K$0-M(&p|R!jpd3i!QygdXJj~K{+2#25PX=S1p$-vT z6UJbvHyCz(&xxPz%jk5ROigcQlWP@iS}Gn67Ep-4I$Y|_S?^X^@I+ zOkLNb-V2ispH<>Uh8vAo>(42+SJ(bY-hBcB#--H$-RqQWow&JciToqPA0>asU(XS= zmfbomHOG+EcD{=pagUG3%AurjV@+P6$U8*w{_|uzYnDV803UkzZTRrH zTT}@+1Th_Ic5ZoqyaTB!$f|PtMO5~+^4_nL-WvL(U!v(!%aDLNR-uP9JdZKc^|sD` zEj#P^j_;Y-79dC|1=BMiBzQ^{zkC1B;rM62Pyi%)0EK43LY7Jg-J!%cLgzM%hFygn z1R8c93RNXK`yCI=Z#n(586P8#L05Lm&9;Z);gs+EMyg@S^J&eIVz>&U{sZR5pZ7TJ z(@ZJ2(YLr(e38{qL6r)EKutheXy}sYxVP`Q}mVRjl}<^f_I0a5=uX`+rxi%pb(^y=bwIRPZic+fR>cAqu72LJ0<{XD+#`sZ_2g-%S+tUgh#53XT% zVHN0x-3EbP@)o-G-4Ff@F3&Fl;Ox2@69j4ZkKKVp!4N0yA>58rN}jJcAT;fRbysJO zysKrW%zZ9fS3SZ5>Cq7OAsgCiB~tA1_OiC+3iB>{U3y zshTqHdHK1@j(Q(gYkk*dlC&NZ*mQ^6c+1HGw)_@S2=#%f_FH6n0zQ&kqRl11tB}s=ZZvmCbSdiQP?vaHF1uucPa& z&~X(=I{VUnJ=YlW?HeEOY9gI6hk$cS#!G)sXDIG~aH=?a=^=dO^ImrTSKo?fLh~k~ zC$0M2u>1Rl!LYOV$Itoc^N&9HQK%A9UT*S?1f&fY_#&D9^{&|KFN0yKL1vtdW4sEe zwyUVSnfO!0=cOd)dECe3xj)+Ee?rNymI~Cm^)_li!**BuKc{Jiu!7qXEVO?PWngE z+uE*wE_bfhQb)Ookzjzcj!YvdVlYg_y~NO-Wu)8(?>s?x^axg=ngAC^C3 z4~GS++R0beo=s7)rmJ&SKacCC*?w+fPM&c?CVMAxp}k%q+x4ehp&b$hsuc) znik#e4nd3~(B1#!w)E~Zw{4$7?&)?igdy6-#87c%uAW2L(A7D#`7{`|*ESjq+omI7 z{#~1w6rqtXiLI>#-kbB|!R>8+)&B0kLp;a6CatEJ8z2P|$qGVrCJ+|fefAdIeeS&q z%KypA4OO%A2r822WM;y!?K){wVA%34n*rjZQ^Z6>2c-Jo-~9dgtsi@1%Z}5HLSolA zU$aIaOCGoNS2u_^hhy8%1M4V&9XaUk5}br{^hs~;8EXR?R)=Lnmiv0{_@BGvbyXGg zLlF~qU8irKWq?)ezP}j^hS>ViUt&5Mw{nlkb0S*a+MK?Zg)X-hUH9j{ddP53GS}ui zjtjEgUy<)gyn6~X zhK?`#IRO@D`VAKY6oa;9yPeUo*I2w<(O3!0_KJJI1mP4Ds~q{Wcwl*%q)rW3K^bbV zty0fmn7DU2IURMYLU;DRbYQ=;;Fh1tc0o-P3_8g%hWc-P`j7s8R*i&5|F3($ z9+$ammcK2HITp_p_AD`C!@e804qZhZ-Bt(M;uI+WMhXQZK_atk-kEUQ{r?v}bMdsi zACxSzAqLIBuhWVm5bF!b-@a+JT>o;Q#@YTCK8^ans+-ejMwYa!(jJp*e!{9U@?JHP4W7oM75fKXn!!0}wkzOJC|Qt~{u>po^65bGLdY^QWej?DInED*a@#;yI}%s*lK(WXa)dZ@Wub)@iUiC z=bqgR%7jFkw`8_6(5SA?`oyS+ zW*lHs+!E>**|Rk>I2mTk4fPz40CW}#O%2Nw>p z6=$%AprSm6J}Tn5JOkA7^G;5^b{v=6&-!7l^Kd3Y*SvT?3Cfh+3%R1^;JVz9wfOI* zCavBc+Yt@KCacVrEG`Hr)&msZGlG3Tor$=s-%kP1othsp}gv}pd!?o-_L$l zCx2D)Rt~Vc&Lz2O{=(t!`FbuTUSq-Qp<66DPl;^fB5xkm=@8a!O_wd>DJD0!jb2zfP zM%>~%yZOoOx=ZeIjaC14;c3L&%asQ|{d1Ls`fd!LZLZy!iKcrcfOAlid&hx>xEByK=mR^SbT&xs&p4F`r>dHbS!E z8zeJ86z@KLGd}smhZRsEfS?3|>K&T#p`W6xIP&O-5~u~02D4KJLx@9=HJ-@+Km-th zaPIQ=rtiG(CvYVJNN3qeXu?2}HL?)YRX0pf3Dx-6V;iWDL?kIDOJUGdeI7ILp6bTn z)AVxR0>dfE-;ncYhc^a;VQiaNhs}#CF?|be-OIOe&ZaRYpC*|55rp^Qd-d(p#nUj^ zJDtb|Cf#om2E<{OoZ7a0fmE!2f*Bzzo2M&a1>W(olX&dn>HG;)6wn)?^FtIB=fo#Y zXbR&%il)P)qF#VsEq9K0XbJ@@+WzHt8OGx>%hP$r}*U}mT!0L?+l!NEk* z_nCf=iCQA+-6`!t;nKfj6xJwcC4nC6D&*QayN++2CZjEZ*?r9=XpNR*?3cpD_1T=~ zw-{efdLBx#(sz*7c168S{`wfNkN#2Lkwo4=a6#b`-9Gk5;j-01yTPzU@Q-d%rRDcnb__B&Vg&UgG0L4TFp zXcs%l-}-xE$1Q>SmyNGqUqd>LtKTv8LE+NNIydYEO=BmaL%#iB=kX*Av`rO)gB824Wb4)7?>-ViinP^0Mt2h8>^gNkYMr zQ?>kx=N?x*TUQQA+g$sRzu@n)^L&HBu*GmomRMfE_J%!LJ4Zh)TjSU_G|I2cPhI-0 zCcneQNBvGfH-FpgaPy*v&5sg;a11FiQe4drEOds)pZYBRr{ga>PbXC~W&zc#8l8w1 zhQX5OhH1kR=!S_$Ba|qhhagrc-TJ`ISI<8AAXu|1b0G$C-q1#7P|N^ks1lT$6FyBz z^leU_596?jw*xv<@;qqmNG40)(iWh{SZ{`H3S(@nV;p*Qm6x*o#Vp{l1j+bG4THfj z6o&URw*AKORQHHJiZ7;~J<>heK4SUBTkGQl#zjkCV_He{Y+TZ-UD$d*G31t~C9 z<`ATLoO1;r!K0Txfm=`CEYSQS0D(Y?1I>yi4-4vEgJDPFe1lpg$?Uw>$|4I7$AOA(9xRQyjXwE5@LTC=UNg=8#%$KMt*1EnF>%JngUq9082Pi*Wb$DDJ`@!ee<4kQ1d}?kQA<5*S2P;%ki~8T+!&V zuHog7h}|?)BI!mwwUe$G8UjTe>OI_Zqu2kuJ;MLJl~Vgh=gs zgt7+OuH!5Jw8M-%H4|Q;+)}FCD{DWE>OYIMs(D;T&#mkSc=+6X zAKVt>sLmjBkDQFGe%bwwO^m%V@;4l)baYEC~I4W>N2?3;ZNH6^$ zUH|{w{{}oQ=Wz@vQly}H0!b7^$@VGSo}O0?VQ983DWtTlG*MO9VP;(C&gL^rmt6HJ z#v#-cljl>noUVI`CCd$l;q2x%j48%Ah4y=0&90hXHnv^awwr%L=*+$*wRj6@TLlD8 z1W(Cj-1flF;PvmK>!IMIqh)f30J;M-1A#Q-Nmomr8>Std84N>hsvrQwN{0l6;zZ7e zg#-Qm&;N<@XD*)x5n(Rbm7s)*t`pK|n!B7u?^<=9Ql6;Q>Q|R7&v!Frho1-5L7?yE zG<~dzYrrv0NR+kTr$ok1+wcIM$1d-iGN0y@WzY1&VAwr?a zgr@lnvl4Lb2pPqT7U#A!gEbj7j0_V%!??~U4mqQ2QGuQm5a5wJX)NTZm+oaI{+Imak*VvXno*D z6C2NPcG+Ms79?Ett<{H2Z9|fI@wbX4~L&@II z@ndMwQ;gU$)ILV_Z>N}JSo68#&yj#gK{*xS$It&M-uKX(1a!!UAkOka&=WUhzd!(G zVxx1iCZmRtVF`4@$T+E>LoQAeNr983de@nEr+@tEQy^goJ)IlbY=f9lnX@hy3tIJt z5o=T1jX*@U=8E#J1aEsCyzaPrH1ztq&F2Ison-5%TkM*;!C=@0`!s~_I^~-?>m1%I zKcm%G_zll~eTcED(D%a!l*#F?`M$p6kzQnYNI8z4Z&>rc!7w6bHU)-Jol_7&RX|`Uu|nn13lHi0KKg${ zB|*YhpmZ#2qJpdml28f|z!Hy#k%)Rro*U}7)X^wDrkE$(3V%Qb`3~3hacxGmuWMb* zWBs?$+FW|tZ|YB%lmOa>Ais{EbfU6pw{;ALes#+Z&~_v~SzE{TX3y72wRqAM9QhU_v|L#|ve)#%DzGT7E=T3Pe-&uqDWrh?!7 z_%Gq-k3TT~qU&D3P{Kjhe3r8MbCpa{)vlx(Muk73(`hd zQEU?*|5SCW&US5sp4j*d27}pw-I%AQ15yjjcT3KQ;`!n+;;(Yze3}y`qsxXwkk3^r z7d!lnxpvDuj8y0dgLIZhPBVZ6%7m-wx~eZL|hfDSdQLbsJL zj0sDi8^$%n1&nXh{Mb5ETCQ<_&d&M>DGk|(7^07e3&)}&)w9br2*Htb}e zW{&q{0?(bXm1LIGU@&a)zG!o5u=$JYBRL%24Canbpf}C76l86Eh!A#jRp{la2rg4>P)vtRyyx6a7DDE;q$`ekSv7A>0Y*Frne+b_XMJ67SmbgQ zva5b9?SspjmFHx*iYI5;9g6>48N|9)F}eLJiU0s007*naRO4>RR29rzQ#IC2FRDK) zu$TG)bfVP^FE%)9F*m|G9kp&yCM^@y$wW(&gZ_{GPgT zmgi8!uMdz8)C98Y=f+HjmFIFXXBsx{)e2z_I$W2Mywt~H>@}AXL64jKdVy|KNq4!i zTKh~f;ven4E?w2Z*DsdNdPPC{;1Zw5whQH=3zmLsWK(pda&aHmH~z)Hk8l05SF#c) zn=q9{?#O6d3?srS&<#C9g$R`GB}9s>87P&qHU!|_v+t2V{PYn>o&nXY9G%3((~Qub z#O5syacnxC*yA(!2Qbb&g8h%q7Rc<4!C)}NwKE=DOL3??hHcvR%Frn{VYR(6uI$-5 zTgWvA|mFqG#W-!k2U$mm_M%>V@j42$aX2|MrQ5>O);2|N$ZC{c89yY zyD&*KS+pS6U@%P6s}i$O>;9nG_1@<(j?cA!1*hAsF)3LCRY;ZBU8AnU&mZeptpTax3v}TwaUh^`K&_g z&f2gPE_HL>KZQ%TryVK?@}qF6et+N1ZG9+QhMYGJ(5d=)n-+yju-!8l_Rf(l>0OKd zj7#CNv=P-5F52mPx=^@u_V82(p>XjTj|=76rvE(*g-h4Z?<*Z$^sMSk-&5lc7u24e zHNt_y#b+KRq8_iyFKKBWs|ss8{IgyuTq5XSHol(eR6C7p`zTzZu5YA0p)-YxGL!|R zf*YRs7xAZ``OWKo@$cwmfS5>CjR)N@A}oqB^olz`Go{1K)aA6lcP!+N_(#9=HJ8p` zehkXb0AUU=A<=o(k-5W`SDe1OBtfj2WE%}i_x+M|dRKLauWl0W@zsxu7fzI6H(*!6 zcYAFO2E#Oy5eCD)CIG7TruzL#BF{kil~uV@PB=QMxuGe<3q!_Nx)-v(TTZ3)yk=SQ zT#51c)1St-e%Wg;5rK}5auOGVVMNSq3JiVXM(7*>(IGf%9jdqym;=D?J@l{BhtB^t zm|eltM$pfT}UQOEg3vi@;qqmX!=(`U8@oLZm`?@{?*mqtVG}C za8q{3K2UAoVhPvqB!IzS*x|&%WRvRLvQPOmNZwCWRgn5tp_2|v!Bz#w}g=XblE~VfWBhP0j$)>Fqr<&BW0* zR)kKzLfQ6lgTc@#)@2ZmfssOa4h7@32X4kIzxtsR$1O!<7*jrN^D=}vKvZ-?K02c0 z#0gEazXKJGLIL0=P;!bCFTInl{d>oM@cd^lejLhIuzrT)?@%`<_v)(BsrW%)1&y#c ze`hR*vuVDn8qstO%6uJi?yzcpv)?61$S5i*E6`(9Ppy5XZ&M#;w`OE3+=tp_2Bp(F zxmT%mn@esfA}BxqdUd|bT*s^0;f=ZE0KJW;6YJmOr}O2&n;IIP#SG4Un)uQ{*NVC} z4{g_vnDu>u%|mw};bJgs6QNFj%1U(d~Fq8{|3JdYG%9^JgY@>jXaU$g!?$VJZ- zRKBvlI=+5<<{_Jjw#DP?Kkw6?R+% z*qYbgoj)clypu5HUPxE0M$p$Wp&!Nv*EZ$rh3YNlfyYHy){1bYuh8Wb)|8XY* za-lMrp{$NHH)5!UrV_2j5z3!lJis$Q@3NaNA3bk!1IypxrK%uZk>*a!)UKIZ`KV6u z2VjaFfGBsQUl059dQG0DxhJD07cmd>d?;moZ0SE^mOr<#;#57X@~^qKTDj4x2r^Dj zFD5>yCr;G`7>}D@sxEJKg*T5Nwpap`38%9mnNb!J9XN9xqW#7;3%RF~A<%chga}CO zR7NkW#;vQ-Rcjq$;KA>)f{;!Nf}@Wc>aWvZ^qFxLL!jEzj{kTikqB(uy(R4o8=hV& zMnn0zWo}0jqzEkCC9+R7v-h9wyAeZ*{@|HH)TR>(@l?cKe5p=n!$PB!f(F2427De0 z+%7*oe>$JM_!yL8i;x`U+on)*Be!>tR3jNb)Hk#-d}gG<(TXJJzBC3rb42a&gb0_4 z@<131?s5A_51h%&d)bMx{*Kg1*l-{~!j^liznICT|Ek+BJ?u5M*AmS?`rXX7uju(Q`y3n=+Of3%le!jNh)s8Q#9p6>uz0YMhytVjAIh?Z?E0Syw{$TVa z!~+wzu8SwPa{@-onb3lAl?VoSq&RR^9^^wvje21)=f2@$^Nxq-D)qyo1#HDP4fSGw zP$*{8i^C`BL6g3*FQWPE&W7XX8JAM%X&h3>J0G1w>zrRwOM_RnlE61vvyJJAXK zvX#QPp-pk(OSxGp2~G!U!Khw%h93^hI1jXat0)I6BvvDpMTqznbp*VBmMU z^9(B`?-M2eDX98AR#vl0aJl)~b(NV_GNLidw48feuWmHFK5_eD?48$stp`d|y>HV^ zq3vo9R2=3e5y(l1r3k^W%9gOP(3D7?eEaS7UuT=YQ-|P;GEB2&i@Q$^72^=HSCN0# z6eHV?=(fuHvQGI|`JB#=v$DEwTAhze(yw19PDOjwpBYeolvbrm+#_H*508E8er47h zFoi7y&xrYd;#o8uI|#AXNqly|(ZcKrGrc;;3b2D(_vpyzITy2fSLcQPi0moS?K3Gv z&+g@@$IQB+uw7WC7YfQ22sU{{IQvy36G0G~%&Z(nei5?%y}4eUs(xRSZaR==pq`9m z;nJVAzd1;ag){|KBKdTV`eAB}31{)k3`GT0veUZ!>&C$tb&_X zQZgx%JN|1KJ`&U+5D9c|9|*B^gmG=W!Es36aom_XQ=HRK%F;Zdgyd+n6US{9I(DI? ztcA{eu@=LUv#PJHD&J|=4hgo=-OK|)4c*KR(JUI#7Q7Zi0*)qyzL*gNP!!OKJHzGJ z*Mx?jWl`EY|BR$aU=-+t^(ZEXfx~D5tn4^%53Ibvz&EwaEu|la)SvnB&QTX`e%1e(YWH!?-z#aYF|shr zg9zTBUoE3=h`*okqMl)Oao85Bn_QDrdm~}2JQ*_3$orE+1t06(mr z-y=;JHuubgkM^u#7LBG8Tmzt4U6Q8*PLWrCB;JMO_~O&Fiq-$FK$E;}LRBYS=Q%gq zjMU4Q9kK#DEFD7d226^7jvT;}h7UKa?#AIUnRod<7;w?dPxr7|67HGD!jB^=W9}P!^rSleazmbDOF2Ai+jq8*&Q+0mW5U zsF(Yp*NBFbn-p5`zgaBuL!r#94{HP!(04vCf0I}n6J|&i%Jx(kI*b1C%eb2dc1x%D z+6Cogx##4!cw9zXQ&_;Ne9P>2A9IKQHuDVeFUM~xTP6b^R+hy^tJl*}vn`kj&-;M>9Eob+(Uf(OvynQ_0=(gYJgaZ0+wxrzrK9J(%AFrkByPT=&<%OkTHKM1jq@x24kCMnxOJ5K~sfCl5k>XI{uq6ByxN zB*T$h@kR=?!Y(Cr7RN}$Z+o5nz11Dm0>GOY7n5Lh6Wku=dRpd&GhaESnSbt>D<7!K(@T$5CzZq;31$ zLh&iKT3Fz6Es)gQPhd_0_qN|>aXM-QPyBaBqC z29drl1q&%)4g3HZc;NLihNxjm@^_du#~UjE}e(51fDZ7Kg58Jm>< z$3BbiBG+4=gm68yY6*I-SMnFagB{^q{5%(M8MAc-GXx?uo>WH&78+wp=w*zy{qv1b zigsL)4*KUA#xI&{?r$;FpeUe5AstTMnYvSTb*Hxr9;{Wk7-45$D4RpW0RzRdERO+Z zD9RM)8%#? zv0vB$W^O-+0R%t*UVExl=_3x@5WH?PzLUqf^OvY&r|gsI`aN2F|4#hLr9?^LjTDKo zb@n3XUU8`7=Rnq64pXo?;(m)I#X@`GBG!EC*nN~zoyYS5_Dx1hKMadz5;-E*Hx8F- z6t5PGKX&H{BTg6p{Ed5XGd*G!=^FD=km=?IJ^H=Ld*1aNM_0&2iNBCS^hE~Sc1t}X zpEIMliruCZO%;%r*bG#;y8>Guv5&P4-CtnAxvBGp&;<90RK2|djsDf84p~2eiU;g4 zfqB4?#;EaE;SBgs}Bln%0B57V)ZGPJG>kB-20(d z&b31(jHC{cMX?9(Hr@umWZ_Ul5JYO`vz6fx44@;KipWs=c7Hu9XK?q2NMIxof@X^*>zmceJPpAps?0%_GSGJO_~IzvSYzq>%pN8r6(tytiNYD10<;ZWww5*gh_<9}e1>Z1}7j z25h!JRy?k4{{Eq8wZ=VHHn|F|`rj`8?{xD%m-LI<;cX0!Tu!1hu$j0vD`E>n}am# z?U^@c9-9wzRv{Opq4d55s9uT_yA%u3gdoBc=tP%tJLfn9X-E>AXxg}je3!xZ$Ezl83$CYlX zX!IGitOW$bq#FZ)4k6iIS&+)RcHra+rq{jO_kR3=I!s=o-lEA~i#*1BxK=)#lrU2P zG8s|2WY2@#6ARAE_aUP*9C2~79c3oGF%*sj1&mjX_hL`au{VT7ii|5ab6*qfZ-H_} z_XoZYaQT1FZoBeS`o2X=T~v|&uZ5i4NPlgNjFFc*e|dP0b0EdUbcLySw}M4>FR_&T zP)SEkj=^0joj54t9By)V#P*zqNs4*Rez!QdYh+y(nYZG z$!F(!WQFWt{)K5Rh~SZw>3VT4tApY3INxV{L^q>R`?;QCblEptL8hP8@UbhQESSd- zg6p-T&7f0$e~lB|XmQBmEIl*)8&{oDAgO|{XJ{z*e1wZ~*Rdn3UU{%O=u))y%cW)I z-+^aKL1$?`>$Bqi`09YVU017!|Fs`Dc z$qK0LSGoKi+ox#2m_I{&g)7#X9qZn*K4V^Y@k-kqgi#$A_H79o7NVT?eHGrL|94Yl z{Lav263T{MLX1`Ou^NjuP{n^7>?~ei)e|Qt-Dalc1~o4kEs+SA96@FQU}ZIdYe7y7YsJe zu}T?uY=-~PP8<&;t=#OmG*0f1@S~b8kJS}~rC6_Kx(=F{`zm<5$F!Z5(*+M`25SMB zfx-41aofnFE=u`S2&C~uNhsHY8@C?S+3hzJX&9oW(fPb?laP^R(`_C!yefTIM-8lk zvU+~Dm;6<-Oc8FS;uooe_-$p8HEDv)bJa0HTIMx6@eHbwyexzUcWiE=Z9Zs(Y~6Jg^+(7Q-VRN*ix z_@;zYH%gs1={_nkaeHD)q;j`;`}!>;LEAhzr}*gixu}1K*qmH;_Dwnq$soL=qYz5z*)VCVg~@P zJ!h_5wA1&Y>F##&t-XeFQ2+cvla0GR$=q5@8NIm`=I=%AYIfpn6hX$rfQk)|^4fI0 z+C076d#f3L*~Dn@vqnM-eaT3gdDbg42)8(Ab)tGK?KfWAKC(O{=+_$Dv&;Mupb$#QL!D;bQX%g7!!aZ0B=sy(5TkIW4l&?O>5y8 zmn?W{ab<5rvv_%O6IOh6dGI$U;{%iBQfL08rn<{wM<>XX7E?1$GwW3UW-hX5jE!Z5 z3IRK&fNmc#kmfzQEaml^iyvXtHzv0nNgH2D-rzMW81o=1Qu#qnf5-X8Hmh-3fX*KA zLiFqjwzJx*lr?$=r#|xYY18ax)Ih=GoBsOqw~m1BH(4{sKYOCLtvrsJ&26=Jz-D0s zIrJs0pU~t&J+B)dn_KOUACufE1!+YP!2FsE2Cvrr8iYH0*eo@$W?f(jIw3)O;pe6c z>Q4$lwg01L3BOl|fFy;kg5$Vpoc-E%h3RU5*~oFgVS5Vqz-bN20?!wgJt`Gh{4jF` zW2oolJDG=4Ggpz#@fVLphxCPio6Fn9^?NPdHFAx-%sDl3#T@tZixch>bwB90nu!0N zQ|u)k%0>bBwy#l4S3fi9bQzc_C8KugQ|dJKy;{TNiw@qk{PVNEMR4VNH_}fGB2p+} zmWLr2i<-Wbv!U+X_onV4$|%s@!58{5CrNRb*fZO4R6zQjY66ZBWiI>*i4fjn0^T=| z!E{7WJ5)wAfyy!ca}&yC8GQgeg3HoXJJ)ggsnTxf6l+X8&0G(6s2Lmo1OM-?#!3y% z!4?|H6?56Mc43}*H4k3fZq2p)ngSNG;zhS*4W1pZt|~21&*!a>?KDvR_Z$KSI#?9)5UooF1`y9HTh$cS?0&kFvK&*T1Gcr6e;9*C8WQ$w zJchoaRWi`tdT@(-9a#Q9h!c4g|(- z;h_BnIQ)!2=y*)=*d5%w9=*PX1M5Hod_;Y9YKABG5F?Hsg)Cl(A9sPc|Tvw@4iJ1 zSZQMt>5o8oFhE_~8hcKAzc;pZ@;ek0^*+Iiju@P}Qqzm8DbUoSB7n zE3t?$OFm$?twim$>b~;H2AC_l>?@8b*%NB3J66pYp3QzVG*(AlR96~Y^b9bO6Qyza zFIx@?p)&bG2M=)8sYwX#UhYf`{3~1Jw*7~m?Wsfjx}ou1F~45m|6>7OJqJh!d+_7^ z)6>};zI|mgm$2M~iigS!6BbztQsDo70+sY8T5EM_P0|Z@4Za?2Ac}c1h3_-K1naR6cUGFcbbRFAC9P=YzL~lh(=`=a0;C zwGRuz(L1lQ1sv&PUyXOyHX~u13X;-)3zo2I@4@)|S?Kq4Vo^LZsfIxtQbdPC#u+@> z>On-SvDy(i`TD=d5&2)EyPX&Q+_h$p39;Lu{-L)?+(3dBs>N^7a(Gvn_hFoMlmEJB zKUnGjeOAY5(^e*YDcgzl-ln}dTh*)JL2zXLsO28aLMTKu#xF~co2oIHSSeh+f?qPr z(Nr_2wt34&a7jnFc(HKMs*$VNUfsWTU_W`qh@*NfGHC>y0&ARv=?4PKh_r5-~neLo~Smjbh;>D90~yN`BdBar&`Y+0TPutXxoPCtB!$()$iX&T4^|1 zyFhPDnJ79_u0o(UFN^@k7d;cm_KANQeum008RMODWk6o_{CDfuwfnC}=NX`dysVWsptoWiW*Q1upFi> z%jU@|AZC%C>4m`jV>J&llJLsQdBGx)zQaM93<-cCc*_@qQ6ZC9in>2 za&~2^;VL>s11PtRGn8&`x>u@VJ(opvqRuIxryiHr?IiAb&@+VTu`6_MimV@(?kyD1WD#oI`Uk^&{SidbUlTov2l3}1Nl%~ZL3i&V?ZyhEq*u8&k6Z) zY0QS|(h1Rj2zUN)^n?oxb;3^>$2ZuMx)8Uda}*)pL2e45Z}4HGABSB)a?Uz5O59E4 z{gB>dIpKVfvYj-_BS({1JDlbG@S{w>z>^~vrtW-rGMv_A$_|=w3v|!zA;J13WA&#?#rVrNj-;pC0@yA5O_GW1^wl)+5i*$i4t6N7aN6tx=JAr)2T2SxmdT&~#V6OyZu zC?uspF~**8`v=DI)Auio>dZR_acj=05b z&UGJ}Q*t!4kYT+wZ%JYn8pax;*g7RCEJMotHByrLYy|zkrTZ^>HvHMuY#Quh%`V#T zz2p0gGBFjZ-^sDfF`&aQkf_b6C;{>sEJpd78D4+zc_;BeI4MY$MkrU_G}EGJfL;n2@i(y#ryVH~bUiSAIq7Euc%c!f z!Hog)rLJeEz-V<1`1p+(Fr}ceU#^C_ot3fdm<>2m{;!0kz;F{h)@ks`LP7@T4%JwK zX3%j1?08xWNvNc1CC2bHfODlwWNzAfKIrA1Y7&WnP&h6Wg?|O8sZZ*8%6(it=LF5* z{3Hx-JsL-MSE6+#Cc@%rgubvaJJ+^vFXWHq%$CpVY9$b%(9ZGE$8Fs{=K9TwGp2x# zRDN@e#Obe|?J=93JV%ocgH2f)oEsPmKoLm%0JEg;Y#;`FSIF`|v@*;qmI}oP6cv$y zvu0DFggxr}{=>x>{!X5YU4#~~2DXPNDezTDoU8qF^5ZbRZX>AA(sF0d%a=JQEXk~P z@42ovdY2Zf1kbL$Voacqm*S88geDsMQX2kWbd%OWFqS1(@vJLg(cgHJ=gwgS-G*Ov zZFPQZ!e{{pUQD}dCvS(u;pZg(wFtj6;h+#oJ0zusRi7?2M3DNSt2U<{l3al!n942m zVgev|g)QiMymf_H^3vs{0CWc^OJxtXwb|(S79fcvZ~ivXLILRr7|(%Wx|NcDBOAOg z2t2A#v8Z8?K=OeVS?!!iYm)_UQS6_r6=8O6yV#=(v74i4@gw{e_gA4wueV7AT&|k^Z{qyF z_7O4g1tN|+ierdp^5L0BG$1nr4vJ8Fp0oF#(xY*`4gA7OpX}Ok@v6yC7=IEra@kQ8 zCbV;cdF?@e*vxEP-Z4Z{GuBqQ{7*4%wZH7F$bxIk1K}Q9epC)&y{QIIguaj6IbZvI z2yW%;*Iwp;3X&! zPzk->HokTu1)qFKJ9oV!L7tP^kd!Tm&yjuWT`@(alj#zh6rjPbCKS#!qDcZ)@CC*? zOU}Welx+)$Iin%LAYvfNqPTFH&o8Utjc7L8C*dPeVZ($x_i_4z3;)iL10)XsAR2TU z!2B1&Qry+bHV@dAK`ir?8vM|ahA41YCc+6FwMd&3_Qz|5phx@q_dj57ZYrbDH^aA= z5F@Hz-ZWpda6|>I2jZ!wp@G3P35hfa+jXexmAk1GEmdWdmU_iiz3IpUr@(`Ib1A&>UDgGeXTO<9C@98hHh#@dSFg_c z!MGE@j+#Fs3-Z2YCGa_x*n-9v*>evUe6_J^7QN}!%WKjNTjG0*2=MIBDnau`I19Xj z8qmrXF^Lqdr9P{@s)Ajt=!W0d=P#Eg>fX0YK84Bp6iS;$bm(R14QblGpH?g?!C?t4 zUfF$_ydJ`Sekh!eno5t|tfN`3n_RIs*1y%Ni+!v+v&dH;v{Fy<&Wwg(iq4_U7y0cG zH*1|9xp?WNkKSSy+8;orQPMiaq!tvG^GVThc@sCXURWW$&cm`ZkPnIpl@K~bfy@#_ zhIEAt?b{aa^I^nj4Qrj`zrQttmx*?RjU)`DI?%$`IGA#rU18$^t1V>lISCBPyJaZi zBiTBpaz&R7?adpV&JRrPNQLC-f_2;D6ou~VrBn_Vgk61hnDB!+!bC6IP?fQbjEKWW zpd;X&%BI4y{fvAcMrHx>05@gKWgJm=fbq9XpuQor2}&$943tOEuZ8?Rrs01`pdh{< zD=uUL8A@lByo*LS3AGA~Z&XSxID|3XzGnEH>L=6YalQGqILohT)+@lZHup4?}` z^Q&Nra(XzuZ%W|eSBS@{Ov2&yWyH4;Hali1PP%J5Y1dwoemfA8-Km?{fMa<^oG$6a zpT2+QH{y8MHQfAnXCd4k_k1r9T~NxDt=-`3;B+l`^b-&)N_ODbe(8_d^C}6r23NmY zf(7HacbOp)C)6EIORhL8cUqnm?wFC9${gCs|DjAeQbU7_yLOZVSZMg1Xm!c%lL?71 zx;di{iT!)4I}?YB-4=Yw>`|f%@1BIL*)4Ztt#`Z^8-0L4Q#6oaj->!Ci1@!`RDGbFte6v z?BW6hH4X}fCj)4viAvGcgl_woc3<`ow%~!N)nB6la2QTs!_lQOkv9f-t!>dO{)aWY z5)n_{#6_}&PN<}zh%lcwTQFj-kXzJ=wqBy?ZACl+^EN7hxO?ueIeZTyP$5Z1s~uWp z*urd}sJ@UuE4yx`6A67z?yD;LqA5UsRYCir#=*k49H7a;J-WLq6T?7Hg6P1sILN>{ z6_ou)&jYi1P*#pJ@y~8$60v3xhNI z5dy zp^Bb=h+S`_KMfDxHx_^2tL+r7)yx;DIQ4&LwDvVz^W3m}o>BDz&QIJK?qBEfc>VSy zG+z+bDApXmEWFf>6{zF@{V)_EWu~PXTe=Sx`Yisyew)%48c?G75?^K`RTjpd^`o#_ zh+$_L*w>@*`bI1y%H{X5`bjMXq4rhV%F-odb;}Z5wevsirNI}GD=BF3i>Sm;P%K~HvZ3euEd5&rxiA$x>gXOu5 zRjzCZj%`tI(H0Hbf`bWFg0lM**FOUE{_(a%MX?5 z_{!XI(?9H;xp;KznDeZLA$kw2^lriQ#q&oVw;A?GToB8N7#Y08YxTdAL{Ra?hCeG;_))3|E9Ou=o^_jk;8zO) zVY(wiO!(PwYW{$rSnEQqt(tc2q>;orjT3Q^LyQHW78Yt1@>+K8lzr-jK=;4YZv&7K zH6oBjO1WsVMz$Z8z3bx(SGBaiRSwu1Oc>2?ecQ05Qu&1*4(P zaI*WDE$)=2Xp^hQ=~K5+qc@ap%7otIxEAp>$Lq#Xf~6|t_A8qEjSN?!_-4upUfx?2 z$EOa5YuCqatJUN0*fhd!2STK@P%b<3bR$z|>h>}A9!ogH0^%Uq zHo$$Xk2Ut*TwH}%k2%fNNp3P99dK9j( zGetwYos{m=gdtWS=C)_1N1x&YK25PZ5hCeyA|Q4I%7vSTMrv$IGTUwn1#i{{yYX7% z%em3w4q%`%oPW;8?x0Yfv5)EI{K%*EUvA~V0T~IB%Or5;l1m3zW;oprR#K2~wp-vB zg!l5GT5P;D>^=YGwK1VaN1_j}XpLYRF+r^F#kyo%UvbEIjs}x!U;dc$^#!^^#+@pu z*C2E}orQo?w@pEW7%_D?8+mcAzBcv>9>LG^xSs{&7;{1)Tk~My;1&9yMkfjjprDN` z81hpA_`%$EW8}2VJC&dWHtYJvFSgHO2I2DGG&qnTW|%EF?c#G3jX8cXcL7bHYa#uA z#uLF_1G90(e{OzGY{Dk(^tEVxoxTq{4hk|Q?t9LnGl8V}c>iBf?W3mv&E3dY7(*ll z^J>3s%8ejNo&7c3gT)s1lQKD^{D9w5>1HI>=ZD^IPDU(8e98npml{Ct#^#WM%O#pV zM$&zQ?RImx)n4njT8eOnCax$(W&HO?233~@bxdf@7l%@o9KBQAdQ8si=9HLvl7H!r z=~DE)&1;dwF<>>gn28d1r&f4O8TAVx-QVgj!Kl@Bo83*VO=s`s)rJB3oT+>Hbz4`9$hv&hXl1dd#-|%d1uJ^J(Ekt8`hFOKqZBYlfGgA($=KH|vJGEZd)? zUyY!J=C2k-SbsBHi$W&cnM##KpLL~4=gbc);#;){zl^?x?T_>rTkTk*6U%(P3FA_4 zHt*V`F~{Ldt|*}t7r|Waswmflx;+cMWw9YBQ6+BSL2j38+ozl5S;3i?bbB&yp&`=3 z1)e{iwFvhdS4`(aJG^5vf?|r0N)e%v7|pL9$Dq&il+UXh^%h`19KmpbFgH(Y!4uKlrfFiBPfA)NeN z`NGfYGA>n_jFGLZL+!XbNj(e@h234!V@%12R=D?Bw~XN;*NEqL#6YWjlC#o%RgueI zG^C$#l9))`V!Y>-Kb77GP1-Osh<({N7vbanBf@k3Pi_(+`7Nq0r`$mplJdhd6Yh99 zz=4%ZKa0P@lzR7ZA*OiO2RWx8xiZRJ3w78I-F3f7vC^^ZqITIEgkKD~`q!^(9`rbM1 zsE|d~v7IFp@N8S{i$(4n=qaqPbhIbZgLZTNOoWYd+(GVc$ zgL3m_uM?fsp$XNdQD*vArYK%m_4)ijfy3``QkbzXy4 z@kkiBNLSv2YxbUBps6^QL6t5`h{HGY8sC5TXlFj>KJR%QUm}Bw+9q+UZi_1Ww=162 zr`>RoDt+74v85^S9XTmS({3(`eY}#SGBU*tlZdtTjq~RI))!$&VESDTv;(txPAMp+ zheNhVWS-8N{2@N?{pgAnzHSEOAdsRp{XEf(IZ^rMCFrW<%Jzol3z(Ot2(r}{VlV- zi)s99*LN%9aY{p6zQdWHf=S^$?}Y`M(MulKhZpK_d6 zM{@?tA=#cTXY%LEB!xZG>GGJ0MCaJn_y}PqC3Sx92al_jX>EuHvQ{jBVJKApunQ}- z_ZBlk>ird+I3@|?`$mp0oM}6{xGe53!WzgCq?o74c7!bqv?jG(GdV<1Iz5c7A!W@& zME>8$PKtNdr@j`B5soB=RS0F8yX8@_Su~PQzSSVkG(VzD1=h4pfi>t>8J}Uo*FmVo z7~7nGoSigDu+^#m^^4VKn$3k}%8~2+NIK zV~v=qy)_-%C811a#|z!G&i+4UUv<%okU5@$V^2{LC*-a_THR7;%Bx$qIP^(`)DsNt z_lw8zteXUjf?b{aFSkQA=}w_qkCw}+h!am6ZOqp61kg}wvNG8ytKT%bGCcg*I1O?*z7HQ(YzdH;(s5w0oaxD4)IKDe8G_Y&($nuV{A~+_iMnuAcs{pd1 z0Uh8+CV#Q;f+)!qEkYp!;9Ai?*x~Iiu@f!`6&d~~Al((;vQOHuAe)&Tfzl@x6W;N-=c%<+-#U$t7B)CTp|({O{n0+B ziLwCs2+t*(#xnaRICS7d*;yJ0LHGJ)Q@QTC5RFCxmQU?tB<^EnKjidKcqz!Kq)X#R zh&p&MO{Wi}<}U{Vx6c_F)`HD6Zlc9@wK#Az>Znq0{j>vm6AoZPSctXk99hbZ3E7i( zwvM3R6a(&q=(R_%-VtJq)Q>{KBzWjO&O&~jLE~Y=1o}~u1=0{)<$+&xM{oIgImLhh z8A$-##pXbo|57VnmseTucsf`kEpp1K7J869nQ#PySfoK80@DlGX2xu1Nnp%s#B1I7 zI7(7-wiD0R85lDT?PNgi!?7Y~8*WpP*cz}#?ETtDrg=|@wgz6w4m(9j&=C55u4rXeG5)!diPjhnw)>12HEA2W8>=xl|Ft~0d^lq+u z8B9)MwXzwu0v2_HLA+{W?>3JR=uPSGIS2qhh>`{EM^soN*)O?1lahtwhnNA6aB@B- zn8>EM@H?k?yU>s52i{4r+4&`0M34e=A8*Ah!P?Hv9|&E*U?^AMuMiTaJU&lE|0Xsy zUD>cAzH+v+L$0ur3^$uyE9MHNxZkM8OG>WooSMn}lY(Kx8F@b6I{qly=hql`=RKxP zVEr@>C#?%k%DV!5Wz-W4V-kGKr3p0%=M)xeVmoj}w&qQPyysyg=%DF*xbKX--aAo2 z{>DZP(cm`nK0 zd75Q2s^LC9XJCoEXnE+YC+RN@Q~!HMr>Z{9z5JcPwQ`(NJ4WBD z`^6`v}HF3+z;0|`@S9p#lW z4(%#Yi}O1Ejtfi-FVVN1Vczj=zc(DlpHovf{ib!8Vw1z!BONUSH8rm_4|x5C9GgG) zX<)2f9oM1^4G2c7&ho@<8|wvsXn!)B2A1uG-CS-f>=#?f)~h+`k0qDE8dH1wLv$_e zXI#x*zDQy_@O>Qcsg$EJkXhyA`9PwDsC}Jr?UgYCi{8P;AWT!`MoMzi!IGI3I2Sux12>vYaV#&Fp+&7xn zLx{xf$c1Kdl>Ik{aMkS?Jf3T15ixI5jo(oR%jyueY~Dz$CpY#-A~8 zX|CI-G2+qU|6>7;uzKaretb@5R%(#f0*mts`ukOrfB98@)2;nqy7!~Ko0))zjEGYG zyaMG6(2o<~dhvL5)~yLG16=>A#4RYI)K7!~IHzuy1dT_hBdHTO_Yd#=f`jPl^nB@6 zA`)3!f*Dc@{^l?t$Lfi4w_gc?4~r;+WNUuOCwo?z^QNetUtdIyY>$okNMp()c1@#|$)Kwc4J#1d033-bLHd7|2EYZM2!_K0(coT{>GVmd zjbQ@j!Jkf27%qCxlN)e1V|ia>s|e{mM>zQi;)X~m(K*AgzvH(r3E{|rRT*1mZp6ArCh$T9=Yy6%2ank20$z;>yL^D$3*t`J^wwlroxv^3Q** z-DthsJAX%v5@Uv2Z9PKrjsj6RwkaFZubFY|2$!4xYee?9fGQ)=F_oer3wV4=&};T)d-LD*8V{gx zMk_K-s^_up)ho5M6y@pJcP9JK1KX@lF!!PkxwYg6#qlag#Mr#FTF!qv*Du6E%HY!V zmNeWcJ>lKz$fbYfL2HqUbGh&vcm3`$E`IyhvBi1w?HM_vQsjLN#znshd*olrp@`cu zQgaqDdBdB0Lxn*4%_yB8w?6*)yKV0l*DT0^h*nf&z1)1W8IFqJxJ`BffqSbHuF1O>LqJ*4irm#vv{2F&(l zFASSwnwQ{1BpcOB2{Qn`8%0{_X+aPMi9D@?|C17dobY2zhdCO~G8#iKd|+9bQX#UM zI#@YN1`Oy2JIm3;YTqwFSgfu#nlEiY6jwgZ1_v}P^K$TEuaJZ^N~tG=T<*VIHM?_W zSg29HQ8hnzZKbkb()>iX&MHB2E~4SjNtM=%vdI2a7)l>7`1uhgvwr~0D$YN|HJ-DLjl@zgr0nwj}KP; z1+Ey;Nlu`I=m5o##!c+Ih8Ajn@3o$dQfE?NB-xNdS3Wjv2zl`XLh^n%6?iF3*b#@i z*R3?_-ORlGN}|t}>%_OHirdds+_-gXRtzy-HS5U0sazHqeHeCLrs5f1Sn^-J(qmdz z&8$A#tbfPl)hDlTDpR6*+jhz6N`1QF3wk9&1pZ3N)ukM}Jt2HmE>JQ-OA{TdkP#{o zx_KvVb8>RUQUdh_HPA?XSK>lIPD;t`l8>kk$y-+t_|5M=JzcjK`5UEo046^}@W8xt zk}7U>*}S}&E$svEk6-=Xb3P9zpSc#r?Y;dj?@Hgdzq>AYl8zR>inyDOJTH@UX^X;8 zze38w$Op8bu~8>?u1@x=GpV=)NRTizL#7;x zCAlh`fuIhCqQ>Gv5E$*GQhNH9-w@u~k(k#W>!?J>~mr8*LX%Kl+2na1wKmEdW_#g5TDk=-^a~xP7xR zdo-Uo5AULJ#ZBeoj`o=fqI=vg_G~kpgG+LlW9JopWc9gBCmn`>hGWw*vpAA72|wRp2L?~Ja zTp^wpf-*rN2^e%zG0yr=Ld*~^5L2nhuPFTLlTGoX$9^gvS`pM=ZW*NVHw26wFm_U4 zq8IkBHBim|r1Mft7yo}WePci+?h|gCZEv>QY}>ZY&9-gZZLSTw*|yD7ZLX7zdw&0W z@3+33>dZUu%rnnB^9*4*G6PoL?%@YH#a~V?b_#{}r)#!Zf2`D=={B>2M%l0Y0M7?Z5tf$*(Q>R7 zwi?@X5hsAgErZFljOB-n!?d}PMgN~_QGggQJ1aDu;)Sr0s)0YFOb-X{3^5L ztdVA9frrv-xjLcp{$-KwRig0tL~v5^O#X{e>lB%I+8yAiqT$Iseela4QkH&4rBYm} zmp&k)Hnn&W>vLk) z9R@XlVWgqU>>OQzyQZ4w9I_&Px;UgW>mng_6rj7zZvT97e|(olvp#Wi@7ioFR!wEx zysP#2zHU|UFtOw4F-R(h(meL56v)Ml$X{vhN~U1DxWxVM#DL7F>*9}WqO8@5u0CzO z=;^Eqln;{)Q(8}jungM+-5b-O{zsz@(ccrlAo0lcE!0)I~ zYI`d2-CFwrMBPV(n$>qXNwn{;Kc(by;5-y%F;CHv%Tr-^0p+el ze)Tjwd#0m}9I%L6#w~|jXyM`5Za;v7Ig~ji^fPrdSgRF1%UeXdkcGQKa+xa2pQl!# z&uV4kq}*Sy-3d)gC|y+0zj7=?%Tm6duRo~2ci!sn%4qaYsVy<gmb8_ci6TWz38xN%Cp5ZnVxe zprn!T%KI~%?bUxqVCJf3w7a1_z#sS9IBW=w6)cKLWb*Nl$NR9*L+^g%)jNuQ8CGd7 z>CBh?34|nIfiLQ*NEH4R#^}=v^PX0mgEyh4`}ag9e9fq?B?#63QP!kfK2c4nElgHd zLWhTAgbr8JEzi|Q50B#~gt7UXlDpSEYECB8ls!g?6?#yr!t^6CbpgPJQ8ag$5+&?( zy>oDK0y~!vMS875#lkwwk@C(&R!KYmWfmnpRBD(p2kNqKZZlD3z8T4ejGy(}nMOpq zdpg0{vU}Gj626Z2@F2)4JyBX@QH>KR!;Qyv5bx(4;eVzurnlYsH9oM0!E&U;AUDFFmU z0UcSC1j#UVxHAJEYO{qM5Bc;_-Hg!TIc zCN#fGQPXFA6ZmHAk@|MJbo%T-kt2k`_$||eGCGLm$Jo~+N)tk4(90(~^*GGvmpc&caOy;v}+&ImY zKgkeH^;t)jIw!6o{Pty_*wYY*`gROUoE|- z2`b3GecgS@aqap)83i4~Nd{nYjK(Cq;>)d|gH!$Yo9>Z-P^pdLGOm&1-0F6t8i3U3 z$U+j{fT657juNhHaZVD7f#zuO3WTWzzNFB=N+=>!VX4OG=K4 z@3Ft8-;XwdX=fB)FTBWZ3G-B$zpM87r1$--3c0$CANXW2W>wo>`K$jh2uybUst7R^ zTjAg3hd-D`7-g@QFLWTt;j|$n{+3EZB4c10-NW~3-?icwA!_VHPNL8x=i=ex$wjd* zc1y6f4#M1CY(odna8|N|$_Rg7niTKM5B{r1BB&(8zTt;bk%w&{z-^aD3G+r!!(jJo z8NL4W+gmQ|fU1U>hZ$Bq{s6VgZX%QHxfNpiny?QhC}jjjJr`RMU!w>;H98S!;~7J{ zslpz;dbY(Tcz(Sf$EHH_FW)m7Wv{)ixw_@Ec4TqBpG}hO+K2+0rWmt(GMH&6UK07f z$ZRN3+=Wt?o2OS2T5=BlxfIF$vohd;@saG|mSx56G5LGMT>E7i9napbb=ZNlTwOe& zs}Zs;ZB0QH%@|!%GWg+ilziKQDND;`mzP8(U$jp~7gHIM2e|{0N{$nQm*(UJ85QPS zAkR+~nO0kMzKR)!h6!#Nt<5Dlzi8pzQ~tv<#qnMmb3R=;VlY`{MvaK;7~v}c185S* z%3pPeK9O+28|zMg%5d~n30IcD`jpOzGChegtNn#&=nc_t^RZGl+pl8k_lj#ryqs-w z%loF!1iIj<*Xa2aNqJe+_w?-k(_R5qi73aM;tTFnB>NQWv+~M#5*5t1tJ6kCB4;kt zaEe>F`7X+l^YxLgnE7Sb+|GjztSb(XagtSj;k$g&T{ftRp^5|D60AhM%-839eJ`{D zUb5U*1_Pu*SWDBti(~!%+SH%86udlRN_+fdY?3&|eQS4Zvu{5i2%K#YvURT`Ri(ym zpFKZGsMkieXn(wufq9jw+G;3x>n2h|TDP0tWZiEj zdi&16_CXnc^UaqHHj*a*ru1Sj!QTiB@ z3aw^^c)tW2G3M{3E$>80(akex@|;Lyy~QNJiGA`I6lL+6s1a!>s^P$B!!d#z8Q?6u z)(_n2RR&cd4FkaB3F>pjSplAO_~@G@!cC|9VBffTq|pvV_e2!Y>z``02IuuPQ1lbI zP6k|!{M-q)wMgbwV8aHzE{I@?z)g*xw7m>H`mU~6f~ZJt*VZk#xnz|r#d9si*FV9e zU*WA=g42W~lA-^W0lH&f3DQ0V0`$ZKQ+ud8h#yE+!jLQ4UXqkFWhFJos++m`8T3pt zP3TFrE#cJXdVet^od=VuS=R0EsAG|vc&Yk54DXK2cAe%$ju50-ioZor&gsd;)13;^ zDtd2aTEfASP%cLnzVhf75QI%|OTuciE{V564PAcVvJ(XYe3X zp)oTRtVXQHPIH2bS{jX9&CnuMxL|3sL7U~0ycz&dQub($7-dBR8M!K&qLl9BhMN)^ zFmNp>7l?L|I6|-|t5Q1h1DOsJUti8mUs-D2StQ|vzGOlZiUOb6d?a#@#889)CKxe3 z)#R|m+)P?q&BcYJIzj^z-yosXHx5WD7?1AAD~Ox!;@eQN#k^<;0YM{zKf^{ z{R3{=(}|!puMMGf$IS}FOF`I>pcRcetzJ3~Hg4wv{D)#0Atl#vxt%YN+7ABu{-Ryo zHwnkS5-RA0LcT8f*B(KZuF2!+#0YEX{eefIXc)`AV8C8)a>%5BeEr(rd>sI8iNooI zdU&?5jC1|^Cv&gw^c0c+uwQ=H=JpS(62XpY2ZT3R?Z*7sEK)`xnFZ}^M*Aoht%Sv$ zK5Sdigu@2utMus>^W_MCT~h zIcv~zIft_k!cjX+;lFBhKw523#GIU{l-raGp-kw=v=p;Et7fF-G&wyUF1>b$aunY| zg-vlUCN^iAy7vn0yh1h~j57u*?&={c5$_$#`!Em%PO^k%+4{u!eOkm$D9ZT)TvbD3 z1LmCQaPNfj<67$z;rB(~B3c+OIi+w3Nj$eK4a1G0t4P2DdE`U3c(uSPD~*24s9#5= zAQyb5EnEqwVNX$Xe%=`z8hqS6do?8mI;lHX z?qIU}BanK=DWTw0{{ZC^Zina4Ec&@BiUO@7*#@R%kU%({iM4E9UpD#Le&6-lb~~D9 z58yJ0JBSL`1jlnLfZ1WKo$~G@taa;Q(3miMsDL#O+_C$dFqk!B-yC1cM@E!`u_^lG zSwLO2f1=nntJEN=Qpa*lKI)i-TxvoM?}{DJLD;P@P%G^^T^9R!!4|Dnc9%2fC+*O4 z=O)7c?-{1=Oc2JI>1aR_Wk#z%tv5!oG#6e2O%afj|Kug5V zxxZcu4{4&%b@6_w>gFEyPS8>!Qm|B3O}PvZ5>s!5#!(YUspR@79x9cFdH9iq|A^rn zL!DSKn;;j6+MIs)<#>ALb1n>67=k%*sEr>e2c|ny3*n-&FY) zN&=enb#$P^CtLE^yGGDKXKnYpgYf{5ZGkXc?tAyj$QN>`u(B#Q&F8D{7xPxY1-YU# zdQg3im`f>Wd*IIpUrPuj=M=D#)L@i+qSqCObjNS)L-+5fO$sP=@xZR%+eiDD^887N4$@Z(`X9zAqfo6e0|fay(kMEOyiOZ` zf3M&J+(W_OF_nU*lRP=)M$%%5z=f~|#LZnoq zVMlRerNbP@H1QJBbZR+2kK&taihswFvO_PzvO*JiUzyDT zHy0Nx6mR{X)~BBEzqFxzCP5SO5S4VKDe9zTyuSZrysvAB!O%cz4mDsGWK8R6Ovc#? z;<5cQ=|CfWux+N`9Z#r^-wf|FmZb3-MaQZ52J}?gP_G2pP4M?8cZ~FN2eOBA%BpM{ zWmOTgDs4Y-=$65M#uS3>V7zCsWP_zPnzEu*#{I~a-=k&;bd~KC7N-q|ZK06KG*OJI z9$aikoJMF@jxV=3@2ovFy6vQSQN&0}0CR79L)wisE5!f{+H~mn-nw(m2^M0;$F+C0 z*~_dx9kXB0*Mlv}+})^r${&GtJ-AtY1XpL#c%q4$9RKY8F` z(e7DZOlD%eHQTOPI|*uAH>O2rdZP2uq@z1Px1Z?9sQ3*2w$+%NLX13@^Y`I^r+MzI zq9ZD0+z+sX5iRG>2WsI9K(zDcas|s{CIE{&Pk(^EK?tf1PTk{Xi{5C``H$MrLxH`E z3Y}+l>YOjNtv-HE>aVU_fTnvjsr`GiamTA`hpE4hQ`CN#r|fD|^LMwaMhrlJ6gdo+ zY8%>~{*ERV#*u}O`QM*>Cb=S4ebP-2_8t{ooh{EYsp1}J0L#ipR~QBQ*WW4WC#QAKVMbl zLC>(DqlWa^89x;zYG=nqTF#P^0gy{GgqLPlu5_0f z&L7>d`S?fCYE zAH!8p{aGp)$_2OZ5qf(vHHs_|l+WcRFaGO7K5%u#By2{&_ZMOgl8-NsVqL1#ON}Kj z&13|z-d(N;XZ1P>{e&mi8qN(wVdNn1BtZHId@NH08|ER%6`@RuGVlfONERY<1Q!Gv zv3Xac_x}9@6@R1v3mKdwhfG*Qch!BF|11Z5=Iffje}o89X=_=%?t;Ohpih6E3U7Gc zZM)a@sm10io{1D)XlNIU{{OuIP_;q3@i}#M*E4$XUR$9K$v2?EtO!7ev)6X$GT^!c zoOb|ph8h%bw|ia|U*BQsbkWCbZ7h^jfQr;bGyp-(bc!})1^&^Xgd+6M&y~&{kVMYG zkU`tOk=CH->ygb~7-|oPZ?p&Xn>z_+oz$!f%(2sMF@E#T;JiPn=n&FPmW!B5+=Z4x>lnUY(%c3vM3pPssIwp~Xdvi26zwVVJOI#K9MM*eUx zTC<`oBlI{0gMeoa8iE3WFYDV29l@*XSvNJ#`n5tuHyN1}vJbxwnJlXHY%#X|^VJcs zpmVL!cBG`|j?#J9QUerAW2@2Nd{e8;{@S&5_xKo;hzQ>3A0TGsMDfK*Fxch?F7#25 z9D#lR%iNue%=JZh!~Rr1F@l4CBoA`;h>&LRPCre^W4bkS(&#=$;5*mh3qqnK>7#v# zDFG z&LQ;(in~?^2)>x-Otz8YW2CGOYT>4Ml5^<3pBB-_Wogw7+YWU0F}KQPOw#B?qV4cQ z6b+T^ov(Q%hX}tChs&q$2TCFHu{6{~F+Poi!Hu|Z>H40dNJ?;td6U4TB7>qL!n~K% znc27bA>Gm_geNb|)}6bXVkY_w6lDe~?7P`qm*}<+o==cK0sJY*8PT!pqtn^Xjm?2z zJB|<7hC|WWcK!$)zRAyBfL~(VTLTgFHS#54Uj#1@d4ZQYUib(5%dTx+mjp#|$1_6E z!)Q!~0*SGiv!P>RMQqi5<@d*?X3Mv;X>UyyJ$2^Rg$aZxK%Rhs;3W_pgoA$7sq>{Q zTtfLsFNaq;{((tb(A15&@HQqGDgT0EAt-T$xRlpcYg6tzLrgSroAM~ZpTCQ-`~*Bu zGg%C{BfGiHhS#G*6Rf5xQYh4@k#N@MYpVH6lTW7ayBxfz)IG9RYxpjMB8xuOKFHbUb{Ubk0I}COQC|ZLkT{U)59{` zTLTL`55?c^N60>}b|Rpj>m4=V3??v~oKuLTdUT;-A!;W!TKBdUr1sJcaY3)5AO3kU zEcFmR+6!KiA7<$B%b~KN`3+-05jf~k*Gkny^v;3gPH?JIIJvoDFB-%)9(q9lYI_uG zce=NmxrfXQ3)Lzn@xbGaLj3wIYh3^XEnPeSOVm%Z;h!0WK+qPj96WSz;`97|=maBm zki?C1W9*@v4`%G;^&?UEOqivC2d;>3-c_bag>0W~d31fK;|P2{OGrSWK^p?*ZVcX@i014F2>k)B;TE9W-$9L|(u5$N}C!O%$ApTX`h?yxif%I~WPXvPd z*9f=cTDM*xgjFAi1HO(wr%N6R_R?1iN0S4waF_~5!)uPgpbLbaX!^nsGL1&c&u7If zaEeN$twz3=_M0Sd$uw`BoxZW3tXZ3}DkfQ^+7f7u(mQ!Eo{SCJ0fq+78(4sgtwvZ(7P6cEEmC#Zp3qb|4&*o0{V!2hj4Drh(1qQ5PBZYFyJ!L>1gk~#H%%yOkJ~b zPv9C3p-6Y39El-#M{r=Td#?{e{bt-MP+&jvUPYY64@T=UX>?^^%X3hF`^O&{fo&j( zB2k45M9>lxNbwV1PX=KeEX2GzS`|ORT1f?t4n98d+3xp~R3s^BfY(oOv_2zZ6IQfm z8NZyq@G#!aN>S3Zs?HNDGMFe+NEk0DZITk>1R$}Ds3gUfYn$dhL5{bfyD|6wPURtj zYd_=rP6(ev0D{L0=a&EIrZDS3w`VWLKfnFO2YOn@aIp3vZ^5~hs4qV~ufL$NPFsO6 zW%v9``o2@R#!A_^lBE5a&ED^Qo(GJc=J;=)r-j1uNabM2qd3=R;Xq+&r_To~r=NbH zSXwyx&#qzbY@V{q<=T9tAPh;2I~*3`0#N$h+3EC@3cSm*Qct>XJyj^?O~`|Vzy+8C2(y0a{)s)dqO!S zYR~|oqS0^ewv2N5lvYBsv19uO|L*t&NOZKZxql93ALIT8x+g_+IoJ!bpA-avOWU4c zG^C$&234$3tExqx(!EeL!*`%FgdKrYaKg3KuXU?L^SiuWm~m*37*49*JbRs7Uo*e8 z6>zlTp`bTe?+#1$1d*30fud(LW<&ldBw&%^95#8VQVUM3G9)x$rpjshsLaO4PF^R) z=w9P2>;3b^#;lrN#2#DeJjJCZ&(=5vr3#Np{3uw*h%5WfFBES{GCtx;RPe_H;-Z7f zmfySw^mgBof5$liw}H@iXjdICN(V!YxlYG*j-%1rxpxp$YGtE%^+5C)`)_Ht1Uolx zVkr^9;Qnk9e*K#M)<1Axr7{+cSo!sn9b{i&mG{5Hn&Q{5S;nDbNum<$f`(ikMm!-P zx9&!sNG@ZORNSk*KGR3RsJ%{?t(^Q_g0zqeMjpnY2=sX(W3`xspK0b>(dQ$e6n${~ zUH=k?gmr5VY5W7zE~ZdnvjPv>vxXtV7+W)A_2BM;u2d98Od<%Us+cjc@`NcRL^+1# z>+G|ZYV>8eA0wGrqYRV1ubqbbAf;(fBxn%eDT{u5xl^^np0=nty0h+Vt6WnWH2p7G zY6l;SO8B;WZxU4#h2<;z&wz(VRzo-;&Jyt_IQG2$KKuUkd{4Xo2Bx9G95lP!w7{DoPNfwvZWG+KfgBM)}@*C)A^hd`~xHzHMBC4B8c7IJY4L<0F>7 ztp_f!yN5CoT##HvMR2kx)z|*3%O%|RAkUs+dJh)FWT(3c-VsGS#oVY!D@Rf5cb@e( zg$@qFWl54oBZVnQ54C&bFPOXl6)*(gDu_vC4V|QIpACZi7AjLkK)@CVPJgh&^?@oK z>%*IQta?CuTFkSx&6Xihxw=$oqTQTh{aIkW{1eoh zVDnA)Z}An`7qzA(+C#J?&r^%2@8^-xZ>+phl-wgE6eDNuo)|5|K>SrI7_XOoF6YZ@ z*apLQ?0O=DA$k}kF`f;O>cRc5Yc!~g&Cps50Q?J4@}q4t z0`&7yD%^MiNxopue4g2Zyr*Fv&ktJxxwe!qz9DXAd$N{0;H$hK%)r{2pkXpZcT9Xn zyFf(s%l_q2kuJLx!O2Ll;cGkNkuZ>pO|^^bK$!U`qS~p*Go2KV0j9%+2A3q{nP#>R zKPS2xhW20u!xPZ!4-|4Pi~8$f_Q{n5g0LX{sn&GIO1fYo-&3~R*@AcDJ#lj+sSlel zcSYwVx#xA2g*u(PjJ8d(=ds2^Jk~<0S=ab&c1SC-kelSa6V&Siv=OPk`DEd)kdrSf z;B+WOXZ_vGE)R32E`E2=36?ec+?q@fSep*I%-F4TNv*{QIYNuQS_T&KB07&p93em= zAk3uWY50h;8t2w{r#l+s-BT; zSKw~68~AUswzj)Ev-(a#%1y)K$d?(|3EXk*dD{jqsgy+WEZ03{luGOueo~nqj&J@| z=G5H+6WS;a*GD!A7l7)!flnPTchaeua#z_$EP$Pw=3z#SF*Qj31>bX{wN zEOWEnaOj$3+;}RAn+k9>+2$jkZ^UCk$*_>iNyK&>s=KSOl?{85LYf3Hy`&kla3q&IO#Lm6H@x9gyBO@ zml)WzR8Gppc2~VP_Xs|=6Z9yh`Xi9Bn0eH4NjgEj`V10(dwyy7c=^HO%S4HVrwvmU z!LZu1S_SUkoPG?YAKc^RYls4WA7IMUE*Jmc}~UGi-*xMQ_f= z?mbWt=v|PRV>iJf!PZTp)`{zh!t7J%{Qu{&Kq z?|*^Ev&N>aHQMb`foftRqFAb~{J1mQ{}Fh4!%jH*I^w_BV+W7v0TX(qq~%Ykn8!zu zV3*zgFk}}xTGrob2j7`_#ocxNP;U9RfTiozu#{p2hPbqeJ4gDq{gHA;si1h-JF+ph z!P(}BAk%6zD^GB;JUbd!h$=lZ9Jdr+hry7LLZ^2vWLry4!mPWoExj|wmV>eeDo0GV zj%dvG2m4k{(WOqAVLyRWH{BeUs6X=R&S#nDXwV}+3B6j8<4|Z@!MTx+<}tjRRuRg3 z9{n6wHmNhz>p-kwH+Xkj!quC*!ez_)!RCffdPbwnVSLkVdz_{VUT4J9&FRyZ&opZ6 zpcMn1ZuLB&T?qZ$sz4tCbHaz>^K`bec+<(C^|uyi&PW+o44D;$SJ|Fl`)$7n=SJ_( zVKy+1BKcDe>by(0(Qh>+?RxUzokt*z7}Laoq60<1<3laG@m_vxg7^bEs&O((G5NLD zH=4nCm4pl5wB%Q!$E)`ETq#G3;}gRvTbVyZ(E6TSYn4>(WO9(x0ume5g*wu64253S z|FW>iq+T1uP8nHcciVlxJh=%HYBI{wCoZa=UR2Fe9^moKjk z1ZovAuZqG_-BLK!&hpXd9Inp>K1ltv4Ir2@rA8(cRTvjluh*X}vTeUZ(ycNi4kk+9 zp*O&r;`CkCiUA2^XuoM}R`>jxCRkQQdC*coIUFkK7 zV$Bb27*y%eeIc5(Nfb;)=GLxws5p)+<9|4U?n;4{?p?Yd5jx=K`~iQ%ee~@gw~RPm zX=&OhZPb zBk0(3@Zx_Bm;B-o<5)&xQRrm$#W7bXKRslO{^>Z<|Bx|a{FHU zuPisW8A@keyD2Gk!;knrK8tw8ujkeqE^65O{nrtr-z&tI2>i|h*2#G!{aj|E(nVFp z95qp^69Y2OpD}JyOETuXuR152t9*5FML66)41=Q5)v}HQjH{GG8m|fQA{1@DttVL3 zol%OEuqQp#uZ_^)cnBJk%Lh0@|Nb?Kk?kMs8!E!$UFw{)K;(Ou3($ut%Gbx$%Ef72 zimUhKWF0EsJaJB{0cLqEc{o@?bbPFJVL8J3e!dY|wDp9F`CeH73;1yBq~6jU$ac01 zh_|OL`aIo4w*esQYZ1r4qDDG;M-AR8^JwYGRvm3-dimaJ>=gLN(k~w4b$X@9H9NB0 z%-FQ1;*MtnSL}f(=26KPbA;zX7$=jS!i|Y~KL*9ce^ZB$aBzv&PA`5{MBcuIZM{7SX+K}Mhs3c`as}jbwQ)aTW281X{TJBl<1nIL36}9q ze}X#~>uH$v+cvu;;A=}0U8dMuiQ++oz2G;ydaE{$dh2t{;j*5^3wM()+2X|)dpFe@ zM&q#lR`#ig*lF-5ckW0v>Z`8=Qe`mrG#b|RvVL;^$0`nNt@ z&f;JlyVdDbAXu->{vt^I{kK6(6q6P*`#s9qzjm=EW!jyJbhCeiq(EUiO`B<$4MteT zeulwUghzC`>L)jX#jG-FG9mRC1IutLo?wy*=>w|A%br*W->dzj`A~ulR}^2*-EpS_ zDj$S#EDF&I-|cAq-{O^0q9uHEZvLJyf08lIkU?LkZNmFBq>X22*iND6NHu#$lY2K8 z&(V;3#?LCI(Q{{Spj<1!0QsPhkEvEydIJW{Cfgynw^$0Fg)^CqP<-DY2E}Q=gQXS$ImpywU>&kxU7$M?Fgj zvZu2<%Wuwv5gwTd2>`*E=~2D^#6y9%5EDsm-(@R5IQSF6`-}@X2Y9CCESvW~f(q&c zSMMuEs)nxVmnz2v+i6qGVR;zsb{k39ZwkIW>~7IvN%d@yW)4(W^0AQdrYnjCXCTDK zp6X)Itc>mZCux{$@3iwF$s^-$1EUGBi7wFne=bTupQvVSwp5x~<32`lAZPW-f%4Hm z%Idup@{=ny4k-fTgkIR~8mqBwTSO4S+dij#VQ@OdLpdLCC`Vf58>;sGB2|LAXKji zgE@~#u3S2@v)$V4I~ss|*_B8hNn|>E{qtg@5hsLdG+puCkL(HIOP%&ot0nSBRJa7C z0~d87LLz!nij#DikJ5aL1?G{K68rvt;YG=RT>ee>uglp=4Kwu-SsbJwFStCQ)J_wiMRj!`f^b0h! z$V#^>H{|QAj-3G)yjk7|aepoNI6h}23Z<&C$)^?a@YJ>*rN0or_l z2k=1_xzedNv2gQTCkKf_CPHuNsW0j6uJcAT>&Qd6JkZHxqEQ16JEiU6awzql2sjF> ztu)bB*i7H~Nw9?MaS$@kIL{r3(@~doE;Cq#aIB}{@-Pg_xEAS-m=iBJLzxh zdIVU%uN?A+FLkheFVCKQ{_`1oW(F&ZIU$s%AqLg1NI|{KIkvB-jUM&muAP_J821yG zolF02?Ft8XCfCo)RUXe%ET-u^6Z=~Ws$Ygy_5PH@q@?-nm5nLx4dI-k*fQ=qSyEX# zTp9CiceOjpIY^bu@kglaIW{I;O5P3$;DmRH$(o7%l)W9j;+gzsXk==I}l*@EWZ~bv0QpnIA7{HwcHFN$4 zNW*D;WKu{;O8(Vi9dkf4aN-P0VhkE!lTuy|zzf`Yy%5%n4pmVBlB?&K(ua> zwOeu6|NDXBviS)!*^U|fGGj5q)H87!eIO4qye;S>W}_^Z#>L3^sFOj~6_ygtx7Da5 ziif8_o}&LyW%I4BM!asFWZk#;Ulgi@;x%DeO)PG;2R>BpAdE2Rj5Oi{id9L&U?i~8 z`LV86-X`y(u8(inW$wcfg4y+z*)3~Bu-DB-FRmsX9B5ca^!g@xbqnqS{qwe%P2Kq> z0#;WVMVW!=<*6wOj@UXzLG_by%o?f&oQIuZO+PhDz}h<0OMadq!#93=vMa?zoTM1G z2;nxS9$(-p>TIcVyUdkm58xD6x>o*cP#c}p%6fF1Hlts}kvU%g#9l?8eoKS?wyb;4 zy1cKO8Zg!3H)5JV@a6Fq?Ah#l=;5&Gp7z1DJ9+uG8>(H$uyIP!@?UBu*yI^ZU|A^7 zh8v+aGPA#>9H}rodOxI>S5Pt@o?)S{{nrt27flp)>AR>v z^4UX#3Nfo~&M-oOYFwdS<4c8!mu59xh-0kW7oTldE zPf<3NgL|%dRup{eIc>W(lWvyT4O}ao&^bI1f%^@;UqaRCjtRf)sIdAg+s7wWK{fd; zd0Pk+g)OU5*0cU|LmfgR#_RrcQ)up>v%zs5GZ4g^bqRn7m(sXD&ja=YF7A27Sj-x=~UJ|6q#fG^l5 z5ZP0D{BIG1`3DEr%-S$UUgtf!)kqKAiln&mq;QS-uk>Mggk+y|ek~2&pT&#FQ&Zw0 zgf*lazj&66E7yy5rfiT~$7>U^P4IohMFhHplQ1#X$-+FVS=7qGVxYc}*%i0`(LM;hgufTLJb}MIqvg^GCVF+qdBpA>xP=cUDy%Wp zFdRb%Fm9Vg3u5p|v@8y`{*G{?ebe>FOL3!oKz}GH z$@#7S$;o>5<6_sH3&v4!qTk@@9+pbhrTapudn2R3&YiO3e;%6f?T>^hS^j<|MNk(g zJ3SAl#wqnow97HyJszIx=LqITo$~T%j$rGmctsMhhi)|;b`nWIu=LoHr=W?(xi`sM zBg5DtmGe-rxg{JjOt zb;}QOveD7NR)OVDA+>LX3_nfW%0tW`I`#f!)zlbs64Q-`Zb37m+L{gC_S9r3RsA-jjKuEiKXNdX4WU!Tj!~6zq*%cBU%&#)36k*)Q%C@S_s99< zZ)-eFiM|dW(s*K1Ge&^-e_D9H$B;Noy(4RU)I5PV@zR==#v-H$kg=T?>P-tr<+Z++ zA=PLhCWKj?d1w@6S(0P6lBHGbQbjwk>^ULUofHtlK9{-_WrPt1w|xw{|0Gz#R8bc+ zlEoi9L4Ui=@EMFa6}mOKbrODKl>M?VhSe35YV+93WWPDs-&sePVJ52aJ*yqwACEh@ zr-UfVc;v?MQ-L8e362AioIWP9LBl~kYRzQR!I zKAttFA-yPEo=(1ZCJw#-0JxcDt22sSeZ!~DBNd820!}p)69WlFQYZ}|n)$H^>r(X) zM3JV`wr+7TzL%F9?4Vil{a`bbnMzV=fU#A;#gC9=Q?8N2B*_Y~h&uYWZ0sylWs+{% z;GM}1q#e7O3I>_*bL3dNwg0Xy{BqyyvFTDR>4dT(>2BcVD>3rXKQO2DV8^p!MhlvH zF7w=LUf#&C^WE?B97rJ5g!o4hzt4qujNp4_8+W~T=ab|5AA%z*V~3TKDY#2bWKy|+ z!VJtq{BF+JpU$?Ba@DkKAuXjw-l(>8aZ4cleryPeX<37O!2RofDH~1?<8R`zSf;d} zlqtO=M~kmcbYyhPq<$nOg4U@gMP+OMnF)FH5jiI2qCJYGL&#HwauH_)ci75d`w6!7 z8_N3V1Wt@;`7yN-VC!%)4>W3k?V^4bg^#VeKpBEHxuQi$m3|4!f|K~Tp zpbCcwdCQNzD&Gh0B7{GY!rT4aP&M~^<1d!7S&yGa9R(#U&hhM>#sAI`rOER7LA)R6 zK8oP!!HPF)FVi4OV>^|#iIx?zuLkG*k(R~jm+}Ml7bW|M9Vz9(>m?FlABjV3zbBvqoOFX2+a1jk5?g z|A!tOk?v#nzYySTu=t(yN?ZAo2{G4wTZdse-}k64a4_GuMCaka`OQa#XbW$Ka!*1$ z^C#(H{P+Q2wdbalMBBpGIG4Ys^krhh@f;h?AU8Y~2kp`kMepFjU>V>wiG?f$zhaJX{a(IKK@4Ej_#HXZhskaZ=^j&aH)5x4pKKA+x5c&J)2}V}C5>is4NI zI3Ii@>xe$)GU*x4B*eR;PHb;AnaMyuauo>Z^cXR268}|3n@^9U-TWI>>3&}QAargS zK?aE-4_@eXZPo7oNY?qnWF)5MjPM(9rYDHCP3DciEeCp;&^& z%^q)pm|7;Ac_WTnmJgA0ZJk7Ow4<0)>$F}oXH{B}x@I1=!wIcg^8s!?Qp|<_c1o$+ zVmn{QCg^w?Y59I3^901ElPH20a&21h&C0wSABxZ5+n{WyztT;Z@w; zce;#7*)7e=!`Q6)w;V7sZ;C>S*uRXwvLKt}OD^Tuey}q%3Rj8D8HN-ZM^Tg*-kKL% zL5#a6m*up|>g%Bg4L@LW7A@2n462IntdPvbc6tA?nbjnp%k0HB(E852DyUe5@=Bza zDr`jWXwrlX-Uj@P?3pxV7Qi00N*Vjf2(#$k3g{?Ip3V~-bOE2^hAA2O$t5tAC~yxd z=@-2Avz!h|N^oQSP`6)4?r?{7 z+n%HCgS-E{YX|QxBuSK!J)sKD6$D_fok(%UV?9!;W zK7f@#g2&J2&0FS=C@Y$AcdTd0oJwP0`RR^~XzWijl$rk2J%Sk$VfGdqJdZO~~3N2K$hnx{%ELr#$Nrg|3hGjytR=AF4%tiMu^E{IbFc z`Y~yhFZ<794n~X&7jW71vSoHEc5xR0ps=r^wF3PmPca(`)dMSM)ivfx<10gBV{XCz(~u=0@GnTHYjVYxy5MlN&8vGITD5_!}XHbq<{ zGbAHMTSPhMA%Cj}DB`gDL;?vFUUljkr_?=l>dMUOaSCZgWk|A$+~1_$TK)Sd791&E zngnuE#Rj!DG$zTYip`s{DBIY(XkI->Y>t4%R-xoFR)gY}QbZLslOs;?$)d4|+b&Ud z`!zaU`DHg8_dmR|hFI1v9ZnK#HTxlTQBasLa%u>0*0J}6>WGSZU>GFR-tzWz{;a!3 z2z-aTt&Nx$RX_ueNiuUjA9NY*1%S%p$TqtUMXjQzeBdi+_fBi3!v&bQ%^<52!JdIo+>n6J=`OgQkRcVv*?o0gH#yF+1=GO!y-grQb}+#fN#>&(H^I)ZS+M zP$N`IPNx%Is(A>KFzLGO2lJvP6=YGU8tpd6rAgnCjVhPZi%m*-%Ge_z{&b`R&quO# zlpKuxZ)!F2#U}FmfeF2K_j9G7ObMlMjLlSHTud^POpV2d4JP<@E7W*!ER;)i$>2cDF0lkl zwK+6kVO9}z+p#co0_o%2b175wgcC~lPN`6Pp+}~p?uvmrelj(9(Xx<(v$5#yU3S)N z{m;LF)J=6?OlAoIPh^8&`@bvqOmwyNM0&OMM6?w16pWTq=)DY+3hzy{eBM}jExMU; zb^ISc_#d{Qw*u1+h3!=Jr5b8u*r<2=66E;!kG#GedBMrb;vYY4Jci3Tc|O!&GWhWI z%rqGmg%*wb!tgG|W6v3xi<&10k!-emQca@P9G|QfzaCiA&9F{#XSe#Jd`{QIM?IG> zY;@}EW8?(xf3TJsf3Bri)KDn04iDKuc9>F%4T~Nghyr~37O=O|TiR$JrPsy~{?iel zy5y(Cgi57EFo@dC@5?D9_{o<=lazS|`G2_j%BZ;3rr99D-Q6X@HMqM&Ah^2(*TLQ0 z-8Hxe4-UaSIKh2z8SGBZd*oYptpWZqkL>QQuCDG))emV!zvZ@Rd@z-20tIngr^dkjZ zoQIJ?4gcgiF-<09Tc0cqHWp_;Ra6M3I8HI)KN#Pkidy7^aX_EC7&ZZO5akDCn{0!> z%zOSh3-=fj{ao;FBg;p8$jl40mZp~v^5ulGeg zYp;D71{8=0-_l*t$cl~G=~P0x*AO;hYJ`Cm6r1;l)wiJbfO`7rj)T=)gd5Q>B#1%# z7lCcXzM&~tYj?hX46Tm-POy%C=f+3Z zFUKyk7EDvQ2e%PB*(83s5o4RCr#Y%JF*36y?(V#I!=YwO>Jchm3c&UkTC^wEWh7AV z>PIJ-j-=fK>0|h#-O{C-h0X1KJGd&xs|Z8{r@@@B9OQ|@jM*<4xO2@xhnRf4g# zvKN&B7@wf=9a&5?rsao8m{70#YgXRKX?mq;+SeXirF_KCSjg0uMw^`EMM5{nsQS4; zc}j}iMGdq<+Nm`8l0SA_8i zIOwBukR}Bl{mUFg0`HUi=A<6%#_){J#uPE^rdXq!YT98c1w_;>t7!#fo)170$o>Tj zb;2vQzI}e+FLWQ)&&rLYGY-Ryz9A!TE3im+gV>=VJIoONty25!agkb4$vdcCZgYEn zK!Zo%b;6W$#6L-kLDycZLYcX9f$^A?qCk+yk|U%5yPGVL{Iw5qEb!9lBg!CXv$6d|>tlredX9_0ayu4ZF?c*C zswyvnma?GtQ{3gRggj*cygSIoo^DqSahsTy)k(QA=Ag(6zyf^W*Ls zp0QR1izZ@#;045HC-65kZzZZjs`XTJ>y-7Rvk&Is`bibcfgW~Ld|A&SMc$lj^*a+g z0&`|DFUcbpNZBUvcooQcYInZv0Z;hX0@aH&ckur{j8_+8MPt`BeKg4oMrXcdRhC#7lEw zQPtk`dl>K%oA~k;2{4W;y3_iwj-c@-y02y`gCvIpY67(;d$Z!44mPBCJTK7Cd(QZ# z8|psW8-1XBRK-bK(eVHIw;}%42|yruA76CU9W;F2O^G zN8se&gHg=4-){t?&KQ||1C48qnRj%C0CxtK9a!KaU#I)qy@h5rFA{pO=6l1{)wJ&+ zh@V+@Lyd&)PxD$U7NO(s`Q^cOZ9CFK01c8%I#_sWKf^v$N83@)ZHBw+KuNKhR#;>N z@Uie!kL#-QHnOI-8sE;c#q--bV^5>Cq9g>E2TvOnw9;Uo=>uQLz?iO?lRSgwl0;ZrV{07(aZ zr%jH4*~4+Ok1Ug{Drke2l!-IENX;s)l;ODN0Qj=~h5L>0T@p>bvG-8kaA- zINMGdOHT=GRv*GHsYLDrbN_}(u?iDph*`-YWdqV;%3 zvrIO(ZX;?PJhvtG`BeARWk0s~(UdujCZk<_ZrjKOPFusXQK!RC6h!}4u|y$^q46g# zEH6DlEbh~V3Ar`?F4*Zo9NYV7f%nXHoii9qSjQKot3lNsVyVg)k`e_UX}oI+gL|;K zUE-cQ3G8_6hb*KhOl^@6u%Z}dp`C689Aavyjh`+VJnO`VSwcXV#eH1_ z#`>2c_kCX7+;K45n6k(?XopN`m>8gS!^`abGiX+M=Uj($rx$z-lVYg#Uc1V&?4nxF zsFh&Dj$+Qftd6~Ats8F#}? zD+=|<^1}xq=R}fgv!CMD?rTsH)3pk?BqZ{3)2qvmk9$=5fOxI-R&F&~F5^=;s1SCTbb5!qg`K1MgZC4}SV# zxBpdVzy?&`8=uSFl9ql*OHpE|Qw$QU_|-KMICZ`XzF0X5H(ya!3*pq|;h zHJ3xBcAjIy#M6HPF?&8P=?0Jl?e%?=5Z~TbX=>{;z9nC|cGwGK6`E_dho`d)_Zg=` zKTAuq}TfcQQ4UusbEQ{(Px#y`*O<; zA$Pue9VaS|Mx&*-K+=&s!{BOH7rpEJ5BVT%* z?xHt-wr;-lcRt+>e+RgbO1GVi{S$c|z6!7D*DkfaEe?BZEylgeGqwg1)yDbdsN#0_ z$ywwYed>|_=``{7Ib6hhm{)I`R(WM8 z+%$ZDAm&B1)m+7!boMCndzGj!VhTmYABX<^6%E!4oKfk#S>4z3{gx4rlX$GJn;5;Z z%@@XHLo3i0^x|@pkl6*P`KOj%{qTN^)dHTGKq{H^HC$11PJGrx(yY z;%bli-m_lx8(ctBHx^dPkNMRAdR#UtRX84e{5CkrFak-tzNhn7Uy`mTukSXSl$W`( zOugKb(bw0FQvR+|||?x};V5SacAOU~qFFqk?azr;~-ftF^FG&9U@XqO|0q z3+7##{!}5CP+KD*1q{p0(_Qgu&UV|9dYX4OU+}I2_x4N7T4m5@4wqGHQj=4e0tJOG z+nyU~-&iRm;R58XgMYEmUvu>Yz~3x5w}LT<8HP3}SC15jDFq^6vC7F2Nwf^_d5-g& zAx-q6{WdtJ?st&9I@)bG%C%#hwS;^D#$X=)9ZFG#Lwi4G+rlIwk4rIh9qeW>J0JIH zc=&Ad(9jt4BL(7REEIav?B;*QA*lv^3o^2vC+t%w;sh(YV#q0JsVD?!lJ$&zl<$5~ zr$i*vboj`+*LBG|>r&R{8h$;uW?|h^L2Vw1=&|PlCivTJ;Hsll-3J{z{ef z!4+aAF31~Zto8a?XBB7j^Z59-JSaFq9Mk)x^ZAZ+W%Z1Gp5u{E?$RHYjZOYaQ|-p{ z)!HWgbinq2$n346KxE_q3Ye|B(kMPfUy)EIDlQs?O7!CUIxm)IcpnRe2SbrSTIy*F(c@YvRbthhx(MRMR><0IOi|-VUgr2cZ(!eX3tU3d zPCg5QNBi>H>vRPYvMF~b6vN90SL&e2e^js~H*D)yU zL9q5lY6;;LZZiqRG9owrcut8PjUXsUqRbp5<8moCS!)COc)x|c^1|K|F)lmwvJ@kQ zlpuw2Vk043R@Vssw=-^ETq-SU>w3)jZW@+zWsVHbF8IQ2WI7VK-q@Y>AS+Ad!eTI27LEAHowLA2bqgUL#-xT_~h0XgH>IZ}W*j_+*ilh6zd(7P-8yOSx z^Biu}T++M!`3FbE?ptbo(l>;iq}pNmc6n%qWh|F#{zm1|s9^2&a#Yw*sg(WjffIy% z947j90Ew&!F)M7B*fT%9{CrB z=EK#k5>b}do4OyQo-3hNJFeaYcdv(quBF0o${^B+gzyT#$Dz`t1?kf&(t^8(`Ck&g zKW6!rGMU2|^n3dTu#xT&n>P6>$eaf#jgx2I`1RDGOc|V>XJtORPmOU*5o#_;QYgAn zm2^dMR-s~1Ff!USo`t}C8?dyV*7HTEV}|E^7SLLC7~Oto!vz!2z2%`X3c6bXlPwzL znlCRa53kKCXF80a4)ihxoX}5EpXfoqW#Ml$pXv^nHL98QF)4gF=@Gxh5A9j5W?km` zknZ?(J^{_mY-MuQl1%PQ8%BIFW=xD*7U1u|!6}VEvrLB{>**$`VP_HeIyol0*eWVe z9-!`lLpzO2oUZPiQ`t=iE?VboIS0vax5%-HIGa5;q3y?@NGdoXnH9Grk-m;>?yrZD z%8`rw_;TSR*8YzcVE14D>?W$o@x;Qe*m)ZU<}^7Kb6+UIqEPJXGDGRoV4`3mO3LB# z2ub?%?Q0687-&g1II({GOjltf5lq(fQwut}xzD}$UqwjPia^PhN*~oH4#4G>NL&ZV zt~W34fOBZj{{7%dhOh>!HneprHMS>_4tvns=QsO~?q}NXeX8O7cE91pV+BIQ6B;ZD z5^{ul>U+ooOBBo##G5P>UlLz7GW?WU0!60aZ*Je?T4&imDf}EXZuqUA6yDzIIXq&Ba;1p_j)H1BGqI~Vwvca_DPd? z9Ixkwx7Of+j=A#=Fxlk{xlrR1t<|7LMu)f1Zt8(H$WS)JA?Z#aMk!eh69U&|O{_va zB@sp1>^;HI)|5#j;ngAf^~gV4eJN*$DaXAwMi|v*{LVA(H@MDpzAh_$W22BynDx=A zLk0e3TN@l+cC+bi@!urnsrR-nmy>DVkHH_2HJU+|E4qDGC$MN%`}*@pZlH1#_M6)th{h!?6M?H9akVxrP0(cOu2 z$j7BF>vaD(1mo_sove4y9k_2x4sKU~Kyy_G)?%`kK4;GVm#x4w7#%^t76h=s!# zDQ=#{=6>&U=2dGQ8L;cqSt|*ok56zp<{{zSTN*&>7XH_}KidTx`MXyr+G*Gv=Md=j z@OYgtdFx$sQvzHD=ZN$7tw7)Oo`)?V4RUo%#+cS@;Z501Hal8wu9wdbOT&JVnKCz~ zN#5XrobWd>_#0p=>13=$2*~Y!Tw-&wQE7xBh9_!&6cl`{_mgexF6!hn2dtQXw0zv_ z3I~s$`5%E*PdsC@{`t9HNJ?6W5D!4LOkF%>4i)z_nZl~i^Mo|jbGI6;&@BsFRB8x_ zElhWb*vh3SFfT_CM^8J=mB=4A>;~!c0+#!@V^z0` zRQ=#xS%bd`IY}bJNf=W=Pnen{-HxfOB4u#??v>fz_^lr`aNJ){B`dD|J94n@8V~Q1CKyc z`@n$T3&!F5(Y90>yED^^mC+-@2;o}rE3der+tEj6?_05&7SWw1^S-RiK@^A1{9Y75 zz0l|Z=CLK8gkM}6 zTuu#0nMFS@K5$4~U$K+>ONVP&VMt~wlkelEULhTIuH4_({E|+aSkg$;LS>>j#$g)E z1!jVecoeRxY@!w=U}JONW)HPi3an|jSiF9&E>%KlZwct|`P6Apd%?rV&?b-js2^Z% zLOZPVycpZz8@){XGq6yJ&h4vQ<ywUB$fn;J?sl;;WMWbj6X+fI`q0HX zFc0OLkA~0>Kl7r%uz_Xx&ax+;*X%Hzb{4zbFPFLxEG#+PO6@b$vDXU+|IK`LsRz7U zWZ&WB9#k+9CnyU!-dT0Kf>1pktA$K!IHP7_B_tQ>rYCkJ7!w7|FDb>cb{5bnA%jT}S!#q?j&(^R!EE#eDF}<^eU%kUq+U8`9A2 zXe?~qPm_7OVqqk4Ko40qlGaY^qlPc_j10PH4{a7g=o`6yNxaPsL2d(J%bYJ*2lKQm zE=3HwkYqSQn zq`I~Y7%HS~JX6q43HG;cJYzJTSv65@LG=ccvEpJhYJP7TpK%U#C*X5KxGfM(U@9pj z{?N4|N5P{tt>#{Nup6N>k~)zKUdnFw&L19KE@v1V@dV1~DW+5Y$VEXxJ>SgD>-Tq) zAcx1{Zy5dORGEL(c0O?VBQ^S4-_XptJi&5}(-F?X-vofm(3hXq;UERxSx1@$#1k3` z?wqmnkk?{TIXvulJnr7VsvgG}%^yS8b9!$5>o`${hxWv}1|<(e)0*97Ssy{^NEf4? z!SB&?gd2(2185Mz`I6@!4-#Mr!VllgPl*9pCAw@Qp14 zmAC8GS6|=l93fG-e8~~)g@#bvPdhW~vzVsJN2Q53DexR`R)SFR&14U4;jdR{Ev zpe8^l1KVEjo%h=Qag4veONJMFEc~QiDMqWD#t&DIxK@jwzhYX>)%QIDhyIDio~hAB zCHAH6=#VNkX`Hsk3W>{XQR$B+T@d|60mOXdHUmPDrs;dotaZ_%ziV#vqrCE&o%q4A z{)VcmYa&OO)FYSBU)Vs2&kj7>3_SRR7#7&|xcZpj&c${L|NaCmDxw|oMRG{ZX3h6? zSqL`Ks36FaaZwBFq~f@%&PaVZ9xQnZ{!^?xlGOT+#T!%MXlBl4NU=7Lx5w;vn&;?o zFR*T0Afu^rQ&2P6_pcFg*HB~d*ovc+9!KQ*GP=bfVhlQ_(uv4y4J?-d9w#gwK+9zr z_6n2*A86=&c&d$Y7`5ct!Ai)kj~13--7T8vJGr*5_vDpS!74dSQTs=gqgCL@v-6lO z><;NualJ+c4a#p>lcxrSYJJB`hfa?6zdS;HrRyLFV;lHdnRM{3x%%~eT$~TnhD4)u z;QbY~|WdDmwbRa|@YQF_yv}xD|Jgvwlm9Dvad&E$g|^4W1BHK$1oGcm!9^fSg61 zxu-9Js#rBlDs7;2l7(kJyM-=T4dJZ=mM-EgXUq|y%pU``gW7S}Pr&bq0H>Iep)&5U z31&l=^;6fs61d-$vSHN-_at0`Ymq8hnK@)sQw_!q9Al~>_IkyCZlnf`wZPrU8>NJM z0g7<)ov9)$^2tKC8+lt^B(HLMgFM(J0BzRf;&X@p6QI6(et=5{zh*1z;_l$Xi~bSf zrbhAC&)QW3Z$Z6s7Cqw;>~Aq?ll=YiHlOxlqE3D|7p}{haMzjOBE=F1UMA%)$tcrYAP(@w8Vr!98vup zV&82chwL^Ow2!&tCiYo*mq$whUS#J^0)Cghuf+Yh98c_4<#pTr6$(5mINMVQ%)tL0 zyjSVnj}s+ArGJF;%>a3xZP&aW_|mPEu4glZb_cQyc6T$%;CoqCN`49)r>qErS;Y)` z2QyyEO8ovJhdHKcPvMf#J^Vrd)nc`zVyrdQ4uWfIsCFlgUO9Jj^1-lfPnWV4xVO9o z>6@;;P^&xTg|~UACZp_w!YHKoOW#4;Jr3d(mG!Wv#=BMw0E*5~A9doP5``xcA_Ya- z{!~cb%TCV^>!rK$d(Y;x9YQYcfbNEBCv(U| z%6Zx5AoS^Ya%=f)j4K{EI`1nJp?*Ct(f~C>TAZc-_8of`jfUt?K}O6lj}`;ib+O6( z^Hev|<{E<~34!<&x5=>g73S5*5nGd)EDiJ)4>Z$ARB*=ehysz$(>0()EGk1B88`N2 zmM0v|41`5S-P9*RjYf@{vI$q!6=$9r%Bm$}!=xvfY5$OBH;gXpmn-edy0MlRhF!7B zO$8)z@cXN+3uS&XKhMMZfdeAcOIHORw-|xFod=tboaT3LqFwetK_l~-n6*>X$rp^$ zobSBb4r}@0!GL=U>Fz6Qd4teN@6$`tHlWlWa@tWTWv?7>HD9vsUzg;NjMr(RgMNet z-=JOSid8gWxkE!D0Al^~LsK>kI6B3B1a3Yk>1Gr6Gl(_C;JGjaF^I`0#3xN5Y7Cv| z=E8Bf3t!XVt)Lf@)qe2(2D2L+FV7)&sTZn6G2=OZh5uF9mO0FXuc#f$%=Lry2k;A^ zN`w`#5Qzg9RmL7c0|$?XXMP_!y+w?)!l~LVIp)4!F0AT^1~8G`y&v6G(#|~%+dasA zxiS|JFE8H`%Mth4lm7NOIl^FJgQwf- z#>XX@{49Je8cn7In`VATrJrCz{B5f{t4DA97sg4nU2~fm;)vQ@i+|yL#>h48tMBPU zVk35|p$GbmEgp2}AtyD)$ZEh?5@&MuJ<;n2px`qeG?aq=5ryW}ecx z9Q>|+(t>JqtQC7VcR*Y168Q6_)sOFf^Yv`J>kWZ+l7FIA@eP7G=Y|)4dFK+XB_88th<5rtI+PeRgZJ+VgO;+*XR~?HD!9sqzR<%bpO6q( z@mjm5>cKuUpJ3n}%`~h6Ry&GWYt@xGmNm={c*;`O4nxN`uPNZl!N#|mX^{#Yh&o6W ze~K0eBf?8)$ZX}l-vVEwV+2Uc?&UFlT}!UpZyf9Z?x;3?8GrkV6l8y*YHm$fyIJfygMu{+#$9V(_dgng^x)uCBAvG>0A?^sA(>BG=rrQq1@=Nu>l#$mIwu!I-vKkV(}dTbG9P zZ2rXf>}`fVoYON@&jfwrlAVGzeW1#<3UszI2u?IZSv#+P<{PV70^YSByzvV?2j->q zp%hG8hcH7YEghWb$#0^O94>sj4|}^}_cR8fNkWmIJi<6Y_*i8*<4#-9<7VCCXYQG; zgdAP|xP_gw<`Xq%{V}Ey9`S*^vClOR~W?Z$){GQsFFE5hbeo#(rNRgDHSC;V9 z7ak3&oW@dk!2SFY&+=Vj6vk;l^yAmG#wjcj1M%;zH~!PC8VjEsyH46V`~`>awI4yd zkRXiI62wEf&##Es(^F@BablZq5nnAX&)o3c(v*v3^ntc-Ll?rE5{6~Vlv2RC9y_7| zON?UFFa2kIIj(vgVv41KR!yKhZkV?B`d)jsNK3l>?nJ|me|@yt;0YXoUbpp;G`ClB zzTx!Dujr*$n5Tg4YyKu6$sMU5Ka4~|$~FM2HY@!-jUvL1Nq~0s(DWxW%SI8nj}Jdt z5QF0jW`8s(!WCQIn9^nqFeQB5F*|c6vRHR`zG3yfJAC0ekbx<6!#%}7B&04l480J3 zqxkG4a9Bq2()GL=CKMzi+!PS3Jyoel5+F!O5tr}ge2)=NZYW(Vc0`nTZSSqcg;-i093fuDCw5j^ z9ni;E(T7kL(|IeND>YsT?b<=OdjIF3Ra{Dk(6BF^UeyJW@FayclVJipR}ZxD73|XCn+cF?$MDxOjb`O>m_rjNR!ThLT9(_#LlF<1eQFmfR1k#> zaHm%%r{3RCe|!r;ILr@!|0>g6x2MYWYcyS4%@o&Ri5GXuC`ddX%KNOXr^PE5B*u1? z%tKf_4upu)_*pXdYRZ|Tq@zb}$RyX@IXy;r3UfrK!l2{BuJyZ^IM?$}0> zI=k!XNz=TP$99NaWrb%e7SCfcFOoM7uegr24G{P&|sgFV=8C#e{|Z5+a7 ziMkH3y(xIZ#|tfA%XwNp!-G@mQ1ejBe2qL$-08Fy9(M?z7N?9_f4g1CG9?7s9@tvI zp6&BPr7v;g*6Zs(o`st}yqP1My;G7Esh**mX+kRo?O=aYOmEgEj|7k|@FX|^73aq! zNQ#u=Ux8+cAxbud+`vtU%xU5wXJ{#h(o9pTg>_gGA(hX3b*=aJp}-*R$MLhX~QKO`$&EeQk53D;RkBh zZrSJ9(^cduR)ywxv&-L=7 zw%+wZR2*6KM#w&sLQ8;#Y=OuYp-3v_MTveG)tsX=YRVa6z%hGa5RNFrI~oepE6I== zLf#fi+`bm`ElhKZB3$cP$P7ng4p#D&9b!Z*Nn2lx!BFIL>B*ogg>&r?lOJ)pWcCyn zi2rueB3zVN{Fg)M0nTTK>J%Xh$6F;17N5@^gVzSbEfcFclw71_|81a+he-NklSUb^ z+CXg8S=2D!p1syZ0CQwFvDGkzM zV1K=Cy`v~1-QW9Q`hZon!TVjHk%-DDJfU)@UfbkYQPvVd84cFJ_d9B?F9aMtg8y6) zH=#`*>EO-SR8OXkfe&rZV@ups64&!5Tw5P>3Q3aWcj4wGzicyE1=fe_#dyN*-VaH# zebt7;vy2Qy<)QhAvO!Qz)8l517r?L8h6lh$qNNbfS>>G^G6FQGDk|7hGAd^*z;leU zba%Z0m%n_n=~(RYK&i?@3hvmpmRPplbPU^ryQ&V#Tj-gA4}=~d;P_H5u~P@AeOJ9K z=t8`%T|QN!iiXgkP3i=HxNhE?*L2-X z5b<1c-wR$jlB|KF8G?v4tMM0tc-Rp(7L#pp!)Fi4w|rKQNe-xf{8D@jqy?Jbs%#Z& zRe1dLJww;x?OxsWmvfcF*hZpz5KKo;F*@#@8c$BnZfa28&ya9gyUWu2-14rraE3jsO~pTIj@Hwq8Fm+)W)S&Q71N`kTY?JO{JSD{9FJ-mD~2 zD_FSyw~Jra#PkqhY=Y)QG@K#${amf#Foyd2iL~>q@%dNfE8{8*Q(skpcBmNyXFtQR zIIESDC*8rrYjgsw(X(dkzF|nR@k}ALX$VEUEAreo$y_h2cIUh4iioz?SwdDoWqiT6 zQvpYznJ3-8p$E5<5Ixp>A`!Gkd$QZ5vy=(28Un)@Qk|CTGtTPaE%oPBmIa4L_2N!) z&9Pl%%THT-%nt>3Kb%qDIsCg?uOzRFzu#a>)M^B&L*2G(QQRKsu{H2{Z$!PzS3s+CMR^&+|)=!|ye%wENG4 zftFSdXH~dtl_F^J*8IM^g1bOd)i!+XU7^%e9aMxm<#C#zk6;O8L zq;pdf%3m7AYoH~sK<{S|&eTwcsSiLd($;X*AH+^=_Qe5hL|QjEC%=6?n7zJlASw=x zA8wI_v~NG_j?LWUc`yb+U&hrj6!$3 zZF$YS&+w#RW?6Ka{uM8zR;QlCERG=V)V~y-=X&hW`D^7}E9>~iUh{3d=z zc6q%xheh~8dYsc^@SH7Z?5HfWMBm4Ykup|xua4?BxEt5sj>e7^eec>5m<>Z?qWuhZ zdgKK~$S74=W_}ff8{)wA;xrQ+aN7jzf86;gXD`hqijs)FRIVrd4_t#he$4tu3oyK_ zj78W!sO^```g0Ujj~DVivUt-Cm*3-{P~R9c2wjpE)e%UUXF8RXXY|dH;`|q_Sk$2l z`jvUoC1pT_2`5ZwOn@`N{E6$gdatuoCcA@eZaL2}d$P)63Q}s35k_S(ZjSo2eoUob z!(od(!X0;CxIcEp6NHj;?UNZz{Z>j#8wRg2+b`ZJWKP)q*C4F#zE<32damVcv7e~W|%a&cAX1TT)74$1`*VDnyc{>;{Wmd0XlCJ*Vt z<#9b&^h4I1n;O}nMX9(o`(Z*@2PsO6ohFVml|zKXCWoWD%A_|y)Yl-u)4xd=chvAW zS~&aA=YmML*52STH{Jd}r|#!9W^fs5vpH(`rtE1j-d3_})?}5t#Pcc9(UMf2xOT=8 z0VA`7v^_kz*RT{)y;8ggR-XD53%!*j;GCN<^DuzKy*F4{N?F{nsVy%mZf&VtGU$AR z@oZwA*YFJSkfxy#?wV^H?eYAwwzx9M3)yuMfM`P8T968rx8LNn9BVVyGuG-+@h{ro z&Y2tQ!=ZRA)SwiNLVl*`;}OHX3{86>B5i-^aay$z9BB)}CHs(3(q@%fL@?;Y`FlWx z`^shL65I}N6*IGs?Z_XjELlLQ5DgQa3NKTknZ_I2$og>wT7*@VLE2g>SW3~m{;b)3 zO$B>WVJRqHUk5d9iz$eC$a$gl4LwN6s(9C?H6Hmbb6)FePWb(qi^T+n@Y&Q zuOx@{&WF|S_B{jK&f?XJFoPcv%Np{>Ry%kU516V_m$X1gI{Ol_5;RW80z=L=CAlFI z1O8(SxZ>l}vF)5bhJ>Ww_v}CGDMFFpPX6mav)Pr$c}vnaydrhTsXonPOuyGUfqz@{hHJ$ay&6LAy#s?0 za|H%l8K>+R|8V8zPh9Migx%Sq9QIy09dG{`1iNLb;Gr3r2Z++ng=yg0^yKp-BQ`Xo zYl|&!oR0prPUn4U$K$2O3(YD_>#%SgzZ?!D0-cJBE#5FH42poQJkeA=KN(J;gW|@g zG+T)xzSu_egeF6D(eRj90*tF)+jyq}m+!lc_!`x-gqs8En%`a)gHTh*0xtrTSUYdC z35Ty3C>x1VGSH{Zc7W)IT|PqQ^gSCFa5s)Ck-P8eLsu!3t%DcOLv4ou!ZX}9$GK{?GgwM03+3jR_+E4Bm=TgJfVEScVNdBFLeV=m9mp@q;rAd2o%nh+Cfag%Kp!-Qth(;lRG=MPq|w7 z)MOjDO|n*^hO``n{BTX{i7@Cq@;kuYeCTmpH?t4mr_-Zbp^3Io;C3D7zDsu478WKX zN4P4i-s+}kD~DVB=z)`mi-b8S+UVWY2!bA{9b8^(87H@=$7z-uElg2Wo~LT24wc?w zwtpS>0I^_3_3<1nHygYyK{%#}S#Nrr+b3FnO|MQ$G8meiT7IW_>QAr|9u)pvQ+#9m zp*yPf>gr$I>bMAk0>6(RfmGR46aq4E*D~<<>}dc9GJ7 zywaj@!(ooTe8?!gG2={Ud(=8oy>MMOo`igVGy1UBJ>KT8H!c;n&5tUtk%_hb)qvV< z*Fq6s;>=M{%t7g0V#xp|`M~_l@2bgdqUTqggK)Ug_=RNtv#^AB08G(yDCqyfQOvjj zBOS{D(%IG`uqWyMZD4)PWnpSA4>=Id5+$)a%53S;YA_yMFGd)1=Vfk z|FaH6%wK4>n&a9=MTMpxmh?b4z9+Ws*K=I2OlkTq#0AlG8dxKv0ntNEte+@|ybC8Q z>b0M<`Q?n;jRwM9iX$qDH+}3*xleNBh2M-JMc;AdI}7bhmVm>SS?@%+xSV23u* zF=Y&)LinCT~dn5(fWant+Y;x&BU!;52!r5yU~yg7`Eep0zMPz+*IKgyFiObsDUrRQc5>ITHxt8Wwi$0VW688;KG;ze7DSW6^YGP#jIrs}QblKQ!;>o>z-p*4|LA)|l?Z=Ry8rj8#Cgo*y ze^?rl#w67+<%k%E+(c<<<$;D9Y@9pJeUmtfhL$9CEd3R896o}qT7?M=2FdUiUk10- zlbqzaB>!X~IbWzuLx^NFE^TnnO)K!fwJ%~g#xf5onN8BA_?1jsLc?8VIEOLspMeqA z=Hs%GX_#IGcuE~y2&dkzNmt_0XQ8su3)h3ZSWoaHlY+OU&kEK2(TVJ`==GFKprVQv z5o29vEd~7VAtf|?rJpt+-5=6;h6MKP?W}oEFwbd_iZ=3B2(b1EaP?3NUYTflFV>GP`lLje|Q0l7wfh#9q&; ziF!#{>kSL+IHP?2)eJOSy^u)8_=0w|n-GhzBgKXygq_X}W6r9r7r^j=6LUcOb7Fc5 z-|zwf>w#vu(Y}ba?NZ;$n``!7txKj$?Jo>|r<*ajK4N!Grjs8-W()6K(9s9vg@55+ z(?gwZFfIp-(%y*gIADx9|M-cCQo~eJ{bX2V`=Z6l+YnWoqciv4y{AUw`4Oz_|A6wz z0-U9^UTo;RM(WsmpdKI_z{xzsl3PTki=gaK2J}CHcmQf5$j#cIfp-Kf4YQ%x;Ty-o zxd(edMMT`G)tOH+W4OAhnYyA*xDkj(m}RKWTe~I%Z*GWbZG_6~MIXx31DadBT20+M zeaZy94;zn4TfkVnT^tM^J%LS;A+SmF(!>fY3ci#0JJoiQkC$AwTMQ2BkXHJjc3xVAro5R(I{77R<7-^JLa*R04YTaV2 z9byY#j~uPf3`LrfjqxUc$^I~*v_u+d+v>d6eMOhQ?++0`@!JufBnoFqws-$yyS;rqC8@+IN}WGlCA^Ozn_1w zgZE7V-_h{^2&2+GqcSzrHHS1F8d)56(9vH2YXCL4f@Io!ByO!oe|Fdl9$c>y|~pGECRJnIfC8~ zM^>a&hB5>)S$u~iqW>livwq<8paK>J0|n#H!~=+shse~s{j(Z(rmL<4TU)=2u;u0# zLFxhXTmA{$641=0boNXK_dEt)YJMi-_<)57#4Jf?s7IPjSv#;Oz9~f|O6Bodg6JI1 zfq;l%m_^^jz=bD%@HR)b8E_rpb6S-CZlB{T0Y?;p-v^V$X#VNO3SA)1tOFE%@d#}7 z;l|&jrDNRN{%baNZ1C?8U=?Bs9qC9>{;2J82{+ZGYC2vo*dAAFyi-+d-tl*#Ek>zA z;eA{Xe>nb(jrL=L=P?e&LaM{D*j9gQF_4+%?wNl>VgL=gxLis#+N;eRaOfq*} z1eAlUZ_fzc@LZTb-aY725L7(gxql36S+M>T>mJu_;B8*9qUny-!1*>F57P_E)FOX3 z5-=e;>9D9H287B7WDqR%k&u4@_V-V|>+=8iro?`i5VUgKIAFW`)Z*>Kt?5c~V$+kR zCBU-LoXF%4Jp@KO=&(FR*|=W?kVGI2rj({X6+i_E2=>(nMFfM5S*;oJ=Vaz_%@QyW zW&;BYu+S@Iqj~xx=D;1J*s{K@hft<#dZ1T#f9w1i*eYYhApUKiRvI6-BaIBUJtfYr z)SgxOi*8_I&$NtXF1P6$S#+KwHu`@JI~-x&=)>>-uR2b7+PX#D{9N5KSX;#9uXjZR z%F|};14)pocfCh0tEDPz(MhBK`=izwA@H2+GO)+SiN` zsT(pw9{0;WC^JV5oeRK@fF5kcX`AUj#JADtEF|h^O4OR4B%@0s1D8{b#Ar;k%l*Hx z7zaEYN%gN{tx5D?uvK+y#M71|TT9Pn3y0mdV6A9|(ZmG3LZ*z#xqi%dxx?(tk%&>r zy*q{kiycRO1!{Deg!~LH55FoHgG11n`2h9UaqNjyl=(AB!+{m4E>4tYs>cy|JS*N zL>5!YRMjgnez)3yMV2`UwS7ji-EFP`KXo6Z?w49fu1o}kDZx75lkK-hqmjYiW<>cb z5ylLS+rx{$xjmt{W{Yg}pz+@_s<)fufSLBSh<*labdVxX2rq8Jwy<(5f+4!zCOwTF zCfp3mGg=mw-7mXy{;LKB*KNs+>+Y6X3vHP>IDxF@|GRO>lz|jh;)G_u*F8+pHsqs* zX)f>aj`0s+)p~;9*In88fQ1J=pa)Y}fk`=eJ(>W>Ph#}Ni@}wdz!sIW8)y-o%7UJc zV>Xix>A>)7*zpJC&87-F^No(HUtG5{A9e@n_vO)5aY2HR)t<;wgul)}DN2kN=g*e7 z5AtN{D(s+ZR(&2m{?b=a;ceTtAa0 z9X=hVzxw%no{)ii7{&yWbB2M6#R3ok1x^O$6$uL@8XYKod5yf(lk-}B({fQ7m8F3+ zf(vSBC4?D`XL8WIzG*_CgNFj=CGey+V-trkvpG30c}s$Hla}U<2Mk{GVp>gp77u0G z+cs+6@oJauN&dk)e|2Vx2vIzk)RI)xwt{mAMsyImeeNsDf)$z3oubf=&JVBmiN#l7 zOTj2ix>5cQat@Rt5XLNny)mkJh5gs$>OS=0gBRM9g9Ks-z%bKu5aD={(fbx+5*v@! z5|~g|rZ+-Pd;Uc9eE6=0Yj%M=?|%R61N?r&^_0M>r9gMQs~45-1X7KmuT|QgbnPc4 zNkoDG(`a7!<}%c#%V8}o#ACBQVu07#H}v~v7o-5+1K8wPyl$zh*{IB~?X#{@L{63?|%VKZS5uLL+9WnPdcdLu&LQC1v zDwh90NK2M39E33Ph^CG!vqsyscj0|@68|vG@4i2qsrI{3CKjefW@s395yOh{zQ*2j zLG+;oA##Ce&T&<~O<)_iFy|Eam0ZLN{S)5DFSo%kY2g6`2l)Hoj1}Urp5O>TBbj=$y`98q0rQDIty+X?TO^#{Hls%!Gdub*8dOI;NY2mid3mw2%yPt^qzq| zXh-kP*m6J1bG&1@Dz4%62kVz-gw&U5%wY1RroW|@jy3uHOm3_Th;0CPF%0)>jxC7;p^VKEL_)%o1KY?m)V08Z{Rg5a)wN1aQ8|1$*Y}36NKJ5K z-6OvN5fsQO+p3M`srGKxBWcLQwGQ!%+cwVOwtX}AE$h4@sp}1pOzh*#M*#ZN3sTNO z<|*b_ZIbDl)gPD1^f<(P*B7l09I1?tpr+Ej3HJ_~>e&B)_Dt}_EOE4tu1S6DT2TB) z%3Sp7_=l-W8>4Tw9~g9<*OtjRumS#r7#TmYlExBB&$2NBIxR+LMda%Y=aoLy7p62S z>CK+boEE3EDxc~e6*tjPWgJ;XVFDr1wmZbSlP~AgG2A*osDaZ@8Qqn?DFtt82BgDT zIekXRN<X6ZG9v*pjTye}6z&$)n;!9I31YGzR8TQ2k z3zGW`P)H}ufolc4tyEgW$NkI+qItokvzrqYB)Om|$+2x;u)01<>+6-6(`U@ZXn=Sut?^NbN z_RihQU>4s*$JTpZ+x|M0IfgzakzlUy$Y>G^9FL08Zg>J4Y^83rr@Y}`b3)ah;oXd6 zwlk;C>x|_?CG@_h5ahDB^*Mtu098JT!$@iei8fy|s9D#EF0m^y3nW2RSy9kR_u`tp z%1Jbmew1A98v#G{UUbWko5e}&>{+*ESTmV2jThiuaa>a52>;LY`4CeVO4(gQUe zIzaDC-10ulyH*0CD^4xBQvJ#Nv8JMn*6}PUNn%W*xrimQtVSs9>NrxF@oGO=M0^Jj zykWK*r|YJMuBwon5D=2PhP(T8be#7W3|Hj({#GKDGXoNa{v> z%f)=*$>OIOzeZ+nMXIQ&d1M7rDS(KGJNvDyl+a;i(+$}@ z4J+q{_+SAFn$wnp=KR(a(Sjyly=sqp7h4M&=K)Clg-a3HstK++JhON-K5@qPmbz@( z9;%WN3+-R$0jOutDuY5TnwfCKckCQuIW+!#uyw+J;yZf zuW_H4c5R1g7(m7h$AXN7k-2mQ?6GA5_D5?1KHQN31XM=QUls$Xf=iw5u};kfXm0IL zEXqa5LY}5F)-%Z_JrbmvO~o7**-8+B5$}h34Bk7?wm40dq3TSwCZpexGLxCsNmC(z zGbN56stbuHm8WQeL~11*UGXV*#Tp14E<;zu4HAiES6W^jv>T&48yx9~R%_J!Zdksh ziS4L;#qlrbj(Q$ETPd+YB+ty-Uv8!$3HVNvwp&VkOH;uBIJD~+RP@j)=bO7L9QjUq}c zLSlEgm5@CehA0R<3=C#JW~vfExa|y>-x)dXPeGm_$nLB3J@n-<&d%hW7%2bFe4)&} zrOYZ?q@%8z2x5@tAjf26zU>|Qv)M?qUm5~rKGW$jgb+RSrz<_cjZjdJ`YzvJ{}&kI zylhg442+#8$j8GS#QwnpGV**$*|@$p?;6&i6IS>_9bMeaeZs|Xvc|CcqAPx*#$sbml32mNh&-i9 z%qiXY>QoU8E;{^l_X7SEXvh0$;TREtK45@A}CoE{>CM_?eh!-sU<-y z7yUp!lC7_}e(sF)wP$_Tx*WjNF?hc~H-pmWey zZ#!mmfS3CkCnCVe)E~=mjsml#=lyH@MXUO8v}upsDjY>acSGB3fnujPuSG+7Vk5LE z`0mt7iKMo1;2Akd&QWuCEZ`4Ov;-Mb2mKW5n=}p35KA>^YqF)z>0QgdWX69 zl|j}>^Qf;%&8VbGS$}bOyTl3eln=c!j#|_P$p0{&+KWzrt4iCTmm4{vvpDz|p^|Xa zp#um@wdd8+8BPlRPxR-+c+)I^N(O)Ho`ATBMkQm)c2slbCRGn zqyVqDfVblCXG<9q^f;cB5{0FFpX5ScARUe@&c;AuUVYLS)YC;Fq(U~=YG=K9rI1m8 zV1WZ7S9)u#J4kRc-14%5MRA^1^07ZgZWkve1vSsphnS7b^}(~=Xu$+wuIcxHM;l~; z>EJq5g03_j%1{D%U*|H1dcP1#Z~<55nbmTEK}Eb66?nO9FoP!XAFzBXQ?M#!jC=)J zQ&XJ|ZBMUtPgD1K=G~z5%fn8V1lrs3bhEz zOy-#1)NCVN{s$v6;Ai3jy%HXeFP*6=5m7~snDYuTFpd;3`=B#P8>81I<-C8YJ?+nN zVion1h(fabj<6sAYFF(5Uyr-v99K_(znR51z=Bq!%z*RNgD`?I>zT%NHHSwvZx~vy zbbqS7fc{p=9ikuOu)i;~Um#$MSycKsYo0wMoMDM~w7=hwPVpqKi98)m{y(ID+#)gh-Ok(9Gb>k*gW-#EfEar3ziCRRpadip-$P`qqIcDzQor1aN z&q!J(E@e!|i$6x;#3W!IxS*+UCQ+*={-%>l>=Eg%HwChb`MTUZVs+>(qh&0qQcK5M z)Oca>q2oP$dWXiTt2YlBb)bMcW`<)Y$}IQ9VCu0>V3U+u|3*9AqzFE$wZzrs`*ux* zSWcWdr^Xdl6_f@dieQxg$sKxj1eC;)W_P=zqIxI*QGKFt{!gJ$)URjpb}%8$I@I>m zk+%&E|IR^1)8AtLf-cX%nnz{L3to0`2d4#mk-)nzaKQ-Beea$HeYF(JuMmz=5H<&8 z3He+#-Ml;P(Lp9Q(uk-6AtK^}ZG%?Y%})G4jyuWYxu$Xwow0P3)%z1%n1kHcMwaiS zs;=>lWZ;7B?gGlTg zIyVg|zczG;j)yM!I_nH0Z)MzdS1;SCC%T4j-SebfEMQNMvtA@t3l#Q!~XsLc`qXmG{+QmPBgi3T)7==*X>S=Rl^yB?Q z?JFD{Ze0%0SVVIGIW}3g@croATpk;NvHl)&W|k#b{&4j~nQG77$-qaKEyjqJLAmmS zg{kt{6S6ivuG{Fsi2=H65+1ZF9~#vYYc20&?vTyT+HYFXqkt|Vy?f-wO{-S2j=ruK zv>l0B(_?sR@4@Sd#cv|E7hQ7vyv*mZS<#El*rd~iWavg*jx@=GRp*ld}=L3f%%cSXZ1$ew*J6mbL#t|M59H0r3=tzYfrp@=*f>Xdikx` zc&2qXhW@PFxbk|JW2jd_z5vga(6V2I^PFTRl!TgD$2uD?0HhKv#Fv!)KSWN__l;6o zDL{bZ|M0#<-F z41>`%j>+9)85>wdLBY(eC|zSfEaG6`l!7UK|CAVZ21B(L6{zRns>6*%kfz|x`rh^< z0cp}>epcJqi}YPs3IgD$0-jEnJ1ryV@4dUNC=Hln=KJ;F)qWVAPpR_+>hCC(=Ucd| zv@Z-K;j!}qB5y*B8oSRMHs2Mv98c=%u}OuGKa+imm%7K=JXuzWn=G?jmDS+W>n-i~ z$~$Y=fg#W*A`bVf;wDil9wsi*eQ4x`(_w~hUIcT;Al9jOx6rnSGHUN|WRb~hritLz z`Pwg!qs4pFgFG0ViBPVhc2d*dGcASN5$Ulc!r@c8Nl7h)8Ft~4ErUG>#(V@!g`ff5*6~%}(-IWS#{ZhY%(npWPpz?A>y5NL zpN<_Xf?J(SC7zU}IERDzY$?(h{RFITYJD88rStao{vqIIDd|=4Di+ZBFYiHqy*^tn z6mu`->Eg<$g4s^J=by3O(1 zQ??BemKnY4pq^$(@>*5`F=iBBCgtc(6Hy%-{AggK-#z5(wP&PT?T?{0eI~zX%+SWt z_>g=|hyZi8^2pDj5*J&3p#e)a9{uLhh4d}X3RUfO2I?;oZLx2nV<_whRE=I zZrQ&vzJ|n|P8j2VX@A$6Zr18-)Llc`Dl)6L;0ML+{&8`o5S?%X&QRrGy}KsOH1%BG zADzPsk-_Nr4#KQd%Nc;fwEHM)L!c;ip$nI4U}{`AK=HiKWb5K+QBs`j(2A%iI6*PQ zf-qWGyL9-GQ>aHBwy4iN@+Kk%_eqxfj^CBi#Y28th61h~A9Qk+U6 z{3U<%F?jBEv&RCc4^ZJxHW48ni#yvfAcM?>iC)(AY`k@hu6e{p0Td>uSwVaRjyUsZ z_H#CCsHh9+RQZ=UsQm*cwH>r2L42xjbt?Un5*)^&Kl@%u%_C2cXgA-XY$428VMiMF ziu~>bpKR2Qu5*qNQkV)6NrK75YC$i6M~$ zxb0G<{?!vzU(^d2iX9{oc~baUOcKZSP{&bEkyj#L8WTi=hBRbH+?UjMaVh8kt%q8s zj+l}y#)v&(Y9W!AQFPz<3R-WaTY)&@Imxa~aWvYh1rWpp;K6#mRweWV;T6`P(dE;{ zY}CN>w#PVUo1fTK+&3&QsUc70Niz| zH`{nq8aGOYsj%50a)QxMGtX}WeEKA4sq64>ZQq@0B>e@SXf&cuTRfHQ?eMW)^Wi4? zZH~KE+KIBm<;ItMRkNr8joa|aoy(;0;!gN1CeV$hSA>*QI9ngP!t2cv=s2|Xloe~EK0p?)!z0x3T?V1DtN|XVXAS4N zV=Nsy8ryx4jt0g;?yD8}zR?0Mt~hg}W*vl8*?r zwkzQdEyLP}1EgKg9-Qx2MQK)PrV)=!cd8tJ-j?h10TN)kv9D1c#HY9LO+$aDK6xgd zN27IQcU(=m7NLb93z8*zuD1+i%@Y0gZpjZvvDhc|TuT$TMUDX1jQ4wNDr)aU*@z$k zTl7rCa7HadY&36lgrmMQd?sd?7(Q8=sYS1`fw zVE4bHuhr#alV7Y2E^al^YnM23&}cV?m~f}3m!TCczSqm&D58^+yp^2RK~<#An(^TF z8y~hdq`zYqbZkf?)y2$#tt6l43QF;cBVMWGJw63X2pgKtduEB^vUv{l7KoS z;#nh5fWPG?QY*DTGsU?s)J&)N`s^)x&2mq&LdB5c#ii!?x4r1^M<+f{GupVf=?Y7t4XBJWm+}GSctcZ<_Ah(31{;R)101JS2+r&rH=*0D z;JToW2hY+ph3SaLO^l$AyH2eR;Qqo@Cl`%w>TI_OuZe0Cr+Rc#q%%i; z`VG_4JLMXr!tbNRgipWQgsaYOW7XpPO4(P5+4$)gu? zgXUm=7mtMHN}fiYRp|#4ryFw9*M(pY(EM{Imx(_|^ID($4$tJgL;H?_I?rz@PPz!Y zg|6wbFN)zEQ_RTqZTDwz0d93hFPNNBH1&2|{jr@*+X0(z+RN>{&6*N|6;3g>PH}^p zPLHS0fQ$P-iBH;U$0g%_DdkaDWj%OSf%6n0_bE35X{c+O2vt`1boj6S7hEqc^fJ2^ zc8JF|=;+|IfP9FWBirh~>1&Fu&9A0lNv2KztmymthfkJjmBTP&dOX{tbOXLq zr9W!8X|nVY#zu^#&@ps`;pvJ-Pg`n~tLYtw$-wv>ge+?hCvSat^yC9rTK*k5##3%w zC{0$_o^kx0*(WT#`V$+tk*)C7X$b0Bkvj4ReA4Pbx}o~}Iiuv@59i?wrwL6ckWt59 zP>>?nHuom|*=+Yjp>NGh%01yWS-OILU*EVwt8K1^txexH76X-UbsC=xx?q}rePpS; zR$$tktxKo?lTCI*l%L0YgZJ&{7bG=tDdgW0-{n_OVs+g5PL{l1=$=Qpd9D7uaU3z6V*aKHi29}gK+rezFbV8ah{j05B?(nwdv)8i#VE%7e&gw2;F;BwcMG*?l&&yk`t zt#{vdDlp3Oh7Y@Ot#REEzc*E7^LdkMSfrp90YofwWMHavRFn&+E$-TF@+QqsoUnBa z=lXO_cGQlWgT{fcUmO*+%b4osw;yVq8NJ;!-shWJChZ#hf39-oI8Dk--SoKv^4SEg2W zl60Sz#vaIJqz?RCNT<%WF-!M|7l=njbJGmGKEXod-J{F*2C}kz{RJ~u@nII!{I#uB zdel^1Lt0NV<|2ra#4oQUWlov;iWWi*C=t9$<|%@$8N@gSq{IL!#i{yxsxxGc{%dOo zn;XT)rG_4TJc%*j*uWHHWB7J6_hCppx2TRplT2Lq*^O>gpyxO?uV!^4np+a7G>mZ} z-cNS7V-suK)@Yd(dUWgVSeX@sBN%PoC|qeO&vnyA!4#6pLFSVBZ>&sM8{Y|l^KDa` zqbero$tjwIZ*e@md7F1v7(6_ZdDe!jSTNQsQ(K#1p(m|(Y9v4B9_aaQfZH?LNI-gt zHePCBy9%_FM|&FI!@;irbe(HX$|lq3z9Ey9BReXB{H4E{5VvjQU;JM$UU}4Xii>-! z&l$)=DX0MShWI1U*9->`pYGv{7E(lKnT^p~mN~D)|U?+NUK` z^J#Q5=G;Iw9Hy8VY|e!0vE3 zHS&N(G#cNAUQxz2e7jzNdD59k{fuhWKR#f9(#7KMS{JoTNo{LfL8e5HU#kMNuK=gp z_Sntv8IH@Gz~0tphF=<{Ke&cUhY7$&Vsgb6x8WcGTdmd|%v?fiP6h&e**jcL5jLpA>e*n-gI zi*#@OxaYubKCl*MZTGiD`kRS?2|{Q-MS7$=ZbSD`8^@K6ib}t_7StC=>XC>(bDSd~ zs36NG=lDL)`xwXjclkspT9$8kWYn&p&>^V9^n~a2tKsQT!BuC;%+mx84aneK;Jd`zFf5qbd--72Im3d9QP!M&SUB{ew5mqW@aI_~L^ zvtf3p3y5;sZ6DMWk{Sy5)#Jg#Vwb{(d5Jn74@5J2X_SC^{)H`3^3nCi2UGNg;&LUT`aTVh6q({rD^|~n;3xY zv9+Lkh48t_<(qSH=0_;-M0_r`as*z-^ad1hgd4=?{7C43642S~$akTV8HrJ0 z3v_ap%4gGp@sZn(=C@m`eIV7ywFlMN)voaDbP{(}UnYM~>H124v-^l2#19Sl;fd+d zovgdl*JOm#^N4a()Ajd#w<5d86^2g1QN#>EXN>BvQc9VW?j)`s(!+QbzEw@aLqR*Y za;BNr+}p1yiO+gZo;wmGlE||0iaG2j*O`bVj(Y*5;~b2GKwW7Kuvf|M0f_s{?;E0t zozFgdt)}~1`MY5PMbTh?>3i?ChzB;)A-(jsw}h0&1vU!IHB*k_WFvql1g)c8uc`J!=)9EYf50m z2-x( zQI7-P7oGU_;B!8CvY}DyBht8+&yTB$KV!Iv=6ueeY6Xg{Bhu=s`RTC61yp7tt~|53 zFq!n)FRSgWV!uwizOp-7y13YN$`P|Yl&GWtLs$kn9yp^==YsvLDX)1fi!SKcSts== zn)ADNFIP3|+@m2O=(^>fSO-NvPUKnt%!rwiv^%f%7fI~@hV|}D z?!*P&E;jt-C*o8pWliRxA3>ay?5=kU6PK7n;RNEPULUMvwZ{AHMT+J2-(=k;Q9;(~S<>)ODb>{*%xRPfRL+osLy^5WTZei*Qu!RMsEW zc^w#`d+7(>=^n!re{p;4Gw{9)$Q|`;wnMX8t9Ftpv`e48@>C`7IS+~h9n=+0a)g7xI0>6Ys={ZeG&Y`bQ1J37YHb)&MxJZ1 z6jHR*soT{9p=>?&zK|?2cHa|9?i+V5`2?u)p|JEs;SII8LXls-g5T9J1%+UP0%Ad> zfd9$uEO*6Lh(U2jg+y1DDQ7D$RxuTWW!pVx3wa zY6hk-qf45Z12*P|*w?F_05>P10#`k@WtSt-BoUcGuKl zxKF6l)O5a_TGy`Os)|6ps84^s+aSA|R*6a4vAczo}ZLrB^U?%$OgDgxJS z&ANFCm;+%rymunm6Zy&#UEA7$+lWo9cRz*r5+K$)Jz~W2J`{LgWxg`CmM&nmrk4Js z!0thluP~e%?oAzY%`eC@6aFAiOE|3)3Hh6XP2Ot1_s@q82Y@!xV()|XOA!XC%3l{I z70?hKNFaDdNhA^RQ5}sRvSjHUwt^y$C1Y{Ch!;lJQSAoU!F89@78D#bE|Qh?I4vw9 z4qAh+fw}UgRWa{Z1$HHam8?D3?5VLED3%=}L1JA75@3zt&NfOaLVobnAO4OfvcBryf9c=Ut8(+g=b2E&6ns6Gx7SZ%0V{&MO_0?!4yum zV+hPiLLvM&+1KFs@4m5lUb#ZU_`_b)znQF-UA<6KvLI5HpS}-ZI1ANC$%S>@3jxog zZ)Z+VSnmrFcfW33fEh7G&gX;t9r`Gj-=PdzOkCS~WR5bx7KyeB*)hrfWWR)5tBd0HVI6DgFFhFeFWpSHW2DuB3zlO6~H2(>;EB2~Us+D=|Y}KAt2bKA%4N+9) z+QC}r`%7V}&7!%kO5so&czOP*0|fS>wb|V&oBde{o5R^FaA?Z_9^2G*T&`f0u`Sgw z`W%aQIK>I&KS^EZd0W5X&QGPS%{j>L11Q$}5|ZSeSO4K^uu%f=XC6my(qEG4`<;AW zRrKtOM52wwCng-YzMjtvP;u(s+0@z^?4N#D7Qs`kfhkEiCSV3oeceWFbv3GYiT{FI zNDc-gfhEjmL8Re)8GB5(ipjyI z=y%hO{%EbeW;8?Z9*U@tb9!7gprz{+R6&P+B#J<_+kt4aYH!hzE~6??YF5fuETfOh ztAKM6FoKtG;Piy9gaPd`?diy2qkQ^kuWkDv2_XA1d0C22@@jcjnIOzITZaMiC=O^2 zXDZ3TIR6Dg<>Xsp0K25(P7bSb(rpWKx2(&3`h(m8(BcC3utJ>gAU5m(1MF7Jf$o znO|T7RZF%q(hTd?;B*PnZ0nA!1@u$b3t5~P*rAA9xH4B<0Aa?jr_}&mjboizob~!f%hbxpB1#LOMe|Wa025DJ}?H9?kMnOzgI?3 zoL3VUe%api>&voziC4*w?)}|`N+RWY-zO2u8tlj?M&}7-`ZUIyC$kP6wvRM z9APVj*?WL@W^?DW083)WcMZFI(><&jxg&6d#^^zE2G+#9xuP}3$+}~rV9W%ZJpl> z0FH(CS_G2ul20qCd|r?69j!hjd5`1r=?5_nrR#AHMvR96w;i#wTkm1eYj6dUP@G|Z zsUQKlI?&^^-vXUg*UM-K8X}f!SIguWk`~`L1bLhBFnCols zK{5WK!9o?F(^_5-j<{Bio@C9vS)-yysruv#JQ_Xj?|L~A5YL7O9IhQ9q za`~QUuOKEV`J*7xaf@baY^(FDG%?!mvL#vy67_gD_@nGk8g)(_`WHva{ywrFk23q1 zbfqy2^pZz~JTpdU3^6*MnePqCmhV%X(|CIal($`(>K_vdTGndCkr8yRdM$A{olA8V z&Lr(LStdx_RMI&?GOxq|zcb`vPp6C`eYZi zs6Jr$f>?R0-p>@popLxhw9?k$?5$A41Fs_5taAb8t%=Ry>NfC|=1<<{svfQZKCC&} zQN_z<-e4?;P5#`Pk^Hx3mAXL-V9SD*b>w=33;+JD=j9*|s2v`X#l~%AYUYSZ7i19q zolYD~1~qJN0eYICjQ2LYH4J~TTMax#K7dwMUIk<&&Up6%)baL|;VTN0ONd*ovB9&$6Mu1f1+RDJys@e+j7;~2hoC+B#3n`Dl%LOT^?e&NK;axt&)hu>~% z3a$pUE9Ya)v(eH?aqQ5R9)o7sPM8WHHaYzW-CxabcY0O?l*u)Qpt-6^ttCjH-G7~ph7o9sv(g8kf16E<60%gSZ}%Wwb*(iuPtp1Urxd^>@6<6 zy$Dt+H&_AxY)SU`2Zy`>)}Oh5NXMzuTzbCm`iOmkww%Y0b~Qyppw1FX+_fA2EByJUdkeAl9=mvJxRA2Q-mc<(#fY@$I~zKm)A`mu zh#XO%+;QrDhGdE@})}k= zA@3QphRfMe;@n9Q(V;;MIceY)Dv`JYv=6C&&`ngYOer`F&kHmv&D;I@oQMO3T)XmbYB(5c#R%vg+X8kRheys> z_>}J8Q%VnkQps1XcUN~)OFjc9-#D+6O9EmE${@tN%->D=h)S45bY zC1$L_k1!>_zSgm}5H}hH@Kw1 z5BSz#uebjH>J(UK@+H1Quy!Sri1-0{R3!9hgFjcfGxs*T0mWlu09zJxz8 z(r#nl{#PK1O&M#&N;#Djx%Kl(5r?0SPk)|Qu78G*h! zN6?Bl=tul9=>)p^1Y`*`L7gIASYO%1|N8FVO2Xs${-PWWhCrQXdNV2_a^mU{|L?0; z@+B%HQQOYGmJu!QCHW zZDlss-tNT#P23!3TIT{c{3VW^{W7O+`~|OFJjm6X=fXH=`sGOl?|n$@;zJ|F;dK#V z260uKf@*kJp|0jkLZb;msDx9?V?=&UdZk$I@F};6qfT^BNYkwC#~N%W@H7|0@cZlZ@w3LJn9-rtMtDBR-M6M7^agADM(7H4x}1M;c&v^OUM5M2Ty&9ci!Td@PZRTbqSX}0rr5CLi4U62WUpt^>>U%m_%=P{%xJ~e;#T<<#IyL#@=A)M*=5p z+q_BKgD%zFM?n%v^w_5*#*ZT4eK=94s-Y+#&kmD3kqm^o=BL84#_CQ5WC@joD>qLt zzjT5Z&WFF6N#@wG=>cw^dn?=5y^)=B4}vB@6~ub<7z~DKc2Kd8S;oUT$6tHr5A*fc z9_QNpybCqUE-Q~`r=*2RxO8#-3|r&2M+t{EX4b{>>J-nGwXfq_?)dL`(=8v0 z@*_fBlc(ZQAC@EahSe&uVtaN$qu)=@Whr8s7t?%ziHyBeBITBHLML++dN6Bz1G!zMwSQ_wK4TZ<_c%|L9(u6?bp*Mr zV*JrbENBF}VIuZti*?*mo<-e?{$LdB+%ypA#W=yIO2xTcD%6W-=iJ-aJ@*i|ZG4Db za}UJ6JmaU6k0)d|O zQWuA)qUfcYr}W?c#rLD6Er}K*(2YRPuiFUpF6O(>1bUo@G;a`<1~Q${u4&E8X71VY zF5b5Ld)dDJp2$RN&B;+TyaoMqFHr-$mBB7Hi0ULki5i$Wpwtua_FP+jJei+|^(6#r>340i*#)#LC&vObcR-z+hoBD3nnf*6lfkvoKZxRVDtI9AK z42B{2hiIdM7J5P|gh$36KJ~?q@s(qrVaHucQi=H|35X{}sb~^;(EX`;L$(e`qh+;TRBN{5^U(*J|%cOiK{@Wq~eiE zf=@k(rolOTVTKvbUOU2>>mTO}FMgc6w!Mctwmih1&F^Ewx^1|OJl8;M3)N_TinOfD zlcLaY%0XVizPyT@n81YBXx-1HPQ5*=4ln#2o~3kw7cYE)7X=j(i4~?Q+&Z_HU9)fH&aLlY*W8;~mu#-v!P)u=k}LIk zlG1dY6^Zch8t;8wReAmItDqHBmN+&4HD0@Xh*#$ia^mt+C<(|CP6YLWNIVWKq2Y=r z&F@e3sE@DIJG@KleXD3t z9?WD~bOyw;#%*NR=C`qX_HDd=%e&aPc6Y%w`_KnR>SNJm!KuMuFbosRs-MCGz^@+r z48Q)wPqKT@2Atf4_;693AgQ$GM@+({tGuwZ_`o#KqfZx*lvISoI}p#Nbvya7cYMkz zXeQYrAo~&7R}z6e>-bT|2^|v%i5$Vbj{%*tPjBY+m;|?%enmX4l=u#N zM!D9YQ^g~=yn}c!IEDNq2wUQ$pj9fc=&y6(%3;o4Kf>i3CpbC(Rn9IPLtQ02wUjE5 zAaXNLmVzeB3`h#<5Cn${RZR#aOW+fHJSwcdf7#J!$y?MOgj!pyE+Z~6qH%oI`(Ty? zBfzhWaz(AdzzFV-e*T@I!?9JVb;!|L5k`mvfo8R78{U+#KnOoK|{Qt!=_r3QwdMHLlwLD z8Me>e&U&|vjq7$Ww|)oWXGlEk*z%UpT{%vbmq6)XT=)hz(i?cGvbc1U3)c>#PB?Mt zt9a*8nZc>gkFbf>&$*C2ZLh}lmLP?uDfxcVq)?Hbl@+Do-h5L3o*nMH;*-`!60x24 z8cYRx{T^>Frqk|vowN#cS9d?FHWe+gp=kEwe)lLFqZ=S;2d7lyexu9n&*+OI5i86vdSZSqN zkZDoS1l5#VHtc2R+=JY??fvYY{ia%x2Pue2xEKtEVIq_~dC~?+sah%2FMaNx@ZzOE zW9v?#Dk;u~o?j}}))}5vpm*)FtOC8+=d_N$>T`9+l2&of;k=NFClR4aD8l@{`+t(X zw|u;!Jd&2xIJAgL?*p5a#|Jk4v14DUOTNpz+Kjj>{KV{ILl8flO1^p$X{v(x7DGLZ z!qeP{iPnPtu4gt@U)QZOx9oRXY@|euRE`~n3)dy}2GWP7OSmP@U3&?53FaFv;zXP4 zKYQ9P!LW*EmEn{v2S=u*^(|BR$B)E03ZNK zL_t(pfZB{m_fjswKB^6L`%L}OI{(`oOD#IhmN5g|GB~% zlos}Rg$#VPeb+LnD{;>*(_o+ClIB*nrRIyI7Pn=bu~_5nY4>9L{zTu zdtcX-blK|+27{rE{AhVUhMN8Omb*X9bB}+CR0O9WF2zg8%_n_a4C9_Zr6f^0Tw~&t zs^-$bM=n2&-jb2$AO~=|F9ri85bC8h5slof{@T@-sXg^7H`=qK#Rfxr`$RSBr`VP; z^u=)*2EVa&q!<)u&#$K*MFLU^$Ak0V7YOjC@!JXyAuZN3w6*?PkgCfFb{*eS6KY#x z;dvxFTm{vA znQBTo)#xP<=%ImVrqb|JmkuH>JS|sE5J`L7;31i6+PKePFf`wlwqGWK92gBEsa^xU zAMZOolRBPy=w(-%w_5j^4V%h+UpzrF1~zI{e0SNU2NZQU-oI# zouc!FqL)xu7uRUBhmXz*WDEvF4?IBv9^QDzdpNQ9B`Py1q7@|Zc<;?W8pay0Ts#zY zVLq3@2Ye75*V3qr5(sq7mBnAV{G|UCDyUWw=$SlUzS(Uv8^*{v4VfovXTR8C&|xnU zKb7{)2(liGN)suUK@tmtB+{pSjg&9PG~ne*($+k$_;WK6hBj@*N-J=j_q^J?4&1+W zc7Z_)z2YP%oT^$yMDV@}k|3cw5N8*@!TIZl`O3+E$NFS5w{3ejcW!(;w{3kNYu(0B zP3~%QF8I9XS6$`01Zf%zp9}^=&v2eM-|=3KedBS~Ziw=xUZR~bBV!m}q~dt#(jouL zTRxD4a*6|PKl_1l0*jI)T*Ms^^E-7u`=cp8#G%{AGw zAM{v-p8f5R&h!tm8r}9tZJ@P@Um5iW4Bj_yu3T(LWH5BUK{B}u9sB*An{?X}L-A?d zz>$JL)hi;E*xt?z zvJSBS9`Pw&6jkBu_2)Tv<1kO2`ZeZewsG60Z|3z| zKEQ1o-@;5~BdRGvk~cUP!N_xi!B9%(zjbyOn`U-^oI!mBCqCF4YvOJgV~7`Cx%@Qm z)bABUy(kjZ)~X}YMD&2be)$}@Q&*3HQ=EFdCUNWa9thb{Zi5jbE5SvbM0-$W=*Rk6 zzTWIyQ5TN9pb_1V%KMCbnN%-nF~I1q>u%<9r3WL$@2xuB(9T*~ZM~Sy81Fe9`D=9v z>SIGtl9-Zva1v5;aR?P)5wwCsP^sd*;+#X&;U()Crii9oT|CG0=O5#_bB`ekcW!?d zyJp|cUEAKzY~>c5Bt@MUgTY|v3Z78tDgt|Mc?YlXSj_SgypiXIu}@Gur>-600I*L* z6!oB`kmmr>bFdPnk}fWs^8fZLf19dvR8Ue4I&I|7-n&o0R70@QGa2u7gl2HEY4NSz*Zu>*a-H(1#OLMP)`9>Ljr!;j%r6qiGe1<5wwnBfd%9rb!Y znOzQcuY_?aIqWKHz4ZRz$Ms}?TEBab^C;1OQQu@y%378xb(M>5fNpKjGA>4jE#J?> z9+OT;%avly;_Dm#)+(S3tFAvUZQF=ZB-ra70GN4O)lT#ay;YzW9t5f&8mi4jB*?Yk zAg1k^@fI9DyehZl;%do&_tntck19|>J-9?sUt#;~UUtoW6ZhWoSJ}RPuPqjX!O%nI zT|3D7)uW%`v#)%NbsGeiDn60cwz%KvqCy%n#shtD6h7P`r~Gzs9PwCU`--)Xm4qV8`R z0`ZRH!4~mDGcEQy8*2o9ZwYjV1q#d7h`Lp)%*$SXL*2{MzLbWL>-iqXU$O`&Z%ZP% zNfzST)%ponG>-`~gPp~{LGL`t({m9)lJI+Rp7RUObMD%6eErmKFf+53J)1tj?OWc* zo!j0S@(-=l`GoN9)mQ~zj2mNtTkI34nifbAlZG)ErU8PFr)1!DTlV0OICOm#?`GnI zyb*Iy4e@|s%GYm0{N7kj=cu9zr>-4=^*a$&)kh`aVYCb)y#xst^&sxVm0!j=hi2EL zV#$ZWU>HtPFOwlNvQn@x+#teaTnse=!WxQ>kiC%cYGBFf>en zp9fCQzl7?0Mc5AyqloGO6}NBL8t4ZQhbV7&=Eq1iIA(`OMQryO3o5(H7ygu&FFnrZU;Y@|*WJtB zTmKvG+WdYttlf!7fJLjCmgyc&v!1op~PEe3ZSMw8sZXUHV`3HR4M(MX4Yz#d~lB85R`}T4DQ! zJGgE3TiAQ+cW}$>UhuJRO4MV|QsazdHy8|4Vt=Cv3NoLb-+c1_;>E?^4OQvPv_9cC z=={1t!o@Vur&|MkrTffzXLYDkNFD$5+x{rYy=Uy*sXda)y-T==>goBT{ui7gg@JYI z(8SwGe}lm=*q}fNM^}yf7`sHen}EV3L=1-Mg!il4c$c&wxV#yUp2C0We0)270BjG* z42I?f6iOA53POr{C5h#ADk-WCVUcqSFL3_)i#&7gKQUL?#ogOJ$evq1$o38QkX56* ztO7ljo%^WMZadFlm|&Zd+ZF)FwhcQuc0*7E#3Yyv;zyXzO840(q_)n~F_?UL#O^DPoMq-8`kaQbsHbzjxF!u4O`!rHxvYq(|CSdObgv$ zm=2qqvSagIaM6>vq-Ak}!7vU!(}|F#o|6kl*fAFmU8t~vw;M{_Oo z!~S);F1r0Zi%ZHK-)67vRzrPM=SIy0mA)s-@<&5i3nkxbkZvjDPF_fygrIJWgxCF&>?dB;C-}o%gpZ^SNl8xN4<=eP>%lml4)_2z$j@oiH7^cD| zizKXNGpIB2+%OSvQo(DLM1&Jpzk#Z%#Gq442t;}&&;`|By!bzXhRS$}N5Djavo;tE zhQXi@8OuloIKxb5j6@&XwKF7b(?M6G5BlC<{O)huNc3(<*Rt%Yh*$w}N%PlEU!HdJ z86(dPo!aSfT?@?%j2uL5ViixSE>?VI2eW3|v+hgb?Z{$(tICeuav}*X<49gS~c-# zSXbSWKg}A6w)0+f6_rh1-g@1+HujG%ys|f0U0bu!<16?%zAIC~q- zsKAEzl~xiAW5l+0qOII3#)UX<+Pr9$^*L(O-S2s9eE;TT)oO1r<}q%cn}BsPf9<|@ zlunw|H3_w+EuF{uWW5O47+ouk4I{a05uH_12wMPyn|X6 zYlVBg;ji%gxli-_xlgku*~p#S-@{#-KENBcerrQ~m$i<|eipu0&3eeyeqOVsR)}FR zj8>3U^JKGY<_bSfR%{|#<4vZ>e~sV+@?L#vvj?=zdTq3hlSdPYDybx>R5`J56bRDqW-{?qEO%8%kC%C$t%c56RkFbs!qu%qYGG0&q3 zOOA%SFm$9My-BV@pVTBlY`!`eFtc|C!zg0pG|#&*tiz7wm#SAea_0AW{>&e-E?LK2 zTR*^^Ti?l@TfQymt!L!8hS*OeBjN>cSx?e}opdNBw`MSmTHcoL$aDshrC@t(n@(p~ zyNcf#CyptNe#VPO)bYy2r_$GNd%($loT~O*{nC;^m%=k`1x3RDa{yroc_%dah(;x#>t<3N2TGOW4im;}mNA1oWX zOEks-Hora2uMOYG?GZm|bJD{D@y76>)3?&7g`QAl$zSCg=ReK!XCGr-Wi5AXdp~d7 z{vmeFJw(lnL}?gRkOB)(Y-XKRysNYQV=#=Et-f{Z?VP>xk|{_`Av!q)I2N(5OTeeW zFKLyN*N^b}M;>%MtUf5I&|e7`%};;F^W}=}M;!YAQ91SeOQb3!;t@&k2$063A4cjK z43-oRD;k)*>cDX#Y(PlI8=aF+5F@{UQ7A{c!# zx%Qycmww-qz2;hJ%k_N!txC`)oFN^gE_yH5HV)%Awn6l?ClQ9W*SAh?A9Ay66E>!U zhzl#JqN-tE$P%K8P$Bh8kR*s-;_A)QJagugJbmuh**LR^Q?IczkG@?UXa~F9GoI!&=JIq1FU?@O}jH zT%3eTbQIOksU+vV@WIePR2>zG4NJV$of{0J=q0VFcm;Rb|E{q~0TTTy*Mv`1t2cLLx#6 zNa{Fy?Rh?cRQ@ECruU521iBLYc8h!TQG|zs9~I!GOHVC5&Obn;5|8VrbsMq-jdp{< z&=E{FY0{mWksjSA*kc(J_K`#%20uRxO>~2ypNXfzVE%4+_c?LV^oTsoQJ;cT(72G) zgHwk{g0hs4kcg1V5>g4(<*Bc5cHt=JUivujv)s1j9o)11uW0Yn zyun}?L_kwelCaZ6(;UNQb3e|UgU8`C#c6_fDIVpQFFmy+TXCwrpEc~rd&RQ%_^1kZ z>iA*QR}c}rOF;!uP#h}pqOB2u!C)}-p3IE2LmTY}H(8QBdZa>|<2GR=dRcQ}50nfB zgQ2;+)Iy7<;6O(%9?*5BuFuOo&&J-=TTShWgp z8-Je|I97>UAOy5n%tA`+8&J{1+ap@Tg5K-VkkuG z67>B2b)}sst>15$!G4DZVpRO?q`)^>IK?Gl<7^m}xV9-TIibsQ5dN-CH+Nhs6Mf9;Vof56c*f6UCx z4EJsOF!$~FYiwJ0H$pX3yVu2zvC*i85Je<5AcaDc(#BZRhcT4IL}CnvR{J~Vs2ZF4 z3JI!;Dv2Jo=cdsCV;z+zlopSu@i`m`xOi;V_ z201hk6+!kX{?SJfM7gr~n%N(NVRX_a8-fYLYEEpWq;+(XSzC{W7V(W`xCVp4Fp^j; zmKb(|Jt@9JRSOTYC*L88gyOoTr6ry|^BX+!weRPVXZ{w4&i)rJ-#mjTi2Cqvc0x}A zc}^tLMFDlF)Jj0|@gzvhSpOq~p^Y3(f*f?%Yt=xci&*|<7<*Ktg5nWLaM9OZS$vHH zQqJOT{U&n2)f_zv;vRk&J)puXXTLVU0M0V)ae@&eqnGd)#y8>Uv>IvH>tLFNyHxs< z2RA0!T@x6hBh{GH%i#S*Mq1R77lu;TzsmA)wsAM~b007JlK$Caizyk(J|2d@7=}MB zYg~uF{+maGN($mha9p}^oG-odZ}{ZX-^+hJ{P%hO+#j%%UL~t654D0mbYqi92MCC| ztRO!Bto&AO&l?PFphDK^5Uo%p!E1H2vN&~vJvnZenAk2DS&t%cNqgshR?cFcRN?KQv>M0}f zA0;k*n52l=%>M0x)xHb{TVEp_DwQlFGR{6?B_CRLz%YE-mi|S7!OsRc7ZF9g0uB)& z(HR_Y>dI4m?$}TBYfpcK&mH|3$L9}%^Qa~uMYTB}V)#^aHK1dPF8XlWy;#uFal z`|}IWBP5_B(BM~mUA{h!ak>?Y3~~9m;BPbq=N#TwsU|6>uDwt#@M98E_USA zpoWf~S$F}T3W=SQGSs2g6tA0Lx2FGRBiU=>V`yAlZlcKZ(QwLcRU1(Dz1q6Yv}cX< z?a#Q{`Z5^C#>d%qow9giQU`ZB7ILG14K;ZoE?1WDI1~j}Wih?ZvllIdk3@MJV#_Hb)4y#@&46f^mT@s3$M zFoPs1>ILU2c&eP5f03QDdwPEVHcE!Y?JsU4MUeyU8fvLe@SlRwW2_#ZP1TP6b1#w{? zjzcayj`_P`P!N!%d@YCOxQ^2cM*-=XplIU_g8G;$xs(gKpOe=PBbp9u5r&0m5wpQy z2HWOzMR(DKUbc>d4%+zUU$FMs93eDUR<Dy zr*R4K1cyC9X+$^bDO?JXa^vOe`9{MCtu@`XKpM7I67!0Fq`^G)_FT?f@dmOU- z)|5)i&JVNy@_2Qj5I?S&L?o)DyPSZ)DG60l0s>BCZxbPq^aC;s*MQj*rM0qeTAihfC9QcH@^GC^w z!^`nxD<|xcwnsqy0mPkOIR50#^vb|ap5>P?#6(ymXhI=A3e%5PfIC_m!M>b@KY$7X zhYvxVlw0Z2?#awz6TdP}bCx;(ek9g*?@w>*j(iWRn|JoP@k_n5bwqBZit^Pe&AX{s zU%vLrkzfhCtGJeYkLw}oTjtl->TB4RuqH>5*=g1GB`S*Z+-wb~XXWN&ofx3vR}>r{mY`nn!E@w2sLfAgYLJ{@Z4Mr5fmPr!M4e>cY>Z!s&&heD1}M@gEO<4}Ws( zW1P8uB$DSL-qgnU(?{#0#h5fks*3p*!?bvsT-ZT@bJva{qNGwmm|5wN*xVZo?S*NjCi&yE##Ja?_NF8Rvc2t4xr?=YCm5P>pgp zQbn}Fjr1zdUHDy|zxWw$S-+RJ?EDCKZF?_kl8t~1h2}zDah|=GSF1laOc4amU3(=y zP%;ADFj)u%-7c7-XSl^7szGtJwPR{Ypl2++)RdC^<;DNOj-MF}BM*nw zfLRhe8tX6wWEPh7fBs9WL?6{4NJnRn8JRxj`=u4ZZuZ0m$mZDqy_4r^vMpt=JEsKN zLlkx3J&1FmxLkvJy0V0mHQ=k9yMB~Ee(_(h_T^dLu>Ji!u=68q+i*`bW3dul((s*B zY=R6^!V|!i8)vwYUd>INJsdZTzkf*!jihTu-y*_=8?R*lEs-q``f9NPEjuv2+WWVq zfet|%1{D$1{p6JwafvIViK(H_!HFgKhOy3BOe?bOeLju*eH^7+`n^v|uS6d)jGC;A zt-r}dt7dY=c^{W*_F`ge1o`yF45JiZjePaF9aYKasSKZ6)|yLQ6(@pL6GVlIcv3GU z2#BZZ7IA(ps#Vk_;Z&|Fi+Y3S&ioF~pZ$Gq+w>Ohzva8Q_m&Su3MJAaYWB}CJvejy zIZ{bu(?zR7H;g}A7F#q(r+M|9yLyUmMrKrXDRSICvM6nn+ijjuQokP&P=8|ng(``s zYI|#B0%0Q<4daMTauQqT{Ygkf>%Ua$zb5_u$9@myaa(!-03ZNKL_t(V0%$FvR}i5mnqOaj~g#5a47qs#h~_dHc!3ISDE)N>QFC;QxH$8 z3T}z2+@y-&dX!KJou?<>qO#m4@l?1^NHL zD_6c6PIO3O17nytBr#V`?c&BU%hdX zRKdB5>4O;z27{pubmuLGmEezSVqZ+q`#E;w_JGA;7)U}Yc5Md3IPF*47vl?2EgsNE zBtg_6>TpQjxIIx%;zJy&0ysf60US;p;*}eEiN84h%Y5R{NBR9D{|6`L58415rUu6^ zJsb9nDpq}Nm=F(-hyo7p(`9VLj!5;7um39G z5naTS;B2D}H-K2>=y)f`%8Reny6JEAmn*jdba;Fh#94kVGJr<4@%>jkme^%C>imdo zi#?d%>xdRG`nvfn*RX}eMcNogDHy}vs&}$vNz`>cx22APm%4S5FgoIwH!YnS_B`Hc!6IRZ+ALwN%H&e_PoH*XDSHdo6FZ4oVuNC=ubhJTKgAp0l5c^!N}$ znpd`GKW-%x(=rBWInI`Vl!LQ|g0h zm5%pky?^S$Cw1VLiwD7rbK#J-_G5l~Ub~9l%0zd*)i)UzSK3+(98TBZFQ>o>sb<x>D zRa6ocO@Hm+K}0KfSH*cuBh0X>@#|)Nn0C9N+l0t4UT(KsvS7SN!=qi3=(b;K0kbm` zVK5k`BpzBRR-SnBydNl0>%6}B!!YVGWHVe|Jk6iJ@Y8&Wb=<$}zva!l{sx<8Zl_rL zqZ0m8Gz~;Nm3aO?Hgu2IsOs_HkQ=EE`MeG5OVUls; z!gEoxNUQt7?NgDgXq+~*(A(G@;SB^$0WL0`#!&@Lg6^2X(63-w&3GI7nSD<7`Wj{O z)c56nWgtn_SgiriischsKcvhA^9`%+J$u+^Fc^mE=gkiF7c2Tul{VXuwdZO_yamWm z{bE1Qk%qQ%ODwX$!Bd~)*Pr@+e(&gyb9~_t8e>iYR8x?o7K4h9WV$b`mD>Bd!YMw3 zVQBl2B>;|I{5;N8!g|$8D3TS+qYRUc3yZICK!T@M_LHffM(10G2717LL`31r_0u`= zq~4yN8Vu7Guo^}wgY(_XzYH1uNqojT==1&3S!j}N_iPtNGy}}^>n%Jn7z`8b{$tOJ zvG8pZSbaXR&Aa=Ca^tgpi!h2y<2A89a%&VVXMFFk=L#d$5PS0m63lZ`9a&$3?;5clY#h<2D1x7;BD z!Xx7Lq5kEI&-j!S%_`5$2^g9#q23Lz5x?cmPgAW{8(+hi+J{ND8$PORmN_Pt*bfKU zdNhA#Fc=1DYeq@7Wu(dTGEY=RA8WS0E4pFy@=%|k;=mu*PpPQ+&dnNB8R9g`h@@A$6!%%G>oLM-=jipQZ!&t$C6YFni=*IPp zSFY_+UcPv!itOj%kE)MC>4Epw)|CnEd6m+i- z$7ZmCYD$8Vpg0m#z$e0u>NURn>M!w$XCCGcUi=uR7mnuFV#8w?%iOtrW1nzw@ikyyCdTDO^6nNb4RqU{M?~b};wivI zXP6yZG8ha7gJE(5fk}`9F%o?==D;e_4F*GBZ2R)et?)HkHgK{gU~($Y`xWnui+2IMl_iqgG&M z7&59zSf^42JP86xijrD2BwP%JF-WY&y%NUIeX=qwb8qW>Wk~FdQokb6`GUyw^(xk0Ob~YYHgh1bmv$uc$|LhD3twUP=W~$I1DpI6i-fO*6ZA z+v|Ra`)~a#ID$@ku8Xd2@&hEpNaN)TPq46b6?F;X9g;e#m77%j3~KzIVd9hHCC!oW$i{fj~>I{h&Xz!s%kn1g@&AiTOakGSyqK=X6L;3c<-C8sdnB!d!Oa& zwXCDFj%M%bx?JatK%EMOh3od@O@-Jn|n>V`;_2C+q`PY*Cd)fU^1|9RL zN>B1FeuW$)eDyzX4s+=cNow=zGF|jIH}T(m?wyjx*k^w66rVf#FZiXee3&mE|7C9Y zg}5GFfuIa`Q#&Epc=s#)YcRChKpK}*6Dy+d)P>K)(-qaQCfy9e z6{hN|$c%tooIet}QA;dbm(&c@n7}F`36-F*%^p+kE*M?``UVQ z79|_ngNsc;z0@@KHPGpomrnEAnaAFLSpvKFbx}o^YbroV80~$_hrY` z+OH08>qXxzT0MRBs82~qNP<2R)ga?CPQj;f$OgkiB$1N>8Q_?W?zp*f663ME?-AHb zer3nl^+VTwmi?8K)Q+;Qy{%b;!C*F_9|2cqW4NLZj3>AZ1M=C+;zBnT`&85+B2=U7 z%XNR1ubulnKKbO|<`0kkH0Q1!3#7RQ@-l=@kx&N|7ry{W3a=gAF))nKdJKyaTsTSL zSpoq;c=Gt8R3$}ZEh&n!6xOT@8tCIuVQw(=#yxlkKl~4mEbR*oL{-Dl=xVJ}ydCCZ zRi}W?r&sV&MOE-rNN~6@d;&VnBzhJM+Hvu*jOBX_>?UjB_jUc6_S-}`$nxZ7hU2dW z(xj!y2lTwZWc2;ZD3j^tpA81XsM&#LvRGr$WV;K0)O76o?2N~~G26JuT&2z>;go(N z4t#>(5vrsD&z<`{9{KtY@?W3*F;2{Xy)gGaFrQKlLX}GRJB`AXB2kpd@)Scmb}wr0 zD>!CQRc@s7eB0T$JN}`+H zHW;QX6dBPX7eDQxgi2py>G5~LMw-{f+2?CVFFd2?ugu)>`-96P+)wN~5chB*Ud+%E^Ush&SW>#fY-GeAh zbynuf_wI7;Ip6*6cOa4#A5)Hr-2n{B!{R=1#Z-?{aHad_E;`3Tv|gPghk}XTYmQG| z|93N%MwuQQmSe9vrXrXTM0YmHvM!T9hoeP{c39{2fdzq_INp0+t!mJ zoytJ;pV9ibJvV47FSOECeEFTvf+az&c}|XTPUMRhlN`EJ>(Qchn*m6$)LH;uAg@X8 zfO8r^VRzt_=>FF2yb?7lAjGgSfr}O`TC@Y3tPhfRmVe5}i^mgR@kBlUksKd;ctkzM z=sWA5c$zF)w8LXjR&m@7tYZPsqwR-{J~r%(MLOf%VPi}p1=MLvt5FCUz-C}!5G79U zTnvN_tfvUCrg!lB*M1J4`N~ziw)i^*4>;(ft2)0Cr6!~G^?%X@1HO3Y*Rd{ZC=@FE zri>6&8Xz=aKp>dde$fuKi6?_EZQTK`LW!JbRd-mU?+Va=MIsQOa`(Y&AX0a%!&ixB zjz@?N3@8#U+L(gSXweTuJHRn^35eYffHs2Y(V|6*7Hv0c$2ixI&JB*bH8Wa_Gkdjx zC;`-PW5y~-GB7pVxW$&!J7)!yk>mirD?GZ|x{cqv{&V=%mwyz0@%AraOV=DNl423j zXoKhbqOKS%AZTan_>0^BFN^@f4mc;)T=Seb1=K=SVJOb!Xoo%db$Ir@^=|;AzJMr7 zZ1g@KA7sJ%zSro)4=sNGs*r-E0in!Etg9Kgkf>Cq^zMU@>PK9i>b~FYKa*G3gEjJse%IS|}wyAS6_hub9&&C<2wzE^tHBi`i?Z9iz6hU8#0Ynco+ z4!W1-i3wRpme5RW%;3l2j-NBk6OyhzA4Qv~stfwZmQYIVIFljApu1lg;0m&{wPnx4 zmmYQeEZY*!p&dG$3x%wKW?0*ZhmMX1vTjUQ+|%( zGsE5cpzf@kS9ktCwtf}LPs|LL-)Vl&P8N!H=dbc6AI_iV?r%U6SLgT34p)ruenY@- zG(Lao&%Q?=C?VHD@5e$Ph=Kn9%)Pj3)d1S?yPpgwAr|S47VRKe45Fe%i#9}n6m4Sr zDfU&gXmOrKi)&xZT@HtlG8$w+VzFV@@u#;wjbDBFZ{g2x{h!#9H4pEwZyr~d&W+ka zxRr{_= zpI0)=TX#X^3Jiz{3<#_YVmU&M95o7~#iSrw91DAG<6C9ohEm@#BIrrQ*fo1=RJyc# zG4`nVIY@Czv}n=dygUj%ZOjkwIRIj`4{uDN31q#RBBqDz<0e)o&OuZ@}(ccmu`Iu zsjT_&EFhF(eg&X_Di`eZ0HQ}b^!dI4@cQxq0zv)0ALHhOFQb73Bo-MdSevnVq8(`? zGR{p?hLzR=RLLOUI`6-Q-gkBSI8+uN-10g(j)AVKunH*C5oqAJO*^auj~2&uv_lRO zL=gS(_H8aq7k$P-1JOHwqD70gck?ZR=La*8iN125#$arMYI~?-3qw;dX#tF58l;Y4 zzNO3f)3^UGeEQ`d!I$3oB(`KDXP^V@hBUeQaRhNdJm7TGqaE&;9YEV|;CHV5oIfd| zATj_XaMPnjJJLqv&i34X@FoDw0AlE10#^fgP~uHpb`^R<0Yp*|HK2O5CtwV+q8&^- zJb_c(2$-zgbMzSNc*kl476+SO5kxN~wFgb&AX>C&F&2rwtbYK5!3&s0+wVN(@Wbr} z1ciknfvCE|fk>d5K}kTG0AxVQXlGma;@h9bXI}hI@ugdzavJIIN-YjChiZ^>Nuz|z zkywiapL^q1chd7}9|*}y6QVvs6815J*;Zz8DPTKX!)0;FjJX6K&F_pmF} z>*s*_$>=YmMT_%klKtDBe*Gb3p7)ANLfkS@_qwB;@n~?>Efi1#QUb%k3A6<8oRP5r zYQaQMq-dx`YFF{+w?2u_y!26g`Igg2ciO({)zAfmM&v>q>ipgni+yeB3wZV2-vkRg z271C!3os{Ojkrv-qds2jW*OKsGOTho}`*iB+RkcJQ)&4;M zZSVYvuP?u7KmFDJ8YJo&bpj7XsB zrTIf6`2NuZedBBWaDTL;&*bxSRQ=T1?|Zy!uO!>Hz_9)zw3YL#RsXOXKQUUrv5gNI zW8itv*-hm(AGH7K=S)#(DvXD&-?{ql>bNz&qBu}(z26*k{&%)l@6C~-eQc>6P0x8+=xtWV;f_5K4NxX*G~;W4rkGIU+6;cwb3w+GSq$6e zy<%aJ(dO$t+jGc#g8&E-lm*9V7T^8Y-^csregy2aU^>#Qa&b|olTICY!6^(!RG{wApuR8Tn` z-Lwz#gef}CXwjk_goHR+v^Z`Cd+xetCkBY!W;xiEo&rwX7VY1^L= z(mzW3Bu*TLqjtH)Z<#yY9N`25{x4`nhngwC#1ef1(|9zg|Z!iR;4z+lY_5#XgPg zcIb^21Pd06O`GELZ~Y>E<>im!8+U&TfedL0PzW@Dmp+c85^bvjZspNJWH9f$OAPS{ z6I@vCVw2C`{Aaki@?vbD!{m1({5tyi!wRvK6H2$#>!ADWv#)#g&M_BF*V5`pM0EGT zt>Vi>!O9@zJ!fJ@+vNbESXDk+41y1CfV6kxVKT==0IUK4R=hgCIZ^$tgBWP-G}sD2 zQuqmgx5u9yaIM9^A?g?rfsO0|>wLW_Gm%R`b|(l@2CwRV&+jE?GqckK4WK;4eXj@3 zdmY19)#91J4(wNgMFBcnU#?5Ysf6}|PAf2K9}Qs_GdU&?iGoC7K>nmmp+&p>T&m-UxF69(7e|Pe?eW|rDuGRC17YXgz|2ls5`7rD8bj{ZTUc0HI( z|9a5;K|vH~0BD0@u-sa}AH4NX@Tr&o7GAsiZ(x928peeM7Mk~kREY;ZG9vf_rqL#3 zZVJ?c=U>0~JNVMuzn*V|M2ogp>)b)a!I%@5E3o43>g&J`B7>3#EI#zY3uLZdb-wps z|K%;0gpfj&U`pqH1hG8iK^rXb6hX8CV3485L-YaB_9Ivv*VwE2NX9`1$-*h!egH!! z@aX?i`kVBWaDft38RS{sDgekyM5&V7mzQqfJTKZt=w>-FAP(sU5$6T46q#p0srw88 z>>0&{6iG?yN&%ZofN{|n{$5lq2uc2tyk?97)+(|rgUa?vV!GLzFT9 zScJ&<{^@4_6EPh7J-ik+O_29vr?g6E3{&@e6@Zt@^@@9BxNfojecW-KaBRPKn@5W_ ziFT;K@*!F0wP5ESX8MPVHMn#6)Isux=vT)%0NJ;*f|wxcbkr4s_4YgX{cHa#Uc2}E z_~0Xd2ale3-Y>x>S9GC3#-#Fps;@=c?>+{UT&}XX@diHk=Kl^u7C?f7xb+*!eFrnw zJh1VSWRB1zisjZqIS(N1GZ>S)H(tF84S@P;`qPz!M@4;N8@*?=y?3f{+)Z9Ue5gV6 z&XYE@znbV{$x-8+>hd*5rA_K3HS&>B@eiF^-l_^E2cC;6C`hO%puzwtMAIBJCM{`- zlg;N3cd&%113$@GPehKj4Th>gYb!;e*=-;eux$_AYXRz}X$ot_&s73Z;u+R}ZPz_p zkR~x0egmab8l?;@H$cPEASo+7YXE{v8y?(GL(t}cv3uU~28k~mU+Ix%N1HS&*J36l z4S?C?dnbSc-;=}v`$c;Qm-&R70_5p*_cp^8>lhMS?2cb>e920+P(4W2wGAu#>S=?Ox zGH$HCgvU-kgYSCe@8Qvt&qH9r?BbuYq)Q&sP)UC~+Wz$~ilVZsTles5U;7x^*#^`w zP$4%&ix#arl4lTGh`5|=rL}~g1@XtBQ05(Sr_*?2%C zTC`{-NMREELELEBc_MJ|5~lGp!$$?83++K6o)PtbD70AskOKJPvJ%=OB#s1toE~{T zRzv{~#W&$4N|Fj_PGkTjWEmq%1qKWeAZ>fSYBf+Lq^X5imm&}~kflJD*nmRjQn&)38n(H%XOZ+;|2y^Y8$0o4Jb-7cg1ZjDwIlJEgo0UL2Lk5^cCXJf4KVl1 zmByswHH(5d?lw5mHuCYqZ1Ms@Md9ko7Kvwqi|FqA`?wjB#8sr+2ZEvE4a1#5Ahx6eh7c%@t?r)#+(P- z>-tElZ1iaR<3j;}1Z}&8fA!6u!G>K0h(Q8J6oHM66fIhx{LH|>s>RaQO?(2XA6FtG z^ktw^_3!Qww$F?4B~bEPfA|CKpmhldSQ*MrJCk<+V$^YVc2m5EdFQOz5Lo8HgzU1y ztg6>8)q;sSrfRt33Iy`|-{E%+7wk&cqCRM_-x&ZpaHe#kmJ?WSh|?V-MelogZaC zw;##uAikn=J#hann7id-lHD~m5+_CCWjfkh?o~sCv@Nii(=@f2BFhwFz215`5%V%1 z81hxOCtwzU=C z6E;K!@$vQFTi*^zgJF!*={H8vXc_&i0yHPe??r)s2QrU8xYkzw z`yiSgT(pmCf|Pvn001BWNklE4B+D$yGY-dXMUOK%C;jj?k5qbVGN6+dc zSV$8v6i@?b=H=BAkl*4VG7tfn4Ad&f5dQMc@8FGlU&Ql|d=&4y@Ha4PP9vP&bH&^( zKqgLzeYAG^0KfL?$FQ*RCK3Rm1Y!X+gL3q7htBsRZeESnmwWk0IH^&qz?}#GB*Tf% z4%EzvugOLDM4?EhKKr_6S0UmOolv#QLc!LZ?C!;`8g!W)1heT(G{vHF%zU6Asn=LTUN6jS7#{~wRKQtx>(mj`U%hDMQXQTi*hWSk4HwFQlwjcMo57`g>^FH5^ z-~KL7uyj6uufE^?m?-?;aAY#>as9%#?4~#R?^tt5Cp!GQkjjwSPZY8pHXjG>TRs7--V*(0q;^@ zf$?Dd4wlz%LpAf)3*V{v&^63Cd#}8FlS)ZTzv^og45m=THVCS7E*r!|P%kSZMI~5- z!l4RsY^)Al_yCea$#3u!e#%W*cWNTbGKh7oLk`dFrGr#TbCccYW@cS+HfA81$^dPk zF_pm>1CU~7HjH0Ck2FE&+00@+CMeo|lPIZrEE1@glHAZ>z)sgG53l!MEB#Z7zlwOL z!yl1!tWR9Jw(YY{a1ReWOe{lVnL(3glfX0&W5V`ppMAd9AVxYayt5n^O-doPQL)Z@ z<;lZ>$I>UJ8$|{YHh*ac1F$Z}Eyr4wTwb{nckeddF8t@1^1Be5Nmx^StRB zYX|EiShsGrWDS3M^Ox|_+n>dUF8?IHG#adS=z&j@#w_y+S0H3lD!UMb1nEV#G9LuS=feLj+2|s$IiFAPzLF?LM7o0YHkM zrVOZn)ZV-K>(P%zn|O-_Zlc9;vpd^v6QLI%bC_ieDnWn;L|t-#LTlR>7t~6i*}aTY z-KJyZs9Nc$WNrB20BWaQNgj08Tw)~zdszzD43JEbwG$9$*j#to(_#&>tjP5ocd{K&Tr-+s{qbJO-{&@a%j|RpvA6_F2+Z(+cmfH} zLIQ~T`zt_y@|)hq5F`^En>mMbXC4M&22g@QiqpsEaPs&?uo|eiK0AGU9w(2_IRFru z(?iD;PR(AxiR0(}hNM8@bRx;K+^eI2OFV81LZdYP3t+Z*Un$@PPZ(XqCo=xFFP;CO z+tlcvfp;Wf+hQ6PCRuAG5DZd1l}ve)xZJa7>D(`)vF{AV5L zCdyzF!2h7ru8!QG0!0I)Zc}G-BLiUk7!Mlz4L{y}dvSs!G2mt!P+)r2jk!kCf{lV1 zXikZjBQn1IGpH0yeP{wAL3w|!9u!ASQPl8X$pQFsu~hBOq#zVFpezhZvQAx_yWv{e z^Hf7;;xfp(+<&d%L%EjMX;@E+OiO+rF@!o~b~H7uV=k~=L~PKKHGJ;c|BNqR|0I6k z>Hh(b9DfE%83+lKeF1qTKYYS=wA)#Zr#m762hb=0Xs#W~ zr?84zAwBQ3LP}R~%^jE9R)mnYTu|KJaNu}*!+oaRDi@P*Y+%(4L0g(!1u0NqgXYKC z$?HvL?oNq)F-8YYG8tG=0EuHfsAu%%g+$dec)2Ru652u?_->dyNGTwHTzF>sG|rvA z1mi*X`BUG9spc#o1fmAcpM4a^rcZdl-r(Y?%b1=zhE6Tkg5_$u+D%J|NtyNDjTSpV znaSkL<<4}bBym;zLDG@Ii z%9qbSQ@T#@Lv_h}U|hL>udXj*dF@WVxwW1y(m=&}y$?@%}X^wV^VF2kY-( z<-r0dkzCO*6kb(Tke`Dpj*adlgEAP?fCzy}JzGz`P@@4;0+kd@Zl0+>4Z-BYXX8Nn za8lB$`eLnagCR`?NZoU#8@?Tx-Mm2nwGF0bobPE)GeCsK6krm7nUGAoteQGbXjVb- zqUE~%WpT=*{$!%>tB{n7!1xW4iZAHuuQDLz>J{~#2hwLfDxX~34kSNZLA}JVYxqR( z@P6H%mnTt36Me@A4{KPI<-n8G%ReZU80eg5%|gY@aaw59^ZHzqM%p%Dt#}qWLsNq^_5Zex zu^l8-+{ARB4?wksK-tSdxc>0eXEh~|Ij~$Dds?h}E@pNN7G*FuAezC#I(9M(V4DD0 z$EdY_1HrSgPnZF$Jvt?p&2mKJcGHjPe5UjNCS^v`~U)2ohJvCBhXc# z3{sW8DZ8e;eZ@1zOA?O|x(k>0qDo8vzGa`Vc?8)2W z`+x>WaJhDd6&puX0l-}%s5SC_Tv}I(b}Gg!*MGNXz>}X#r;g3z)Uo->{k->)kJQMM z_(#hb+zG(I%{yO(AcFoZKd_VNc2kHAV_o$z1)wB&ckNAl_SKJJLs!wT z2`rZiZ<4rEqX@EwHa2UtXxq&?1AkJQz2sGK74^=yG@)a{o}qSCNGt zEgJeHztL*_Mg-1}z=L-eD-8^su0^sWUtzRc)c zNZ6$KhB8223A7n176b-S21t;ja4cyyPSac&02wI4JthuJga=dE>vn4cpe?{Q@WjRE zz}mocV+wQUzYP$@$>Z}ldF%oj+{CGwa}L0)Xzz1P@);O4>MExwmzXu>L6RgwTKATS=k@B#l!0?4!O_<84;$)Ce*I=y}8Yp|3-NpN%F>wvM?*xJD2{nwxb zwA&k4Sb7~qDWDC&gWZmWo_XXm=+26t8%{0>@XU2e^G%$f&##c8$ZIYqUjW?%!q%EX zDZ$o;fGEN0eFePK5K)5ZSqGa>PbW|aW{))>+A08cqS9%XaQ*J~aNkM!iIeUap>Cr^ zcCqFJaDI?Dg>e)#MCyR^Xgz|o*giw85r?7ecHlx{{96P5g$x1z=p96Nm%;|6jfLZ}B_d z{CP0BlVQSANV|DSxv*hgsYeuZ$!O8)$E6fGTZn5P*kuf=f0X2PHq`x6mVH^-d^fgm zv}nNRL8i1MRIb1mVI9M8( zKX(aJ$qCFfPvHF7%P=ntAY7tNeY?ax-L9@A#fbf?q zfKH_fgSgt4WpK9q8kaW1xn82B^LHxgb9%TuFtSNYqTr&QR z0P;@8Mq6<@!dC99U?KM_Qp_do?CR!%8wR~TFO_Qq@VsD#bLliixF^%`-8QF@hZ6wB zxT$J)7yUz}2j>o8zN}Bre#lT|9F|${!5*FaK;Gw1JoLfR22p6g%x|71j zFeph#0R!x~i0C+T%xKXjcl{HviZ6)HSlwC*SvdnLeeeQ@ockDE%OCnjpZ{xG7u2!B z7kV21?qcmxJdz>4SF1_Kc8xQFXB*}yA+hGibBIUdgt|eBoq$V2EyepXJ#$<&|qdUqw(I;0(cJ-XS{8O*jJF9c|`@ZsZ_OAV8;BhFW_A?+~ zh{u!y(9{(m)C6hE=29zJ3Z!WUyOs3rk{yoHa(f0aijd{3&j8?x64g;#bPR4O3m{!)Y_Q5_m z{psD*Q11df2kPN&ZE`m@sYcVelXcy#_3VIhlXr5bgvi8rzaj5OYsRv6j&ad$%6Z-{ zK|&~K^K)r@D7sHCbgC;_XeqU3r6g>ui5uL zRoeDm#@#-0;7>sC*!yuWbkCJ`mv?vjc-`CiGJz*r%WcUZWTapvkg6k;Fo29eocX24 z0AyVqk;GvB`1|pZOaBn(XWuhGEOq3)e>EloLE)qU1%kEKJ^bs}eg^l}-vEn+vOz`` zMh&0}B^OuaF|By!I;mr)?*Krr#W;o>pN=I@`{vh1;Y)TszlYesP$7Syb^j$}2l0tS zG&I7evOoEgg@S}2K9*+{=wJQ3`FJ1j-1$D2L^8pPd#X!URyW?U|Jz^u80wlO0_ej7 zR0CmR5kMc>B7i=4|BPxbM*w{vY)AmT^RvBP9|oYGQ~*8rL)HZCmW8#1wC#1s+YO{! z?XCdE6{lo??Y8~l*rnTO1$T{9cR$PtrW#GmpZN|n8#9m*$qWaUp>>}ZRO>*q%=Alz%G1IZ4bvlp{pO00W# z-R`m0eT|z7Ux6CM;=A9(X1b1*jeA&HzlqKEI_@sL39zjN$Ah*xn7Vv<{yftCdgpr- zPWr}OE6BINf$E0PoK+a3Xfz2)Ltzq>=K3T_8BHjBG6Z-&s<>v{$@N&jU&f!qZ9m&1 zfIfKUx3>Vg3l(-B5BG|JM>=0796)!mE_t@I2WtJgtit}i-rq9;HUlF8MDg^+AHrXI z{HKtZW~qJ*9f??JN9k#~E|8ByEP&S+|1Ey+#xJ04H(`}vC@}y1+R8yP($z7_LGuzu zwv%}s0rdLuzw7xO0rZ_e^B(&J3xaA2R)!GgvH4GZ_pjj8%v|3aNdVx57eIOy;*Wq5 zNcG+OFS_HUT0hamM=r^Ms}$jOQg@Ru?tO4bwLsLk65>G}%kF@%Zixlfoh1_e^)=dnj| z_Sig5%$}<-oQDq_d7Bwg){&+G#s}}{_wscs``|>o7^_vVd$+T@_%5!kqe)cPHg)aG z69Z|s$V&~taB+q;K?CNEX_9{QC0ICu1 z+i|QvZrW6@J|Uukf`e-8THOpE#hlx2{wOA(&Soi(>D886B2}mc0z-GePNH5P-G$uW zxbwSsbLk8C?#KQPo}2rq7al8gx~rSYQu5Xzr#U#AuDyMcucOnMH;LQaE;jCq;;S37 zj?caRkMY{Of07d?O*!8wh3N{5Tv|=3f=3MGyG!{ikHgjLc|tJ{&6;Lz%!L5K3U6SZdwGshPR{?lWhg6IAmaBL2p`FWP_c^`Gf` zfn>}YSEFa3V_*P%lndz419_JW)4Jyw_PF_|nCN=}pqp`rC_@71eL*ALTW2x~0DbtS zY#a&L2(LB40QxwR?#^*g2cXyan7VtYzE_ZV34$)I*Px-O+#?;UV>%ZsnIL&$Ta5Nr z3dyFBwt@DBmi(V%!V)Au`UqtxXJATPaO%PCT!KOheQ;3bitw>QELWJ9h8?r{iMbD= znM`B;{F9hUj^p98--c!~i;HKT$~Ck*XG>(~M2i+}7+FZ8J%U~QD8VvUz-$VQ7F^@l?@vb2g4WRF0 z?B>_eDsgA0Z8%B*{ctnT9cG|PPP&B+NGv}3fq#r|Klix1Z#;5GR zyY=b5jTZs*JsQX(0?>PbU^{ESpk(@J5JvkV+LzdAK*kTfhzBg6D`0&W0FBOok_I;c)a zz-nsNs%iosmPx8KPMN7kJ=V(M8R(M^pdT1rdZn>QY7EjohnabIFaHCaYCKZPU$`XeCjKXx5Q_dza-(j&_nfF- z-pH-wknZxny{j=w}P4&3dVHzJgw&Q6> z+fUSKMpnRduMr-WAtEXU*TGUAgs4CL7K-vk~Eke6xq3sklOY+aD zWL4msGheumK}@dRA#8qacQ2vVgfD8T3Z@2B6v`Gv86KbeE}S`b0q2fChPhKuVY)ek zOBbKZWxUu*mHrp#$Sh|jt_1jq9xBxiND}uhe*G>m{ zUu-=vum10ZtxXGT0z^|-1pskAdTJIhhS6v;8q+|dDPZG1!_x4DpGpr(00NU<6(+3F z1fXVvH7Bv@t#WzDm?k7c#=?e zNI_U`-@%{X_#|Gv`&&>}G^FVy(nLXq{O98`aHN;OSbTkq^H%{22(d4h;Z9pR{Cj{R zqHrv9^^=~|N5r3b`KR>S$}8x%w;UPhySV>d-Sp@g=(}&fVZJ#s&SsB(UtQBlMNRVZUYzxRzf=C#n z0+2w(I2jhQ5D^eHkh55{2{ozHW(52Ga-g}0fDHTZPQV~sJaY-tjnjDI;r!mzy?iWibfmGFkoIP8)#4-t*!bByYE9kQ$0yfd2McaK`6;9+-UITl-Rh7J| z6v-7T7RHL?iT@{sqNi1j;CGkb#74G?n+vaCJzc@V{nxO#@;WwKTa{wJ06?5UC2_t_ z7{ua1^#mdSGw09XWM9aiI+FZJ^0>k%np1>EgJ7moG#UoYDFZfTo%QfAbrLVz)&0<2 z7hc;<@_W>afcC$^?T@g>BB8@Y<)i9<*{3XE6b$t8SaLBW?cMyE020n90ouH7M*eT{ zq#1T{h?GG!15tvenZfgOAI1A0{wQYn7>J7X$`zZJd>uFl>u=rMB40<4I*ODpwHEN= z%}?W%yT1!)>i$Lz-#(65Bw_Pmn?8yR^uy0UALf1{1ATk5(0mLPZDw%g~|J5l>>X<3wZR*dvWr_Lzp}LBnp;D0F1oPg!lqIjDnr$ih%}cAavE1@P(IZi)M0B zs4p)DRI0dM@z-%7jutIiue3b`@LXGrv|5bK7ceHc%}00!JJ-jEL_s1Z@l3)(ii${a z?(}7xC&FXrpY@+{AURlYku!aB?=HVoer!4tfU}v zr-X`ANUrFqio4GsttFPQwE?sUP)+jtVM9n7z|=I*oB~V&G^QGUlUYlNb_tBvGV8oE zMu>7-`PQk0KL&ob2YXA(VcoBz-7?A(8XDRTlKzm_TE;B-N`hLKx4!uvL#5`9eNL$fv0j(_{Z41(M>UpnWEEaz&F&c9j z7*l{lL8=8U00xiEKZ|q69>J;Ei@0?0S)8AJ1gDPAxuDb&WfZ;j@e3|ZzoY$CX6B%6 z9yc_jMT<6^gDyMFZhy3W99GD_R2b>rR})diSNhYf_1~fH6{0G?yUfB(1V z+`RJ=Hro$y>;4;fcljDx>9Pk*$ro4?zqnUxXegBnMt85jZnLx+cQ>a9jcG;FuxL&@ z=DL%X)zWjLrXl+;?y*SdxNfXIzUcMZ7}M)R?%3wn zJ~Plq-Iiqrda>bEEFh#omP7$&zlvC>GMZ)v?>YYi_|8ZEbDVC@c~5*WWVO2QO&k=b z4S;}aOMi?n-To}zdGHEUtt*yo-Dz88>!i&z1yLg}!mVm^iOXJn6&dJ}fxg{!A_IMZ z_{{>!>I#V>gHQuAW(xn^hkmm^1HJ2&t1e>LvTcvWM+Qc;Xh$X=j|`t^9SdWxfS_}% zI8ei_7b1c*O<^|^WG(f8xdN6ut{V~-%Jpyy5J!t*b0O%?zbj}A3ND;^59ZH5i8IIM zae3}poSA(br;eZNC_*Gf!7M6Xr<&wEXJFO@W!r)0g;g2sz1orJ08~ke7A@KYJLtmq zQl4ycAOBQ-Pw>ZuG*tymr_w2!?GjZIaM<7sz7(FD;|s(Q2xn#=#+liNap|FF3f8(B zw6b+9+`op!<=3&iehZ6B*Kqg#TUbw*^Y0EU#%i(kDQmw7WtE*ZM%uD-uVrDeevJmu@leY+b{euo;vekB&ON5$vJ6YdKXC%78@H6 z@XFF};I+ly#`4x}C^Y~Om<-fru$1IAqQX{PsP&|SnC!^#VFa(GzMz*Zd!cU zL)6{L)_4LhhlO^Gg}q>xgzwCYrAf8*G^w1}agrqdC_ZbKZ{mu6p=Sg7Y`t*e%8UdgIxql653TdPdj5kekHBLc2QuKd%g~WN#?nvyn3$uQA0b^*)#^R& zx5jxnKd8x7b%cG@seeA~MpwxFE}#28Jazi}arxZ$Vw#Q@xKO8$?0|!+CrG((Mp-$n zU-sW9NIcy+R`psd?c2Ea?w{c6i@%Gztye)}YR7PBmqCl9DvimX4?8~V@Q+f?ws$AA zc%*)1PzHCW!o#p@c31!IvVZoDflfoMpZUojp<1Pkac`1^G0KV@uW)0Q@n3z!^^C=@rvCZtn25-_7h8$|#_z96O7%yxd! z0A%xJ&N283UBRM4d^oPpP?D0bC8%buun!fiaBG2d-7(7T8X;{LQY!AVQYx6~q+FPb z;KB;PY;vg}_udqN{m8<2;^FVasbdf0;WJNR?(9=|V($GQBtHK2Kt}BSXwjlYJG3^{ zn8KyGXK`unS+B?L)`+!@dstb!iMQ^)g!`+DSXliQ7MI=#dR!nC7-4@jSA8+mzzz&~ zgN48VFb)SG#m1^)Yt1v+VZf}InkG!mD3ZWbpK^(o3PY$Uc&dT0pC=fuaGr|#q(l8x zeWEA|8#Hgcl4d;dyT{>E%s+;<$k#6FAx+$PswLJOocvoz@y5~@aBb<|!@N$obpEg6 zk>l^frBffo`PnBsq~wx3Tb6!ojnPk_kk zSI5FY-HqisjCHR^>pwsyST{^!7she32hVY2jKoQupeW#@%AV!mYbda~au=$8u}ECi z8`S8V!-A-R_NGO;sYo{s+ASx;l8HM7L_XjY5fG^_3`$;l#oZ4BASEZK>x*XwJYlMF z0vFG{2bbrb!Rgs~JaYC5=FUD{EyP(i21FT5K_4*!4$-1Ti*_iNG&KA=4$nAu82QO# z=W+7bSv+##c@PDbpkVRcx3IEx8w<;?;l}M(u(Y{=l?MwT7Kj>uop36u&{x-RDw2&q zapiSAMXE@4hC?AyRxChMmQM5U$0NQXTg$Q`_-sfGSx4 zS~MFc@c8lX!ntEt@W|=sac<^GOq&x0S;%ir(zc_wQ_9TyUGj1>a2ul_cfG~6*YWP= z8@Rji4cuG1hMTKj=}5s8Oa?-Lutc)v5%aSVmppj=Mnr%i$AV`8G=oYLN;Cb}7j@MO zBuKabDu49GXY_M7Kjp!Y{VgIIn3WY;KkEo2NREbtOTQw3!?od{Ar|jj*cByQh6|5J zxgbmi)H$AojyjL`?bXpuCP1{i$gqT?Rn{32jGv#Nx^|+*o)S_cv~0ap@Wsm#t(cxwa6`a`iW62^98pFNrdMlB71u4* z03kDWkZOO!G1~Td#v$QyxG;Mj>-dln-_MAL%TVKBT!2jVg$W^ebmYF{DIQvm+(+p7 zNx@oZsk^$Q3^ss>E%^$@OmYfmW*)=onM*j{xPVjB7jb<0JV>T+a{2;JOg&UD$)XIU z3=7L&cV(JbaC_+`w3)HI{wD6XZ{gnR>wsa12#i0qYyCxnT$rks3R`mR42lvid+!H# z>Q{Cm;WCN-V=pCKMp1lqza(5NwA5Cn^xY}``eQ$Vk6iijeiAP3;Gy5JQ&nO$p9GOD z1&;d}V08~54Wc!>93bJi)lss}b{bl%Y2Y^ORlq79Z8{jidE7$Z(R|qJfLX0S0v>`E zAJkL|ZkFacIaUP0vE|ZDSKza~1*BUHn-NF|YdM$z?>per;_fDQE9&R1TmHXmOu@Z2wNM##u{P8gDac>)3b_3lQ1;{G@1gKkT5cc zn(ny2PuvO}aIO^7KC}T7Mh}?p6ex){>UkP1+6ZwA702if(v51s>?9&HA+Yzg;(hX3 zx{M83!8;GWmgg@3{ApRq0bP}0=zA+V}2{s_FPVs`2j=FUEaOBbKP!>6Cd#WPRh?D31G zDn8vuCAHHB5W6?$th)?ENiO-~%%GEv7%f_~XweR{`Sqi+p7?dHx)A!eYp#V3W-n=0 zTqk_yv5U{*F+2<4hkQ%`+`WGd@2q?a%j>ss{mx6cz4T4A(=E?>B)EKqf< zb(e%0S7$^8nhG|S3$`b-qB#XLXBEvU@p|eef5sraMqa#^4(krF(!S)CM+rRNyY`K7 z*ufeYqh+6udz=|!SPQ5TN3M||3#l;VFl;BIK=~Tr-vSWjdgc;}Gkc&{nkclmX!wN{)|u8>nvli~<4V7-QV0-~&tBeR69O<-%YC{AY@gho>_JqyoL zS1{Aaj=t-pL_Q_dF0vC3UamX=!^Vwa-Tv>Djt zp`^%xZ-gW3jy<7J%%6E0^XIPM;;HxI^2HBe{_GQB*{Cu0$LT`|++F*is_R-X0x3TV zS^k{l0dZcG%?IRE`h2u#(V|5=jO!cxrYdTDJ$J;qUSg%AUPb`U%Q3NAhfvz&g+IC* zK9}U!bcICX3e?|I}y_^^NP;{9v5yYwyGT6qI^?|%a~Zhr-8tYem78fQ-bcfBC#bLXLuPipc@#IYFqjey}n!QW)Z{`6O{# zMT>R-%-7dsC)=c26quI*>U)x_R_p|6iHH=6pZnt^Ox-J~m0@Qi!vKF-K$l=CsxKGCZ784z3C{w4OKg{^y z9ew-GKeM~`?r#4<5z$zEG*r?;_~abzW8MZpM(5qH22;hCxad_(4g_rxGBO) z+vg#2;0ad!BH!}38E;$5t|Iu+^zW`eUjjAD<8@S<2Yu(jeisof<+;0U%euKfZf@mn z@;(`||LXJH{q4VT6%Q5vT&yD+U!}2LzSr?@B=Elam%6^$DLzv9{66}NUcPes_8U!+ z?{OCq?d+4f0=LC`TujwyH1IRu``he_A81y0-6V{n7hZr-R~8=aKYRdUcg!U+0HQ^U zcI5Kn9W5Xs&-x-9Q- zybPQdz&p8H0C^G5pdGA3$4Akk?d$ZU{V7&^6my|y(V{$EWmFtmuf?T6(c;cv#i2-X zcP;Mj#ogTOmR~c&(0RLCyTcx`Dco|PC~zQ@jQQZKoMc4k&O6$H!A7B)1scQ8j(r0M(L-KbZEW4ZQu zTbWKRb@K>WtE&yTZ1~Z8_QG<8{^rf(DB$)?$N>wR%UZynM=0_l(d^Ode=hET4aP5} zVS_~>-(~vPLquQM9vd>hc)75u-xt%y^mX4p-skFsGo&FCx!lwBYa4FQ{MRdM_Le&z zEBrKWI9WM5{5tuRn5p1=-H7aZijf0Wo{k{Pl}A|@76TvSsqDsQV!u6NZ9llD4Hlpq zCO8vy85@Mh8o%86_Q5buL9@9R4v5rH8{4iS-|9VnPmgJ>I*~TXjYE^e?eIIAXX1hU zYnpf5Ll+?4@ovdy-}~ox(&=wxisa-BJ~Z%C`tAYa^JP|c^j(EHo{wF3*9_=_7q&|q z@@_!veqvC4*_a%H|HjccSjIpL;n|!sP2`s;I)7|f>|i6HREpt68;)Xggu7i?B^9uc zLb`5Jzkpc55TJ3t`}=`6NqAm3!I^Mka?Qb1KKFN;4dKvQ%UG--|2uD`j(epDXZsXI z)d{dUi1>eAmmfo?=~8RscCOz?;HKB#J9^@7y-lZ~=|}JzYCn`oBegl-eQ$*{>*>ON z;1&j3RB#63mpMYrGV&}N92Gi{gR}B2;&b>w4r?W<0e8}po}d>Id5!wxv=aQ((8R|S zpXe>vf@DoR30W^ih+8FKoD`sFEdEtvz>TUePZYwZ-J%yf1=H21fI2jWoI2S_W%bvC zn;5Gd@B=}2`L~&8Tl=DAr0nDqiYgE?=^9}VFBC|BsL#kXa7UqH)=4!jA0^EQ(-yfrMaG^ojumM2Dw*I8_3Q z*55cOU6ZXSdKh4#66PsKUU`jMf*euiD4y3Gkcs}hIl=x33YNhgO8JJh)zUYL1jp?F zRRVM0^7*g~#BqZp9>w8Hx40aOZR3aYmc9CF`bX1UR%(bXh{dh zgh72IOJhBP1S1?5jgnM4-jVYs4D{OeuHLS^DHt6f%T9uBXV*O4~u-yKSoH` zzZGhC2R$QGMu7L9#}@8&Qmv1%9fXjz}tZ(cuv|G834R}4wF##DI<7Gaorl*CSw=gGx1JlO}-j8uRBzbxqQn<0WJIa{BpA7Gq+KZR0$ZI3H?@u=`s zW5{m4kp%zQPH(}Tkq6=D6 zXI5r>WOb5dVK;`{#XUavjm$6fm`S%rLN19Swkb6J{(k*G_O)u)h2^7EKdk{-?xyb3 z*7o$7|LW=@q|EYQv^ptj4B`0j!zEh0a70}bKeQ@zcK|O2CqDSQjxy%WpDWOnme;^q zfU0l-%Z2kB5aUrIsM;t6EHHkz1~(xGmXR9LY5paOD%Tfe zH7?qD$EHnOU25sL7o!1IlfrIQKfOonK_t)M(&4W~2>9_%w=rKK6p39Oxsn5n-B=iUA_c%44u&gAibX6w#{y^u#ikr>CiKU^KscP6%q+@ z2cW;O8a)3DLC`Z-Z)BCh;*uXCUI59Vn%;zvLc&fUV9=mG!@!anv0&#d8tPMT5$55G zbES6YoK-QXCE3fPsY)pzUBZYV!wdgSC^tuS%gbb-?$Z(u#`*@Uiy)6v-c51!qeDNR zMQmgpUGQOF$E*go!6vqW-?(5@Z#4y;Y!=iTv|qNk_8}|yVpxL*#a!t;ZY1NiMX#`c zMB(n6!~&B@?50kt*Wt5K$!O(}6uaU^RCPO-m4^Q9DBFOy5QF=>Y8EQl*e5^MKqu;x zNINKhG3%h}hC#_iyhfXV%&Gw~X+fxaiM(_?s3hv)^6v3D7G4&RuTJ{Atq%7)Au<2E z=%>qbds8u%HD>V=ziZ{26kICt|HZ4tF8gnvfaj{!ED}1%BQ9H+kJFl}9&foG{)u$EzO#_j5w$~|@FJz0@n+K3fNCkL7_a4;M?KUji8NA>PY@=DKE1)zj1A&*Df4{Lfu zVEH?fsO*)%(t4dIy%T5-Jz)IUAYoXM_s7R{;v>cWNW?u91BN3F-8smsSrD-fh@kB^ zSi;yufcr-}hHA?H7mxft({2~!^OnT2d&OGNJc})72&#&N*v*lkC^BmQ3%5ZY`v=b4 zrhV=kYr*Q|mH^gAVV8006H1HJT1oE_^X= ziGygl_FZcrYD3>Z{Wuoaus{xwpjk+v9808$Cl&h%fKciUa!1E)H-lrFW#gM&pH=mk z^leVp)SRDRSJnOd^8jahmTz4(IUVHl23m8fY>qgoY7IK6Y7aZ9>I_OReVnX?jTiS| zirwV`e>xC#1*~6cb=p&~6o1Wn*!mv4uDw(~vltY)WbfL&A`0+c@#*nBV%cSTKu%;~ z1Zi~YMNY!}5+;rM`HokDKM^H-lW$KruP!y+Q$fB_U3QNTM*geyRuG6zK zc6}B%fL^g{6VnuKPVR4!a#Vf{n30d%lMaMK4w?KWA)~GNTo^>NaYM>Bd5Obc_9fC~ zaGD==L5{O+{K4_nt&RO<)Nj!w*-t=T(D5#q!wZd9Ci)R0YRVvzO;jz7cNN!bN#B%{ zeqqjF86W#TIG)MEHw%F^OV6DtLs^(DB_W^MNbTeMZiFQxSMy4nAE(btQU{?Ol(pf5 zyg6`#K0lv*ZXW8Dqp&qjbr&*_HRsy2I8>$wY}OvcOjdhKf??)!r3X2UE00AOCYBw* zU3U4NK}JDKmD$;CWkm-tirh2eMd}C3{HF!TB|iy&*BN6|H_BafA?f4Q<|}4!IW^U& zU(Y+|K5Xq&q&74%%5_^%)jhMFW0|5PIj0v$Swkntg83vyAF4O95G*r>h?B}%D+86{ zZ;~sFfM|k8F06JdG>f}K33`&Q?`ex`&mpL64_n=EB9ZvGskkA9UL%+5rqRut?Wyuo z&|__ZSGM`ryoAMeBJFF@i#o_ab@HMLwc$TAS5E_bP415s)DPPu)b4kGKwrJKQRs1v zq)BtBG{SQ%GDtmev1ik6=Pby{gAK;V5od3h*^LV=brve%amK174j`B_8ra1_bJqz7u>_MgR(05 zliIcu`SR6@BUmC!5sw z|HdRsH4n_~Qg&i&YO?skWJ_?N9ayQ`Uq3;UP;B-0Wc8LScE`jK)QO!AYElz2ho zg|sYcDhjzM_Fo2jmOUMv5FfOoK2zB68TNHW8->`KWx;>6sx63Ob%N}$y|Sce``sSE ze^xz$Gj;NvEr~qWu2f>l7HdkR!ey>%fBNKLoxd_#UoebSJ#$pUdCm~V8NvIJ!Y`iG zEY4T8x5iirjj1=gxSzQbZaAVS^_C7}U7S)dpit+EXp7 z*=eA|OZ_DkvmYt|871_Op+@}@VQv5*M^RXC__t%tM-SV>RJEGGXwT+SEfUEKgSqCccEezwNJ zoQzE5h;D&3bS0iS>fVsGYazguYl$;9Cgnc&Xal?XvZd!&_SF1GMlQW7AugY{$I}lY zzSy(8EbT5?Ba3Y|;uMVW=`Os2)iZNZ@dym^`2NXABQ5KLKq0`dE=pt6=1U*q zbo(37=5vGJt6U^B>`+wo)gL{wW;JOtk%P4>JL0rATu2ZyJKO_P72c?`{mxd~9gn3_ z)S1M8*5P*+wEBLGv=DZc($rF(>|&72VCEr7%dlqQ%poN!TdPtFe$*=)_FbqJTiZ~I zs?e^us;`}_*j$>nSyz3n$t^>%k1t*uWz?k}J-A2i1XNQqfXDaCMz^NM3(=p!@l&hy4aM7%q%<6q!( z$aJhR#|;tuq;8Ztp*ym^_zrN>d&fZ!!aUI1YT@jhWUJzz)Hc`a)}W9SCRW8_hqe^E zaF*L`Wf^O>-;kW-f=ET3aex*oX~powDn^= zUXxg*U7Bry<3I;4uGUYA#;Shvv;AuR9_tchTbZH_VOqb*+K+ft5GU<8PS3V=w14F4 zZJgwj*gtS3G>ezXFLs-^Dg>8;2I6hlS+6W2LyJV%6&fDb(3a-%`Ala0S^UF{D1E#| zpmb>HgXPT;eN+2$-c4q5{AP)j!~B0@gF*BfHZ_xpz?kE6LJ_qPVSI!{aV5p{brM0{4#_;@sxh|ZnSl_D?` z+in+HVK&45mHxGQ@K#opuqiSiGxxXP0-Kr5b_hS>w`&?+AV~K4H_OIlwkb>v5M)HK zvXnNw-H$or`h~I5ciY21O zuK)Zu8-daG%qDyOLCbznnp*6NCh>qW+Na^*Sv$>W2R#7%FDYg;sEYUi#3o%3+4VQ881 z2Q4>;&}$C2kPj^G)>o$4UT4<4PA?~!8s5Y;wDIgJut>s~l%$fs3XN!lQ%vcZ@W;%H z6Y59C%bA_J!Kwj34eO6$SV@GIcw*1;#rrn*^WN?A3qQTEpW8c+G((K5dCS{`|?+hgy+Xi!#~Ymc0pj}ERz_TwE<9cPbFl4O!R zzG}z^P?`ys${_%jNv7VA*-%r5t#2_Ke+K zHtuiA8`V7a8>+5GOQ}8X=LO7KT(Uz4c?Gef$G-*k6SAvusni?X3HJ_AiUqQ;*e`;ESMcd-+fxqbgG{Br7E%IIqY)*-lknfj|zK z?0utvjJbQc#_oN3-Zg2n_y%i6aH9*dArj9CM-yJT(-}ZFhWy@x63){rA?RfRb#f~G)0(&UGE-K*MB~jX zIb&Nm{~T5-0R>0zlaegV*lunZL<(YR%J%6`e>r1UL^d)+L^B3#eE5f9A_3s*1>ZvD zCaX@=9%5p{nhaJU#ezp0U$ERtw8B69ZY3sC_Mp0+~(wk&a*MlcFv+V&JNa&ZDs^AkxLikSgn<1EX_-03v62=4#QpTd9X9tlChD-`t z`DUX0a)@uF2+YM zySHezMX=ttCLZbV7u+>ds-H${%aB-%R}K$P!xVv?w=m8$b=?aVb2KUBJpC`>NsP%X zCGds-fLXWJmvB)*HsaH*L^f zzI%d62O2DbO_*K%INfZE!RLiXGW~)*4~tPy$hAw@e%1DnE&m|A9si(}QwaEfO(ki6 z!UeX!p)l%d^%SaB-i)sle+a@&OJEP#8wlPLqm+d;+mAlNGrsps8E?o>G&|dr#@>P>d{SXVXsZq> z@GY)wz~6dYut~nFV>MGrG`JnpvlFZurJ=D;0eeB3*Y&Qb776Hn=$R z5@fbUm)y0|WmO3_unEiVZ7DZqXUgdMRh?7oct*l4?;JPSV{-EiMZ438#;b5p;cn@iRvi$$6n7OX7PxIrK2lhO zVGpiwtQhKqj(u&+x;|Uz<<9m~r|Mfpi8 z4-cfgZ&h>UGayPFQ97gg;Mg*hv+qJ}miY49hYZB4-ap(&XyU_Jf0#ha^Nn=g#BY+g zC(sV-!oi4j3ad`-r~(UzqT8onNfkGi9a?6;Jp1nI{_g>X<(U>zR z>QrZ0ZOr{ow`KBtd)SoI{tvX@Pxy;RvB`?et@K5b-BKLCxTeYW_|@Q+RGtdnQ$6JE z8CD9D!O@W5Jxb%NLKYs*f!hG8IwZuj_;iB^!wl+VR5<*_Uk(lU`MH*wl$w?=+T@MK z5;rcX^LeXh;NLX~Afu5Cl7Q$(jrNSLF*UG3W>QRFH`@M^n&?w~HT8 z*bS{O6k5YOgyoFkuolFLHCvz1=ogO>YdB#O^N`6FxFU*3(Z_iCm;nlL@vP;HkK{Dk zOkgk!P*8e#P&dYb0+*n#^_Q`Z>WV6)?z{gh?1z?rTHuL`i+=s$^@*hQ$oz3iE@%HG zM7iA^TvEE9o$$UMb5<8JqbN^Np``Z$`c%kpGZ5*(EdvlfPt4 zl83{_t9eK-0E9*)%d3S-0iB?RLsuJloP{LhL7{45ptd;z4(({+hWY2`WtfTfJ^;oiHU!8zni3iR zTtvyi70T_?GFk>3bW}7-fZ3YcDvR$9@SUL?Bd}=Pi{7Sh^k0JnIR`6Pyh}T-nlNbZ zx2SiH$k_RX9q2t%(Sb2bry^h76B#8D5|Uz|98oZ__tv|2JdlNFQy5kj6JP>^WMW1t zCj9gQsQ)m6+e`S}RyA7B9$#&Iz1({coyuqo*hGYV_CdSek)!}=^V<J^gXK0MI z6u{D%%EgLoZBc}j>n`ux&OdbbroClP0fxQC~oAbm2G%`Z6G&RcW^H(j$()@&7n{nLyR^A`Yxhzrt!jCt0G z#rks7DJf2evb&@Lj|cBmptVcP9|X)T4JHFVxj()p68(cld=kaJl|+FuFWN|Cu+YiT z2YP;)=5Yd(sIh)o^ZNsl9q9RTpYmz+BE|AsY|6&z0ps-D1@rwNU8HG%5aAafs`K8z0x)2VpzETe*d2oI>R8yK5qRtIN(UQPzE~hpu zcZBqDLpT&8c)phCO!-rF?jF=X0D1ih?Rf=SGcYedoXP_pPgW3>%voi)8Rj;h4100& zo~tGf_V(CzbrR{YAVYYuY1zf)80rZPWO}ml&F>D)Vj55wXok^OZ({${T-*3rQgN#L z(^gr=N*-*z&o-tLtuJdMXE+c_Q?q98O#Q)uiVH?t@P6Q48|vi8mfL9B^rp4&!W@G zGUmoTJbCE|>c78uDF|K=5>TBy>IhzFlP%@CH2d?XI8qYe-O@7a_$c?daQ3PjGGm z+fc3Zgfc$%kbDO9k_aWZEQ&?KaMW?;OGV;u9;58)Yu!w|x2S0fr~!ki(!|hp7LG<@ z`Us4HUjjHcA76F#o0}4D3Xl=#KPf$+?9l%8aU!TC% zg{dbhO~I-@S64_MYoFXoY_%|dJ#`H$oGG^`e`17Ryw}rig@Xl93&-x2JJ9m&i7IS0{tEH_^_VPK+8L@oWl zAZFMmO+1Vy>58D(3 zH^*bk-*`VW^uXTUJxMI0AkQ;W%Lt<0d7`)ZpF=&l3XH+tu9M}$lbQ7TIRW1%5`qNq z>HS1bpU5rAlqwoiu0Q6%KxusV0{# zxGxtpN2mAN*#Ghyrw7e+r49x35ZwxQeh!R?Lm%z2IYgwXB3b{V8H#_|SW@N?<{kl} z^r61MLY7s8(V(d5UREAGPV~o<3n~b++rL45y%^`%Hy*!56NLT|dysdK0UU7pmxhQQ zc5vvJA4sMuDgz2p3BP*t2{?e5dilDBwWEMHe{Q<^Q1$+rM&VEPL4dF`Co5?bR3CnM zIjkBsnWZT37CIGa#$r$2%u<$Dnuf^wx2tx_!EL18^$47TlE$Z_P@q69yKfBANlfDH zd=U}WJuoysE@Z^2asA$Vbr?^}9K0nwfSYgf#KNb*J~ax+$d~Pcd|!QQIkXrotuFQP z1OnHSvZkVou&h<`eMKI(^#y)L3)h#Ts$z9X@lM$-IIg1qk0B;E&aOuS0U(yo_&8`l z2czQ)I=k%CsaJ*}nPEeNC>F3lk2nl-_6K=c$(L5RjA&9GwnHxC>^d*!UWY7F`}?oh zakE;(Xv%6S7>tY`AP1n@4=%xvjwvhgRxw`C_x~yQhgQTYQ-8)2?oAl8-N{2;>!%jD zbOV>99-Z$q7gyhp()GA9(RY7YBcl{RwWds4^>}F9B#@eT ze7{pkoDorKBedt10!5!KGnma;dS#5^@(tu?#R^<-O)?TlG=5E~bedd`$Q$bAVASE7 z<%mR8iuV^=Z_f;fIoFNhntf_zQ6lvDL2HnaIYtbIa z!vMyn0GE{_5|1w;@9~d=!h}uZG`(5i3i$6pDt0$IFxLKuhuITFnBR}Gi$^8m_5-`C z{pEuP1%-EPFd27I%fL~>L?#Qzh&0~u4-IaR5kL81NDZZ2$LyERTt_OYC>s+Dmu^~L z7$ON(NR&s?u(+a`$Y*FCJJaZ(GgR`-d+lqZ-g&fh*GiCM{S3rua9kC%`wV+aU!Iaa zhIA#PBj-vL>=1G2HFD!?-eyPW-pDuxK?PXY87GKmXNwOGG0Et9iqJRowIZuZ+o3Uz zt|5Ncm&m~XpXU+|l`cUOVR(W(+3fF3o352X>!bQRQM}JjobLPcc9*>;n>#zlM&`DR z(12VxjNtvxnI0l!2siM-=|K!;2R}rk%XLl~xhvDd|K#cKG`RK3;uYB+7H1Dx5uL}q zSvMANa+OE{gnp4fjm;wuKoBPdbqRr+SA; zQBj9Ma%()m1qm}zWHt?M1``~NFD(1N@Lyu!-w|G0(RU>lvh^nQ8Ta$KrqsT0wC#) zAHBDBX0XV*Z-oZ~`h>ZY(S=FrDkZw-xWO#=io2x+jVlsv!KJd>^T6r%Vkg6Qre%2I z0Ymb^_>L*JwP)`OmPu{~G3OX``N5(Z)uFFM%rG$)q0$DHdjLZE8~{l@-q499_iLB?riU-+OM)@pAoYz$y!Z=-b913eVHEd`q#C$p5Yv3#EqK>bKHZKz< z53M3!?J@B`WiOZf`ib%qFPT0Bo9N6#EAQ8pii;QKrp}u_dQQKH1HD({Y>zWk_w#u> z^h9K3if{uf(&=s-Iczos27a`LpK~gE0#I(jQ`!q=5xa7;ji(LiSZ^yDz z2jADzbYa%h-?p+3CoAnhBFIE$EVTFXR5ohMZ=pRq*Wit!;C;4|85i)wBt}J?C7N(E z*fStw<7FnjLl^TKA`}~RpQZtki_XP!xl$>w6o;l)gWhFM6dOD$hV!nwS?{gTuxrS+ zOAeq&qYCTHMe;-3KS(ecToG3KXEi}sWXhG*D~Y&Kd9}uvQFp7dAZM$RfWK#S1q0Z5 zkJ9;S-Zk>`AUlW@+<(f}mjF=V*yw)96tFp5MO1deyMd2M_lW-z)#A^)7Qu#=Wn=2+ zp0j92pn$(SgV|oewMy&}Su@yM+3toeGD4F%AtoBlG{#a_j=W7yCZ>v*x1^NnlPWsr zka<<>Ej0BhFT`Pe`{%<5$+KMmw(y4{!429>3aOXh0-VwRtIcEUHe=o^m1Dn^OUOWL zLzW_(Y3-lTrWQZqySN@hxDmZ#xe>o&MUB~A2Bmfl>@s;L-^dg*XS8&arfIwoVgy!9 zYuQ%2D^=GIj0^YvZ9fo%v&?{qJHIk#u%2D5=AqEoXj_2o({Z4L{`+d^J3L3g>LZ%7 zKL{Zdqp+tSj(jLq%!9BKnHR%2mtbje905`kHo-ry#3F|?8TA*G6^o%Xf?;$>qI&$m z(%CTcCyL>Zr;RqxzKa90vjMRKHuoSsKZ4ugbsFKIXo`BpHI}k%Vcokr+c1s#lzV*! zV}g9X`3`-v6bJ@@KYpRdyHyAbi-zZ{Mc%jq(I2MGcS-=Xj4-iRtr(@8$i_ZKYdPM3 zNRlwMxLNe%S4yu?lp2BDMJTgeg40c<9Y%;hd55@~Xzzd{PH& z^X*FTuO(V7s`nY^o@ktTTY()63<;2%tl%3dDjz&P*#W-ljB_w_wW?$r2GzSZu@MBc zJun+;%oWJU!3+T!b>mgW`>N`N!*7q` zoxrW*fVvyGX!)v@o<`Hckm^vYx20~dl*Ej_Go{v((I7>1ng*9DfWCosq(n0DeNBd| zX^n-l9=4FQ0nuls@21+8r?z?V72ABp1)m9X$U&SlB@Hj7-u-rZQdbn3?$ht0jgs;o4^Il}l`z-9?3iSnp39Oa9Xusuox$Jj~2m>1De`BKvj#hy! z>>0hrFQY@uv|{DVVRqvp%d#PFakCo70VFrn?{nzXp1Y~?RM`^P&GqhAN1VGhl0|dj zof6|DyG0RiM~G(W@|a%#c>PZcp#CYZ1GEh{KXYvf)>)K4gTT4te5bVCzV;grDo3_G z-sJfq@8o%UE_KnW)wFA(zh<-zqmU-|ydZ;+g~Mr|zVP+Z{?$^Mzvi9)zk$4aidcvt zMG@(LC2*q*@4k75di6#?yz_Fc?G1X|+#5;t3#-G?qX5&r&kX%gLWybNU%oUq|ETeX z#8*}x*EbudHv!}Y>_d5A2?_5VsD&Bmy%MlNHJ{P-U2@X?#WwEUMc}qCd*TmJAFk~Pq6;9x+(Ocv+RXEad!8gbr z7AKruntmA4XPvbX}&;Jj3gQ>&W&sA-4x~9ow=(>w7V>j=V_w(X668`sy_oOyk z_xSXq$A?Z*8kTc6UfXPc{0a8m(-EqwE*NupnhCHpV$3N?%nlS!Lj@@eMJ9A}*RmJu z`YlPlKI-7-4-}U37NqjKl}s=MHN?!$J^S%@b1FU6!Aq=7c z%b!uOie<75EcAib9{Vdr@dgcS?mt9OGf%(pF|Z;UfOlM_RXg&7KWO-)FQ#CKk7kDb z1zsNFJI>3UBa<7xsRdScD<}KR9aFX{!?w{L3 zS$?UDM6Yu;Vo#8DD<9dhhQLp7>z7p-Lr#Mm6Iij~b zZi4zk%k+y{u8?2=z1Af*&LKwd@*uC<#wF?B^B3LE1fJ!xea}wl^|<76VQH)q!%Slq zr>MYt0>PPSd=d!>K4j2a_6t5A5PI5L%u%Ql79g@JZ>w+~J&}=m&F^*Xc{ad(YKTJ$ zcJ$cO8#3;FL?tchwe?06;pX6YS6NrHohx9L9FFMo5g0&32#usLzlowGNAG7$Y00H! zYi=>E-5+^J6rXL=Fqskb7!Cguqz+UDE;^M?t2M*B7rXB_@pONT#N!WRV#P~Wx4^>% z-rED;2>rKo{oSsYVfE&-)@P?Yh1DF(D>~9}s`5;n4c~S58`HgB%_({z05lev5TI0y z*P(61!If#lFY7h7WKZ}>ePJTp6d~2`p4txwNqAWG#vnJs|9&$d61n0tsqwMGc%=6$}TTiB~R`7Cu3ee zY0jQLu)E^bHLGI7-3l%ZQ~0#qsr4(5_e*}+Uc(a_ez;`tOJ6R1ymR?UfZFMH@UMe; zh;)qovU=~b=A%z$ufw`m6qIba1COD{TtIbeyhF3VcVhkbp04e+nV)E^;~3+t!MskT zA9OG#bFi=#Xm8*#_EBk-ofu>birX}?lcKP%mV*qaelBgy%u-d(RvuYixMEKNN|}DH z9u;&v4D*Y4Y3#i&w-ubQ)!57_zxSPd8n{`=8Ivhm_wA)wDOKgT6<^B=95_%ay|9E^ ze<*9ri@h4_9ei~o*qxKV5L3Bp^L19L3HY-qH`b$qW|?Yt_uPE2DD=f}8aYXPZvS+w zn_@nphG&8fK-V|jvCb2B_;8`(C_6N z|EB{*XwLOh9Y9Ls6eHrj)u?cDVEb&T$A4AKdo^Qsvvy@yTkA=N6RKr`p}s{fUfo@E z(|i0C0mb+{vimfz*_IOpHxy<(7l~+P!<&zvJe50LIhFKt1W-+91lgWGFEw& zxj~{GH*7vI23ban_ld^wRO1e$L7U5@#~Tv+{$c2>%WmccT& zwRfd9-W1R6^-yHIIP4Pl;NM7ND{)Yu9TwN-Y;t3WGSr~Ae@%9C1fSZc03`7A%Axc1 zw(4eK=O!FZR!a~X(?Toi3(OZ84Nv8k%M@lG&B0znW2==Z)|x~JGS4FGNXgJYEJpbn zN;%Y*8)engt2_i*a7(iTRvY`7PfxMCcgxwz+b;vk-uD{B@_E9vxjFj$A%Lsl6vT$m zq@@U{-!z<3?dV@8<@0`{X%!~cy7MzS@_VoLWeq9*Yu=%w*#(B+YvE?ws3Wn5Ph)GYbKfWduQLZVrYtc@sdQnNPYJG|Pmgww7pYtnWR?|1sg*EX{+ zFhxa(bU19b?fb7fjrIWjwdeDce;Dj8vT#jktZRho=_jENEFD%dQZMTbVRn02ppD+* zg_OrS^{&aLoL7_$u4j63L}BQnh!4R!w-b!8lkIr`snInu4X$!Y5f+(%!!#V>$lpc_ zZK`tHzOA+T51D4P1UJ5!!-BM`fV7sA2!7a7t7gDy(}>9}=KNFn;(5Z^ zcW(dE8A*dZLP86|t!x64$o!Uwvj6w@YDnZjMXDRDh>e2~T{GHw=)J2ZGF0Wta2PPz z((yT@^4;Hfj|f{}yzH0h8f(gaT+HmU4-^zOi-Z}*!ayv+v&J#l6CQmOAMD-kUK4_} zB%gu6_(8>J8(TwGO(G^}ryslfot5?Dwrm#KOhBiXkR8sSmNVL)pK4J7sM97U%? z2Z)5lOeyw%Lu;BI6tY9<2R?AOiBK+0v&<0*d8LjK(%dut!wz`$#p1|&U37S=6zET{UxOqCsBKh>jC-h`d8_zubHd~ zUJOCjSkqpV?eiJjexL(M_Q#^bsS(#Pklhahvtihh86FA$rLDWhERYbPLS*#^5y?a_*Tn^(L4f%T7 zoREM{KdIXB0kD39gp5dhFzn!pfEm&Nm(jrGIdSGszDK^!H}zl2s6{?}IJOaQ{KKp0x%qMr>Kx|X%AnTZ?*Z?#lTVTsKv3NY-@Yyey% zB`!|x5L~jJYuxkL*=azS^Nte->jE_K^-^gO?HOhk``rguSFe_V?^k-9Nm2$jE-?j* zYkfM#*bRbcIFtYV6|5+o$N)$TBgb!hDb^Re`_=;eEk#K$J)n0yVEYqFN?nwJ+*GuR z8MC#~XAAH}k7WC8=fSl+S~FDB!dYwuQR^88)|D^W7&uBsvY_#^U}U?^}teITkYJV3Dm#JD(iDsy+jyvg^3=(1rZRkf%4~pMMAiSCSYNOMm1K zakXIzYj{Q;drASP3!KNcoHv)ZC?3BAy8X(5!h{0_B#jwSO3!%flv-~N`{TXn0nXqB z)^H8pJp2z#u5=BNnT-Qk}(h8pB2F*Xp#X)HBI44_I zNIsG9r7F?0ioBR((Tp3xT#t*pQwW^W8EZcJ%_EpN3CPzA{VysFPe9mc`Ljtdf@t0J zA^=FRLf+%t=fb=h)RD8jhpBKqVNSf4u1*5|O%#K50fCH+EhxU)8&WORz>1sMXGw!A z9>Q8M@!nyQt1Hqj&0?%eDd=um98~gyzDF7gDx7B4|dEIElOqEe&T@ z=*YWIWwr-TV6pG*0O!M#@_V2F-y5#=1om(1U>*CoK=0n`B6h|s$rg8R<8#HWZnEmB ze)<8?cmToX^p0-G=?%w`jgj5f&g;fe5;wZ>Y_r&H2MP3gul=$!$Y-cJdx(N+%&VCp z-;4J)1HQ1b_EfL!djq6VPqX9OugRW<=z#N@|9A5Ig?9vvPHE0-_roj>gGavB(xC3W z8;OTfuXlK2t2d%fP;b|PHt>yLQpY>XfUF5G0|tFqkimZ2MuR(v#jm>O#ZYL5$_VnN zd7RJYS}E^Sl&51(haPpO(5a@KjLWv`JF$(@*_)*y@Z#>w1Cr0PT^g7bk!AnrmiZdu zIB&{XAfC;O6&)W}#vk$DcpadG5IxiID=wo&(It@Ox|&gOpC`kT-u|Lus2Q{n=JC`p z^tjK~)iY+t2Mg5$m0?n3L7P$Syr7}=z1E(_)V0TA+>6M0ZYb?fFsfre4A6uvNFK>W1~W+u>k@YvM+|maR5n&O?~X5L zDnoOZ7rer5JFE1Xjvc0a^jBlp1%7KB8e1w*X%BT1c#>gWn+7ecqDogKK(6+|5g!rw z$X9o7htOT`=CGvX%eLnP{Yx!Q6+C}_%R2xTxo_M#UA1a&fk4q;8!RH1)u;6ho_z9|=FoGWbu!L?wmM?s62wY{xoWzt~)83*_uTwNHZpqtu=0N$MMdI;Pf zMD+h43EceTwSPWH%i%_#vGC=Xhx*JA4Av6v-k#u5>y2vgFNQZ#MW!3%OQ!e_xSxKG z41uZO;*eqnEcJ&vte<(xT(Uae2UKUB^{&UGtu04< zQgmCmoAxZc;HafHW=yw3Eiv;T6q^7BG{^rtj^OedT1i=x(^O1y$6yz2JDlp}d3ZR> z+1t?eO)1uYhVy%v6WBdNs!Et4oyJOv7FGTctv05?4T&QUzt0r53@x1qNHZxK%7f4d zjrSs(=^ZrmJByFuEbXm54>DilBeJLCGxAHVYN{gpsUC-b^&H_W;2e(&*P%5Hu2v=H z(pwc+9vHKoUgzEReX1}hY7!sj|E)_iJB+ZfBLs^m@B<7z+@TE3tg_qUd=uN^_`B( zH``!;1e#8t@Ztb>w?2nI1pZew-_Ko>OrZK$J7=@__gZJh;&z!KHtBegaO!)JfNvFH zDRf@iJ#NT!!3Q7KGscr4Fa+Y$QDZP>EA4+Q94DESEJkSnbaI=peDp*gHC1sfZ9#!9 zzyfx7ODu3sbQeW6Qf+fj-mfTDZJs4LI4DE%anPtoXZ674y>g&EZnZ7r&cH~m@FYDR zp^~Wmhp4EP@9d%|^$3(?g4^jdiiLU6T6)BhV$mm6o&pyw#~=9}hYs9N72aU|o<9d&a}Zq&7j)NI6U9L}qD z{n_!L%s>k7U(^yrX4L2ERoHxn?YvCaKLB^4VwX#y!vidcW%+PR{LOT@7kB1lP~^K7 zavMGyOweSi_co;1RqMY%!_`Jqy0m$PqxI-#anjpDQinFWh##r}fjC}> zOwTH<=k{9bKa?x#B=7lSJ|8?rD%DQ{_>cATOv_I735 zE^x2?f2w5H4ziSfSC|JP&lGfk^us#z=-?nnW)UnqNg3Y`cUt~KQ>j83rg%B$;ND-q z+i5~&6w<86?Lq&jJg;AI-uT|PpWlNAikgful-LwS5O7CVB5UtQv0^kenuXIe<6XV= zA~ieN!~)flu`U|-58&7Bw0^FjMlL* zLke)%B8dkG3>5_#{PR0g{1COUV3z**wtAzv7zwh$P~9AU+9A<@mt;8o-WjsA7$;k7 zUk!Gia~{r@ip7POdRdEMr^zBN>#a}{YZ8f$T7m9GZlIm6-QPete)09euKh;KRXh7! zyX|^VZVOt9BK6??LfLul)E5G>YBk$ab7(c z@`F$LW9yNuPC3(@I+UXtI|D?q{f5av_>F;3fz&d#7+oy15WrZxB$l)pMkXqCA)*zW zhi4r2@ZzBw8trx;mH`CK$(;O?r=RkKB(^D*oWnYBweFEQsWB`!$(v%V$GYVbuAT5I zP8!bvB-pCmE*HHm22E{jxwXH3IGCcBt#33~trFCKl5=OL+P>f&Yob)@5}A|+O&dyD z5>uCXgP5MMxUuG7>}=kHLCZxDuzciG6qJJ^V)jeRMEfW8u-6Eez7NA+x4JyhKwjJe z`mn_N@curMcN3o|I=(+YCy&}fe(*Rwu1+?jfg!2roVu9A+<$ej{`VZu_*E>yYYaBx zPUN!>mB9Vik;?9Pl=ps%p1gfkepkzS>b{H(i$O{e?DwI;h8 zH2O@{hAcnztgpm;`=<%;Slm70!7G$p*#A+4MD>p8i1*sYzkI9tPQMt!|5|K=+x)AR zk7Tc2?~m;BQ+5B7x61yAqs{uY?_9&DWPqg7U{m`kMw*G3=m zd2MS%+Wh{7_WvI!h_2mGDhh`GzLPtca?~91!bhOaBjCoF-TdD_^kBG!lKrC5aXaHn zTKnDeYvj?xThEe<$Bo?K!zKQ=7SsWXs2LtPJoz;+0I1(tLZ`r%hc+(#`t~I)3RJ{+ ziu(Oy^?{FUys$CUKnpIijUoT`wTf@j0gFFt>4cx8q(Mvn>H>|t%vUyhpF;1bX#Hi$ zY+|(Ex^a~zcshUdq4R?G*X($pOWrp?U^LRSqqwHE$9E#~vpzHO<~uY%^9BpSyTBlr z=+z)cS9+w8$&lbZ|A{#)hE>C=ZILCaRdxm(XYdwY2lO`*3dYDQv?RTV_F5x?Ryw?h z|2?j|)egCWn-3DqJtYY{$SbDSFbUM;$bAsW%!J$9uN{4mjlR+Lg0J>Z6A_;zqvNLN zb#>Y5>4BOf%iM{#g?faZ(miJoxL=A}orRG~8mu1{pLndcUT!WE%G8NDyuAo96m_;< zh;|q*zs1q4$`1{88i%2TKe&$Zs6Fbm&H!(9LI$6=haR2@>sHsqqp!B@KM$u14EjdMStE~h=V!soCFLBiMxzfwky>rG+_rhdCNqfYq zT%Kl3GeJo$V~*@5YYxxZ7+n1?MYy1~_%MkD9Y~)tqVDG4s7enRE)OZENso8?jegS~ zE)=?IeD*>S9-*=OB#bH^l~c?`^^kyA*8Td-t-euzMETJaDYW(}dW385xsCkz0+z^8 zoiSH`+oFp%PH<$lGj{^D+=6NLw;?+#iuD(}V&`v~NqRxX%W|#r#H<&u^C71zXR${4 zAM2-;^Pl)7zl#@A!UiYzE=!DYHc1%H z<_bAZ&L;7=SMwNPAPsM3<;^U4bi8Fh?8;T&!_)f~(VnJA^BAj5!1TjQ6am*sm}cGJ zgh9PRW6H0AYE+*38}iGPcCrDw1OcVFE0b3T=xR1wM6$u&Maa( zeirH8ww|f@yD|NF9tp1n#pd*n(92!8uT9h9$5SFO)8GS)9rnc`wnha7w@%q(fJ;_0@Me*Nz=U8Y>B(~ zq|--YL|lqIw)UWEE+&nLHiudr_W!?*63;{vs%%PH+RotR6K}`WmDclhtI*)s9k_0B zT#(ttHZeefV=*!x1D?iUtjXmE%dk^7%_;f-X|Owdvn?~h${X+)gWuU_RXOUCN`KTL z;~sD)Bh_Qfuct6ZROAcHP|2WmSN};vSVFr3z}PF*Ii*+K?gOYFk~plGv7`jwAmI8 z2{g>O+`wE2&E29ed#e*&30!x`KrF6S_IoRaW%=y_?g}QIZ0t%*olDWhiTq}va20?5 zeF|bO`ngSG_h}vThQMPsKrlgGu$P)?ul*}Za#B>IA@@WEs%~j`3hJ*kM-RBDg*XI3TSIgEE&PFt zerL*PgWdHJG`H2f9oOEbxpv4#ez&_7w7F9Z_eVR5jSEoZ03yhqkuspZ*8cGYze5$m z3Z5p}Fm&Qj(px0H+SB5HhN%@^S`(T_cB+96w_s44brx_KJba$%{Y*n41fl)_m;Vy) z5s`=l%{%uDYpG6Tu+-EaBq=%Dz1Q9U*^w{1wJuy9Vq!WoGiQ#1ZY|y9x&M(xcrq=- zb$Vz_LvU#aSIfHMe(7ZowQ&tD2hvP$o0j#kzApBzP6K0v>Rv1D{vWJp8UMrR5{%m_ z;C6<_NaO_t?Jp8(DwN4|mHPTA-QjSy*nB80EK2X2nTjSBUk6YWs_7|57!Jq*&Eib|6deJZ&GaecFuoyg8 z!za_tSfxodrp^0O)?DHOlLW6Y{XA#E>F|KayVq zm*)4L{UwxseIT7YrM0-M^u(?lL17k%Z4ALSqm}6p0TP=3sn+2uz4tw}|Co*v9))Qp zIe!9O%3JTpEL{QAy*nmO3G$YOF54EW1_JGTvKw^TZSHE5awW}q?F&Cj>pYXMVXPIo zbHAYZ13%daVLq~6{h@d=BgSD=@2%E#s@GRf`sH|YD=S?{)(V_GiVY$BOGlXHtH*oa zR_&t8syL*;m3da@9DHVNQZ^cJk`Ec_8C}-9tIvu_phFpee7+-ekBp@PAxGMKBE0>x zH28;dQC7)9&AJ-Yyc&G(xlgIp59}S>SX!&!P47d z`9Nmm`TeF*45@fF9Bl=oh;|_XeRrBnMd)o2CEN`bB@`0m?D3w1DbF|cvW%aKmHt2) zflWb=z71U$MJKQ9rVku+1L0eV3{*zRf2b8-njJ@1?w0MHu_H{E2qAk-!sJtPp#@?< zg{y&di{jM9VwFM~|uQ`3vuN!sPL`-RGva)-5Oa(^aOwEi4W{U}NADVnI zRY;V@a8;Tc>7f#pg&hmLvK}O=#LpWtEEnw^OJ}8^C>7e$9ZGHA)8w6mn0*2SRJsN) z?Z;qO4U2rcx*PreSa-U~()oVTK^U?6NWM{61rB@ImI|T-gEQ53!BY$8(*;uU=U`cj z($oLo&}=-}FaCLByh+G#3DP0B#L#Zv7 z!H}hjl9~V+eD)0?cfZWe?C9H4%5Kf-W7Ly*Yqu;zWO>%(>43s{GYRNeS8w^j8GTRs z+fQkqJ~)>CD(MkCzsgXHaP3wTF*}B+ixgR-jMy%zWyh%U$MDh9Rx6&Y7UBp;feo%U zews^i2KB9ZXJx0}?bp1;zh7$}T$o)3m#ug0Fk+C9r34)Jw}AJtjoXG-}uq&<=$75A<73`|dw8KjMvP4TF&3>9WflVr={IKLB3fq9av?|kq>2j%>P6nX*+*wP z)hm}D6nvboX=O0u{j4<6!Z0E=CYag>bY1b0hY#opzw}@-Y1ap-m*Sy4US{xt@_Ga3 zhN~Hb9s6VRU)Su*RR)*uR*)y-zhN2GlT?WwwTM=oL4P6!6tm2pP ziQK=X6s&RaDj9=69x)=UEYNXmWO~;e=f7;{HzmJBFFKM&S>YDSatb1hKTn%`N`>>h zY72@s*{}~=R77-xul{?WGCCx1G{oTcrK%D~LvceN|62<3F(kaTto4zy`Ens_a=k`N z`8~~EonWdZ?9w4GJDyvx&7@irSjig3bRp0JeA<)<74Wo>)B%Gxzb2K}~ z(!3WN-029_F>ucsbT0tcU=;t2`M7rJt=_j9oxS`KiK^OH!)TtZD9n$mL+_*MHKt`? z_@b1(+iHT(4HJie;|Pf$EaZ5w6oQkB8T!fH74+(&3A9=9Bww0 z)*(X~Pqh;ZH^Fg_&j1!3@nZqfNWN*Q3bJWe?>GAC&{xhR?)V}{;a9wVE zGhFFmK@BTRHStvRNKw># zw9VT)l@XFA(Q6I@aqB7naZDY&r8P}KiMDlEF8V@%j2LCW3WBCC%aUHdp{#@r!_&Vu zA$U8i_U7#{)E{N#?wUUpsAx`eJIVxpNJcIQxO0Z5lxF<&q>ZS)(XB4T&c0I=W2B1( zTs~KpEf7U%SLZkufQZ9toNw&penf^xq&6s${93btt}uy%|N3dv#v%I)SueMIOmRK5 z1akK_gru|-r8I;GTmS`|LA%Q6bON^D1XO$eeV(8Df;0TI{3iOhaMw@njnUr8H*iCb z1C%CR1CjcG>81O8K*?FRquzdBQ`eeDujMY^L-Ys&m% zZwv3q%$>Hu<99KzhOYTGQGaOCvhoNna=jy+b)j<&oKB&g@RH&K2A}1vU)Z0k_+e8z*{fVyPA@YW7lkwJZDh4}?^M%xSK z`*M9XOYB)q5+rUZ+L$PHr+0RJhVN4BU9As@2YdQ#saQI<6Ug5LaPsE8s7n4?m$iJh zOb(~Dr-grlBL47$XcWF-usn8{W#G+aW#&-iGR%63R*&Qu*bUJ;}$S6xy{aQ?%xuPRRj4 z+Lw#h=U8uf2aS`mKx`J1d7kTHCes?6ldWPpu}%=&3y4pZX=7i2*QB>5MnFA!A>Fe1 z!N1}%ZdH}fU6@shk{G;A$#2ynUVkW}1_o*$<$z4%kSg*++i>39+&_O6N`Wqv>py;? z!*%p8!0Io8O7g6V=tQP9APp26bi0^P`&Y4Xy4qA z|NXOzpwvG2XX1=YYr385llFHsNml-X%PwtGhBV{DyK&s0;p_LY$jg4PRcd>HJjb!B ztKy;x9rK*Uxkk->$HI#hO3^TP4=3=-zc@lrF%z-`}Fd`lmTIrFWy)k_Q0cR*ZhuC135X#B5s-hEDiXU;CXo(X5XBVW}$sxa{BQ$a(v z9}$?!lWDa(%?!!q_1klO2vrPr6*bH$D<2oX;lXi{MQM^*j)kkoDs^!4iFvK|a*+KY z+XAU1B}2e37Ay;v!2dY&H_I&hDgG5Lt@WpouwDY*1{+E32_SFq318knMJEMAa1LEtkivo$>wT<80nMECD}B zFFNqMQKvDeg=vk3`@I;a#cHup__c0Hg7|InO#*E4_m?5dz($EQOGMR|d7HCIK#R;b zix+RKLa$MqTm5Rvd=-!2eD-ae9qMGqjJEqO<`pgGkjrCzN2EV$PpAKH}WCgpJD^_{1-#vV>; zL3&3(&ip1;_r;O^(Pd|ZdT(E5-4#Vf;8Z{3PCO@res=>iQp75OkOakvmaM6CbJsk1 zvp^)gm+z|DNfAA74k3YQ!=Z3$-|vsZXeRfG%} zFLS>Vggz+~CTu*gs-j*{BQ$zd%fkuNvG6>Pb8OXXxdVwFC5Sjg#)8 zlY&xXrHnO!;*h3g!XTVM%QOySA<+QR4seedNMCI#KB2Uvi|MZpf~AtTfo3 z=ave=Sf$aaGE+Hhvix_`$bwH-SlKY#NXjB;SyF8A{vA0P306&3GeT?n4;J@~a0D)$ zgS-5^A_>AyZw|fR)Hma5^D5yq%bXz8YBaBgjVRd!kX|P;fYHlrGpV{;jU+x`Ev-Mj zxXVO0Kkg;js#jh1J<7atm%i28--B9ox9iqSm9_gnGnU;xv79fW{mt4^iNdp8XnRW% z40#Uvyx0|Zm^pmft@KUUzlr-=_CU`b%HmtmvmX(>yxz07fE%yA_K~>1Xt2|*i+&9< zUBcb+0GsHoJw7W4HaEZtvaQIspx=rvJbAT6? zdHCA^i%hD@@;6*!DK>Si-9<{&fO07q{5uP-qJFOk*5Z6h;B%?{)m^jjK{>xqQQDMw z7dbc&3Ktu@cR3|xqM74Xl6j(8yntzILCXQw6fV{%!L70})%M;_d>JKThMspU*3Z4Q znTQIcC2ruDd>P!2g6$$O_XsUO&>h8oMvy2YCV;8mkosk{CH! zm~ITZ3Eh&!&rP(=NKWNI^t>fXrzk7g?=*;QLZ47Z?zY#ghXfl>5{i*6);HJ#xt0ye zo5TO`0Iz#Q3VrcsX&sQg?sm3XrRpard6Js)!@r!=niunTn9?>w{>zUHOMJcJj^;VV z7!MuGC5|CUB}Gk7$`bCcqe`Fb{lxsU7N4<@TOF z?5;j-JyGxb?1<*ToNR8eH?w%ws6~iL{{CtNYeGJWvs<5ukcKqZXwN1gKXC{&M@kix ztccK}&afqv5+Onlmet7@izxW-f?nvs27X66yw)TsP2gZ84Pa7&>}8ISi93`qp6qVY zKEL~k#_(7vPmCHR(B=3B7XSDLw7U9`iatwimg=3$q-UDj2&mEO$!nG7zQ-%LOtbuvn_M9&=0%%HC5(lwpa@WbBr`r@>XbDa3YKnur-(5!`%*6 z{E$@okmH`J>Pu zW%&inGQYZX?_Ujcbz+Um$yLRg!(Zr-SSu=H$j-x59Chn((a|?&dwtWQ{Wae>HCe1m zKE!X!97T(HHjM(qHACB6;`q1ZQ2pZ({{#$VXulQcB@ev9;{i-YU0oCGj86ANspV?V zvOEa%av5#1+bH5A<3@-e9sZYeHT%A)o!jrqh!tZdyNl2>UnaL}BI^}C-0nVp zpg*p?y?cLzDPO%6(}Y3=LTQ*H^(HCunbFamZ$CGwH4^gR#uBa-fpsIIiK z7t@gcw5I&+OI;8&_r9n*F;9nEwW68C`w9QA8(_ucTk-JiXTJyzn#WQ8X1`T`Vm8}S zpB7hQ(&Rz4C$C&-q-K_$TUJL~)Du$^jra#fiEkk-g z4g9quDrgx-jg^8|lA~@^nBTiFeaf`cJZEFu4z^80Eze|uCT)+EB)3r2lQi*{fOd)k zeYGm&6Ha3F1~(dZN|+U(Oy2STbgeA?rr8m>Eel&iFsMkP)4o^;D=AGHt(_@#RNKqW z5iKvbeX^u8Sg_2Hz=#PA3>>8|)M@iXXns?iYq3JxO^;KuU+&#Y5tv@+(;L6-INJ8oyR9nT4FU!!Aw4r<=8&Q3WCF zGk2WNpZwz^@>&Kva!#ojx0^=WD>~Sf9TQl{Iar!0_f?B_EUbo&We|RC=GzGH5Zqo}9pS z@5^g4>|!L@dNEFG?}04qrGqPECCl&g*Y_&tABtw!NbIqMAIvt{@yzDtfsy05lf7`i z7KFrSVO*#2l4ed)>%+4V0aCBj=Rr*EK(;%t+?*e)ARY<^%iK)C(%j|eth4E1w}zQz z+mea6s+3$HRRMRU9V&CEkX7^is%$WLiKBrHypO+ z1e@*<5V9FtZ{&A?P7WTV9>k0gQXT|ZIMX<&u9$I zYeq<_T7Av93GOi2K!n!WYWq7j)eo9!bvC^vP7o|V^*0t{*DCYV%(4DO_IgvT19Ns+ zh%l?PgZ$b&sR=MOQ&~Ca`*)%e{A&)gA881vucL?BFWrVvh}breI+6+6?S}UiSCAn> zV{UExgU@4&AA)~~Uy)*|mxi>Mse?xnLuUuc&0OKsS}RPEH#y6R$96WQx=O}H+z76+ zfdFps-*9Yw?V0xJr98qYHm#k=?bt5d%iB~EE%_htO~JVx8G~(3$HRNO(!Rk04p|#6 z?Mex%ra6mMI`!$m4H6j7cSt+|$V_U|NbHNi;2?j4y~A(pkf4;zA1@3r<06gJumLvIXyCjk`@LE0&VgGh{@@Z|2ZKFgo$)Q~jpZF{6 zt1fV~yM~@H0L2E~SSlF^oN%+jwh%CvtK93Q5wVpLeZWZt>(8(A9>F=MbMElbFBiL}Q(+cTW`JrJHv29p=u?(D|S-tng#@VdnA#?7Ap zA5)^&+jw|GGK}GN;**T{XSSVu6OfArhH3Wei>>8S@;eKoDA7<26L#=^p4)v}p8j)t zb4;UOiCh{Ao&K;NYV6v`Ry{9!VHwATQ*Xc`rCB|vCVV0U1U)Yn80W~_U)E}$qCl?S z>z&--6K#$ieBD0aTgaObQ68M%dqH-6NsYoidKS<*#XTArWza3Y+7>6-FDQ3A#66lY z;g+7u{o|h`GNK{U!_$pd@ zy}ShG1qjxyiUM%mcSe#O;Fp+^S8JEzej(Mgr0{#N2z3>#gzB^U60|~W;zP_ot<*y_ z^r?2QK=zQ-jhgIC4DMR56RrkjVk3sMalgL_Rqs47A%qoHx zXusa~Rf{ZLN)n>M&>sKxkSD{APt;9;2b#CW%6CKkeW!XY82E*rd~TB@9P|TMyj(}+ zj9M`p!qAHfJsm8-SY2&;M>j|YE+BbNZr`_(TQDucR;zXWQtW1WnNZHI<+vOi1?}~CN=@>Ad^q( zpwc_|M%ZsfnQ8emB`264ihjUl%4IKdB6H=LqUn$ zJp7+UtqqaJw#%7p5&iB-RD1{Ecp~UF}uu(o|B{p4)HR{h*fj2PAecZ2EH(D-(4 z$^IK!>{H%GSgPGCOP6Q(mLDtpfGRTYT(l)hGHlU#o@Wkar_d;h8SQw#fug>Oo;Yi` zUc z9tm-w$^kpN{SWV9D|H060V7l5R9_T*UM3-LGhLl-RQf6f$YhQYWzlKgSfbhW^>1Iix^a;u<7qZ0BJl45~8hzy64)9 z0Wp{dB(4LcO5n3{7TNg=3DoJZl`O30y6CU0@5w?C8WPdpry+A6d^N)!LEE6kQ12@% z=;68={IB@_z8wc^94L!^Yv(P|7+Oi57~0y^@J6jA{z_#Zopkj4fX5yw{K68v6U;>7 zwZSeLRO|ELGCPZH!}tV+R6mt|FKS4In#+a5eSzpc&N!x^ zM?5Mp5XbV-DeZ1~$naw4Aao2!`#=OJtfiNzhDM(<!VM-kz2EN1#qMyxgKTB;(1hLx#2Im+fC1*HO{3cXGeWl%tNKK| zjFXR=^Gq;=ev@}FEXfB}bhc-BA7#<_C@Gw32}>)xnUm7#6&Uj+ITIHbmoB$s%}@B` zm$kNOw~O&;t;j%mwmVcad-Adu^Zb(2c>Y+E1xexrw``3gWN9Qu~ z#NKlpcAWwY1ia-D7AZ&xn`89LT2JSYAA$5UptL{(yVE7$@ncGok&lMo25Qjc;(fgK ze#YA73xb<0V{Szi>(RrMM$3L*K9l2Vzrw5>C)s~X1Z3#E&@&}Zz9_9g{40}4R{f_a zRgN%_l5weQ1u=?ADy;~UVUEs{c41F-$ox*6e}bP98YyDAzyfjfOlk)O_ThSI?*e{D zj!{1G;J>vFdq&LGTncB+Xw@gL+w4IUW`gk>fBcQYD)c+A;Yt1Mt)srD;+*<^_0{LZ zt0AX_x_{rZ1r6S@Uq;{Ssj9&6Me*tqHz@R{8fz;_TEeW z%T57NNzV>k@&8m6OG08cK4mq_U#W6W7%lA?KZqN3;x3bWp&CyL7>(R?`TNC2)!vW#Pd* zBOYPF+UW^6{Ftn2lm=-biK4y;ubAw%+fDSrS+ekrsfHHbBGv7^p4yDoH?0gE-ht>Q zmPfL@DG3*Kusf-;+rLKHjWaG~rKALcsO^Ti^bN4bKdZ#D!jqN4`YaE^Wg4U~N;Hb3 z05R4)O9kqtq6IVb>WeigbcpLkmV5*yS;7OySE?Z81*jG4`iO2B2&~=@+mIz84+5`2 zd$ZHKa+`F4ql$TT0>S@_kugn;>86FOGz9g1I8u+}QV#59r*pP1DreUF`B-?%$&~mv z>4}INeRN6z|cnWV)# zSP3-iPB+94|IhPVk}ABEAkq2^=)7|y=mI~FJWibBHUVu3&2?#tswTtX<~pxWDW@&o z(3F;%6cZv~>t^SoNNY+!rxy4OZ+`%(>--8X^%`S)W!hmn?0O5ZsT@oo<6FH<;L1?- zToLIo6#wBypYqy+J#C_y6+nhWJs9CUfa|!Wz#2Gzqe$>kfh}W+q+9d}X6f#z-ShxM zbC|t#HlNJn{EVl>EFev;r3d48|J9H$(>T`1Vg0u-@_7K-&H>U95JhtXR8Z3Im-auxCzpAtP=}s=w=epe-+`3P zO6#IP0c+2NMPvBXzzF*S?@ce^3=n=BYdGK3@(MMmrrao|Dx}Ms20-HkXCnP*!Q|jTZMZzu1q&^LXwT>Z!EhevXw&t8bvwuJ7tUw|;@% zw)aieyz*M?3Bc~YvJkS}r+Q@R*mNk)B=Np~$R`y}(gb?3;wU>BxOL(&QO}$b8FmJ` zD6;6^xKS*qM1+EZLP?w^Ww5E%iEZmk8nC1Fi9y$!P#?&nXKxd2@H;6FUq0?u@Tu9R zifphJnZBmw9^vN-Vf4Z|atJH6`6S#ef7t$>p)Yd!+X-QJwqJ$q*H*q#dB%^N%VeSBKfM(>{%9X6N+Jpd>AE9iSH)VF=-`Uy$`E|SET=0HEXNm%P=aP94Ir2lOUUhERqj)GW z=9Lt9v-`KUbqg4s6N-v?78=9&!i$u~R;WJ~({xSD1|DjxPe@y-zd5XrHCDSl*wutH z=sP>#I|j$h5-52!7-?Q6N8OMAU#G>FRqY_aie3V1v}gbJO&H}9@e2(xoTC2oL+i#h zw>p$k*UY9P3>1&i77sjFPi$(|iN7@rLmfCQp44U?4GEq@!~2DYtX-H`q7NJU|JW zG^`D-aEG<|G+>|dDhC+|7xr5vU*vE?wqpx7tnoa4D~Nu#^4<%jCMPc`S>AkRz6;9} zt{h|%4BLa4c>!x&B=rQFN~hW)z!fRvp-ol-yVKk(<(UFJf#0QhOq+ud-G^18?3N(o zBWp6m>Y}rrrSf$tuU(%;_OlmhQ_3{m(ZE$Mqo~^Ci19X$9Pi_}@6M=Y=zAwD9{w?* zB`bi?L}OSW=_+^$(@C*|vVQMQEw?2s-uXKJC);A@w0ho>2$qBLc#iV=e!LRRSMI8bd(rbhvV>g z#J+O2S6Xs>eR;5USdLi{vQcTixM9|?Jg>e8=g{YAj{TPsuj+(D4r3}sCSg{d`L@{H zC%V(;RoL9s+Yq<42jc8Rc$odD4JKVCkS^gIqSDYaZIM}cBrHgpxA84}Gt!$~I?OLo z!2~v}minKP8rOQ!njTWH1SSOPpq+bY8B1$HqT$u(NmV-KfWd zD!?CAeE?c~CH{?3gSJ~rI=E1=ZdWJ22aSY<-S@ z75R?D9tX2qOFIg>nA~m|F=?|qf_3k?{WSH41UJaJ(hQw{uR{mR>kJI=4DKVbc|MA zaC&1L!rp_zhcBtEh}I^x9*pxj$@;e(lCn)n>O9p?$VyBCC!q_8I$8iAb{%+fLLQ*B82uLVCZ%*oMmO+P1aPrclX zpsRZxQX%z)-(4RjI--8YH?j*h5Ru(Tr052#{fFD{1*JxuzB>ux5BVH)@jc?_kI5Hi zAIAHBM0`=901hcx71A_RS3_mZ{P<*-x7MH<@d)8IHbFQ8$K#W03slwuYkFS3V%zITmvNk_&eMfE! zvmA0-rHZu2utzVrqyA1Kh@I^t{|+3DF}+i)3c1KqE^XojJ8B1?Ds_x7+tr^XUPD$21C5 zNdhkanb|$CAafZKsi)kT-H=s#POblyhfjqX7C~KaMc=dff|JfKU6i_<#Ofe%x78FvJRfH3T5p{geH~gZ=0n4|n~~^c6WEEFVTh zdG~v1F{ZVxP#hZk$c6*X`E*5z)#E_tLZRA+ixgiU5u~lv1Rv#Q00* zO#A3Xg4&u*onh!1o6O`Bw8-omzsM|fvAm%aqCZzXN>nPa{_<-NBv)l*Jsu@x6YryG z@E>-|drl5?!prKslQc;AWK-#;qbAT-&b_rocQwLw*L?OKm)(7LIRYW?r=`R zQL$c6J>7*JQ$7!-ul7#1XMjquV8J$SJmg$TjH}x*yz8F7&GgJ3{_EF&lcn-JHZoMM zmbh>Nwp~{tioLJ#V5-ZfT(zl)8Opw9A(sd?%5yDLqW0frC-?YV5K&idZ&N83$v>1M_H~cV5Ed-1<{B^Y&OZo z`sc33$>oSHh)=E99 z9g1qS(w#3kasFBU)1Ulf7OpJ-7NdqObFl5YTwX#6q~Z?uh*tG}>$y}*(=XR01AS2U zy)Nq=1AXJ}*o?!@iED+{U;U$v|(G54HQfi(sI;&+C#&-v$G{5!R(f z`Ou+X<(92)H>#SF%{679*8u3R%pcW{KlYD&C}xO!bDm~SrN%)O4xr~c*>`i94nxR6 z1L)}jXxbcYKw9Yi0&9xCpH2XVego)34a|%XfS&4a8-(#_nnU}3oOj;vQ*P_=GA`B! zhLYcxRBM{{4mSBVs{+^Bz0>A=N?nzPCP0LutmqQ2Egt3N^N(<1{z(>BPJl)rNp>++ zi%N`|gg8liE~xYCD%osJnIyv&?hgfs9su;=+dot=A^@jnwzg9^$&0L)-HK`la9(734OJqEwIpo1q-&8 zxhmDPKZII?3;Qc5F@ojf`Iq^`7k-Hgm(O7V;TjWjFba-w?>zZ4nd1=K_yM#7ps#g9ZCRA+P9=M5j`j$I$1hPCCB zP$j|S8dnb1(nQzRf#W$w*(wcmGTFkj%P;Zrg@<@;@lj4(d>W11!K1N}FQIhB;EqwA zzT2VmwFTd;((|DSTZY7cS+4`R#(W=jK)uWLJ1QFKjrx*|g0%`PP2ivg3)Yu)nEDJ? zL7Fz#aur=h5|qTnC|D!|)zdP>3Zk)V_NM|8p(I>Cdx(8Ahq!6`yV}FJfXg@ql^c@(#Ra zu~f1gYg7UBgi&H3m?nAUI)_f0$D4xy&{cKl@~K`m53S}Q!vUbLhkahe`Y{FdA7}-j zH^Qn@De{lp{qK0wj=PK(2|&EOfhsDh%7YI|bmWL8_ai3x;&&$^g3;!SuMvQ*Vpj!g z-2muX*F8s(^t6dXxAQ*lkP4kD zo;j+(E ztTIvB%AV;vxncI(*gtca9g{bM)PY`2a++Ylf(2_HNFtr~}}8t(!D^!k2K{RYsB7~}N<=wcJ(eP51#Iqec5s%Z5~c?lP%=W@Sb z>=)mcpaw(Qe9pEA9IF62bwE(J0D4uYc>Ojk0J>7$klOl68vsDh;dhW$kjFc)yVMT}6WE=`%^hGtqr=S`xUH2pT0|gssu_+yQW_$P zR-SF{g4qNm_P+Z3(kr~W@C}|l_vgH^^gPBGG_g(OC?eD+){YILHHp25cwKmtRkSo} zn%L?39ZbRaV4FQ|dH}t}_ZxH*scx9%8v%d{4E12adbw+_PEu?0iE34uO|rsT2e_+2 zs5otOYe6E!B-O)nzjo6-+%eaU+q)?-xn&zSPJcUl=ibV`nR}QW+n1AkF@b>|ELgBM zVg%opK@(R2LtIQ$4Jkpgf zZj@uLGyYERGXUrTK<_5LZvXM6dwjjzWB|~6381f)o?3DEvA*~7(Jw2ipa|j`CHt1| zH8&^7eP8&1bqgeED~ap{fbJNjsiaE(0Q5Y-v^o&(7Ut;;K$oc5#MyWNdPAMsAq3Dn zy(a^L2QB=Cu6duG|QW}-pf7vkFay% z;1Fh*F8QsXoH+kgp1Jf8rxu=K{@Mw*mJ;_IOzJ1|j5krothcs?nVsN%p1v;MW-P56 zGL2?q2Mlz(Mkv?g5T@sTtRsIA0Q8>t@qPusJ+DOT_5544F56?M2PS&3V4cN^t&-?e zbfUh)jpq8Q z4;C!gn$feg{Qc_a{^Z4%`1harS*~4Mq=GUrYuL6=Fp`jI7MuQRpce(8=kIYe0Q9vg z+(#Qgua^!{D?=5R_ZmP?S75e|T6|u=0Q5$LyGAzZ0J>4Q?|ok|qW6pP%O2Fkrp-h} zRlPc{M2IH8_SK)T7q6Tpl7w~`=;{MMl{_3n%bRQe6-@NY001BWNklCOP8ro}ppVav@YckhM26}#) z57VYy!$5EJEV=~+4My|xdZTNkce%bt ziRHaE({zJB#&z#~t^9jvau}@p_^Igmr7|k((xMuHNgUIi7`Q^DW9-^`m|N%WW$)bG z?3lRCv(tgBx;X8QVj9FIjbj{D5MSNqWM}G|mD<%!&VvODHp2e3wucl7DivON;~9SI zv;TmKREUV#cAa5-HUTRn#uC{+_|abEpE=NO7;^E(*nsKrQ?HX7TSxp?DEqwN2a$fi zRu!0y@U31P0=couV}1VTiFP*DJ;*1U9ip^)PdAS&!ym15HRhTfOwh1jXDfJl+xTvN z_MM-KKvRaeI0+Z0+7g49Bd%3M1(4avJP%Ra83J;V*Ohq+K zUAMiaiJ=d$GH|;E12?ZtDizemh%bjMSC)Bg;V7>!KE|t;j&kA3E0BOOF%?!2Q=-uU zZni#E0>f-*owe7#6&uE41Ma_RrP`Y8(_q1Z1?zQuBZ^h=4M-~(#z;swd+8{r=Z^xG z*vxVBj(2nY%v~JV_FhU7BL>$5TgbYSSHW)|*J$;3;9rZWR=CywKL5J`iMj;~7OW^U zbyz{V-eYdscLzW69ek!`vbEPQICKQwN!hrr1AoI9qRNgHLH|`C85{9^)#Pd1L-*&ab?|`Q>kM zZsApbLR$7fyLFN*lrbjuO+K3gbQ*%wA)k0A0O%Vwz^#>g3)VUT4;C!UucG~sxFOJV zED}@=A`uDKP#wpJ!PC)t=4fq2N0@_ z1#1}QLZ*@IquAUt32Q%Cu+jH1E)UOFn+SlGxcAN<=ft@e`Nm6sik2(PpBZEOzA_>) zHf@AFO5fEEA9~zR7W^<)UNx3B>CQxlzshX0r}s`NV6YDuxj5f5ZneB(~oHGKzL;u2!x zmgDf+!c(NWrPf{pV&B)nfKeD>9Dy_hCjRYl?>CAvMNHB<$F+@2aJuuIM1=}bwPc@!Nb(M3~C<;IJ&Y$7r z`4@TP(lM?rBwU+=u}LsQSVjay&!jIlZ}46ZD0hSZ6a`MZ0}a^(<1QoN3;Jhf0H3+q zwodKJe}Aq2w|ECBX;;zDzw+D3!^eLQ65CcHxEA1-0zuvRwfqOHAGC`)oS;P~!PQ28 zw(6YPnn$ggS{66KnrVM`0xA@3MKhi|0=IJdNvjz8h1&3f(e=Z>N}GPjHPF+0mQ2^q z)@a>ltbd#M=Pigj-=|`H(=R8(Vr_*ecFI?(2}snsr-3A(QC(v&8t8;N$t-uAVzQ0Z z^f{p07-_He%#U|G_vo$(=g3=^>w6Tz=#2!R*Ea#{YCjDYY{)@_cIW-@SSq`sb#X=Y z=hen(G;aO(Ow=OAfGWEtZ|CN1_j2=&?_&GJ&7OT;tp>G@Yn9?>S&x`hADxDp z>cN5q+ZbkZ`Hah0PF^_9um0B$W7IM>ZP;8SI@qjZhNbv%P2&HAQPLPSA!4Kz?&5 z20+-n^SalvHF@^A`?otvQ{M?Fab5 z!yn-@NB9RtgFQdG7vdHrJ1QZ+#Ipt=HL#5G>l7)%97sMyFiVNU$oj9dCg zOAL}~h&l$m`<@oTVr7hkO4giIa+`=H!4UbNH6Tr+>|e5~hoFl-TKn%Bb7!rl-u>Q3 z2fZJxdwfHR7Hp97Z{*i!O1?}d`y;BLc6M@aF2Qf@{2GH^rl-MaudX|c zPIPsW4jWCUYcn@PtZp_zGqRTfV1AxS+A%pB`oxtbYt|meKN}0W0#>t)LmihKgS3|C z$b6^;Dz4!d7AuAX#bQuRF#fYMy-|PNSfj8;3iU5CMC*N%Ka&lxi=|CO>zj$DhjwAj zwvh~@YyJrq=0}(xYx|%yuE-AAt*fjvrVJiD&-*CF$SN z@yCqOI>X+q;Y62TJO${T@JxbbeE@pmhj6tPdgBw2qEA|~Yo#YS>Cf8$;i`<;S`biG zGYzF)X`vVS+V#9Y)xcV%z1`=)p?4crUNpnx)=w(vF<4-|`DwrFwi?wxk9Ag_^tu!; zT{kIEUIo!z8FH-}x%dTIO#+=KwCi9@R-dh8%|dJg{qw5dO#fXw_QrSRwmqMh8>_MC z>Hwg2>;@<6;_hXGYos^1|F&oE2uAez`DLwb`nz^@6$jlEcaZyIw@*JLgacmw-Aeon z2!$PngU)t+g3ATc_`o69ilEjYMpuntz84D^#MmqybUlYO3(CY_FMkc!%Ly)Ad65fC zFYx%82bmh*&GoZ~xn?zGOn> zYOr7fj%_lcq)J9f-@olv-gD>Q;-P0AR-NRWYFk6lOvWkIK6m5-FK4F~F_K+3 z|K7yY2-0{%BqE{AxQ!4#7^yyx0CJyao~Oa7s45>sq6g3fVE>~s`FG~uzXa~#nl=F5 z{9x5fxR`?I`NlZ5X8yC*Bj>UffL`xR`VpW>yW?Yo+U9t*Wk-Vo(EGk|ciWV%(_w1a z#I@UR)@T#7-3JPNvS?NPR%Z^~n(BA$+g#F7F5R|$U8ftdE@IFCdPn|WH+N->OnLsWwt~!TsQr8Zr=88 z4s5@dv3RC22&14cxpy%=zR$%lV$fXdE30B*56soY$FeHX409-0uz}_yzORpB5OwlUe|q#| z{MqBb!_=f<_9hUUAR3bxOKeM6DR=j&?E>g6^3i0ZWWf3dpkq59mm>FiRqbi+GiU%E z(_DAIUGUs(fx`!&cY5wo51?z86Uj;r8}@TZjILt*n_NYIm3i)@>#SIeTJC%Am(rQy z2f7NNi=;oi--RI$J}8lb8aqxmAf62PA@(|P9c&^fn=)!Fs*I-7wb{(U27TL*+@}G^ zV1uY&gY+le--ucLCs?os^E9um9`>L1eS9~6!uW;p>(?4@wAP2+(!MZAgrVf+@GKTZ z#Sp3G)$m*at`=wDX>0eW)vfvo6VeSMAHdx=4 z_Ej*R3=*NLy!);rOvh7PU5-dr6nt}G$EYrcSU2R-t@EN@?pr!oa>ZJE`!MbM{k|9N zd(aq@AQG0dV2>&)ii#7`_Q<+m{=?CKn4Gxuq`OF)K>Dgv zhk_aC)yrzz+M$gs>M-gW76!VAv~W^dxI-99DVKt&${meaUCCK74D_ZeY9uyJQ!j@y zLJahdpMS%rRK0*1Kt?A$jsiA%h&xO!Zq=jAbwA&<)@{Yn5Fd&+)IMx-|LwdsSn&yf z+2kl)w+F;wo_6O88qME^CEo)Y=d9FUs;t3G>)a@6C1Za%wrY$KMXa)Q`Zn&`{XOiN zeFwWH4*F~P2B+EV%dNm1<2;bhg9RHF=apcIFj4_xw_~XCg~va}XO4c7t$Qt#+Y-+0Ky#TPhL|Cm=9}H;nn_0P_d{@Ff!(w zVkcf=(&dn8E-4Z@?N@6N6LDerMIL(j1;i+`<9oPi+k3ce*AK9J;-E*!>!PVz3cBAc z!yXwd*x-^LMNLH7@y;D=Srp}+cOK!PZ~XyF=jNH*<_^}dmc$Hx)s7B!RQyO43U=DuIR`EZAT-h*fJ-y(apWv1#7@mcP#Dj{ZL7s|jOUs;t?xa<-L!3wh!2 z>_t7=Ksv5k--t0*Q$6${!8qF?{8*u5yf~{?6QOH8|3xuncVO=i<;4o) z@l^WV{kO_D5q1AIAm%=Y!P_H8G!ay{&mM54HXTglW)4j{bd1B~NcuT(+dWa*%mDdN zB(rOj*96VaU^Y8JhIjZOq=Xu7YnJ0c1k0{!O!Mw8I!HB7jxv zgJJBNx`)+=AWdK%@rzFjbz;NBKoXJM`A`2BT-Ld{!F&*r9yz~SDVKWfra_yP+Ep%? z&iWpB|I+MrS(Qq`?5eC(x*jY@*I=7nx(6-UVr>e-^=3WTCbMxP0vC<_x(>w0RK2>b zStpw>;_vc%4>%(2G`{HMb$O7j)%(^0@Vi?d8#`f?O@64hnK1D6%<*>DRWmK~RS9&b zYfA@V+}Iv& z-}S@Xw(I+t9p6)(paM!c$Hq6*6^k~BYz$EGua}yNYsFn4KzzZ1wY5=JvjeGS3v)9& zdFz|r&2wkJg6>Gb8Z@#PHRxLMq-tF5*QI-_$&uAmg{dlT=cGPG+kirbPD zE^7SpIH8s7!4!h66`;saxc<3PvoY0N->w#SX&5*7Dh?MvbpQQoG{~FWTPr-Y5&OIy ze5&^IyI)7?XR4~3Nn&fRT|b^yyf0EA!d4xFC?#urAdtN~o2>Vt_C6qn`vvY(FN|(M z%y?AM&M1l7_0 zE~qft!HS6^*3x3{B<>3PU$D)vUe>!#LbXV7RxvRpQ8a=1m9u>9#DC`NCw`kf6Swo` z-QUN-?eAi`v=^b2V(t!p7eLD(r3Mc(0TK6FjWfL7)eokGrqHG|Sg`KRaL?`E!;`N) z1W620g4)P|=Z)k$2I%9%9;g97>h&1Q>iA)^&&xqpfV;!16xA>uM70pXuGs@E-yfs* zSp@Nby@J}w(xX_Dc;G!6N~bK+{~)!OEG&1br0v~94O3k%8JM)gw+{d6qM;KF7nyKgzD@1KhU#M>x3sT~%f}0Hv&{A=ZGg zK7WdQwP9`_#b6Ok_DsUQ8Z1~5cIOT6Vrp!TYfFoa&k05?h6pQ3fAU<<)(ts*aeG&9 zK0m~h!;(Mre?FM5KX*OrNL3*+kdzTAfh6uaR+x$>+qr8o>S*d2+BG5DQ7zRrsA(h6 z85LXiKo8t}P-mN;*lZeIKs5u=P!nxFAA%<1>Sr`^w>;v{P_;iS^z_e$ow_*|;ZQVj zk>Fo~^=_W8rL*2_{s%@^u#GVP+x>YvYb990@irC2Sdhr+rmKQgga}o|h;nZ66~1!( z-|@ST|4lyg{4eqBna^<5F61h5txuf{B9S91dB%AfqdF40KQy;&@aQI3uzsyy*C*kQ zH{Hv%tAc8R6H7=!4mu?HZAPpZ&~~P(o8mdGmA{D&{y=>YpWjR}rejQ^gIlka@Q$zv zL?$Ott}!aYWHi_MTA((SdVBCei2(M_9xx*1j=xa9K=dL4*LDZI{{Q&~shu&qn%QjeZw=Nw8q+pZl%W*GBXsef?e< zJEDvxUei>-2-d3^yL(W?l(8D|;`}50=_~(=-}%N5^C!>$Q;yAlnS?ma`kY8hrgiQm zB+`9U9jKSK3=6(5Sg>x*g23H3eitjt>Ignk39;*du#QIleUJ^mgO44Kv8azJo zR)|%*qYQ&=MZ3*ygKhFz+r>N$@+`rEtv|U9Yo|azW;A0ciU`;QNE~wv;}apLWv+m+ zil{5YK6dF5j$M3+l9}L^?eF8xT|daa*}JkvZlXA}+;8!&;yP_QCgC{-3syuf0^D}v zcM?A`O(e@$O|Zr`%;hrdsKEGk^Z>!)4i{R%cLUEOHpxLsQ>?B--T7V+G&jp3Mk9YC zQgQ`pkpLB38&EKn7*!%%m+`e%s<`Sk0j;Dg^c!{_9>U3O2KwN88Bk?%0n+|G1Q2Dj zIo{FQY|cd~>5H7pCQ0K-Ps)ZR*a(s?!PZPl{GV0~(q?SZW(-pi$^HZj zwjsRx8gqKBwb9m^%B{s<>xlPMoP3M87N2S&)Cfv~vEsGOl>~A7p+s0QCmTR9uO?pPzUJin5XT^)`c|?Y$LCaX6vA% zc^~rP?y^2n)IubJigo(r;`Uz^%}Sm{9O#``#TW;yi>V+OtaV^rEO2?{HIBaVX`VRs z0NW;S;jKHqpM!gTfGyDsU=cM`t3AV^N3dYs`&id8>6U$mdHwaTc%PUcMgnjcEaa0z zxCSy^{DH*y{Zw0Ud%P8c!BEizF-p|tBZ+;HT-N6t#Gq>Tyl^weFFjTBHLTZ+>qtgH zjn}TLUdhUOq!`@$)AxxLj2MzGn^7bvM%zmg^T0{B@-dCZDZ8AA>m1+KVoN8wsqJKJ z{NJ|uM3goZdYXO5b{AH!B?r<*q9Rf=$NB|w_4+eJ>jU|!b1_)ACP!d%5Ys?%0S0o!mp+OVlWZk}YWda&zDn#{BMb+K6) zkX?RnZ4t<|^8fi|*EdE{mp-u*P_cfW?Q}nE#&T~zxv&jAPXPCaJxR1}?HH}*LO1iR zRg7539FqRa?sejGTCT4F?;B}+Z#9>!Ln;Bf6Uiz9(3)+N{9v=KY8G!i!+%Jcd-4 z1Fl=1(;uGI`1aH<$f%7|u{mQiAT;~`IxwuF;#e)9A)!@bT#+$u)A_N+7+Gm=ebry9Muyv_{Ycbbs`Gd$FB@KuEu|80&3ua$i?JoF zNiZmu7_k-h&Kzj#v*WbqxIfcZe-rYhmw%T89~2C8P`>G76=^H6?WH3OHb8J_1fOBG zdcua%=3aY;K3B^G-qtYr2HP6k3F|Ldfq{3oiCwozXRy%(3)VZ=x|v7ekR_NAK~*?* z@h^Dk;#Zi6X1HVbk8tbG?_=lWO}Sjjco0NtFqtgrmk6Icd-7B8OTmIUAMJV;?B4b! zN@fOQ7d_~$bd6R1|Lnba&?VP(AND)v_WQoU>@%1d%wS)*S|Uh_iYzNpqFAzGNu|;X zh_)q1mdbIJDwbr&siab>08lBkMaNZ2#qlDG3rJgv0MW9Qk_3R-ELi|W5*rB;TmTZp z0GPqd5WsA}eBbLnCx6`A>+AQr`*rtw{oZ$v>WPDy_jS3B)!J76@#~6l0V3!!}6KnMrg|7)d}?REZyd z>1u$?V2>cCAe?tlUY4(i(~>*tQ9)OV54;Ybw?YBK>h&M66 z@v^n&!Sfv-;CCGm*9~B^8+&zw`>y=5mtn+5T_i+x^=M`i=WWgo*3SsQCJ0*~GI%|@ zh{sO-I-WT78#uh@&3N0`YxdoY*XT(AK%}Oh)uD{lS9mPF001BWNkl29drmAv8MJx7tWCvR#FGCz?_ zK7eC|J13Eqd zxsnATi+|>M^Mg(|W(PJ`%fM?$hqcYq=NQ!OtJD~4Xn7ft5QGhYLMRhR1p>m=dybH%aWF*lZ8rBZzh(BTY|VKJm)?WUoxaXy(QXv~T;?<9 z9Y1g1hCBaF{=IB{H9h6v?UR?OIs&mgwUNNMu$mMgWyhZi03n`x?LXnGul^AZZeEXD z5B@j!#%q57`!}w0JiBav7t_4N{g_siV$qA24gm_Jn@YgJ&1<0-wEG;;#j)Pr%?7&X z0nfWW(KOWR^M(2yt*@)@H6M|fIFepU+0S#_O_vi zfIv0=UmcWwCH67U0J&;A}1V%btjToKI}03-56B3^d9 zWKJCuaCq-EfO1+$HK1H_c+%W{tOj_#Zu#_#+^gmLCVjKlBxdQlsG+OtF#x6jky;4- z>HTjS)%+bX03;M{ZcE@UsBH2@lc+Cchp)+j?g zS!y34LZatG3wm-ux|i;fw^^T^qV?`XUh!mJUEp%szfA*PJrKG+@j%o0teA+J|FuqK;tWj>}Szca77?WMAty1QrZdYxH^ zecr1nJ>$IBZM@RRt~U_@5n-1(fd7^>s)wSKLjVL2A3Jz!ziVp+gMBYvX0Jx=+`zA6 z((J|0!@3F)TQ54~b5|ZbX&t-x)l*$rTm@XV1vNd8zS?>-Z^BeZpJr|3c)js<%qGC@ z_&RF>Brpsh1SJ7E?q)E7SOF1RmF_57R2AFd>v-zSefY>{?!a$8^)vY5xsPKzJiqkg z>0Z1HE5{J*8tr$Dd%KN6J$Syeb5D=0414|b1)g^i0mK`8#0n+Fk^Q$qm8gyb4*<*4 z;+l_8Z9LQ@59}zc&+4Th2G# zHIMMbwG!u2H9qd&MeQ6?k%zl1WL{S*kn*Z6tI9}eHf+{?lfS%v<{J18fuf(vbzR-F zbFm7cMNrkmF12+mnv8zCL0)FV$Gm-zUe=71XzNZ2Mzlb$HH7NNG}yEa&}Oc?4%g?( z`joO^sr&N^BTOv~R@QzjBZ*kHK2N)UZGOoar^fsVvNC(NnjG~*n#(TNtV-MXV`p!q z+VLY7&{^(sTOFIX@wsuGg z1D*98>t4!pa<0hf+r;(VJTtVm9IsS>mTk1V@Sc0DD2nKVD%F%BrRhmKTiLvW3tL@){%A%Ilt zG@31Kcv%%0GNp;v%N?wj zeh3D(82wRxFFQHGC-0}`^wmD*_OjFcrfLLGlu6tpfDEpLui@F3AH;8b_V41ipZ+;~ z;gyH%cO?ZBNVCOdF4HGL3ZBQsK(^Uwyc=(Szv4ZRyzEe6?B2Zx%nXS-T#f_w)#BZx zkDaXL_HlMHF2nF$iU?4KZ~%g(ZoBEo?SR$`ZUYcV^)9jh>p*lj@qufwVs}8CV^`w_ zLtO{cp+F+Gny!08Dl-?z&?;Mq%XebCu6!QayASEvG!y79O7~+uT@`1Y%ux}%+vydr zJ9yoAS;CyB8~C%=p_c^(+0z5hS@Y+mo8W$x67!*rm6v8Vw+s6KPZQF!!k3@W%Fdw$ zv~6B2G6rBkJVK-)gcvVgcmgk-e*%B@-2a2O9sBFJ{n!uVz^>y^kgb4ATao2}1N8eXq` zHa^%*im5=l7cV>BN9|nkfVmehlYX)+g0a3m01!b{02mliC<(v;u0-eY#OZ&BCr;gm z8}_{$x1ac7+;ZsKZA7G$0*VTNZFPE*+H2$r);OhbQTE}pmlcqdj);h$Vk~uC2T0>; z`sePmo>@bws*TM=mmsjJ`a-y5DQL4f=vlp&gA;P6_s!vLjaQ;T*lz3z7zT`+kK7Jd zPG0$t0q7o9y-SFQR8`&oMo@rXRj^S=Bv5s`-XwMxkqDyIrXU8!OSzCU_*of$aU-sE zwUNiV1^i8cjaiDhV}RSlSX1jt%3P&M&9TdUk9PQ6G&ZhYmK)gl%Hw{8CYme`+lCe& z-=7d!w0)eZP2P5T-{i+&ZP&M#zTI(qe-`J)wlQtoL|p8NXfTKjG!l>@gc2ZN!pVz| z;pD}~uy6A^y#2^`;@v0izy|NP6}2QFFksnW+;iGLXkzo@+{XJ~FGH6}J+(amq*eT= zsw=pGw0Q8``?p0-F9Fl~rZoU~{mIgKGV6KPP5>YTL!inKjA5XF+5=)$AUZ?u5?g_8 z`;Mx$N&yIT%b|BMq7a5`^H59YV$vO@Il)Onf8gP|Em$954`=MUHfr5oZ;}&{)$91m z>+r%I>0%Qy$$WN}t0xRG`d9M7n-?D=c%S6Q$cvXMzZes%*tH-qtd>esM^FjuiBtv1 zC{!Z^%uv}%nt)!qavGm}@gw-vPklfB;H&=_FJ62ck%j;)bqg!Q zfNRyi7?z zs-j0PD`)H04H)s#*ZTG+!iL}nr^YNIAj7WckfF(RMJ}rs2O6-=u!R5`xl`OgL=o80 zy*z#P_wc!M58&ADoAK`BKaRIv{auI%U?Pyj`K_^)yAXf^KS5pwEm_bk#w5rkWdwuX zIV!wV+P7i|=?iG)({q8YETdMPJXC0y%hjG{$^2QhK642fsEBPtf(V=D0PX~t`v678 zn@R>E z(1fi-_kffaFUwCLy!f&5;-yjYp3t@cN|dWskBA*8VR_31kt_0wpb8iPCaD^DfvHdsWey9K{RHLdUfOXw=C@d*eRz+nW?#5{j?Zi_A10Zx=ORH2N z%VEx1s17ZutjK=9?ookwXEXjCfg#DYrLjU3tM;djVf^=TH|gYiBv7HwDw<--99v*d zt}A=HE9Q3{+pU7)&Q)W6U(wuCEUFSLe*fN$aQuE${%+!U`@1b95iPrB zQ{$TG5gJlM>1^5>Vd>T2OUe4l&ShJDUS5GIyTni{ZEyEu+JCq)yf7wBF!E!%WDY}` zJ#v2Uv+V%4(?qQ>=)^urojE=g@tq$Z@?+EE`s~|2c&QlPVkFRTx~zZ97E|feoHDLD zSB>f0wBTPg1sMsr5?;o`FZ~-l^5O^a)??p=w;%a_T)+PvNaz~liohHfdR+1DnvQBV z*XHC2DdwN{OW8}W62ieuhG(@8HaDr_9I8W2iMdy#R@UTwvGU_%D zYlDX@)SS0q!RRwz)dWl~2k42WHy6!)&#_}0BD)DeX!WiMQ-+R7m+UN~l6{>jeC+Y##Sh{l=v-C< zxS5P%JcpzgFJ8QO@v^4=p9}&BR6>A_t4buiwtW(hy!Zip`h^eTEl0l>-+aSQ;?Tx5 zu5c_!PI4wxEiQtJ8;}y%0_*T6#Jns-p)E}U7?-Y`g_6JsSK>Q5)Xs$q6E4jw>*o#C zIch(7OaGZKxqcHfzoYHPsP$Mh0ME4pdpzs?7QLRbn+RpiJ?F-EV+^_k&ufD9#3ut( zBLE{D-F+M&(06FPy}e2Rec$NnCkf#@feqVQ)JVcptiri!(MAL7rm=`C76-I(B{*fM zRN9)Z26QReiKS`(Hm{-}Rg^M=P`poF1JN}dNg0SwBTPwnNjs)JCqt!?Isf>j>o3jeop&StI@SvQCrDE2opHTTlw1 z6ab_k5|C?JsuCe0!spNZKAt}NySQ=xyYbD}{}is@|4!KMJPcA!3#MRu4B5O}Z1YlY zzP&5#Eu(N5;G@9#o-3rYd zU(`4$tPLnp=6b(mPIRvIGw)hqMW%Uh{k1vOniSa|TH?h3K_~)Y1R%tLjcZ)f5>knorU~M_VD_x* zWkKe8hn`XcMrQUiP!9%Ws618iwQ0%VbQJ|m_hZoS`}R@Yy)a;=XRwdgE<=%}gJwIq zW@_korDysX+Js2Y`sfiDFRKka^m_we;XA1Lrd85Mo2eKAR2Yg7%%@sGzXmta^*{4!fFn43*D#6ymBx*r%vI^XVy`D^1rQ&^hPN3uC8PrcQ-4uYV z3XpTx%XIDS3qT-I=#;ASsuKVP)Y{3DIIcPBPZzl7rnJ54Z_6=hq;EY1Q_BD?m&u9V zHzXkozF!^v&)X?o*!pPiAAAD2+zF@=zqI(h2hhE&G`Le0|Fu2A<^lBO&M&Q^E>h0d z3k+Zc9L4@rp^la^ufGw9jHNjz!tPy1@eNmhKi+ZO4`4SPfRF{vNh1KpHxc!+5RsIv z7$+}2j{Bba`P#N|H34*XmABJ#|0el+lV{E5;&p!0dVFKPZg*p;Ft{{NZ^4f3cwDV- zuR8<_n63lK%txIki+zALdH3wxRm3mVlrmEDzLa^_d_&W=Yj@TOld1m(&gd6`RB-&j zt@xS$;sdOzdjI`o?zlrM55NS~Zw}r4_wPDtSjhtB3tF6cqWP$@rD=m7Bm#sITh~T#EBPoRNLzEvS~-w8F<(!q1y^IfEa>`BVv&_=Zee9N0#HEFa#kA4r{ed0&3 zZ{s+W4B#nTyevog+R-{M(iho3NbTc)b!`sMMn)xhU#E;9VLT6`h z*RQeY5Nig@{Ke$P)9}Fb(qp07ss~@8+4BT&ufMMhV|)d>mj&C!X8KRdU0+^W2Dp>H zWcXF*G^I&+`F90sd0gD<#!yK5o>XdUZJf-w5?#WRr|-j)r|-ktkA4rn<%XZafsLan z>OxhK7MK5<2jIQbiq<*7ATDg3MhF7~MF6$=b4gCuRpt5OBVz6V0zv)0G_Gli73J?M zQ?aNh9+{T@D_7W~RNMB}?R}TWr{;J*&0k9kux-6-=5eul@5lu_l&0eYI{mIqw1`Wp z(b;jZ=f)gnhP#nce59_vPw1+o1n5~DvhMmQ1J7%Z$FyxMa$8j_p6lgMWwS)c@OJG(l30{SxixIRl5Ph8z zWs0eSRxoj0$ExjVrw6<&Fdbi!aZmhu)zLu@uzT_1#fz7^zF=j*^9ARtVvhzBF(87c z&pv?9oqY&5?0*OT^7TKB>s-T8LR=MSTm`#@7bYnVGymKg`tW4w+UfhanvCepr)z%fy>FFF7my1uGgC*X3kbXEr`+e1DKR|aU8yZAJTJd7F z0QBQ~-vSr`!V$n@KpCp!8k%GcpQR8P0+5ircgNsD>x%XP#T`Z6P@=h-iVkh@)Hz19 z3SeeGAbRz&u3>u#9-OYNQsUM}fE*`Qb*l=zbno*tJ^nN3@lQ{5aW!D#Wyc5o-j;a% z@Zu%$4Mo4PLcn>k*kBPLx*Zoqq_#qlRAC@^@xl|h|LLE{P5W=hd#?LCxN-lxkn*C7 z%Q`qyWgZjRaq{OO<+5Ieylw&22q1$OFFpl=K!l(qc|KnlP3eCKufR&&o!m&NK0r)`$C;&99!=6zKKtFWVF+?FDU;)%5 zw^sGZZi%Maz+z<+Kgua?n_a`98`fn#U(-e(Jv;$c8bt3y7h1Z>y4-K;%N?Eq>5G)U z>0DOBYF!T5HZ3x!>cbiC zxS(kYm+H#un5S&7=Ve`#iC0$t^DxHZ;-nD-0)-fR!dN08Bmtl>0;m~* z5#jvyOZdI7-h&T4`8VAx(x_$oqlL)W@)d;Ez z=H;yCE^}2@PCh$1KVOBu6J2EdLw(OCZ?aQ2GqiN_2r7V4#i7mPaU)B2tnKdV^ zG6n-6aKvCya4@z}k{bK>650F$3?l}Y-Z+DgKl{u0$YXyCkG}jLuq_uXn2yl4e56cn zMaJu=mkRvBB+KCBYoCEi2xj~DDq)htEy`c4N*>*1&cEz;o)?Gd(!S5DTRXAHQkOjD zeaEvCZ%c|BuD;!thAXsg1JM}msJf*n8#b@J4hVFI{-l{*4+CLVy>$pw}%oT^@EYX`^T5r(IvO z;V4Zl^sQT5I^F;93a&<7h#wtZ&RITJthVFF9&->NRU#r#{oQV^RzDEo9zL?~CZ}-Z zjG_Qa1Fmph4uDffan`Ler*9qbAo}_S_^WA*y?9v}>*9h)zlOXF$fm=$3-jXj$jch) zq$-AU0*q|0G7?v5ZXhHOiZCVzt}8$sAOwNgfFU3uuo#TTZ{Xn;=L>Kll9sUfz^@@r=wwzNM)06B_b4OTwqTUH*;0~sD)o%RgVxc*w|cj>+2_iPK^ zi>uJ!;xorb%PO|5llwbQA`{vrwSKQP<~MTRXHEZY-uE|%pOXe)!y_-%zDqfQ+=TtaK*d~I{lHZqyVaxY6= zRclA&b;d|C&4A0SR@L1ogI^I*YNIP`W>vaZeD;a!wJ^z9n?CC~>zI9Vo1x`CNlN?& z7v52Ff8WN}lq|Jmn9A_Q>;j03s;Nq6=Wgtq&${D&8KBv^I`z4G7S27n;K;^xX@gaD z&~uHYSnbKwzS+Tz>tRUTFd6q_U;EKyQRl$(mGO@2KQBA^*?HBcSh+j!eA%zEhGJIP7cXAw1^_i}@e;uEb&a{Y z6oWCwED#Pr(HP_KB0h2QgZSv<@5Li8d=%T!>mZ12E~;v|tu+F`cG?p?aOtEU@AZlS z5d)PGfDk|kQVZI~vHw69ojp zfnC?S(=qBW^m^cD?XBQj4*y~Rv0Dm6@v#-zF^jzq#nJ(f#kwq{x}4O-u^Fn9P}RGKJ=G<)v*vs_>0R*K18{ z0+uTRK#bFY_+&8fDa8_Z-wRr1nLG z-MnY90)O}kB`f2W!}kQLptxYXE)RO8CAj~75D28WUk~iM7Q_M)1@r8mcL`j#nbe4Z22Mb`Oz?l^S(fgSDK0tKeRJ2)R(RJSt@F4oo006>p_*xei7kKgF#fukj z$LEOZknJt9&M_Nd4CvTqhH5EU7z$DaL-B@Oz+b%h0o?o8kKvIQ@5NSl*~VF{36-e= z5Hb)%AQFi9GlA=)A8BN*P}>-k;OKh*V!`7teGDWcM4X|#P;JihJM;dlFGJ0hWBQh? zt<#mLl<&qrt#Xdn7Q0xFxL^iO&A`50$Dz1iE$6w)J)oGMiRZtvlRNH=S)f%8?miBQ ztWkuT3Kz(o4`>_ZPL+vw?1JiIQLS?zzs&I&29=8ECLGEdUFUV`ZO~A4ZVhzLbE13k z;>C-%%{|0qv5(v-LQw!R426Lr0)!!LXg+j242 zf5!k|HGndRIn#T|%W6x^nar+_m{_6$08qi{%g^HFOV2<#veoAq&f`AUkx*ZNH1}d# zBGj)wUnEx0bdJcUc7yR(P1L{3>hrct7u{m4v!6*-apG;O+)nFO}ASZ1XX_ z6J(C>yUjKgP=in-RPX-{HDKa}D)Ow!%k~>xzESL>Dj;rNx7>hfo~Eb^*UAAnAMQEH z(S_^uFrRnGdbkMtyUcggYjVOxamcaTIkE3H+<#wMZGN{q8gow_e?Hs1%04sTowC_^*?Gp|Gw(WHCu zl0&YtNnB=A(SkZG0bw1+2`wHJmYJ2&56FKqiKDD=(+&?>r3ZJCllA@ zC$P)qME8e?XM3&qlNKL+_twPDZb;n4})afe3t z-%kbr?t%(XuM)jR1v&_HHvr(+zMH{ndDazCH6Yo6LiGv0Eecrod3^UHvC{yOzTY)( z_!i@^{kquU$@LCSqC9}_#fuj&Uc7i2Y8+vRz-qv-Rpw#{fI=cb8~_lgK>z|oYy(jM zDgg)qw&VgHdFft!=<&aSr_TMZ1AgrH$VWhHp@9TAM2{0~R1$ph`FkNo5wX<~XaFSv zD1>6%tLFBRyO!n1URsXn%HTM|0newd#5rh)y9CekW5J!hPoF|WICkI`5I}d_p>B7N zYCk;H8*E}g<$w7d+IQ97zj{GHU_=m@$|`i~cw4IJ~I#3#Cj9y4vO?WIYkI4!<3YUy2IfCJ__ z{FNJj0&hF=JvN?jL!FMeqzx#Ozm!xjNqm$xsHo7QLAEETBsSsV)+zkjr+?f6=9nK~ zY?>o>pRe|-Yf(jpTL&M=ekUGhq4x197uI?aGs)su#W*_4bzhph4a9Dj<-hen<$B|q zoc`|My^}_+73)PCEP}28^yDU1=?}^4;YAn_A_^-F%uWT+vx~?^_0noEL*JRG7 z1!$ek zp05rYK%Zv6x(3ion>~5}y;gpq9aC0SUTFycdO~kBFaSM%u0&6X>}IZ$-J@OjU%&l# z$^DycC2GIPa8xdxVg<+c-3CDI&f^l2>9zx$a=P+DpOtDXig^|pEp*J=u|XH#Z>ik$ z9ncRo2`2kGj#AdadlAm6cb!8!p0A;EH&cssxSrHS%{bB5u^M`{00u8!ym;}lbBzCF zGf?l_yv$B~A{b&QLI@iG0@VnwZlA`-pZ#Ur|M|a-^6!td-Lg!!1h5bj(=j`ZTWy|g_Lrs0h01c z*EHnETpKHKY~Y3zq%MNPe^E}e?AhnP7bl;%q3FC-?#161pGg-m4pZlmmdn9Nt=Hii z=I8?e(FLBPyv~~PfP)t=UcBre+rMVOx%Xww;-cpI5;$&j0w5q&m}mr6+kEuYg~#z* zPyGY@))#&jCoesjQ1U4Njw{RsAjuZJ2~=rexs;cRc-=^v&nWQ6&;4I8I%-5EG1t9I z^6Rpkvu)1WwM3|AbDG|Nk@$j_VfwPg3wM&AcOO$&{3;m}cLR%ND^vx5;@bUh#U$r> zeGYW4KRHl9< zs>K+T1)8H?WlLS@L-?f&xjsZOc>d_AkKyEnC!rE9MmGz&WLG)rB66Eq@#Q?=`Lb{j z8ibjoDY?t~fHnb+}g2s8Q^r{>^cO` zwPmVBK1VCnIp6Vq6_cs;v*!x7$CESk1zx;(@v@fU>|D|At;>d@r+#Y~zqifnnV=Ft zID%5jl+Ci~=0hPt2!((l`24y54KvRrGYl5bS{1>2NA2siO?0j zgoj`FK)lcvULe)BKx;BL?Ka+4Zz=jA?WH41oSuwz=k!qCRG0u_KkAZQZTab&MU zG6Iu9^I)oUZy3^r2{~km8|d-iPgQ3lT+# z=|B6-zxo4?3-k{I%6Gj?<{wiTT6@}ITLx~RwML>@J~Nc@=!d=~uLC)z4;R!hjd0VU zZ(snpJKm1Hk2tMWudWbpkPWKfP8HC7vUm42R%vNG*GnyZ$t`&6dVQ-Xwbj^nH7@?B zeJW88TQFVgZ!xec1{FWDvV5?ZGj@Hld71sscn739K925IFKNloGt?8~ z#phxQ?4~Ul?A>z^YCVeF9dE*+f)@U^kDUz|pHtxP9VLtw2C}FiP`Q zY$ny7qV_YjWn6b{~hhyL41F9A8%D?r= z3SxQ#oi#Hg^{Ara@74Xj7cX9Rt~9k5CEw4*3w3PS3W5xEPe4ugW9=%`+xp%9&V06y zb>f)2PW-H{h>vUuc{<(^0Er5jc4(V}!T_+nDM^7>w@>4NXWoZMh zrH#3z5O<6H2bb#$)EBz6)Dnf|wvPw>?B%cEk>@{v?I46kX{;B{SU-*Own8p7Eq`p= zSIhpE8$VxNo7BCHQ$$KROaV3X$E<%K*5p(45%b!&c>K!pvlKsSpBK&=*E&N^s-Qw1 zowdS~I=~<@rTRfSnUA*3ODFCzMJF|jHPwj+YG<=D^Apb_gPe_>&sTQ6HX>CBfItI) zZBG+J_Ot>KA);*@8C{icD_#cVR?{ZUy0JDh5d9i^+Yf>{rkd-39>lVG8yE!iPG90qe5(Kn%< znr{~4wW>LboZ6n>I*0q8`XBMJ&;J}wU;bjc`J)k_072}whZgjn;3|;V(a3neTVKRM z=58Yb45)(KCdU=IhzFi|KdywAF%koq2qIlz3tpCDGd)FNPWKm0Z#=I*^DAx2_+c9m zO$(ICHV;(^aQ)$Tw7!1jYM}`$sB-PT+h8jurJ*7WadC3yligkEw+5hUVd%gChjj}M zt9HS%74EAL7_1`K+M=I%oj~+rclVu;ye-QTjNUrnY2HN)eECZN8HADjUSJ&0eGjpY&ujZzht2vL%pWfg z&(5#7Q(SSs*KJI~g==$0X9PwAAYu@2G*Etb3>`Qv`=$n*R=~bKIv;Mimew;_9@ZwT z2h->GGam5p;>C-Xev{L62F|@NS*MtVOXfEM44{$NTtRIGp)ld;mmkE3o_H_*;-wGU zSeXDJP3c`U03m>C1R_^QEda>CHY@cyQ+rMv?L(AFO#b96{~TX>?N2}wB>ASrHQea} z4LZ-2=go(gk=xFLanyOIF6e5?ACy-rZ|uk953k5qJH!aO9jEDIRhyov=NAwFtj7JB z5m5+G1ZaS(uR68>Z6U=UgA9@LQABiX?=4IUcVMbt1)XLjx(W)r3clufmLb`xRSrPA z0?(UNq4%=bdjQdUF`dfE$IE(4kqf;GzV+^$_wc}UFJBCk@`i!dU7sCZbJ9MS|J}3#;AJs1 z=+udn99@`_fR%8~-Z!;g^ju@KTg_-fotAGaV`f@>bz-f z7UC?USpQx72*1efBvPyAUz-4zx?K~!?N+a0Le`f9Y?I10)$c`vkju7tLjzzv4sC2CQn|xc=1vNeiUe0*0t)~^D3`)Y|=DdQzaN;L{LOf7>r@Wg0x@*<^Ze# zE^nX0Z-3!`!*6}zXL0t8uOK-$FRm%1En|n}HFl%nlcJglh*&BDwL|*ZGY{aQXWj>b zK>$n5OJe_5Rlx}DCdUqqhMS%$Z<5P*KE001zVBSF%WKC?qPrS_A?Md!-@ostU$%dq z0QEWRZvbY7fN|sDcfyjZw3B)q=nB2#&a|269d|$vZ`{}d)E-je$o(3MCKV)0^pT`k zY9qd(R3Fx@LifRZcfp32dh4@klCrS1JD_TGf0TRWv>RzPIwn5rChHDxdGX@K+mr6{ zf-;|wtu*(tI&-O|G286$E`q>T4T1@gG6Vq(LQrV08IfQc1`NWIHFxU`C70e8T27tlg&Eq}ZwGhEr z5Se{3?jnk z&ip<;_{4kh_{k4hltKZJfXF}*CjafFYPQJkGbr%X*B`)x&%O_&0v!{`xSWOwq67m% zqy|t1K|0Cb`pW}(i8#jqb5kFR4?33~rjGIotQ|$93T8l6KpcV*;hO!o0MOPBAgHsj z$b#q!wSNSl*I)G}M8q&;c}(FNN2ueQN&XI+gGJR|tDEW5>4re#BS#hYVrWXy3eJQ( zQp@4T{J_%(kgGj}b3)lrtFNFiR(N)^GWqQ8$Xm^wIVE+wlT}}V5f7oiNyYQ=7`&}6OMQ-<%Xgn38)H;>Q)x44pkCEwSPNWl{C+nE-oug?G@r^ z84yy&hRyaL)z6{R*LzmaDUD$iCilB$_nTO)CF`?re+m*mI5B#CB>c*D4BWMgKCH@r zR*glLf41!TX^Q`hpG#el#;p4l?~%rzoZPeQU9>R1@pp2+_u{3o$&Ax&rkEwh$#InQG7iHfn)U6

    tECHMb9!1nke{`AFr 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%0AE7@;dbUDb>q-R;JV&2XcD- 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

    kv?OF9>HmV*{0BKl<7H2QpYZ0QO#eVOua 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 literal 3450 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 M-V9%Q1uZ=Jf7C6ZV*mgE diff --git a/data/images/spotify-sourceicon.svg b/data/images/spotify-sourceicon.svg index b20952cc8d..e85d87c1ec 100644 --- a/data/images/spotify-sourceicon.svg +++ b/data/images/spotify-sourceicon.svg @@ -1,92 +1,30 @@ - - -image/svg+xml \ No newline at end of file + + Slice 1 + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 8f7671ae65d2d28c65840565172500f07abe5e46 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sat, 15 Jun 2013 02:16:47 +0200 Subject: [PATCH 400/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 32 +++++++-------- lang/tomahawk_bg.ts | 36 ++++++++--------- lang/tomahawk_bn_IN.ts | 32 +++++++-------- lang/tomahawk_ca.ts | 32 +++++++-------- lang/tomahawk_ca@valencia.ts | 32 +++++++-------- lang/tomahawk_cs.ts | 72 +++++++++++++++++----------------- lang/tomahawk_da.ts | 32 +++++++-------- lang/tomahawk_de.ts | 36 ++++++++--------- lang/tomahawk_el.ts | 36 ++++++++--------- lang/tomahawk_en.ts | 36 ++++++++--------- lang/tomahawk_es.ts | 32 +++++++-------- lang/tomahawk_fi.ts | 32 +++++++-------- lang/tomahawk_fr.ts | 32 +++++++-------- lang/tomahawk_gl.ts | 32 +++++++-------- lang/tomahawk_hi_IN.ts | 32 +++++++-------- lang/tomahawk_hu.ts | 32 +++++++-------- lang/tomahawk_id.ts | 32 +++++++-------- lang/tomahawk_it.ts | 32 +++++++-------- lang/tomahawk_ja.ts | 32 +++++++-------- lang/tomahawk_lt.ts | 32 +++++++-------- lang/tomahawk_pl.ts | 32 +++++++-------- lang/tomahawk_pt_BR.ts | 32 +++++++-------- lang/tomahawk_ru.ts | 32 +++++++-------- lang/tomahawk_sv.ts | 76 ++++++++++++++++++------------------ lang/tomahawk_tr.ts | 32 +++++++-------- lang/tomahawk_zh_CN.ts | 32 +++++++-------- lang/tomahawk_zh_TW.ts | 32 +++++++-------- 27 files changed, 482 insertions(+), 482 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index c1a11cf7ae..9baee582f5 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -281,12 +281,12 @@ connect and stream from you? نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! - + Sorry, we could not find any tracks for this album! نعتذر، لم نستطيع إيجاد أغاني أخرى لهذا الألبوم! - + Other Albums by %1 ألبومات أخرى ل%1 @@ -313,12 +313,12 @@ connect and stream from you? الأكثر شهرة - + Related Artists الفنانين ذات الذوق القريب - + Albums ألبومات @@ -338,8 +338,8 @@ connect and stream from you? نعتذر، لم نستطيع إيجاد أغاني مشهورة جدا لهذا الفنان! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4047,47 +4047,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks أغاني قريبة - + Sorry, but we could not find similar tracks for this song! نعتذر، لم نستطيع إيجاد أغاني قريبة من هذه الأغنية! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). لقد استمعت إلى هذه الأغنية %n مرة.لقد استمعت إلى هذه الأغنية مرة %n.لقد استمعت إلى هذه الأغنية مرتين %n.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات. - + You've never listened to this track before. لم تستمع لهذه الأغنية من قبل. - + You first listened to it on %1. استمعت إليها أولاً في %1. - + You've listened to %1 %n time(s). لقد استمعت إلى %1 %n مرة.لقد استمعت إلى %1 مرة %n.لقد استمعت إلى %1 مرتين %n.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات. - + You've never listened to %1 before. لم تستمع إلى %1 من قبل. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 724d889ed3..8c35ab5503 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -280,12 +280,12 @@ connect and stream from you? Съжалявам, но не откривам нито един албум от този изпълнител! - + Sorry, we could not find any tracks for this album! Съжалявам, но не откривам нито една песен за този изпълнител! - + Other Albums by %1 Други албуми от %1 @@ -312,12 +312,12 @@ connect and stream from you? Най-известни изпълнения - + Related Artists Изпълнители с подобно звучене - + Albums Албуми @@ -337,9 +337,9 @@ connect and stream from you? Съжалявам, но не откривам нито една хитова песен на този изпълнител! - - # IN YOUR CHARTS - # В ТВОИТЕ КЛАСАЦИИ + + YOUR ARTIST RANK + @@ -4064,47 +4064,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Подобни песни - + Sorry, but we could not find similar tracks for this song! Съжалявам, но не откривам нито една подобна на тази песен! - + # PLAYS / ARTIST # ИЗПЪЛНЕНИЯ / АРТИСТ - - # IN YOUR CHARTS - # В ТВОИТЕ КЛАСАЦИИ + + YOUR SONG RANK + - + You've listened to this track %n time(s). Ти си слушал тази песен %n път(и)Ти си слушал тази песен %n път(и) - + You've never listened to this track before. Никога не си слушал тази песен преди - + You first listened to it on %1. Първоначално си я слушал на %1 - + You've listened to %1 %n time(s). Слушал си %1 път(и)Слушал си %1 %n път(и) - + You've never listened to %1 before. Никога не си слушал %1 преди diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 9495ffeb03..5898345e44 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -280,12 +280,12 @@ connect and stream from you? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -312,12 +312,12 @@ connect and stream from you? - + Related Artists - + Albums @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4032,47 +4032,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 849a84170e..afb9db66e7 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -280,12 +280,12 @@ connect and stream from you? No s'ha trobat cap altre àlbum d'aquest artista - + Sorry, we could not find any tracks for this album! No s'ha trobat cap altra cançó d'aquest àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -312,12 +312,12 @@ connect and stream from you? Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums @@ -337,8 +337,8 @@ connect and stream from you? No s'ha trobat cap gran èxit d'aquest artista - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4047,47 +4047,47 @@ introduïu el PIN aquí: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a aquesta cançó - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Heu escoltat aquesta cançó %n cop.Heu escoltat aquesta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai aquesta cançó abans. - + You first listened to it on %1. Vau escoltar aquesta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index a6c42a8fe1..b6e05811ce 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -280,12 +280,12 @@ connect and stream from you? No s'ha trobat cap altre àlbum d'este artista - + Sorry, we could not find any tracks for this album! No s'ha trobat cap altra cançó d'este àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -312,12 +312,12 @@ connect and stream from you? Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums @@ -337,8 +337,8 @@ connect and stream from you? No s'ha trobat cap gran èxit d'este artista - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4047,47 +4047,47 @@ introduïu el PIN ací: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a esta cançó - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Heu escoltat esta cançó %n cop.Heu escoltat esta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai esta cançó abans. - + You first listened to it on %1. Vau escoltar esta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 195ea47c27..dc2cf0fcbf 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -281,12 +281,12 @@ se s vámi spojil? Promiňte, nepodařilo se najít žádná jiná alba tohoto umělce! - + Sorry, we could not find any tracks for this album! Promiňte, nepodařilo se najít žádné skladby pro toto album! - + Other Albums by %1 Jiná alba %1 @@ -313,12 +313,12 @@ se s vámi spojil? Nejlepší písně - + Related Artists Podobní umělci - + Albums Alba @@ -338,9 +338,9 @@ se s vámi spojil? Sorry, wir konnten keine Lieder für diesen Künstler finden! - - # IN YOUR CHARTS - # VE VAŠICH ŽEBŘÍČCÍCH + + YOUR ARTIST RANK + @@ -450,7 +450,7 @@ se s vámi spojil? Unknown - + Neznámý @@ -458,7 +458,7 @@ se s vámi spojil? Sorry, your filter '%1' did not match any results. - + Promiňte, vašemu filtru '%1' se nepodařilo najít žádné výsledky. @@ -466,57 +466,57 @@ se s vámi spojil? Composer - + Skladatel Age - + Stáří Year - + Rok Duration - + Doba trvání Bitrate - + Datový tok Composer: - + Skladatel: Duration: - + Doba trvání: Bitrate: - + Datový tok: Year: - + Rok: Age: - + Stáří: %1 kbps - + %1 kb/s @@ -607,12 +607,12 @@ se s vámi spojil? Dashboard - + Nástěnka An overview of your recent activity - + Přehled vaší poslední činnosti @@ -790,12 +790,12 @@ se s vámi spojil? This playlist is currently empty. - + Tento seznam skladeb je nyní prázdný. This playlist is currently empty. Add some tracks to it and enjoy the music! - + Tento seznam skladeb je nyní prázdný. Přidejte do něj nějaké skladby a vychutnávejte hudbu! @@ -2267,7 +2267,7 @@ heslo Dashboard - Přístrojová deska + Nástěnka @@ -4047,47 +4047,47 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackInfoWidget - + Similar Tracks Podobné skladby - + Sorry, but we could not find similar tracks for this song! Promiňte, ale nepodařilo se najít podobné skladby pro tuto píseň! - + # PLAYS / ARTIST # HRAJE / UMĚLEC - - # IN YOUR CHARTS - # VE VAŠICH ŽEBŘÍČCÍCH + + YOUR SONG RANK + - + You've listened to this track %n time(s). Tuto píseň jste si poslechl jednou.Tuto píseň jste si poslechl %n krát.Tuto píseň jste si poslechl %n krát. - + You've never listened to this track before. Tuto píseň jste si ještě nikdy neposlechl. - + You first listened to it on %1. Tuto píseň jste si poprvé poslechl %1. - + You've listened to %1 %n time(s). %1 jste si poslechl jednou.%1 jste si poslechl %n krát.%1 jste si poslechl %n krát. - + You've never listened to %1 before. %1 jste si předtím ještě nikdy neposlechl. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 6098dbedd6..94451f4167 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -280,12 +280,12 @@ connect and stream from you? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Andre Albums af %1 @@ -312,12 +312,12 @@ connect and stream from you? Top Hits - + Related Artists Relaterede Kunstnere - + Albums Albums @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4034,47 +4034,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index c45b9d2574..1ce5388a2b 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -281,12 +281,12 @@ erlauben sich mit dir zu verbinden? Sorry, wir konnten keine anderen Alben für diesen Künstler finden! - + Sorry, we could not find any tracks for this album! Sorry, wir konnten keine anderen Lieder für dieses Album finden! - + Other Albums by %1 Andere Alben von %1 @@ -313,12 +313,12 @@ erlauben sich mit dir zu verbinden? Top Hits - + Related Artists Ähnliche Künstler - + Albums Alben @@ -338,9 +338,9 @@ erlauben sich mit dir zu verbinden? Sorry, wir konnten keine Lieder für diesen Künstler finden! - - # IN YOUR CHARTS - # IN DEINEN CHARTS + + YOUR ARTIST RANK + @@ -4042,47 +4042,47 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackInfoWidget - + Similar Tracks Ähnliche Lieder - + Sorry, but we could not find similar tracks for this song! Sorry, wir konnten keine ähnlichen Lieder finden! - + # PLAYS / ARTIST # WIEDERGEGEBEN/ KÜNSTLER - - # IN YOUR CHARTS - # IN DEINEN CHARTS + + YOUR SONG RANK + - + You've listened to this track %n time(s). Du hast dieses Lied einmal gehört.Du hast dieses Lied %n mal gehört. - + You've never listened to this track before. Du hast dieses Lied noch nie angehört. - + You first listened to it on %1. Du hast dieses Lied zum ersten mal am %1 gehört. - + You've listened to %1 %n time(s). Du hast %1 einmal angehört.Du hast %1 %n mal angehört. - + You've never listened to %1 before. Du hast %1 vorher noch nie gehört. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 140f774c0c..77a7f6cf87 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -280,12 +280,12 @@ connect and stream from you? Συγνωμη, δεν βρεθηκαν αλλα αλμπουμ αυτου του καλλιτεχνη! - + Sorry, we could not find any tracks for this album! Συγνωμη, δεν βρεθηκαν αλλα τραγουδια αυτου του αλμπουμ! - + Other Albums by %1 Άλλα Άλμπουμ από %1 @@ -312,12 +312,12 @@ connect and stream from you? Κορυφαία τραγούδια - + Related Artists Παρόμοιοι Καλλιτέχνες - + Albums Άλμπουμ @@ -337,9 +337,9 @@ connect and stream from you? Συγνωμη, δεν βρεθηκαν κορυφαια τραγουδια αυτου του καλλιτεχνη! - - # IN YOUR CHARTS - # ΣΤΑ ΔΙΚΑ ΣΑΣ CHART + + YOUR ARTIST RANK + @@ -4047,47 +4047,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Παρόμοια Κομμάτια - + Sorry, but we could not find similar tracks for this song! Συγγνωμη, δεν βρεθηκαν παρόμοια κομμάτια για αυτό το τραγούδι! - + # PLAYS / ARTIST # ΑΝΑΠΑΡΑΓΩΓΕΣ / ΚΑΛΛΙΤΕΧΝΗΣ - - # IN YOUR CHARTS - # ΣΤΑ ΔΙΚΑ ΣΑΣ CHARTS + + YOUR SONG RANK + - + You've listened to this track %n time(s). Έχετε ακούσει το κομμάτι %n φορά.Έχετε ακούσει το κομμάτι %n φορές. - + You've never listened to this track before. Δεν έχετε ακούσει αυτό το κομμάτι παλιότερα. - + You first listened to it on %1. Το ακούσατε για πρώτη φορά στις %1. - + You've listened to %1 %n time(s). Ακούσατε το %1 %n φορά.Ακούσατε το %1 %n φορές. - + You've never listened to %1 before. Δεν έχετε ακούσει το %1 ποτέ πριν. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 8d3f7c3c62..5cdfeee67e 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -281,12 +281,12 @@ connect and stream from you? Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! Sorry, we could not find any tracks for this album! - + Other Albums by %1 Other Albums by %1 @@ -313,12 +313,12 @@ connect and stream from you? Top Hits - + Related Artists Related Artists - + Albums Albums @@ -338,9 +338,9 @@ connect and stream from you? Sorry, we could not find any top hits for this artist! - - # IN YOUR CHARTS - # IN YOUR CHARTS + + YOUR ARTIST RANK + YOUR ARTIST RANK @@ -4050,47 +4050,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Similar Tracks - + Sorry, but we could not find similar tracks for this song! Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST # PLAYS / ARTIST - - # IN YOUR CHARTS - # IN YOUR CHARTS + + YOUR SONG RANK + YOUR SONG RANK - + You've listened to this track %n time(s). You've listened to this track %n time.You've listened to this track %n times. - + You've never listened to this track before. You've never listened to this track before. - + You first listened to it on %1. You first listened to it on %1. - + You've listened to %1 %n time(s). You've listened to %1 %n time.You've listened to %1 %n times. - + You've never listened to %1 before. You've never listened to %1 before. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index dafc78cff0..f60e8657c7 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -281,12 +281,12 @@ conectarse a usted y transmitir música? No se encontraron otros álbumes de este artista - + Sorry, we could not find any tracks for this album! No se encontraron pistas de este álbum - + Other Albums by %1 Otros álbumes de %1 @@ -313,12 +313,12 @@ conectarse a usted y transmitir música? Grandes éxitos - + Related Artists Artistas relacionados - + Albums Álbumes @@ -338,8 +338,8 @@ conectarse a usted y transmitir música? No se encontraron éxitos de este artista - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4048,47 +4048,47 @@ introduzca su número PIN aquí: TrackInfoWidget - + Similar Tracks Pistas similares - + Sorry, but we could not find similar tracks for this song! No se han encontrado pistas similares - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Ha escuchado esta pista %n vez.Ha escuchado esta pista %n veces. - + You've never listened to this track before. Nunca ha escuchado esta pista antes. - + You first listened to it on %1. Escuchó esta pista por primera vez en %1. - + You've listened to %1 %n time(s). Ha escuchado %1 una vez.Ha escuchado %1 %n veces. - + You've never listened to %1 before. Nunca ha escuchado %1 antes. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 1919d8f4c6..aa9a90a207 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -281,12 +281,12 @@ yhdistää ja toistaa sinulta virtaa? Valitettavasti emme löytänet mitään muita albumeita tältä artistilta! - + Sorry, we could not find any tracks for this album! Valitettavasti emme löytäneet mitään tämän albumin kappaleita! - + Other Albums by %1 Muita artistin %1 levyjä @@ -313,12 +313,12 @@ yhdistää ja toistaa sinulta virtaa? Parhaat hitit - + Related Artists Samankaltaisia artisteja - + Albums Albumit @@ -338,8 +338,8 @@ yhdistää ja toistaa sinulta virtaa? Valitettavasti emme löytäneet yhtään tämän artistin parhaista hiteistä! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4053,47 +4053,47 @@ anna siellä näytetty PIN-koodi tähän: TrackInfoWidget - + Similar Tracks Samankaltaisia kappaleita - + Sorry, but we could not find similar tracks for this song! Valitettavasti emme löytäneet tälle kappaleelle samankaltaisia kappaleita! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Olet kuunnellut tätä kappaletta %n kerran.Olet kuunnellut tätä kappaletta %n kertaa. - + You've never listened to this track before. Et ole kuunnellut tätä kappaletta aiemmin. - + You first listened to it on %1. Kuuntelit sitä ensi kerran %1. - + You've listened to %1 %n time(s). Olet kuunnellut artistia %1 %n kerran.Olet kuunnellut artistia %1 %n kertaa. - + You've never listened to %1 before. Et ole kuunnellut artistia %1 aiemmin. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 9c2dddf1da..d10ffbca9d 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -281,12 +281,12 @@ de se connecter et streamer de vous? Désolé, aucun autre album n'a pu être trouvé pour cet artiste ! - + Sorry, we could not find any tracks for this album! Désolé, nous n'avons pu trouver aucune piste pour cet album ! - + Other Albums by %1 Autres albums de %1 @@ -313,12 +313,12 @@ de se connecter et streamer de vous? Top Hits - + Related Artists Artistes similaires - + Albums Albums @@ -338,8 +338,8 @@ de se connecter et streamer de vous? Désolé, on a pas pu trouver aucun top hit pour cet artiste! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4045,47 +4045,47 @@ saisissez le numéro PIN ici : TrackInfoWidget - + Similar Tracks Piste similaire - + Sorry, but we could not find similar tracks for this song! Désolé, nous n'avons pu trouver aucune piste similaire pour cette chanson ! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Vous avez écouté cette piste %n fois.Vous avez écouté cette piste %n fois. - + You've never listened to this track before. Vous n'avez encore jamais écouté cette piste. - + You first listened to it on %1. Vous l'avez écouté pour la première fois le %1. - + You've listened to %1 %n time(s). Vous avez écouté %1 %n fois.Vous avez écouté %1 %n fois. - + You've never listened to %1 before. Vous n'avez encore jamais écouté %1. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index e506643a6e..47df260576 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -280,12 +280,12 @@ connect and stream from you? Non se atopou ningún outro álbum para este artista! - + Sorry, we could not find any tracks for this album! Non se puido atopar ningunha outra pista para este álbum! - + Other Albums by %1 Outros álbums de %1 @@ -312,12 +312,12 @@ connect and stream from you? Maiores éxitos - + Related Artists Artistas relacionados - + Albums Álbums @@ -337,8 +337,8 @@ connect and stream from you? Non se atopou ningún éxito deste artista! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4047,47 +4047,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Pistas parecidas - + Sorry, but we could not find similar tracks for this song! Non se atopan pistas parecidas a esta canción! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Escoitaches esta pista %n vece(s).Escoitaches esta pista %n vece(s). - + You've never listened to this track before. Nunca antes escoitaras esta pista. - + You first listened to it on %1. Escoitaches esta pista por primeira vez en %1. - + You've listened to %1 %n time(s). Escoitaches %1 %n veces.Escoitaches %1 %n veces. - + You've never listened to %1 before. Nunca antes escoitaras a %1. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 6939aeb7b6..4145e93803 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -280,12 +280,12 @@ connect and stream from you? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -312,12 +312,12 @@ connect and stream from you? - + Related Artists - + Albums @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4032,47 +4032,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 9b614cdf39..92b8ab94e8 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -280,12 +280,12 @@ connect and stream from you? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -312,12 +312,12 @@ connect and stream from you? Top Hits - + Related Artists Kapcsolódó előadók - + Albums Albumok @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4032,47 +4032,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Hasonló zeneszámok - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 67d569f3db..1bb4381e38 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -280,12 +280,12 @@ connect and stream from you? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -312,12 +312,12 @@ connect and stream from you? - + Related Artists - + Albums @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4032,47 +4032,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index ff60340ec1..7d68290c79 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -280,12 +280,12 @@ connect and stream from you? Siamo spiacenti, non è stato possibile trovare altri album di questo artista! - + Sorry, we could not find any tracks for this album! Ci dispiace, ma non abbiamo trovato nessuna traccia di questo album! - + Other Albums by %1 Altri album di %1 @@ -312,12 +312,12 @@ connect and stream from you? Top Hits - + Related Artists Artisti simili - + Albums Album @@ -337,8 +337,8 @@ connect and stream from you? Ci dispiace, ma non abbiamo trovato alcun risultato top per l'artista! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4034,47 +4034,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Tracce simili - + Sorry, but we could not find similar tracks for this song! Ci dispiace, ma non abbiamo trovato tracce simili per questa canzone! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Hai ascoltato questa traccia una volta.Hai ascoltato questa traccia %n volte. - + You've never listened to this track before. Non hai mai ascoltato questa traccia. - + You first listened to it on %1. L'hai ascoltata la prima volta su %1. - + You've listened to %1 %n time(s). Hai ascoltato %1 una volta.Hai ascoltato %1 %n volte. - + You've never listened to %1 before. Non hai mai ascoltato %1. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index e95722a65b..d44b9c9878 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -280,12 +280,12 @@ connect and stream from you? このアーティストのアルバムは他に見つかりませんでした。 - + Sorry, we could not find any tracks for this album! このアルバムの曲は見つかりませんでした。 - + Other Albums by %1 %1の他のアルバム @@ -312,12 +312,12 @@ connect and stream from you? 大ヒット曲 - + Related Artists 似たアーティスト - + Albums アルバム @@ -337,8 +337,8 @@ connect and stream from you? このアーティストの大ヒット曲は見つかりませんでした。 - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4045,47 +4045,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 似ているトラック - + Sorry, but we could not find similar tracks for this song! この曲に似ているトラックが見つかりませんでした。 - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). このトラックは%n回聴いています。 - + You've never listened to this track before. このトラックを一度も聴いていません。 - + You first listened to it on %1. 初めてこの曲を聴いたのは、%1です。 - + You've listened to %1 %n time(s). %1を%n回聴いています。 - + You've never listened to %1 before. %1を一度も聴いていません。 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 8363879a5f..9d4338cc64 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -280,12 +280,12 @@ connect and stream from you? Atsiprašome, neradome jokių kitų šio atlikėjo albumų! - + Sorry, we could not find any tracks for this album! Atsiprašome, neradome jokių takelių iš šio albumo! - + Other Albums by %1 Kiti %1 albumai @@ -312,12 +312,12 @@ connect and stream from you? - + Related Artists Susiję atlikėjai - + Albums Albumai @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4032,47 +4032,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Panašūs takeliai - + Sorry, but we could not find similar tracks for this song! Atsiprašome, neradome jokių į šią dainą panašių takelių! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Jūs klausėtės šio takelio %n kartą.Jūs klausėtės šio takelio %n kartus.Jūs klausėtės šio takelio %n kartų. - + You've never listened to this track before. Jūs niekad anksčiau nesiklausėte šio takelio. - + You first listened to it on %1. Pirmąkart klausėtės jo %1. - + You've listened to %1 %n time(s). Jūs klausėtės %1 %n kartą.Jūs klausėtės %1 %n kartus.Jūs klausėtės %1 %n kartų. - + You've never listened to %1 before. Jūs niekada anksčiau nesiklausėte %1. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index df3588d85b..cfd4fe7962 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -281,12 +281,12 @@ połączyć się i strumieniować od ciebie? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Inne albumy %1 @@ -313,12 +313,12 @@ połączyć się i strumieniować od ciebie? Hity na Topie - + Related Artists Powiązani artyści - + Albums Albumy @@ -338,8 +338,8 @@ połączyć się i strumieniować od ciebie? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4042,47 +4042,47 @@ wprowadź pokazany numer PIN tutaj: TrackInfoWidget - + Similar Tracks Podobne utwory - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Słuchałeś tego utworu %n raz.Słuchałeś tego utworu %n razy.Słuchałeś tego utworu %n razy. - + You've never listened to this track before. Nie słuchałeś wcześniej tego utworu. - + You first listened to it on %1. Pierwszy raz słuchałeś tego utworu %1. - + You've listened to %1 %n time(s). Słuchałeś %1 %n raz.Słuchałeś %1 %n razy.Słuchałeś %1 %n razy. - + You've never listened to %1 before. Nie słuchałeś wcześniej %1. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 94b46cba17..9c4146da4b 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -281,12 +281,12 @@ se conecte e faça o stream de você? Desculpe, mas não conseguimos encontrar outro álbum para este artista! - + Sorry, we could not find any tracks for this album! Desculpe, mas não conseguimos encontrar outras faixas para este álbum! - + Other Albums by %1 Outros álbuns de %1 @@ -313,12 +313,12 @@ se conecte e faça o stream de você? Mais Tocadas - + Related Artists Artistas Relacionados - + Albums Álbuns @@ -338,8 +338,8 @@ se conecte e faça o stream de você? Desculpe, mas não conseguimos encontrar outras faixas mais tocadas deste artista! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4042,47 +4042,47 @@ colocar o número PIN mostrado aqui: TrackInfoWidget - + Similar Tracks Faixas Similares - + Sorry, but we could not find similar tracks for this song! Desculpe, mas não conseguimos encontrar faixas similares para esta música! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Você ouviu esta faixa %n vez.Você ouviu esta faixa %n vezes. - + You've never listened to this track before. Você nunca ouviu esta faixa antes. - + You first listened to it on %1. Você ouviu pela primeira vez em %1. - + You've listened to %1 %n time(s). Você ouviu %1 %n vez.Você ouviu %1 %n vezes. - + You've never listened to %1 before. Você nunca ouviu %1 antes. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index dd502321b8..85a06287c9 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -284,12 +284,12 @@ connect and stream from you? К сожалению, мы не смогли найти другие альбомы этого исполнителя! - + Sorry, we could not find any tracks for this album! К сожалению, мы не смогли найти никаких треков для этого альбома! - + Other Albums by %1 Другие альбомы %1 @@ -316,12 +316,12 @@ connect and stream from you? Хиты - + Related Artists Похожие исполнители - + Albums Альбомы @@ -341,8 +341,8 @@ connect and stream from you? К сожалению, мы не смогли найти никаких хитов этого исполнителя! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4048,47 +4048,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Похожие Песни - + Sorry, but we could not find similar tracks for this song! Извините, но мы не смогли найти похожие на эту песни ! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). Вы слушали эту песню %n раз.Вы слушали эту песню %n раз.Вы слушали эту песню %n раз. - + You've never listened to this track before. Вы никогда не слушали эту песню раньше. - + You first listened to it on %1. Первый раз слушали эту песню %1. - + You've listened to %1 %n time(s). Вы слушали %1 %n раз.Вы слушали %1 %n раза.Вы слушали %1 %n раз. - + You've never listened to %1 before. Вы никогда не слушали %1 до этого. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 306901b2b5..b5c84b4c2e 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -281,12 +281,12 @@ ansluta och strömma från dig? Tyvärr! Det gick inte hitta några andra album av denna artisten - + Sorry, we could not find any tracks for this album! Tyvärr! Det gick inte hitta några spår från detta albumet! - + Other Albums by %1 Andra album av %1 @@ -313,12 +313,12 @@ ansluta och strömma från dig? Största hits - + Related Artists Relaterade artister - + Albums Album @@ -338,9 +338,9 @@ ansluta och strömma från dig? Tyvärr! Det gick inte hitta några tophits av denna artisten - - # IN YOUR CHARTS - # I DINA LISTOR + + YOUR ARTIST RANK + @@ -450,7 +450,7 @@ ansluta och strömma från dig? Unknown - + Okänd @@ -458,7 +458,7 @@ ansluta och strömma från dig? Sorry, your filter '%1' did not match any results. - + Tyvärr kunde inte ditt filter '%1' matcha några resultat @@ -466,57 +466,57 @@ ansluta och strömma från dig? Composer - + Kompositör Age - + Ålder Year - + År Duration - + Längd Bitrate - + Bitrate Composer: - + Kompositör: Duration: - + Längd: Bitrate: - + Bitrate: Year: - + År: Age: - + Ålder: %1 kbps - + %1 kbps @@ -607,12 +607,12 @@ ansluta och strömma från dig? Dashboard - + Dashboard An overview of your recent activity - + Överblick över din senaste aktivitet @@ -790,12 +790,12 @@ ansluta och strömma från dig? This playlist is currently empty. - + Denna spellistan är för närvarande tom This playlist is currently empty. Add some tracks to it and enjoy the music! - + Denna spellistan är för närvarande tom. Lägg till några spår och njut av musiken! @@ -2332,12 +2332,12 @@ och radiostationer baserat på din personliga profil Use this to force Spotify to never announce listening data to Social Networks - + Använder detta för att tvinga Spotify att aldrig tlilkännage lyssningsinformation på sociala nätverk Always run in Private Mode - + Kör alltid i Privat läge @@ -2934,7 +2934,7 @@ username@jabber.org &Love - &Kärlek + &Älska @@ -4048,47 +4048,47 @@ anger du PIN-koden här: TrackInfoWidget - + Similar Tracks Liknande spår - + Sorry, but we could not find similar tracks for this song! Tyvärr! Det gick inte hitta några spår som liknande denna låten! - + # PLAYS / ARTIST # UPPSPELNINGAR / ARTIST - - # IN YOUR CHARTS - # I DINA LISTOR + + YOUR SONG RANK + - + You've listened to this track %n time(s). Du har lyssnat på detta spåret %n gånger.Du har lyssnat på detta spåret %n gånger - + You've never listened to this track before. Du har inte lyssnat på detta spåret tidigare. - + You first listened to it on %1. Du har lyssnat på det på %1. - + You've listened to %1 %n time(s). Du har lyssnat på %1 %n gång.Du har lyssnat på %1 %n gånger. - + You've never listened to %1 before. Du har inte lyssnat på %1 tidigare. diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 988084b3b5..86a105d1bd 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -280,12 +280,12 @@ connect and stream from you? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Diğer %1 Albümleri @@ -312,12 +312,12 @@ connect and stream from you? En Çok Dinlenenler - + Related Artists Benzer Sanatçılar - + Albums Albümler @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4032,47 +4032,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index fc4a0e19b4..e5f459ed21 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -280,12 +280,12 @@ connect and stream from you? 抱歉,找到此艺术家的其他专辑! - + Sorry, we could not find any tracks for this album! 抱歉,没有找到这张专辑的其他歌曲! - + Other Albums by %1 %1 的其他专辑 @@ -312,12 +312,12 @@ connect and stream from you? 最热歌曲 - + Related Artists 相关艺人 - + Albums 专辑 @@ -337,8 +337,8 @@ connect and stream from you? 抱歉,没有找到该艺术家的任何人气歌曲! - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4043,47 +4043,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 相似歌曲 - + Sorry, but we could not find similar tracks for this song! 抱歉,无法找到与这首歌类似的歌曲! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). 你已经收听过此歌曲 %n 遍。 - + You've never listened to this track before. 你之前从未听过此歌曲。 - + You first listened to it on %1. 你第一次听的是 %1. - + You've listened to %1 %n time(s). 你已经听过 %1 有 %n 遍了。 - + You've never listened to %1 before. 你之前从未听过 %1。 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index e789c98c59..3578b4649a 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -280,12 +280,12 @@ connect and stream from you? - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 列出所有其他專輯,依 %1 @@ -312,12 +312,12 @@ connect and stream from you? 流行精選 - + Related Artists 相關演出者 - + Albums 專輯 @@ -337,8 +337,8 @@ connect and stream from you? - - # IN YOUR CHARTS + + YOUR ARTIST RANK @@ -4032,47 +4032,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - - # IN YOUR CHARTS + + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. From 1af86b32575ef84e407b753337bf5204c36d7b3d Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Fri, 14 Jun 2013 20:35:13 -0400 Subject: [PATCH 401/565] More font size tweaks --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 4 ++-- src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 9981aad83b..219deca36e 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -126,7 +126,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->biography->font(); - f.setPointSize( f.pointSize() + 4 ); + f.setPointSize( f.pointSize() + 3 ); f.setFamily( "Titillium Web" ); QPalette p = ui->biography->palette(); @@ -149,7 +149,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->artistLabel->font(); f.setFamily( "Titillium Web" ); - f.setPointSize( TomahawkUtils::defaultFontSize() + 20 ); + f.setPointSize( TomahawkUtils::defaultFontSize() + 16 ); f.setBold( true ); QPalette p = ui->artistLabel->palette(); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 6bf243fd54..f01aad0ebf 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -71,7 +71,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->trackLabel->font(); f.setFamily( "Titillium Web" ); - f.setPointSize( TomahawkUtils::defaultFontSize() + 20 ); + f.setPointSize( TomahawkUtils::defaultFontSize() + 16 ); f.setBold( true ); QPalette p = ui->trackLabel->palette(); From 4145bf14f8d50fd999f1ff8ed4d981055f98fdfc Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 04:05:16 +0200 Subject: [PATCH 402/565] * Fixed Artist page's header size and moved font definitions to .ui file. --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 3 --- .../widgets/infowidgets/ArtistInfoWidget.ui | 11 ++++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 219deca36e..8e29641892 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -126,7 +126,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->biography->font(); - f.setPointSize( f.pointSize() + 3 ); f.setFamily( "Titillium Web" ); QPalette p = ui->biography->palette(); @@ -149,8 +148,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->artistLabel->font(); f.setFamily( "Titillium Web" ); - f.setPointSize( TomahawkUtils::defaultFontSize() + 16 ); - f.setBold( true ); QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_LABEL ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 7a800570f1..1d2d478028 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -7,7 +7,7 @@ 0 0 965 - 822 + 831 @@ -77,7 +77,7 @@ - 20 + 26 75 true @@ -101,9 +101,14 @@ 0 - 220 + 190 + + + 13 + + Qt::ScrollBarAlwaysOff From d1e97b1dec476c4dd17b1e7e28a001e569a186f2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 04:05:46 +0200 Subject: [PATCH 403/565] * Moved Track page's font definitions to .ui file. --- src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp | 3 --- src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index f01aad0ebf..43508e2ad7 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -71,8 +71,6 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->trackLabel->font(); f.setFamily( "Titillium Web" ); - f.setPointSize( TomahawkUtils::defaultFontSize() + 16 ); - f.setBold( true ); QPalette p = ui->trackLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_LABEL ); @@ -84,7 +82,6 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->artistLabel->font(); f.setFamily( "Titillium Web" ); - f.setPointSize( TomahawkUtils::defaultFontSize() + 10 ); QPalette p = ui->artistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index 404249f7d4..7735a27eea 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -77,7 +77,7 @@ - 20 + 26 75 true @@ -100,7 +100,7 @@ - 16 + 20 @@ -155,7 +155,7 @@ - 220 + 440 16777215 From ff08efbdf8d85cc632ac33910e56aee78246a1d5 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 05:11:07 +0200 Subject: [PATCH 404/565] * Clean up for Track page. --- .../widgets/infowidgets/TrackInfoWidget.cpp | 118 +++++++++--------- .../widgets/infowidgets/TrackInfoWidget.ui | 11 +- 2 files changed, 62 insertions(+), 67 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 43508e2ad7..b8f45653f5 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -44,11 +44,6 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par QWidget* widget = new QWidget; ui->setupUi( widget ); - ui->artistLabel->setContentsMargins( 6, 2, 6, 2 ); - ui->artistLabel->setElideMode( Qt::ElideMiddle ); - ui->artistLabel->setType( QueryLabel::Artist ); - connect( ui->artistLabel, SIGNAL( clickedArtist() ), SLOT( onArtistClicked() ) ); - ui->statsLabel->setStyleSheet( "QLabel { background-image:url(); border: 2px solid #dddddd; background-color: #faf9f9; border-radius: 4px; padding: 12px; }" ); ui->statsLabel->setVisible( false ); @@ -56,17 +51,43 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par ui->lyricsView->setFrameShape( QFrame::NoFrame ); ui->lyricsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->lyricsView->setVisible( false ); // FIXME eventually - - ui->similarTracksView->setAutoResize( true ); - ui->similarTracksView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + TomahawkStyle::styleScrollBar( ui->lyricsView->verticalScrollBar() ); ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); -// TomahawkUtils::styleScrollBar( ui->similarTracksView->verticalScrollBar() ); - TomahawkStyle::styleScrollBar( ui->lyricsView->verticalScrollBar() ); + m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Original, QSize( 48, 48 ) ); + ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Grid, ui->cover->size() ) ); + ui->cover->setShowText( false ); + + QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); + m_playStatsGauge = new StatsGauge( ui->statsWidget ); + m_playStatsGauge->setText( tr( "# PLAYS / ARTIST" ) ); + m_playStatsTotalGauge = new StatsGauge( ui->statsWidget ); + m_playStatsTotalGauge->setText( tr( "YOUR SONG RANK" ) ); + m_playStatsTotalGauge->setInvertedAppearance( true ); -// ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; } QListView::item { background-color: transparent; }" ); + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + l->addWidget( m_playStatsGauge ); + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + l->addWidget( m_playStatsTotalGauge ); + l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); + ui->statsWidget->setLayout( l ); + TomahawkUtils::unmarginLayout( l ); + + { + m_relatedTracksModel = new PlayableModel( ui->similarTracksView ); + ui->similarTracksView->setPlayableModel( m_relatedTracksModel ); + ui->similarTracksView->proxyModel()->sort( -1 ); + ui->similarTracksView->setEmptyTip( tr( "Sorry, but we could not find similar tracks for this song!" ) ); + ui->similarTracksView->setAutoResize( true ); + ui->similarTracksView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; }" ); + // TomahawkUtils::styleScrollBar( ui->similarTracksView->verticalScrollBar() ); + // ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; } QListView::item { background-color: transparent; }" ); + + TomahawkStyle::stylePageFrame( ui->frame ); +} { QFont f = ui->trackLabel->font(); @@ -80,6 +101,11 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par } { + ui->artistLabel->setContentsMargins( 6, 2, 6, 2 ); + ui->artistLabel->setElideMode( Qt::ElideMiddle ); + ui->artistLabel->setType( QueryLabel::Artist ); + connect( ui->artistLabel, SIGNAL( clickedArtist() ), SLOT( onArtistClicked() ) ); + QFont f = ui->artistLabel->font(); f.setFamily( "Titillium Web" ); @@ -116,53 +142,31 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par ui->lyricsView->setPalette( p ); } - m_relatedTracksModel = new PlayableModel( ui->similarTracksView ); - ui->similarTracksView->setPlayableModel( m_relatedTracksModel ); - ui->similarTracksView->proxyModel()->sort( -1 ); - ui->similarTracksView->setEmptyTip( tr( "Sorry, but we could not find similar tracks for this song!" ) ); - - m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Original, QSize( 48, 48 ) ); - ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultTrackImage, TomahawkUtils::Grid, ui->cover->size() ) ); - ui->cover->setShowText( false ); - - m_scrollArea = new QScrollArea(); - m_scrollArea->setWidgetResizable( true ); - m_scrollArea->setWidget( widget ); - m_scrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); - - QPalette pal = palette(); - pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); - m_scrollArea->setPalette( pal ); - m_scrollArea->setAutoFillBackground( true ); - m_scrollArea->setFrameShape( QFrame::NoFrame ); - m_scrollArea->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); - ui->widget->setPalette( pal ); - ui->widget->setAutoFillBackground( true ); - - QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); - m_playStatsGauge = new StatsGauge( ui->statsWidget ); - m_playStatsGauge->setText( tr( "# PLAYS / ARTIST" ) ); - m_playStatsTotalGauge = new StatsGauge( ui->statsWidget ); - m_playStatsTotalGauge->setText( tr( "YOUR SONG RANK" ) ); - m_playStatsTotalGauge->setInvertedAppearance( true ); - - l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); - l->addWidget( m_playStatsGauge ); - l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); - l->addWidget( m_playStatsTotalGauge ); - l->addSpacerItem( new QSpacerItem( 0, 1, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) ); - ui->statsWidget->setLayout( l ); - TomahawkUtils::unmarginLayout( l ); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget( m_scrollArea ); - setLayout( layout ); - TomahawkUtils::unmarginLayout( layout ); + { + m_scrollArea = new QScrollArea(); + m_scrollArea->setWidgetResizable( true ); + m_scrollArea->setWidget( widget ); + m_scrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); + m_scrollArea->setPalette( pal ); + m_scrollArea->setAutoFillBackground( true ); + m_scrollArea->setFrameShape( QFrame::NoFrame ); + m_scrollArea->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget( m_scrollArea ); + setLayout( layout ); + TomahawkUtils::unmarginLayout( layout ); + } - ui->similarTracksView->setStyleSheet( "QListView { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->frame ); + { + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + ui->widget->setPalette( pal ); + ui->widget->setAutoFillBackground( true ); + } load( query ); } diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index 7735a27eea..0c4e7ea6ba 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -55,16 +55,7 @@ 0 - - 0 - - - 8 - - - 0 - - + 0 From 1d742426703ade022a61e772e811bacfcc73f958 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 05:11:29 +0200 Subject: [PATCH 405/565] * Clean up for Artist page. --- .../widgets/infowidgets/ArtistInfoWidget.cpp | 162 +++++++++--------- .../widgets/infowidgets/ArtistInfoWidget.ui | 13 +- 2 files changed, 86 insertions(+), 89 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 8e29641892..f8fbcdd869 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -57,58 +57,11 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* artist->loadStats(); connect( artist.data(), SIGNAL( statsLoaded() ), SLOT( onArtistStatsLoaded() ) ); - m_relatedModel = new PlayableModel( ui->relatedArtists ); - ui->relatedArtists->setPlayableModel( m_relatedModel ); - ui->relatedArtists->proxyModel()->sort( -1 ); - ui->relatedArtists->setEmptyTip( tr( "Sorry, we could not find any related artists!" ) ); - - m_topHitsModel = new PlaylistModel( ui->topHits ); - ui->topHits->proxyModel()->setStyle( PlayableProxyModel::Short ); - ui->topHits->setPlayableModel( m_topHitsModel ); - ui->topHits->setSortingEnabled( false ); - ui->topHits->setEmptyTip( tr( "Sorry, we could not find any top hits for this artist!" ) ); - ui->topHits->setAutoResize( true ); - - AlbumItemDelegate* del = new AlbumItemDelegate( ui->topHits, ui->topHits->proxyModel() ); - ui->topHits->setPlaylistItemDelegate( del ); - - ui->relatedArtists->setAutoFitItems( true ); -/* ui->relatedArtists->setWrapping( false ); - ui->relatedArtists->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - ui->relatedArtists->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ - ui->relatedArtists->delegate()->setItemSize( QSize( 170, 170 ) ); - - ui->albums->setAutoResize( true ); - ui->albums->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); -/* ui->albums->setWrapping( false ); - ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ - ui->albums->delegate()->setItemSize( QSize( 170, 170 ) ); - ui->albums->proxyModel()->setHideDupeItems( true ); - - m_albumsModel = new PlayableModel( ui->albums ); - ui->albums->setPlayableModel( m_albumsModel ); - ui->albums->proxyModel()->sort( -1 ); - ui->albums->setEmptyTip( tr( "Sorry, we could not find any albums for this artist!" ) ); - ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); ui->lineAbove2->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); ui->lineBelow2->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); - { - QPalette p = ui->topHits->palette(); - p.setColor( QPalette::Text, TomahawkStyle::PAGE_TRACKLIST_TRACK_SOLVED ); - p.setColor( QPalette::BrightText, TomahawkStyle::PAGE_TRACKLIST_TRACK_UNRESOLVED ); - p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_TRACKLIST_NUMBER ); - p.setColor( QPalette::Highlight, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT ); - p.setColor( QPalette::HighlightedText, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT_TEXT ); - - ui->topHits->setPalette( p ); - ui->topHits->setAlternatingRowColors( false ); - ui->topHits->setFrameShape( QFrame::NoFrame ); - ui->topHits->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - } - QHBoxLayout* l = new QHBoxLayout( ui->statsWidget ); m_playStatsGauge = new StatsGauge( ui->statsWidget ); m_playStatsGauge->setText( tr( "YOUR ARTIST RANK" ) ); @@ -124,6 +77,66 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::Grid, ui->cover->size() ) ); ui->cover->setShowText( false ); + { + m_relatedModel = new PlayableModel( ui->relatedArtists ); + ui->relatedArtists->setPlayableModel( m_relatedModel ); + ui->relatedArtists->proxyModel()->sort( -1 ); + ui->relatedArtists->setEmptyTip( tr( "Sorry, we could not find any related artists!" ) ); + + ui->relatedArtists->setAutoFitItems( true ); + /* ui->relatedArtists->setWrapping( false ); + ui->relatedArtists->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + ui->relatedArtists->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ + ui->relatedArtists->delegate()->setItemSize( QSize( 170, 170 ) ); + + ui->relatedArtists->setStyleSheet( "QListView { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->artistFrame ); + TomahawkStyle::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); + } + + { + ui->albums->setAutoResize( true ); + ui->albums->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + /* ui->albums->setWrapping( false ); + ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ + ui->albums->delegate()->setItemSize( QSize( 170, 170 ) ); + ui->albums->proxyModel()->setHideDupeItems( true ); + + m_albumsModel = new PlayableModel( ui->albums ); + ui->albums->setPlayableModel( m_albumsModel ); + ui->albums->proxyModel()->sort( -1 ); + ui->albums->setEmptyTip( tr( "Sorry, we could not find any albums for this artist!" ) ); + + ui->albums->setStyleSheet( QString( "QListView { background-color: %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); + TomahawkStyle::stylePageFrame( ui->albumFrame ); + TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); + } + + { + m_topHitsModel = new PlaylistModel( ui->topHits ); + AlbumItemDelegate* del = new AlbumItemDelegate( ui->topHits, ui->topHits->proxyModel() ); + ui->topHits->setPlaylistItemDelegate( del ); + ui->topHits->proxyModel()->setStyle( PlayableProxyModel::Short ); + ui->topHits->setPlayableModel( m_topHitsModel ); + ui->topHits->setSortingEnabled( false ); + ui->topHits->setEmptyTip( tr( "Sorry, we could not find any top hits for this artist!" ) ); + ui->topHits->setAutoResize( true ); + ui->topHits->setAlternatingRowColors( false ); + + QPalette p = ui->topHits->palette(); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TRACKLIST_TRACK_SOLVED ); + p.setColor( QPalette::BrightText, TomahawkStyle::PAGE_TRACKLIST_TRACK_UNRESOLVED ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_TRACKLIST_NUMBER ); + p.setColor( QPalette::Highlight, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT ); + p.setColor( QPalette::HighlightedText, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT_TEXT ); + + ui->topHits->setPalette( p ); + ui->topHits->setFrameShape( QFrame::NoFrame ); + ui->topHits->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->topHits->setStyleSheet( "QTreeView#topHits { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->trackFrame ); + } + { QFont f = ui->biography->font(); f.setFamily( "Titillium Web" ); @@ -182,38 +195,31 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* ui->albumLabel->setPalette( p ); } - QScrollArea* area = new QScrollArea(); - area->setWidgetResizable( true ); - area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); - area->setWidget( widget ); - - QPalette pal = palette(); - pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); - area->setPalette( pal ); - area->setAutoFillBackground( true ); - area->setFrameShape( QFrame::NoFrame ); - area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); - ui->widget->setPalette( pal ); - ui->widget->setAutoFillBackground( true ); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget( area ); - setLayout( layout ); - TomahawkUtils::unmarginLayout( layout ); - - TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); - TomahawkStyle::styleScrollBar( ui->relatedArtists->verticalScrollBar() ); - - ui->albums->setStyleSheet( QString( "QListView { background-color: %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); - TomahawkStyle::stylePageFrame( ui->albumFrame ); - - ui->relatedArtists->setStyleSheet( "QListView { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->artistFrame ); + { + QScrollArea* area = new QScrollArea(); + area->setWidgetResizable( true ); + area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + area->setWidget( widget ); + + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); + area->setPalette( pal ); + area->setAutoFillBackground( true ); + area->setFrameShape( QFrame::NoFrame ); + area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget( area ); + setLayout( layout ); + TomahawkUtils::unmarginLayout( layout ); + } - ui->topHits->setStyleSheet( "QTreeView#topHits { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->trackFrame ); + { + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + ui->widget->setPalette( pal ); + ui->widget->setAutoFillBackground( true ); + } MetaPlaylistInterface* mpl = new MetaPlaylistInterface(); mpl->addChildInterface( ui->relatedArtists->playlistInterface() ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 1d2d478028..7cb9174c87 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -13,7 +13,7 @@ Form - + 0 @@ -55,16 +55,7 @@ 8 - - 0 - - - 8 - - - 0 - - + 0 From 5678f612111c6e002251a5d57df616d1629f33eb Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 05:11:57 +0200 Subject: [PATCH 406/565] * New layout for Album page. --- .../widgets/infowidgets/AlbumInfoWidget.cpp | 164 ++++++---- .../widgets/infowidgets/AlbumInfoWidget.ui | 300 +++++++++++------- 2 files changed, 277 insertions(+), 187 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index 6f22bbb87c..c27f85b0e6 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -51,84 +51,118 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par QWidget* widget = new QWidget; ui->setupUi( widget ); - m_albumsModel = new PlayableModel( ui->albums ); - ui->albums->setPlayableModel( m_albumsModel ); - ui->albums->setEmptyTip( tr( "Sorry, we could not find any other albums for this artist!" ) ); - - - m_tracksModel = new TreeModel( ui->tracks ); - m_tracksModel->setMode( Mixed ); - - QPalette trackViewPal = ui->tracks->palette(); - trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); - trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); - trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); - trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); - - ui->tracks->setPalette( trackViewPal ); - ui->tracks->setAlternatingRowColors( false ); - ui->tracks->setRootIsDecorated( false ); - ui->tracks->setEmptyTip( tr( "Sorry, we could not find any tracks for this album!" ) ); - ui->tracks->proxyModel()->setStyle( PlayableProxyModel::Large ); - ui->tracks->setAutoResize( true ); - ui->tracks->setPlayableModel( m_tracksModel ); - - AlbumItemDelegate* del = new AlbumItemDelegate( ui->tracks, ui->tracks->proxyModel() ); - ui->tracks->setPlaylistItemDelegate( del ); - -/* ui->albums->setAutoFitItems( false ); - ui->albums->setWrapping( false ); - ui->albums->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ - ui->albums->delegate()->setItemSize( QSize( 170, 170 ) ); - ui->albums->proxyModel()->setHideDupeItems( true ); - - ui->tracks->setFrameShape( QFrame::NoFrame ); - ui->tracks->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::Original, QSize( 48, 48 ) ); ui->cover->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::Grid, ui->cover->size() ) ); ui->cover->setShowText( true ); - ui->biography->setFrameShape( QFrame::NoFrame ); - ui->biography->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); + ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); + ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + + { + m_tracksModel = new TreeModel( ui->tracks ); + m_tracksModel->setMode( Mixed ); + + AlbumItemDelegate* del = new AlbumItemDelegate( ui->tracks, ui->tracks->proxyModel() ); + ui->tracks->setPlaylistItemDelegate( del ); + ui->tracks->setEmptyTip( tr( "Sorry, we could not find any tracks for this album!" ) ); + ui->tracks->proxyModel()->setStyle( PlayableProxyModel::Large ); + ui->tracks->setAutoResize( true ); + ui->tracks->setPlayableModel( m_tracksModel ); + ui->tracks->setAlternatingRowColors( false ); + + QPalette p = ui->tracks->palette(); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TRACKLIST_TRACK_SOLVED ); + p.setColor( QPalette::BrightText, TomahawkStyle::PAGE_TRACKLIST_TRACK_UNRESOLVED ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_TRACKLIST_NUMBER ); + p.setColor( QPalette::Highlight, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT ); + p.setColor( QPalette::HighlightedText, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT_TEXT ); + + ui->tracks->setPalette( p ); + ui->tracks->setFrameShape( QFrame::NoFrame ); + ui->tracks->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->tracks->setStyleSheet( "QTreeView#tracks { background-color: transparent; }" ); + + TomahawkStyle::styleScrollBar( ui->tracks->horizontalScrollBar() ); + TomahawkStyle::stylePageFrame( ui->trackFrame ); + } + + { + m_albumsModel = new PlayableModel( ui->albums ); + ui->albums->setPlayableModel( m_albumsModel ); + ui->albums->setEmptyTip( tr( "Sorry, we could not find any other albums for this artist!" ) ); + + /* ui->albums->setAutoFitItems( true ); + * ui->albums->setWrapping( false ); + * ui->albums->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + * ui->albums->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );*/ + ui->albums->delegate()->setItemSize( QSize( 170, 170 ) ); + ui->albums->proxyModel()->setHideDupeItems( true ); + + ui->albums->setStyleSheet( "QListView { background-color: transparent; }" ); + TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); + TomahawkStyle::stylePageFrame( ui->albumFrame ); + } + + { + QFont f = ui->biography->font(); + f.setFamily( "Titillium Web" ); - QPalette p = ui->biography->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); - p.setColor( QPalette::Text, TomahawkStyle::PAGE_TEXT ); + QPalette p = ui->biography->palette(); + p.setColor( QPalette::Text, TomahawkStyle::HEADER_TEXT ); - ui->biography->setPalette( p ); - ui->label->setPalette( p ); - ui->label_2->setPalette( p ); + ui->biography->setFont( f ); + ui->biography->setPalette( p ); + ui->biography->setOpenLinks( false ); + ui->biography->setOpenExternalLinks( true ); + ui->biography->setFrameShape( QFrame::NoFrame ); + ui->biography->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - QScrollArea* area = new QScrollArea(); - area->setWidgetResizable( true ); - area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); - area->setWidget( widget ); + ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); + ui->biography->document()->setDefaultStyleSheet( QString( "a { text-decoration: none; font-weight: bold; color: %1; }" ).arg( TomahawkStyle::HEADER_LINK.name() ) ); + TomahawkStyle::styleScrollBar( ui->biography->verticalScrollBar() ); - QPalette pal = palette(); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); - area->setPalette( pal ); - area->setAutoFillBackground( true ); - area->setFrameShape( QFrame::NoFrame ); - area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); +// connect( ui->biography, SIGNAL( anchorClicked( QUrl ) ), SLOT( onBiographyLinkClicked( QUrl ) ) ); + } - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget( area ); - setLayout( layout ); - TomahawkUtils::unmarginLayout( layout ); + { + QFont f = ui->label->font(); + f.setBold( false ); + f.setFamily( "Fauna One" ); - TomahawkStyle::styleScrollBar( ui->tracks->horizontalScrollBar() ); - TomahawkStyle::styleScrollBar( ui->albums->verticalScrollBar() ); + QPalette p = ui->label->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); - ui->biography->setStyleSheet( "QTextBrowser#biography { background-color: transparent; }" ); + ui->label->setFont( f ); + ui->label_2->setFont( f ); + ui->label->setPalette( p ); + ui->label_2->setPalette( p ); + } - ui->albums->setStyleSheet( "QListView { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->albumFrame ); + { + QScrollArea* area = new QScrollArea(); + area->setWidgetResizable( true ); + area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + area->setWidget( widget ); + + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); + area->setPalette( pal ); + area->setAutoFillBackground( true ); + area->setFrameShape( QFrame::NoFrame ); + area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget( area ); + setLayout( layout ); + TomahawkUtils::unmarginLayout( layout ); + } - ui->tracks->setStyleSheet( "QTreeView#tracks { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->trackFrame ); + { + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + ui->widget->setPalette( pal ); + ui->widget->setAutoFillBackground( true ); + } MetaPlaylistInterface* mpl = new MetaPlaylistInterface(); mpl->addChildInterface( ui->tracks->playlistInterface() ); diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui index ebe54566d5..cbf7b8e731 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui @@ -7,18 +7,18 @@ 0 0 965 - 912 + 591 Form - + - 16 + 0 - 12 + 0 @@ -26,7 +26,7 @@ 16 - 0 + 12 @@ -61,7 +61,7 @@ 0 - 240 + 190 @@ -72,142 +72,198 @@ - - - 0 + + + + 16777215 + 1 + - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 8 - - - 4 - - - 8 - - - 8 - - - - - - 18 - 75 - true - - - - Tracklist - - - 0 - - - - - - - - 0 - 0 - - - - true - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - + + Qt::Horizontal + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 16 + + + 0 + + + 16 + + + - 8 + 12 - 4 + 0 - 8 + 12 - 4 + 0 - - - - Arial - 18 - 75 - true - + + + QFrame::StyledPanel - - Other Albums - - - 0 + + QFrame::Raised + + + 4 + + + 8 + + + 4 + + + 8 + + + 8 + + + + + + 20 + 75 + true + + + + Tracklist + + + 0 + + + + + + + + 0 + 0 + + + + true + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + - - - - 0 - 0 - + + + QFrame::StyledPanel - - - 0 - 190 - + + QFrame::Raised + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + Arial + 20 + 75 + true + + + + Other Albums + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 190 + + + + + - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - + + + From 10cb39dd2e379ba6ef79e8eaf74b5853779cec54 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 05:16:54 +0200 Subject: [PATCH 407/565] * Fixed albums area on artist page growing too big. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 7cb9174c87..99bdf74983 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -13,7 +13,7 @@ Form - + 0 From 06dca3111f5c8ee84433b0e600f0c96f27a16ad3 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 07:23:40 +0200 Subject: [PATCH 408/565] * Nitpicky fix. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index f8fbcdd869..5f4680b356 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -188,7 +188,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* f.setBold( false ); f.setFamily( "Fauna One" ); - QPalette p = ui->biography->palette(); + QPalette p = ui->albumLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); ui->albumLabel->setFont( f ); From 36ae09b698c61410bea79cb65b579338b4d16fe2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 07:23:54 +0200 Subject: [PATCH 409/565] * New layout for Dashboard. --- src/libtomahawk/widgets/Dashboard.cpp | 200 +++++++++------ src/libtomahawk/widgets/Dashboard.ui | 357 ++++++++++++++++---------- 2 files changed, 338 insertions(+), 219 deletions(-) diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index b024965c16..145f7891df 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -61,83 +61,127 @@ Dashboard::Dashboard( QWidget* parent ) m_header->setCaption( tr( "Dashboard" ) ); m_header->setDescription( tr( "An overview of your recent activity" ) ); - RecentPlaylistsModel* model = new RecentPlaylistsModel( HISTORY_PLAYLIST_ITEMS, this ); - - QPalette trackViewPal = ui->tracksView->palette(); - trackViewPal.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); - trackViewPal.setColor( QPalette::Text, TomahawkStyle::PAGE_FOREGROUND ); - trackViewPal.setColor( QPalette::Highlight, QColor( "#252020" ) ); - trackViewPal.setColor( QPalette::HighlightedText, Qt::white ); - - ui->playlistWidget->setFrameShape( QFrame::NoFrame ); - ui->playlistWidget->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - ui->playlistWidget->setItemDelegate( new PlaylistDelegate() ); - ui->playlistWidget->setModel( model ); - ui->playlistWidget->overlay()->resize( 380, 86 ); - ui->playlistWidget->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); - ui->playlistWidget->setPalette( trackViewPal ); - ui->playlistWidget->setMinimumHeight( 400 ); - updatePlaylists(); - - m_tracksModel = new RecentlyPlayedModel( ui->tracksView, HISTORY_TRACK_ITEMS ); - ui->tracksView->proxyModel()->setStyle( PlayableProxyModel::ShortWithAvatars ); - ui->tracksView->overlay()->setEnabled( false ); - ui->tracksView->setPlaylistModel( m_tracksModel ); - ui->tracksView->setAutoResize( true ); - m_tracksModel->setSource( source_ptr() ); - - ui->tracksView->setPalette( trackViewPal ); - ui->tracksView->setAlternatingRowColors( false ); - ui->tracksView->setFrameShape( QFrame::NoFrame ); - ui->tracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - m_recentAlbumsModel = new AlbumModel( ui->additionsView ); - ui->additionsView->setPlayableModel( m_recentAlbumsModel ); - ui->additionsView->proxyModel()->sort( -1 ); - - QScrollArea* area = new QScrollArea(); - area->setWidgetResizable( true ); - area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); - area->setWidget( widget ); - - QPalette pal = palette(); - // background: qradialgradient(cx: 0.5, cy: -1.8, fx: 0.5, fy: 0, radius: 2, stop: 0 %1, stop: 1 %2); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); - area->setPalette( pal ); - area->setAutoFillBackground( true ); - area->setFrameShape( QFrame::NoFrame ); - area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget( m_header ); - layout->addWidget( area ); - setLayout( layout ); - TomahawkUtils::unmarginLayout( layout ); - - TomahawkStyle::styleScrollBar( ui->playlistWidget->verticalScrollBar() ); - TomahawkStyle::styleScrollBar( ui->additionsView->verticalScrollBar() ); - - QFont f; - f.setBold( true ); - QFontMetrics fm( f ); - ui->tracksView->setMinimumWidth( fm.width( tr( "Recently played tracks" ) ) * 2 ); - - QPalette p = ui->label->palette(); - p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_FOREGROUND ); - p.setColor( QPalette::Text, TomahawkStyle::PAGE_TEXT ); - - ui->label->setPalette( p ); - ui->label_2->setPalette( p ); - ui->label_3->setPalette( p ); - - ui->playlistWidget->setStyleSheet( "QListView { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->playlistFrame ); - - ui->additionsView->setStyleSheet( "QListView { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->additionsFrame ); - - ui->tracksView->setStyleSheet( "QTreeView { background-color: transparent; }" ); - TomahawkStyle::stylePageFrame( ui->trackFrame ); + ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); + + { + m_tracksModel = new RecentlyPlayedModel( ui->tracksView, HISTORY_TRACK_ITEMS ); + ui->tracksView->proxyModel()->setStyle( PlayableProxyModel::ShortWithAvatars ); + ui->tracksView->overlay()->setEnabled( false ); + ui->tracksView->setPlaylistModel( m_tracksModel ); + ui->tracksView->setAutoResize( true ); + ui->tracksView->setAlternatingRowColors( false ); + m_tracksModel->setSource( source_ptr() ); + + QPalette p = ui->tracksView->palette(); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TRACKLIST_TRACK_SOLVED ); + p.setColor( QPalette::BrightText, TomahawkStyle::PAGE_TRACKLIST_TRACK_UNRESOLVED ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_TRACKLIST_NUMBER ); + p.setColor( QPalette::Highlight, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT ); + p.setColor( QPalette::HighlightedText, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT_TEXT ); + + ui->tracksView->setPalette( p ); + ui->tracksView->setFrameShape( QFrame::NoFrame ); + ui->tracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->tracksView->setStyleSheet( "QTreeView { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->trackFrame ); + + QFont f; + f.setBold( true ); + QFontMetrics fm( f ); + ui->tracksView->setMinimumWidth( fm.width( tr( "Recently played tracks" ) ) * 2 ); + } + + { + RecentPlaylistsModel* model = new RecentPlaylistsModel( HISTORY_PLAYLIST_ITEMS, this ); + + ui->playlistWidget->setFrameShape( QFrame::NoFrame ); + ui->playlistWidget->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->playlistWidget->setItemDelegate( new PlaylistDelegate() ); + ui->playlistWidget->setModel( model ); + ui->playlistWidget->overlay()->resize( 380, 86 ); + ui->playlistWidget->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + + QPalette p = ui->playlistWidget->palette(); + p.setColor( QPalette::Text, TomahawkStyle::HEADER_TEXT ); + p.setColor( QPalette::BrightText, TomahawkStyle::HEADER_TEXT ); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + p.setColor( QPalette::Highlight, TomahawkStyle::HEADER_TEXT ); + p.setColor( QPalette::HighlightedText, TomahawkStyle::HEADER_BACKGROUND ); + + ui->playlistWidget->setPalette( p ); + ui->playlistWidget->setMinimumHeight( 400 ); + ui->playlistWidget->setStyleSheet( "QListView { background-color: transparent; }" ); + TomahawkStyle::styleScrollBar( ui->playlistWidget->verticalScrollBar() ); + TomahawkStyle::stylePageFrame( ui->playlistFrame ); + + updatePlaylists(); + connect( ui->playlistWidget, SIGNAL( activated( QModelIndex ) ), SLOT( onPlaylistActivated( QModelIndex ) ) ); + connect( model, SIGNAL( emptinessChanged( bool ) ), this, SLOT( updatePlaylists() ) ); + } + + { + m_recentAlbumsModel = new AlbumModel( ui->additionsView ); + ui->additionsView->setPlayableModel( m_recentAlbumsModel ); + ui->additionsView->proxyModel()->sort( -1 ); + + ui->additionsView->setStyleSheet( "QListView { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->additionsFrame ); + TomahawkStyle::styleScrollBar( ui->additionsView->verticalScrollBar() ); + } + + { + QFont f = ui->label->font(); + f.setBold( false ); + f.setFamily( "Fauna One" ); + + QPalette p = ui->label->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); + + ui->label->setFont( f ); + ui->label_2->setFont( f ); + ui->label->setPalette( p ); + ui->label_2->setPalette( p ); + } + + { + QFont f = ui->playlistLabel->font(); + f.setBold( false ); + f.setFamily( "Fauna One" ); + + QPalette p = ui->playlistLabel->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + + ui->playlistLabel->setFont( f ); + ui->playlistLabel->setPalette( p ); + } + + { + QScrollArea* area = new QScrollArea(); + area->setWidgetResizable( true ); + area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + area->setWidget( widget ); + + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); + area->setPalette( pal ); + area->setAutoFillBackground( true ); + area->setFrameShape( QFrame::NoFrame ); + area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget( m_header ); + layout->addWidget( area ); + setLayout( layout ); + TomahawkUtils::unmarginLayout( layout ); + } + + { + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + ui->widget->setPalette( pal ); + ui->widget->setAutoFillBackground( true ); + } MetaPlaylistInterface* mpl = new MetaPlaylistInterface(); mpl->addChildInterface( ui->tracksView->playlistInterface() ); @@ -146,8 +190,6 @@ Dashboard::Dashboard( QWidget* parent ) connect( SourceList::instance(), SIGNAL( ready() ), SLOT( onSourcesReady() ) ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) ); - connect( ui->playlistWidget, SIGNAL( activated( QModelIndex ) ), SLOT( onPlaylistActivated( QModelIndex ) ) ); - connect( model, SIGNAL( emptinessChanged( bool ) ), this, SLOT( updatePlaylists() ) ); } diff --git a/src/libtomahawk/widgets/Dashboard.ui b/src/libtomahawk/widgets/Dashboard.ui index cebdebd935..6b2d56a6fc 100644 --- a/src/libtomahawk/widgets/Dashboard.ui +++ b/src/libtomahawk/widgets/Dashboard.ui @@ -7,189 +7,266 @@ 0 0 965 - 1179 + 616 Form - + - 16 + 0 - 12 + 0 - - - 0 - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - - - 8 - - - 4 - - - 8 - - - 8 - - - - - - 18 - 75 - true - - - - Recently Played Tracks - - - 0 - - - - - - - - 0 - 0 - - - - true - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 4 - + + + + 0 + + + 0 + + + 16 + + + 0 + + + 16 + + + - 8 + 12 - 4 + 0 - 8 + 12 - 4 + 0 - - - - Arial - 18 - 75 - true - + + + QFrame::StyledPanel - - Recent Additions - - - 0 + + QFrame::Raised + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + 20 + 75 + true + + + + Recently Played Tracks + + + 0 + + + + + + + + 0 + 0 + + + + true + + + + - - - - 0 - 0 - + + + QFrame::StyledPanel - - - 0 - 190 - + + QFrame::Raised + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + Arial + 20 + 75 + true + + + + Recent Additions + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 190 + + + + + - - - + + + - - - QFrame::StyledPanel + + + + 16777215 + 1 + - - QFrame::Raised + + Qt::Horizontal + + + + + + + + 16777215 + 1 + - + + Qt::Horizontal + + + + + + - 4 + 0 - 8 + 12 - 4 + 16 - 8 + 12 - 4 + 16 - - - - Arial - 18 - 75 - true - + + + QFrame::StyledPanel - - Newest Stations & Playlists - - - 0 + + QFrame::Raised + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + Arial + 20 + 75 + true + + + + Newest Stations & Playlists + + + 0 + + + + + + + - - - @@ -209,16 +286,16 @@ + + PlaylistView + QTreeView +

    playlist/PlaylistView.h
    + GridView QListView
    playlist/GridView.h
    - - PlaylistView - QTreeView -
    playlist/PlaylistView.h
    -
    PlaylistWidget QListWidget From 74972ca5e1621aeea177caa8b049282c3b06a390 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 07:24:55 +0200 Subject: [PATCH 410/565] * Removed .ui font definitions. --- src/libtomahawk/widgets/Dashboard.ui | 2 -- src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui | 1 - 2 files changed, 3 deletions(-) diff --git a/src/libtomahawk/widgets/Dashboard.ui b/src/libtomahawk/widgets/Dashboard.ui index 6b2d56a6fc..17f5bf4d99 100644 --- a/src/libtomahawk/widgets/Dashboard.ui +++ b/src/libtomahawk/widgets/Dashboard.ui @@ -137,7 +137,6 @@ - Arial 20 75 true @@ -247,7 +246,6 @@ - Arial 20 75 true diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui index cbf7b8e731..c23a463f2e 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui @@ -227,7 +227,6 @@ - Arial 20 75 true From b2db38db099572ea250bfd3e1949b71d907db1ef Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 07:28:52 +0200 Subject: [PATCH 411/565] * Fixed inbox header color. --- src/libtomahawk/infobar/InfoBar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/infobar/InfoBar.cpp b/src/libtomahawk/infobar/InfoBar.cpp index b0acbdf465..9e9a182096 100644 --- a/src/libtomahawk/infobar/InfoBar.cpp +++ b/src/libtomahawk/infobar/InfoBar.cpp @@ -96,7 +96,7 @@ InfoBar::InfoBar( QWidget* parent ) ui->horizontalLayout->addWidget( m_searchWidget ); QPalette pal = m_whitePal; - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); setAutoFillBackground( true ); setPalette( pal ); From 30447cee2b5433c7ad000b41e753244123a25a3f Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 09:48:21 +0200 Subject: [PATCH 412/565] * Set captions to non-bold in .ui. --- .../widgets/infowidgets/AlbumInfoWidget.cpp | 1 - .../widgets/infowidgets/AlbumInfoWidget.ui | 9 +++++---- .../widgets/infowidgets/ArtistInfoWidget.cpp | 2 -- .../widgets/infowidgets/ArtistInfoWidget.ui | 12 ++++++------ .../widgets/infowidgets/TrackInfoWidget.cpp | 2 -- .../widgets/infowidgets/TrackInfoWidget.ui | 6 +++--- 6 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index c27f85b0e6..3398e8add2 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -126,7 +126,6 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par { QFont f = ui->label->font(); - f.setBold( false ); f.setFamily( "Fauna One" ); QPalette p = ui->label->palette(); diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui index c23a463f2e..5fe56966a5 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.ui @@ -158,8 +158,8 @@ 20 - 75 - true + 50 + false @@ -227,9 +227,10 @@ + Arial 20 - 75 - true + 50 + false diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index 5f4680b356..de3dbba80c 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -171,7 +171,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->label->font(); - f.setBold( false ); f.setFamily( "Fauna One" ); QPalette p = ui->label->palette(); @@ -185,7 +184,6 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->albumLabel->font(); - f.setBold( false ); f.setFamily( "Fauna One" ); QPalette p = ui->albumLabel->palette(); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui index 99bdf74983..3ba399a411 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.ui @@ -215,8 +215,8 @@ 20 - 75 - true + 50 + false @@ -272,8 +272,8 @@ 20 - 75 - true + 50 + false @@ -381,8 +381,8 @@ 20 - 75 - true + 50 + false diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index b8f45653f5..9eec9f44a4 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -118,7 +118,6 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->label->font(); - f.setBold( false ); f.setFamily( "Fauna One" ); QPalette p = ui->label->palette(); @@ -131,7 +130,6 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->statsLabel->font(); f.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); - f.setBold( true ); ui->statsLabel->setFont( f ); } diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui index 0c4e7ea6ba..b40ea0d332 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.ui @@ -6,7 +6,7 @@ 0 0 - 965 + 989 832 @@ -252,8 +252,8 @@ 20 - 75 - true + 50 + false From 4be8717525ac6197e53e3a864ccbbb963a924972 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 09:52:10 +0200 Subject: [PATCH 413/565] * Set captions to non-bold in .ui. --- src/libtomahawk/widgets/Dashboard.cpp | 7 ------- src/libtomahawk/widgets/Dashboard.ui | 13 +++++++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index 145f7891df..e8ed6708e1 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -85,11 +85,6 @@ Dashboard::Dashboard( QWidget* parent ) ui->tracksView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); ui->tracksView->setStyleSheet( "QTreeView { background-color: transparent; }" ); TomahawkStyle::stylePageFrame( ui->trackFrame ); - - QFont f; - f.setBold( true ); - QFontMetrics fm( f ); - ui->tracksView->setMinimumWidth( fm.width( tr( "Recently played tracks" ) ) * 2 ); } { @@ -132,7 +127,6 @@ Dashboard::Dashboard( QWidget* parent ) { QFont f = ui->label->font(); - f.setBold( false ); f.setFamily( "Fauna One" ); QPalette p = ui->label->palette(); @@ -146,7 +140,6 @@ Dashboard::Dashboard( QWidget* parent ) { QFont f = ui->playlistLabel->font(); - f.setBold( false ); f.setFamily( "Fauna One" ); QPalette p = ui->playlistLabel->palette(); diff --git a/src/libtomahawk/widgets/Dashboard.ui b/src/libtomahawk/widgets/Dashboard.ui index 17f5bf4d99..5745baee01 100644 --- a/src/libtomahawk/widgets/Dashboard.ui +++ b/src/libtomahawk/widgets/Dashboard.ui @@ -81,8 +81,8 @@ 20 - 75 - true + 50 + false @@ -137,9 +137,10 @@ + Arial 20 - 75 - true + 50 + false @@ -247,8 +248,8 @@ 20 - 75 - true + 50 + false From 5300ddae2c55d18a80b481632c73055844b81a1d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 09:52:29 +0200 Subject: [PATCH 414/565] * New layout for search page. --- src/libtomahawk/widgets/SearchWidget.cpp | 129 +++++++++-- src/libtomahawk/widgets/SearchWidget.h | 2 +- src/libtomahawk/widgets/SearchWidget.ui | 260 +++++++++++++++++++++-- 3 files changed, 351 insertions(+), 40 deletions(-) diff --git a/src/libtomahawk/widgets/SearchWidget.cpp b/src/libtomahawk/widgets/SearchWidget.cpp index 572a5b68bb..c0fbaf5561 100644 --- a/src/libtomahawk/widgets/SearchWidget.cpp +++ b/src/libtomahawk/widgets/SearchWidget.cpp @@ -20,52 +20,139 @@ #include "SearchWidget.h" #include "ui_SearchWidget.h" -#include -#include - #include "SourceList.h" #include "ViewManager.h" #include "playlist/PlayableModel.h" #include "playlist/PlaylistModel.h" #include "utils/AnimatedSpinner.h" - +#include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" +#include +#include + SearchWidget::SearchWidget( const QString& search, QWidget* parent ) : QWidget( parent ) , ui( new Ui::SearchWidget ) , m_search( search ) { - ui->setupUi( this ); + QWidget* widget = new QWidget; + ui->setupUi( widget ); + + ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); - ui->resultsView->setGuid( "searchwidget" ); - m_resultsModel = new PlaylistModel( ui->resultsView ); - ui->resultsView->setPlaylistModel( m_resultsModel ); - ui->resultsView->sortByColumn( PlaylistModel::Score, Qt::DescendingOrder ); + { + ui->resultsView->setGuid( "searchwidget" ); + m_resultsModel = new PlaylistModel( ui->resultsView ); + + QPalette p = ui->resultsView->palette(); + p.setColor( QPalette::Text, TomahawkStyle::PAGE_TRACKLIST_TRACK_SOLVED ); + p.setColor( QPalette::BrightText, TomahawkStyle::PAGE_TRACKLIST_TRACK_UNRESOLVED ); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_TRACKLIST_NUMBER ); + p.setColor( QPalette::Highlight, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT ); + p.setColor( QPalette::HighlightedText, TomahawkStyle::PAGE_TRACKLIST_HIGHLIGHT_TEXT ); + + ui->resultsView->setPalette( p ); + ui->resultsView->setFrameShape( QFrame::Panel ); + ui->resultsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->resultsView->setStyleSheet( "QTreeView { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->resultsFrame ); + + ui->resultsView->setAlternatingRowColors( false ); + ui->resultsView->setAutoResize( true ); + ui->resultsView->setPlaylistModel( m_resultsModel ); + ui->resultsView->sortByColumn( PlaylistModel::Score, Qt::DescendingOrder ); + ui->resultsView->setEmptyTip( tr( "Sorry, we could not find any tracks!" ) ); + } - m_albumsModel = new PlayableModel( ui->albumView ); - ui->albumView->setPlayableModel( m_albumsModel ); + { + m_albumsModel = new PlayableModel( ui->albumView ); + ui->albumView->setPlayableModel( m_albumsModel ); + + ui->albumView->setFrameShape( QFrame::NoFrame ); + ui->albumView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->albumView->proxyModel()->sort( -1 ); + ui->albumView->proxyModel()->setHideDupeItems( true ); + + ui->albumView->setAutoResize( true ); + ui->albumView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + ui->albumView->setStyleSheet( "QListView { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->albumFrame ); + } - m_artistsModel = new PlayableModel( ui->artistView ); - ui->artistView->setPlayableModel( m_artistsModel ); + { + m_artistsModel = new PlayableModel( ui->artistView ); + ui->artistView->setPlayableModel( m_artistsModel ); + + ui->artistView->setFrameShape( QFrame::NoFrame ); + ui->artistView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + ui->artistView->proxyModel()->sort( -1 ); + ui->artistView->proxyModel()->setHideDupeItems( true ); + + ui->artistView->setAutoResize( true ); + ui->artistView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + ui->artistView->setStyleSheet( "QListView { background-color: transparent; }" ); + TomahawkStyle::stylePageFrame( ui->artistFrame ); + } - ui->artistView->proxyModel()->sort( -1 ); - ui->albumView->proxyModel()->sort( -1 ); - ui->artistView->proxyModel()->setHideDupeItems( true ); - ui->albumView->proxyModel()->setHideDupeItems( true ); + { + QFont f = ui->label->font(); + f.setFamily( "Fauna One" ); - TomahawkUtils::unmarginLayout( ui->verticalLayout ); + QPalette p = ui->label->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); + + ui->label->setFont( f ); + ui->label->setPalette( p ); + } + + { + QFont f = ui->label_2->font(); + f.setFamily( "Fauna One" ); + + QPalette p = ui->label_2->palette(); + p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + + ui->label_2->setFont( f ); + ui->label_3->setFont( f ); + ui->label_2->setPalette( p ); + ui->label_3->setPalette( p ); + } + + { + QScrollArea* area = new QScrollArea(); + area->setWidgetResizable( true ); + area->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + area->setWidget( widget ); + + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); + area->setPalette( pal ); + area->setAutoFillBackground( true ); + area->setFrameShape( QFrame::NoFrame ); + area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + QVBoxLayout* layout = new QVBoxLayout(); + layout->addWidget( area ); + setLayout( layout ); + TomahawkUtils::unmarginLayout( layout ); + } + + { + QPalette pal = palette(); + pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND ); + ui->resultsContainer->setPalette( pal ); + ui->resultsContainer->setAutoFillBackground( true ); + } m_artistsModel->startLoading(); m_albumsModel->startLoading(); m_resultsModel->startLoading(); m_queries << Tomahawk::Query::get( search, uuid() ); - ui->splitter_2->setStretchFactor( 0, 0 ); - ui->splitter_2->setStretchFactor( 1, 1 ); - foreach ( const Tomahawk::query_ptr& query, m_queries ) { connect( query.data(), SIGNAL( artistsAdded( QList ) ), SLOT( onArtistsFound( QList ) ) ); diff --git a/src/libtomahawk/widgets/SearchWidget.h b/src/libtomahawk/widgets/SearchWidget.h index c4da8481ee..14877363b1 100644 --- a/src/libtomahawk/widgets/SearchWidget.h +++ b/src/libtomahawk/widgets/SearchWidget.h @@ -54,8 +54,8 @@ Q_OBJECT virtual QString description() const { return tr( "Results for '%1'" ).arg( m_search ); } virtual QPixmap pixmap() const; + virtual bool showInfoBar() const { return true; } virtual bool isTemporaryPage() const { return true; } - virtual bool jumpToCurrentTrack(); protected: diff --git a/src/libtomahawk/widgets/SearchWidget.ui b/src/libtomahawk/widgets/SearchWidget.ui index e2df0a078c..823bfc7e03 100644 --- a/src/libtomahawk/widgets/SearchWidget.ui +++ b/src/libtomahawk/widgets/SearchWidget.ui @@ -6,33 +6,257 @@ 0 0 - 985 - 460 + 939 + 647 Qt::TabFocus - + + + 0 + + + 0 + - + + + + 12 + + + 16 + + + 12 + + + 16 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 8 + + + 4 + + + 8 + + + 4 + + + + + + 20 + 50 + false + + + + Tracks + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + + + 16777215 + 1 + + - Qt::Vertical + Qt::Horizontal + + + + + + + + 16777215 + 1 + - - 1 + + Qt::Horizontal - - - Qt::Horizontal + + + + + + + 0 + + + 16 + + + 0 + + + 16 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 28 + + + 4 + + + 8 + + + 4 + + + + + + 20 + 50 + false + + + + Artists + + + + + + + + 0 + 190 + + + + + + + + + + + + + + + 0 + + + 16 + + + 0 - - 1 + + 16 - - - - + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 4 + + + 28 + + + 4 + + + 8 + + + 4 + + + + + + 20 + 50 + false + + + + Albums + + + + + + + + 0 + 190 + + + + + + + + @@ -41,12 +265,12 @@ PlaylistView QTreeView -
    playlist/PlaylistView.h
    +
    playlist/PlaylistView.h
    GridView QListView -
    playlist/GridView.h
    +
    playlist/GridView.h
    From bb69caed626140e1c6a6f667c66d9287dd62f036 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 10:33:22 +0200 Subject: [PATCH 415/565] * Made deprecated InfoBar look the same as the other headers. --- src/libtomahawk/infobar/InfoBar.cpp | 18 +++--- src/libtomahawk/infobar/InfoBar.ui | 98 ++++++++++++++++++++++------- 2 files changed, 88 insertions(+), 28 deletions(-) diff --git a/src/libtomahawk/infobar/InfoBar.cpp b/src/libtomahawk/infobar/InfoBar.cpp index 9e9a182096..934d53197a 100644 --- a/src/libtomahawk/infobar/InfoBar.cpp +++ b/src/libtomahawk/infobar/InfoBar.cpp @@ -35,7 +35,7 @@ #include "utils/Logger.h" #define ANIMATION_TIME 400 -#define IMAGE_HEIGHT 64 +#define IMAGE_HEIGHT 48 using namespace Tomahawk; @@ -46,12 +46,9 @@ InfoBar::InfoBar( QWidget* parent ) , m_queryLabel( 0 ) { ui->setupUi( this ); - TomahawkUtils::unmarginLayout( layout() ); - layout()->setContentsMargins( 8, 4, 8, 4 ); QFont boldFont = ui->captionLabel->font(); boldFont.setPointSize( TomahawkUtils::defaultFontSize() + 4 ); - boldFont.setBold( true ); ui->captionLabel->setFont( boldFont ); ui->captionLabel->setElideMode( Qt::ElideRight ); @@ -66,14 +63,15 @@ InfoBar::InfoBar( QWidget* parent ) ui->longDescriptionLabel->setFont( regFont ); m_whitePal = ui->captionLabel->palette(); - m_whitePal.setColor( QPalette::Foreground, Qt::white ); + m_whitePal.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); + m_whitePal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); ui->captionLabel->setPalette( m_whitePal ); ui->descriptionLabel->setPalette( m_whitePal ); ui->longDescriptionLabel->setPalette( m_whitePal ); ui->captionLabel->setMargin( 2 ); - ui->descriptionLabel->setMargin( 1 ); + ui->descriptionLabel->setMargin( 2 ); ui->longDescriptionLabel->setMargin( 4 ); ui->captionLabel->setText( QString() ); @@ -81,6 +79,11 @@ InfoBar::InfoBar( QWidget* parent ) ui->longDescriptionLabel->setText( QString() ); ui->imageLabel->setText( QString() ); + ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); + ui->lineAbove->setFrameShape( QFrame::HLine ); + ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); + ui->lineBelow->setFrameShape( QFrame::HLine ); + m_queryLabel = new QueryLabel( this ); m_queryLabel->setType( QueryLabel::Artist ); m_queryLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); @@ -98,9 +101,10 @@ InfoBar::InfoBar( QWidget* parent ) QPalette pal = m_whitePal; pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND ); + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + setFixedHeight( 58 ); setAutoFillBackground( true ); setPalette( pal ); - setFixedHeight( 80 ); connect( ViewManager::instance(), SIGNAL( filterAvailable( bool ) ), SLOT( setFilterAvailable( bool ) ) ); } diff --git a/src/libtomahawk/infobar/InfoBar.ui b/src/libtomahawk/infobar/InfoBar.ui index a24e59030b..cac1f8edcb 100644 --- a/src/libtomahawk/infobar/InfoBar.ui +++ b/src/libtomahawk/infobar/InfoBar.ui @@ -26,47 +26,61 @@ InfoBar
    + + 0 + + + 0 + + + 8 + + + 8 + + + 4 + + + 8 + + + 4 + - 64 - 64 + 48 + 48 - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 16 - 20 - + + + 0 - - - - - + 0 - + 0 0 + + + 75 + true + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop @@ -75,7 +89,7 @@ - + 0 0 @@ -85,6 +99,22 @@ + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 20 + 1 + + + + @@ -150,6 +180,32 @@ + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + +
    From 9d90c07b703a352dde0f99ccd71a9397028deed2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 10:33:43 +0200 Subject: [PATCH 416/565] * Dashboard should use the vanilla InfoBar. --- src/libtomahawk/widgets/Dashboard.cpp | 15 ++++++++------- src/libtomahawk/widgets/Dashboard.h | 8 ++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index e8ed6708e1..f6ab17bcfc 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -52,15 +52,10 @@ using namespace Tomahawk; Dashboard::Dashboard( QWidget* parent ) : QWidget( parent ) , ui( new Ui::Dashboard ) - , m_header( new BasicHeader( this ) ) { QWidget* widget = new QWidget; ui->setupUi( widget ); - m_header->setPixmap( ImageRegistry::instance()->pixmap( RESPATH "images/dashboard.svg", QSize( 0, 0 ) ) ); - m_header->setCaption( tr( "Dashboard" ) ); - m_header->setDescription( tr( "An overview of your recent activity" ) ); - ui->lineAbove->setStyleSheet( QString( "QFrame { border: 1px solid black; }" ) ); ui->lineBelow->setStyleSheet( QString( "QFrame { border: 1px solid %1; }" ).arg( TomahawkStyle::HEADER_BACKGROUND.name() ) ); @@ -163,7 +158,6 @@ Dashboard::Dashboard( QWidget* parent ) area->setAttribute( Qt::WA_MacShowFocusRect, 0 ); QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget( m_header ); layout->addWidget( area ); setLayout( layout ); TomahawkUtils::unmarginLayout( layout ); @@ -292,6 +286,13 @@ Dashboard::changeEvent( QEvent* e ) } +QPixmap +Dashboard::pixmap() const +{ + return ImageRegistry::instance()->pixmap( RESPATH "images/dashboard.svg", QSize( 0, 0 ) ); +} + + QSize PlaylistDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const { @@ -456,4 +457,4 @@ PlaylistWidget::verifySize() if ( model()->rowCount() > 0 ) setFixedHeight( model()->rowCount() * itemDelegate()->sizeHint( QStyleOptionViewItem(), model()->index( 0, 0 ) ).height() + frameWidth() * 2 ); -} \ No newline at end of file +} diff --git a/src/libtomahawk/widgets/Dashboard.h b/src/libtomahawk/widgets/Dashboard.h index f8372b2afa..74f4e14b65 100644 --- a/src/libtomahawk/widgets/Dashboard.h +++ b/src/libtomahawk/widgets/Dashboard.h @@ -92,10 +92,11 @@ Q_OBJECT virtual QWidget* widget() { return this; } virtual Tomahawk::playlistinterface_ptr playlistInterface() const; - virtual QString title() const { return tr( "Welcome to Tomahawk" ); } - virtual QString description() const { return QString(); } + virtual QString title() const { return tr( "Dashboard" ); } + virtual QString description() const { return tr( "An overview of your recent activity" ); } + virtual QPixmap pixmap() const; - virtual bool showInfoBar() const { return false; } + virtual bool showInfoBar() const { return true; } virtual bool isBeingPlayed() const; virtual bool jumpToCurrentTrack(); @@ -120,7 +121,6 @@ private slots: private: Ui::Dashboard *ui; - BasicHeader* m_header; RecentlyPlayedModel* m_tracksModel; AlbumModel* m_recentAlbumsModel; Tomahawk::playlistinterface_ptr m_playlistInterface; From d6fd70f1f1d02f93c2205b178569940fca2b1b59 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 10:34:00 +0200 Subject: [PATCH 417/565] * Fixed HistoryWidget's header color. --- src/libtomahawk/widgets/HistoryWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/HistoryWidget.cpp b/src/libtomahawk/widgets/HistoryWidget.cpp index 3841b887a7..8a79ae4559 100644 --- a/src/libtomahawk/widgets/HistoryWidget.cpp +++ b/src/libtomahawk/widgets/HistoryWidget.cpp @@ -58,7 +58,7 @@ HistoryWidget::HistoryWidget( const source_ptr& source, QWidget* parent ) QPalette pal = m_header->palette(); pal.setColor( QPalette::Foreground, Qt::white ); pal.setColor( QPalette::Text, Qt::white ); - pal.setBrush( backgroundRole(), TomahawkStyle::PAGE_BACKGROUND.lighter() ); + pal.setBrush( backgroundRole(), TomahawkStyle::HEADER_BACKGROUND.lighter() ); m_header->setPalette( pal ); m_header->setAutoFillBackground( true ); From df4aa8ce13b6e63954a76af2a73b2635b392c831 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 11:11:55 +0200 Subject: [PATCH 418/565] * Trim the artist biography before showing it. --- src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index de3dbba80c..d7021d84a8 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -365,7 +365,7 @@ ArtistInfoWidget::onBiographyLoaded() m_longDescription = m_artist->biography(); emit longDescriptionChanged( m_longDescription ); - ui->biography->setHtml( m_artist->biography().replace( '\n', "
    " ) ); + ui->biography->setHtml( m_artist->biography().trimmed().replace( '\n', "
    " ) ); } From 18d38f5a4691073b6e53481275b4597b5af8c481 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 11:25:49 +0200 Subject: [PATCH 419/565] * Change AudioEngine's state before emitting the signal. --- src/libtomahawk/audio/AudioEngine.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index 1a10737120..e25b0dd39f 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -81,14 +81,17 @@ AudioEnginePrivate::onStateChanged( Phonon::State newState, Phonon::State oldSta } if ( newState == Phonon::PlayingState ) { + bool emitSignal = false; if ( q_ptr->state() != AudioEngine::Paused && q_ptr->state() != AudioEngine::Playing ) { underrunCount = 0; underrunNotified = false; - emit q_ptr->started( currentTrack ); + emitSignal = true; } - q_ptr->setState( AudioEngine::Playing ); + + if ( emitSignal ) + emit q_ptr->started( currentTrack ); } if ( newState == Phonon::StoppedState && oldState == Phonon::PausedState ) { From f79744fb72ab997f1d048e464a49e0ba8bc49ae1 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 11:31:54 +0200 Subject: [PATCH 420/565] * FlexibleView's GridView looks nicer with black background. --- src/libtomahawk/playlist/FlexibleView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/playlist/FlexibleView.cpp b/src/libtomahawk/playlist/FlexibleView.cpp index 50903e0044..17b93787e4 100644 --- a/src/libtomahawk/playlist/FlexibleView.cpp +++ b/src/libtomahawk/playlist/FlexibleView.cpp @@ -76,6 +76,8 @@ FlexibleView::FlexibleView( QWidget* parent, QWidget* extraHeader ) lineBelow2->setFrameShape( QFrame::HLine ); lineBelow2->setMaximumHeight( 1 ); + m_gridView->setStyleSheet( QString( "QListView { background-color: black; }" ) ); + layout()->addWidget( m_header ); layout()->addWidget( m_modeHeader ); if ( extraHeader ) From feaaa6c7cdd59b7be4b886058299ca049712c8dc Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 12:18:48 +0200 Subject: [PATCH 421/565] * Adjust the splitter handle width. --- src/tomahawk/TomahawkWindow.cpp | 1 + src/tomahawk/TomahawkWindow.ui | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tomahawk/TomahawkWindow.cpp b/src/tomahawk/TomahawkWindow.cpp index d7ee9d8d26..699a562abe 100644 --- a/src/tomahawk/TomahawkWindow.cpp +++ b/src/tomahawk/TomahawkWindow.cpp @@ -198,6 +198,7 @@ TomahawkWindow::loadSettings() // Always set stretch factor. If user hasn't manually set splitter sizes, // this will ensure a sane default on all startups. If the user has, the manual // size will override the default stretching + ui->splitter->setHandleWidth( 3 ); ui->splitter->setStretchFactor( 0, 0 ); ui->splitter->setStretchFactor( 1, 1 ); diff --git a/src/tomahawk/TomahawkWindow.ui b/src/tomahawk/TomahawkWindow.ui index 3e81ad9658..1afb6f9a3c 100644 --- a/src/tomahawk/TomahawkWindow.ui +++ b/src/tomahawk/TomahawkWindow.ui @@ -37,7 +37,7 @@ Qt::Horizontal
    - 1 + 3 From 53e03f90e7e239c9150ca1c859c5fd65cf7b76fc Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 15 Jun 2013 12:19:28 +0200 Subject: [PATCH 422/565] * Draw a horizontal splitter. --- src/libtomahawk/utils/ProxyStyle.cpp | 32 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/utils/ProxyStyle.cpp b/src/libtomahawk/utils/ProxyStyle.cpp index ed19bec791..782176f7f4 100644 --- a/src/libtomahawk/utils/ProxyStyle.cpp +++ b/src/libtomahawk/utils/ProxyStyle.cpp @@ -92,26 +92,46 @@ ProxyStyle::drawControl( ControlElement ce, const QStyleOption* opt, QPainter* p p->setPen( TomahawkStyle::BORDER_LINE ); // We must special-case this because of the AnimatedSplitterHandle which has a // SizeHint of 0,0. - if( splitter->orientation() == Qt::Vertical ) + if ( splitter->orientation() == Qt::Vertical ) + { p->drawLine( opt->rect.topLeft(), opt->rect.topRight() ); + } else - p->drawLine( opt->rect.topLeft(), opt->rect.bottomRight() ); + { + if ( splitter->handleWidth() == 1 ) + { + p->setPen( TomahawkStyle::BORDER_LINE ); + p->drawLine( opt->rect.topLeft(), opt->rect.bottomLeft() ); + } + else if ( splitter->handleWidth() == 3 ) + { + p->setPen( TomahawkStyle::BORDER_LINE ); + p->drawLine( opt->rect.topLeft(), opt->rect.bottomLeft() ); + p->setPen( TomahawkStyle::BORDER_LINE.darker() ); + p->drawLine( opt->rect.topLeft() + QPoint( 1, 0 ), opt->rect.bottomLeft() + QPoint( 1, 0 ) ); + p->setPen( TomahawkStyle::BORDER_LINE ); + p->drawLine( opt->rect.topLeft() + QPoint( 2, 0 ), opt->rect.bottomLeft() + QPoint( 2, 0 ) ); + } + else + Q_ASSERT( false ); + } } } else QProxyStyle::drawControl( ce, opt, p, w ); } + QSize ProxyStyle::sizeFromContents( QStyle::ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget ) const { - if( type == CT_Splitter ) + if ( type == CT_Splitter ) { const QSplitter* splitter = qobject_cast< const QSplitter* >( widget ); - if( splitter->orientation() == Qt::Horizontal ) - return QSize( 1, size.height() ); + if ( splitter->orientation() == Qt::Horizontal ) + return QSize( 2, size.height() ); else - return QSize( size.width(), 1 ); + return QSize( size.width(), 2 ); } else return QProxyStyle::sizeFromContents( type, option, size, widget ); From f074d4dea7d400c4198415cf749b493c7b5e8269 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 12:09:19 +0200 Subject: [PATCH 423/565] Set nodeid directly, not as a property --- src/libtomahawk/network/Connection.cpp | 21 ++++++++++++++----- src/libtomahawk/network/Connection.h | 4 ++++ src/libtomahawk/network/ConnectionManager.cpp | 2 +- src/libtomahawk/network/Servent.cpp | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 21cf176fbd..450d2f354a 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -193,7 +193,7 @@ Connection::start( QTcpSocket* sock ) void Connection::checkACL() { - if ( !property( "nodeid" ).isValid() ) + if ( m_nodeid.isEmpty() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Not checking ACL, nodeid is empty"; QTimer::singleShot( 0, this, SLOT( doSetup() ) ); @@ -206,10 +206,9 @@ Connection::checkACL() return; } - QString nodeid = property( "nodeid" ).toString(); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking ACL for" << name(); connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ), this, SLOT( checkACLResult( QString, QString, ACLRegistry::ACL ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, nodeid ), Q_ARG( QString, bareName() ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, m_nodeid ), Q_ARG( QString, bareName() ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); } @@ -222,9 +221,9 @@ Connection::bareName() const void Connection::checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ) { - if ( nodeid != property( "nodeid" ).toString() ) + if ( nodeid != m_nodeid ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( property( "nodeid" ).toString() ).arg( username ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( m_nodeid ).arg( username ); return; } if ( username != bareName() ) @@ -368,6 +367,18 @@ Connection::setId( const QString& id ) m_id = id; } +QString +Connection::nodeId() const +{ + return m_nodeid; +} + +void +Connection::setNodeId( const QString& nodeid ) +{ + m_nodeid = nodeid; +} + void Connection::readyRead() diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index 075ddefed0..8e4d30990e 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -58,6 +58,9 @@ Q_OBJECT QString id() const; void setId( const QString& ); + QString nodeId() const; + void setNodeId( const QString& ); + void setFirstMessage( const QVariant& m ); void setFirstMessage( msg_ptr m ); msg_ptr firstMessage() const { return m_firstmsg; } @@ -145,6 +148,7 @@ private slots: qint64 m_tx_bytes, m_tx_bytes_requested; qint64 m_rx_bytes; QString m_id; + QString m_nodeid; QTimer* m_statstimer; QTime m_statstimer_mark; diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 3ff264514c..26a9a41569 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -243,7 +243,7 @@ ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool l if ( peerInfo->nodeId().length() ) d_func()->controlConnection->setId( peerInfo->nodeId() ); - d_func()->controlConnection->setProperty( "nodeid", peerInfo->nodeId() ); + d_func()->controlConnection->setNodeId( peerInfo->nodeId() ); Servent::instance()->registerControlConnection( d_func()->controlConnection.data() ); tryConnect(); diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 96ae4a7ebe..8731972b00 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -1105,7 +1105,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString { // Used by the connection for the ACL check // If there isn't a nodeid it's not the first connection and will already have been stopped - conn.data()->setProperty( "nodeid", nodeid ); + conn->setNodeId( nodeid ); } if ( conn.data()->onceOnly() ) From 29ba51b32c5692341348d1ddd2ae77fb5f324377 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 13:56:37 +0200 Subject: [PATCH 424/565] Move private members of Connection into a Dpointer --- src/libtomahawk/network/Connection.cpp | 214 ++++++++++++++++++------- src/libtomahawk/network/Connection.h | 55 +++---- src/libtomahawk/network/Connection_p.h | 73 +++++++++ 3 files changed, 252 insertions(+), 90 deletions(-) create mode 100644 src/libtomahawk/network/Connection_p.h diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 450d2f354a..917f825969 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -18,7 +18,7 @@ * along with Tomahawk. If not, see . */ -#include "Connection.h" +#include "Connection_p.h" #include "QTcpSocketExtra.h" #include "network/Servent.h" @@ -38,29 +38,18 @@ Connection::Connection( Servent* parent ) , m_servent( parent ) , m_ready( false ) , m_onceonly( true ) - , m_do_shutdown( false ) - , m_actually_shutting_down( false ) - , m_peer_disconnected( false ) - , m_tx_bytes( 0 ) - , m_tx_bytes_requested( 0 ) - , m_rx_bytes( 0 ) - , m_id( "Connection()" ) - , m_statstimer( 0 ) - , m_stats_tx_bytes_per_sec( 0 ) - , m_stats_rx_bytes_per_sec( 0 ) - , m_rx_bytes_last( 0 ) - , m_tx_bytes_last( 0 ) + , d_ptr( new ConnectionPrivate( this ) ) { moveToThread( m_servent->thread() ); tDebug( LOGVERBOSE ) << "CTOR Connection (super)" << thread(); - connect( &m_msgprocessor_out, SIGNAL( ready( msg_ptr ) ), + connect( &d_func()->msgprocessor_out, SIGNAL( ready( msg_ptr ) ), SLOT( sendMsg_now( msg_ptr ) ), Qt::QueuedConnection ); - connect( &m_msgprocessor_in, SIGNAL( ready( msg_ptr ) ), + connect( &d_func()->msgprocessor_in, SIGNAL( ready( msg_ptr ) ), SLOT( handleMsg( msg_ptr ) ), Qt::QueuedConnection ); - connect( &m_msgprocessor_in, SIGNAL( empty() ), + connect( &d_func()->msgprocessor_in, SIGNAL( empty() ), SLOT( handleIncomingQueueEmpty() ), Qt::QueuedConnection ); } @@ -73,7 +62,8 @@ Connection::~Connection() m_sock->deleteLater(); } - delete m_statstimer; + delete d_func()->statstimer; + delete d_ptr; } @@ -85,11 +75,11 @@ Connection::handleIncomingQueueEmpty() // << "m_peer_disconnected" << m_peer_disconnected // << "bytes rx" << bytesReceived(); - if ( !m_sock.isNull() && m_sock->bytesAvailable() == 0 && m_peer_disconnected ) + if ( !m_sock.isNull() && m_sock->bytesAvailable() == 0 && d_func()->peer_disconnected ) { tDebug( LOGVERBOSE ) << "No more data to read, peer disconnected. shutting down connection." << "bytesavail" << m_sock->bytesAvailable() - << "bytesrx" << m_rx_bytes; + << "bytesrx" << d_func()->rx_bytes; shutdown(); } } @@ -114,18 +104,60 @@ Connection::setFirstMessage( msg_ptr m ) // << "msg len:" << m_firstmsg->length() ; } +msg_ptr +Connection::firstMessage() const +{ + return m_firstmsg; +} + +const QPointer& +Connection::socket() +{ + return m_sock; +} + +void +Connection::setOutbound(bool o) +{ + m_outbound = o; +} + +bool +Connection::outbound() const +{ + return m_outbound; +} + +Servent* +Connection::servent() const +{ + return m_servent; +} + +int +Connection::peerPort() const +{ + return m_peerport; +} + +void +Connection::setPeerPort(int p) +{ + m_peerport = p; +} + void Connection::shutdown( bool waitUntilSentAll ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << waitUntilSentAll << id(); - if ( m_do_shutdown ) + if ( d_func()->do_shutdown ) { //qDebug() << id() << " already shutting down"; return; } - m_do_shutdown = true; + d_func()->do_shutdown = true; if ( !waitUntilSentAll ) { // qDebug() << "Shutting down immediately " << id(); @@ -134,7 +166,7 @@ Connection::shutdown( bool waitUntilSentAll ) else { tDebug( LOGVERBOSE ) << "Shutting down after transfer complete " << id() - << "Actual/Desired" << m_tx_bytes << m_tx_bytes_requested; + << "Actual/Desired" << d_func()->tx_bytes << d_func()->tx_bytes_requested; bytesWritten( 0 ); // trigger shutdown if we've already sent everything // otherwise the bytesWritten slot will call actualShutdown() @@ -146,12 +178,12 @@ Connection::shutdown( bool waitUntilSentAll ) void Connection::actualShutdown() { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << m_actually_shutting_down << id(); - if ( m_actually_shutting_down ) + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << d_func()->actually_shutting_down << id(); + if ( d_func()->actually_shutting_down ) { return; } - m_actually_shutting_down = true; + d_func()->actually_shutting_down = true; if ( !m_sock.isNull() && m_sock->isOpen() ) { @@ -171,6 +203,72 @@ Connection::markAsFailed() shutdown(); } +void +Connection::setName( const QString& n ) +{ + m_name = n; +} + +QString +Connection::name() const +{ + return m_name; +} + +void +Connection::setOnceOnly( bool b ) +{ + m_onceonly = b; +} + +bool +Connection::onceOnly() const +{ + return m_onceonly; +} + +bool +Connection::isReady() const +{ + return m_ready; +} + +bool +Connection::isRunning() const +{ + return m_sock != 0; +} + +qint64 +Connection::bytesSent() const +{ + return d_func()->tx_bytes; +} + +qint64 +Connection::bytesReceived() const +{ + return d_func()->rx_bytes; +} + +void +Connection::setMsgProcessorModeOut(quint32 m) +{ + d_func()->msgprocessor_out.setMode( m ); +} + +void +Connection::setMsgProcessorModeIn(quint32 m) +{ + d_func()->msgprocessor_in.setMode( m ); +} + +const QHostAddress +Connection::peerIpAddress() const +{ + return d_func()->peerIpAddress; +} + void Connection::start( QTcpSocket* sock ) @@ -193,14 +291,14 @@ Connection::start( QTcpSocket* sock ) void Connection::checkACL() { - if ( m_nodeid.isEmpty() ) + if ( d_func()->nodeid.isEmpty() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Not checking ACL, nodeid is empty"; QTimer::singleShot( 0, this, SLOT( doSetup() ) ); return; } - if ( Servent::isIPWhitelisted( m_peerIpAddress ) ) + if ( Servent::isIPWhitelisted( d_func()->peerIpAddress ) ) { QTimer::singleShot( 0, this, SLOT( doSetup() ) ); return; @@ -208,7 +306,7 @@ Connection::checkACL() tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking ACL for" << name(); connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ), this, SLOT( checkACLResult( QString, QString, ACLRegistry::ACL ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, m_nodeid ), Q_ARG( QString, bareName() ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); } @@ -221,9 +319,9 @@ Connection::bareName() const void Connection::checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ) { - if ( nodeid != m_nodeid ) + if ( nodeid != d_func()->nodeid ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( m_nodeid ).arg( username ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( d_func()->nodeid ).arg( username ); return; } if ( username != bareName() ) @@ -273,12 +371,12 @@ Connection::doSetup() } //stats timer calculates BW used by this connection - m_statstimer = new QTimer; - m_statstimer->moveToThread( this->thread() ); - m_statstimer->setInterval( 1000 ); - connect( m_statstimer, SIGNAL( timeout() ), SLOT( calcStats() ) ); - m_statstimer->start(); - m_statstimer_mark.start(); + d_func()->statstimer = new QTimer; + d_func()->statstimer->moveToThread( this->thread() ); + d_func()->statstimer->setInterval( 1000 ); + connect( d_func()->statstimer, SIGNAL( timeout() ), SLOT( calcStats() ) ); + d_func()->statstimer->start(); + d_func()->statstimer_mark.start(); m_sock->moveToThread( thread() ); @@ -326,10 +424,10 @@ Connection::socketDisconnected() << "bytesavail:" << bytesAvailable << "bytesRecvd" << bytesReceived(); - m_peer_disconnected = true; + d_func()->peer_disconnected = true; emit socketClosed(); - if ( m_msgprocessor_in.length() == 0 && bytesAvailable == 0 ) + if ( d_func()->msgprocessor_in.length() == 0 && bytesAvailable == 0 ) { handleIncomingQueueEmpty(); actualShutdown(); @@ -345,7 +443,7 @@ Connection::socketDisconnectedError( QAbstractSocket::SocketError e ) if ( e == QAbstractSocket::RemoteHostClosedError ) return; - m_peer_disconnected = true; + d_func()->peer_disconnected = true; emit socketErrored(e); emit socketClosed(); @@ -357,26 +455,26 @@ Connection::socketDisconnectedError( QAbstractSocket::SocketError e ) QString Connection::id() const { - return m_id; + return d_func()->id; } void Connection::setId( const QString& id ) { - m_id = id; + d_func()->id = id; } QString Connection::nodeId() const { - return m_nodeid; + return d_func()->nodeid; } void Connection::setNodeId( const QString& nodeid ) { - m_nodeid = nodeid; + d_func()->nodeid = nodeid; } @@ -399,7 +497,7 @@ Connection::readyRead() } m_msg = Msg::begin( (char*) &msgheader ); - m_rx_bytes += Msg::headerSize(); + d_func()->rx_bytes += Msg::headerSize(); } if ( m_sock->bytesAvailable() < m_msg->length() ) @@ -413,7 +511,7 @@ Connection::readyRead() return; } m_msg->fill( ba ); - m_rx_bytes += ba.length(); + d_func()->rx_bytes += ba.length(); handleReadMsg(); // process m_msg and clear() it @@ -457,7 +555,7 @@ Connection::handleReadMsg() } else { - m_msgprocessor_in.append( m_msg ); + d_func()->msgprocessor_in.append( m_msg ); } m_msg.clear(); @@ -467,7 +565,7 @@ Connection::handleReadMsg() void Connection::sendMsg( QVariant j ) { - if ( m_do_shutdown ) + if ( d_func()->do_shutdown ) return; QJson::Serializer serializer; @@ -480,15 +578,15 @@ Connection::sendMsg( QVariant j ) void Connection::sendMsg( msg_ptr msg ) { - if ( m_do_shutdown ) + if ( d_func()->do_shutdown ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "SHUTTING DOWN, NOT SENDING msg flags:" << (int)msg->flags() << "length:" << msg->length() << id(); return; } - m_tx_bytes_requested += msg->length() + Msg::headerSize(); - m_msgprocessor_out.append( msg ); + d_func()->tx_bytes_requested += msg->length() + Msg::headerSize(); + d_func()->msgprocessor_out.append( msg ); } @@ -517,9 +615,9 @@ Connection::sendMsg_now( msg_ptr msg ) void Connection::bytesWritten( qint64 i ) { - m_tx_bytes += i; + d_func()->tx_bytes += i; // if we are waiting to shutdown, and have sent all queued data, do actual shutdown: - if ( m_do_shutdown && m_tx_bytes == m_tx_bytes_requested ) + if ( d_func()->do_shutdown && d_func()->tx_bytes == d_func()->tx_bytes_requested ) actualShutdown(); } @@ -527,13 +625,13 @@ Connection::bytesWritten( qint64 i ) void Connection::calcStats() { - int elapsed = m_statstimer_mark.restart(); // ms since last calc + int elapsed = d_func()->statstimer_mark.restart(); // ms since last calc - m_stats_tx_bytes_per_sec = (float)1000 * ( (m_tx_bytes - m_tx_bytes_last) / (float)elapsed ); - m_stats_rx_bytes_per_sec = (float)1000 * ( (m_rx_bytes - m_rx_bytes_last) / (float)elapsed ); + d_func()->stats_tx_bytes_per_sec = (float)1000 * ( (d_func()->tx_bytes - d_func()->tx_bytes_last) / (float)elapsed ); + d_func()->stats_rx_bytes_per_sec = (float)1000 * ( (d_func()->rx_bytes - d_func()->rx_bytes_last) / (float)elapsed ); - m_rx_bytes_last = m_rx_bytes; - m_tx_bytes_last = m_tx_bytes; + d_func()->rx_bytes_last = d_func()->rx_bytes; + d_func()->tx_bytes_last = d_func()->tx_bytes; - emit statsTick( m_stats_tx_bytes_per_sec, m_stats_rx_bytes_per_sec ); + emit statsTick( d_func()->stats_tx_bytes_per_sec, d_func()->stats_rx_bytes_per_sec ); } diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index 8e4d30990e..a024537042 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,7 +43,7 @@ #include #include - +class ConnectionPrivate; class Servent; class DLLEXPORT Connection : public QObject @@ -63,37 +64,37 @@ Q_OBJECT void setFirstMessage( const QVariant& m ); void setFirstMessage( msg_ptr m ); - msg_ptr firstMessage() const { return m_firstmsg; } + msg_ptr firstMessage() const; - const QPointer& socket() { return m_sock; } + const QPointer& socket(); - void setOutbound( bool o ) { m_outbound = o; } - bool outbound() const { return m_outbound; } + void setOutbound( bool o ); + bool outbound() const; - Servent* servent() { return m_servent; } + Servent* servent() const; // get public port of remote peer: - int peerPort() { return m_peerport; } - void setPeerPort( int p ) { m_peerport = p; } + int peerPort() const; + void setPeerPort( int p ); void markAsFailed(); - void setName( const QString& n ) { m_name = n; } - QString name() const { return m_name; } + void setName( const QString& n ); + QString name() const; - void setOnceOnly( bool b ) { m_onceonly = b; } - bool onceOnly() const { return m_onceonly; } + void setOnceOnly( bool b ); + bool onceOnly() const; - bool isReady() const { return m_ready; } - bool isRunning() const { return m_sock != 0; } + bool isReady() const; + bool isRunning() const; - qint64 bytesSent() const { return m_tx_bytes; } - qint64 bytesReceived() const { return m_rx_bytes; } + qint64 bytesSent() const; + qint64 bytesReceived() const; - void setMsgProcessorModeOut( quint32 m ) { m_msgprocessor_out.setMode( m ); } - void setMsgProcessorModeIn( quint32 m ) { m_msgprocessor_in.setMode( m ); } + void setMsgProcessorModeOut( quint32 m ); + void setMsgProcessorModeIn( quint32 m ); - const QHostAddress peerIpAddress() const { return m_peerIpAddress; } + const QHostAddress peerIpAddress() const; QString bareName() const; signals: @@ -139,23 +140,13 @@ private slots: bool m_outbound, m_ready, m_onceonly; msg_ptr m_firstmsg; QString m_name; - QHostAddress m_peerIpAddress; private: + Q_DECLARE_PRIVATE( Connection ) + ConnectionPrivate* d_ptr; + void handleReadMsg(); void actualShutdown(); - bool m_do_shutdown, m_actually_shutting_down, m_peer_disconnected; - qint64 m_tx_bytes, m_tx_bytes_requested; - qint64 m_rx_bytes; - QString m_id; - QString m_nodeid; - - QTimer* m_statstimer; - QTime m_statstimer_mark; - qint64 m_stats_tx_bytes_per_sec, m_stats_rx_bytes_per_sec; - qint64 m_rx_bytes_last, m_tx_bytes_last; - - MsgProcessor m_msgprocessor_in, m_msgprocessor_out; }; #endif // CONNECTION_H diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h new file mode 100644 index 0000000000..3ea207c36a --- /dev/null +++ b/src/libtomahawk/network/Connection_p.h @@ -0,0 +1,73 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef CONNECTION_P_H +#define CONNECTION_P_H + +#include "Connection.h" + +class ConnectionPrivate : public QObject +{ +Q_OBJECT + +public: + ConnectionPrivate( Connection* q ) + : q_ptr ( q ) + , do_shutdown( false ) + , actually_shutting_down( false ) + , peer_disconnected( false ) + , tx_bytes( 0 ) + , tx_bytes_requested( 0 ) + , rx_bytes( 0 ) + , id( "Connection()" ) + , statstimer( 0 ) + , stats_tx_bytes_per_sec( 0 ) + , stats_rx_bytes_per_sec( 0 ) + , rx_bytes_last( 0 ) + , tx_bytes_last( 0 ) + { + } + Connection* q_ptr; + Q_DECLARE_PUBLIC ( Connection ) + +private: + QHostAddress peerIpAddress; + bool do_shutdown; + bool actually_shutting_down; + bool peer_disconnected; + qint64 tx_bytes; + qint64 tx_bytes_requested; + qint64 rx_bytes; + QString id; + QString nodeid; + + QTimer* statstimer; + QTime statstimer_mark; + qint64 stats_tx_bytes_per_sec; + qint64 stats_rx_bytes_per_sec; + qint64 rx_bytes_last; + qint64 tx_bytes_last; + + MsgProcessor msgprocessor_in; + MsgProcessor msgprocessor_out; +}; + + +#endif // CONNECTION_P_H From d62bd306be40f10f3c054fc26a2354c32e0c18e9 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 14:13:35 +0200 Subject: [PATCH 425/565] Remove unused qjson includes and member --- src/libtomahawk/network/Connection.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index a024537042..88cbc1f6ee 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -27,10 +27,6 @@ #include "DllMacro.h" -#include -#include -#include - #include #include #include @@ -135,7 +131,6 @@ private slots: QPointer m_sock; int m_peerport; msg_ptr m_msg; - QJson::Parser parser; Servent* m_servent; bool m_outbound, m_ready, m_onceonly; msg_ptr m_firstmsg; From 8824466840e2c7e1dc9e0004804004b570f67c24 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Sat, 15 Jun 2013 08:17:42 -0400 Subject: [PATCH 426/565] More padding in breadcrumb. Don't yet know how to make font bigger. --- src/libtomahawk/widgets/Breadcrumb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp index ac7889ef9e..d3433143e0 100644 --- a/src/libtomahawk/widgets/Breadcrumb.cpp +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -72,7 +72,7 @@ Breadcrumb::setRootIcon( const QPixmap& pm ) QPushButton* button = new QPushButton( QIcon( m_rootIcon ), "", this ); button->setFlat( true ); - button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:16px; height:16px;}" ); + button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:36px; height:36px;}" ); m_buttonlayout->insertWidget( 0, button ); m_buttonlayout->insertSpacing( 0,5 ); m_buttonlayout->insertSpacing( 2,5 ); From 2ee369393cc5fb91052ee53abef4393a49388317 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 15:52:14 +0200 Subject: [PATCH 427/565] Unprefix Qt includes --- src/libtomahawk/Source.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index c4dcdf8680..e52ae81cbb 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -21,9 +21,9 @@ #ifndef SOURCE_H #define SOURCE_H -#include -#include -#include +#include +#include +#include #include "Typedefs.h" #include "accounts/AccountManager.h" From bf5fcbd0b647762224fe3da70138020e6b4b62b6 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 15:52:26 +0200 Subject: [PATCH 428/565] Remove unused include --- src/libtomahawk/Source.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index e52ae81cbb..16e41876d4 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -30,7 +30,6 @@ #include "network/ControlConnection.h" #include "network/DbSyncConnection.h" #include "collection/Collection.h" -#include "Query.h" #include "utils/TomahawkUtils.h" #include "DllMacro.h" From 3d33c68778517cab016fa0f556c03b9804e0dfbe Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 16:14:17 +0200 Subject: [PATCH 429/565] Move all implementations out of the header --- src/libtomahawk/Source.cpp | 43 ++++++++++++++++++++++++++++++++++++++ src/libtomahawk/Source.h | 14 ++++++------- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 8e7fd96c4e..2c1e2489f9 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -87,6 +88,18 @@ Source::~Source() tDebug() << Q_FUNC_INFO << friendlyName(); } +bool +Source::isLocal() const +{ + return m_isLocal; +} + +bool +Source::isOnline() const +{ + return m_online || m_isLocal; +} + bool Source::setControlConnection( ControlConnection* cc ) @@ -147,6 +160,12 @@ Source::dbCollection() const return tmp; } +QList +Source::collections() const +{ + return m_collections; +} + void Source::setStats( const QVariantMap& m ) @@ -343,6 +362,18 @@ Source::removeCollection( const collection_ptr& c ) emit collectionRemoved( c ); } +int +Source::id() const +{ + return m_id; +} + +ControlConnection* +Source::controlConnection() const +{ + return m_cc.data(); +} + void Source::handleDisconnect( Tomahawk::Accounts::Account*, Tomahawk::Accounts::AccountManager::DisconnectReason reason ) { @@ -484,6 +515,12 @@ Source::trackCount() const return m_stats.value( "numfiles", 0 ).toUInt(); } +query_ptr +Source::currentTrack() const +{ + return m_currentTrack; +} + Tomahawk::playlistinterface_ptr Source::playlistInterface() @@ -700,3 +737,9 @@ Source::textStatus() const return tr( "Offline" ); } } + +DBSyncConnection::State +Source::state() const +{ + return m_state; +} diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 16e41876d4..bae4492750 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -61,8 +61,8 @@ friend class ::MusicScanner; explicit Source( int id, const QString& nodeId = QString() ); virtual ~Source(); - bool isLocal() const { return m_isLocal; } - bool isOnline() const { return m_online || m_isLocal; } + bool isLocal() const; + bool isOnline() const; QString nodeId() const; @@ -79,12 +79,12 @@ friend class ::MusicScanner; #endif collection_ptr dbCollection() const; - QList< Tomahawk::collection_ptr > collections() const { return m_collections; } + QList< Tomahawk::collection_ptr > collections() const; void addCollection( const Tomahawk::collection_ptr& c ); void removeCollection( const Tomahawk::collection_ptr& c ); - int id() const { return m_id; } - ControlConnection* controlConnection() const { return m_cc.data(); } + int id() const; + ControlConnection* controlConnection() const; bool setControlConnection( ControlConnection* cc ); const QSet< Tomahawk::peerinfo_ptr > peerInfos() const; @@ -94,9 +94,9 @@ friend class ::MusicScanner; unsigned int trackCount() const; - Tomahawk::query_ptr currentTrack() const { return m_currentTrack; } + Tomahawk::query_ptr currentTrack() const; QString textStatus() const; - DBSyncConnection::State state() const { return m_state; } + DBSyncConnection::State state() const; Tomahawk::playlistinterface_ptr playlistInterface(); From 34196ec001a1645ba85e1add7be6a863757d36f3 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 20:22:25 +0200 Subject: [PATCH 430/565] Pimple Source, remove some headers from Source.h and Connection.h --- .../generic/charts/ChartsPlugin.cpp | 1 + .../generic/newreleases/NewReleasesPlugin.cpp | 2 + src/libtomahawk/AclRegistry.cpp | 4 +- src/libtomahawk/AclRegistry.h | 23 +- src/libtomahawk/GlobalActionManager.cpp | 3 + src/libtomahawk/Source.cpp | 288 +++++++++++------- src/libtomahawk/Source.h | 49 +-- src/libtomahawk/SourceList.cpp | 2 + src/libtomahawk/Source_p.h | 86 ++++++ src/libtomahawk/Typedefs.h | 18 +- .../accounts/spotify/SpotifyInfoPlugin.cpp | 1 + .../DatabaseCommand_CreatePlaylist.cpp | 8 +- .../DatabaseCommand_DeletePlaylist.cpp | 3 + ...baseCommand_SetDynamicPlaylistRevision.cpp | 8 +- .../DatabaseCommand_SetPlaylistRevision.cpp | 8 +- src/libtomahawk/jobview/AclJobItem.cpp | 6 +- src/libtomahawk/jobview/AclJobItem.h | 4 +- src/libtomahawk/jobview/JobStatusView.cpp | 2 +- src/libtomahawk/network/Connection.cpp | 10 +- src/libtomahawk/network/Connection.h | 23 +- src/libtomahawk/network/Connection_p.h | 9 +- src/libtomahawk/network/ControlConnection.cpp | 1 + src/libtomahawk/network/ControlConnection.h | 3 + .../network/DBSyncConnectionState.h | 41 +++ src/libtomahawk/network/DbSyncConnection.cpp | 9 +- src/libtomahawk/network/DbSyncConnection.h | 20 +- src/libtomahawk/network/Msg.h | 5 +- src/libtomahawk/network/Servent.cpp | 7 +- src/libtomahawk/network/Servent.h | 2 +- src/libtomahawk/network/StreamConnection.cpp | 1 + .../playlist/PlaylistItemDelegate.cpp | 2 + .../playlist/PlaylistLargeItemDelegate.cpp | 1 + .../playlist/dynamic/DynamicModel.cpp | 1 + src/libtomahawk/utils/TomahawkUtils.cpp | 1 + .../widgets/RecentlyPlayedPlaylistsModel.cpp | 8 +- src/tomahawk/AclRegistryImpl.cpp | 26 +- src/tomahawk/AclRegistryImpl.h | 4 +- src/tomahawk/TomahawkApp.cpp | 3 +- src/tomahawk/sourcetree/SourceDelegate.cpp | 4 +- 39 files changed, 450 insertions(+), 247 deletions(-) create mode 100644 src/libtomahawk/Source_p.h create mode 100644 src/libtomahawk/network/DBSyncConnectionState.h diff --git a/src/infoplugins/generic/charts/ChartsPlugin.cpp b/src/infoplugins/generic/charts/ChartsPlugin.cpp index dd76dc1f7f..e1e2d3b51e 100644 --- a/src/infoplugins/generic/charts/ChartsPlugin.cpp +++ b/src/infoplugins/generic/charts/ChartsPlugin.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #define CHART_URL "http://charts.tomahawk-player.org/" //#define CHART_URL "http://localhost:8080/" diff --git a/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp index 2ce6f532e2..856eb980df 100644 --- a/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp +++ b/src/infoplugins/generic/newreleases/NewReleasesPlugin.cpp @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include diff --git a/src/libtomahawk/AclRegistry.cpp b/src/libtomahawk/AclRegistry.cpp index 143d16a1b3..8c69e02b23 100644 --- a/src/libtomahawk/AclRegistry.cpp +++ b/src/libtomahawk/AclRegistry.cpp @@ -66,7 +66,7 @@ QDataStream& operator>>( QDataStream &in, ACLRegistry::User &user ) } int aclIn; in >> aclIn; - user.acl = (ACLRegistry::ACL)( aclIn ); + user.acl = (Tomahawk::ACL)( aclIn ); } return in; } @@ -91,7 +91,7 @@ ACLRegistry::setInstance( ACLRegistry* instance ) ACLRegistry::ACLRegistry( QObject* parent ) : QObject( parent ) { - qRegisterMetaType< ACLRegistry::ACL >( "ACLRegistry::ACL" ); + qRegisterMetaType< Tomahawk::ACL >( "Tomahawk::ACL" ); qRegisterMetaType< ACLRegistry::User >( "ACLRegistry::User" ); qRegisterMetaTypeStreamOperators< ACLRegistry::User >( "ACLRegistry::User" ); } diff --git a/src/libtomahawk/AclRegistry.h b/src/libtomahawk/AclRegistry.h index 7fd9cb5881..a31dce5dd8 100644 --- a/src/libtomahawk/AclRegistry.h +++ b/src/libtomahawk/AclRegistry.h @@ -21,6 +21,7 @@ #define TOMAHAWK_ACLREGISTRY_H #include "DllMacro.h" +#include "Typedefs.h" #include #include @@ -43,32 +44,25 @@ class DLLEXPORT ACLRegistry : public QObject static ACLRegistry* instance(); static void setInstance( ACLRegistry* instance ); - enum ACL { - NotFound = 0, - Deny = 1, - Read = 2, - Stream = 3 - }; - struct User { QString uuid; QString friendlyName; QStringList knownDbids; QStringList knownAccountIds; - ACLRegistry::ACL acl; + Tomahawk::ACL acl; User() : uuid( QUuid::createUuid().toString() ) , friendlyName() , knownDbids() , knownAccountIds() - , acl( ACLRegistry::NotFound ) + , acl( Tomahawk::NotFound ) {} ~User() {} - User( QString p_uuid, QString p_friendlyName, QStringList p_knownDbids, QStringList p_knownAccountIds, ACLRegistry::ACL p_acl ) + User( QString p_uuid, QString p_friendlyName, QStringList p_knownDbids, QStringList p_knownAccountIds, Tomahawk::ACL p_acl ) : uuid( p_uuid ) , friendlyName( p_friendlyName ) , knownDbids( p_knownDbids ) @@ -89,7 +83,7 @@ class DLLEXPORT ACLRegistry : public QObject virtual ~ACLRegistry(); signals: - void aclResult( QString nodeid, QString username, ACLRegistry::ACL peerStatus ); + void aclResult( QString nodeid, QString username, Tomahawk::ACL peerStatus ); public slots: /** @@ -98,9 +92,9 @@ public slots: * @param dbid DBID of peer * @param globalType Global ACL to store if peer not found; if ACLRegistry::NotFound, does not store the peer Defaults to ACLRegistry::NotFound. * @param username If not empty, will store the given username along with the new ACL value. Defaults to QString(). - * @return ACLRegistry::ACL + * @return Tomahawk::ACL **/ - virtual ACLRegistry::ACL isAuthorizedUser( const QString &dbid, const QString &username, ACLRegistry::ACL globalType = ACLRegistry::NotFound, bool skipEmission = false ) = 0; + virtual Tomahawk::ACL isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL globalType = Tomahawk::NotFound, bool skipEmission = false ) = 0; virtual void wipeEntries(); protected: @@ -117,7 +111,6 @@ public slots: static ACLRegistry* s_instance; }; -Q_DECLARE_METATYPE( ACLRegistry::ACL ); -Q_DECLARE_METATYPE( ACLRegistry::User ); +Q_DECLARE_METATYPE( ACLRegistry::User ) #endif // TOMAHAWK_ACLREGISTRY_H diff --git a/src/libtomahawk/GlobalActionManager.cpp b/src/libtomahawk/GlobalActionManager.cpp index fcb074d148..94fea71975 100644 --- a/src/libtomahawk/GlobalActionManager.cpp +++ b/src/libtomahawk/GlobalActionManager.cpp @@ -41,6 +41,9 @@ #include "utils/ShortenedLinkParser.h" #include "utils/RdioParser.h" +#include +#include + #ifndef ENABLE_HEADLESS #include "ViewManager.h" #include "playlist/PlaylistView.h" diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 2c1e2489f9..278d817a3c 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -18,7 +18,7 @@ * along with Tomahawk. If not, see . */ -#include "Source.h" +#include "Source_p.h" #include "collection/Collection.h" #include "SourceList.h" @@ -52,26 +52,18 @@ using namespace Tomahawk; Source::Source( int id, const QString& nodeId ) : QObject() - , m_isLocal( false ) - , m_online( false ) - , m_nodeId( nodeId ) - , m_id( id ) - , m_updateIndexWhenSynced( false ) - , m_state( DBSyncConnection::UNKNOWN ) - , m_avatar( 0 ) - , m_avatarLoaded( false ) - , m_cc( 0 ) - , m_commandCount( 0 ) -{ - m_scrubFriendlyName = qApp->arguments().contains( "--demo" ); + , d_ptr( new SourcePrivate( this, id, nodeId ) ) +{ + Q_D( Source ); + d->scrubFriendlyName = qApp->arguments().contains( "--demo" ); if ( id == 0 ) - m_isLocal = true; + d->isLocal = true; - m_currentTrackTimer.setSingleShot( true ); - connect( &m_currentTrackTimer, SIGNAL( timeout() ), this, SLOT( trackTimerFired() ) ); + d->currentTrackTimer.setSingleShot( true ); + connect( &d->currentTrackTimer, SIGNAL( timeout() ), this, SLOT( trackTimerFired() ) ); - if ( m_isLocal ) + if ( d->isLocal ) { connect( Accounts::AccountManager::instance(), SIGNAL( connected( Tomahawk::Accounts::Account* ) ), @@ -86,35 +78,39 @@ Source::Source( int id, const QString& nodeId ) Source::~Source() { tDebug() << Q_FUNC_INFO << friendlyName(); + delete d_ptr; } bool Source::isLocal() const { - return m_isLocal; + Q_D( const Source ); + return d->isLocal; } bool Source::isOnline() const { - return m_online || m_isLocal; + Q_D( const Source ); + return d->online || d->isLocal; } bool Source::setControlConnection( ControlConnection* cc ) { - QMutexLocker locker( &m_setControlConnectionMutex ); - if ( !m_cc.isNull() && m_cc->isReady() && m_cc->isRunning() ) + Q_D( Source ); + QMutexLocker locker( &d->setControlConnectionMutex ); + if ( !d->cc.isNull() && d->cc->isReady() && d->cc->isRunning() ) { const QString& nodeid = Database::instance()->impl()->dbid(); peerInfoDebug( (*cc->peerInfos().begin()) ) << Q_FUNC_INFO << "Comparing" << cc->id() << "and" << nodeid << "to detect duplicate connection, outbound:" << cc->outbound(); - if ( cc->id() < nodeid && m_cc->outbound() ) + if ( cc->id() < nodeid && d->cc->outbound() ) { // This ControlConnection is not needed anymore, get rid of it! - m_cc->deleteLater(); + d->cc->deleteLater(); // Use new ControlConnection - m_cc = cc; + d->cc = cc; return true; } else @@ -124,7 +120,7 @@ Source::setControlConnection( ControlConnection* cc ) } else { - m_cc = cc; + d->cc = cc; return true; } } @@ -149,11 +145,16 @@ Source::peerInfos() const collection_ptr Source::dbCollection() const { - if ( m_collections.length() ) + Q_D( const Source ); + if ( d->collections.length() ) { - foreach ( const collection_ptr& collection, m_collections ) + foreach ( const collection_ptr& collection, d->collections ) + { if ( collection->backendType() == Collection::DatabaseCollectionType ) + { return collection; // We assume only one is a db collection. Now get off my lawn. + } + } } collection_ptr tmp; @@ -163,15 +164,16 @@ Source::dbCollection() const QList Source::collections() const { - return m_collections; + return d_func()->collections; } void Source::setStats( const QVariantMap& m ) { - m_stats = m; - emit stats( m_stats ); + Q_D( Source ); + d->stats = m; + emit stats( d->stats ); emit stateChanged(); } @@ -179,7 +181,7 @@ Source::setStats( const QVariantMap& m ) QString Source::nodeId() const { - return m_nodeId; + return d_func()->nodeId; } @@ -187,6 +189,8 @@ Source::nodeId() const QString Source::friendlyName() const { + Q_D( const Source ); + QStringList candidateNames; foreach ( const peerinfo_ptr& peerInfo, peerInfos() ) { @@ -204,10 +208,12 @@ Source::friendlyName() const return candidateNames.first(); } - if ( m_friendlyname.isEmpty() ) + if ( d->friendlyname.isEmpty() ) + { return dbFriendlyName(); + } - return m_friendlyname; + return d->friendlyname; } @@ -274,6 +280,8 @@ Source::friendlyNamesLessThan( const QString& first, const QString& second ) QPixmap Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) { + Q_D( Source ); + foreach ( const peerinfo_ptr& peerInfo, peerInfos() ) { if ( peerInfo && !peerInfo->avatar( style, size ).isNull() ) @@ -282,17 +290,17 @@ Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) } } - if ( m_avatarLoaded ) + if ( d->avatarLoaded ) { - if ( m_avatar ) - return *m_avatar; + if ( d->avatar ) + return *d->avatar; else return QPixmap(); } // Try to get the avatar from the cache // Hint: We store the avatar for each xmpp peer using its contactId, the dbFriendlyName is a contactId of a peer - m_avatarLoaded = true; + d->avatarLoaded = true; QByteArray avatarBuffer = TomahawkUtils::Cache::instance()->getData( "Sources", dbFriendlyName() ).toByteArray(); if ( !avatarBuffer.isNull() ) { @@ -301,8 +309,8 @@ Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) avatar.loadFromData( avatarBuffer ); avatarBuffer.clear(); - m_avatar = new QPixmap( TomahawkUtils::createRoundedImage( avatar, QSize( 0, 0 ) ) ); - return *m_avatar; + d->avatar = new QPixmap( TomahawkUtils::createRoundedImage( avatar, QSize( 0, 0 ) ) ); + return *d->avatar; } return QPixmap(); @@ -313,14 +321,20 @@ Source::avatar( TomahawkUtils::ImageMode style, const QSize& size ) void Source::setFriendlyName( const QString& fname ) { + Q_D( Source ); + if ( fname.isEmpty() ) + { return; + } - m_friendlyname = fname; - if ( m_scrubFriendlyName ) + d->friendlyname = fname; + if ( d->scrubFriendlyName ) { - if ( m_friendlyname.indexOf( "@" ) > 0 ) - m_friendlyname = m_friendlyname.split( "@" ).first(); + if ( d->friendlyname.indexOf( "@" ) > 0 ) + { + d->friendlyname = d->friendlyname.split( "@" ).first(); + } } } @@ -328,28 +342,36 @@ Source::setFriendlyName( const QString& fname ) QString Source::dbFriendlyName() const { - if( m_dbFriendlyName.isEmpty() ) + Q_D( const Source ); + + if( d->dbFriendlyName.isEmpty() ) + { return nodeId(); + } - return m_dbFriendlyName; + return d->dbFriendlyName; } void Source::setDbFriendlyName( const QString& dbFriendlyName ) { + Q_D( Source ); + if ( dbFriendlyName.isEmpty() ) return; - m_dbFriendlyName = dbFriendlyName; + d->dbFriendlyName = dbFriendlyName; } void Source::addCollection( const collection_ptr& c ) { + Q_D( Source ); + //Q_ASSERT( m_collections.length() == 0 ); // only 1 source supported atm - m_collections.append( c ); + d->collections.append( c ); emit collectionAdded( c ); } @@ -357,21 +379,27 @@ Source::addCollection( const collection_ptr& c ) void Source::removeCollection( const collection_ptr& c ) { + Q_D( Source ); + //Q_ASSERT( m_collections.length() == 1 && m_collections.first() == c ); // only 1 source supported atm - m_collections.removeAll( c ); + d->collections.removeAll( c ); emit collectionRemoved( c ); } int Source::id() const { - return m_id; + Q_D( const Source ); + + return d->id; } ControlConnection* Source::controlConnection() const { - return m_cc.data(); + Q_D( const Source ); + + return d->cc.data(); } void @@ -385,19 +413,21 @@ Source::handleDisconnect( Tomahawk::Accounts::Account*, Tomahawk::Accounts::Acco void Source::setOffline() { + Q_D( Source ); + qDebug() << Q_FUNC_INFO << friendlyName(); - if ( !m_online ) + if ( !d->online ) return; - m_online = false; + d->online = false; emit offline(); if ( !isLocal() ) { - m_currentTrack.clear(); + d->currentTrack.clear(); emit stateChanged(); - m_cc = 0; + d->cc = 0; DatabaseCommand_SourceOffline* cmd = new DatabaseCommand_SourceOffline( id() ); Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) ); } @@ -407,17 +437,19 @@ Source::setOffline() void Source::setOnline() { + Q_D( Source ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << friendlyName(); - if ( m_online ) + if ( d->online ) return; - m_online = true; + d->online = true; emit online(); if ( !isLocal() ) { // ensure username is in the database - DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( m_nodeId, dbFriendlyName() ); + DatabaseCommand_addSource* cmd = new DatabaseCommand_addSource( d->nodeId, dbFriendlyName() ); connect( cmd, SIGNAL( done( unsigned int, QString ) ), SLOT( dbLoaded( unsigned int, const QString& ) ) ); Database::instance()->enqueue( QSharedPointer(cmd) ); @@ -428,7 +460,9 @@ Source::setOnline() void Source::dbLoaded( unsigned int id, const QString& fname ) { - m_id = id; + Q_D( Source ); + + d->id = id; setDbFriendlyName( fname ); emit syncedWithDatabase(); @@ -438,10 +472,12 @@ Source::dbLoaded( unsigned int id, const QString& fname ) void Source::scanningProgress( unsigned int files ) { + Q_D( Source ); + if ( files ) - m_textStatus = tr( "Scanning (%L1 tracks)" ).arg( files ); + d->textStatus = tr( "Scanning (%L1 tracks)" ).arg( files ); else - m_textStatus = tr( "Scanning" ); + d->textStatus = tr( "Scanning" ); emit stateChanged(); } @@ -450,11 +486,13 @@ Source::scanningProgress( unsigned int files ) void Source::scanningFinished( bool updateGUI ) { - m_textStatus = QString(); + Q_D( Source ); + + d->textStatus = QString(); - if ( m_updateIndexWhenSynced ) + if ( d->updateIndexWhenSynced ) { - m_updateIndexWhenSynced = false; + d->updateIndexWhenSynced = false; updateTracks(); } @@ -466,34 +504,36 @@ Source::scanningFinished( bool updateGUI ) void -Source::onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info ) +Source::onStateChanged( DBSyncConnectionState newstate, DBSyncConnectionState oldstate, const QString& info ) { + Q_D( Source ); + Q_UNUSED( oldstate ); QString msg; switch( newstate ) { - case DBSyncConnection::CHECKING: + case CHECKING: { msg = tr( "Checking" ); break; } - case DBSyncConnection::FETCHING: + case FETCHING: { msg = tr( "Syncing" ); break; } - case DBSyncConnection::PARSING: + case PARSING: { msg = tr( "Importing" ); break; } - case DBSyncConnection::SCANNING: + case SCANNING: { msg = tr( "Scanning (%L1 tracks)" ).arg( info ); break; } - case DBSyncConnection::SYNCED: + case SYNCED: { msg = QString(); break; @@ -503,8 +543,8 @@ Source::onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::Stat msg = QString(); } - m_state = newstate; - m_textStatus = msg; + d->state = newstate; + d->textStatus = msg; emit stateChanged(); } @@ -512,44 +552,54 @@ Source::onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::Stat unsigned int Source::trackCount() const { - return m_stats.value( "numfiles", 0 ).toUInt(); + Q_D( const Source ); + + return d->stats.value( "numfiles", 0 ).toUInt(); } query_ptr Source::currentTrack() const { - return m_currentTrack; + Q_D( const Source ); + + return d->currentTrack; } Tomahawk::playlistinterface_ptr Source::playlistInterface() { - if ( m_playlistInterface.isNull() ) + Q_D( Source ); + + if ( d->playlistInterface.isNull() ) { Tomahawk::source_ptr source = SourceList::instance()->get( id() ); - m_playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::SourcePlaylistInterface( source.data() ) ); + d->playlistInterface = Tomahawk::playlistinterface_ptr( new Tomahawk::SourcePlaylistInterface( source.data() ) ); } - return m_playlistInterface; + return d->playlistInterface; } QSharedPointer Source::acquireLock() { - return QSharedPointer( new QMutexLocker( &m_mutex ) ); + Q_D( Source ); + + return QSharedPointer( new QMutexLocker( &d->mutex ) ); } void Source::onPlaybackStarted( const Tomahawk::track_ptr& track, unsigned int duration ) { + Q_D( Source ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << track->toString(); - m_currentTrack = track->toQuery(); - m_currentTrackTimer.start( duration * 1000 + 900000 ); // duration comes in seconds + d->currentTrack = track->toQuery(); + d->currentTrackTimer.start( duration * 1000 + 900000 ); // duration comes in seconds - if ( m_playlistInterface.isNull() ) + if ( d->playlistInterface.isNull() ) playlistInterface(); emit playbackStarted( track ); @@ -560,10 +610,12 @@ Source::onPlaybackStarted( const Tomahawk::track_ptr& track, unsigned int durati void Source::onPlaybackFinished( const Tomahawk::track_ptr& track, const Tomahawk::PlaybackLog& log ) { + Q_D( Source ); + tDebug() << Q_FUNC_INFO << track->toString(); emit playbackFinished( track, log ); - m_currentTrack.clear(); + d->currentTrack.clear(); emit stateChanged(); } @@ -571,7 +623,9 @@ Source::onPlaybackFinished( const Tomahawk::track_ptr& track, const Tomahawk::Pl void Source::trackTimerFired() { - m_currentTrack.clear(); + Q_D( Source ); + + d->currentTrack.clear(); emit stateChanged(); } @@ -579,37 +633,47 @@ Source::trackTimerFired() QString Source::lastCmdGuid() const { - QMutexLocker lock( &m_cmdMutex ); - return m_lastCmdGuid; + Q_D( const Source ); + + QMutexLocker lock( &d->cmdMutex ); + return d->lastCmdGuid; } void Source::setLastCmdGuid( const QString& guid ) { + Q_D( Source ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "name is" << friendlyName() << "and guid is" << guid; - QMutexLocker lock( &m_cmdMutex ); - m_lastCmdGuid = guid; + QMutexLocker lock( &d->cmdMutex ); + d->lastCmdGuid = guid; } void Source::addCommand( const QSharedPointer& command ) { - QMutexLocker lock( &m_cmdMutex ); + Q_D( Source ); + + QMutexLocker lock( &d->cmdMutex ); - m_cmds << command; + d->cmds << command; if ( !command->singletonCmd() ) - m_lastCmdGuid = command->guid(); + { + d->lastCmdGuid = command->guid(); + } - m_commandCount = m_cmds.count(); + d->commandCount = d->cmds.count(); } void Source::executeCommands() { + Q_D( Source ); + if ( QThread::currentThread() != thread() ) { QMetaObject::invokeMethod( this, "executeCommands", Qt::QueuedConnection ); @@ -618,20 +682,20 @@ Source::executeCommands() bool commandsAvail = false; { - QMutexLocker lock( &m_cmdMutex ); - commandsAvail = !m_cmds.isEmpty(); + QMutexLocker lock( &d->cmdMutex ); + commandsAvail = !d->cmds.isEmpty(); } if ( commandsAvail ) { - QMutexLocker lock( &m_cmdMutex ); + QMutexLocker lock( &d->cmdMutex ); QList< QSharedPointer > cmdGroup; - QSharedPointer cmd = m_cmds.takeFirst(); + QSharedPointer cmd = d->cmds.takeFirst(); while ( cmd->groupable() ) { cmdGroup << cmd; - if ( !m_cmds.isEmpty() && m_cmds.first()->groupable() && m_cmds.first()->commandname() == cmd->commandname() ) - cmd = m_cmds.takeFirst(); + if ( !d->cmds.isEmpty() && d->cmds.first()->groupable() && d->cmds.first()->commandname() == cmd->commandname() ) + cmd = d->cmds.takeFirst(); else break; } @@ -648,20 +712,20 @@ Source::executeCommands() Database::instance()->enqueue( cmd ); } - int percentage = ( float( m_commandCount - m_cmds.count() ) / (float)m_commandCount ) * 100.0; - m_textStatus = tr( "Saving (%1%)" ).arg( percentage ); + int percentage = ( float( d->commandCount - d->cmds.count() ) / (float)d->commandCount ) * 100.0; + d->textStatus = tr( "Saving (%1%)" ).arg( percentage ); emit stateChanged(); } else { - if ( m_updateIndexWhenSynced ) + if ( d->updateIndexWhenSynced ) { - m_updateIndexWhenSynced = false; + d->updateIndexWhenSynced = false; updateTracks(); } - m_textStatus = QString(); - m_state = DBSyncConnection::SYNCED; + d->textStatus = QString(); + d->state = SYNCED; emit commandsFinished(); emit stateChanged(); @@ -712,15 +776,21 @@ Source::updateTracks() void Source::updateIndexWhenSynced() { - m_updateIndexWhenSynced = true; + Q_D( Source ); + + d->updateIndexWhenSynced = true; } QString Source::textStatus() const { - if ( !m_textStatus.isEmpty() ) - return m_textStatus; + Q_D( const Source ); + + if ( !d->textStatus.isEmpty() ) + { + return d->textStatus; + } if ( !currentTrack().isNull() ) { @@ -728,7 +798,7 @@ Source::textStatus() const } // do not use isOnline() here - it will always return true for the local source - if ( m_online ) + if ( d->online ) { return tr( "Online" ); } @@ -738,8 +808,10 @@ Source::textStatus() const } } -DBSyncConnection::State +DBSyncConnectionState Source::state() const { - return m_state; + Q_D( const Source ); + + return d->state; } diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index bae4492750..13f8c647f2 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -27,23 +27,29 @@ #include "Typedefs.h" #include "accounts/AccountManager.h" -#include "network/ControlConnection.h" -#include "network/DbSyncConnection.h" -#include "collection/Collection.h" +#include "network/DBSyncConnectionState.h" #include "utils/TomahawkUtils.h" #include "DllMacro.h" +class ControlConnection; +class DatabaseCommand; +class DatabaseCommand_AddFiles; class DatabaseCommand_DeleteFiles; class DatabaseCommand_LoadAllSources; class DatabaseCommand_LogPlayback; class DatabaseCommand_SocialAction; class DatabaseCommand_UpdateSearchIndex; +class DBSyncConnection; class MusicScanner; namespace Tomahawk { +class PlaybackLog; +class Resolver; +class SourcePrivate; + class DLLEXPORT Source : public QObject { Q_OBJECT @@ -96,7 +102,7 @@ friend class ::MusicScanner; Tomahawk::query_ptr currentTrack() const; QString textStatus() const; - DBSyncConnection::State state() const; + Tomahawk::DBSyncConnectionState state() const; Tomahawk::playlistinterface_ptr playlistInterface(); @@ -138,7 +144,7 @@ private slots: void setOffline(); void setOnline(); - void onStateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info ); + void onStateChanged( DBSyncConnectionState newstate, DBSyncConnectionState oldstate, const QString& info ); void onPlaybackStarted( const Tomahawk::track_ptr& track, unsigned int duration ); void onPlaybackFinished( const Tomahawk::track_ptr& track, const Tomahawk::PlaybackLog& log ); @@ -148,40 +154,13 @@ private slots: void addCommand( const QSharedPointer& command ); private: + Q_DECLARE_PRIVATE( Source ) + SourcePrivate* d_ptr; + static bool friendlyNamesLessThan( const QString& first, const QString& second ); //lessThan for sorting void updateTracks(); void reportSocialAttributesChanged( DatabaseCommand_SocialAction* action ); - - QList< QSharedPointer > m_collections; - QVariantMap m_stats; - - bool m_isLocal; - bool m_online; - QString m_nodeId; - QString m_friendlyname; - QString m_dbFriendlyName; - int m_id; - bool m_scrubFriendlyName; - bool m_updateIndexWhenSynced; - - Tomahawk::query_ptr m_currentTrack; - QString m_textStatus; - DBSyncConnection::State m_state; - QTimer m_currentTrackTimer; - - QPixmap* m_avatar; - bool m_avatarLoaded; - - QPointer m_cc; - QList< QSharedPointer > m_cmds; - int m_commandCount; - QString m_lastCmdGuid; - mutable QMutex m_cmdMutex; - QMutex m_setControlConnectionMutex; - QMutex m_mutex; - - Tomahawk::playlistinterface_ptr m_playlistInterface; }; } //ns diff --git a/src/libtomahawk/SourceList.cpp b/src/libtomahawk/SourceList.cpp index 90b687e8e1..619d73095f 100644 --- a/src/libtomahawk/SourceList.cpp +++ b/src/libtomahawk/SourceList.cpp @@ -30,6 +30,8 @@ #include "utils/Logger.h" +#include + using namespace Tomahawk; SourceList* SourceList::s_instance = 0; diff --git a/src/libtomahawk/Source_p.h b/src/libtomahawk/Source_p.h new file mode 100644 index 0000000000..7249937eaa --- /dev/null +++ b/src/libtomahawk/Source_p.h @@ -0,0 +1,86 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef SOURCE_P_H +#define SOURCE_P_H + +#include "Source.h" + +#include + +namespace Tomahawk +{ + +class SourcePrivate +{ +public: + SourcePrivate( Source* q, int _id, const QString& _nodeid ) + : q_ptr ( q ) + , isLocal( false ) + , online( false ) + , nodeId( _nodeid ) + , id( _id ) + , updateIndexWhenSynced( false ) + , state( UNKNOWN ) + , avatar( 0 ) + , avatarLoaded( false ) + , cc( 0 ) + , commandCount( 0 ) + { + } + Source* q_ptr; + Q_DECLARE_PUBLIC ( Source ) + +private: + QList< QSharedPointer > collections; + QVariantMap stats; + + bool isLocal; + bool online; + QString nodeId; + QString friendlyname; + QString dbFriendlyName; + int id; + bool scrubFriendlyName; + bool updateIndexWhenSynced; + + Tomahawk::query_ptr currentTrack; + QString textStatus; + DBSyncConnectionState state; + QTimer currentTrackTimer; + + QPixmap* avatar; + bool avatarLoaded; + + QPointer cc; + QList< QSharedPointer > cmds; + int commandCount; + QString lastCmdGuid; + QMutex setControlConnectionMutex; + QMutex mutex; + + Tomahawk::playlistinterface_ptr playlistInterface; + + mutable QMutex cmdMutex; +}; + +} + +#endif // SOURCE_P_H diff --git a/src/libtomahawk/Typedefs.h b/src/libtomahawk/Typedefs.h index 0150a31f72..9c38318d60 100644 --- a/src/libtomahawk/Typedefs.h +++ b/src/libtomahawk/Typedefs.h @@ -30,6 +30,10 @@ #include +// TODO: Move into Tomahawk namespace +class Msg; +typedef QSharedPointer msg_ptr; + namespace Tomahawk { class Artist; @@ -76,6 +80,13 @@ namespace Tomahawk typedef QString QID; //query id typedef QString RID; //result id + enum ACL { + NotFound = 0, + Deny = 1, + Read = 2, + Stream = 3 + }; + enum GeneratorMode { OnDemand = 0, @@ -86,7 +97,7 @@ namespace Tomahawk { Mixed = 0, DatabaseMode, - InfoSystemMode, + InfoSystemMode }; enum ModelTypes @@ -228,7 +239,7 @@ namespace Tomahawk typedef QPointer< InfoPlugin > InfoPluginPtr; } -}; // ns +} // ns typedef int AudioErrorCode; typedef int AudioState; @@ -246,6 +257,7 @@ inline static QString uuid() Q_DECLARE_METATYPE( QModelIndex ) Q_DECLARE_METATYPE( QPersistentModelIndex ) -Q_DECLARE_METATYPE( QNetworkReply* ); +Q_DECLARE_METATYPE( QNetworkReply* ) +Q_DECLARE_METATYPE( Tomahawk::ACL ) #endif // TYPEDEFS_H diff --git a/src/libtomahawk/accounts/spotify/SpotifyInfoPlugin.cpp b/src/libtomahawk/accounts/spotify/SpotifyInfoPlugin.cpp index ead27cba6d..aa0fcc1548 100644 --- a/src/libtomahawk/accounts/spotify/SpotifyInfoPlugin.cpp +++ b/src/libtomahawk/accounts/spotify/SpotifyInfoPlugin.cpp @@ -22,6 +22,7 @@ #include "utils/Closure.h" #include "utils/Logger.h" +#include using namespace Tomahawk; using namespace Tomahawk::InfoSystem; diff --git a/src/libtomahawk/database/DatabaseCommand_CreatePlaylist.cpp b/src/libtomahawk/database/DatabaseCommand_CreatePlaylist.cpp index c6a545c27e..64b17d7331 100644 --- a/src/libtomahawk/database/DatabaseCommand_CreatePlaylist.cpp +++ b/src/libtomahawk/database/DatabaseCommand_CreatePlaylist.cpp @@ -20,12 +20,14 @@ #include -#include "SourceList.h" -#include "DatabaseImpl.h" -#include "TomahawkSqlQuery.h" #include "network/Servent.h" #include "utils/Logger.h" +#include "DatabaseImpl.h" +#include "Playlist.h" +#include "SourceList.h" +#include "TomahawkSqlQuery.h" + using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_DeletePlaylist.cpp b/src/libtomahawk/database/DatabaseCommand_DeletePlaylist.cpp index 7fc38f64a1..4b1209bd10 100644 --- a/src/libtomahawk/database/DatabaseCommand_DeletePlaylist.cpp +++ b/src/libtomahawk/database/DatabaseCommand_DeletePlaylist.cpp @@ -20,9 +20,12 @@ #include +#include "collection/Collection.h" #include "network/Servent.h" #include "utils/Logger.h" +#include "Playlist.h" + using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp index 5071871dfc..1ee47c8d13 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp @@ -18,14 +18,16 @@ #include "DatabaseCommand_SetDynamicPlaylistRevision.h" -#include "Source.h" -#include "DatabaseImpl.h" -#include "TomahawkSqlQuery.h" +#include "collection/Collection.h" #include "playlist/dynamic/DynamicPlaylist.h" #include "playlist/dynamic/DynamicControl.h" #include "network/Servent.h" #include "utils/Logger.h" +#include "DatabaseImpl.h" +#include "Source.h" +#include "TomahawkSqlQuery.h" + #include DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRevision( const Tomahawk::source_ptr& s, diff --git a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp index add79ce73e..94fc9983f2 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp @@ -20,12 +20,14 @@ #include -#include "Source.h" -#include "DatabaseImpl.h" -#include "TomahawkSqlQuery.h" +#include "collection/Collection.h" #include "network/Servent.h" #include "utils/Logger.h" +#include "DatabaseImpl.h" +#include "Source.h" +#include "TomahawkSqlQuery.h" + using namespace Tomahawk; diff --git a/src/libtomahawk/jobview/AclJobItem.cpp b/src/libtomahawk/jobview/AclJobItem.cpp index f33fd199f0..5078df1268 100644 --- a/src/libtomahawk/jobview/AclJobItem.cpp +++ b/src/libtomahawk/jobview/AclJobItem.cpp @@ -146,9 +146,9 @@ ACLJobDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QSt { QMouseEvent* me = static_cast< QMouseEvent* >( event ); if ( m_savedAcceptRect.contains( me->pos() ) ) - emit aclResult( ACLRegistry::Stream ); + emit aclResult( Tomahawk::Stream ); else if ( m_savedDenyRect.contains( me->pos() ) ) - emit aclResult( ACLRegistry::Deny ); + emit aclResult( Tomahawk::Deny ); return true; } @@ -195,7 +195,7 @@ ACLJobDelegate::emitSizeHintChanged( const QModelIndex& index ) void -ACLJobItem::aclResult( ACLRegistry::ACL result ) +ACLJobItem::aclResult( Tomahawk::ACL result ) { tLog() << Q_FUNC_INFO; m_user.acl = result; diff --git a/src/libtomahawk/jobview/AclJobItem.h b/src/libtomahawk/jobview/AclJobItem.h index e9b5152645..22fb32d99d 100644 --- a/src/libtomahawk/jobview/AclJobItem.h +++ b/src/libtomahawk/jobview/AclJobItem.h @@ -43,7 +43,7 @@ class DLLEXPORT ACLJobDelegate : public QStyledItemDelegate signals: void update( const QModelIndex& idx ); - void aclResult( ACLRegistry::ACL result ); + void aclResult( Tomahawk::ACL result ); protected: virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); @@ -84,7 +84,7 @@ class DLLEXPORT ACLJobItem : public JobStatusItem void userDecision( ACLRegistry::User user ); public slots: - void aclResult( ACLRegistry::ACL result ); + void aclResult( Tomahawk::ACL result ); private: QStyledItemDelegate* m_delegate; diff --git a/src/libtomahawk/jobview/JobStatusView.cpp b/src/libtomahawk/jobview/JobStatusView.cpp index 119ec506e1..8c15d3bed7 100644 --- a/src/libtomahawk/jobview/JobStatusView.cpp +++ b/src/libtomahawk/jobview/JobStatusView.cpp @@ -104,7 +104,7 @@ JobStatusView::customDelegateJobInserted( int row, JobStatusItem* item ) if ( delegate ) { connect( delegate, SIGNAL( update( const QModelIndex& ) ), m_view, SLOT( update( const QModelIndex & ) ) ); - connect( delegate, SIGNAL( aclResult( ACLRegistry::ACL ) ), item, SLOT( aclResult( ACLRegistry::ACL ) ) ); + connect( delegate, SIGNAL( aclResult( Tomahawk::ACL ) ), item, SLOT( aclResult( Tomahawk::ACL ) ) ); delegate->emitSizeHintChanged( m_model->index( row, 0 ) ); } else diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 917f825969..44699ef118 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -305,8 +305,8 @@ Connection::checkACL() } tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking ACL for" << name(); - connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ), this, SLOT( checkACLResult( QString, QString, ACLRegistry::ACL ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( ACLRegistry::ACL, ACLRegistry::NotFound ) ); + connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL ) ), this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL ) ), Qt::QueuedConnection ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( Tomahawk::ACL, Tomahawk::NotFound ) ); } @@ -317,7 +317,7 @@ Connection::bareName() const } void -Connection::checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ) +Connection::checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL peerStatus ) { if ( nodeid != d_func()->nodeid ) { @@ -330,9 +330,9 @@ Connection::checkACLResult( const QString &nodeid, const QString &username, ACLR return; } - disconnect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ) ); + disconnect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL ) ) ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; - if ( peerStatus == ACLRegistry::Stream ) + if ( peerStatus == Tomahawk::Stream ) { QTimer::singleShot( 0, this, SLOT( doSetup() ) ); return; diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index 88cbc1f6ee..df361da7f7 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -21,23 +21,14 @@ #ifndef CONNECTION_H #define CONNECTION_H -#include "Msg.h" -#include "MsgProcessor.h" -#include "AclRegistry.h" - +#include "Typedefs.h" #include "DllMacro.h" -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include class ConnectionPrivate; class Servent; @@ -69,7 +60,9 @@ Q_OBJECT Servent* servent() const; - // get public port of remote peer: + /** + * Get public port of remote peer. + */ int peerPort() const; void setPeerPort( int p ); @@ -123,7 +116,7 @@ private slots: void readyRead(); void doSetup(); void checkACL(); - void checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ); + void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL peerStatus ); void bytesWritten( qint64 ); void calcStats(); diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h index 3ea207c36a..e3cd042dee 100644 --- a/src/libtomahawk/network/Connection_p.h +++ b/src/libtomahawk/network/Connection_p.h @@ -23,10 +23,13 @@ #include "Connection.h" -class ConnectionPrivate : public QObject -{ -Q_OBJECT +#include "MsgProcessor.h" + +#include +#include +class ConnectionPrivate +{ public: ConnectionPrivate( Connection* q ) : q_ptr ( q ) diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 2a870aad7e..1de34b0257 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -24,6 +24,7 @@ #include "database/DatabaseCommand_CollectionStats.h" #include "DbSyncConnection.h" #include "SourceList.h" +#include "MsgProcessor.h" #include "network/DbSyncConnection.h" #include "network/Servent.h" #include "sip/PeerInfo.h" diff --git a/src/libtomahawk/network/ControlConnection.h b/src/libtomahawk/network/ControlConnection.h index f4bfb3c4fe..bddc0f9f03 100644 --- a/src/libtomahawk/network/ControlConnection.h +++ b/src/libtomahawk/network/ControlConnection.h @@ -32,6 +32,9 @@ #include "DllMacro.h" +#include +#include + class Servent; class DBSyncConnection; diff --git a/src/libtomahawk/network/DBSyncConnectionState.h b/src/libtomahawk/network/DBSyncConnectionState.h new file mode 100644 index 0000000000..c862c09bf2 --- /dev/null +++ b/src/libtomahawk/network/DBSyncConnectionState.h @@ -0,0 +1,41 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef DBSYNCCONNECTIONSTATE_H +#define DBSYNCCONNECTIONSTATE_H + +namespace Tomahawk +{ + +enum DBSyncConnectionState +{ + UNKNOWN, + CHECKING, + FETCHING, + PARSING, + SAVING, + SYNCED, + SCANNING, + SHUTDOWN +}; + +} + + +#endif // DBSYNCCONNECTIONSTATE_H diff --git a/src/libtomahawk/network/DbSyncConnection.cpp b/src/libtomahawk/network/DbSyncConnection.cpp index d06baf6155..86f30c2130 100644 --- a/src/libtomahawk/network/DbSyncConnection.cpp +++ b/src/libtomahawk/network/DbSyncConnection.cpp @@ -35,6 +35,7 @@ #include "database/DatabaseCommand.h" #include "database/DatabaseCommand_CollectionStats.h" #include "database/DatabaseCommand_LoadOps.h" +#include "MsgProcessor.h" #include "RemoteCollection.h" #include "Source.h" #include "SourceList.h" @@ -51,8 +52,8 @@ DBSyncConnection::DBSyncConnection( Servent* s, const source_ptr& src ) { qDebug() << Q_FUNC_INFO << src->id() << thread(); - connect( this, SIGNAL( stateChanged( DBSyncConnection::State, DBSyncConnection::State, QString ) ), - m_source.data(), SLOT( onStateChanged( DBSyncConnection::State, DBSyncConnection::State, QString ) ) ); + connect( this, SIGNAL( stateChanged( Tomahawk::DBSyncConnectionState, Tomahawk::DBSyncConnectionState, QString ) ), + m_source.data(), SLOT( onStateChanged( Tomahawk::DBSyncConnectionState, Tomahawk::DBSyncConnectionState, QString ) ) ); connect( m_source.data(), SIGNAL( commandsFinished() ), this, SLOT( lastOpApplied() ) ); @@ -71,12 +72,12 @@ DBSyncConnection::~DBSyncConnection() void -DBSyncConnection::changeState( State newstate ) +DBSyncConnection::changeState( DBSyncConnectionState newstate ) { if ( m_state == SHUTDOWN ) return; - State s = m_state; + DBSyncConnectionState s = m_state; m_state = newstate; qDebug() << "DBSYNC State changed from" << s << "to" << newstate << "- source:" << m_source->id(); emit stateChanged( newstate, s, "" ); diff --git a/src/libtomahawk/network/DbSyncConnection.h b/src/libtomahawk/network/DbSyncConnection.h index 427a2c4cea..cad382ea36 100644 --- a/src/libtomahawk/network/DbSyncConnection.h +++ b/src/libtomahawk/network/DbSyncConnection.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +21,7 @@ #define DBSYNCCONNECTION_H #include "network/Connection.h" +#include "network/DBSyncConnectionState.h" #include "database/Op.h" #include "Typedefs.h" @@ -37,18 +39,6 @@ class DBSyncConnection : public Connection Q_OBJECT public: - enum State - { - UNKNOWN, - CHECKING, - FETCHING, - PARSING, - SAVING, - SYNCED, - SCANNING, - SHUTDOWN - }; - explicit DBSyncConnection( Servent* s, const Tomahawk::source_ptr& src ); virtual ~DBSyncConnection(); @@ -56,7 +46,7 @@ Q_OBJECT Connection* clone(); signals: - void stateChanged( DBSyncConnection::State newstate, DBSyncConnection::State oldstate, const QString& info ); + void stateChanged( Tomahawk::DBSyncConnectionState newstate, Tomahawk::DBSyncConnectionState oldstate, const QString& info ); protected slots: virtual void handleMsg( msg_ptr msg ); @@ -77,7 +67,7 @@ private slots: private: void synced(); - void changeState( State newstate ); + void changeState( Tomahawk::DBSyncConnectionState newstate ); int m_fetchCount; Tomahawk::source_ptr m_source; @@ -85,7 +75,7 @@ private slots: QString m_lastSentOp; - State m_state; + Tomahawk::DBSyncConnectionState m_state; }; #endif // DBSYNCCONNECTION_H diff --git a/src/libtomahawk/network/Msg.h b/src/libtomahawk/network/Msg.h index 058d89270b..77aff75b21 100644 --- a/src/libtomahawk/network/Msg.h +++ b/src/libtomahawk/network/Msg.h @@ -30,6 +30,8 @@ #ifndef MSG_H #define MSG_H +#include "Typedefs.h" + #include #include #include @@ -39,9 +41,6 @@ #include #include -class Msg; -typedef QSharedPointer msg_ptr; - class Msg { friend class MsgProcessor; diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 8731972b00..b8632b43fb 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -30,6 +30,7 @@ #include "database/Database.h" #include "database/DatabaseImpl.h" #include "network/ConnectionManager.h" +#include "network/DbSyncConnection.h" #include "StreamConnection.h" #include "SourceList.h" #include "sip/SipInfo.h" @@ -210,7 +211,7 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) break; } - connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, ACLRegistry::ACL ) ), this, SLOT( checkACLResult( QString, QString, ACLRegistry::ACL ) ), Qt::QueuedConnection ); + connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL ) ), this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL ) ), Qt::QueuedConnection ); return true; } @@ -914,7 +915,7 @@ Servent::socketError( QAbstractSocket::SocketError e ) void -Servent::checkACLResult( const QString& nodeid, const QString& username, ACLRegistry::ACL peerStatus ) +Servent::checkACLResult( const QString& nodeid, const QString& username, Tomahawk::ACL peerStatus ) { if ( !d_func()->queuedForACLResult.contains( username ) ) @@ -928,7 +929,7 @@ Servent::checkACLResult( const QString& nodeid, const QString& username, ACLRegi tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; QSet peerInfos = d_func()->queuedForACLResult.value( username ).value( nodeid ); - if ( peerStatus == ACLRegistry::Stream ) + if ( peerStatus == Tomahawk::Stream ) { foreach ( Tomahawk::peerinfo_ptr peerInfo, peerInfos ) { diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 76ccbc4228..47612968e3 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -168,7 +168,7 @@ private slots: void deleteLazyOffer( const QString& key ); void readyRead(); void socketError( QAbstractSocket::SocketError e ); - void checkACLResult( const QString &nodeid, const QString &username, ACLRegistry::ACL peerStatus ); + void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL peerStatus ); Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); diff --git a/src/libtomahawk/network/StreamConnection.cpp b/src/libtomahawk/network/StreamConnection.cpp index 1a02627a3b..c204ce3477 100644 --- a/src/libtomahawk/network/StreamConnection.cpp +++ b/src/libtomahawk/network/StreamConnection.cpp @@ -26,6 +26,7 @@ #include "network/Servent.h" #include "database/DatabaseCommand_LoadFiles.h" #include "database/Database.h" +#include "MsgProcessor.h" #include "SourceList.h" #include "utils/Logger.h" diff --git a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp index d146eec0f0..01f10ca6f3 100644 --- a/src/libtomahawk/playlist/PlaylistItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistItemDelegate.cpp @@ -22,6 +22,8 @@ #include #include #include +#include + #include #include "Query.h" diff --git a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp index e6b36b079b..fdb6bf5145 100644 --- a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "Query.h" #include "Result.h" diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp index dfbf52956e..303f8d26cb 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp @@ -19,6 +19,7 @@ #include "playlist/dynamic/DynamicModel.h" +#include "DynamicPlaylist.h" #include "GeneratorInterface.h" #include "Pipeline.h" #include "Query.h" diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index f4d1e0857d..54801067b9 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -21,6 +21,7 @@ #include "utils/TomahawkUtils.h" +#include "Query.h" #include "TomahawkVersion.h" #include "config.h" #include "TomahawkSettings.h" diff --git a/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.cpp b/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.cpp index df0ffc51d1..c905c95a10 100644 --- a/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.cpp +++ b/src/libtomahawk/widgets/RecentlyPlayedPlaylistsModel.cpp @@ -20,11 +20,13 @@ #include "RecentlyPlayedPlaylistsModel.h" -#include "TomahawkSettings.h" #include "audio/AudioEngine.h" -#include "SourceList.h" -#include "utils/Logger.h" +#include "collection/Collection.h" #include "playlist/dynamic/DynamicPlaylist.h" +#include "utils/Logger.h" + +#include "TomahawkSettings.h" +#include "SourceList.h" #include "Playlist.h" using namespace Tomahawk; diff --git a/src/tomahawk/AclRegistryImpl.cpp b/src/tomahawk/AclRegistryImpl.cpp index 0ecde655de..5e6422d39a 100644 --- a/src/tomahawk/AclRegistryImpl.cpp +++ b/src/tomahawk/AclRegistryImpl.cpp @@ -53,14 +53,14 @@ ACLRegistryImpl::~ACLRegistryImpl() } -ACLRegistry::ACL -ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, ACLRegistry::ACL globalType, bool skipEmission ) +Tomahawk::ACL +ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, Tomahawk::ACL globalType, bool skipEmission ) { if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() ) { if ( !skipEmission ) - QMetaObject::invokeMethod( this, "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( const QString&, dbid ), Q_ARG( const QString &, username ), Q_ARG( ACLRegistry::ACL, globalType ), Q_ARG( bool, skipEmission ) ); - return ACLRegistry::NotFound; + QMetaObject::invokeMethod( this, "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( const QString&, dbid ), Q_ARG( const QString &, username ), Q_ARG( Tomahawk::ACL, globalType ), Q_ARG( bool, skipEmission ) ); + return Tomahawk::NotFound; } #ifndef ENABLE_HEADLESS @@ -77,8 +77,8 @@ ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, if ( account->accountFriendlyName() == username ) { if ( !skipEmission ) - emit aclResult( dbid, username, ACLRegistry::Stream ); - return ACLRegistry::Stream; + emit aclResult( dbid, username, Tomahawk::Stream ); + return Tomahawk::Stream; } } } @@ -119,24 +119,24 @@ ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, } if ( skipEmission ) - return ACLRegistry::NotFound; + return Tomahawk::NotFound; // User was not found, create a new user entry ACLRegistry::User user; user.knownDbids.append( dbid ); user.knownAccountIds.append( username ); - if ( globalType != ACLRegistry::NotFound ) + if ( globalType != Tomahawk::NotFound ) user.acl = globalType; #ifdef ENABLE_HEADLESS - user.acl = ACLRegistry::Stream; + user.acl = Tomahawk::Stream; #else if ( !TomahawkUtils::headless() ) { getUserDecision( user, username ); - return ACLRegistry::NotFound; + return Tomahawk::NotFound; } else - user.acl = ACLRegistry::Stream; + user.acl = Tomahawk::Stream; #endif m_cache.append( user ); save(); @@ -200,8 +200,8 @@ ACLRegistryImpl::queueNextJob() bool found = false; foreach( QString dbid, user.knownDbids ) { - ACLRegistry::ACL acl = isAuthorizedUser( dbid, job->username(), ACLRegistry::NotFound, true ); - if ( acl != ACLRegistry::NotFound ) + Tomahawk::ACL acl = isAuthorizedUser( dbid, job->username(), Tomahawk::NotFound, true ); + if ( acl != Tomahawk::NotFound ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Found existing acl entry for =" << user.knownAccountIds.first(); found = true; diff --git a/src/tomahawk/AclRegistryImpl.h b/src/tomahawk/AclRegistryImpl.h index d0a9037bcf..414c0d73f6 100644 --- a/src/tomahawk/AclRegistryImpl.h +++ b/src/tomahawk/AclRegistryImpl.h @@ -51,9 +51,9 @@ public slots: * @param dbid DBID of peer * @param globalType Global ACL to store if peer not found; if ACLRegistry::NotFound, does not store the peer Defaults to ACLRegistry::NotFound. * @param username If not empty, will store the given username along with the new ACL value. Defaults to QString(). - * @return ACLRegistry::ACL + * @return Tomahawk::ACL **/ - virtual ACLRegistry::ACL isAuthorizedUser( const QString &dbid, const QString &username, ACLRegistry::ACL globalType = ACLRegistry::NotFound, bool skipEmission = false ); + virtual Tomahawk::ACL isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL globalType = Tomahawk::NotFound, bool skipEmission = false ); virtual void wipeEntries(); protected: diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index 1158d39472..6bdf8919e5 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -43,6 +43,7 @@ //#include "playlist/dynamic/database/DatabaseGenerator.h" #include "playlist/XspfUpdater.h" #include "network/Servent.h" +#include "network/DbSyncConnection.h" #include "web/Api_v1.h" #include "SourceList.h" #include "ShortcutHandler.h" @@ -361,7 +362,7 @@ void TomahawkApp::registerMetaTypes() { qRegisterMetaType< QSharedPointer >("QSharedPointer"); - qRegisterMetaType< DBSyncConnection::State >("DBSyncConnection::State"); + qRegisterMetaType< DBSyncConnectionState >("DBSyncConnectionState"); qRegisterMetaType< msg_ptr >("msg_ptr"); qRegisterMetaType< QList >("QList"); qRegisterMetaType< QList >("QList"); diff --git a/src/tomahawk/sourcetree/SourceDelegate.cpp b/src/tomahawk/sourcetree/SourceDelegate.cpp index 86a95bf2aa..edc7ccd9b0 100644 --- a/src/tomahawk/sourcetree/SourceDelegate.cpp +++ b/src/tomahawk/sourcetree/SourceDelegate.cpp @@ -38,11 +38,13 @@ #include "ViewManager.h" #include "ContextMenu.h" #include "resolvers/ScriptCollection.h" +#include "network/DBSyncConnectionState.h" #include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" +#include #include #include #include @@ -402,7 +404,7 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem& Q_ASSERT( colItem ); bool status = !( !colItem || colItem->source().isNull() || !colItem->source()->isOnline() ); - if ( colItem->source() && colItem->source()->currentTrack() && colItem->source()->state() == DBSyncConnection::SYNCED ) + if ( colItem->source() && colItem->source()->currentTrack() && colItem->source()->state() == Tomahawk::SYNCED ) m_trackRects[ index ] = textRect.adjusted( 0, 0, -textRect.width() + painter->fontMetrics().width( text ), 0 ); else m_trackRects.remove( index ); From aaf4ced6db5bdbeae586a8dac34ab90a17023623 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 20:25:36 +0200 Subject: [PATCH 431/565] Remove not needed ControlConnection include --- src/libtomahawk/network/RemoteCollection.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/RemoteCollection.h b/src/libtomahawk/network/RemoteCollection.h index 16ef70f7e6..f1706a94d8 100644 --- a/src/libtomahawk/network/RemoteCollection.h +++ b/src/libtomahawk/network/RemoteCollection.h @@ -21,9 +21,10 @@ #include "Typedefs.h" -#include "network/ControlConnection.h" #include "database/DatabaseCollection.h" +class ControlConnection; + class RemoteCollection : public DatabaseCollection { Q_OBJECT From 70706c9eb1a7f8e524ff1060dc15ab49ee301347 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 20:36:13 +0200 Subject: [PATCH 432/565] Move m_name into ConnectionPrivate --- src/libtomahawk/network/Connection.cpp | 13 +++++++++---- src/libtomahawk/network/Connection.h | 1 - src/libtomahawk/network/Connection_p.h | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 44699ef118..f1bdbb9e2e 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -206,13 +206,17 @@ Connection::markAsFailed() void Connection::setName( const QString& n ) { - m_name = n; + Q_D( Connection ); + + d->name = n; } QString Connection::name() const { - return m_name; + Q_D( const Connection ); + + return d->name; } void @@ -273,15 +277,16 @@ Connection::peerIpAddress() const void Connection::start( QTcpSocket* sock ) { + Q_D( Connection ); Q_ASSERT( m_sock.isNull() ); Q_ASSERT( sock ); Q_ASSERT( sock->isValid() ); m_sock = sock; - if ( m_name.isEmpty() ) + if ( d->name.isEmpty() ) { - m_name = QString( "peer[%1]" ).arg( m_sock->peerAddress().toString() ); + d->name = QString( "peer[%1]" ).arg( m_sock->peerAddress().toString() ); } QTimer::singleShot( 0, this, SLOT( checkACL() ) ); diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index df361da7f7..e890208eac 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -127,7 +127,6 @@ private slots: Servent* m_servent; bool m_outbound, m_ready, m_onceonly; msg_ptr m_firstmsg; - QString m_name; private: Q_DECLARE_PRIVATE( Connection ) diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h index e3cd042dee..7f08795b14 100644 --- a/src/libtomahawk/network/Connection_p.h +++ b/src/libtomahawk/network/Connection_p.h @@ -59,6 +59,7 @@ class ConnectionPrivate qint64 tx_bytes_requested; qint64 rx_bytes; QString id; + QString name; QString nodeid; QTimer* statstimer; From 7efa2fd781812c65bf268104257de09aa783829d Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 20:52:43 +0200 Subject: [PATCH 433/565] Tomahawk::ACL -> Tomahawk::ACL::Type --- src/libtomahawk/AclRegistry.cpp | 4 ++-- src/libtomahawk/AclRegistry.h | 12 +++++------ src/libtomahawk/Typedefs.h | 16 ++++++++------ src/libtomahawk/jobview/AclJobItem.cpp | 6 +++--- src/libtomahawk/jobview/AclJobItem.h | 4 ++-- src/libtomahawk/jobview/JobStatusView.cpp | 2 +- src/libtomahawk/network/Connection.cpp | 12 ++++++----- src/libtomahawk/network/Connection.h | 2 +- src/libtomahawk/network/Servent.cpp | 8 ++++--- src/libtomahawk/network/Servent.h | 2 +- src/tomahawk/AclRegistryImpl.cpp | 26 +++++++++++------------ src/tomahawk/AclRegistryImpl.h | 4 ++-- 12 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/libtomahawk/AclRegistry.cpp b/src/libtomahawk/AclRegistry.cpp index 8c69e02b23..6c6486feb3 100644 --- a/src/libtomahawk/AclRegistry.cpp +++ b/src/libtomahawk/AclRegistry.cpp @@ -66,7 +66,7 @@ QDataStream& operator>>( QDataStream &in, ACLRegistry::User &user ) } int aclIn; in >> aclIn; - user.acl = (Tomahawk::ACL)( aclIn ); + user.acl = (Tomahawk::ACL::Type)( aclIn ); } return in; } @@ -91,7 +91,7 @@ ACLRegistry::setInstance( ACLRegistry* instance ) ACLRegistry::ACLRegistry( QObject* parent ) : QObject( parent ) { - qRegisterMetaType< Tomahawk::ACL >( "Tomahawk::ACL" ); + qRegisterMetaType< Tomahawk::ACL::Type >( "Tomahawk::ACL::Type" ); qRegisterMetaType< ACLRegistry::User >( "ACLRegistry::User" ); qRegisterMetaTypeStreamOperators< ACLRegistry::User >( "ACLRegistry::User" ); } diff --git a/src/libtomahawk/AclRegistry.h b/src/libtomahawk/AclRegistry.h index a31dce5dd8..80aee719a8 100644 --- a/src/libtomahawk/AclRegistry.h +++ b/src/libtomahawk/AclRegistry.h @@ -49,20 +49,20 @@ class DLLEXPORT ACLRegistry : public QObject QString friendlyName; QStringList knownDbids; QStringList knownAccountIds; - Tomahawk::ACL acl; + Tomahawk::ACL::Type acl; User() : uuid( QUuid::createUuid().toString() ) , friendlyName() , knownDbids() , knownAccountIds() - , acl( Tomahawk::NotFound ) + , acl( Tomahawk::ACL::NotFound ) {} ~User() {} - User( QString p_uuid, QString p_friendlyName, QStringList p_knownDbids, QStringList p_knownAccountIds, Tomahawk::ACL p_acl ) + User( QString p_uuid, QString p_friendlyName, QStringList p_knownDbids, QStringList p_knownAccountIds, Tomahawk::ACL::Type p_acl ) : uuid( p_uuid ) , friendlyName( p_friendlyName ) , knownDbids( p_knownDbids ) @@ -83,7 +83,7 @@ class DLLEXPORT ACLRegistry : public QObject virtual ~ACLRegistry(); signals: - void aclResult( QString nodeid, QString username, Tomahawk::ACL peerStatus ); + void aclResult( QString nodeid, QString username, Tomahawk::ACL::Type peerStatus ); public slots: /** @@ -92,9 +92,9 @@ public slots: * @param dbid DBID of peer * @param globalType Global ACL to store if peer not found; if ACLRegistry::NotFound, does not store the peer Defaults to ACLRegistry::NotFound. * @param username If not empty, will store the given username along with the new ACL value. Defaults to QString(). - * @return Tomahawk::ACL + * @return Tomahawk::ACL::Type **/ - virtual Tomahawk::ACL isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL globalType = Tomahawk::NotFound, bool skipEmission = false ) = 0; + virtual Tomahawk::ACL::Type isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL::Type globalType = Tomahawk::ACL::NotFound, bool skipEmission = false ) = 0; virtual void wipeEntries(); protected: diff --git a/src/libtomahawk/Typedefs.h b/src/libtomahawk/Typedefs.h index 9c38318d60..94d2fa1d0e 100644 --- a/src/libtomahawk/Typedefs.h +++ b/src/libtomahawk/Typedefs.h @@ -80,12 +80,14 @@ namespace Tomahawk typedef QString QID; //query id typedef QString RID; //result id - enum ACL { - NotFound = 0, - Deny = 1, - Read = 2, - Stream = 3 - }; + namespace ACL { + enum Type { + NotFound = 0, + Deny = 1, + Read = 2, + Stream = 3 + }; + } enum GeneratorMode { @@ -258,6 +260,6 @@ inline static QString uuid() Q_DECLARE_METATYPE( QModelIndex ) Q_DECLARE_METATYPE( QPersistentModelIndex ) Q_DECLARE_METATYPE( QNetworkReply* ) -Q_DECLARE_METATYPE( Tomahawk::ACL ) +Q_DECLARE_METATYPE( Tomahawk::ACL::Type ) #endif // TYPEDEFS_H diff --git a/src/libtomahawk/jobview/AclJobItem.cpp b/src/libtomahawk/jobview/AclJobItem.cpp index 5078df1268..48be2ab18d 100644 --- a/src/libtomahawk/jobview/AclJobItem.cpp +++ b/src/libtomahawk/jobview/AclJobItem.cpp @@ -146,9 +146,9 @@ ACLJobDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QSt { QMouseEvent* me = static_cast< QMouseEvent* >( event ); if ( m_savedAcceptRect.contains( me->pos() ) ) - emit aclResult( Tomahawk::Stream ); + emit aclResult( Tomahawk::ACL::Stream ); else if ( m_savedDenyRect.contains( me->pos() ) ) - emit aclResult( Tomahawk::Deny ); + emit aclResult( Tomahawk::ACL::Deny ); return true; } @@ -195,7 +195,7 @@ ACLJobDelegate::emitSizeHintChanged( const QModelIndex& index ) void -ACLJobItem::aclResult( Tomahawk::ACL result ) +ACLJobItem::aclResult( Tomahawk::ACL::Type result ) { tLog() << Q_FUNC_INFO; m_user.acl = result; diff --git a/src/libtomahawk/jobview/AclJobItem.h b/src/libtomahawk/jobview/AclJobItem.h index 22fb32d99d..d1476fbc02 100644 --- a/src/libtomahawk/jobview/AclJobItem.h +++ b/src/libtomahawk/jobview/AclJobItem.h @@ -43,7 +43,7 @@ class DLLEXPORT ACLJobDelegate : public QStyledItemDelegate signals: void update( const QModelIndex& idx ); - void aclResult( Tomahawk::ACL result ); + void aclResult( Tomahawk::ACL::Type result ); protected: virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); @@ -84,7 +84,7 @@ class DLLEXPORT ACLJobItem : public JobStatusItem void userDecision( ACLRegistry::User user ); public slots: - void aclResult( Tomahawk::ACL result ); + void aclResult( Tomahawk::ACL::Type result ); private: QStyledItemDelegate* m_delegate; diff --git a/src/libtomahawk/jobview/JobStatusView.cpp b/src/libtomahawk/jobview/JobStatusView.cpp index 8c15d3bed7..afead13f8d 100644 --- a/src/libtomahawk/jobview/JobStatusView.cpp +++ b/src/libtomahawk/jobview/JobStatusView.cpp @@ -104,7 +104,7 @@ JobStatusView::customDelegateJobInserted( int row, JobStatusItem* item ) if ( delegate ) { connect( delegate, SIGNAL( update( const QModelIndex& ) ), m_view, SLOT( update( const QModelIndex & ) ) ); - connect( delegate, SIGNAL( aclResult( Tomahawk::ACL ) ), item, SLOT( aclResult( Tomahawk::ACL ) ) ); + connect( delegate, SIGNAL( aclResult( Tomahawk::ACL::Type ) ), item, SLOT( aclResult( Tomahawk::ACL::Type ) ) ); delegate->emitSizeHintChanged( m_model->index( row, 0 ) ); } else diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index f1bdbb9e2e..21d4d15aef 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -310,8 +310,10 @@ Connection::checkACL() } tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking ACL for" << name(); - connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL ) ), this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( Tomahawk::ACL, Tomahawk::NotFound ) ); + connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL::Type ) ), + this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL::Type ) ), + Qt::QueuedConnection ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( Tomahawk::ACL::Type, Tomahawk::ACL::NotFound ) ); } @@ -322,7 +324,7 @@ Connection::bareName() const } void -Connection::checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL peerStatus ) +Connection::checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL::Type peerStatus ) { if ( nodeid != d_func()->nodeid ) { @@ -335,9 +337,9 @@ Connection::checkACLResult( const QString &nodeid, const QString &username, Toma return; } - disconnect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL ) ) ); + disconnect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL::Type ) ) ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; - if ( peerStatus == Tomahawk::Stream ) + if ( peerStatus == Tomahawk::ACL::Stream ) { QTimer::singleShot( 0, this, SLOT( doSetup() ) ); return; diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index e890208eac..6047224b97 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -116,7 +116,7 @@ private slots: void readyRead(); void doSetup(); void checkACL(); - void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL peerStatus ); + void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL::Type peerStatus ); void bytesWritten( qint64 ); void calcStats(); diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index b8632b43fb..47b1c43360 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -211,7 +211,9 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) break; } - connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL ) ), this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL ) ), Qt::QueuedConnection ); + connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL::Type ) ), + this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL::Type ) ), + Qt::QueuedConnection ); return true; } @@ -915,7 +917,7 @@ Servent::socketError( QAbstractSocket::SocketError e ) void -Servent::checkACLResult( const QString& nodeid, const QString& username, Tomahawk::ACL peerStatus ) +Servent::checkACLResult( const QString& nodeid, const QString& username, Tomahawk::ACL::Type peerStatus ) { if ( !d_func()->queuedForACLResult.contains( username ) ) @@ -929,7 +931,7 @@ Servent::checkACLResult( const QString& nodeid, const QString& username, Tomahaw tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; QSet peerInfos = d_func()->queuedForACLResult.value( username ).value( nodeid ); - if ( peerStatus == Tomahawk::Stream ) + if ( peerStatus == Tomahawk::ACL::Stream ) { foreach ( Tomahawk::peerinfo_ptr peerInfo, peerInfos ) { diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 47612968e3..53e20c4153 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -168,7 +168,7 @@ private slots: void deleteLazyOffer( const QString& key ); void readyRead(); void socketError( QAbstractSocket::SocketError e ); - void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL peerStatus ); + void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL::Type peerStatus ); Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); diff --git a/src/tomahawk/AclRegistryImpl.cpp b/src/tomahawk/AclRegistryImpl.cpp index 5e6422d39a..6dfd09bd54 100644 --- a/src/tomahawk/AclRegistryImpl.cpp +++ b/src/tomahawk/AclRegistryImpl.cpp @@ -53,14 +53,14 @@ ACLRegistryImpl::~ACLRegistryImpl() } -Tomahawk::ACL -ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, Tomahawk::ACL globalType, bool skipEmission ) +Tomahawk::ACL::Type +ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, Tomahawk::ACL::Type globalType, bool skipEmission ) { if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() ) { if ( !skipEmission ) - QMetaObject::invokeMethod( this, "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( const QString&, dbid ), Q_ARG( const QString &, username ), Q_ARG( Tomahawk::ACL, globalType ), Q_ARG( bool, skipEmission ) ); - return Tomahawk::NotFound; + QMetaObject::invokeMethod( this, "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( const QString&, dbid ), Q_ARG( const QString &, username ), Q_ARG( Tomahawk::ACL::Type, globalType ), Q_ARG( bool, skipEmission ) ); + return Tomahawk::ACL::NotFound; } #ifndef ENABLE_HEADLESS @@ -77,8 +77,8 @@ ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, if ( account->accountFriendlyName() == username ) { if ( !skipEmission ) - emit aclResult( dbid, username, Tomahawk::Stream ); - return Tomahawk::Stream; + emit aclResult( dbid, username, Tomahawk::ACL::Stream ); + return Tomahawk::ACL::Stream; } } } @@ -119,24 +119,24 @@ ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, } if ( skipEmission ) - return Tomahawk::NotFound; + return Tomahawk::ACL::NotFound; // User was not found, create a new user entry ACLRegistry::User user; user.knownDbids.append( dbid ); user.knownAccountIds.append( username ); - if ( globalType != Tomahawk::NotFound ) + if ( globalType != Tomahawk::ACL::NotFound ) user.acl = globalType; #ifdef ENABLE_HEADLESS - user.acl = Tomahawk::Stream; + user.acl = Tomahawk::ACL::Stream; #else if ( !TomahawkUtils::headless() ) { getUserDecision( user, username ); - return Tomahawk::NotFound; + return Tomahawk::ACL::NotFound; } else - user.acl = Tomahawk::Stream; + user.acl = Tomahawk::ACL::Stream; #endif m_cache.append( user ); save(); @@ -200,8 +200,8 @@ ACLRegistryImpl::queueNextJob() bool found = false; foreach( QString dbid, user.knownDbids ) { - Tomahawk::ACL acl = isAuthorizedUser( dbid, job->username(), Tomahawk::NotFound, true ); - if ( acl != Tomahawk::NotFound ) + Tomahawk::ACL::Type acl = isAuthorizedUser( dbid, job->username(), Tomahawk::ACL::NotFound, true ); + if ( acl != Tomahawk::ACL::NotFound ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Found existing acl entry for =" << user.knownAccountIds.first(); found = true; diff --git a/src/tomahawk/AclRegistryImpl.h b/src/tomahawk/AclRegistryImpl.h index 414c0d73f6..fae57c2107 100644 --- a/src/tomahawk/AclRegistryImpl.h +++ b/src/tomahawk/AclRegistryImpl.h @@ -51,9 +51,9 @@ public slots: * @param dbid DBID of peer * @param globalType Global ACL to store if peer not found; if ACLRegistry::NotFound, does not store the peer Defaults to ACLRegistry::NotFound. * @param username If not empty, will store the given username along with the new ACL value. Defaults to QString(). - * @return Tomahawk::ACL + * @return Tomahawk::ACL::Type **/ - virtual Tomahawk::ACL isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL globalType = Tomahawk::NotFound, bool skipEmission = false ); + virtual Tomahawk::ACL::Type isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL::Type globalType = Tomahawk::ACL::NotFound, bool skipEmission = false ); virtual void wipeEntries(); protected: From 611b108eb978c12a6c1c7ed7cd51b28125315aec Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 21:04:46 +0200 Subject: [PATCH 434/565] Move peerport and msg into ConnectionPrivate --- src/libtomahawk/network/Connection.cpp | 58 ++++++++++++++++---------- src/libtomahawk/network/Connection.h | 3 -- src/libtomahawk/network/Connection_p.h | 4 ++ 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 21d4d15aef..a2de13aae1 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -34,7 +34,6 @@ Connection::Connection( Servent* parent ) : QObject() , m_sock( 0 ) - , m_peerport( 0 ) , m_servent( parent ) , m_ready( false ) , m_onceonly( true ) @@ -99,7 +98,9 @@ Connection::setFirstMessage( const QVariant& m ) void Connection::setFirstMessage( msg_ptr m ) { - m_firstmsg = m; + Q_D( Connection ); + + d->firstmsg = m; //qDebug() << id() << " first msg set to " << QString::fromAscii(m_firstmsg->payload()) // << "msg len:" << m_firstmsg->length() ; } @@ -107,7 +108,9 @@ Connection::setFirstMessage( msg_ptr m ) msg_ptr Connection::firstMessage() const { - return m_firstmsg; + Q_D( const Connection ); + + return d->firstmsg; } const QPointer& @@ -137,13 +140,17 @@ Connection::servent() const int Connection::peerPort() const { - return m_peerport; + Q_D( const Connection ); + + return d->peerport; } void Connection::setPeerPort(int p) { - m_peerport = p; + Q_D( Connection ); + + d->peerport = p; } @@ -363,6 +370,8 @@ Connection::authCheckTimeout() void Connection::doSetup() { + Q_D( Connection ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << thread(); /* New connections can be created from other thread contexts, such as @@ -404,8 +413,8 @@ Connection::doSetup() if ( outbound() ) { - Q_ASSERT( !m_firstmsg.isNull() ); - sendMsg( m_firstmsg ); + Q_ASSERT( !d->firstmsg.isNull() ); + sendMsg( d->firstmsg ); } else { @@ -489,8 +498,9 @@ void Connection::readyRead() { // qDebug() << "readyRead, bytesavail:" << m_sock->bytesAvailable(); + Q_D( Connection ); - if ( m_msg.isNull() ) + if ( d->msg.isNull() ) { if ( m_sock->bytesAvailable() < Msg::headerSize() ) return; @@ -503,22 +513,22 @@ Connection::readyRead() return; } - m_msg = Msg::begin( (char*) &msgheader ); - d_func()->rx_bytes += Msg::headerSize(); + d->msg = Msg::begin( (char*) &msgheader ); + d->rx_bytes += Msg::headerSize(); } - if ( m_sock->bytesAvailable() < m_msg->length() ) + if ( m_sock->bytesAvailable() < d->msg->length() ) return; - QByteArray ba = m_sock->read( m_msg->length() ); - if ( ba.length() != (qint32)m_msg->length() ) + QByteArray ba = m_sock->read( d->msg->length() ); + if ( ba.length() != (qint32)d->msg->length() ) { tDebug() << "Failed to read full msg payload"; this->markAsFailed(); return; } - m_msg->fill( ba ); - d_func()->rx_bytes += ba.length(); + d->msg->fill( ba ); + d->rx_bytes += ba.length(); handleReadMsg(); // process m_msg and clear() it @@ -533,9 +543,11 @@ Connection::readyRead() void Connection::handleReadMsg() { + Q_D( Connection ); + if ( outbound() == false && - m_msg->is( Msg::SETUP ) && - m_msg->payload() == "ok" ) + d->msg->is( Msg::SETUP ) && + d->msg->payload() == "ok" ) { m_ready = true; tDebug( LOGVERBOSE ) << "Connection" << id() << "READY"; @@ -544,9 +556,9 @@ Connection::handleReadMsg() } else if ( !m_ready && outbound() && - m_msg->is( Msg::SETUP ) ) + d->msg->is( Msg::SETUP ) ) { - if ( m_msg->payload() == PROTOVER ) + if ( d->msg->payload() == PROTOVER ) { sendMsg( Msg::factory( "ok", Msg::SETUP ) ); m_ready = true; @@ -562,17 +574,19 @@ Connection::handleReadMsg() } else { - d_func()->msgprocessor_in.append( m_msg ); + d->msgprocessor_in.append( d->msg ); } - m_msg.clear(); + d->msg.clear(); } void Connection::sendMsg( QVariant j ) { - if ( d_func()->do_shutdown ) + Q_D( Connection ); + + if ( d->do_shutdown ) return; QJson::Serializer serializer; diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index 6047224b97..c9be81ec60 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -122,11 +122,8 @@ private slots: protected: QPointer m_sock; - int m_peerport; - msg_ptr m_msg; Servent* m_servent; bool m_outbound, m_ready, m_onceonly; - msg_ptr m_firstmsg; private: Q_DECLARE_PRIVATE( Connection ) diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h index 7f08795b14..4682bcfe49 100644 --- a/src/libtomahawk/network/Connection_p.h +++ b/src/libtomahawk/network/Connection_p.h @@ -36,6 +36,7 @@ class ConnectionPrivate , do_shutdown( false ) , actually_shutting_down( false ) , peer_disconnected( false ) + , peerport( 0 ) , tx_bytes( 0 ) , tx_bytes_requested( 0 ) , rx_bytes( 0 ) @@ -61,6 +62,9 @@ class ConnectionPrivate QString id; QString name; QString nodeid; + msg_ptr msg; + msg_ptr firstmsg; + int peerport; QTimer* statstimer; QTime statstimer_mark; From bf48967305a7f3c72e5e22d2ec503e90a45ed28d Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 21:39:11 +0200 Subject: [PATCH 435/565] Less (global) includes in/of SipPlugin.h --- src/accounts/xmpp/sip/XmppSip.h | 1 + src/accounts/zeroconf/Zeroconf.cpp | 1 + src/libtomahawk/accounts/AccountManager.cpp | 14 ++++++++------ src/libtomahawk/accounts/AccountManager.h | 1 - src/libtomahawk/network/ConnectionManager.cpp | 1 + src/libtomahawk/sip/PeerInfo.cpp | 11 +++++++---- src/libtomahawk/sip/SipPlugin.h | 15 +++++++++++---- src/tomahawk/DiagnosticsDialog.cpp | 10 ++++++---- 8 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/accounts/xmpp/sip/XmppSip.h b/src/accounts/xmpp/sip/XmppSip.h index 0a70b734fa..24ff380da6 100644 --- a/src/accounts/xmpp/sip/XmppSip.h +++ b/src/accounts/xmpp/sip/XmppSip.h @@ -32,6 +32,7 @@ #endif #include "accounts/AccountDllMacro.h" +#include "accounts/Account.h" #include "../XmppInfoPlugin.h" diff --git a/src/accounts/zeroconf/Zeroconf.cpp b/src/accounts/zeroconf/Zeroconf.cpp index 442e403c88..579b7528fc 100644 --- a/src/accounts/zeroconf/Zeroconf.cpp +++ b/src/accounts/zeroconf/Zeroconf.cpp @@ -24,6 +24,7 @@ #include "ZeroconfAccount.h" #include "Source.h" #include "sip/PeerInfo.h" +#include "sip/SipInfo.h" #include "network/ControlConnection.h" #include diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index fcd0ad6cac..8bd948eb92 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -20,16 +20,18 @@ #include "AccountManager.h" -#include "CredentialsManager.h" -#include "config.h" -#include "SourceList.h" -#include "TomahawkSettings.h" -#include "ResolverAccount.h" -#include "utils/Logger.h" +#include "sip/SipPlugin.h" #include "sip/SipStatusMessage.h" #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" #include "utils/Closure.h" +#include "utils/Logger.h" + +#include "CredentialsManager.h" +#include "config.h" +#include "ResolverAccount.h" +#include "SourceList.h" +#include "TomahawkSettings.h" #include #include diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 420b73859a..3e74802cc0 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -27,7 +27,6 @@ #include "Typedefs.h" #include "DllMacro.h" #include "infosystem/InfoSystem.h" -#include "sip/SipPlugin.h" #include "Account.h" namespace Tomahawk diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 26a9a41569..790d8f5d61 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -24,6 +24,7 @@ #include "database/Database.h" #include "database/DatabaseImpl.h" +#include "sip/SipInfo.h" #include "sip/SipPlugin.h" #include "utils/Logger.h" diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 3855242022..1e7c3a524f 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -19,13 +19,16 @@ #include "PeerInfo_p.h" -#include "SipPlugin.h" -#include "WeakPeerHash.h" -#include "utils/TomahawkCache.h" -#include "utils/TomahawkUtilsGui.h" +#include "accounts/Account.h" #include "network/ControlConnection.h" #include "network/Servent.h" #include "utils/Logger.h" +#include "utils/TomahawkCache.h" +#include "utils/TomahawkUtilsGui.h" + +#include "SipInfo.h" +#include "SipPlugin.h" +#include "WeakPeerHash.h" #include #include diff --git a/src/libtomahawk/sip/SipPlugin.h b/src/libtomahawk/sip/SipPlugin.h index caa23877e7..a11d4046db 100644 --- a/src/libtomahawk/sip/SipPlugin.h +++ b/src/libtomahawk/sip/SipPlugin.h @@ -4,6 +4,7 @@ * 2011, Dominik Schmidt * 2010-2011, Leo Franchi * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,20 +23,26 @@ #ifndef SIPPLUGIN_H #define SIPPLUGIN_H -#include "SipInfo.h" +#include "Typedefs.h" #include #include -#include -#include "accounts/Account.h" #ifndef ENABLE_HEADLESS #include #endif #include "DllMacro.h" -class SipPlugin; +class SipInfo; +namespace Tomahawk +{ + namespace Accounts + { + class Account; + } + class PeerInfo; +} class DLLEXPORT SipPlugin : public QObject { diff --git a/src/tomahawk/DiagnosticsDialog.cpp b/src/tomahawk/DiagnosticsDialog.cpp index 2332422f0d..5e26db6a82 100644 --- a/src/tomahawk/DiagnosticsDialog.cpp +++ b/src/tomahawk/DiagnosticsDialog.cpp @@ -27,14 +27,16 @@ #include "SourceList.h" #include "accounts/AccountManager.h" +#include "database/Database.h" +#include "database/DatabaseImpl.h" +#include "infosystem/InfoSystem.h" +#include "infosystem/InfoSystemWorker.h" #include "network/Servent.h" #include "sip/PeerInfo.h" +#include "sip/SipInfo.h" +#include "sip/SipPlugin.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" -#include "infosystem/InfoSystem.h" -#include "infosystem/InfoSystemWorker.h" -#include "database/Database.h" -#include "database/DatabaseImpl.h" #include #include From 5bf09cced0c74645d9e653b11264e892ee466e86 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 22:01:20 +0200 Subject: [PATCH 436/565] Rename Tomahawk::ACL -> Tomahawk::ACLStatus --- src/libtomahawk/AclRegistry.cpp | 4 ++-- src/libtomahawk/AclRegistry.h | 12 +++++------ src/libtomahawk/Typedefs.h | 4 ++-- src/libtomahawk/jobview/AclJobItem.cpp | 6 +++--- src/libtomahawk/jobview/AclJobItem.h | 4 ++-- src/libtomahawk/jobview/JobStatusView.cpp | 2 +- src/libtomahawk/network/Connection.cpp | 12 +++++------ src/libtomahawk/network/Connection.h | 2 +- src/libtomahawk/network/Servent.cpp | 8 +++---- src/libtomahawk/network/Servent.h | 2 +- src/tomahawk/AclRegistryImpl.cpp | 26 +++++++++++------------ src/tomahawk/AclRegistryImpl.h | 4 ++-- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/libtomahawk/AclRegistry.cpp b/src/libtomahawk/AclRegistry.cpp index 6c6486feb3..18390b4007 100644 --- a/src/libtomahawk/AclRegistry.cpp +++ b/src/libtomahawk/AclRegistry.cpp @@ -66,7 +66,7 @@ QDataStream& operator>>( QDataStream &in, ACLRegistry::User &user ) } int aclIn; in >> aclIn; - user.acl = (Tomahawk::ACL::Type)( aclIn ); + user.acl = (Tomahawk::ACLStatus::Type)( aclIn ); } return in; } @@ -91,7 +91,7 @@ ACLRegistry::setInstance( ACLRegistry* instance ) ACLRegistry::ACLRegistry( QObject* parent ) : QObject( parent ) { - qRegisterMetaType< Tomahawk::ACL::Type >( "Tomahawk::ACL::Type" ); + qRegisterMetaType< Tomahawk::ACLStatus::Type >( "Tomahawk::ACLStatus::Type" ); qRegisterMetaType< ACLRegistry::User >( "ACLRegistry::User" ); qRegisterMetaTypeStreamOperators< ACLRegistry::User >( "ACLRegistry::User" ); } diff --git a/src/libtomahawk/AclRegistry.h b/src/libtomahawk/AclRegistry.h index 80aee719a8..6df255fd8e 100644 --- a/src/libtomahawk/AclRegistry.h +++ b/src/libtomahawk/AclRegistry.h @@ -49,20 +49,20 @@ class DLLEXPORT ACLRegistry : public QObject QString friendlyName; QStringList knownDbids; QStringList knownAccountIds; - Tomahawk::ACL::Type acl; + Tomahawk::ACLStatus::Type acl; User() : uuid( QUuid::createUuid().toString() ) , friendlyName() , knownDbids() , knownAccountIds() - , acl( Tomahawk::ACL::NotFound ) + , acl( Tomahawk::ACLStatus::NotFound ) {} ~User() {} - User( QString p_uuid, QString p_friendlyName, QStringList p_knownDbids, QStringList p_knownAccountIds, Tomahawk::ACL::Type p_acl ) + User( QString p_uuid, QString p_friendlyName, QStringList p_knownDbids, QStringList p_knownAccountIds, Tomahawk::ACLStatus::Type p_acl ) : uuid( p_uuid ) , friendlyName( p_friendlyName ) , knownDbids( p_knownDbids ) @@ -83,7 +83,7 @@ class DLLEXPORT ACLRegistry : public QObject virtual ~ACLRegistry(); signals: - void aclResult( QString nodeid, QString username, Tomahawk::ACL::Type peerStatus ); + void aclResult( QString nodeid, QString username, Tomahawk::ACLStatus::Type peerStatus ); public slots: /** @@ -92,9 +92,9 @@ public slots: * @param dbid DBID of peer * @param globalType Global ACL to store if peer not found; if ACLRegistry::NotFound, does not store the peer Defaults to ACLRegistry::NotFound. * @param username If not empty, will store the given username along with the new ACL value. Defaults to QString(). - * @return Tomahawk::ACL::Type + * @return Tomahawk::ACLStatus::Type **/ - virtual Tomahawk::ACL::Type isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL::Type globalType = Tomahawk::ACL::NotFound, bool skipEmission = false ) = 0; + virtual Tomahawk::ACLStatus::Type isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACLStatus::Type globalType = Tomahawk::ACLStatus::NotFound, bool skipEmission = false ) = 0; virtual void wipeEntries(); protected: diff --git a/src/libtomahawk/Typedefs.h b/src/libtomahawk/Typedefs.h index 94d2fa1d0e..1a96df8230 100644 --- a/src/libtomahawk/Typedefs.h +++ b/src/libtomahawk/Typedefs.h @@ -80,7 +80,7 @@ namespace Tomahawk typedef QString QID; //query id typedef QString RID; //result id - namespace ACL { + namespace ACLStatus { enum Type { NotFound = 0, Deny = 1, @@ -260,6 +260,6 @@ inline static QString uuid() Q_DECLARE_METATYPE( QModelIndex ) Q_DECLARE_METATYPE( QPersistentModelIndex ) Q_DECLARE_METATYPE( QNetworkReply* ) -Q_DECLARE_METATYPE( Tomahawk::ACL::Type ) +Q_DECLARE_METATYPE( Tomahawk::ACLStatus::Type ) #endif // TYPEDEFS_H diff --git a/src/libtomahawk/jobview/AclJobItem.cpp b/src/libtomahawk/jobview/AclJobItem.cpp index 48be2ab18d..7ffdb58eb7 100644 --- a/src/libtomahawk/jobview/AclJobItem.cpp +++ b/src/libtomahawk/jobview/AclJobItem.cpp @@ -146,9 +146,9 @@ ACLJobDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QSt { QMouseEvent* me = static_cast< QMouseEvent* >( event ); if ( m_savedAcceptRect.contains( me->pos() ) ) - emit aclResult( Tomahawk::ACL::Stream ); + emit aclResult( Tomahawk::ACLStatus::Stream ); else if ( m_savedDenyRect.contains( me->pos() ) ) - emit aclResult( Tomahawk::ACL::Deny ); + emit aclResult( Tomahawk::ACLStatus::Deny ); return true; } @@ -195,7 +195,7 @@ ACLJobDelegate::emitSizeHintChanged( const QModelIndex& index ) void -ACLJobItem::aclResult( Tomahawk::ACL::Type result ) +ACLJobItem::aclResult( Tomahawk::ACLStatus::Type result ) { tLog() << Q_FUNC_INFO; m_user.acl = result; diff --git a/src/libtomahawk/jobview/AclJobItem.h b/src/libtomahawk/jobview/AclJobItem.h index d1476fbc02..53508f3001 100644 --- a/src/libtomahawk/jobview/AclJobItem.h +++ b/src/libtomahawk/jobview/AclJobItem.h @@ -43,7 +43,7 @@ class DLLEXPORT ACLJobDelegate : public QStyledItemDelegate signals: void update( const QModelIndex& idx ); - void aclResult( Tomahawk::ACL::Type result ); + void aclResult( Tomahawk::ACLStatus::Type result ); protected: virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ); @@ -84,7 +84,7 @@ class DLLEXPORT ACLJobItem : public JobStatusItem void userDecision( ACLRegistry::User user ); public slots: - void aclResult( Tomahawk::ACL::Type result ); + void aclResult( Tomahawk::ACLStatus::Type result ); private: QStyledItemDelegate* m_delegate; diff --git a/src/libtomahawk/jobview/JobStatusView.cpp b/src/libtomahawk/jobview/JobStatusView.cpp index afead13f8d..3efe690ed8 100644 --- a/src/libtomahawk/jobview/JobStatusView.cpp +++ b/src/libtomahawk/jobview/JobStatusView.cpp @@ -104,7 +104,7 @@ JobStatusView::customDelegateJobInserted( int row, JobStatusItem* item ) if ( delegate ) { connect( delegate, SIGNAL( update( const QModelIndex& ) ), m_view, SLOT( update( const QModelIndex & ) ) ); - connect( delegate, SIGNAL( aclResult( Tomahawk::ACL::Type ) ), item, SLOT( aclResult( Tomahawk::ACL::Type ) ) ); + connect( delegate, SIGNAL( aclResult( Tomahawk::ACLStatus::Type ) ), item, SLOT( aclResult( Tomahawk::ACLStatus::Type ) ) ); delegate->emitSizeHintChanged( m_model->index( row, 0 ) ); } else diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index a2de13aae1..3366d66407 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -317,10 +317,10 @@ Connection::checkACL() } tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking ACL for" << name(); - connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL::Type ) ), - this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL::Type ) ), + connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACLStatus::Type ) ), + this, SLOT( checkACLResult( QString, QString, Tomahawk::ACLStatus::Type ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( Tomahawk::ACL::Type, Tomahawk::ACL::NotFound ) ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( Tomahawk::ACLStatus::Type, Tomahawk::ACLStatus::NotFound ) ); } @@ -331,7 +331,7 @@ Connection::bareName() const } void -Connection::checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL::Type peerStatus ) +Connection::checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACLStatus::Type peerStatus ) { if ( nodeid != d_func()->nodeid ) { @@ -344,9 +344,9 @@ Connection::checkACLResult( const QString &nodeid, const QString &username, Toma return; } - disconnect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL::Type ) ) ); + disconnect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACLStatus::Type ) ) ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; - if ( peerStatus == Tomahawk::ACL::Stream ) + if ( peerStatus == Tomahawk::ACLStatus::Stream ) { QTimer::singleShot( 0, this, SLOT( doSetup() ) ); return; diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index c9be81ec60..de9bb155e6 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -116,7 +116,7 @@ private slots: void readyRead(); void doSetup(); void checkACL(); - void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL::Type peerStatus ); + void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACLStatus::Type peerStatus ); void bytesWritten( qint64 ); void calcStats(); diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 47b1c43360..801aaae905 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -211,8 +211,8 @@ Servent::startListening( QHostAddress ha, bool upnp, int port ) break; } - connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACL::Type ) ), - this, SLOT( checkACLResult( QString, QString, Tomahawk::ACL::Type ) ), + connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACLStatus::Type ) ), + this, SLOT( checkACLResult( QString, QString, Tomahawk::ACLStatus::Type ) ), Qt::QueuedConnection ); return true; @@ -917,7 +917,7 @@ Servent::socketError( QAbstractSocket::SocketError e ) void -Servent::checkACLResult( const QString& nodeid, const QString& username, Tomahawk::ACL::Type peerStatus ) +Servent::checkACLResult( const QString& nodeid, const QString& username, Tomahawk::ACLStatus::Type peerStatus ) { if ( !d_func()->queuedForACLResult.contains( username ) ) @@ -931,7 +931,7 @@ Servent::checkACLResult( const QString& nodeid, const QString& username, Tomahaw tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "ACL status for user %1 is" ).arg( username ) << peerStatus; QSet peerInfos = d_func()->queuedForACLResult.value( username ).value( nodeid ); - if ( peerStatus == Tomahawk::ACL::Stream ) + if ( peerStatus == Tomahawk::ACLStatus::Stream ) { foreach ( Tomahawk::peerinfo_ptr peerInfo, peerInfos ) { diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 53e20c4153..e713b5d1b2 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -168,7 +168,7 @@ private slots: void deleteLazyOffer( const QString& key ); void readyRead(); void socketError( QAbstractSocket::SocketError e ); - void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACL::Type peerStatus ); + void checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACLStatus::Type peerStatus ); Connection* claimOffer( ControlConnection* cc, const QString &nodeid, const QString &key, const QHostAddress peer = QHostAddress::Any ); diff --git a/src/tomahawk/AclRegistryImpl.cpp b/src/tomahawk/AclRegistryImpl.cpp index 6dfd09bd54..8b95241f51 100644 --- a/src/tomahawk/AclRegistryImpl.cpp +++ b/src/tomahawk/AclRegistryImpl.cpp @@ -53,14 +53,14 @@ ACLRegistryImpl::~ACLRegistryImpl() } -Tomahawk::ACL::Type -ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, Tomahawk::ACL::Type globalType, bool skipEmission ) +Tomahawk::ACLStatus::Type +ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, Tomahawk::ACLStatus::Type globalType, bool skipEmission ) { if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() ) { if ( !skipEmission ) - QMetaObject::invokeMethod( this, "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( const QString&, dbid ), Q_ARG( const QString &, username ), Q_ARG( Tomahawk::ACL::Type, globalType ), Q_ARG( bool, skipEmission ) ); - return Tomahawk::ACL::NotFound; + QMetaObject::invokeMethod( this, "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( const QString&, dbid ), Q_ARG( const QString &, username ), Q_ARG( Tomahawk::ACLStatus::Type, globalType ), Q_ARG( bool, skipEmission ) ); + return Tomahawk::ACLStatus::NotFound; } #ifndef ENABLE_HEADLESS @@ -77,8 +77,8 @@ ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, if ( account->accountFriendlyName() == username ) { if ( !skipEmission ) - emit aclResult( dbid, username, Tomahawk::ACL::Stream ); - return Tomahawk::ACL::Stream; + emit aclResult( dbid, username, Tomahawk::ACLStatus::Stream ); + return Tomahawk::ACLStatus::Stream; } } } @@ -119,24 +119,24 @@ ACLRegistryImpl::isAuthorizedUser( const QString& dbid, const QString &username, } if ( skipEmission ) - return Tomahawk::ACL::NotFound; + return Tomahawk::ACLStatus::NotFound; // User was not found, create a new user entry ACLRegistry::User user; user.knownDbids.append( dbid ); user.knownAccountIds.append( username ); - if ( globalType != Tomahawk::ACL::NotFound ) + if ( globalType != Tomahawk::ACLStatus::NotFound ) user.acl = globalType; #ifdef ENABLE_HEADLESS - user.acl = Tomahawk::ACL::Stream; + user.acl = Tomahawk::ACLStatus::Stream; #else if ( !TomahawkUtils::headless() ) { getUserDecision( user, username ); - return Tomahawk::ACL::NotFound; + return Tomahawk::ACLStatus::NotFound; } else - user.acl = Tomahawk::ACL::Stream; + user.acl = Tomahawk::ACLStatus::Stream; #endif m_cache.append( user ); save(); @@ -200,8 +200,8 @@ ACLRegistryImpl::queueNextJob() bool found = false; foreach( QString dbid, user.knownDbids ) { - Tomahawk::ACL::Type acl = isAuthorizedUser( dbid, job->username(), Tomahawk::ACL::NotFound, true ); - if ( acl != Tomahawk::ACL::NotFound ) + Tomahawk::ACLStatus::Type acl = isAuthorizedUser( dbid, job->username(), Tomahawk::ACLStatus::NotFound, true ); + if ( acl != Tomahawk::ACLStatus::NotFound ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Found existing acl entry for =" << user.knownAccountIds.first(); found = true; diff --git a/src/tomahawk/AclRegistryImpl.h b/src/tomahawk/AclRegistryImpl.h index fae57c2107..c1bb3b8aae 100644 --- a/src/tomahawk/AclRegistryImpl.h +++ b/src/tomahawk/AclRegistryImpl.h @@ -51,9 +51,9 @@ public slots: * @param dbid DBID of peer * @param globalType Global ACL to store if peer not found; if ACLRegistry::NotFound, does not store the peer Defaults to ACLRegistry::NotFound. * @param username If not empty, will store the given username along with the new ACL value. Defaults to QString(). - * @return Tomahawk::ACL::Type + * @return Tomahawk::ACLStatus::Type **/ - virtual Tomahawk::ACL::Type isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACL::Type globalType = Tomahawk::ACL::NotFound, bool skipEmission = false ); + virtual Tomahawk::ACLStatus::Type isAuthorizedUser( const QString &dbid, const QString &username, Tomahawk::ACLStatus::Type globalType = Tomahawk::ACLStatus::NotFound, bool skipEmission = false ); virtual void wipeEntries(); protected: From 1a5406bd44a11f85535e08870499e947752b05cb Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 15 Jun 2013 22:02:16 +0200 Subject: [PATCH 437/565] Pimple SipStatusMessage --- src/libtomahawk/sip/SipStatusMessage.cpp | 39 ++++++++++------- src/libtomahawk/sip/SipStatusMessage.h | 13 ++---- src/libtomahawk/sip/SipStatusMessage_p.h | 54 ++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 src/libtomahawk/sip/SipStatusMessage_p.h diff --git a/src/libtomahawk/sip/SipStatusMessage.cpp b/src/libtomahawk/sip/SipStatusMessage.cpp index bf7588c662..7ddbd7c978 100644 --- a/src/libtomahawk/sip/SipStatusMessage.cpp +++ b/src/libtomahawk/sip/SipStatusMessage.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2013, Dominik Schmidt + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,30 +17,32 @@ * along with Tomahawk. If not, see . */ -#include "SipStatusMessage.h" +#include "SipStatusMessage_p.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" #include #include +QHash< SipStatusMessage::SipStatusMessageType, QPixmap > SipStatusMessagePrivate::s_typesPixmaps = QHash< SipStatusMessage::SipStatusMessageType, QPixmap >(); + SipStatusMessage::SipStatusMessage( SipStatusMessageType statusMessageType, const QString& contactId, const QString& message ) - : m_contactId( contactId ) - , m_statusMessageType( statusMessageType ) - , m_message( message ) + : d_ptr( new SipStatusMessagePrivate( this, statusMessageType, contactId, message ) ) { + Q_D( SipStatusMessage ); + // make this temporary for now, as soon as i know how: add ack button - m_timer = new QTimer( this ); - m_timer->setInterval( 8 * 1000 ); - m_timer->setSingleShot( true ); + d->timer = new QTimer( this ); + d->timer->setInterval( 8 * 1000 ); + d->timer->setSingleShot( true ); - connect( m_timer, SIGNAL( timeout() ), this, SIGNAL( finished() ) ); - m_timer->start(); + connect( d->timer, SIGNAL( timeout() ), this, SIGNAL( finished() ) ); + d->timer->start(); - if( s_typesPixmaps.value( m_statusMessageType ).isNull() ) + if( SipStatusMessagePrivate::s_typesPixmaps.value( d->statusMessageType ).isNull() ) { TomahawkUtils::ImageType imageType; - switch( m_statusMessageType ) + switch( d->statusMessageType ) { case SipLoginFailure: case SipInviteFailure: @@ -51,7 +54,7 @@ SipStatusMessage::SipStatusMessage( SipStatusMessageType statusMessageType, cons default: imageType = TomahawkUtils::AddContact; } - s_typesPixmaps.insert( m_statusMessageType, TomahawkUtils::defaultPixmap( imageType, TomahawkUtils::Original, QSize( 64, 64 ) ) ); + SipStatusMessagePrivate::s_typesPixmaps.insert( d->statusMessageType, TomahawkUtils::defaultPixmap( imageType, TomahawkUtils::Original, QSize( 64, 64 ) ) ); } } @@ -59,15 +62,19 @@ SipStatusMessage::SipStatusMessage( SipStatusMessageType statusMessageType, cons QPixmap SipStatusMessage::icon() const { - return s_typesPixmaps.value( m_statusMessageType ); + Q_D( const SipStatusMessage ); + + return SipStatusMessagePrivate::s_typesPixmaps.value( d->statusMessageType ); } QString SipStatusMessage::mainText() const { + Q_D( const SipStatusMessage ); + QString text; - switch( m_statusMessageType ) + switch( d->statusMessageType ) { case SipInviteFailure: text = "Could not invite %1. Please check his/her id!"; @@ -94,9 +101,9 @@ SipStatusMessage::mainText() const Q_ASSERT(false); } - text = text.arg( m_contactId ); + text = text.arg( d->contactId ); if(text.contains( "%2") ) - text = text.arg( m_message ); + text = text.arg( d->message ); return text; } diff --git a/src/libtomahawk/sip/SipStatusMessage.h b/src/libtomahawk/sip/SipStatusMessage.h index 5a34cdd2ae..f248875a99 100644 --- a/src/libtomahawk/sip/SipStatusMessage.h +++ b/src/libtomahawk/sip/SipStatusMessage.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2013, Dominik Schmidt + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +24,8 @@ #include "DllMacro.h" #include -#include -class QTimer; +class SipStatusMessagePrivate; class DLLEXPORT SipStatusMessage : public JobStatusItem { @@ -49,13 +49,8 @@ class DLLEXPORT SipStatusMessage : public JobStatusItem bool allowMultiLine() const { return true; } private: - QString m_contactId; - SipStatusMessageType m_statusMessageType; - QString m_message; - - QHash< SipStatusMessageType, QPixmap > s_typesPixmaps; - - QTimer* m_timer; + Q_DECLARE_PRIVATE( SipStatusMessage ) + SipStatusMessagePrivate* d_ptr; }; #endif // SIPSTATUSMESSAGE_H diff --git a/src/libtomahawk/sip/SipStatusMessage_p.h b/src/libtomahawk/sip/SipStatusMessage_p.h new file mode 100644 index 0000000000..033eaaaaf2 --- /dev/null +++ b/src/libtomahawk/sip/SipStatusMessage_p.h @@ -0,0 +1,54 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Dominik Schmidt + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef SIPSTATUSMESSAGE_P_H +#define SIPSTATUSMESSAGE_P_H + +#include "SipStatusMessage.h" + +#include + +class QTimer; + +class SipStatusMessagePrivate +{ +public: + SipStatusMessagePrivate( SipStatusMessage* q, SipStatusMessage::SipStatusMessageType _statusMessageType, const QString& _contactId, const QString& _message ) + : q_ptr ( q ) + , contactId( _contactId ) + , statusMessageType( _statusMessageType ) + , message( _message ) + + { + } + SipStatusMessage* q_ptr; + Q_DECLARE_PUBLIC ( SipStatusMessage ) + +private: + QString contactId; + SipStatusMessage::SipStatusMessageType statusMessageType; + QString message; + + static QHash< SipStatusMessage::SipStatusMessageType, QPixmap > s_typesPixmaps; + + QTimer* timer; +}; + + +#endif // SIPSTATUSMESSAGE_P_H From ed5cb1d93d751c66671c4dd879261282d8d9356a Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sun, 16 Jun 2013 02:16:44 +0200 Subject: [PATCH 438/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 256 ++++++++++++------------ lang/tomahawk_bg.ts | 365 ++++++++++++++++++----------------- lang/tomahawk_bn_IN.ts | 256 ++++++++++++------------ lang/tomahawk_ca.ts | 256 ++++++++++++------------ lang/tomahawk_ca@valencia.ts | 256 ++++++++++++------------ lang/tomahawk_cs.ts | 256 ++++++++++++------------ lang/tomahawk_da.ts | 256 ++++++++++++------------ lang/tomahawk_de.ts | 294 ++++++++++++++-------------- lang/tomahawk_el.ts | 294 ++++++++++++++-------------- lang/tomahawk_en.ts | 256 ++++++++++++------------ lang/tomahawk_es.ts | 256 ++++++++++++------------ lang/tomahawk_fi.ts | 256 ++++++++++++------------ lang/tomahawk_fr.ts | 256 ++++++++++++------------ lang/tomahawk_gl.ts | 256 ++++++++++++------------ lang/tomahawk_hi_IN.ts | 256 ++++++++++++------------ lang/tomahawk_hu.ts | 256 ++++++++++++------------ lang/tomahawk_id.ts | 256 ++++++++++++------------ lang/tomahawk_it.ts | 256 ++++++++++++------------ lang/tomahawk_ja.ts | 256 ++++++++++++------------ lang/tomahawk_lt.ts | 256 ++++++++++++------------ lang/tomahawk_pl.ts | 256 ++++++++++++------------ lang/tomahawk_pt_BR.ts | 256 ++++++++++++------------ lang/tomahawk_ru.ts | 256 ++++++++++++------------ lang/tomahawk_sv.ts | 256 ++++++++++++------------ lang/tomahawk_tr.ts | 256 ++++++++++++------------ lang/tomahawk_zh_CN.ts | 256 ++++++++++++------------ lang/tomahawk_zh_TW.ts | 256 ++++++++++++------------ 27 files changed, 3684 insertions(+), 3413 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 9baee582f5..2aee8974b4 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -266,27 +266,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist قائمة المسارات - + Other Albums ألبومات أخرى - + Sorry, we could not find any other albums for this artist! نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! - + Sorry, we could not find any tracks for this album! نعتذر، لم نستطيع إيجاد أغاني أخرى لهذا الألبوم! - + Other Albums by %1 ألبومات أخرى ل%1 @@ -308,37 +308,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits الأكثر شهرة - + Related Artists الفنانين ذات الذوق القريب - + Albums ألبومات - + Sorry, we could not find any albums for this artist! نعتذر, لم نستطيع إيجاد ألبومات أخرى لهذا الفنان! - + Sorry, we could not find any related artists! نعتذر، لم نستطيع إيجاد فنانين! - + Sorry, we could not find any top hits for this artist! نعتذر، لم نستطيع إيجاد أغاني مشهورة جدا لهذا الفنان! - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 نعتذر، لم نستطيع إيجاد الأغنية '%1' ل%2 - + Sorry, Tomahawk couldn't find the artist '%1' نعتذر، لم نستطيع إيجاد الفنان '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 نعتذر، لم نستطيع إيجاد الألبوم '%1' ل%2 @@ -590,45 +590,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. هذه المجموعة فارغة حاليا. - + This playlist is currently empty. Add some tracks to it and enjoy the music! هذه القائمة فارغة حاليا. أضف لها بعض الأغاني واستمتع للموسيقى! @@ -926,7 +916,7 @@ Password InfoBar - + Filter... مرشح... @@ -1376,12 +1366,12 @@ Password PlaylistItemDelegate - + played %1 by you سمعت %1 - + played %1 by %2 %2 سمع %1 @@ -1389,31 +1379,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you سمعت %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 سمع %1 - + added %1 e.g. added 3 hours ago أضيفت %1 - + by <b>%1</b> e.g. by SomeArtist للفنان <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum للفنان <b>%1</b> في البوم <b>%2</b> @@ -1558,67 +1548,67 @@ Password QObject - + %n year(s) ago منذ %n سنةمنذ سنة %nمنذ سنتين %nمنذ %n سنواتمنذ %n سنواتمنذ %n سنوات - + %n year(s) منذ %n سنةمنذ سنة %nمنذ سنتين %nمنذ %n سنواتمنذ %n سنواتمنذ %n سنوات - + %n month(s) ago منذ %n شهرمنذ شهر %nمنذ شهرين %nمنذ %n أشهرمنذ %n أشهرمنذ %n أشهر - + %n month(s) منذ %n شهرمنذ شهر %nمنذ شهرين %nمنذ %n أشهرمنذ %n أشهرمنذ %n أشهر - + %n week(s) ago منذ %n أسبوعمنذ أسبوع %nمنذ أسبوعين %nمنذ %n أسابيعمنذ %n أسابيعمنذ %n أسابيع - + %n week(s) منذ %n أسبوعمنذ أسبوع %nمنذ أسبوعين %nمنذ %n أسابيعمنذ %n أسابيعمنذ %n أسابيع - + %n day(s) ago منذ %n يوممنذ يوم %nمنذ يومين %nمنذ %n أياممنذ %n أياممنذ %n أيام - + %n day(s) منذ %n يوممنذ يوم %nمنذ يومين %nمنذ %n أياممنذ %n أياممنذ %n أيام - + %n hour(s) ago منذ %n ساعةمنذ ساعة %nمنذ ساعتين %nمنذ %n ساعاتمنذ %n ساعاتمنذ %n ساعات - + %n hour(s) منذ %n ساعةمنذ ساعة %nمنذ ساعتين %nمنذ %n ساعاتمنذ %n ساعاتمنذ %n ساعات - + %1 minutes ago منذ %1 دقائق - + %1 minutes %1 دقائق - + just now الآن @@ -1762,6 +1752,26 @@ Password Results for '%1' نتائج البحث عن '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2025,49 +2035,49 @@ Password SourceDelegate - + Track اغنية - + Album البوم - + Artist فنان - + Local محلية - + Top 10 توب ١٠ - + All available tracks جميع الأغاني المتاحة - + Drop to send tracks أنزل لإرسال الأغاني - - + + Show أظهر - - + + Hide إخفي @@ -3035,8 +3045,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3045,7 +3055,7 @@ Please change the filters or try again. غير كلمة الترشيح وأعد الكرة. - + Failed to generate preview with the desired filters فشل في إيجاد عرض مسبق للبحث المطلوب @@ -3488,17 +3498,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists فنانين - + Albums ألبومات - + Tracks أغاني @@ -3581,7 +3591,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums ألبومات @@ -3694,43 +3704,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل @@ -3797,7 +3807,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة @@ -3882,156 +3892,156 @@ enter the displayed PIN number here: توماهوك - + Back إلى الوراء - + Go back one page العودة صفحة واحدة إلى الوراء - + Forward تقدم - + Go forward one page تقدم صفحة واحدة - - + + Hide Menu Bar إخفي شريط القائمة - - + + Show Menu Bar أظهر شريط القائمة - + Search for any artist, album or song... ابحث عن أي ألبوم، فنان أو أغنية... - + &Main Menu ال&قائمة الرئيسية - + Exit Full Screen الخروج من وضع ملء الشاشة - + Enter Full Screen الدخول إلى وضع ملء الشاشة - + XSPF Error خطأ XSPF - + This is not a valid XSPF playlist. قائمة الأغاني XSPF هذه ليست صالحة. - + Failed to save tracks فشل في حفظ الأغاني - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. بعض الأغاني في قائمة الأغاني لا تحتوي على إسم الفنان أو إسم الأغنية. هذه الأغاني سوف تتجاهل. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. تأكد أن لديك خلفية فونون المناسبة والإضافات المطلوبة مثبتة. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. - + Station إذاعة - + Create New Station إنشاء قائمة أغاني جديدة - + Name: الاسم: - + Playlist قائمة الأغاني - + Automatic Playlist قائمة أغاني أوتوماتيكية - + Pause تعليق - + &Play &إستمع - + %1 by %2 track, artist name %1 من قبل %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 حق النشر ٢٠١٠ - ٢٠١٣ - + Thanks to: شكر لكل من: - + About Tomahawk عن توماهوك @@ -4047,47 +4057,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks أغاني قريبة - + Sorry, but we could not find similar tracks for this song! نعتذر، لم نستطيع إيجاد أغاني قريبة من هذه الأغنية! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). لقد استمعت إلى هذه الأغنية %n مرة.لقد استمعت إلى هذه الأغنية مرة %n.لقد استمعت إلى هذه الأغنية مرتين %n.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات.لقد استمعت إلى هذه الأغنية %n مرات. - + You've never listened to this track before. لم تستمع لهذه الأغنية من قبل. - + You first listened to it on %1. استمعت إليها أولاً في %1. - + You've listened to %1 %n time(s). لقد استمعت إلى %1 %n مرة.لقد استمعت إلى %1 مرة %n.لقد استمعت إلى %1 مرتين %n.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات.لقد استمعت إلى %1 %n مرات. - + You've never listened to %1 before. لم تستمع إلى %1 من قبل. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 8c35ab5503..1005a5d514 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Списък за изпълняване - + Other Albums Други албуми - + Sorry, we could not find any other albums for this artist! Съжалявам, но не откривам нито един албум от този изпълнител! - + Sorry, we could not find any tracks for this album! Съжалявам, но не откривам нито една песен за този изпълнител! - + Other Albums by %1 Други албуми от %1 @@ -307,39 +307,39 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Най-известни изпълнения - + Related Artists Изпълнители с подобно звучене - + Albums Албуми - + Sorry, we could not find any albums for this artist! Съжалявам, но не откривам нито един албум за този изпълнител! - + Sorry, we could not find any related artists! Съжалявам, но не откривам нито един подобен на този изпълнтел! - + Sorry, we could not find any top hits for this artist! Съжалявам, но не откривам нито една хитова песен на този изпълнител! - + YOUR ARTIST RANK - + ТВОЙ РЕЙТИНГ НА АРТИСТ @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Съжалявам. Не успявам да открия изпълнение '%1' от '%2' - + Sorry, Tomahawk couldn't find the artist '%1' Съжалявам, но не откривам '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Съжалявам, но не откривам албум с име '%1' от '%2' @@ -457,7 +457,7 @@ connect and stream from you? Sorry, your filter '%1' did not match any results. - + Съжалявам, но филтъра ти '%1' не върна резултати. @@ -465,57 +465,57 @@ connect and stream from you? Composer - + Композитор Age - + Възраст Year - + Година Duration - + Продължителност Bitrate - + Кб/сек: Composer: - + Композитор: Duration: - + Продължителност: Bitrate: - + Кб/сек: Year: - + Година: Age: - + Възраст: %1 kbps - + %1 кб/сек @@ -595,45 +595,35 @@ Tomahawk създаде доклад относно това и изпращай Dashboard - + Recently Played Tracks Наскоро изпълнени - + Recent Additions Нови попълнения - + Newest Stations & Playlists Най-нови станции и списъци - + Dashboard - + Табло - + An overview of your recent activity - + Изглед на скорошната ти активност - - Recently played tracks - Наскоро изпълнени - - - + No recently created playlists in your network. Няма наскоро създавани списъци в твоята мрежа - - - Welcome to Tomahawk - Здравей! - DatabaseCommand_AllAlbums @@ -795,26 +785,26 @@ Tomahawk създаде доклад относно това и изпращай This playlist is currently empty. - + Празен списък за изпълняване This playlist is currently empty. Add some tracks to it and enjoy the music! - + Този списък в момента е празен. Добави няколко изпълнения и се наслади на музиката! FlexibleView - + This playlist is currently empty. Този списък е празен, в момента. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Този списък е празен в момента. -Добави няколко песни и се наслади на музиката. +Добави няколко изпълнения и се наслади на музиката! @@ -887,12 +877,12 @@ Password Your recently played tracks - Наскоро изпълнени песни от теб + Наскоро изпълнени %1's recently played tracks - Наскоро изпълнените песни от %1 + Наскоро изпълнените от %1 @@ -932,7 +922,7 @@ Password InfoBar - + Filter... Филтър... @@ -952,7 +942,7 @@ Password Scrobble tracks to Last.fm - Изпращане на изпълнени песни към Last.fm + Изпращане на статистика към Last.fm @@ -972,12 +962,12 @@ Password Import Playback History - Импортирай история на просвирените песни + Импортирай списък на слушаните изпълнения Synchronize Loved Tracks - Синхронизирай харесваните песни + Синхронизирай харесаните изпълнения @@ -1047,7 +1037,7 @@ Password After you have scanned your music collection you will find your tracks right here. - След като сканираш твоята колекция, ще откриеш песните ти точно тук. + След като приключи сканирането, ще откриеш музиката ти тук. @@ -1070,7 +1060,7 @@ Password Sorry, we could not find any loved tracks! - Съжалявам, не откривам песни, които са маркирани като любими. + Съжалявам, не откривам изпълнения, които са маркирани като любими. @@ -1080,12 +1070,12 @@ Password All of your loved tracks - Всички песни, които харесваш + Всички изпълнения, които си харесал All of %1's loved tracks - Всички песни, които %1 харесва + Всички изпълнения, които %1 е харесал @@ -1249,7 +1239,8 @@ Password Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: - Просто въведи стил или име и Tomahawk ще ти предложи няколко песни с които да стартира новият ти списък: + Просто въведи стил или име и Tomahawk ще ти предложи +няколко изпълнения с които да стартира новият ти списък: @@ -1382,12 +1373,12 @@ Password PlaylistItemDelegate - + played %1 by you изпълнена %1 от мен - + played %1 by %2 изпълнена %1 от %2 @@ -1395,31 +1386,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you Изпълнена %1 от теб - + played %1 by %2 e.g. played 3 hours ago by SomeSource Изпълнена %1 от %2 - + added %1 e.g. added 3 hours ago Добавена %1 - + by <b>%1</b> e.g. by SomeArtist от <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum от <b>%1</b> на <b>%2</b> @@ -1441,12 +1432,12 @@ Password All tracks by %1 on album %2 - Всички песни от %1, от албум %2 + Всички изпълнения от %1, от албум %2 All tracks by %1 - Всички песни от %1 + Всички изпълнения от %1 @@ -1460,14 +1451,14 @@ Password Just a regular old playlist... Give it a name, drag in some tracks, and go! Обикновен списък. -Име, няколко песни и си ти на ход! +Име, няколко изпълнения и си ти на ход! Don't know exactly what you want? Give Tomahawk a few pointers and let it build a playlist for you! Не знаеш точно какво искаш? Дай на Tomahawk няколко начални идеи -и му позволи да създаде списък с песни. +и му позволи да създаде списък с изпълнения за теб! @@ -1501,7 +1492,7 @@ Password This playlist is currently empty. Add some tracks to it and enjoy the music! Този списък е празен в момента. -Добави няколко песни и се наслади на музиката. +Добави няколко изпълнения и се наслади на музиката! @@ -1566,67 +1557,67 @@ Password QObject - + %n year(s) ago преди %n годинапреди %n години - + %n year(s) %n година%n години - + %n month(s) ago преди %n месецпреди %n месеца - + %n month(s) %n месец%n месеца - + %n week(s) ago преди %n седмицапреди %n седмици - + %n week(s) %n седмица%n седмици - + %n day(s) ago преди %n денпреди %n дена - + %n day(s) %n ден%n дена - + %n hour(s) ago преди %n часпреди %n часа - + %n hour(s) %n час %n часа - + %1 minutes ago преди %1 минути - + %1 minutes %1 минути - + just now току-що @@ -1770,6 +1761,26 @@ Password Results for '%1' Резултати за '%1' + + + Tracks + Изпълнения + + + + Artists + Артисти + + + + Albums + Албуми + + + + Sorry, we could not find any tracks! + Съжалявам, не откривам никакви изпълнения. + SettingsDialog @@ -1997,7 +2008,7 @@ Password Most Played Tracks You Don't Have - Най-изпълнявани песни, които нямаш в наличност + Най-слушани изпълнения, които нямаш в наличност @@ -2036,49 +2047,49 @@ Password SourceDelegate - + Track Песен - + Album Албум - + Artist Изпълнител - + Local Локално - + Top 10 Първите 10 - + All available tracks Всички налични изпълнения - + Drop to send tracks Пусни при изпратените - - + + Show Покажи - - + + Hide Скрий @@ -2098,7 +2109,7 @@ Password Recently Played Tracks - Наскоро изпълнени песни + Наскоро слушани изпълнения @@ -2127,7 +2138,7 @@ Password Recently Played - Наскоро изпълнени песни + Наскоро изпълнени @@ -2285,7 +2296,7 @@ Password Recently Played - Наскоро изпълнени песни + Наскоро изпълнени @@ -2333,7 +2344,7 @@ Password Sync Starred tracks to Loved tracks - Синхронизирай песните означени със звезда, като харесани песни + Синхронизирай изпълнения е означени със звезда, като харесани @@ -2343,12 +2354,12 @@ Password Use this to force Spotify to never announce listening data to Social Networks - + Ползвай тази опция, за да накараш Spotify никога да не споделя с твоите социални кръгове Always run in Private Mode - + Винаги ползвай не-споделящ режим @@ -2529,7 +2540,7 @@ Password Scrobble your tracks to last.fm, and find freely downloadable tracks to play - Качи информация за изпълнените песни в Last.fm и открий свободно разпространявани. + Качи твоята статистика в Last.fm и открий свободно разпространявани изпълнения. @@ -2564,7 +2575,7 @@ Password Playback History Imported - Историята на просвирените песни е импортирана + Списъка на прослушаните изпълнения е импортиран @@ -3049,8 +3060,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3058,7 +3069,7 @@ Please change the filters or try again. Моля, промени филтрите и опитай отново. - + Failed to generate preview with the desired filters Не мога да генерирам предварителен преглед на избраните филтри. @@ -3298,7 +3309,7 @@ Try tweaking the filters for a new set of songs to play. Song Hotttnesss - Популярност на песните + Популярност на изпълнението @@ -3501,17 +3512,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists Изпълнители - + Albums Албуми - + Tracks Песни @@ -3569,17 +3580,17 @@ Try tweaking the filters for a new set of songs to play. Top Tracks - Най-слушани песни + Най-актуални изпълнения Loved Tracks - Харесани песни + Харесвани изпълнения Hyped Tracks - Песни, изпъкващи сред останалите + Изпълнения слушани най-често @@ -3595,7 +3606,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Албуми @@ -3631,7 +3642,7 @@ Try tweaking the filters for a new set of songs to play. Some tracks in the playlist do not contain an artist and a title. They will be ignored. - Някои песни в този списък нямат изпълнител и заглавие. + Някои изпълнения в този списък нямат изпълнител и заглавие. Те ще бъдат игнорирани. @@ -3709,43 +3720,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - Сканиране (%L1 песни) + Сканиране (%L1 изпълнения) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия @@ -3812,7 +3823,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -3896,159 +3907,159 @@ enter the displayed PIN number here: Tomahawk - + Back Назад - + Go back one page Една страница назад - + Forward Напред - + Go forward one page Една страница напред - - + + Hide Menu Bar Скрий лентата с менюто - - + + Show Menu Bar Покажи лентата с менюто - + Search for any artist, album or song... Търси всеки изпълнител, албум или песен... - + &Main Menu &Основно меню - + Exit Full Screen Излез от режим на цял екран - + Enter Full Screen Превключи в режим на цял екран - + XSPF Error XSPF Грешка - + This is not a valid XSPF playlist. Това не е валиден XSPF списък - + Failed to save tracks - Не успях да запазя избраните песни + Не успях да запазя селектираните изпълнения - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - Някои от песните в този списък нямат изпълнител и заглавие. + Някои от изпълнения в този списък нямат изпълнител и заглавие. Те ще бъдат игнорирани. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Съжалявам. Има проблем с достъпа до твоето аудио-устройство или до избраната песен - тя ще бъде прескочена. Моля, увери се, че са инсталирани подходящ Phonon и приставки. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Съжалявам. Има проблем с достъпа до твоето аудио устройство или избраната песен. Тя ще бъде пропусната. - + Station Станция - + Create New Station Създай нова станция - + Name: Име: - + Playlist Списък - + Automatic Playlist Автоматично-генериран списък - + Pause Пауза - + &Play &Възпроизвеждане - + %1 by %2 track, artist name %1 от %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Всички права - запазени 2010 - 2013 - + Thanks to: Благодарности на: - + About Tomahawk Относно Tomahawk @@ -4064,47 +4075,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - Подобни песни + Подобни изпълнения - + Sorry, but we could not find similar tracks for this song! Съжалявам, но не откривам нито една подобна на тази песен! - + # PLAYS / ARTIST # ИЗПЪЛНЕНИЯ / АРТИСТ - + YOUR SONG RANK - + ТВОЙ РЕЙТИНГ НА ИЗПЪЛНЕНИЕ - + You've listened to this track %n time(s). Ти си слушал тази песен %n път(и)Ти си слушал тази песен %n път(и) - + You've never listened to this track before. Никога не си слушал тази песен преди - + You first listened to it on %1. Първоначално си я слушал на %1 - + You've listened to %1 %n time(s). Слушал си %1 път(и)Слушал си %1 %n път(и) - + You've never listened to %1 before. Никога не си слушал %1 преди @@ -4269,12 +4280,12 @@ You can re-send a sync message at any time simply by sending another tweet using Recently Played Tracks - Наскоро изпълени песни + Наскоро изпълени Recently played tracks from all your friends - Наскоро изпълнени песни от всичките ти приятели + Наскоро изпълнени от всичките ти приятели diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 5898345e44..a9fba07974 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1554,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now @@ -1758,6 +1748,26 @@ Password Results for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2021,49 +2031,49 @@ Password SourceDelegate - + Track - + Album - + Artist - + Local - + Top 10 - + All available tracks - + Drop to send tracks - - + + Show - - + + Hide @@ -3025,15 +3035,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3474,17 +3484,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists - + Albums - + Tracks @@ -3567,7 +3577,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -3680,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3783,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3867,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4032,47 +4042,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index afb9db66e7..3704a3fc2f 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Llista de cançons - + Other Albums Altres àlbums - + Sorry, we could not find any other albums for this artist! No s'ha trobat cap altre àlbum d'aquest artista - + Sorry, we could not find any tracks for this album! No s'ha trobat cap altra cançó d'aquest àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'aquest artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'aquest artista - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 No s'ha trobat la cançó «%1» de «%2» - + Sorry, Tomahawk couldn't find the artist '%1' No s'ha trobat l'artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 No s'ha trobat l'àlbum «%1» de «%2» @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... Filtra... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you reproduït %1 - + played %1 by %2 reproduït %1 per %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduït %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduït %1 per %2 - + added %1 e.g. added 3 hours ago afegeix %1 - + by <b>%1</b> e.g. by SomeArtist per <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum per <b>%1</b> el <b>%2</b> @@ -1555,67 +1545,67 @@ Password QObject - + %n year(s) ago fa %n anyfa %n anys - + %n year(s) %n any%n anys - + %n month(s) ago fa %n mesfa %n mesos - + %n month(s) %n mes%n mesos - + %n week(s) ago fa %n setmanafa %n setmanes - + %n week(s) %n setmana%n setmanes - + %n day(s) ago fa %n diafa %n dies - + %n day(s) %n dia%n dies - + %n hour(s) ago fa %n horafa %n hores - + %n hour(s) %n hora%n hores - + %1 minutes ago fa %1 minut - + %1 minutes %1 minuts - + just now ara mateix @@ -1759,6 +1749,26 @@ Password Results for '%1' Resultats per '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2025,49 +2035,49 @@ i emissores basades en els vostres gusts musicals. SourceDelegate - + Track Cançó - + Album Àlbum - + Artist Artista - + Local Local - + Top 10 Top 10 - + All available tracks Totes les cançons disponibles - + Drop to send tracks - - + + Show Mostra - - + + Hide Amaga @@ -3035,8 +3045,8 @@ nomdusuari@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3045,7 +3055,7 @@ Please change the filters or try again. Canvieu els filtres o intenteu-ho de nou. - + Failed to generate preview with the desired filters S'ha produït un error en generar la previsualització amb els filtres @@ -3488,17 +3498,17 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artistes - + Albums Àlbums - + Tracks Cançons @@ -3581,7 +3591,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Àlbums @@ -3694,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia @@ -3797,7 +3807,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció @@ -3882,156 +3892,156 @@ introduïu el PIN aquí: Tomahawk - + Back Enrere - + Go back one page Retrocedeix una pàgina - + Forward Endavant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Surt de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en desar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4047,47 +4057,47 @@ introduïu el PIN aquí: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a aquesta cançó - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Heu escoltat aquesta cançó %n cop.Heu escoltat aquesta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai aquesta cançó abans. - + You first listened to it on %1. Vau escoltar aquesta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index b6e05811ce..53bfb44213 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Llista de cançons - + Other Albums Altres àlbums - + Sorry, we could not find any other albums for this artist! No s'ha trobat cap altre àlbum d'este artista - + Sorry, we could not find any tracks for this album! No s'ha trobat cap altra cançó d'este àlbum - + Other Albums by %1 Altres àlbums de %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Grans èxits - + Related Artists Artistes relacionats - + Albums Àlbums - + Sorry, we could not find any albums for this artist! No s'ha trobat cap àlbum d'este artista - + Sorry, we could not find any related artists! No s'ha trobat cap artista relacionat - + Sorry, we could not find any top hits for this artist! No s'ha trobat cap gran èxit d'este artista - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 No s'ha trobat la cançó «%1» de «%2» - + Sorry, Tomahawk couldn't find the artist '%1' No s'ha trobat l'artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 No s'ha trobat l'àlbum «%1» de «%2» @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. La llista de reproducció és buida actualment. - + This playlist is currently empty. Add some tracks to it and enjoy the music! La llista de reproducció és buida actualment. Afegiu-hi algunes cançons i gaudiu la música! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... Filtra... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you reproduït %1 - + played %1 by %2 reproduït %1 per %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduït %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduït %1 per %2 - + added %1 e.g. added 3 hours ago afig %1 - + by <b>%1</b> e.g. by SomeArtist per <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum per <b>%1</b> el <b>%2</b> @@ -1555,67 +1545,67 @@ Password QObject - + %n year(s) ago fa %n anyfa %n anys - + %n year(s) %n any%n anys - + %n month(s) ago fa %n mesfa %n mesos - + %n month(s) %n mes%n mesos - + %n week(s) ago fa %n setmanafa %n setmanes - + %n week(s) %n setmana%n setmanes - + %n day(s) ago fa %n diafa %n dies - + %n day(s) %n dia%n dies - + %n hour(s) ago fa %n horafa %n hores - + %n hour(s) %n hora%n hores - + %1 minutes ago fa %1 minut - + %1 minutes %1 minuts - + just now ara mateix @@ -1759,6 +1749,26 @@ Password Results for '%1' Resultats per '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2025,49 +2035,49 @@ i emissores basades en els vostres gusts musicals. SourceDelegate - + Track Cançó - + Album Àlbum - + Artist Artista - + Local Local - + Top 10 Top 10 - + All available tracks Totes les cançons disponibles - + Drop to send tracks - - + + Show Mostra - - + + Hide Amaga @@ -3035,8 +3045,8 @@ nomdusuari@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3045,7 +3055,7 @@ Please change the filters or try again. Canvieu els filtres o intenteu-ho de nou. - + Failed to generate preview with the desired filters S'ha produït un error en generar la previsualització amb els filtres @@ -3488,17 +3498,17 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artistes - + Albums Àlbums - + Tracks Cançons @@ -3581,7 +3591,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Àlbums @@ -3694,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia @@ -3797,7 +3807,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció @@ -3882,156 +3892,156 @@ introduïu el PIN ací: Tomahawk - + Back Arrere - + Go back one page Retrocedeix una pàgina - + Forward Avant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Ix de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en alçar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4047,47 +4057,47 @@ introduïu el PIN ací: TrackInfoWidget - + Similar Tracks Cançons Semblants - + Sorry, but we could not find similar tracks for this song! No s'han trobat cançons similars a esta cançó - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Heu escoltat esta cançó %n cop.Heu escoltat esta cançó %n cops. - + You've never listened to this track before. No heu escoltat mai esta cançó abans. - + You first listened to it on %1. Vau escoltar esta cançó per primer cop el %1. - + You've listened to %1 %n time(s). Heu escoltat %1 %n cop.Heu escoltat %1 %n cops. - + You've never listened to %1 before. No heu escoltat mai %1 abans. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index dc2cf0fcbf..a698668723 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -266,27 +266,27 @@ se s vámi spojil? AlbumInfoWidget - + Tracklist Seznam skladeb - + Other Albums Jiná alba - + Sorry, we could not find any other albums for this artist! Promiňte, nepodařilo se najít žádná jiná alba tohoto umělce! - + Sorry, we could not find any tracks for this album! Promiňte, nepodařilo se najít žádné skladby pro toto album! - + Other Albums by %1 Jiná alba %1 @@ -308,37 +308,37 @@ se s vámi spojil? ArtistInfoWidget - + Top Hits Nejlepší písně - + Related Artists Podobní umělci - + Albums Alba - + Sorry, we could not find any albums for this artist! Promiňte, nepodařilo se najít žádná alba tohoto umělce! - + Sorry, we could not find any related artists! Promiňte, nepodařilo se najít žádné podobné umělce! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ se s vámi spojil? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Promiňte, Tomahawku se nepodařilo najít skladbu '%1' od %2 - + Sorry, Tomahawk couldn't find the artist '%1' Promiňte, Tomahawku se nepodařilo najít umělce '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Promiňte, Tomahawku se nepodařilo najít album '%1' od %2 @@ -590,45 +590,35 @@ se s vámi spojil? Dashboard - + Recently Played Tracks Nedávno poslouchané skladby - + Recent Additions Nedávné přídavky - + Newest Stations & Playlists Nejnovější stanice a seznamy skladeb - + Dashboard Nástěnka - + An overview of your recent activity Přehled vaší poslední činnosti - - Recently played tracks - Nedávno poslouchané skladby - - - + No recently created playlists in your network. Ve vaší síti nejsou žádné nedávno vytvořené seznamy skladeb. - - - Welcome to Tomahawk - Vítejte v Tomahawku - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ se s vámi spojil? FlexibleView - + This playlist is currently empty. Tento seznam skladeb je nyní prázdný. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tento seznam skladeb je nyní prázdný. Přidejte do něj nějaké skladby a vychutnávejte hudbu! @@ -926,7 +916,7 @@ heslo InfoBar - + Filter... Filtr... @@ -1376,12 +1366,12 @@ heslo PlaylistItemDelegate - + played %1 by you vyslechnuto %1 vámi - + played %1 by %2 vyslechnuto %1 %2 @@ -1389,31 +1379,31 @@ heslo PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you vyslechnuto %1 vámi - + played %1 by %2 e.g. played 3 hours ago by SomeSource vyslechnuto %1 %2 - + added %1 e.g. added 3 hours ago přidáno %1 - + by <b>%1</b> e.g. by SomeArtist od <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum od <b>%1</b> na <b>%2</b> @@ -1557,67 +1547,67 @@ heslo QObject - + %n year(s) ago před %n rokempřed %n rokypřed %n roky - + %n year(s) %n rok%n roky%n roků - + %n month(s) ago před %n měsícempřed %n měsícipřed %n měsíci - + %n month(s) %n měsíc%n měsíce%n měsíců - + %n week(s) ago před %n týdnempřed %n týdnypřed %n týdny - + %n week(s) %n týden%n týdny%n týdnů - + %n day(s) ago před %n dnempřed %n dnypřed %n dny - + %n day(s) %n den%n dny%n dnů - + %n hour(s) ago před %n hodinoupřed %n hodinamipřed %n hodinami - + %n hour(s) %n hodina%n hodiny%n hodin - + %1 minutes ago před %1 minutami - + %1 minutes %1 minuty - + just now právě teď @@ -1761,6 +1751,26 @@ heslo Results for '%1' Výsledky pro '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2024,49 +2034,49 @@ heslo SourceDelegate - + Track Skladba - + Album Album - + Artist Umělec - + Local Místní - + Top 10 Nejlepších deset - + All available tracks Všechny dostupné skladby - + Drop to send tracks Upustit pro poslání skladeb - - + + Show Ukázat - - + + Hide Skrýt @@ -3034,8 +3044,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3044,7 +3054,7 @@ Please change the filters or try again. Změňte, prosím, filtr a zkuste to znovu. - + Failed to generate preview with the desired filters Nepodařilo se vytvořit náhled a vybraným filtrem @@ -3487,17 +3497,17 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::InfoSystem::ChartsPlugin - + Artists Umělci - + Albums Alba - + Tracks Skladby @@ -3581,7 +3591,7 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Alba @@ -3694,43 +3704,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený @@ -3797,7 +3807,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka @@ -3882,156 +3892,156 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: Tomahawk - + Back Zpět - + Go back one page Jít o jednu stranu zpět - + Forward Vpřed - + Go forward one page Jít o jednu stranu vpřed - - + + Hide Menu Bar Skrýt pruh s hlavní nabídkou - - + + Show Menu Bar Ukázat pruh s hlavní nabídkou - + Search for any artist, album or song... Hledat umělce, album nebo píseň... - + &Main Menu Hlavní &nabídka - + Exit Full Screen Ukončit režim na celou obrazovku - + Enter Full Screen Vstoupit do režimu na celou obrazovku - + XSPF Error Chyba XSPF - + This is not a valid XSPF playlist. Toto není platný seznam skladeb XSPF. - + Failed to save tracks Nepodařilo se uložit skladby - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Některé skladby v seznamu skladeb neobsahují ani umělce ani název. Tyto budou přehlíženy. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. Ujistěte se, že máte nainstalováno vhodné jádro Phonona potřebné přídavné moduly. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. - + Station Stanice - + Create New Station Vytvořit novou stanici - + Name: Název: - + Playlist Seznam skladeb - + Automatic Playlist Automatický seznam skladeb - + Pause Pozastavit - + &Play &Přehrát - + %1 by %2 track, artist name %1 od %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorské právo 2010 - 2013 - + Thanks to: Poděkování: - + About Tomahawk O Tomahawku @@ -4047,47 +4057,47 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackInfoWidget - + Similar Tracks Podobné skladby - + Sorry, but we could not find similar tracks for this song! Promiňte, ale nepodařilo se najít podobné skladby pro tuto píseň! - + # PLAYS / ARTIST # HRAJE / UMĚLEC - + YOUR SONG RANK - + You've listened to this track %n time(s). Tuto píseň jste si poslechl jednou.Tuto píseň jste si poslechl %n krát.Tuto píseň jste si poslechl %n krát. - + You've never listened to this track before. Tuto píseň jste si ještě nikdy neposlechl. - + You first listened to it on %1. Tuto píseň jste si poprvé poslechl %1. - + You've listened to %1 %n time(s). %1 jste si poslechl jednou.%1 jste si poslechl %n krát.%1 jste si poslechl %n krát. - + You've never listened to %1 before. %1 jste si předtím ještě nikdy neposlechl. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 94451f4167..ee80cef82f 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Andre Albums af %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Relaterede Kunstnere - + Albums Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... Filter... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you afspillede %1 af dig - + played %1 by %2 afspillede %1 af %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1555,67 +1545,67 @@ Password QObject - + %n year(s) ago %n år siden%n år siden - + %n year(s) %n år%n år - + %n month(s) ago %n måned siden%n måneder siden - + %n month(s) %n måned%n måneder - + %n week(s) ago %n uge siden%n uger siden - + %n week(s) %n uge%n uger - + %n day(s) ago %n dag siden%n dage siden - + %n day(s) %n dag%n dage - + %n hour(s) ago %n time siden%n timer siden - + %n hour(s) %n time%n timer - + %1 minutes ago %1 minutter siden - + %1 minutes %1 minutter - + just now lige nu @@ -1759,6 +1749,26 @@ Password Results for '%1' Resultater for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2022,49 +2032,49 @@ Password SourceDelegate - + Track Musiknummer - + Album Album - + Artist Kunstner - + Local Lokal - + Top 10 Top 10 - + All available tracks Alle tilgængelige numre - + Drop to send tracks - - + + Show Vis - - + + Hide Skjul @@ -3027,15 +3037,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3476,17 +3486,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists Kunstnere - + Albums Albums - + Tracks Numre @@ -3569,7 +3579,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -3682,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline @@ -3785,7 +3795,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling @@ -3869,156 +3879,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF Fejl - + This is not a valid XSPF playlist. Dette er ikke en gyldig XSPF spilleliste - + Failed to save tracks Fejlede i at gemme numrene - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station Lav Ny Station - + Name: Navn: - + Playlist - + Automatic Playlist - + Pause Pause - + &Play - + %1 by %2 track, artist name %1 af %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4034,47 +4044,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 1ce5388a2b..90baf61a29 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -266,27 +266,27 @@ erlauben sich mit dir zu verbinden? AlbumInfoWidget - + Tracklist Tracklist - + Other Albums Andere Alben - + Sorry, we could not find any other albums for this artist! Sorry, wir konnten keine anderen Alben für diesen Künstler finden! - + Sorry, we could not find any tracks for this album! Sorry, wir konnten keine anderen Lieder für dieses Album finden! - + Other Albums by %1 Andere Alben von %1 @@ -308,39 +308,39 @@ erlauben sich mit dir zu verbinden? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Ähnliche Künstler - + Albums Alben - + Sorry, we could not find any albums for this artist! Sorry, wir konnten keine Alben für diesen Künstler finden! - + Sorry, we could not find any related artists! Sorry, wir konnten keine ähnlichen Künstler finden! - + Sorry, we could not find any top hits for this artist! Sorry, wir konnten keine Lieder für diesen Künstler finden! - + YOUR ARTIST RANK - + RANG IHRES KÜNSTLERS @@ -384,17 +384,17 @@ erlauben sich mit dir zu verbinden? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Sorry, Tomahawk konnte '%1' von %2 nicht finden - + Sorry, Tomahawk couldn't find the artist '%1' Sorry, Tomahawk konnte den Künstler '%1' nicht finden - + Sorry, Tomahawk couldn't find the album '%1' by %2 Sorry, Tomahawk konnte das Album '%1' von %2 nicht finden @@ -450,7 +450,7 @@ erlauben sich mit dir zu verbinden? Unknown - + Unbekannt @@ -458,7 +458,7 @@ erlauben sich mit dir zu verbinden? Sorry, your filter '%1' did not match any results. - + Sorry, dein Filter '%1' konnte keine Ergebnisse finden. @@ -466,57 +466,57 @@ erlauben sich mit dir zu verbinden? Composer - + Komponist Age - + Alter Year - + Jahr Duration - + Dauer Bitrate - + Bitrate Composer: - + Komponist: Duration: - + Dauer: Bitrate: - + Bitrate: Year: - + Jahr: Age: - + Alter: %1 kbps - + %1 kbps @@ -590,45 +590,35 @@ erlauben sich mit dir zu verbinden? Dashboard - + Recently Played Tracks Kürzlich gehörte Lieder - + Recent Additions Kürzlich hinzugekommen - + Newest Stations & Playlists Neueste Stationen & Wiedergabelisten - + Dashboard - + Dashboard - + An overview of your recent activity - - - - - Recently played tracks - Zuletzt gehörte Lieder + Ein Überblick über Ihre aktuellen Aktivitäten - + No recently created playlists in your network. Es gibt keine kürzlich erstellten Wiedergabelisten in deinem Netzwerk. - - - Welcome to Tomahawk - Willkommen bei Tomahawk - DatabaseCommand_AllAlbums @@ -790,23 +780,23 @@ erlauben sich mit dir zu verbinden? This playlist is currently empty. - + Diese Playlist ist momentan leer. This playlist is currently empty. Add some tracks to it and enjoy the music! - + Diese Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! FlexibleView - + This playlist is currently empty. Diese Playlist ist momentan leer. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Diese Playlist ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik! @@ -926,7 +916,7 @@ Passwort InfoBar - + Filter... Filter... @@ -1376,12 +1366,12 @@ Passwort PlaylistItemDelegate - + played %1 by you angehört %1 von dir - + played %1 by %2 angehört %1 von %2 @@ -1389,31 +1379,31 @@ Passwort PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you angehört %1 von dir - + played %1 by %2 e.g. played 3 hours ago by SomeSource angehört %1 von %2 - + added %1 e.g. added 3 hours ago hinzugefügt %1 - + by <b>%1</b> e.g. by SomeArtist von <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum von <b>%1</b> auf <b>%2</b> @@ -1557,67 +1547,67 @@ Passwort QObject - + %n year(s) ago vor %n Jahrvor %n Jahren - + %n year(s) %n Jahr%n Jahre - + %n month(s) ago vor %n Monatvor %n Monaten - + %n month(s) %n Monat%n Monate - + %n week(s) ago vor %n Wochevor %n Wochen - + %n week(s) %n Woche%n Wochen - + %n day(s) ago vor %n Tagvor %n Tagen - + %n day(s) %n Tag%n Tage - + %n hour(s) ago vor %n Stundevor %n Stunden - + %n hour(s) %n Stunde%n Stunden - + %1 minutes ago vor %1 Minuten - + %1 minutes %1 Minuten - + just now gerade eben @@ -1761,6 +1751,26 @@ Passwort Results for '%1' Ergebnisse für '%1' + + + Tracks + Lieder + + + + Artists + Künstler + + + + Albums + Alben + + + + Sorry, we could not find any tracks! + Sorry, wir konnten keine Lieblingslieder finden! + SettingsDialog @@ -2024,49 +2034,49 @@ Passwort SourceDelegate - + Track Lied - + Album Album - + Artist Künstler - + Local Lokal - + Top 10 Top 10 - + All available tracks Alle verfügbaren Stücke - + Drop to send tracks Ziehen um Titel zu senden - - + + Show Einblenden - - + + Hide Verstecken @@ -3029,8 +3039,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3039,7 +3049,7 @@ Please change the filters or try again. Bitte ändere den Filter oder versuche es erneut. - + Failed to generate preview with the desired filters Konnte keine Vorschau für die gewählten Filter erzeugen @@ -3482,17 +3492,17 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::ChartsPlugin - + Artists Künstler - + Albums Alben - + Tracks Stücke @@ -3576,7 +3586,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Alben @@ -3689,43 +3699,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline @@ -3792,7 +3802,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -3877,156 +3887,156 @@ Tomahawk auf Twitter's Website authentifiziert hast: Tomahawk - + Back Zurück - + Go back one page Gehe eine Seite zurück - + Forward Vorwärts - + Go forward one page Gehe eine Seite vorwärts - - + + Hide Menu Bar Menüleiste ausblenden - - + + Show Menu Bar Menüleiste einblenden - + Search for any artist, album or song... Suche nach Künstler, Album oder Lied... - + &Main Menu Haupt&menü - + Exit Full Screen Vollbildmodus deaktivieren - + Enter Full Screen Vollbildmodus aktivieren - + XSPF Error XSPF-Fehler - + This is not a valid XSPF playlist. Dies ist keine gültige XSPF-Playlist. - + Failed to save tracks Konnte Stücke nicht abspeichern - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Einige Stücke in der Playlist enthalten weder Künstler noch Titel. Diese werden ignoriert. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. Vergewisser dich, dass ein geignetes Phonon-Backend mitsamt benötigten Plugins installiert ist. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. - + Station Station - + Create New Station Neue Station erstellen - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatische Playlist - + Pause Pause - + &Play Abs&pielen - + %1 by %2 track, artist name %1 von %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Danke an: - + About Tomahawk Über Tomahawk @@ -4042,47 +4052,47 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackInfoWidget - + Similar Tracks Ähnliche Lieder - + Sorry, but we could not find similar tracks for this song! Sorry, wir konnten keine ähnlichen Lieder finden! - + # PLAYS / ARTIST # WIEDERGEGEBEN/ KÜNSTLER - + YOUR SONG RANK - + RANG IHRES LIEDES - + You've listened to this track %n time(s). Du hast dieses Lied einmal gehört.Du hast dieses Lied %n mal gehört. - + You've never listened to this track before. Du hast dieses Lied noch nie angehört. - + You first listened to it on %1. Du hast dieses Lied zum ersten mal am %1 gehört. - + You've listened to %1 %n time(s). Du hast %1 einmal angehört.Du hast %1 %n mal angehört. - + You've never listened to %1 before. Du hast %1 vorher noch nie gehört. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 77a7f6cf87..6badcc5253 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Λιστα τραγουδιων - + Other Albums Αλλα Αλμπουμ - + Sorry, we could not find any other albums for this artist! Συγνωμη, δεν βρεθηκαν αλλα αλμπουμ αυτου του καλλιτεχνη! - + Sorry, we could not find any tracks for this album! Συγνωμη, δεν βρεθηκαν αλλα τραγουδια αυτου του αλμπουμ! - + Other Albums by %1 Άλλα Άλμπουμ από %1 @@ -307,39 +307,39 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Κορυφαία τραγούδια - + Related Artists Παρόμοιοι Καλλιτέχνες - + Albums Άλμπουμ - + Sorry, we could not find any albums for this artist! Συγνωμη, δεν βρεθηκαν αλμπουμ αυτου του καλλιτεχνη! - + Sorry, we could not find any related artists! Συγνωμη, δεν βρεθηκαν καλλιτεχνες! - + Sorry, we could not find any top hits for this artist! Συγνωμη, δεν βρεθηκαν κορυφαια τραγουδια αυτου του καλλιτεχνη! - + YOUR ARTIST RANK - + ΚΑΤΑΤΑΞΗ ΤΟΥ ΚΑΛΛΙΤΕΧΝΗ ΣΑΣ @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Συγνωμη, το Tomahawk δεν βρηκε το τραγουδι '%1' απο %2 - + Sorry, Tomahawk couldn't find the artist '%1' Συγνωμη, το Tomahawk δεν μπορεσε να βρει τον καλλιτεχνη '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Συγνωμη, το Tomahawk δεν μπορεσε να βρει το αλμπουμ του '%1' απο %2 @@ -449,7 +449,7 @@ connect and stream from you? Unknown - + Άγνωστο @@ -457,7 +457,7 @@ connect and stream from you? Sorry, your filter '%1' did not match any results. - + Συγγνώμη, το φίλτρο «%1» δεν αντιστοίχισε με τα αποτελέσματα. @@ -465,57 +465,57 @@ connect and stream from you? Composer - + Συνθέτης Age - + Ηλικία Year - + Έτος Duration - + Διάρκεια Bitrate - + Ρυθμός δεδομένων Composer: - + Συνθέτης Duration: - + Διάρκεια Bitrate: - + Ρυθμός δεδομένων: Year: - + Έτος: Age: - + Ηλικία %1 kbps - + %1 kbps @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks Πρόσφατες αναπαραγωγές - + Recent Additions Τελευταίες Προσθήκες - + Newest Stations & Playlists Τελευταίοι Σταθμοί & Λίστες Αναπαραγωγής - + Dashboard - + Πίνακας Ελέγχου - + An overview of your recent activity - - - - - Recently played tracks - Τελευταίες αναπαραγωγές κομματιών + Μια επισκόπηση των πρόσφατων δραστηριοτήτων σας - + No recently created playlists in your network. Δεν δημιουργηθηκαν πρόσφατα λίστες αναπαραγωγής στο δίκτυό σας. - - - Welcome to Tomahawk - Καλως ήρθατε στο Tomahawk - DatabaseCommand_AllAlbums @@ -789,23 +779,23 @@ connect and stream from you? This playlist is currently empty. - + Αυτη η λιστα αναπαραγωγης ειναι αδεια. This playlist is currently empty. Add some tracks to it and enjoy the music! - + Αυτή η λίστα αναπαραγωγής είναι άδεια προς το παρόν. Προσθέστε μερικά κομμάτια σε αυτήν και απολαύστε την μουσική! FlexibleView - + This playlist is currently empty. Αυτη η λιστα αναπαραγωγης ειναι αδεια. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Αυτή η λίστα αναπαραγωγής είναι άδεια προς το παρόν. Προσθέστε μερικά κομμάτια σε αυτήν και απολαύστε την μουσική! @@ -925,7 +915,7 @@ Password InfoBar - + Filter... Φιλτράρισμα... @@ -1375,12 +1365,12 @@ Password PlaylistItemDelegate - + played %1 by you παίχθηκε %1 από εσάς - + played %1 by %2 παίχθηκε %1 από τον/την %2 @@ -1388,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you αναπαράχθηκε το %1 από εσάς - + played %1 by %2 e.g. played 3 hours ago by SomeSource αναπαράχθηκε το%1 από %2 - + added %1 e.g. added 3 hours ago προστέθηκε %1 - + by <b>%1</b> e.g. by SomeArtist από <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum από <b>%1</b> σε <b>%2</b> @@ -1556,67 +1546,67 @@ Password QObject - + %n year(s) ago %n χρόνο πριν%n χρόνια πριν - + %n year(s) %n χρόνο%n χρόνια - + %n month(s) ago %n μήνα πριν%n μήνες πριν - + %n month(s) %n μήνα%n μήνες - + %n week(s) ago %n εβδομάδα πριν%n εβδομάδες πριν - + %n week(s) %n εβδομάδα%n εβδομάδες - + %n day(s) ago %n μέρα πριν%n μέρες πριν - + %n day(s) %n μέρα %n μέρες - + %n hour(s) ago %n ώρα πριν%n ώρες πριν - + %n hour(s) %n ώρα%n ώρες - + %1 minutes ago %1 λεπτά πριν - + %1 minutes %1 λεπτά - + just now μόλις τώρα @@ -1760,6 +1750,26 @@ Password Results for '%1' Αποτελέσματα για '%1' + + + Tracks + Κομμάτια + + + + Artists + Καλλιτέχνες + + + + Albums + Άλμπουμ + + + + Sorry, we could not find any tracks! + Συγνωμη, δεν βρεθηκε κανενα αγαπημενο τραγουδι! + SettingsDialog @@ -2024,49 +2034,49 @@ Password SourceDelegate - + Track Τραγούδι - + Album Άλμπουμ - + Artist Καλλιτέχνες - + Local Τοπικό - + Top 10 Κορυφαία 10 - + All available tracks Όλα τα διαθέσιμα τραγούδια - + Drop to send tracks Πτώση για αποστολή κομμάτιων - - + + Show Εμφάνιση - - + + Hide Απόκρυψη @@ -3035,8 +3045,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3045,7 +3055,7 @@ Please change the filters or try again. Παρακαλώ αλλάξτε τα φίλτρα ή προσπαθήστε ξανά. - + Failed to generate preview with the desired filters Αποτυχία δημιουργίας προεπισκόπησης με τα επιθυμητά φίλτρα @@ -3488,17 +3498,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists Καλλιτέχνες - + Albums Άλμπουμ - + Tracks Κομμάτια @@ -3582,7 +3592,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Άλμπουμ @@ -3695,43 +3705,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης @@ -3798,7 +3808,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου @@ -3882,156 +3892,156 @@ enter the displayed PIN number here: Tomahawk - + Back Πίσω - + Go back one page Πήγαινε πίσω μία σελίδα - + Forward Μπροστά - + Go forward one page Πήγαινε μπροστά μία σελίδα - - + + Hide Menu Bar Απόκρυψη Γραμμής Μενού - - + + Show Menu Bar Εμφανιση του κεντρικου μενου - + Search for any artist, album or song... Αναζήτηση για οποιονδήποτε καλλιτέχνη, άλμπουμ ή τραγούδι... - + &Main Menu &Κεντρικο Μενου - + Exit Full Screen Έξοδος από Πλήρη Οθόνη - + Enter Full Screen Εισαγωγή σε Πλήρη Οθόνη - + XSPF Error Σφάλμα XSPF - + This is not a valid XSPF playlist. Αυτή δεν είναι μια έγκυρη λίστα αναπαραγωγής XSPF. - + Failed to save tracks Αποτυχία αποθήκευσης κομματιών. - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Μερικά κομμάτια στην λίστα αναπαραγωγής δεν περιέχουν έναν καλλιτέχνη ή έναν τίτλο. Θα αγνοηθούν. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. Σιγουρευτείτε ότι έχετε εγκαταστήσει ένα κατάλληλο Phonon backend και τα απαιτούμενα πρόσθετα. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. - + Station Σταθμός - + Create New Station Δημιουργία Νέου Σταθμού - + Name: Όνομα: - + Playlist Λίστας Αναπαραγωγής - + Automatic Playlist Αυτόματη Λίστα Αναπαραγωγής - + Pause Παύση - + &Play &Αναπαραγωγή - + %1 by %2 track, artist name %1 από %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Χάρη στους: - + About Tomahawk Σχετικά με το Tomahawk @@ -4047,47 +4057,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Παρόμοια Κομμάτια - + Sorry, but we could not find similar tracks for this song! Συγγνωμη, δεν βρεθηκαν παρόμοια κομμάτια για αυτό το τραγούδι! - + # PLAYS / ARTIST # ΑΝΑΠΑΡΑΓΩΓΕΣ / ΚΑΛΛΙΤΕΧΝΗΣ - + YOUR SONG RANK - + ΚΑΤΑΤΑΞΗ ΤΟΥ ΤΡΑΓΟΥΔΙΟΥ ΣΑΣ - + You've listened to this track %n time(s). Έχετε ακούσει το κομμάτι %n φορά.Έχετε ακούσει το κομμάτι %n φορές. - + You've never listened to this track before. Δεν έχετε ακούσει αυτό το κομμάτι παλιότερα. - + You first listened to it on %1. Το ακούσατε για πρώτη φορά στις %1. - + You've listened to %1 %n time(s). Ακούσατε το %1 %n φορά.Ακούσατε το %1 %n φορές. - + You've never listened to %1 before. Δεν έχετε ακούσει το %1 ποτέ πριν. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 5cdfeee67e..124b266488 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -266,27 +266,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Tracklist - + Other Albums Other Albums - + Sorry, we could not find any other albums for this artist! Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! Sorry, we could not find any tracks for this album! - + Other Albums by %1 Other Albums by %1 @@ -308,37 +308,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Related Artists - + Albums Albums - + Sorry, we could not find any albums for this artist! Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK YOUR ARTIST RANK @@ -384,17 +384,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -590,45 +590,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks Recently Played Tracks - + Recent Additions Recent Additions - + Newest Stations & Playlists Newest Stations & Playlists - + Dashboard Dashboard - + An overview of your recent activity An overview of your recent activity - - Recently played tracks - Recently played tracks - - - + No recently created playlists in your network. No recently created playlists in your network. - - - Welcome to Tomahawk - Welcome to Tomahawk - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -926,7 +916,7 @@ Password InfoBar - + Filter... Filter... @@ -1376,12 +1366,12 @@ Password PlaylistItemDelegate - + played %1 by you played %1 by you - + played %1 by %2 played %1 by %2 @@ -1389,31 +1379,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you played %1 by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource played %1 by %2 - + added %1 e.g. added 3 hours ago added %1 - + by <b>%1</b> e.g. by SomeArtist by <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum by <b>%1</b> on <b>%2</b> @@ -1557,67 +1547,67 @@ Password QObject - + %n year(s) ago %n year ago%n years ago - + %n year(s) %n year%n years - + %n month(s) ago %n month ago%n months ago - + %n month(s) %n month%n months - + %n week(s) ago %n week ago%n weeks ago - + %n week(s) %n week%n weeks - + %n day(s) ago %n day ago%n days ago - + %n day(s) %n day%n days - + %n hour(s) ago %n hour ago%n hours ago - + %n hour(s) %n hour%n hours - + %1 minutes ago %1 minutes ago - + %1 minutes %1 minutes - + just now just now @@ -1761,6 +1751,26 @@ Password Results for '%1' Results for '%1' + + + Tracks + Tracks + + + + Artists + Artists + + + + Albums + Albums + + + + Sorry, we could not find any tracks! + Sorry, we could not find any tracks! + SettingsDialog @@ -2027,49 +2037,49 @@ Password SourceDelegate - + Track Track - + Album Album - + Artist Artist - + Local Local - + Top 10 Top 10 - + All available tracks All available tracks - + Drop to send tracks Drop to send tracks - - + + Show Show - - + + Hide Hide @@ -3037,8 +3047,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3047,7 +3057,7 @@ Please change the filters or try again. Please change the filters or try again. - + Failed to generate preview with the desired filters Failed to generate preview with the desired filters @@ -3490,17 +3500,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artists - + Albums Albums - + Tracks Tracks @@ -3584,7 +3594,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albums @@ -3697,43 +3707,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline @@ -3800,7 +3810,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -3885,156 +3895,156 @@ enter the displayed PIN number here: Tomahawk - + Back Back - + Go back one page Go back one page - + Forward Forward - + Go forward one page Go forward one page - - + + Hide Menu Bar Hide Menu Bar - - + + Show Menu Bar Show Menu Bar - + Search for any artist, album or song... Search for any artist, album or song... - + &Main Menu &Main Menu - + Exit Full Screen Exit Full Screen - + Enter Full Screen Enter Full Screen - + XSPF Error XSPF Error - + This is not a valid XSPF playlist. This is not a valid XSPF playlist. - + Failed to save tracks Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Station - + Create New Station Create New Station - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatic Playlist - + Pause Pause - + &Play &Play - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk About Tomahawk @@ -4050,47 +4060,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Similar Tracks - + Sorry, but we could not find similar tracks for this song! Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST # PLAYS / ARTIST - + YOUR SONG RANK YOUR SONG RANK - + You've listened to this track %n time(s). You've listened to this track %n time.You've listened to this track %n times. - + You've never listened to this track before. You've never listened to this track before. - + You first listened to it on %1. You first listened to it on %1. - + You've listened to %1 %n time(s). You've listened to %1 %n time.You've listened to %1 %n times. - + You've never listened to %1 before. You've never listened to %1 before. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index f60e8657c7..8833ab8a3e 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -266,27 +266,27 @@ conectarse a usted y transmitir música? AlbumInfoWidget - + Tracklist Pistas - + Other Albums Otros álbumes - + Sorry, we could not find any other albums for this artist! No se encontraron otros álbumes de este artista - + Sorry, we could not find any tracks for this album! No se encontraron pistas de este álbum - + Other Albums by %1 Otros álbumes de %1 @@ -308,37 +308,37 @@ conectarse a usted y transmitir música? ArtistInfoWidget - + Top Hits Grandes éxitos - + Related Artists Artistas relacionados - + Albums Álbumes - + Sorry, we could not find any albums for this artist! No se encontraron álbumes de este artista - + Sorry, we could not find any related artists! No se encontraron artistas relacionados - + Sorry, we could not find any top hits for this artist! No se encontraron éxitos de este artista - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ conectarse a usted y transmitir música? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawk no pudo encontrar la pista '%1' de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawk no pudo encontrar el artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawk no pudo encontrar el álbum '%1' de %2 @@ -590,45 +590,35 @@ conectarse a usted y transmitir música? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ conectarse a usted y transmitir música? FlexibleView - + This playlist is currently empty. Lista de reproducción vacía - + This playlist is currently empty. Add some tracks to it and enjoy the music! Lista de reproducción vacía. ¡Añada pistas y disfrute de la música! @@ -925,7 +915,7 @@ Password InfoBar - + Filter... Filtrar… @@ -1375,12 +1365,12 @@ Password PlaylistItemDelegate - + played %1 by you %1 reproducido por usted - + played %1 by %2 %1 reproducido por %2 @@ -1388,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproducido %1 por usted - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproducido %1 de %2 - + added %1 e.g. added 3 hours ago añadido %1 - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum por <b>%1</b> en <b>%2</b> @@ -1556,67 +1546,67 @@ Password QObject - + %n year(s) ago hace %n añohace %n años - + %n year(s) %n año%n años - + %n month(s) ago hace %n meshace %n meses - + %n month(s) %n mes%n meses - + %n week(s) ago hace %n semanahace %n semanas - + %n week(s) %n semana%n semanas - + %n day(s) ago hace %n díahace %n días - + %n day(s) %n día%n días - + %n hour(s) ago hace %n horahace %n horas - + %n hour(s) %n hora%n horas - + %1 minutes ago hace %1 minutos - + %1 minutes %1 minutos - + just now justo ahora @@ -1760,6 +1750,26 @@ Password Results for '%1' Resultados para «%1» + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2026,49 +2036,49 @@ y estaciones basadas en sus gustos personales. SourceDelegate - + Track Pista - + Album Álbum - + Artist Artista - + Local Local - + Top 10 10 mejores - + All available tracks Todas las pistas disponibles - + Drop to send tracks - - + + Show Mostrar - - + + Hide Ocultar @@ -3036,8 +3046,8 @@ usuario@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3046,7 +3056,7 @@ Please change the filters or try again. Por favor, cambie los filtros o inténtelo de nuevo. - + Failed to generate preview with the desired filters Fallo al generar una vista previa con los filtros deseados @@ -3489,17 +3499,17 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artistas - + Albums Álbumes - + Tracks Pistas @@ -3582,7 +3592,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Álbumes @@ -3695,43 +3705,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado @@ -3798,7 +3808,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección @@ -3883,156 +3893,156 @@ introduzca su número PIN aquí: Tomahawk - + Back Atrás - + Go back one page Ir una página hacia atrás - + Forward Adelante - + Go forward one page Ir una página hacia adelante - - + + Hide Menu Bar Ocultar barra de menús - - + + Show Menu Bar Mostrar barra de menús - + Search for any artist, album or song... Buscar un artista, álbum o pista… - + &Main Menu &Menú principal - + Exit Full Screen Salir de pantalla completa - + Enter Full Screen Modo a pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. Esta no es una lista de reproducción XSPF válida. - + Failed to save tracks Fallo al guardar pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunas pistas en la lista de reproducción no contienen artista ni título. Serán ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Se ha producido un error al acceder al dispostivo de audio o a la pista deseada. Asegúrese de que ha instalado un backend de Phonon adecuado y los plugins necesarios. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Se ha producido un error al acceder al dispostivo de audio o a la pista deseado y se va saltar. - + Station Estación - + Create New Station Crear estación nueva - + Name: Nombre: - + Playlist Lista de reproducción - + Automatic Playlist Lista de reproducción automática - + Pause Pausar - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gracias a: - + About Tomahawk Acerca de Tomahawk @@ -4048,47 +4058,47 @@ introduzca su número PIN aquí: TrackInfoWidget - + Similar Tracks Pistas similares - + Sorry, but we could not find similar tracks for this song! No se han encontrado pistas similares - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Ha escuchado esta pista %n vez.Ha escuchado esta pista %n veces. - + You've never listened to this track before. Nunca ha escuchado esta pista antes. - + You first listened to it on %1. Escuchó esta pista por primera vez en %1. - + You've listened to %1 %n time(s). Ha escuchado %1 una vez.Ha escuchado %1 %n veces. - + You've never listened to %1 before. Nunca ha escuchado %1 antes. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index aa9a90a207..e0880d901f 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -266,27 +266,27 @@ yhdistää ja toistaa sinulta virtaa? AlbumInfoWidget - + Tracklist Kappalelista - + Other Albums Muita albumeita - + Sorry, we could not find any other albums for this artist! Valitettavasti emme löytänet mitään muita albumeita tältä artistilta! - + Sorry, we could not find any tracks for this album! Valitettavasti emme löytäneet mitään tämän albumin kappaleita! - + Other Albums by %1 Muita artistin %1 levyjä @@ -308,37 +308,37 @@ yhdistää ja toistaa sinulta virtaa? ArtistInfoWidget - + Top Hits Parhaat hitit - + Related Artists Samankaltaisia artisteja - + Albums Albumit - + Sorry, we could not find any albums for this artist! Valitettavasti emme löytänet yhtään tämän artistin albumia! - + Sorry, we could not find any related artists! Valitettavasti emme löytäneet yhtään samankaltaista artistia! - + Sorry, we could not find any top hits for this artist! Valitettavasti emme löytäneet yhtään tämän artistin parhaista hiteistä! - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ yhdistää ja toistaa sinulta virtaa? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Valitettavasti Tomahawk ei löytänyt artistin %2 kappaletta ”%1” - + Sorry, Tomahawk couldn't find the artist '%1' Valitettavasti Tomahawk ei löytänyt artistia ”%1” - + Sorry, Tomahawk couldn't find the album '%1' by %2 Valitettavasti Tomahawk ei löytänyt artistin %2 albumia ”%1” @@ -590,45 +590,35 @@ yhdistää ja toistaa sinulta virtaa? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ yhdistää ja toistaa sinulta virtaa? FlexibleView - + This playlist is currently empty. Tämä soittolista on parhaillaan tyhjä. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Tämä soittolista on parhaillaan tyhjä. Lisää kappaleita ja nauti musiikista! @@ -926,7 +916,7 @@ salasana InfoBar - + Filter... Suodata... @@ -1376,12 +1366,12 @@ salasana PlaylistItemDelegate - + played %1 by you kuuntelit %1 - + played %1 by %2 %2 kuunteli %1 @@ -1389,31 +1379,31 @@ salasana PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you kuuntelit %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 kuunteli %1 - + added %1 e.g. added 3 hours ago lisätty %1 - + by <b>%1</b> e.g. by SomeArtist artistilta <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum artistilta <b>%1</b> albumilla <b>%2</b> @@ -1557,67 +1547,67 @@ salasana QObject - + %n year(s) ago %n vuosi sitten%n vuotta sitten - + %n year(s) %n vuosi%n vuotta - + %n month(s) ago %n kuukausi sitten%n kuukautta sitten - + %n month(s) %n kuukausi%n kuukautta - + %n week(s) ago %n viikko sitten%n viikkoa sitten - + %n week(s) %n viikko%n viikkoa - + %n day(s) ago %n päivä sitten%n päivää sitten - + %n day(s) %n päivä%n päivää - + %n hour(s) ago %n tunti sitten%n tuntia sitten - + %n hour(s) %n tunti%n tuntia - + %1 minutes ago %1 minuuttia sitten - + %1 minutes %1 minuuttia - + just now juuri nyt @@ -1761,6 +1751,26 @@ salasana Results for '%1' Tulokset haulle ”%1” + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2029,49 +2039,49 @@ käyttäjäradion käyttöönottamiseksi SourceDelegate - + Track Kappale - + Album Albumi - + Artist Artisti - + Local Paikallinen - + Top 10 Kymmenen kärki - + All available tracks Kaikki saatavilla olevat kappaleet - + Drop to send tracks Lähetä kappaleet pudottamalla - - + + Show Näytä - - + + Hide Piilota @@ -3040,8 +3050,8 @@ käyttäjätunnus@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3050,7 +3060,7 @@ Please change the filters or try again. Muuta suodattimia tai yritä uudelleen. - + Failed to generate preview with the desired filters Esikatselun luonti halutuilla suodattimilla epäonnistui @@ -3493,17 +3503,17 @@ Koeta säätää suodattimia saadaksesi uuden joukon kappaleita kuunneltavaksi.< Tomahawk::InfoSystem::ChartsPlugin - + Artists Artistit - + Albums Albumit - + Tracks Kappaleet @@ -3587,7 +3597,7 @@ kappaleen %2%4 %3. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumit @@ -3700,43 +3710,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa @@ -3803,7 +3813,7 @@ kappaleen %2%4 %3. TomahawkApp - + My Collection Oma kokoelma @@ -3888,156 +3898,156 @@ anna siellä näytetty PIN-koodi tähän: Tomahawk - + Back Takaisin - + Go back one page Mene yksi sivu takaisin - + Forward Eteenpäin - + Go forward one page Mene yksi sivu eteenpäin - - + + Hide Menu Bar Piilota valikkorivi - - + + Show Menu Bar Näytä valikkorivi - + Search for any artist, album or song... Hae artistia, albumia tai kappaletta... - + &Main Menu &Päävalikko - + Exit Full Screen Poistu koko näytöstä - + Enter Full Screen Siirry koko näyttöön - + XSPF Error XSPF-virhe - + This is not a valid XSPF playlist. Tämä ei ole kelvollinen XSPF-soittolista. - + Failed to save tracks Kappaleiden tallentaminen epäonnistui - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Joillakin soittolistan kappaleilla ei ole artistia ja nimeä. Ne jätetään huomiotta. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. Varmista, että sopiva Phononin taustaosa ja vaaditut liitännäiset on asennettu. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. - + Station Asema - + Create New Station Luo uusi asema - + Name: Nimi: - + Playlist Soittolista - + Automatic Playlist Automaattinen soittolista - + Pause Tauko - + &Play &Soita - + %1 by %2 track, artist name %1 artistilta %2 - + %1 - %2 current track, some window title %1 – %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010–2013 - + Thanks to: Kiitokset: - + About Tomahawk Tietoa Tomahawkista @@ -4053,47 +4063,47 @@ anna siellä näytetty PIN-koodi tähän: TrackInfoWidget - + Similar Tracks Samankaltaisia kappaleita - + Sorry, but we could not find similar tracks for this song! Valitettavasti emme löytäneet tälle kappaleelle samankaltaisia kappaleita! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Olet kuunnellut tätä kappaletta %n kerran.Olet kuunnellut tätä kappaletta %n kertaa. - + You've never listened to this track before. Et ole kuunnellut tätä kappaletta aiemmin. - + You first listened to it on %1. Kuuntelit sitä ensi kerran %1. - + You've listened to %1 %n time(s). Olet kuunnellut artistia %1 %n kerran.Olet kuunnellut artistia %1 %n kertaa. - + You've never listened to %1 before. Et ole kuunnellut artistia %1 aiemmin. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index d10ffbca9d..40081adf7c 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -266,27 +266,27 @@ de se connecter et streamer de vous? AlbumInfoWidget - + Tracklist Liste des pistes - + Other Albums Autres Albums - + Sorry, we could not find any other albums for this artist! Désolé, aucun autre album n'a pu être trouvé pour cet artiste ! - + Sorry, we could not find any tracks for this album! Désolé, nous n'avons pu trouver aucune piste pour cet album ! - + Other Albums by %1 Autres albums de %1 @@ -308,37 +308,37 @@ de se connecter et streamer de vous? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artistes similaires - + Albums Albums - + Sorry, we could not find any albums for this artist! Désolé, on a pas pu trouver aucun album pour cet artiste! - + Sorry, we could not find any related artists! Désolé, on a rien trouvé par rapport a cet artite! - + Sorry, we could not find any top hits for this artist! Désolé, on a pas pu trouver aucun top hit pour cet artiste! - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ de se connecter et streamer de vous? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Désolé, on a pas pu trouver la piste '%1' pour %2 - + Sorry, Tomahawk couldn't find the artist '%1' Désolé, on a pas pu trouver l'artiste '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Désolé, on a pas pu trouver l'album '%1' pour %2 @@ -590,45 +590,35 @@ de se connecter et streamer de vous? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ de se connecter et streamer de vous? FlexibleView - + This playlist is currently empty. Cette liste de lecture est vide. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Cette liste de lecture est vide. Ajoutez des morceaux et profitez de la musique ! @@ -925,7 +915,7 @@ Password InfoBar - + Filter... Filtre... @@ -1375,12 +1365,12 @@ Password PlaylistItemDelegate - + played %1 by you joué %1 par vous - + played %1 by %2 joué %1 par %2 @@ -1388,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you joué %1 par vous - + played %1 by %2 e.g. played 3 hours ago by SomeSource joué %1 par %2 - + added %1 e.g. added 3 hours ago ajouté %1 - + by <b>%1</b> e.g. by SomeArtist par <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum par <b>%1</b> sur <b>%2</b> @@ -1556,67 +1546,67 @@ Password QObject - + %n year(s) ago il y a %n anil y a %n ans - + %n year(s) %n an%n ans - + %n month(s) ago il y a %n moisil y a %n mois - + %n month(s) %n mois%n mois - + %n week(s) ago il y a %n semaineil y a %n semaines - + %n week(s) %n semaine%n semaines - + %n day(s) ago il y a %n jouril y a %n jours - + %n day(s) %n jour%n jours - + %n hour(s) ago il y a %n heureil y a %n heures - + %n hour(s) %n heure%n heures - + %1 minutes ago il y a %1 minutes - + %1 minutes %1 minutes - + just now à l'instant @@ -1760,6 +1750,26 @@ Password Results for '%1' Résultats pour '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2023,49 +2033,49 @@ Password SourceDelegate - + Track Piste - + Album Album - + Artist Artiste - + Local Local - + Top 10 Top 10 - + All available tracks Tous les titres disponibles - + Drop to send tracks - - + + Show Afficher - - + + Hide Masquer @@ -3033,8 +3043,8 @@ utilisateur@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3043,7 +3053,7 @@ Please change the filters or try again. Veuillez changer les filtres et essayez de nouveau. - + Failed to generate preview with the desired filters Échec de la génération de l'aperçu avec ces filtres @@ -3486,17 +3496,17 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artistes - + Albums Albums - + Tracks Pistes @@ -3579,7 +3589,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albums @@ -3692,43 +3702,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne @@ -3795,7 +3805,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection @@ -3880,156 +3890,156 @@ saisissez le numéro PIN ici : Tomahawk - + Back Retour - + Go back one page Reculer d'une page - + Forward Avancer - + Go forward one page Avancer d'une page - - + + Hide Menu Bar Masquer la barre de menu - - + + Show Menu Bar Afficher la barre de menu - + Search for any artist, album or song... Chercher un artiste, un album, ou un morceau... - + &Main Menu &Menu Principal - + Exit Full Screen Quitter le mode plein écran - + Enter Full Screen Activer le mode plein écran - + XSPF Error Erreur XSPF - + This is not a valid XSPF playlist. Ceci n'est pas une liste de lecture XSPF valide. - + Failed to save tracks Échec de la sauvegarde des pistes - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Certaines pistes dans la liste de lecture ne contiennent pas d'artiste ou de titre. Elles seront ignorées. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sautée. Vérifiez que vous avez un backend Phonon et les plugins requis installés. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours , celle-ci va être sautée. - + Station Station - + Create New Station Créer une nouvelle station - + Name: Nom : - + Playlist Liste de lecture - + Automatic Playlist Liste de lecture automatique - + Pause Pause - + &Play &Lire - + %1 by %2 track, artist name %1 par %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Droit d'auteur 2010 - 2013 - + Thanks to: Merci a: - + About Tomahawk A propos de Tomahawk @@ -4045,47 +4055,47 @@ saisissez le numéro PIN ici : TrackInfoWidget - + Similar Tracks Piste similaire - + Sorry, but we could not find similar tracks for this song! Désolé, nous n'avons pu trouver aucune piste similaire pour cette chanson ! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Vous avez écouté cette piste %n fois.Vous avez écouté cette piste %n fois. - + You've never listened to this track before. Vous n'avez encore jamais écouté cette piste. - + You first listened to it on %1. Vous l'avez écouté pour la première fois le %1. - + You've listened to %1 %n time(s). Vous avez écouté %1 %n fois.Vous avez écouté %1 %n fois. - + You've never listened to %1 before. Vous n'avez encore jamais écouté %1. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 47df260576..8c4fa8ba9a 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Lista de reprodución - + Other Albums Outros álbums - + Sorry, we could not find any other albums for this artist! Non se atopou ningún outro álbum para este artista! - + Sorry, we could not find any tracks for this album! Non se puido atopar ningunha outra pista para este álbum! - + Other Albums by %1 Outros álbums de %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Maiores éxitos - + Related Artists Artistas relacionados - + Albums Álbums - + Sorry, we could not find any albums for this artist! Non se atopou ningún álbum para este artista! - + Sorry, we could not find any related artists! Non se atopou ningún artista relacionado! - + Sorry, we could not find any top hits for this artist! Non se atopou ningún éxito deste artista! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawk non atopa a pista «%1» de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawk non atopa o artista «%1» - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawk non atopa o álbum «%1» de %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Esta lista de reprodución está baleira. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodución está baleira. Engádelle algunhas pistas para gozar da música! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... Filtro... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you escoitou %1 por coñecela por ti - + played %1 by %2 escoitou %1 por coñecela por %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduciu %3 horas atrás por ti - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduciu % por %2 - + added %1 e.g. added 3 hours ago engadiu %1 - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum by <b>%1</b> on <b>%2</b> @@ -1555,67 +1545,67 @@ Password QObject - + %n year(s) ago %n ano(s) atrás%n ano(s) atrás - + %n year(s) %n ano(s)%n ano(s) - + %n month(s) ago %n mes(es) atrás%n mes(es) atrás - + %n month(s) %n mes(es)%n mes(es) - + %n week(s) ago %n semana(s) atrás%n semana(s) atrás - + %n week(s) %n semana(s)%n semana(s) - + %n day(s) ago %n día(s) atrás%n día(s) atrás - + %n day(s) %n día(s)%n día(s) - + %n hour(s) ago %n hora(s) atrás%n hora(s) atrás - + %n hour(s) %n hora(s)%n hora(s) - + %1 minutes ago Hai %1 minutos - + %1 minutes %1 minutos - + just now só agora @@ -1759,6 +1749,26 @@ Password Results for '%1' Resultados para «%1» + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2022,49 +2032,49 @@ Password SourceDelegate - + Track Pista - + Album Álbum - + Artist Artista - + Local Local - + Top 10 Os 10 primeiros - + All available tracks Tódalas pistas dispoñíbeis - + Drop to send tracks - - + + Show Mostrar - - + + Hide Agochar @@ -3035,8 +3045,8 @@ nomedeusuario@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3045,7 +3055,7 @@ Please change the filters or try again. Cambia os filtros e proba de novo. - + Failed to generate preview with the desired filters Fallou a previsualización cos filtros que se querían @@ -3488,17 +3498,17 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artistas - + Albums Álbums - + Tracks Pistas @@ -3581,7 +3591,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Álbums @@ -3694,43 +3704,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado @@ -3797,7 +3807,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección @@ -3881,157 +3891,157 @@ enter the displayed PIN number here: Tomahawk - + Back Atrás - + Go back one page Ir unha páxina atrás - + Forward Adiante - + Go forward one page Ir unha páxina adiante - - + + Hide Menu Bar Agochar a barra de menú - - + + Show Menu Bar Mostrar a barra de menú - + Search for any artist, album or song... Buscar a calquera artista, álbum ou canción... - + &Main Menu Menú &principal - + Exit Full Screen Saír da pantalla ao completo - + Enter Full Screen Entrar na pantalla ao completo - + XSPF Error Erro XSPF - + This is not a valid XSPF playlist. Esta non é unha lista de XSPF válida. - + Failed to save tracks Fallou o gardado de pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunhas pistas na lista de reprodución non indican nin artista nin o título. Ignoraranse. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. Asegúrate de ter o motor Phonon e os engadidos necesarios instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. - + Station - + Create New Station Crear unha nova emisión - + Name: Nome: - + Playlist Lista de reprodución - + Automatic Playlist Lista de reprodución automática - + Pause Pausa - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1.- %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecementos: - + About Tomahawk Acerca de Tomahawk @@ -4047,47 +4057,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Pistas parecidas - + Sorry, but we could not find similar tracks for this song! Non se atopan pistas parecidas a esta canción! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Escoitaches esta pista %n vece(s).Escoitaches esta pista %n vece(s). - + You've never listened to this track before. Nunca antes escoitaras esta pista. - + You first listened to it on %1. Escoitaches esta pista por primeira vez en %1. - + You've listened to %1 %n time(s). Escoitaches %1 %n veces.Escoitaches %1 %n veces. - + You've never listened to %1 before. Nunca antes escoitaras a %1. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 4145e93803..69062275cd 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1554,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now अभी @@ -1758,6 +1748,26 @@ Password Results for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2021,49 +2031,49 @@ Password SourceDelegate - + Track - + Album - + Artist कलाकार - + Local - + Top 10 - + All available tracks - + Drop to send tracks - - + + Show दिखाओ - - + + Hide छुपाओ @@ -3025,15 +3035,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3474,17 +3484,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists कलाकार - + Albums - + Tracks @@ -3567,7 +3577,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -3680,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3783,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3867,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4032,47 +4042,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 92b8ab94e8..19532e7be9 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Zeneszám lista - + Other Albums Egyéb albumok - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Kapcsolódó előadók - + Albums Albumok - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1554,67 +1544,67 @@ Password QObject - + %n year(s) ago %n évvel ezelőtt%n évvel ezelőtt - + %n year(s) %n év%n év - + %n month(s) ago %n hónappal ezelőtt%n hónappal ezelőtt - + %n month(s) %n hónap%n hónap - + %n week(s) ago %n héttel ezelőtt%n héttel ezelőtt - + %n week(s) %n hét%n hét - + %n day(s) ago %n nappal ezelőtt%n nappal ezelőtt - + %n day(s) %n nap%n nap - + %n hour(s) ago %n órával ezelőtt%n órával ezelőtt - + %n hour(s) %n óra%n óra - + %1 minutes ago %1 perccel ezelőtt - + %1 minutes %1 perc - + just now éppen most @@ -1758,6 +1748,26 @@ Password Results for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2021,49 +2031,49 @@ Password SourceDelegate - + Track Zeneszám - + Album Album - + Artist Előadó - + Local Helyi - + Top 10 Top 10 - + All available tracks Összes elérhető zeneszám - + Drop to send tracks - - + + Show Mutatás - - + + Hide Elrejtés @@ -3025,15 +3035,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3474,17 +3484,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists - + Albums - + Tracks @@ -3567,7 +3577,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumok @@ -3680,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető @@ -3783,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció @@ -3867,156 +3877,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF hiba - + This is not a valid XSPF playlist. Nem érvényes XSPF lejátszólista. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Rádióállomás - + Create New Station - + Name: - + Playlist Lejátszólista - + Automatic Playlist Automatikus lejátszólista - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk Tomahawkról @@ -4032,47 +4042,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Hasonló zeneszámok - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 1bb4381e38..205d9fe1c2 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists - + Albums - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1554,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now @@ -1758,6 +1748,26 @@ Password Results for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2021,49 +2031,49 @@ Password SourceDelegate - + Track - + Album - + Artist - + Local - + Top 10 - + All available tracks - + Drop to send tracks - - + + Show - - + + Hide @@ -3025,15 +3035,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3474,17 +3484,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists - + Albums - + Tracks @@ -3567,7 +3577,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -3680,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3783,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3867,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4032,47 +4042,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 7d68290c79..4af3f74224 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Elenco tracce - + Other Albums Altri album - + Sorry, we could not find any other albums for this artist! Siamo spiacenti, non è stato possibile trovare altri album di questo artista! - + Sorry, we could not find any tracks for this album! Ci dispiace, ma non abbiamo trovato nessuna traccia di questo album! - + Other Albums by %1 Altri album di %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Top Hits - + Related Artists Artisti simili - + Albums Album - + Sorry, we could not find any albums for this artist! Ci dispiace, ma non abbiamo trovato nessun album di questo artista! - + Sorry, we could not find any related artists! Siamo spiacenti, non è stato possibile trovare artisti simili! - + Sorry, we could not find any top hits for this artist! Ci dispiace, ma non abbiamo trovato alcun risultato top per l'artista! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Spiacente, Tomahawk non ha trovato la traccia '%1' di %2 - + Sorry, Tomahawk couldn't find the artist '%1' Spiacente, Tomahawk non ha trovato l'artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Spiacente, Tomahawk non ha trovato l'album '%1' di '%2' @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Questa playlist al momento è vuota. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Questa playlist al momento è vuota. Aggiungi qualche traccia e goditi la musica @@ -925,7 +915,7 @@ temporanea InfoBar - + Filter... Filtra... @@ -1375,12 +1365,12 @@ temporanea PlaylistItemDelegate - + played %1 by you ascoltata %1 da te - + played %1 by %2 ascoltata %1 da %2 @@ -1388,31 +1378,31 @@ temporanea PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you riprodotta %1 da te - + played %1 by %2 e.g. played 3 hours ago by SomeSource riprotta %1 da %2 - + added %1 e.g. added 3 hours ago aggiunta %1 - + by <b>%1</b> e.g. by SomeArtist da <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum da <b>%1</b> su <b>%2</b> @@ -1555,67 +1545,67 @@ temporanea QObject - + %n year(s) ago un anno fa %n anni fa - + %n year(s) un anno fa%n anni fa - + %n month(s) ago un mese fa%n mesi fa - + %n month(s) un mese%n mesi - + %n week(s) ago una settimana fa%n settimane fa - + %n week(s) una settimana%n settimane - + %n day(s) ago un giorno fa%n giorni fa - + %n day(s) un giorno%n giorni - + %n hour(s) ago un'ora fa%n ore fa - + %n hour(s) un'ora%n ore - + %1 minutes ago %1 minuti fa - + %1 minutes %1 minuti - + just now proprio ora @@ -1759,6 +1749,26 @@ temporanea Results for '%1' Risultati per '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2022,49 +2032,49 @@ temporanea SourceDelegate - + Track Traccia - + Album Album - + Artist Artista - + Local Locale - + Top 10 Top 10 - + All available tracks Tutte le tracce disponibili - + Drop to send tracks Lascia cadere per spedire le tracce - - + + Show Mostra - - + + Hide Nascondi @@ -3026,15 +3036,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. Nessuna traccia riproducibile trovata⏎ ⏎ Per favore cambia i filtri o prova ancora. - + Failed to generate preview with the desired filters Impossibile generare anteprima con i filtri selezionati @@ -3475,17 +3485,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artisti - + Albums Album - + Tracks Tracce @@ -3569,7 +3579,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Album @@ -3682,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso @@ -3785,7 +3795,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione @@ -3869,156 +3879,156 @@ enter the displayed PIN number here: Tomahawk - + Back Indietro - + Go back one page Vai indietro di una pagina - + Forward Avanti - + Go forward one page Vai avanti di una pagina - - + + Hide Menu Bar Nascondi barra menu - - + + Show Menu Bar Mostra barra menu - + Search for any artist, album or song... Cerca qualunque artista, album o canzone... - + &Main Menu &Menu principale - + Exit Full Screen Esci da schermo intero - + Enter Full Screen Modalità schermo intero - + XSPF Error Errore XSPF - + This is not a valid XSPF playlist. Questa non è una valida playlist XSPF. - + Failed to save tracks Errore nel salvare le tracce - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Alcune tracce nella playlist non contengono l'artista e il titolo. Verrano ignorate. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. Assicurati di avere le giuste librerie Phonon e i plugin necessari installati. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. - + Station Stazione - + Create New Station Crea una nuova stazione - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist automatica - + Pause Pausa - + &Play Ri&produci - + %1 by %2 track, artist name %1 di %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Grazie a: - + About Tomahawk Info su Tomahawk @@ -4034,47 +4044,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Tracce simili - + Sorry, but we could not find similar tracks for this song! Ci dispiace, ma non abbiamo trovato tracce simili per questa canzone! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Hai ascoltato questa traccia una volta.Hai ascoltato questa traccia %n volte. - + You've never listened to this track before. Non hai mai ascoltato questa traccia. - + You first listened to it on %1. L'hai ascoltata la prima volta su %1. - + You've listened to %1 %n time(s). Hai ascoltato %1 una volta.Hai ascoltato %1 %n volte. - + You've never listened to %1 before. Non hai mai ascoltato %1. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index d44b9c9878..b9dba53ac8 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist トラックリスト - + Other Albums 他のアルバム - + Sorry, we could not find any other albums for this artist! このアーティストのアルバムは他に見つかりませんでした。 - + Sorry, we could not find any tracks for this album! このアルバムの曲は見つかりませんでした。 - + Other Albums by %1 %1の他のアルバム @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 大ヒット曲 - + Related Artists 似たアーティスト - + Albums アルバム - + Sorry, we could not find any albums for this artist! このアーティストのアルバムは見つかりませんでした。 - + Sorry, we could not find any related artists! 関連アーティストは見つかりませんでした。 - + Sorry, we could not find any top hits for this artist! このアーティストの大ヒット曲は見つかりませんでした。 - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tomahawkは%2の%1を見つかりませんでした。 - + Sorry, Tomahawk couldn't find the artist '%1' Tomahawkは'%1'と言うアーティストを見つかりませんでした。 - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tomahawkは%2の%1を見つかりませんでした。 @@ -590,45 +590,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. このプレイリストには何も入っていません。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! プレイリストには何も入っていません。トラックを追加して、音楽を楽しみましょう! @@ -925,7 +915,7 @@ Password InfoBar - + Filter... フィルター... @@ -1375,12 +1365,12 @@ Password PlaylistItemDelegate - + played %1 by you %1を再生しました。 - + played %1 by %2 %2が%1を再生しました。 @@ -1388,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you %1を再生しました。 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2が%1を再生しました。 - + added %1 e.g. added 3 hours ago %1を追加しました - + by <b>%1</b> e.g. by SomeArtist <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum <b>%1</b>の<b>%2</b> @@ -1556,67 +1546,67 @@ Password QObject - + %n year(s) ago %n年前 - + %n year(s) %n年 - + %n month(s) ago %nヶ月前 - + %n month(s) %nヶ月 - + %n week(s) ago %n週間前 - + %n week(s) %n週間 - + %n day(s) ago %n日前 - + %n day(s) %n日 - + %n hour(s) ago %n時間前 - + %n hour(s) %n時間 - + %1 minutes ago %1分前 - + %1 minutes %1分 - + just now たった今 @@ -1760,6 +1750,26 @@ Password Results for '%1' '%1'の結果 + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2026,49 +2036,49 @@ Password SourceDelegate - + Track トラック - + Album アルバム - + Artist アーティスト - + Local ローカル - + Top 10 トップ10 - + All available tracks 利用可能トラック - + Drop to send tracks - - + + Show 表示 - - + + Hide 隠す @@ -3033,8 +3043,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3043,7 +3053,7 @@ Please change the filters or try again. フィルターを変更して、もう一度お試し下さい。 - + Failed to generate preview with the desired filters 指定したフィルタに一致するプレビューが作成出来ませんでした @@ -3486,17 +3496,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists アーティスト - + Albums アルバム - + Tracks トラック @@ -3579,7 +3589,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums アルバム @@ -3692,43 +3702,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン @@ -3795,7 +3805,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション @@ -3880,156 +3890,156 @@ enter the displayed PIN number here: Tomahawk - + Back プレイリスト - + Go back one page 前のページ - + Forward 次へ - + Go forward one page 次のページ - - + + Hide Menu Bar メニューバーを隠す - - + + Show Menu Bar メニューバーを表示 - + Search for any artist, album or song... アーティスト、又はアルバムや曲で検索して下さい - + &Main Menu メインメニュー - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPFエラー - + This is not a valid XSPF playlist. このプレイリストは有利なXSPFプレイリストではありません。 - + Failed to save tracks トラックの保存に失敗しました。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. プレイリストにアーティストもタイトルの無いトラックが見つかりました。この項目は無視されます。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。適しているPhononのバックエンドを確認の上、必須プラグインのインストールを確認して下さい。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。 - + Station ステーション - + Create New Station 新規ステーションを作成 - + Name: 名前: - + Playlist プレイリスト - + Automatic Playlist 自動プレイリスト - + Pause 一時停止 - + &Play 再生 - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk Tomahawkについて @@ -4045,47 +4055,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 似ているトラック - + Sorry, but we could not find similar tracks for this song! この曲に似ているトラックが見つかりませんでした。 - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). このトラックは%n回聴いています。 - + You've never listened to this track before. このトラックを一度も聴いていません。 - + You first listened to it on %1. 初めてこの曲を聴いたのは、%1です。 - + You've listened to %1 %n time(s). %1を%n回聴いています。 - + You've never listened to %1 before. %1を一度も聴いていません。 diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 9d4338cc64..41c47ed8c6 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! Atsiprašome, neradome jokių kitų šio atlikėjo albumų! - + Sorry, we could not find any tracks for this album! Atsiprašome, neradome jokių takelių iš šio albumo! - + Other Albums by %1 Kiti %1 albumai @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits - + Related Artists Susiję atlikėjai - + Albums Albumai - + Sorry, we could not find any albums for this artist! Atsiprašome, neradome jokių šio atlikėjo albumų! - + Sorry, we could not find any related artists! Atsiprašome, neradome jokių susijusių atlikėjų! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Atsiprašome, Tomahawk nepavyko rasti takelio '%1', atliekamo %2 - + Sorry, Tomahawk couldn't find the artist '%1' Atsiprašome, Tomahawk nepavyko rasti atlikėjo '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Atsiprašome, Tomahawk nepavyko rasti %2 atliekamo albumo '%1' @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... Filtruoti... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1554,67 +1544,67 @@ Password QObject - + %n year(s) ago prieš %n metusprieš %n metusprieš %n metų - + %n year(s) %n metai%n metai%n metų - + %n month(s) ago prieš %n mėnesįprieš %n mėnesiusprieš %n mėnesių - + %n month(s) %n mėnuo%n mėnesiai%n mėnesių - + %n week(s) ago prieš %n savaitęprieš %n savaitesprieš %n savaičių - + %n week(s) %n savaitė%n savaitės%n savaičių - + %n day(s) ago prieš %n dienąprieš %n dienasprieš %n dienų - + %n day(s) %n diena%n dienos%n dienų - + %n hour(s) ago prieš %n valandąprieš %n valandasprieš %n valandų - + %n hour(s) %n valanda%n valandos%n valandų - + %1 minutes ago - + %1 minutes - + just now ką tik @@ -1758,6 +1748,26 @@ Password Results for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2021,49 +2031,49 @@ Password SourceDelegate - + Track Takelis - + Album Albumas - + Artist Atlikėjas - + Local - + Top 10 Top 10 - + All available tracks Visi prieinami takeliai - + Drop to send tracks - - + + Show Rodyti - - + + Hide Slėpti @@ -3025,15 +3035,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3474,17 +3484,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists Atlikėjai - + Albums Albumai - + Tracks Takeliai @@ -3567,7 +3577,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumai @@ -3680,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs @@ -3783,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija @@ -3867,156 +3877,156 @@ enter the displayed PIN number here: Tomahawk - + Back Atgal - + Go back one page Grįžti vienu puslapiu atgal - + Forward Pirmyn - + Go forward one page Eiti vienu puslapiu pirmyn - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF klaida - + This is not a valid XSPF playlist. - + Failed to save tracks Nepavyko išsaugoti takelių - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Stotis - + Create New Station Sukurti naują stotį - + Name: Pavadinimas: - + Playlist Grojaraštis - + Automatic Playlist Automatinis grojaraštis - + Pause Pristabdyti - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorinės teisės 2010 - 2013 - + Thanks to: Dėkojame: - + About Tomahawk Apie Tomahawk @@ -4032,47 +4042,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Panašūs takeliai - + Sorry, but we could not find similar tracks for this song! Atsiprašome, neradome jokių į šią dainą panašių takelių! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Jūs klausėtės šio takelio %n kartą.Jūs klausėtės šio takelio %n kartus.Jūs klausėtės šio takelio %n kartų. - + You've never listened to this track before. Jūs niekad anksčiau nesiklausėte šio takelio. - + You first listened to it on %1. Pirmąkart klausėtės jo %1. - + You've listened to %1 %n time(s). Jūs klausėtės %1 %n kartą.Jūs klausėtės %1 %n kartus.Jūs klausėtės %1 %n kartų. - + You've never listened to %1 before. Jūs niekada anksčiau nesiklausėte %1. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index cfd4fe7962..f0ff46b561 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -266,27 +266,27 @@ połączyć się i strumieniować od ciebie? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Inne albumy %1 @@ -308,37 +308,37 @@ połączyć się i strumieniować od ciebie? ArtistInfoWidget - + Top Hits Hity na Topie - + Related Artists Powiązani artyści - + Albums Albumy - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ połączyć się i strumieniować od ciebie? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Przepraszamy, Tomahawk nie mógł znaleźć utworu '%1' wykonawcy %2 - + Sorry, Tomahawk couldn't find the artist '%1' Przepraszamy, Tomahawk nie mógł znaleźć wykonawcy '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Przepraszamy, Tomahawk nie mógł znaleźć albumu '%1' wykonawcy %2 @@ -590,45 +590,35 @@ połączyć się i strumieniować od ciebie? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ połączyć się i strumieniować od ciebie? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -925,7 +915,7 @@ Password InfoBar - + Filter... Filtruj... @@ -1375,12 +1365,12 @@ Password PlaylistItemDelegate - + played %1 by you odtworzone %1 przez ciebie - + played %1 by %2 odtworzone %1 przez %2 @@ -1388,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1556,67 +1546,67 @@ Password QObject - + %n year(s) ago %n rok temu%n lata temu%n lat temu - + %n year(s) %n rok%n lata%n lat - + %n month(s) ago %n miesiąc temu%n miesiące temu%n miesięcy temu - + %n month(s) %n miesiąc%n miesiące%n miesięcy - + %n week(s) ago %n tydzień temu%n tygodnie temu%n tygodni temu - + %n week(s) %n tydzień%n tygodnie%n tygodni - + %n day(s) ago %n dzień temu%n dni temu%n dni temu - + %n day(s) %n dzień%n dni%n dni - + %n hour(s) ago %n godzinę temu%n godziny temu%n godzin temu - + %n hour(s) %n godzinę%n godziny%n godzin - + %1 minutes ago %1 minut temu - + %1 minutes %1 minut - + just now przed chwilą @@ -1760,6 +1750,26 @@ Password Results for '%1' Wyniki dla '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2023,49 +2033,49 @@ Password SourceDelegate - + Track Utwór - + Album Album - + Artist Artysta - + Local Lokalny - + Top 10 Top 10 - + All available tracks Wszystkie dostępne utwory - + Drop to send tracks - - + + Show Pokaż - - + + Hide Ukryj @@ -3030,8 +3040,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3040,7 +3050,7 @@ Please change the filters or try again. Proszę zmienić filtry lub spróbować ponownie. - + Failed to generate preview with the desired filters Nie udało się wygenerować podglądu z żądanymi filtrami @@ -3483,17 +3493,17 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artyści - + Albums Albumy - + Tracks Utwory @@ -3576,7 +3586,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Albumy @@ -3689,43 +3699,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline @@ -3792,7 +3802,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja @@ -3877,156 +3887,156 @@ wprowadź pokazany numer PIN tutaj: Tomahawk - + Back Wstecz - + Go back one page Cofnij o jedną stronę - + Forward Naprzód - + Go forward one page Przejdź naprzód o jedną stronę - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error Błąd XSPF - + This is not a valid XSPF playlist. To nie jest poprawna lista XSPF. - + Failed to save tracks Nie udało się zapisać utworów - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Niektóre utwory na liście nie zawierają artysty i tytułu. Zostaną one zignorowane. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Przepraszamy, wystąpił problem z połączeniem z twoim urządzeniem audio lub z żądanym utworem, zostanie on pominięty. - + Station - + Create New Station Utwórz Nową Stację - + Name: Nazwa: - + Playlist - + Automatic Playlist - + Pause Pauza - + &Play - + %1 by %2 track, artist name %1 wykonawcy %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Podziękowania dla: - + About Tomahawk O Tomahawku @@ -4042,47 +4052,47 @@ wprowadź pokazany numer PIN tutaj: TrackInfoWidget - + Similar Tracks Podobne utwory - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Słuchałeś tego utworu %n raz.Słuchałeś tego utworu %n razy.Słuchałeś tego utworu %n razy. - + You've never listened to this track before. Nie słuchałeś wcześniej tego utworu. - + You first listened to it on %1. Pierwszy raz słuchałeś tego utworu %1. - + You've listened to %1 %n time(s). Słuchałeś %1 %n raz.Słuchałeś %1 %n razy.Słuchałeś %1 %n razy. - + You've never listened to %1 before. Nie słuchałeś wcześniej %1. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 9c4146da4b..0b3ec9389b 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -266,27 +266,27 @@ se conecte e faça o stream de você? AlbumInfoWidget - + Tracklist Lista de faixas - + Other Albums Outros álbuns - + Sorry, we could not find any other albums for this artist! Desculpe, mas não conseguimos encontrar outro álbum para este artista! - + Sorry, we could not find any tracks for this album! Desculpe, mas não conseguimos encontrar outras faixas para este álbum! - + Other Albums by %1 Outros álbuns de %1 @@ -308,37 +308,37 @@ se conecte e faça o stream de você? ArtistInfoWidget - + Top Hits Mais Tocadas - + Related Artists Artistas Relacionados - + Albums Álbuns - + Sorry, we could not find any albums for this artist! Desculpe, mas não conseguimos encontrar outros álbuns para este artista! - + Sorry, we could not find any related artists! Desculpe, mas não conseguimos encontrar outros artistas relacionados a este! - + Sorry, we could not find any top hits for this artist! Desculpe, mas não conseguimos encontrar outras faixas mais tocadas deste artista! - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ se conecte e faça o stream de você? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Desculpe, o Tomahawk não encontrou a faixa '%1' de %2 - + Sorry, Tomahawk couldn't find the artist '%1' Desculpe, o Tomahawk não encontrou o artista '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Desculpe, o Tomahawk não encontrou o álbum '%1' de %2 @@ -590,45 +590,35 @@ se conecte e faça o stream de você? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ se conecte e faça o stream de você? FlexibleView - + This playlist is currently empty. Esta lista de reprodução está vazia no momento. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Esta lista de reprodução está vazia no momento. Adicione algumas faixas a ela e aproveite a música! @@ -925,7 +915,7 @@ Password InfoBar - + Filter... Filtro... @@ -1375,12 +1365,12 @@ Password PlaylistItemDelegate - + played %1 by you tocou %1 por você - + played %1 by %2 tocou %1 por %2 @@ -1388,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum por <b>%1</b> em <b>%2</b> @@ -1556,67 +1546,67 @@ Password QObject - + %n year(s) ago %n ano atrás%n anos atrás - + %n year(s) %n ano%n anos - + %n month(s) ago %n mês atrás%n meses atrás - + %n month(s) %n mês%n meses - + %n week(s) ago %n semana atrás%n semanas atrás - + %n week(s) %n semana%n semanas - + %n day(s) ago %n dia atrás%n dias atrás - + %n day(s) %n dia%n dias - + %n hour(s) ago %n hora atrás%n horas atrás - + %n hour(s) %n hora%n horas - + %1 minutes ago %1 minutos atrás - + %1 minutes %1 minutos - + just now agora @@ -1760,6 +1750,26 @@ Password Results for '%1' Resultados para '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2023,49 +2033,49 @@ Password SourceDelegate - + Track Faixa - + Album Álbum - + Artist Artista - + Local Local - + Top 10 10 Mais - + All available tracks Todas as faixas disponíveis - + Drop to send tracks - - + + Show Mostrar - - + + Hide Ocultar @@ -3030,8 +3040,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3040,7 +3050,7 @@ Please change the filters or try again. Por favor, mude os filtros ou tente novamente. - + Failed to generate preview with the desired filters Falha ao criar uma visualização com os filtros solicitados @@ -3483,17 +3493,17 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::InfoSystem::ChartsPlugin - + Artists Artistas - + Albums Álbuns - + Tracks Faixas @@ -3576,7 +3586,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Álbuns @@ -3689,43 +3699,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline @@ -3792,7 +3802,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção @@ -3877,156 +3887,156 @@ colocar o número PIN mostrado aqui: Tomahawk - + Back Voltar - + Go back one page Voltar uma página - + Forward Avançar - + Go forward one page Avançar uma página - - + + Hide Menu Bar Esconder barra de menu - - + + Show Menu Bar Mostrar barra de menu - + Search for any artist, album or song... Pesquisar por qualquer artista, álbum ou música... - + &Main Menu &Menu principal - + Exit Full Screen - + Enter Full Screen - + XSPF Error Erro de XSPF - + This is not a valid XSPF playlist. Esta não é uma lista de reprodução XSPF válida. - + Failed to save tracks Falha ao salvar faixas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algumas faixas da lista de reprodução não contem artista e título. Estas serão ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. Certifique-se de ter um backend do Phonon adequado e os plugins necessários instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. - + Station Estação - + Create New Station Criar uma nova estação - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist Automática - + Pause PIN do Twitter - + &Play Re&produzir - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecimentos: - + About Tomahawk Sobre o Tomahawk @@ -4042,47 +4052,47 @@ colocar o número PIN mostrado aqui: TrackInfoWidget - + Similar Tracks Faixas Similares - + Sorry, but we could not find similar tracks for this song! Desculpe, mas não conseguimos encontrar faixas similares para esta música! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Você ouviu esta faixa %n vez.Você ouviu esta faixa %n vezes. - + You've never listened to this track before. Você nunca ouviu esta faixa antes. - + You first listened to it on %1. Você ouviu pela primeira vez em %1. - + You've listened to %1 %n time(s). Você ouviu %1 %n vez.Você ouviu %1 %n vezes. - + You've never listened to %1 before. Você nunca ouviu %1 antes. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 85a06287c9..da7492901b 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -269,27 +269,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist Список Песен - + Other Albums Другие Альбомы - + Sorry, we could not find any other albums for this artist! К сожалению, мы не смогли найти другие альбомы этого исполнителя! - + Sorry, we could not find any tracks for this album! К сожалению, мы не смогли найти никаких треков для этого альбома! - + Other Albums by %1 Другие альбомы %1 @@ -311,37 +311,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits Хиты - + Related Artists Похожие исполнители - + Albums Альбомы - + Sorry, we could not find any albums for this artist! К сожалению, мы не смогли найти никаких альбомов этого исполнителя! - + Sorry, we could not find any related artists! К сожалению, мы не смогли найти никаких исполнители! - + Sorry, we could not find any top hits for this artist! К сожалению, мы не смогли найти никаких хитов этого исполнителя! - + YOUR ARTIST RANK @@ -387,17 +387,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 К сожалению, Tomahawk не смог найти песню '%1' %2 - + Sorry, Tomahawk couldn't find the artist '%1' К сожалению, Tomahawk не смог найти исполнителя '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 К сожалению, Tomahawk не смог найти альбом '%1' %2 @@ -593,45 +593,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -804,12 +794,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. Плейлист пуст - + This playlist is currently empty. Add some tracks to it and enjoy the music! Этот плейлист пуст. Добавьте какие-нибудь песни и наслаждайтесь музыкой! @@ -928,7 +918,7 @@ Password InfoBar - + Filter... Фильтр... @@ -1378,12 +1368,12 @@ Password PlaylistItemDelegate - + played %1 by you Воспроизводилась %1 мной - + played %1 by %2 Песня %1 воспроизводилась %2 @@ -1391,31 +1381,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you Воспроизводилась %1 вами - + played %1 by %2 e.g. played 3 hours ago by SomeSource Воспроизводилась %1 %2 - + added %1 e.g. added 3 hours ago Добавлена %1 - + by <b>%1</b> e.g. by SomeArtist <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum <b>%1</b> на <b>%2</b> @@ -1559,67 +1549,67 @@ Password QObject - + %n year(s) ago %n год назад%n года назад%n лет назад - + %n year(s) %n год%n года%n лет - + %n month(s) ago %n месяц назад%n месяца назад%n месяцей назад - + %n month(s) %n месяц%n месяца%n месяцей - + %n week(s) ago %n неделю назад%n недели назад%n недель назад - + %n week(s) %n неделю%n недели%n недель - + %n day(s) ago %n день назад%n дня назад%n дней назад - + %n day(s) %n день%n дня%n дней - + %n hour(s) ago %n час назад%n часа назад%n часов назад - + %n hour(s) %n час%n часа%n часов - + %1 minutes ago %1 минут(ы) назад - + %1 minutes %1 минут(ы) - + just now только что @@ -1763,6 +1753,26 @@ Password Results for '%1' Результаты для '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2029,49 +2039,49 @@ Password SourceDelegate - + Track Трек - + Album Альбом - + Artist Исполнитель - + Local Локальная - + Top 10 Топ 10 - + All available tracks Доступные песни - + Drop to send tracks - - + + Show Показать - - + + Hide Спрятать @@ -3039,8 +3049,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3049,7 +3059,7 @@ Please change the filters or try again. Пожалуйста, измените фильтры и попробуйте еще раз. - + Failed to generate preview with the desired filters Не удалось создать предварительный просмотр с желаемым фильтры @@ -3490,17 +3500,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists Исполнители - + Albums Альбомы - + Tracks Песни @@ -3583,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Альбом @@ -3696,43 +3706,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети @@ -3799,7 +3809,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция @@ -3883,156 +3893,156 @@ enter the displayed PIN number here: Tomahawk - + Back Назад - + Go back one page Перейти на предыдущую страницу - + Forward Вперед - + Go forward one page Перейдите на следующую страницу - - + + Hide Menu Bar Спрятать Строку Меню - - + + Show Menu Bar Показать Строку Меню - + Search for any artist, album or song... Поиск любого исполнителя, альбома или песни ... - + &Main Menu &Главное меню - + Exit Full Screen Выход из полноэкранного режима - + Enter Full Screen Переход в полноэкранный режим - + XSPF Error Ошибка XSPF - + This is not a valid XSPF playlist. Это не является допустимым XSPF плейлистом. - + Failed to save tracks Не удалось сохранить песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Некоторые песни в плейлисте не содержат исполнителя и название. Они будут проигнорированы. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. Убедитесь, что у вас есть подходящий Phonon backend и необходимые плагины установлены. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. - + Station Станция - + Create New Station Создать Новую Станцию - + Name: Имя: - + Playlist Плейлист - + Automatic Playlist Автоматический Плейлист - + Pause Пауза - + &Play &Играть - + %1 by %2 track, artist name %1 %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Авторское право 2010 - 2013 - + Thanks to: Благодарность - + About Tomahawk О Tomahawk @@ -4048,47 +4058,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks Похожие Песни - + Sorry, but we could not find similar tracks for this song! Извините, но мы не смогли найти похожие на эту песни ! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Вы слушали эту песню %n раз.Вы слушали эту песню %n раз.Вы слушали эту песню %n раз. - + You've never listened to this track before. Вы никогда не слушали эту песню раньше. - + You first listened to it on %1. Первый раз слушали эту песню %1. - + You've listened to %1 %n time(s). Вы слушали %1 %n раз.Вы слушали %1 %n раза.Вы слушали %1 %n раз. - + You've never listened to %1 before. Вы никогда не слушали %1 до этого. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index b5c84b4c2e..bbd760ea19 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -266,27 +266,27 @@ ansluta och strömma från dig? AlbumInfoWidget - + Tracklist Spårlista - + Other Albums Andra album - + Sorry, we could not find any other albums for this artist! Tyvärr! Det gick inte hitta några andra album av denna artisten - + Sorry, we could not find any tracks for this album! Tyvärr! Det gick inte hitta några spår från detta albumet! - + Other Albums by %1 Andra album av %1 @@ -308,37 +308,37 @@ ansluta och strömma från dig? ArtistInfoWidget - + Top Hits Största hits - + Related Artists Relaterade artister - + Albums Album - + Sorry, we could not find any albums for this artist! Tyvärr! Det gick inte hitta några album av denna artisten! - + Sorry, we could not find any related artists! Tyvärr! Det gick inte hitta några relaterade artister! - + Sorry, we could not find any top hits for this artist! Tyvärr! Det gick inte hitta några tophits av denna artisten - + YOUR ARTIST RANK @@ -384,17 +384,17 @@ ansluta och strömma från dig? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 Tyvärr! Tomahawk kunde inte hitta spåret '%1' av %2 - + Sorry, Tomahawk couldn't find the artist '%1' Tyvärr! Tomahawk kunde inte hitta artisten '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 Tyvärr! Tomahawk kunde inte hitta albumet '%1' av %2 @@ -590,45 +590,35 @@ ansluta och strömma från dig? Dashboard - + Recently Played Tracks Nyligen uppspelade spår - + Recent Additions Nyligen tillagda - + Newest Stations & Playlists Nyaste Stationer och Spellistor - + Dashboard Dashboard - + An overview of your recent activity Överblick över din senaste aktivitet - - Recently played tracks - Nyligen uppspelade spår - - - + No recently created playlists in your network. Det finns inga nyligen skapade spellistor på ditt nätverk - - - Welcome to Tomahawk - Välkommen till Tomahawk - DatabaseCommand_AllAlbums @@ -801,12 +791,12 @@ ansluta och strömma från dig? FlexibleView - + This playlist is currently empty. Spellistan är för närvarande tom. - + This playlist is currently empty. Add some tracks to it and enjoy the music! Spellistan är för tillfället tom. Lägg till några låtar och avnjut musiken! @@ -926,7 +916,7 @@ Password InfoBar - + Filter... Filter... @@ -1376,12 +1366,12 @@ Password PlaylistItemDelegate - + played %1 by you %1 spelades av dig - + played %1 by %2 %1 spelades av %2 @@ -1389,31 +1379,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you spelade %1 av dig - + played %1 by %2 e.g. played 3 hours ago by SomeSource Spelade %1 av %2 - + added %1 e.g. added 3 hours ago La till %1 - + by <b>%1</b> e.g. by SomeArtist av <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum av <b>%1</b> på <b>%2</b> @@ -1557,67 +1547,67 @@ Password QObject - + %n year(s) ago %n år sedan%n år sedan - + %n year(s) %n år sedan%n år sedan - + %n month(s) ago %n månad sedan%n månader sedan - + %n month(s) %n månad%n månader - + %n week(s) ago %n vecka sedan%n veckor sedan - + %n week(s) %n vecka%n veckor - + %n day(s) ago %n dag sedan%n dagar sedan - + %n day(s) %n dag%n dagar - + %n hour(s) ago %n timme sedan%n timmar sedan - + %n hour(s) %n timme%n timmar - + %1 minutes ago %1 minuter sedan - + %1 minutes %1 minuter - + just now precis nyss @@ -1761,6 +1751,26 @@ Password Results for '%1' Resultat för "%1" + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2026,49 +2036,49 @@ och radiostationer baserat på din personliga profil SourceDelegate - + Track Spår - + Album Album - + Artist Artist - + Local Lokalt - + Top 10 Topp 10 - + All available tracks Alla tillgängliga spår - + Drop to send tracks Släpp för att skicka spåret - - + + Show Visa - - + + Hide Göm @@ -3035,8 +3045,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3045,7 +3055,7 @@ Please change the filters or try again. Var god och ändra filtrerna eller försök igen - + Failed to generate preview with the desired filters Det gick inte att generera en förhandsvisning med det valda filtret @@ -3488,17 +3498,17 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::InfoSystem::ChartsPlugin - + Artists Artister - + Albums Album - + Tracks Spår @@ -3582,7 +3592,7 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::InfoSystem::NewReleasesPlugin - + Albums Album @@ -3695,43 +3705,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline @@ -3798,7 +3808,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling @@ -3883,156 +3893,156 @@ anger du PIN-koden här: Tomahawk - + Back Tillbaka - + Go back one page Gå tillbaks en sida - + Forward Framåt - + Go forward one page Gå framåt en sida - - + + Hide Menu Bar Göm Menyrad - - + + Show Menu Bar Visa Menyrad - + Search for any artist, album or song... Sök efter valfri artist, album eller låt... - + &Main Menu &Huvudmeny - + Exit Full Screen Gå ur fullskärmsläge - + Enter Full Screen Fullskärmsläge - + XSPF Error XSPF-fel - + This is not a valid XSPF playlist. Detta är inte en giltig XSPF-spellista. - + Failed to save tracks Misslyckades med att spara spår - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Några spår i spellistan innehåller inte någon artist och titel. De kommer att ignoreras. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Tyvärr! Det uppstod ett problem med kontakten till ditt ljudkort eller det önskade spåret. Nuvarande spår kommer att hoppas över. Kontrollera att du har en lämplig Phonon-backend och alla nödvändiga plugins installerade - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Tyvärr blev det problem att hitta din ljudenhet eller den valda låten! Nuvarande låt kommer att hoppas över - + Station Station - + Create New Station Skapa ny station - + Name: Namn: - + Playlist Spellista - + Automatic Playlist Automatisk spellista - + Pause Paus - + &Play &Spela - + %1 by %2 track, artist name %1 av %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Tack till: - + About Tomahawk Om Tomahawk @@ -4048,47 +4058,47 @@ anger du PIN-koden här: TrackInfoWidget - + Similar Tracks Liknande spår - + Sorry, but we could not find similar tracks for this song! Tyvärr! Det gick inte hitta några spår som liknande denna låten! - + # PLAYS / ARTIST # UPPSPELNINGAR / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). Du har lyssnat på detta spåret %n gånger.Du har lyssnat på detta spåret %n gånger - + You've never listened to this track before. Du har inte lyssnat på detta spåret tidigare. - + You first listened to it on %1. Du har lyssnat på det på %1. - + You've listened to %1 %n time(s). Du har lyssnat på %1 %n gång.Du har lyssnat på %1 %n gånger. - + You've never listened to %1 before. Du har inte lyssnat på %1 tidigare. diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 86a105d1bd..ca163ff008 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 Diğer %1 Albümleri @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits En Çok Dinlenenler - + Related Artists Benzer Sanatçılar - + Albums Albümler - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... Filtre... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1554,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now @@ -1758,6 +1748,26 @@ Password Results for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2021,49 +2031,49 @@ Password SourceDelegate - + Track - + Album - + Artist - + Local - + Top 10 - + All available tracks - + Drop to send tracks - - + + Show - - + + Hide @@ -3025,15 +3035,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3474,17 +3484,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists - + Albums - + Tracks @@ -3567,7 +3577,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums @@ -3680,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3783,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3867,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4032,47 +4042,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index e5f459ed21..e60ca5d1ee 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums 其他专辑 - + Sorry, we could not find any other albums for this artist! 抱歉,找到此艺术家的其他专辑! - + Sorry, we could not find any tracks for this album! 抱歉,没有找到这张专辑的其他歌曲! - + Other Albums by %1 %1 的其他专辑 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 最热歌曲 - + Related Artists 相关艺人 - + Albums 专辑 - + Sorry, we could not find any albums for this artist! 抱歉,未找到该艺术家的其他专辑! - + Sorry, we could not find any related artists! 抱歉,没有找到相关的艺术家! - + Sorry, we could not find any top hits for this artist! 抱歉,没有找到该艺术家的任何人气歌曲! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 抱歉,Tomahawk 未找到 %2 的歌曲 '%1' - + Sorry, Tomahawk couldn't find the artist '%1' 抱歉,Tomahawk 无法找到艺术家 '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 抱歉,Tomahawk 无法找到 %2 的专辑 '%1' @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. 当前播放列表为空。 - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... 过滤... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you %1 - + played %1 by %2 已播放 %2 的 %1 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you 你播放于 %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 播放于 %1 - + added %1 e.g. added 3 hours ago 添加于 %1 - + by <b>%1</b> e.g. by SomeArtist 来自 <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum 来自 <b>%1</b> 的专辑 <b>%2</b> @@ -1555,67 +1545,67 @@ Password QObject - + %n year(s) ago %n 年前 - + %n year(s) %n 年 - + %n month(s) ago %n 月前 - + %n month(s) %n 月 - + %n week(s) ago %n 周前 - + %n week(s) %n 周 - + %n day(s) ago %n 天前 - + %n day(s) %n 天 - + %n hour(s) ago %n 小时前 - + %n hour(s) %n 小时 - + %1 minutes ago %1 分钟前 - + %1 minutes %1 分钟 - + just now 刚刚 @@ -1759,6 +1749,26 @@ Password Results for '%1' '%1' 的搜索结果: + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2024,49 +2034,49 @@ Password SourceDelegate - + Track 歌曲 - + Album 专辑 - + Artist 艺术家 - + Local 本地 - + Top 10 Top 10 - + All available tracks 所有可用的歌曲 - + Drop to send tracks - - + + Show 显示 - - + + Hide 隐藏 @@ -3031,8 +3041,8 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. @@ -3041,7 +3051,7 @@ Please change the filters or try again. 请更改过滤选项或者再试一次。 - + Failed to generate preview with the desired filters 在使用制定过滤器生成预览时失败 @@ -3484,17 +3494,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists 艺术家 - + Albums 专辑 - + Tracks 歌曲 @@ -3577,7 +3587,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums 专辑 @@ -3690,43 +3700,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 @@ -3793,7 +3803,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3878,156 +3888,156 @@ enter the displayed PIN number here: Tomahawk - + Back 后退 - + Go back one page 转向上一页 - + Forward 下一个 - + Go forward one page 转向下一页 - - + + Hide Menu Bar 隐藏菜单栏 - - + + Show Menu Bar 显示菜单栏 - + Search for any artist, album or song... 搜索任意艺人,专辑,或歌曲... - + &Main Menu 主菜单 - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 错误 - + This is not a valid XSPF playlist. 这不是一个合法的 XSPF 播放列表。 - + Failed to save tracks 保存歌曲失败。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. 播放列表中的一些歌曲缺失艺术家和标题,它们将被忽略。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. 抱歉,访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。请确认你正在使用合适的 Phonon 后端并安装了必要的插件。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. 抱歉,在访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。 - + Station 电台 - + Create New Station 创建新电台 - + Name: 名字: - + Playlist 播放列表 - + Automatic Playlist 自动播放列表 - + Pause 暂停 - + &Play 播放 - + %1 by %2 track, artist name %2 的 %1 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 版权所有 2010 - 2013 - + Thanks to: 感谢: - + About Tomahawk 关于 Tomahawk @@ -4043,47 +4053,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks 相似歌曲 - + Sorry, but we could not find similar tracks for this song! 抱歉,无法找到与这首歌类似的歌曲! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). 你已经收听过此歌曲 %n 遍。 - + You've never listened to this track before. 你之前从未听过此歌曲。 - + You first listened to it on %1. 你第一次听的是 %1. - + You've listened to %1 %n time(s). 你已经听过 %1 有 %n 遍了。 - + You've never listened to %1 before. 你之前从未听过 %1。 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 3578b4649a..a16f8ce7cb 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -265,27 +265,27 @@ connect and stream from you? AlbumInfoWidget - + Tracklist - + Other Albums - + Sorry, we could not find any other albums for this artist! - + Sorry, we could not find any tracks for this album! - + Other Albums by %1 列出所有其他專輯,依 %1 @@ -307,37 +307,37 @@ connect and stream from you? ArtistInfoWidget - + Top Hits 流行精選 - + Related Artists 相關演出者 - + Albums 專輯 - + Sorry, we could not find any albums for this artist! - + Sorry, we could not find any related artists! - + Sorry, we could not find any top hits for this artist! - + YOUR ARTIST RANK @@ -383,17 +383,17 @@ connect and stream from you? AudioEngine - + Sorry, Tomahawk couldn't find the track '%1' by %2 - + Sorry, Tomahawk couldn't find the artist '%1' - + Sorry, Tomahawk couldn't find the album '%1' by %2 @@ -589,45 +589,35 @@ connect and stream from you? Dashboard - + Recently Played Tracks - + Recent Additions - + Newest Stations & Playlists - + Dashboard - + An overview of your recent activity - - Recently played tracks - - - - + No recently created playlists in your network. - - - Welcome to Tomahawk - - DatabaseCommand_AllAlbums @@ -800,12 +790,12 @@ connect and stream from you? FlexibleView - + This playlist is currently empty. - + This playlist is currently empty. Add some tracks to it and enjoy the music! @@ -924,7 +914,7 @@ Password InfoBar - + Filter... 過濾器... @@ -1374,12 +1364,12 @@ Password PlaylistItemDelegate - + played %1 by you - + played %1 by %2 @@ -1387,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1554,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago %1 分鐘前 - + %1 minutes %1 分鐘 - + just now 剛才 @@ -1758,6 +1748,26 @@ Password Results for '%1' + + + Tracks + + + + + Artists + + + + + Albums + + + + + Sorry, we could not find any tracks! + + SettingsDialog @@ -2021,49 +2031,49 @@ Password SourceDelegate - + Track 曲目 - + Album 專輯 - + Artist 演出者 - + Local 本地 - + Top 10 前10名 - + All available tracks - + Drop to send tracks - - + + Show 顯示 - - + + Hide 隱藏 @@ -3025,15 +3035,15 @@ username@jabber.org Tomahawk::DynamicModel - - + + Could not find a playable track. Please change the filters or try again. - + Failed to generate preview with the desired filters @@ -3474,17 +3484,17 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Artists 演出者 - + Albums 專輯 - + Tracks 曲目 @@ -3567,7 +3577,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::NewReleasesPlugin - + Albums 專輯 @@ -3680,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3783,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3867,156 +3877,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 錯誤 - + This is not a valid XSPF playlist. - + Failed to save tracks 無法儲存曲目 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: 名稱: - + Playlist - + Automatic Playlist - + Pause 暫停 - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4032,47 +4042,47 @@ enter the displayed PIN number here: TrackInfoWidget - + Similar Tracks - + Sorry, but we could not find similar tracks for this song! - + # PLAYS / ARTIST - + YOUR SONG RANK - + You've listened to this track %n time(s). - + You've never listened to this track before. - + You first listened to it on %1. - + You've listened to %1 %n time(s). - + You've never listened to %1 before. From 6e910803111588d1741f3579544fc35f0b040993 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 16 Jun 2013 10:46:23 +0200 Subject: [PATCH 439/565] * Fixed compiling. --- src/accounts/hatchet/sip/HatchetSip.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/accounts/hatchet/sip/HatchetSip.cpp b/src/accounts/hatchet/sip/HatchetSip.cpp index 8a965a49ba..4b72509726 100644 --- a/src/accounts/hatchet/sip/HatchetSip.cpp +++ b/src/accounts/hatchet/sip/HatchetSip.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include From bfd5690af716c265d1d7edb1e2bcb6d37332d5df Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 10:52:16 +0200 Subject: [PATCH 440/565] Move outbound, ready, onceonly into ConnectionPrivate --- src/libtomahawk/network/Connection.cpp | 34 ++++++++++++------- src/libtomahawk/network/Connection.h | 1 - src/libtomahawk/network/Connection_p.h | 7 +++- src/libtomahawk/network/ControlConnection.cpp | 2 +- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 3366d66407..a447440e68 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -35,8 +35,6 @@ Connection::Connection( Servent* parent ) : QObject() , m_sock( 0 ) , m_servent( parent ) - , m_ready( false ) - , m_onceonly( true ) , d_ptr( new ConnectionPrivate( this ) ) { moveToThread( m_servent->thread() ); @@ -120,15 +118,19 @@ Connection::socket() } void -Connection::setOutbound(bool o) +Connection::setOutbound( bool o ) { - m_outbound = o; + Q_D( Connection ); + + d->outbound = o; } bool Connection::outbound() const { - return m_outbound; + Q_D( const Connection ); + + return d->outbound; } Servent* @@ -229,19 +231,25 @@ Connection::name() const void Connection::setOnceOnly( bool b ) { - m_onceonly = b; + Q_D( Connection ); + + d->onceonly = b; } bool Connection::onceOnly() const { - return m_onceonly; + Q_D( const Connection ); + + return d->onceonly; } bool Connection::isReady() const { - return m_ready; + Q_D( const Connection ); + + return d->ready; } bool @@ -359,7 +367,9 @@ Connection::checkACLResult( const QString &nodeid, const QString &username, Toma void Connection::authCheckTimeout() { - if ( m_ready ) + Q_D( Connection ); + + if ( d->ready ) return; tDebug( LOGVERBOSE ) << "Closing connection, not authed in time."; @@ -549,19 +559,19 @@ Connection::handleReadMsg() d->msg->is( Msg::SETUP ) && d->msg->payload() == "ok" ) { - m_ready = true; + d->ready = true; tDebug( LOGVERBOSE ) << "Connection" << id() << "READY"; setup(); emit ready(); } - else if ( !m_ready && + else if ( !d->ready && outbound() && d->msg->is( Msg::SETUP ) ) { if ( d->msg->payload() == PROTOVER ) { sendMsg( Msg::factory( "ok", Msg::SETUP ) ); - m_ready = true; + d->ready = true; tDebug( LOGVERBOSE ) << "Connection" << id() << "READY"; setup(); emit ready(); diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index de9bb155e6..e3c7b14364 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -123,7 +123,6 @@ private slots: protected: QPointer m_sock; Servent* m_servent; - bool m_outbound, m_ready, m_onceonly; private: Q_DECLARE_PRIVATE( Connection ) diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h index 4682bcfe49..6f64d944f2 100644 --- a/src/libtomahawk/network/Connection_p.h +++ b/src/libtomahawk/network/Connection_p.h @@ -36,11 +36,13 @@ class ConnectionPrivate , do_shutdown( false ) , actually_shutting_down( false ) , peer_disconnected( false ) - , peerport( 0 ) + , ready( false ) + , onceonly( true ) , tx_bytes( 0 ) , tx_bytes_requested( 0 ) , rx_bytes( 0 ) , id( "Connection()" ) + , peerport( 0 ) , statstimer( 0 ) , stats_tx_bytes_per_sec( 0 ) , stats_rx_bytes_per_sec( 0 ) @@ -56,6 +58,9 @@ class ConnectionPrivate bool do_shutdown; bool actually_shutting_down; bool peer_disconnected; + bool outbound; + bool ready; + bool onceonly; qint64 tx_bytes; qint64 tx_bytes_requested; qint64 rx_bytes; diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 1de34b0257..2a749d50de 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -277,7 +277,7 @@ ControlConnection::handleMsg( msg_ptr msg ) void ControlConnection::authCheckTimeout() { - if ( m_ready ) + if ( isReady() ) return; Servent::instance()->queueForAclResult( bareName(), m_peerInfos ); From bd0c3e29ebdc667611b71a39699dda22f08af5c7 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 11:17:05 +0200 Subject: [PATCH 441/565] If a ControlConnection is not anymore responsible for a source, it should not touch it. --- src/libtomahawk/Source.cpp | 3 +++ src/libtomahawk/network/ControlConnection.cpp | 26 +++++++++++++++++-- src/libtomahawk/network/ControlConnection.h | 10 +++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 278d817a3c..3d817c6c0a 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -100,6 +100,7 @@ bool Source::setControlConnection( ControlConnection* cc ) { Q_D( Source ); + QMutexLocker locker( &d->setControlConnectionMutex ); if ( !d->cc.isNull() && d->cc->isReady() && d->cc->isRunning() ) { @@ -107,6 +108,8 @@ Source::setControlConnection( ControlConnection* cc ) peerInfoDebug( (*cc->peerInfos().begin()) ) << Q_FUNC_INFO << "Comparing" << cc->id() << "and" << nodeid << "to detect duplicate connection, outbound:" << cc->outbound(); if ( cc->id() < nodeid && d->cc->outbound() ) { + // Tell the ControlConnection it is not anymore responsible for us. + d->cc->unbindFromSource(); // This ControlConnection is not needed anymore, get rid of it! d->cc->deleteLater(); // Use new ControlConnection diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 2a749d50de..b6e93bba56 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,10 +56,13 @@ ControlConnection::ControlConnection( Servent* parent ) ControlConnection::~ControlConnection() { + QReadLocker locker( &m_sourceLock ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << id() << name(); if ( !m_source.isNull() ) + { m_source->setOffline(); + } delete m_pingtimer; m_servent->unregisterControlConnection( this ); @@ -70,9 +74,18 @@ ControlConnection::~ControlConnection() source_ptr ControlConnection::source() const { + // We return a copy of the shared pointer, no need for a longer lock + QReadLocker locker( &m_sourceLock ); return m_source; } +void +ControlConnection::unbindFromSource() +{ + QWriteLocker locker( &m_sourceLock ); + m_source.clear(); +} + Connection* ControlConnection::clone() @@ -88,6 +101,7 @@ void ControlConnection::setup() { qDebug() << Q_FUNC_INFO << id() << name(); + QWriteLocker sourceLocker( &m_sourceLock ); if ( !m_source.isNull() ) { @@ -132,9 +146,10 @@ ControlConnection::setup() void ControlConnection::registerSource() { + QReadLocker sourceLocker( &m_sourceLock ); QSharedPointer locker = m_source->acquireLock(); // Only continue if we are still the ControlConnection associated with this source. - if ( m_source->controlConnection() == this ) + if ( !m_source.isNull() && m_source->controlConnection() == this ) { qDebug() << Q_FUNC_INFO << m_source->id(); Source* source = (Source*) sender(); @@ -150,6 +165,13 @@ ControlConnection::registerSource() void ControlConnection::setupDbSyncConnection( bool ondemand ) { + QReadLocker locker( &m_sourceLock ); + if ( m_source.isNull() ) + { + // We were unbind from the Source, nothing to do here, just waiting to be deleted. + return; + } + qDebug() << Q_FUNC_INFO << ondemand << m_source->id() << m_dbconnkey << m_dbsyncconn << m_registered; if ( m_dbsyncconn || !m_registered ) @@ -206,7 +228,6 @@ ControlConnection::dbSyncConnFinished( QObject* c ) DBSyncConnection* ControlConnection::dbSyncConnection() { - qDebug() << Q_FUNC_INFO << m_source->id(); if ( !m_dbsyncconn ) { setupDbSyncConnection( true ); @@ -292,6 +313,7 @@ ControlConnection::onPingTimer() { if ( m_pingtimer_mark.elapsed() >= TCP_TIMEOUT * 1000 ) { + QReadLocker locker( &m_sourceLock ); qDebug() << "Timeout reached! Shutting down connection to" << m_source->friendlyName(); shutdown( true ); } diff --git a/src/libtomahawk/network/ControlConnection.h b/src/libtomahawk/network/ControlConnection.h index bddc0f9f03..c6753eda24 100644 --- a/src/libtomahawk/network/ControlConnection.h +++ b/src/libtomahawk/network/ControlConnection.h @@ -32,6 +32,7 @@ #include "DllMacro.h" +#include #include #include @@ -51,6 +52,11 @@ Q_OBJECT Tomahawk::source_ptr source() const; + /** + * Tell this ControlConnection that is no longer controlling the source and should not do any actions on it. + */ + void unbindFromSource(); + void addPeerInfo( const Tomahawk::peerinfo_ptr& peerInfo ); void removePeerInfo( const Tomahawk::peerinfo_ptr& peerInfo ); void setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos ); @@ -72,6 +78,10 @@ private slots: void setupDbSyncConnection( bool ondemand = false ); Tomahawk::source_ptr m_source; + /** + * Lock acces to the source member. A "write" access is only if we change the value of source, not if doing a non-const call. + */ + mutable QReadWriteLock m_sourceLock; DBSyncConnection* m_dbsyncconn; QString m_dbconnkey; From 69c2d603b8539f0a3bb6192d2420f1d4543f7543 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 11:21:34 +0200 Subject: [PATCH 442/565] Make access to Connection->nodeid thread-safe --- src/libtomahawk/network/Connection.cpp | 22 ++++++++++++++++------ src/libtomahawk/network/Connection_p.h | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index a447440e68..791922aa09 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -311,7 +311,10 @@ Connection::start( QTcpSocket* sock ) void Connection::checkACL() { - if ( d_func()->nodeid.isEmpty() ) + Q_D( Connection ); + QReadLocker nodeidLocker( &d->nodeidLock ); + + if ( d->nodeid.isEmpty() ) { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Not checking ACL, nodeid is empty"; QTimer::singleShot( 0, this, SLOT( doSetup() ) ); @@ -328,7 +331,7 @@ Connection::checkACL() connect( ACLRegistry::instance(), SIGNAL( aclResult( QString, QString, Tomahawk::ACLStatus::Type ) ), this, SLOT( checkACLResult( QString, QString, Tomahawk::ACLStatus::Type ) ), Qt::QueuedConnection ); - QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d_func()->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( Tomahawk::ACLStatus::Type, Tomahawk::ACLStatus::NotFound ) ); + QMetaObject::invokeMethod( ACLRegistry::instance(), "isAuthorizedUser", Qt::QueuedConnection, Q_ARG( QString, d->nodeid ), Q_ARG( QString, bareName() ), Q_ARG( Tomahawk::ACLStatus::Type, Tomahawk::ACLStatus::NotFound ) ); } @@ -341,9 +344,12 @@ Connection::bareName() const void Connection::checkACLResult( const QString &nodeid, const QString &username, Tomahawk::ACLStatus::Type peerStatus ) { - if ( nodeid != d_func()->nodeid ) + Q_D( Connection ); + QReadLocker nodeidLocker( &d->nodeidLock ); + + if ( nodeid != d->nodeid ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( d_func()->nodeid ).arg( username ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << QString( "nodeid (%1) not ours (%2) for user %3" ).arg( nodeid ).arg( d->nodeid ).arg( username ); return; } if ( username != bareName() ) @@ -494,13 +500,17 @@ Connection::setId( const QString& id ) QString Connection::nodeId() const { - return d_func()->nodeid; + Q_D( const Connection ); + QReadLocker locker( &d->nodeidLock ); + return d->nodeid; } void Connection::setNodeId( const QString& nodeid ) { - d_func()->nodeid = nodeid; + Q_D( Connection ); + QWriteLocker locker( &d->nodeidLock ); + d->nodeid = nodeid; } diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h index 6f64d944f2..83435ac87d 100644 --- a/src/libtomahawk/network/Connection_p.h +++ b/src/libtomahawk/network/Connection_p.h @@ -25,6 +25,7 @@ #include "MsgProcessor.h" +#include #include #include @@ -67,6 +68,7 @@ class ConnectionPrivate QString id; QString name; QString nodeid; + mutable QReadWriteLock nodeidLock; msg_ptr msg; msg_ptr firstmsg; int peerport; From ceb856acb24a204314ab56838301d26291b7e103 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 11:43:32 +0200 Subject: [PATCH 443/565] Finish Connection pimpeling --- src/libtomahawk/network/Connection.cpp | 117 ++++++++++-------- src/libtomahawk/network/Connection.h | 6 +- src/libtomahawk/network/Connection_p.h | 6 +- src/libtomahawk/network/ControlConnection.cpp | 12 +- 4 files changed, 76 insertions(+), 65 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 791922aa09..4e9cc41405 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -33,11 +33,9 @@ Connection::Connection( Servent* parent ) : QObject() - , m_sock( 0 ) - , m_servent( parent ) - , d_ptr( new ConnectionPrivate( this ) ) + , d_ptr( new ConnectionPrivate( this, parent ) ) { - moveToThread( m_servent->thread() ); + moveToThread( parent->thread() ); tDebug( LOGVERBOSE ) << "CTOR Connection (super)" << thread(); connect( &d_func()->msgprocessor_out, SIGNAL( ready( msg_ptr ) ), @@ -53,13 +51,14 @@ Connection::Connection( Servent* parent ) Connection::~Connection() { - tDebug( LOGVERBOSE ) << "DTOR connection (super)" << id() << thread() << m_sock.isNull(); - if ( !m_sock.isNull() ) + Q_D( Connection ); + tDebug( LOGVERBOSE ) << "DTOR connection (super)" << id() << thread() << d->sock.isNull(); + if ( !d->sock.isNull() ) { - m_sock->deleteLater(); + d->sock->deleteLater(); } - delete d_func()->statstimer; + delete d->statstimer; delete d_ptr; } @@ -67,16 +66,17 @@ Connection::~Connection() void Connection::handleIncomingQueueEmpty() { + Q_D( Connection ); //qDebug() << Q_FUNC_INFO << "bavail" << m_sock->bytesAvailable() // << "isopen" << m_sock->isOpen() // << "m_peer_disconnected" << m_peer_disconnected // << "bytes rx" << bytesReceived(); - if ( !m_sock.isNull() && m_sock->bytesAvailable() == 0 && d_func()->peer_disconnected ) + if ( !d->sock.isNull() && d->sock->bytesAvailable() == 0 && d->peer_disconnected ) { tDebug( LOGVERBOSE ) << "No more data to read, peer disconnected. shutting down connection." - << "bytesavail" << m_sock->bytesAvailable() - << "bytesrx" << d_func()->rx_bytes; + << "bytesavail" << d->sock->bytesAvailable() + << "bytesrx" << d->rx_bytes; shutdown(); } } @@ -112,9 +112,11 @@ Connection::firstMessage() const } const QPointer& -Connection::socket() +Connection::socket() const { - return m_sock; + Q_D( const Connection ); + + return d->sock; } void @@ -136,7 +138,9 @@ Connection::outbound() const Servent* Connection::servent() const { - return m_servent; + Q_D( const Connection ); + + return d->servent; } int @@ -187,16 +191,17 @@ Connection::shutdown( bool waitUntilSentAll ) void Connection::actualShutdown() { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << d_func()->actually_shutting_down << id(); - if ( d_func()->actually_shutting_down ) + Q_D( Connection ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << d->actually_shutting_down << id(); + if ( d->actually_shutting_down ) { return; } - d_func()->actually_shutting_down = true; + d->actually_shutting_down = true; - if ( !m_sock.isNull() && m_sock->isOpen() ) + if ( !d->sock.isNull() && d->sock->isOpen() ) { - m_sock->disconnectFromHost(); + d->sock->disconnectFromHost(); } // qDebug() << "EMITTING finished()"; @@ -255,7 +260,9 @@ Connection::isReady() const bool Connection::isRunning() const { - return m_sock != 0; + Q_D( const Connection ); + + return d->sock != 0; } qint64 @@ -293,15 +300,15 @@ void Connection::start( QTcpSocket* sock ) { Q_D( Connection ); - Q_ASSERT( m_sock.isNull() ); + Q_ASSERT( d->sock.isNull() ); Q_ASSERT( sock ); Q_ASSERT( sock->isValid() ); - m_sock = sock; + d->sock = sock; if ( d->name.isEmpty() ) { - d->name = QString( "peer[%1]" ).arg( m_sock->peerAddress().toString() ); + d->name = QString( "peer[%1]" ).arg( d->sock->peerAddress().toString() ); } QTimer::singleShot( 0, this, SLOT( checkACL() ) ); @@ -396,32 +403,32 @@ Connection::doSetup() HINT: export QT_FATAL_WARNINGS=1 helps to catch these kind of errors. */ - if ( QThread::currentThread() != m_servent->thread() ) + if ( QThread::currentThread() != d->servent->thread() ) { // Connections should always be in the same thread as the servent. - moveToThread( m_servent->thread() ); + moveToThread( d->servent->thread() ); } //stats timer calculates BW used by this connection - d_func()->statstimer = new QTimer; - d_func()->statstimer->moveToThread( this->thread() ); - d_func()->statstimer->setInterval( 1000 ); - connect( d_func()->statstimer, SIGNAL( timeout() ), SLOT( calcStats() ) ); - d_func()->statstimer->start(); - d_func()->statstimer_mark.start(); + d->statstimer = new QTimer; + d->statstimer->moveToThread( this->thread() ); + d->statstimer->setInterval( 1000 ); + connect( d->statstimer, SIGNAL( timeout() ), SLOT( calcStats() ) ); + d->statstimer->start(); + d->statstimer_mark.start(); - m_sock->moveToThread( thread() ); + d->sock->moveToThread( thread() ); - connect( m_sock.data(), SIGNAL( bytesWritten( qint64 ) ), + connect( d->sock.data(), SIGNAL( bytesWritten( qint64 ) ), SLOT( bytesWritten( qint64 ) ), Qt::QueuedConnection ); - connect( m_sock.data(), SIGNAL( disconnected() ), + connect( d->sock.data(), SIGNAL( disconnected() ), SLOT( socketDisconnected() ), Qt::QueuedConnection ); - connect( m_sock.data(), SIGNAL( error( QAbstractSocket::SocketError ) ), + connect( d->sock.data(), SIGNAL( error( QAbstractSocket::SocketError ) ), SLOT( socketDisconnectedError( QAbstractSocket::SocketError ) ), Qt::QueuedConnection ); - connect( m_sock.data(), SIGNAL( readyRead() ), + connect( d->sock.data(), SIGNAL( readyRead() ), SLOT( readyRead() ), Qt::QueuedConnection ); // if connection not authed/setup fast enough, kill it: @@ -446,20 +453,22 @@ Connection::doSetup() void Connection::socketDisconnected() { + Q_D( Connection ); + qint64 bytesAvailable = 0; - if ( !m_sock.isNull() ) + if ( !d->sock.isNull() ) { - bytesAvailable = m_sock->bytesAvailable(); + bytesAvailable = d->sock->bytesAvailable(); } tDebug( LOGVERBOSE ) << "SOCKET DISCONNECTED" << this->name() << id() << "shutdown will happen after incoming queue empties." << "bytesavail:" << bytesAvailable << "bytesRecvd" << bytesReceived(); - d_func()->peer_disconnected = true; + d->peer_disconnected = true; emit socketClosed(); - if ( d_func()->msgprocessor_in.length() == 0 && bytesAvailable == 0 ) + if ( d->msgprocessor_in.length() == 0 && bytesAvailable == 0 ) { handleIncomingQueueEmpty(); actualShutdown(); @@ -522,11 +531,11 @@ Connection::readyRead() if ( d->msg.isNull() ) { - if ( m_sock->bytesAvailable() < Msg::headerSize() ) + if ( d->sock->bytesAvailable() < Msg::headerSize() ) return; char msgheader[ Msg::headerSize() ]; - if ( m_sock->read( (char*) &msgheader, Msg::headerSize() ) != Msg::headerSize() ) + if ( d->sock->read( (char*) &msgheader, Msg::headerSize() ) != Msg::headerSize() ) { tDebug() << "Failed reading msg header"; this->markAsFailed(); @@ -537,10 +546,10 @@ Connection::readyRead() d->rx_bytes += Msg::headerSize(); } - if ( m_sock->bytesAvailable() < d->msg->length() ) + if ( d->sock->bytesAvailable() < d->msg->length() ) return; - QByteArray ba = m_sock->read( d->msg->length() ); + QByteArray ba = d->sock->read( d->msg->length() ); if ( ba.length() != (qint32)d->msg->length() ) { tDebug() << "Failed to read full msg payload"; @@ -553,7 +562,7 @@ Connection::readyRead() handleReadMsg(); // process m_msg and clear() it // since there is no explicit threading, use the event loop to schedule this: - if ( m_sock->bytesAvailable() ) + if ( d->sock->bytesAvailable() ) { QTimer::singleShot( 0, this, SLOT( readyRead() ) ); } @@ -634,17 +643,18 @@ Connection::sendMsg( msg_ptr msg ) void Connection::sendMsg_now( msg_ptr msg ) { + Q_D( Connection ); Q_ASSERT( QThread::currentThread() == thread() ); // Q_ASSERT( this->isRunning() ); - if ( m_sock.isNull() || !m_sock->isOpen() || !m_sock->isWritable() ) + if ( d->sock.isNull() || !d->sock->isOpen() || !d->sock->isWritable() ) { tDebug() << "***** Socket problem, whilst in sendMsg(). Cleaning up. *****"; shutdown( false ); return; } - if ( !msg->write( m_sock.data() ) ) + if ( !msg->write( d->sock.data() ) ) { //qDebug() << "Error writing to socket in sendMsg() *************"; shutdown( false ); @@ -666,13 +676,14 @@ Connection::bytesWritten( qint64 i ) void Connection::calcStats() { - int elapsed = d_func()->statstimer_mark.restart(); // ms since last calc + Q_D( Connection ); + int elapsed = d->statstimer_mark.restart(); // ms since last calc - d_func()->stats_tx_bytes_per_sec = (float)1000 * ( (d_func()->tx_bytes - d_func()->tx_bytes_last) / (float)elapsed ); - d_func()->stats_rx_bytes_per_sec = (float)1000 * ( (d_func()->rx_bytes - d_func()->rx_bytes_last) / (float)elapsed ); + d->stats_tx_bytes_per_sec = (float)1000 * ( (d->tx_bytes - d->tx_bytes_last) / (float)elapsed ); + d->stats_rx_bytes_per_sec = (float)1000 * ( (d->rx_bytes - d->rx_bytes_last) / (float)elapsed ); - d_func()->rx_bytes_last = d_func()->rx_bytes; - d_func()->tx_bytes_last = d_func()->tx_bytes; + d->rx_bytes_last = d->rx_bytes; + d->tx_bytes_last = d->tx_bytes; - emit statsTick( d_func()->stats_tx_bytes_per_sec, d_func()->stats_rx_bytes_per_sec ); + emit statsTick( d->stats_tx_bytes_per_sec, d->stats_rx_bytes_per_sec ); } diff --git a/src/libtomahawk/network/Connection.h b/src/libtomahawk/network/Connection.h index e3c7b14364..4d3e695b32 100644 --- a/src/libtomahawk/network/Connection.h +++ b/src/libtomahawk/network/Connection.h @@ -53,7 +53,7 @@ Q_OBJECT void setFirstMessage( msg_ptr m ); msg_ptr firstMessage() const; - const QPointer& socket(); + const QPointer& socket() const; void setOutbound( bool o ); bool outbound() const; @@ -120,10 +120,6 @@ private slots: void bytesWritten( qint64 ); void calcStats(); -protected: - QPointer m_sock; - Servent* m_servent; - private: Q_DECLARE_PRIVATE( Connection ) ConnectionPrivate* d_ptr; diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h index 83435ac87d..6e0d519faf 100644 --- a/src/libtomahawk/network/Connection_p.h +++ b/src/libtomahawk/network/Connection_p.h @@ -32,8 +32,10 @@ class ConnectionPrivate { public: - ConnectionPrivate( Connection* q ) + ConnectionPrivate( Connection* q, Servent* _servent ) : q_ptr ( q ) + , servent( _servent ) + , sock( 0 ) , do_shutdown( false ) , actually_shutting_down( false ) , peer_disconnected( false ) @@ -55,6 +57,8 @@ class ConnectionPrivate Q_DECLARE_PUBLIC ( Connection ) private: + Servent* servent; + QPointer sock; QHostAddress peerIpAddress; bool do_shutdown; bool actually_shutting_down; diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index b6e93bba56..19bd38a604 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -65,7 +65,7 @@ ControlConnection::~ControlConnection() } delete m_pingtimer; - m_servent->unregisterControlConnection( this ); + servent()->unregisterControlConnection( this ); if ( m_dbsyncconn ) m_dbsyncconn->deleteLater(); } @@ -112,7 +112,7 @@ ControlConnection::setup() QString friendlyName = name(); - tDebug() << "Detected name:" << name() << friendlyName << m_sock->peerAddress(); + tDebug() << "Detected name:" << name() << friendlyName; // setup source and remote collection for this peer m_source = SourceList::instance()->get( id(), friendlyName, true ); @@ -182,18 +182,18 @@ ControlConnection::setupDbSyncConnection( bool ondemand ) if ( !m_dbconnkey.isEmpty() ) { qDebug() << "Connecting to DBSync offer from peer..."; - m_dbsyncconn = new DBSyncConnection( m_servent, m_source ); + m_dbsyncconn = new DBSyncConnection( servent(), m_source ); - m_servent->createParallelConnection( this, m_dbsyncconn, m_dbconnkey ); + servent()->createParallelConnection( this, m_dbsyncconn, m_dbconnkey ); m_dbconnkey.clear(); } else if ( !outbound() || ondemand ) // only one end makes the offer { qDebug() << "Offering a DBSync key to peer..."; - m_dbsyncconn = new DBSyncConnection( m_servent, m_source ); + m_dbsyncconn = new DBSyncConnection( servent(), m_source ); QString key = uuid(); - m_servent->registerOffer( key, m_dbsyncconn ); + servent()->registerOffer( key, m_dbsyncconn ); QVariantMap m; m.insert( "method", "dbsync-offer" ); m.insert( "key", key ); From 54e8ee172b2b54e203ea0b41c146704baaf502b7 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 11:44:48 +0200 Subject: [PATCH 444/565] Add missing include for OSX --- src/libtomahawk/widgets/CheckDirTree.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libtomahawk/widgets/CheckDirTree.cpp b/src/libtomahawk/widgets/CheckDirTree.cpp index 34563d031a..e05105c2d7 100644 --- a/src/libtomahawk/widgets/CheckDirTree.cpp +++ b/src/libtomahawk/widgets/CheckDirTree.cpp @@ -25,6 +25,9 @@ #include #include +#ifdef Q_OS_MAC + #include +#endif static QString s_macVolumePath = "/Volumes"; From 5afc5b09092078b4f8879abd4ed5b317455088d6 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 12:00:19 +0200 Subject: [PATCH 445/565] Remove not needed includes --- src/libtomahawk/Result.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libtomahawk/Result.h b/src/libtomahawk/Result.h index 577e3daff1..ffabfaaba4 100644 --- a/src/libtomahawk/Result.h +++ b/src/libtomahawk/Result.h @@ -24,10 +24,8 @@ #include #include #include -#include #include "utils/TomahawkUtils.h" -#include "Track.h" #include "Typedefs.h" #include "DllMacro.h" From c2b54683d3a645143105f01aab96e933fcbfa314 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 12:32:14 +0200 Subject: [PATCH 446/565] Remove not needed includes in Query.h --- src/libtomahawk/Artist.h | 1 + src/libtomahawk/PlaylistEntry.cpp | 4 +++- src/libtomahawk/Query.h | 2 -- src/libtomahawk/SourcePlaylistInterface.cpp | 7 ++++--- .../database/DatabaseCommand_DeleteInboxEntry.cpp | 1 + .../database/DatabaseCommand_LoadInboxEntries.cpp | 2 ++ .../database/DatabaseCommand_ModifyInboxEntry.cpp | 1 + src/libtomahawk/jobview/InboxJobItem.cpp | 6 ++++-- src/libtomahawk/utils/TomahawkUtils.cpp | 9 +++++---- 9 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/Artist.h b/src/libtomahawk/Artist.h index 420c5b5ea1..3f51d47ea6 100644 --- a/src/libtomahawk/Artist.h +++ b/src/libtomahawk/Artist.h @@ -27,6 +27,7 @@ #include +#include "TrackData.h" #include "Typedefs.h" #include "DllMacro.h" #include "Query.h" diff --git a/src/libtomahawk/PlaylistEntry.cpp b/src/libtomahawk/PlaylistEntry.cpp index 47877d1771..46e6054b7b 100644 --- a/src/libtomahawk/PlaylistEntry.cpp +++ b/src/libtomahawk/PlaylistEntry.cpp @@ -20,9 +20,11 @@ #include "PlaylistEntry.h" -#include "Source.h" #include "utils/Logger.h" +#include "Result.h" +#include "Source.h" + using namespace Tomahawk; diff --git a/src/libtomahawk/Query.h b/src/libtomahawk/Query.h index e471d9ade9..05bc12564b 100644 --- a/src/libtomahawk/Query.h +++ b/src/libtomahawk/Query.h @@ -26,8 +26,6 @@ #include #include "Typedefs.h" -#include "Result.h" -#include "Track.h" #include "infosystem/InfoSystem.h" #include "DllMacro.h" diff --git a/src/libtomahawk/SourcePlaylistInterface.cpp b/src/libtomahawk/SourcePlaylistInterface.cpp index 810869048f..34d7453261 100644 --- a/src/libtomahawk/SourcePlaylistInterface.cpp +++ b/src/libtomahawk/SourcePlaylistInterface.cpp @@ -19,12 +19,13 @@ #include "SourcePlaylistInterface.h" -#include "Source.h" -#include "Pipeline.h" #include "audio/AudioEngine.h" - #include "utils/Logger.h" +#include "Pipeline.h" +#include "Result.h" +#include "Source.h" + using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp b/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp index 8867957b3b..c5c42536e7 100644 --- a/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp +++ b/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp @@ -19,6 +19,7 @@ #include "DatabaseCommand_DeleteInboxEntry.h" #include "DatabaseImpl.h" #include "Query.h" +#include "Track.h" DatabaseCommand_DeleteInboxEntry::DatabaseCommand_DeleteInboxEntry( const Tomahawk::query_ptr& query, diff --git a/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp b/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp index 04d67cfbf2..0d226444ff 100644 --- a/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp +++ b/src/libtomahawk/database/DatabaseCommand_LoadInboxEntries.cpp @@ -23,6 +23,8 @@ #include "Query.h" #include "SourceList.h" #include "TomahawkSqlQuery.h" +#include "Track.h" +#include "TrackData.h" DatabaseCommand_LoadInboxEntries::DatabaseCommand_LoadInboxEntries( QObject* parent ) diff --git a/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp b/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp index 5055e71836..386eb8511c 100644 --- a/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp @@ -19,6 +19,7 @@ #include "DatabaseCommand_ModifyInboxEntry.h" #include "DatabaseImpl.h" #include "Query.h" +#include "Track.h" DatabaseCommand_ModifyInboxEntry::DatabaseCommand_ModifyInboxEntry( const Tomahawk::query_ptr& query, diff --git a/src/libtomahawk/jobview/InboxJobItem.cpp b/src/libtomahawk/jobview/InboxJobItem.cpp index e3b0421412..0193175a2b 100644 --- a/src/libtomahawk/jobview/InboxJobItem.cpp +++ b/src/libtomahawk/jobview/InboxJobItem.cpp @@ -20,10 +20,12 @@ #include "InboxJobItem.h" -#include "Query.h" +#include "audio/AudioEngine.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" -#include "audio/AudioEngine.h" + +#include "Query.h" +#include "TrackData.h" #include diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index 54801067b9..d6a6559e6d 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -21,14 +21,15 @@ #include "utils/TomahawkUtils.h" -#include "Query.h" -#include "TomahawkVersion.h" #include "config.h" -#include "TomahawkSettings.h" -#include "Source.h" #include "BinaryExtractWorker.h" +#include "Query.h" #include "SharedTimeLine.h" +#include "Source.h" +#include "TomahawkSettings.h" +#include "TomahawkVersion.h" +#include "Track.h" #ifdef LIBLASTFM_FOUND #include From 96f28f53ce30654fc998a39b3aa16097c7fa5e82 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 12:56:38 +0200 Subject: [PATCH 447/565] Move implementations out of the header --- src/libtomahawk/Result.cpp | 119 +++++++++++++++++++++++++++++++++++++ src/libtomahawk/Result.h | 44 +++++++------- 2 files changed, 141 insertions(+), 22 deletions(-) diff --git a/src/libtomahawk/Result.cpp b/src/libtomahawk/Result.cpp index 32691a3aab..e99c815249 100644 --- a/src/libtomahawk/Result.cpp +++ b/src/libtomahawk/Result.cpp @@ -140,6 +140,24 @@ Result::collection() const return m_collection; } +QString +Result::url() const +{ + return m_url; +} + +bool +Result::checked() const +{ + return m_checked; +} + +QString +Result::mimetype() const +{ + return m_mimetype; +} + float Result::score() const @@ -271,6 +289,66 @@ Result::setCollection( const Tomahawk::collection_ptr& collection ) connect( m_collection->source().data(), SIGNAL( offline() ), SLOT( onOffline() ), Qt::QueuedConnection ); } +void +Result::setFriendlySource(const QString &s) +{ + m_friendlySource = s; +} + +void +Result::setPurchaseUrl(const QString &u) +{ + m_purchaseUrl = u; +} + +void +Result::setLinkUrl(const QString &u) +{ + m_linkUrl = u; +} + +void +Result::setChecked( bool checked ) +{ + m_checked = checked; +} + +void +Result::setMimetype( const QString &mimetype ) +{ + m_mimetype = mimetype; +} + +void +Result::setBitrate( unsigned int bitrate ) +{ + m_bitrate = bitrate; +} + +void +Result::setSize( unsigned int size ) +{ + m_size = size; +} + +void +Result::setModificationTime( unsigned int modtime ) +{ + m_modtime = modtime; +} + +void +Result::setTrack(const track_ptr &track) +{ + m_track = track; +} + +unsigned int +Result::fileId() const +{ + return m_fileId; +} + QString Result::friendlySource() const @@ -283,6 +361,18 @@ Result::friendlySource() const return collection()->source()->friendlyName(); } +QString +Result::purchaseUrl() const +{ + return m_purchaseUrl; +} + +QString +Result::linkUrl() const +{ + return m_linkUrl; +} + QPixmap Result::sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize ) const @@ -339,6 +429,35 @@ Result::sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize ) c } } +unsigned int +Result::bitrate() const +{ + return m_bitrate; +} + +unsigned int +Result::size() const +{ + return m_size; +} + +unsigned int +Result::modificationTime() const +{ + return m_modtime; +} + +void +Result::setScore( float score ) +{ + m_score = score; +} + +void +Result::setFileId(unsigned int id) +{ + m_fileId = id; +} Tomahawk::Resolver* Result::resolvedBy() const diff --git a/src/libtomahawk/Result.h b/src/libtomahawk/Result.h index ffabfaaba4..8860b713f2 100644 --- a/src/libtomahawk/Result.h +++ b/src/libtomahawk/Result.h @@ -71,40 +71,40 @@ friend class ::DatabaseCommand_LoadFile; bool playable() const; collection_ptr collection() const; - QString url() const { return m_url; } + QString url() const; /** * Has the given url been checked that it is accessible/valid. * * Results marked as true will bypass the ResultUrlChecker. */ - bool checked() const { return m_checked; } - QString mimetype() const { return m_mimetype; } + bool checked() const; + QString mimetype() const; QString friendlySource() const; - QString purchaseUrl() const { return m_purchaseUrl; } - QString linkUrl() const { return m_linkUrl; } + QString purchaseUrl() const; + QString linkUrl() const; QPixmap sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize = QSize() ) const; - unsigned int bitrate() const { return m_bitrate; } - unsigned int size() const { return m_size; } - unsigned int modificationTime() const { return m_modtime; } + unsigned int bitrate() const; + unsigned int size() const; + unsigned int modificationTime() const; - void setScore( float score ) { m_score = score; } - void setFileId( unsigned int id ) { m_fileId = id; } + void setScore( float score ); + void setFileId( unsigned int id ); void setRID( RID id ) { m_rid = id; } void setCollection( const Tomahawk::collection_ptr& collection ); - void setFriendlySource( const QString& s ) { m_friendlySource = s; } - void setPurchaseUrl( const QString& u ) { m_purchaseUrl = u; } - void setLinkUrl( const QString& u ) { m_linkUrl = u; } - void setChecked( bool checked ) { m_checked = checked; } - void setMimetype( const QString& mimetype ) { m_mimetype = mimetype; } - void setBitrate( unsigned int bitrate ) { m_bitrate = bitrate; } - void setSize( unsigned int size ) { m_size = size; } - void setModificationTime( unsigned int modtime ) { m_modtime = modtime; } - - void setTrack( const track_ptr& track ) { m_track = track; } - - unsigned int fileId() const { return m_fileId; } + void setFriendlySource( const QString& s ); + void setPurchaseUrl( const QString& u ); + void setLinkUrl( const QString& u ); + void setChecked( bool checked ); + void setMimetype( const QString& mimetype ); + void setBitrate( unsigned int bitrate ); + void setSize( unsigned int size ); + void setModificationTime( unsigned int modtime ); + + void setTrack( const track_ptr& track ); + + unsigned int fileId() const; track_ptr track() const; From 8f7e2f1f8ad741a4fd0d0783ef2e094d0a67e79a Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 12:57:03 +0200 Subject: [PATCH 448/565] Move includes from global headers into local .cpp --- src/libtomahawk/DropJob.cpp | 17 ++++++++-------- src/libtomahawk/Pipeline.cpp | 8 +++++--- src/libtomahawk/Playlist.h | 1 - src/libtomahawk/Query.cpp | 8 +++++--- .../database/DatabaseCommand_AllTracks.cpp | 8 +++++--- .../jobview/PipelineStatusItem.cpp | 2 ++ .../jobview/TransferStatusItem.cpp | 16 ++++++--------- src/libtomahawk/playlist/PlayableItem.cpp | 4 +++- src/libtomahawk/playlist/PlayableModel.cpp | 14 +++++++------ .../playlist/PlayableProxyModel.cpp | 9 +++++---- .../PlayableProxyModelPlaylistInterface.cpp | 10 ++++++---- src/libtomahawk/playlist/QueueProxyModel.cpp | 6 ++++-- src/libtomahawk/resolvers/JSResolver.cpp | 20 ++++++++----------- src/libtomahawk/resolvers/ScriptResolver.cpp | 1 + src/tomahawk/web/Api_v1.cpp | 9 ++++----- 15 files changed, 71 insertions(+), 62 deletions(-) diff --git a/src/libtomahawk/DropJob.cpp b/src/libtomahawk/DropJob.cpp index ce4dd3553d..0611697daf 100644 --- a/src/libtomahawk/DropJob.cpp +++ b/src/libtomahawk/DropJob.cpp @@ -22,10 +22,9 @@ #include "DropJob.h" #include -#include "Artist.h" -#include "Album.h" -#include "Source.h" - +#include "jobview/JobStatusView.h" +#include "jobview/JobStatusModel.h" +#include "jobview/ErrorStatusMessage.h" #include "utils/SpotifyParser.h" #include "utils/ItunesParser.h" #include "utils/ItunesLoader.h" @@ -36,12 +35,14 @@ #include "utils/ExfmParser.h" #include "utils/Logger.h" #include "utils/TomahawkUtils.h" -#include "GlobalActionManager.h" #include "utils/XspfLoader.h" -#include "jobview/JobStatusView.h" -#include "jobview/JobStatusModel.h" -#include "jobview/ErrorStatusMessage.h" + +#include "Artist.h" +#include "Album.h" +#include "GlobalActionManager.h" #include "Pipeline.h" +#include "Result.h" +#include "Source.h" #ifdef QCA2_FOUND #include "utils/GroovesharkParser.h" diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index 5e4776438e..2f43262036 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -20,16 +20,18 @@ #include -#include "FuncTimeout.h" #include "database/Database.h" #include "resolvers/ExternalResolver.h" #include "resolvers/ScriptResolver.h" #include "resolvers/JSResolver.h" -#include "Source.h" -#include "SourceList.h" #include "utils/ResultUrlChecker.h" #include "utils/Logger.h" +#include "FuncTimeout.h" +#include "Result.h" +#include "Source.h" +#include "SourceList.h" + #include #define DEFAULT_CONCURRENT_QUERIES 4 diff --git a/src/libtomahawk/Playlist.h b/src/libtomahawk/Playlist.h index 3fe6fcede3..a800dd69a6 100644 --- a/src/libtomahawk/Playlist.h +++ b/src/libtomahawk/Playlist.h @@ -28,7 +28,6 @@ #include #include "Typedefs.h" -#include "Result.h" #include "PlaylistEntry.h" #include "PlaylistInterface.h" #include "playlist/PlaylistUpdaterInterface.h" diff --git a/src/libtomahawk/Query.cpp b/src/libtomahawk/Query.cpp index 98bac5dff4..bf0bcd0d40 100644 --- a/src/libtomahawk/Query.cpp +++ b/src/libtomahawk/Query.cpp @@ -21,6 +21,8 @@ #include +#include "audio/AudioEngine.h" +#include "collection/Collection.h" #include "database/Database.h" #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_LogPlayback.h" @@ -28,12 +30,12 @@ #include "database/DatabaseCommand_LoadSocialActions.h" #include "database/DatabaseCommand_SocialAction.h" #include "database/DatabaseCommand_TrackStats.h" +#include "resolvers/Resolver.h" + #include "Album.h" -#include "collection/Collection.h" #include "Pipeline.h" -#include "resolvers/Resolver.h" +#include "Result.h" #include "SourceList.h" -#include "audio/AudioEngine.h" #include "utils/Logger.h" diff --git a/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp b/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp index 8c273f5b9a..acae31b8d0 100644 --- a/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp +++ b/src/libtomahawk/database/DatabaseCommand_AllTracks.cpp @@ -21,11 +21,13 @@ #include -#include "DatabaseImpl.h" -#include "Artist.h" +#include "utils/Logger.h" + #include "Album.h" +#include "Artist.h" +#include "DatabaseImpl.h" +#include "Result.h" #include "SourceList.h" -#include "utils/Logger.h" void diff --git a/src/libtomahawk/jobview/PipelineStatusItem.cpp b/src/libtomahawk/jobview/PipelineStatusItem.cpp index c4692e75f7..e823a8a65f 100644 --- a/src/libtomahawk/jobview/PipelineStatusItem.cpp +++ b/src/libtomahawk/jobview/PipelineStatusItem.cpp @@ -21,8 +21,10 @@ #include "PipelineStatusItem.h" #include "utils/TomahawkUtilsGui.h" + #include "Pipeline.h" #include "Source.h" +#include "Track.h" #ifndef ENABLE_HEADLESS #include "JobStatusModel.h" diff --git a/src/libtomahawk/jobview/TransferStatusItem.cpp b/src/libtomahawk/jobview/TransferStatusItem.cpp index 0fe8a1e6b2..76a07feb37 100644 --- a/src/libtomahawk/jobview/TransferStatusItem.cpp +++ b/src/libtomahawk/jobview/TransferStatusItem.cpp @@ -18,23 +18,19 @@ #include "TransferStatusItem.h" - -#include "JobStatusView.h" -#include "JobStatusModel.h" #include "network/StreamConnection.h" #include "network/Servent.h" #include "utils/TomahawkUtils.h" -#include "Result.h" -#include "Source.h" -#include "Artist.h" -#include "network/StreamConnection.h" -#include "network/Servent.h" #include "utils/TomahawkUtilsGui.h" +#include "Artist.h" +#include "Result.h" +#include "Source.h" +#include "Track.h" #ifndef ENABLE_HEADLESS -#include "JobStatusModel.h" -#include "JobStatusView.h" + #include "JobStatusModel.h" + #include "JobStatusView.h" #endif TransferStatusItem::TransferStatusItem( TransferStatusManager* p, StreamConnection* sc ) diff --git a/src/libtomahawk/playlist/PlayableItem.cpp b/src/libtomahawk/playlist/PlayableItem.cpp index 9ffe017f83..dde8371007 100644 --- a/src/libtomahawk/playlist/PlayableItem.cpp +++ b/src/libtomahawk/playlist/PlayableItem.cpp @@ -18,12 +18,14 @@ #include "PlayableItem.h" +#include "utils/Logger.h" #include "utils/TomahawkUtils.h" + #include "Artist.h" #include "Album.h" #include "Query.h" +#include "Result.h" #include "Source.h" -#include "utils/Logger.h" using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/PlayableModel.cpp b/src/libtomahawk/playlist/PlayableModel.cpp index 670ad88136..9d7bcc72c7 100644 --- a/src/libtomahawk/playlist/PlayableModel.cpp +++ b/src/libtomahawk/playlist/PlayableModel.cpp @@ -20,20 +20,22 @@ #include "PlayableModel.h" -#include -#include -#include +#include "audio/AudioEngine.h" +#include "utils/TomahawkUtils.h" +#include "utils/Logger.h" #include "Artist.h" #include "Album.h" #include "Pipeline.h" #include "PlayableItem.h" #include "PlayableProxyModel.h" +#include "Result.h" #include "Source.h" #include "Typedefs.h" -#include "audio/AudioEngine.h" -#include "utils/TomahawkUtils.h" -#include "utils/Logger.h" + +#include +#include +#include using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/PlayableProxyModel.cpp b/src/libtomahawk/playlist/PlayableProxyModel.cpp index d0f048d692..2beafc3bc7 100644 --- a/src/libtomahawk/playlist/PlayableProxyModel.cpp +++ b/src/libtomahawk/playlist/PlayableProxyModel.cpp @@ -19,16 +19,17 @@ #include "PlayableProxyModel.h" -#include +#include "utils/Logger.h" -#include "PlayableProxyModelPlaylistInterface.h" #include "Artist.h" #include "Album.h" +#include "PlayableItem.h" +#include "PlayableProxyModelPlaylistInterface.h" #include "Query.h" +#include "Result.h" #include "Source.h" -#include "PlayableItem.h" -#include "utils/Logger.h" +#include PlayableProxyModel::PlayableProxyModel( QObject* parent ) : QSortFilterProxyModel( parent ) diff --git a/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.cpp b/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.cpp index 0bc6a110e6..2e87f18917 100644 --- a/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.cpp +++ b/src/libtomahawk/playlist/PlayableProxyModelPlaylistInterface.cpp @@ -19,13 +19,15 @@ #include "PlayableProxyModelPlaylistInterface.h" -#include "PlayableProxyModel.h" -#include "Artist.h" +#include "utils/Logger.h" + #include "Album.h" -#include "Query.h" +#include "Artist.h" #include "PlayableItem.h" +#include "PlayableProxyModel.h" +#include "Query.h" +#include "Result.h" #include "Source.h" -#include "utils/Logger.h" using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/QueueProxyModel.cpp b/src/libtomahawk/playlist/QueueProxyModel.cpp index 66463f2998..8aed6f092e 100644 --- a/src/libtomahawk/playlist/QueueProxyModel.cpp +++ b/src/libtomahawk/playlist/QueueProxyModel.cpp @@ -21,10 +21,12 @@ #include "audio/AudioEngine.h" #include "playlist/TrackView.h" +#include "utils/Logger.h" + #include "PlayableItem.h" -#include "ViewManager.h" +#include "Result.h" #include "Source.h" -#include "utils/Logger.h" +#include "ViewManager.h" using namespace Tomahawk; diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index f01c64af66..9ea63cdf4c 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -20,26 +20,22 @@ #include "JSResolver.h" -#include "Artist.h" -#include "Album.h" -#include "config.h" -#include "Pipeline.h" -#include "ScriptCollection.h" -#include "SourceList.h" -#include "TomahawkSettings.h" -#include "TomahawkVersion.h" - #include "accounts/AccountConfigWidget.h" - #include "network/Servent.h" - #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" #include "jobview/ErrorStatusMessage.h" - #include "utils/TomahawkUtilsGui.h" +#include "Artist.h" +#include "Album.h" #include "config.h" +#include "Pipeline.h" +#include "Result.h" +#include "ScriptCollection.h" +#include "SourceList.h" +#include "TomahawkSettings.h" +#include "TomahawkVersion.h" #include #include diff --git a/src/libtomahawk/resolvers/ScriptResolver.cpp b/src/libtomahawk/resolvers/ScriptResolver.cpp index 3f22470907..7449888ce7 100644 --- a/src/libtomahawk/resolvers/ScriptResolver.cpp +++ b/src/libtomahawk/resolvers/ScriptResolver.cpp @@ -23,6 +23,7 @@ #include "Artist.h" #include "Album.h" #include "Pipeline.h" +#include "Result.h" #include "ScriptCollection.h" #include "SourceList.h" diff --git a/src/tomahawk/web/Api_v1.cpp b/src/tomahawk/web/Api_v1.cpp index 0905659373..f23106ed34 100644 --- a/src/tomahawk/web/Api_v1.cpp +++ b/src/tomahawk/web/Api_v1.cpp @@ -21,22 +21,21 @@ #include "Api_v1.h" -#include "utils/Logger.h" - -#include "utils/TomahawkUtils.h" #include "database/Database.h" #include "database/DatabaseCommand_AddClientAuth.h" #include "database/DatabaseCommand_ClientAuthValid.h" #include "network/Servent.h" +#include "utils/Logger.h" +#include "utils/TomahawkUtils.h" + #include "Pipeline.h" +#include "Result.h" #include "Source.h" #include #include - - using namespace Tomahawk; using namespace TomahawkUtils; From 4d43577eda1ed23a1b6789410cbf230f69d3c4df Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 13:05:41 +0200 Subject: [PATCH 449/565] Add missing include --- tests/TestResult.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/TestResult.h b/tests/TestResult.h index b098760207..f84cb05107 100644 --- a/tests/TestResult.h +++ b/tests/TestResult.h @@ -22,6 +22,7 @@ #include #include "libtomahawk/Result.h" +#include "libtomahawk/Track.h" #include "libtomahawk/Source.h" class TestResult : public QObject From ace775d6a63b81199bf22110939b15aaa93720fe Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 15:54:26 +0200 Subject: [PATCH 450/565] Fight all compiler warnings in libtomahawk --- src/libtomahawk/ActionCollection.cpp | 6 ++++++ src/libtomahawk/AlbumPlaylistInterface.cpp | 10 ++++++++-- src/libtomahawk/ArtistPlaylistInterface.cpp | 11 ++++++++--- src/libtomahawk/LatchManager.cpp | 6 ++++++ src/libtomahawk/context/ContextPage.cpp | 7 +++++++ src/libtomahawk/database/DatabaseCommand.cpp | 9 +++++++-- .../database/DatabaseCommand_ArtistStats.cpp | 6 ++++++ .../DatabaseCommand_DeleteDynamicPlaylist.cpp | 5 +++++ .../database/DatabaseCommand_DeleteInboxEntry.cpp | 5 +++++ .../database/DatabaseCommand_ModifyInboxEntry.cpp | 5 +++++ .../database/DatabaseCommand_NetworkCharts.cpp | 6 ++++++ .../database/DatabaseCommand_TrackStats.cpp | 5 +++++ src/libtomahawk/jobview/InboxJobItem.cpp | 6 ++++++ src/libtomahawk/jobview/TransferStatusItem.cpp | 6 ++++++ .../playlist/PlaylistLargeItemDelegate.cpp | 6 ++++++ src/libtomahawk/playlist/TrackView.cpp | 6 ++++++ src/libtomahawk/utils/ItunesParser.cpp | 6 ++++++ src/libtomahawk/widgets/OverlayWidget.cpp | 5 +++++ 18 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/ActionCollection.cpp b/src/libtomahawk/ActionCollection.cpp index e835d635dd..2c4c9083f3 100644 --- a/src/libtomahawk/ActionCollection.cpp +++ b/src/libtomahawk/ActionCollection.cpp @@ -30,6 +30,12 @@ #include +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Query.h" +#endif + + ActionCollection* ActionCollection::s_instance = 0; ActionCollection* ActionCollection::instance() { diff --git a/src/libtomahawk/AlbumPlaylistInterface.cpp b/src/libtomahawk/AlbumPlaylistInterface.cpp index 67a07c1a71..07266cdd69 100644 --- a/src/libtomahawk/AlbumPlaylistInterface.cpp +++ b/src/libtomahawk/AlbumPlaylistInterface.cpp @@ -20,17 +20,23 @@ #include "AlbumPlaylistInterface.h" -#include "Artist.h" +#include "utils/Logger.h" #include "collection/TracksRequest.h" #include "database/Database.h" #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_AllTracks.h" + +#include "Artist.h" #include "Pipeline.h" #include "Query.h" #include "Source.h" #include "SourceList.h" -#include "utils/Logger.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Result.h" +#endif + using namespace Tomahawk; diff --git a/src/libtomahawk/ArtistPlaylistInterface.cpp b/src/libtomahawk/ArtistPlaylistInterface.cpp index bc901abcbb..2571d8740a 100644 --- a/src/libtomahawk/ArtistPlaylistInterface.cpp +++ b/src/libtomahawk/ArtistPlaylistInterface.cpp @@ -19,14 +19,19 @@ #include "ArtistPlaylistInterface.h" -#include "Artist.h" #include "collection/Collection.h" -#include "Query.h" #include "database/Database.h" #include "database/DatabaseCommand_AllTracks.h" +#include "utils/Logger.h" + +#include "Artist.h" +#include "Query.h" #include "Source.h" -#include "utils/Logger.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Result.h" +#endif using namespace Tomahawk; diff --git a/src/libtomahawk/LatchManager.cpp b/src/libtomahawk/LatchManager.cpp index 9073429ca3..aeb05dfccd 100644 --- a/src/libtomahawk/LatchManager.cpp +++ b/src/libtomahawk/LatchManager.cpp @@ -29,6 +29,12 @@ #include +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Result.h" +#endif + + using namespace Tomahawk; LatchManager::LatchManager( QObject* parent ) diff --git a/src/libtomahawk/context/ContextPage.cpp b/src/libtomahawk/context/ContextPage.cpp index 59435edb67..45106f5dce 100644 --- a/src/libtomahawk/context/ContextPage.cpp +++ b/src/libtomahawk/context/ContextPage.cpp @@ -24,6 +24,13 @@ #include "utils/TomahawkStyle.h" #include "utils/TomahawkUtilsGui.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "collection/Collection.h" + #include "Source.h" +#endif + + using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand.cpp b/src/libtomahawk/database/DatabaseCommand.cpp index b86fd2d53c..4c9df989a7 100644 --- a/src/libtomahawk/database/DatabaseCommand.cpp +++ b/src/libtomahawk/database/DatabaseCommand.cpp @@ -18,6 +18,8 @@ #include "DatabaseCommand.h" +#include "utils/Logger.h" + #include "DatabaseCommand_AddFiles.h" #include "DatabaseCommand_CreatePlaylist.h" #include "DatabaseCommand_DeleteFiles.h" @@ -30,11 +32,14 @@ #include "DatabaseCommand_SetDynamicPlaylistRevision.h" #include "DatabaseCommand_SocialAction.h" #include "DatabaseCommand_ShareTrack.h" - -#include "utils/Logger.h" #include "DatabaseCommand_SetCollectionAttributes.h" #include "DatabaseCommand_SetTrackAttributes.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "collection/Collection.h" +#endif + DatabaseCommand::DatabaseCommand( QObject* parent ) : QObject( parent ) diff --git a/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp index 52deff020b..adad259593 100644 --- a/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ArtistStats.cpp @@ -23,6 +23,12 @@ #include "SourceList.h" #include "utils/Logger.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "collection/Collection.h" +#endif + + using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_DeleteDynamicPlaylist.cpp b/src/libtomahawk/database/DatabaseCommand_DeleteDynamicPlaylist.cpp index d2c99bcbcf..5dfb174401 100644 --- a/src/libtomahawk/database/DatabaseCommand_DeleteDynamicPlaylist.cpp +++ b/src/libtomahawk/database/DatabaseCommand_DeleteDynamicPlaylist.cpp @@ -24,6 +24,11 @@ #include +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "collection/Collection.h" +#endif + using namespace Tomahawk; diff --git a/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp b/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp index c5c42536e7..a0beff7a15 100644 --- a/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp +++ b/src/libtomahawk/database/DatabaseCommand_DeleteInboxEntry.cpp @@ -21,6 +21,11 @@ #include "Query.h" #include "Track.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Source.h" +#endif + DatabaseCommand_DeleteInboxEntry::DatabaseCommand_DeleteInboxEntry( const Tomahawk::query_ptr& query, QObject* parent ) diff --git a/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp b/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp index 386eb8511c..8ef0bd2529 100644 --- a/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp +++ b/src/libtomahawk/database/DatabaseCommand_ModifyInboxEntry.cpp @@ -21,6 +21,11 @@ #include "Query.h" #include "Track.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Source.h" +#endif + DatabaseCommand_ModifyInboxEntry::DatabaseCommand_ModifyInboxEntry( const Tomahawk::query_ptr& query, bool newValue, diff --git a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp index ac7f267691..96e8c3be45 100644 --- a/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp +++ b/src/libtomahawk/database/DatabaseCommand_NetworkCharts.cpp @@ -22,6 +22,12 @@ #include "DatabaseImpl.h" #include "TomahawkSqlQuery.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Source.h" +#endif + + DatabaseCommand_NetworkCharts::DatabaseCommand_NetworkCharts( const QDateTime &from, const QDateTime &to, QObject *parent ) : DatabaseCommand( parent ) , m_amount( 0 ) diff --git a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp index dc763b9418..2cfd203317 100644 --- a/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp +++ b/src/libtomahawk/database/DatabaseCommand_TrackStats.cpp @@ -23,6 +23,11 @@ #include "SourceList.h" #include "utils/Logger.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "collection/Collection.h" +#endif + using namespace Tomahawk; diff --git a/src/libtomahawk/jobview/InboxJobItem.cpp b/src/libtomahawk/jobview/InboxJobItem.cpp index 0193175a2b..b6f5b5a18c 100644 --- a/src/libtomahawk/jobview/InboxJobItem.cpp +++ b/src/libtomahawk/jobview/InboxJobItem.cpp @@ -29,6 +29,12 @@ #include +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Source.h" +#endif + + InboxJobItem::InboxJobItem( Side side, const QString& prettyName, diff --git a/src/libtomahawk/jobview/TransferStatusItem.cpp b/src/libtomahawk/jobview/TransferStatusItem.cpp index 76a07feb37..bc794697b4 100644 --- a/src/libtomahawk/jobview/TransferStatusItem.cpp +++ b/src/libtomahawk/jobview/TransferStatusItem.cpp @@ -33,6 +33,12 @@ #include "JobStatusView.h" #endif +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "collection/Collection.h" +#endif + + TransferStatusItem::TransferStatusItem( TransferStatusManager* p, StreamConnection* sc ) : m_parent( p ) , m_stream( QPointer< StreamConnection >( sc ) ) diff --git a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp index fdb6bf5145..62ed153b3b 100644 --- a/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp +++ b/src/libtomahawk/playlist/PlaylistLargeItemDelegate.cpp @@ -39,6 +39,12 @@ #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "utils/PixmapDelegateFader.h" +#endif + + using namespace Tomahawk; diff --git a/src/libtomahawk/playlist/TrackView.cpp b/src/libtomahawk/playlist/TrackView.cpp index 18a07adb2f..bda8908f90 100644 --- a/src/libtomahawk/playlist/TrackView.cpp +++ b/src/libtomahawk/playlist/TrackView.cpp @@ -36,6 +36,12 @@ #include "utils/Logger.h" #include "InboxModel.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "collection/Collection.h" + #include "utils/PixmapDelegateFader.h" +#endif + #include #include diff --git a/src/libtomahawk/utils/ItunesParser.cpp b/src/libtomahawk/utils/ItunesParser.cpp index d08d0f9e23..9959da5b45 100644 --- a/src/libtomahawk/utils/ItunesParser.cpp +++ b/src/libtomahawk/utils/ItunesParser.cpp @@ -34,6 +34,12 @@ #include "utils/TomahawkUtils.h" #include "utils/Logger.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Playlist.h" +#endif + + using namespace Tomahawk; QPixmap* ItunesParser::s_pixmap = 0; diff --git a/src/libtomahawk/widgets/OverlayWidget.cpp b/src/libtomahawk/widgets/OverlayWidget.cpp index ba518c8e33..f8af87db51 100644 --- a/src/libtomahawk/widgets/OverlayWidget.cpp +++ b/src/libtomahawk/widgets/OverlayWidget.cpp @@ -26,6 +26,11 @@ #include #include +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Source.h" +#endif + #define CORNER_ROUNDNESS 8.0 #define FADING_DURATION 500 From 67fc355f1605728f6a689b672d7e8015b4875d42 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 16:25:39 +0200 Subject: [PATCH 451/565] No more warnings in tomahawk code --- src/accounts/xmpp/XmppInfoPlugin.cpp | 5 +++++ src/infoplugins/linux/mpris/MprisPlugin.h | 5 +++++ src/tomahawk/sourcetree/items/SourceTreeItem.cpp | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/src/accounts/xmpp/XmppInfoPlugin.cpp b/src/accounts/xmpp/XmppInfoPlugin.cpp index 56397580d9..fdeff5d8ba 100644 --- a/src/accounts/xmpp/XmppInfoPlugin.cpp +++ b/src/accounts/xmpp/XmppInfoPlugin.cpp @@ -25,6 +25,11 @@ #include "utils/Logger.h" #include "TomahawkSettings.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Source.h" +#endif + // remove now playing status after PAUSE_TIMEOUT seconds static const int PAUSE_TIMEOUT = 10; diff --git a/src/infoplugins/linux/mpris/MprisPlugin.h b/src/infoplugins/linux/mpris/MprisPlugin.h index a5997cf55c..13cf210b82 100644 --- a/src/infoplugins/linux/mpris/MprisPlugin.h +++ b/src/infoplugins/linux/mpris/MprisPlugin.h @@ -27,6 +27,11 @@ #include "../../InfoPluginDllMacro.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "PlaylistInterface.h" +#endif + #include #include diff --git a/src/tomahawk/sourcetree/items/SourceTreeItem.cpp b/src/tomahawk/sourcetree/items/SourceTreeItem.cpp index f042b561a0..768a2f0cd1 100644 --- a/src/tomahawk/sourcetree/items/SourceTreeItem.cpp +++ b/src/tomahawk/sourcetree/items/SourceTreeItem.cpp @@ -21,6 +21,12 @@ #include "audio/AudioEngine.h" #include "utils/Logger.h" +// Forward Declarations breaking QSharedPointer +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Query.h" +#endif + + using namespace Tomahawk; From 4b29d34278aed41ca1e3ec2f3bf204e5e02d4339 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 16:50:11 +0200 Subject: [PATCH 452/565] Add stub page to pull out again --- src/libtomahawk/ViewManager.cpp | 38 ++++++++++++++++++++++++ src/libtomahawk/ViewManager.h | 7 +++++ src/tomahawk/sourcetree/SourcesModel.cpp | 8 +++++ 3 files changed, 53 insertions(+) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 0fd93eed3b..a84769cfb9 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -61,6 +61,7 @@ #include "utils/Logger.h" #include +#include #include @@ -89,6 +90,7 @@ ViewManager::ViewManager( QObject* parent ) , m_radioView( 0 ) , m_networkActivityWidget( 0 ) , m_currentPage( 0 ) + , m_stubWidget( 0 ) , m_loaded( false ) { s_instance = this; @@ -925,6 +927,42 @@ ViewPage *ViewManager::networkActivityWidget() const } +class StubWidget : public ViewPage +{ +public: + StubWidget(QObject* parent) +// : ViewPage(parent) + { + m_widget = (QWidget*) (new QLabel("Foobar")); + } + + virtual QWidget* widget() { return m_widget; } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return Tomahawk::playlistinterface_ptr(); } + virtual QString title() const { return QString("Great title"); } + virtual QString description() const { return QString("Great description"); } + virtual bool jumpToCurrentTrack() { return false; } + +private: + QWidget* m_widget; +}; + +ViewPage* ViewManager::stubWidget() const +{ + return m_stubWidget; +} + + +ViewPage* ViewManager::showStub() +{ + if ( !m_stubWidget ) + { + m_stubWidget = new StubWidget( m_widget ); + } + + return show( m_stubWidget ); +} + + Tomahawk::ViewPage* ViewManager::superCollectionView() const { diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 34a04a847f..8dd80c06cf 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -58,6 +58,7 @@ class WhatsHotWidget; class QPushButton; class InboxModel; class NetworkActivityWidget; +class StubWidget; namespace Tomahawk { @@ -97,6 +98,7 @@ Q_OBJECT Tomahawk::ViewPage* superCollectionView() const; Tomahawk::ViewPage* inboxWidget() const; Tomahawk::ViewPage* networkActivityWidget() const; + Tomahawk::ViewPage* stubWidget() const; InboxModel* inboxModel(); @@ -144,6 +146,9 @@ public slots: Tomahawk::ViewPage* showRecentPlaysPage(); Tomahawk::ViewPage* showInboxPage(); Tomahawk::ViewPage* showNetworkActivityPage(); + + Tomahawk::ViewPage* showStub(); + void showCurrentTrack(); // Returns the shown viewpage @@ -200,6 +205,8 @@ private slots: InboxModel* m_inboxModel; NetworkActivityWidget* m_networkActivityWidget; + StubWidget* m_stubWidget; + QList< Tomahawk::collection_ptr > m_superCollections; QHash< Tomahawk::dynplaylist_ptr, QPointer > m_dynamicWidgets; diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index f6a6549fb3..958aa2f9ed 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -337,6 +337,14 @@ SourcesModel::appendGroups() boost::bind( &ViewManager::newReleasesWidget, ViewManager::instance() ) ); newReleases->setSortValue( 6 ); + + GenericPageItem* stub = new GenericPageItem( this, browse, tr( "Stub page" ), ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), + boost::bind( &ViewManager::showStub, ViewManager::instance() ), + boost::bind( &ViewManager::stubWidget, ViewManager::instance() ) ); + stub->setSortValue( 7 ); + + + InboxItem* inbox = new InboxItem( this, browse ); inbox->setSortValue( 7 ); From cb5916b42aa92d1689c63c0c72d05899da9f19ef Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 17:22:30 +0200 Subject: [PATCH 453/565] Use QList < ViewPage* > in ViewManager to store simple view pages --- src/libtomahawk/ViewManager.cpp | 19 ++++++++++++------- src/libtomahawk/ViewManager.h | 7 ++++--- src/tomahawk/sourcetree/SourcesModel.cpp | 5 +++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index a84769cfb9..2d3f80b3b4 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -90,7 +90,6 @@ ViewManager::ViewManager( QObject* parent ) , m_radioView( 0 ) , m_networkActivityWidget( 0 ) , m_currentPage( 0 ) - , m_stubWidget( 0 ) , m_loaded( false ) { s_instance = this; @@ -946,20 +945,26 @@ class StubWidget : public ViewPage QWidget* m_widget; }; -ViewPage* ViewManager::stubWidget() const + +ViewPage* +ViewManager::dynamicPageWidget( const QString& pageName ) const { - return m_stubWidget; + return m_dynamicPages.value( pageName ); } -ViewPage* ViewManager::showStub() +ViewPage* +ViewManager::showDynamicPage( const QString& pageName ) { - if ( !m_stubWidget ) + tLog() << Q_FUNC_INFO << "pageName: " << pageName; + + //HACK: this should be initialized somewhere else + if ( !dynamicPageWidget( pageName ) ) { - m_stubWidget = new StubWidget( m_widget ); + m_dynamicPages.insert( pageName, new StubWidget( m_widget ) ); } - return show( m_stubWidget ); + return show( dynamicPageWidget( pageName ) ); } diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 8dd80c06cf..95f1127df3 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -98,7 +98,8 @@ Q_OBJECT Tomahawk::ViewPage* superCollectionView() const; Tomahawk::ViewPage* inboxWidget() const; Tomahawk::ViewPage* networkActivityWidget() const; - Tomahawk::ViewPage* stubWidget() const; + + Tomahawk::ViewPage* dynamicPageWidget( const QString& pageName ) const; InboxModel* inboxModel(); @@ -147,7 +148,7 @@ public slots: Tomahawk::ViewPage* showInboxPage(); Tomahawk::ViewPage* showNetworkActivityPage(); - Tomahawk::ViewPage* showStub(); + Tomahawk::ViewPage* showDynamicPage( const QString& pageName ); void showCurrentTrack(); @@ -205,7 +206,7 @@ private slots: InboxModel* m_inboxModel; NetworkActivityWidget* m_networkActivityWidget; - StubWidget* m_stubWidget; + QHash< QString, Tomahawk::ViewPage* > m_dynamicPages; QList< Tomahawk::collection_ptr > m_superCollections; diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 958aa2f9ed..e6c61eb8b0 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -339,8 +339,9 @@ SourcesModel::appendGroups() GenericPageItem* stub = new GenericPageItem( this, browse, tr( "Stub page" ), ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), - boost::bind( &ViewManager::showStub, ViewManager::instance() ), - boost::bind( &ViewManager::stubWidget, ViewManager::instance() ) ); + boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), QString( "stub" )), + boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), QString( "stub" ) ) ); + stub->setSortValue( 7 ); From 426dd354ebdc721fdeb7756ed9dd7a922b3bc28d Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 17:44:56 +0200 Subject: [PATCH 454/565] Move stub page adding from appendGroups to SourcesModel ctor --- src/tomahawk/sourcetree/SourcesModel.cpp | 42 ++++++++++++++---------- src/tomahawk/sourcetree/SourcesModel.h | 6 ++++ 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index e6c61eb8b0..91a5c59a64 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -58,6 +58,10 @@ SourcesModel::SourcesModel( QObject* parent ) m_rootItem = new SourceTreeItem( this, 0, Invalid ); appendGroups(); + + // Add stub page + appendPageItem( ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), "Stub Page", "stub"); + onSourcesAdded( SourceList::instance()->sources() ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), @@ -292,18 +296,18 @@ SourcesModel::appendGroups() { beginInsertRows( QModelIndex(), rowCount(), rowCount() + 4 ); - GroupItem* browse = new GroupItem( this, m_rootItem, tr( "Browse" ), 0 ); + m_browse = new GroupItem( this, m_rootItem, tr( "Browse" ), 0 ); new HistoryItem( this, m_rootItem, tr( "Search History" ), 1 ); // new SourceTreeItem( this, m_rootItem, SourcesModel::Divider, 2 ); m_myMusicGroup = new GroupItem( this, m_rootItem, tr( "My Music" ), 3 ); - GenericPageItem* dashboard = new GenericPageItem( this, browse, tr( "Dashboard" ), ImageRegistry::instance()->icon( RESPATH "images/dashboard.svg" ), + GenericPageItem* dashboard = new GenericPageItem( this, m_browse, tr( "Dashboard" ), ImageRegistry::instance()->icon( RESPATH "images/dashboard.svg" ), boost::bind( &ViewManager::showDashboard, ViewManager::instance() ), boost::bind( &ViewManager::dashboard, ViewManager::instance() ) ); dashboard->setSortValue( 0 ); // super collection - GenericPageItem* sc = new GenericPageItem( this, browse, tr( "SuperCollection" ), ImageRegistry::instance()->icon( RESPATH "images/supercollection.svg" ), + GenericPageItem* sc = new GenericPageItem( this, m_browse, tr( "SuperCollection" ), ImageRegistry::instance()->icon( RESPATH "images/supercollection.svg" ), boost::bind( &ViewManager::showSuperCollection, ViewManager::instance() ), boost::bind( &ViewManager::superCollectionView, ViewManager::instance() ) ); sc->setSortValue( 1 ); @@ -314,39 +318,31 @@ SourcesModel::appendGroups() boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); radio->setSortValue( 2 ); - LovedTracksItem* loved = new LovedTracksItem( this, browse ); + LovedTracksItem* loved = new LovedTracksItem( this, m_browse ); loved->setSortValue( 3 ); - GenericPageItem* networkActivity = new GenericPageItem( this, browse, tr( "Network Activity" ), TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ), + GenericPageItem* networkActivity = new GenericPageItem( this, m_browse, tr( "Network Activity" ), TomahawkUtils::defaultPixmap( TomahawkUtils::NetworkActivity, TomahawkUtils::Original ), boost::bind( &ViewManager::showNetworkActivityPage, ViewManager::instance() ), boost::bind( &ViewManager::networkActivityWidget, ViewManager::instance() ) ); networkActivity->setSortValue( 3 ); - GenericPageItem* recent = new GenericPageItem( this, browse, tr( "Recently Played" ), ImageRegistry::instance()->icon( RESPATH "images/recently-played.svg" ), + GenericPageItem* recent = new GenericPageItem( this, m_browse, tr( "Recently Played" ), ImageRegistry::instance()->icon( RESPATH "images/recently-played.svg" ), boost::bind( &ViewManager::showRecentPlaysPage, ViewManager::instance() ), boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); recent->setSortValue( 4 ); - GenericPageItem* hot = new GenericPageItem( this, browse, tr( "Charts" ), ImageRegistry::instance()->icon( RESPATH "images/charts.svg" ), + GenericPageItem* hot = new GenericPageItem( this, m_browse, tr( "Charts" ), ImageRegistry::instance()->icon( RESPATH "images/charts.svg" ), boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ), boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() ) ); hot->setSortValue( 5 ); - GenericPageItem* newReleases = new GenericPageItem( this, browse, tr( "New Releases" ), ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), + GenericPageItem* newReleases = new GenericPageItem( this, m_browse, tr( "New Releases" ), ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), boost::bind( &ViewManager::showNewReleasesPage, ViewManager::instance() ), boost::bind( &ViewManager::newReleasesWidget, ViewManager::instance() ) ); newReleases->setSortValue( 6 ); - GenericPageItem* stub = new GenericPageItem( this, browse, tr( "Stub page" ), ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), - boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), QString( "stub" )), - boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), QString( "stub" ) ) ); - - stub->setSortValue( 7 ); - - - - InboxItem* inbox = new InboxItem( this, browse ); + InboxItem* inbox = new InboxItem( this, m_browse ); inbox->setSortValue( 7 ); m_collectionsGroup = new GroupItem( this, m_rootItem, tr( "Friends" ), 4 ); @@ -355,6 +351,18 @@ SourcesModel::appendGroups() endInsertRows(); } +void +SourcesModel::appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ) +{ + +// beginInsertRows(); + GenericPageItem* pageItem = new GenericPageItem( this, m_browse, pageTitle, pageIcon, + boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), pageName ), + boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), pageName ) ); + m_browse; +// endInsertRows(); +} + void SourcesModel::appendItem( const Tomahawk::source_ptr& source ) diff --git a/src/tomahawk/sourcetree/SourcesModel.h b/src/tomahawk/sourcetree/SourcesModel.h index deff172b39..6376420034 100644 --- a/src/tomahawk/sourcetree/SourcesModel.h +++ b/src/tomahawk/sourcetree/SourcesModel.h @@ -103,6 +103,11 @@ class SourcesModel : public QAbstractItemModel void appendGroups(); + /* + * pageIcon and pageTitle are visible in the source tree, pageName is the internal name in the ViewManager + */ + void appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ); + void appendItem( const Tomahawk::source_ptr& source ); bool removeItem( const Tomahawk::source_ptr& source ); @@ -152,6 +157,7 @@ private slots: SourceTreeItem* activatePlaylistPage( Tomahawk::ViewPage* p, SourceTreeItem* i ); SourceTreeItem* m_rootItem; + GroupItem* m_browse; GroupItem* m_collectionsGroup; GroupItem* m_myMusicGroup; GroupItem* m_cloudGroup; From 187602c42410f6b332d444bb5ca2992b49a03d00 Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Sun, 16 Jun 2013 18:06:29 +0200 Subject: [PATCH 455/565] GenericPageItem insertion fixage. --- src/tomahawk/sourcetree/SourcesModel.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 91a5c59a64..277c4c7170 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -354,13 +354,14 @@ SourcesModel::appendGroups() void SourcesModel::appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ) { - -// beginInsertRows(); + QModelIndex parentIndex = indexFromItem( m_browse ); + beginInsertRows( parentIndex, rowCount( parentIndex ), rowCount( parentIndex ) ); GenericPageItem* pageItem = new GenericPageItem( this, m_browse, pageTitle, pageIcon, boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), pageName ), boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), pageName ) ); - m_browse; -// endInsertRows(); + pageItem->setSortValue( rowCount( parentIndex ) ); + + endInsertRows(); } From eaf1b58df604bfa1ca632fb7ecbca757006b3d7c Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 18:29:49 +0200 Subject: [PATCH 456/565] Remove stub page from ViewManager implementation --- src/libtomahawk/ViewManager.cpp | 43 ++++++++++++++------------------- src/libtomahawk/ViewManager.h | 3 +++ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 2d3f80b3b4..21aa95b18b 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -926,30 +926,29 @@ ViewPage *ViewManager::networkActivityWidget() const } -class StubWidget : public ViewPage +ViewPage* +ViewManager::dynamicPageWidget( const QString& pageName ) const { -public: - StubWidget(QObject* parent) -// : ViewPage(parent) - { - m_widget = (QWidget*) (new QLabel("Foobar")); - } - - virtual QWidget* widget() { return m_widget; } - virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return Tomahawk::playlistinterface_ptr(); } - virtual QString title() const { return QString("Great title"); } - virtual QString description() const { return QString("Great description"); } - virtual bool jumpToCurrentTrack() { return false; } + if( m_dynamicPages.contains( pageName ) ) + return m_dynamicPages.value( pageName ); -private: - QWidget* m_widget; -}; + return 0; +} -ViewPage* -ViewManager::dynamicPageWidget( const QString& pageName ) const +void +ViewManager::addDynamicPage(const QString& pageName, ViewPage* page ) { - return m_dynamicPages.value( pageName ); + tLog() << Q_FUNC_INFO << "Trying to add " << pageName; + + if( dynamicPageWidget( pageName ) ) + { + tLog() << "Not adding a second ViewPage with name " << pageName; + Q_ASSERT( false ); + } + + m_dynamicPages.insert( pageName, page ); + emit viewPageAdded( pageName ); } @@ -958,12 +957,6 @@ ViewManager::showDynamicPage( const QString& pageName ) { tLog() << Q_FUNC_INFO << "pageName: " << pageName; - //HACK: this should be initialized somewhere else - if ( !dynamicPageWidget( pageName ) ) - { - m_dynamicPages.insert( pageName, new StubWidget( m_widget ) ); - } - return show( dynamicPageWidget( pageName ) ); } diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 95f1127df3..84a188350b 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -138,6 +138,8 @@ Q_OBJECT void historyBackAvailable( bool avail ); void historyForwardAvailable( bool avail ); + void viewPageAdded( const QString& pageName ); + public slots: Tomahawk::ViewPage* showRadioPage(); Tomahawk::ViewPage* showSuperCollection(); @@ -148,6 +150,7 @@ public slots: Tomahawk::ViewPage* showInboxPage(); Tomahawk::ViewPage* showNetworkActivityPage(); + void addDynamicPage( const QString& pageName, Tomahawk::ViewPage* page ); Tomahawk::ViewPage* showDynamicPage( const QString& pageName ); void showCurrentTrack(); From 68448db75729ebf1f032800382a63d335e6609a2 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 19:01:51 +0200 Subject: [PATCH 457/565] No need for QObject on Private classes --- src/libtomahawk/network/ConnectionManager_p.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libtomahawk/network/ConnectionManager_p.h b/src/libtomahawk/network/ConnectionManager_p.h index 5c2241c0bb..e7f203b511 100644 --- a/src/libtomahawk/network/ConnectionManager_p.h +++ b/src/libtomahawk/network/ConnectionManager_p.h @@ -23,10 +23,8 @@ #include -class ConnectionManagerPrivate : public QObject +class ConnectionManagerPrivate { -Q_OBJECT - public: ConnectionManagerPrivate( ConnectionManager* q, const QString& _nodeid ) : q_ptr ( q ) From e0a6331142c0e79304ffe91f2ff00ac68d031f3c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 19:02:08 +0200 Subject: [PATCH 458/565] Pimple BufferIoDevice --- src/libtomahawk/network/BufferIoDevice.cpp | 109 +++++++++++++++------ src/libtomahawk/network/BufferIoDevice.h | 16 +-- src/libtomahawk/network/BufferIoDevice_p.h | 47 +++++++++ 3 files changed, 133 insertions(+), 39 deletions(-) create mode 100644 src/libtomahawk/network/BufferIoDevice_p.h diff --git a/src/libtomahawk/network/BufferIoDevice.cpp b/src/libtomahawk/network/BufferIoDevice.cpp index d8a7d622f0..0f79c2a273 100644 --- a/src/libtomahawk/network/BufferIoDevice.cpp +++ b/src/libtomahawk/network/BufferIoDevice.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +18,7 @@ * along with Tomahawk. If not, see . */ -#include "BufferIoDevice.h" +#include "BufferIoDevice_p.h" #include #include @@ -30,18 +31,23 @@ BufferIODevice::BufferIODevice( unsigned int size, QObject* parent ) : QIODevice( parent ) - , m_size( size ) - , m_received( 0 ) - , m_pos( 0 ) + , d_ptr( new BufferIODevicePrivate( this, size ) ) { } +BufferIODevice::~BufferIODevice() +{ + delete d_ptr; +} + + bool BufferIODevice::open( OpenMode mode ) { + Q_D( BufferIODevice ); Q_UNUSED( mode ); - QMutexLocker lock( &m_mut ); + QMutexLocker lock( &d->mut ); qDebug() << Q_FUNC_INFO; QIODevice::open( QIODevice::ReadOnly | QIODevice::Unbuffered ); // FIXME? @@ -52,7 +58,8 @@ BufferIODevice::open( OpenMode mode ) void BufferIODevice::close() { - QMutexLocker lock( &m_mut ); + Q_D( BufferIODevice ); + QMutexLocker lock( &d->mut ); qDebug() << Q_FUNC_INFO; QIODevice::close(); @@ -62,16 +69,17 @@ BufferIODevice::close() bool BufferIODevice::seek( qint64 pos ) { - qDebug() << Q_FUNC_INFO << pos << m_size; + Q_D( BufferIODevice ); + qDebug() << Q_FUNC_INFO << pos << d->size; - if ( pos >= m_size ) + if ( pos >= d->size ) return false; int block = blockForPos( pos ); if ( isBlockEmpty( block ) ) emit blockRequest( block ); - m_pos = pos; + d->pos = pos; qDebug() << "Finished seeking"; return true; @@ -81,30 +89,40 @@ BufferIODevice::seek( qint64 pos ) void BufferIODevice::seeked( int block ) { - qDebug() << Q_FUNC_INFO << block << m_size; + Q_D( BufferIODevice ); + qDebug() << Q_FUNC_INFO << block << d->size; } void BufferIODevice::inputComplete( const QString& errmsg ) { + Q_D( BufferIODevice ); qDebug() << Q_FUNC_INFO; setErrorString( errmsg ); - m_size = m_received; + d->size = d->received; emit readChannelFinished(); } +bool +BufferIODevice::isSequential() const +{ + return false; +} + + void BufferIODevice::addData( int block, const QByteArray& ba ) { + Q_D( BufferIODevice ); { - QMutexLocker lock( &m_mut ); + QMutexLocker lock( &d->mut ); - while ( m_buffer.count() <= block ) - m_buffer << QByteArray(); + while ( d->buffer.count() <= block ) + d->buffer << QByteArray(); - m_buffer.replace( block, ba ); + d->buffer.replace( block, ba ); } // If this was the last block of the transfer, check if we need to fill up gaps @@ -116,7 +134,7 @@ BufferIODevice::addData( int block, const QByteArray& ba ) } } - m_received += ba.count(); + d->received += ba.count(); emit bytesWritten( ba.count() ); emit readyRead(); } @@ -125,21 +143,24 @@ BufferIODevice::addData( int block, const QByteArray& ba ) qint64 BufferIODevice::bytesAvailable() const { - return m_size - m_pos; + Q_D( const BufferIODevice ); + + return d->size - d->pos; } qint64 BufferIODevice::readData( char* data, qint64 maxSize ) { + Q_D( BufferIODevice ); // qDebug() << Q_FUNC_INFO << m_pos << maxSize << 1; if ( atEnd() ) return 0; QByteArray ba; - ba.append( getData( m_pos, maxSize ) ); - m_pos += ba.count(); + ba.append( getData( d->pos, maxSize ) ); + d->pos += ba.count(); // qDebug() << Q_FUNC_INFO << maxSize << ba.count() << 2; memcpy( data, ba.data(), ba.count() ); @@ -162,26 +183,44 @@ BufferIODevice::writeData( const char* data, qint64 maxSize ) qint64 BufferIODevice::size() const { - qDebug() << Q_FUNC_INFO << m_size; - return m_size; + Q_D( const BufferIODevice ); + qDebug() << Q_FUNC_INFO << d->size; + return d->size; } bool BufferIODevice::atEnd() const { + Q_D( const BufferIODevice ); // qDebug() << Q_FUNC_INFO << ( m_size <= m_pos ); - return ( m_size <= m_pos ); + return ( d->size <= d->pos ); +} + + +qint64 +BufferIODevice::pos() const +{ + Q_D( const BufferIODevice ); + return d->pos; } void BufferIODevice::clear() { - QMutexLocker lock( &m_mut ); + Q_D( BufferIODevice ); + QMutexLocker lock( &d->mut ); + + d->pos = 0; + d->buffer.clear(); +} + - m_pos = 0; - m_buffer.clear(); +QIODevice::OpenMode +BufferIODevice::openMode() const +{ + return QIODevice::ReadOnly | QIODevice::Unbuffered; } @@ -217,8 +256,10 @@ BufferIODevice::offsetForPos( qint64 pos ) const int BufferIODevice::nextEmptyBlock() const { + Q_D( const BufferIODevice ); + int i = 0; - foreach( const QByteArray& ba, m_buffer ) + foreach( const QByteArray& ba, d->buffer ) { if ( ba.isEmpty() ) return i; @@ -236,9 +277,11 @@ BufferIODevice::nextEmptyBlock() const int BufferIODevice::maxBlocks() const { - int i = m_size / BLOCKSIZE; + Q_D( const BufferIODevice ); + + int i = d->size / BLOCKSIZE; - if ( ( m_size % BLOCKSIZE ) > 0 ) + if ( ( d->size % BLOCKSIZE ) > 0 ) i++; return i; @@ -248,22 +291,24 @@ BufferIODevice::maxBlocks() const bool BufferIODevice::isBlockEmpty( int block ) const { - if ( block >= m_buffer.count() ) + Q_D( const BufferIODevice ); + if ( block >= d->buffer.count() ) return true; - return m_buffer.at( block ).isEmpty(); + return d->buffer.at( block ).isEmpty(); } QByteArray BufferIODevice::getData( qint64 pos, qint64 size ) { + Q_D( BufferIODevice ); // qDebug() << Q_FUNC_INFO << pos << size << 1; QByteArray ba; int block = blockForPos( pos ); int offset = offsetForPos( pos ); - QMutexLocker lock( &m_mut ); + QMutexLocker lock( &d->mut ); while( ba.count() < size ) { if ( block > maxBlocks() ) @@ -272,7 +317,7 @@ BufferIODevice::getData( qint64 pos, qint64 size ) if ( isBlockEmpty( block ) ) break; - ba.append( m_buffer.at( block++ ).mid( offset ) ); + ba.append( d->buffer.at( block++ ).mid( offset ) ); } // qDebug() << Q_FUNC_INFO << pos << size << 2; diff --git a/src/libtomahawk/network/BufferIoDevice.h b/src/libtomahawk/network/BufferIoDevice.h index a118b9eddc..86c96c147f 100644 --- a/src/libtomahawk/network/BufferIoDevice.h +++ b/src/libtomahawk/network/BufferIoDevice.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +24,15 @@ #include #include +class BufferIODevicePrivate; + class BufferIODevice : public QIODevice { Q_OBJECT public: explicit BufferIODevice( unsigned int size = 0, QObject* parent = 0 ); + ~BufferIODevice(); virtual bool open( OpenMode mode ); virtual void close(); @@ -39,16 +43,16 @@ Q_OBJECT virtual qint64 bytesAvailable() const; virtual qint64 size() const; virtual bool atEnd() const; - virtual qint64 pos() const { return m_pos; } + virtual qint64 pos() const; void addData( int block, const QByteArray& ba ); void clear(); - OpenMode openMode() const { return QIODevice::ReadOnly | QIODevice::Unbuffered; } + OpenMode openMode() const; void inputComplete( const QString& errmsg = "" ); - virtual bool isSequential() const { return false; } + virtual bool isSequential() const; static unsigned int blockSize(); @@ -68,11 +72,9 @@ Q_OBJECT int offsetForPos( qint64 pos ) const; QByteArray getData( qint64 pos, qint64 size ); - QList m_buffer; - mutable QMutex m_mut; //const methods need to lock - unsigned int m_size, m_received; + Q_DECLARE_PRIVATE( BufferIODevice ) + BufferIODevicePrivate* d_ptr; - unsigned int m_pos; }; #endif // BUFFERIODEVICE_H diff --git a/src/libtomahawk/network/BufferIoDevice_p.h b/src/libtomahawk/network/BufferIoDevice_p.h new file mode 100644 index 0000000000..c5170e64a0 --- /dev/null +++ b/src/libtomahawk/network/BufferIoDevice_p.h @@ -0,0 +1,47 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef BUFFERIODEVICE_P_H +#define BUFFERIODEVICE_P_H + +#include "BufferIoDevice.h" + +class BufferIODevicePrivate +{ +public: + BufferIODevicePrivate( BufferIODevice* q, unsigned int size = 0 ) + : q_ptr ( q ) + , size( size ) + , received( 0 ) + , pos( 0 ) + + { + } + BufferIODevice* q_ptr; + Q_DECLARE_PUBLIC ( BufferIODevice ) + +private: + QList buffer; + mutable QMutex mut; + unsigned int size; + unsigned int received; + unsigned int pos; +}; + +#endif // BUFFERIODEVICE_P_H From 8128f1ea48b99912418516f1bd3e1b131c7782db Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 19:04:26 +0200 Subject: [PATCH 459/565] Clean up headers --- src/libtomahawk/network/BufferIoDevice.cpp | 1 + src/libtomahawk/network/BufferIoDevice.h | 2 -- src/libtomahawk/network/BufferIoDevice_p.h | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/network/BufferIoDevice.cpp b/src/libtomahawk/network/BufferIoDevice.cpp index 0f79c2a273..d444d14a92 100644 --- a/src/libtomahawk/network/BufferIoDevice.cpp +++ b/src/libtomahawk/network/BufferIoDevice.cpp @@ -21,6 +21,7 @@ #include "BufferIoDevice_p.h" #include +#include #include #include "utils/Logger.h" diff --git a/src/libtomahawk/network/BufferIoDevice.h b/src/libtomahawk/network/BufferIoDevice.h index 86c96c147f..29367eed62 100644 --- a/src/libtomahawk/network/BufferIoDevice.h +++ b/src/libtomahawk/network/BufferIoDevice.h @@ -21,8 +21,6 @@ #define BUFFERIODEVICE_H #include -#include -#include class BufferIODevicePrivate; diff --git a/src/libtomahawk/network/BufferIoDevice_p.h b/src/libtomahawk/network/BufferIoDevice_p.h index c5170e64a0..9a0481f2ef 100644 --- a/src/libtomahawk/network/BufferIoDevice_p.h +++ b/src/libtomahawk/network/BufferIoDevice_p.h @@ -22,6 +22,8 @@ #include "BufferIoDevice.h" +#include + class BufferIODevicePrivate { public: From 63a3339ca54488b228f05ce930b0b17b6334a5ad Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 19:35:58 +0200 Subject: [PATCH 460/565] Add generic page item for new view pages --- src/tomahawk/sourcetree/SourcesModel.cpp | 13 ++++++++++--- src/tomahawk/sourcetree/SourcesModel.h | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 277c4c7170..9e1d57bd21 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -59,9 +59,6 @@ SourcesModel::SourcesModel( QObject* parent ) appendGroups(); - // Add stub page - appendPageItem( ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), "Stub Page", "stub"); - onSourcesAdded( SourceList::instance()->sources() ); connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), @@ -80,6 +77,9 @@ SourcesModel::SourcesModel( QObject* parent ) this, SLOT( onScriptCollectionAdded( Tomahawk::collection_ptr ) ) ); connect( SourceList::instance(), SIGNAL( scriptCollectionRemoved( Tomahawk::collection_ptr ) ), this, SLOT( onScriptCollectionRemoved( Tomahawk::collection_ptr ) ) ); + + + connect( ViewManager::instance(), SIGNAL( viewPageAdded( QString ) ), SLOT( onViewPageAdded( QString ) ) ); } @@ -676,6 +676,13 @@ SourcesModel::onWidgetDestroyed( QWidget* w ) } +void +SourcesModel::onViewPageAdded( const QString& name ) +{ + appendPageItem( ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), name, name); +} + + void SourcesModel::removeSourceItemLink( SourceTreeItem* item ) { diff --git a/src/tomahawk/sourcetree/SourcesModel.h b/src/tomahawk/sourcetree/SourcesModel.h index 6376420034..48172e57d7 100644 --- a/src/tomahawk/sourcetree/SourcesModel.h +++ b/src/tomahawk/sourcetree/SourcesModel.h @@ -151,6 +151,8 @@ private slots: void onWidgetDestroyed( QWidget* w ); + void onViewPageAdded( const QString& name ); + private: SourceTreeItem* itemFromIndex( const QModelIndex& idx ) const; int rowForItem( SourceTreeItem* item ) const; From e5b418ac24059bf8b567998fef14345130d519fb Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 19:39:56 +0200 Subject: [PATCH 461/565] More include cleanups --- src/libtomahawk/network/Connection.cpp | 8 ++++-- src/libtomahawk/network/Servent.cpp | 39 +++++++++++++------------- src/libtomahawk/network/Servent.h | 13 +++------ src/libtomahawk/network/Servent_p.h | 2 ++ 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 4e9cc41405..5a74ea6484 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -20,13 +20,15 @@ #include "Connection_p.h" -#include "QTcpSocketExtra.h" #include "network/Servent.h" #include "utils/Logger.h" + +#include "AclRegistry.h" +#include "QTcpSocketExtra.h" #include "Source.h" -#include -#include +#include +#include #define PROTOVER "4" // must match remote peer, or we can't talk. diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 801aaae905..3d6d13b7ca 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -21,37 +21,38 @@ #include "Servent_p.h" -#include "Result.h" -#include "Source.h" -#include "BufferIoDevice.h" -#include "Connection.h" -#include "ControlConnection.h" -#include "QTcpSocketExtra.h" +#include "accounts/AccountManager.h" #include "database/Database.h" #include "database/DatabaseImpl.h" #include "network/ConnectionManager.h" #include "network/DbSyncConnection.h" -#include "StreamConnection.h" -#include "SourceList.h" #include "sip/SipInfo.h" #include "sip/PeerInfo.h" #include "sip/SipPlugin.h" -#include "PortFwdThread.h" -#include "TomahawkSettings.h" #include "utils/Closure.h" #include "utils/TomahawkUtils.h" #include "utils/Logger.h" -#include "accounts/AccountManager.h" +#include "AclRegistry.h" +#include "BufferIoDevice.h" +#include "Connection.h" +#include "ControlConnection.h" +#include "PortFwdThread.h" +#include "QTcpSocketExtra.h" +#include "Result.h" +#include "Source.h" +#include "SourceList.h" +#include "StreamConnection.h" +#include "TomahawkSettings.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index e713b5d1b2..895dbba5d8 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -25,18 +25,13 @@ // time before new connection terminate if it could not be established #define CONNECT_TIMEOUT 10000 -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include "Typedefs.h" #include "Msg.h" -#include "AclRegistry.h" #include "DllMacro.h" diff --git a/src/libtomahawk/network/Servent_p.h b/src/libtomahawk/network/Servent_p.h index 60ecc1231e..8ac9b9b780 100644 --- a/src/libtomahawk/network/Servent_p.h +++ b/src/libtomahawk/network/Servent_p.h @@ -24,6 +24,8 @@ #include "Servent.h" +#include + #include #include #include From 8817aa65c41e8234e27ef107d7df7e2ad4da7cf5 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 21:13:59 +0200 Subject: [PATCH 462/565] Pimple Msg --- src/libtomahawk/CMakeLists.txt | 1 + ...baseCommand_SetDynamicPlaylistRevision.cpp | 2 + .../DatabaseCommand_SetPlaylistRevision.cpp | 7 +- src/libtomahawk/network/Connection.cpp | 3 + src/libtomahawk/network/ConnectionManager.cpp | 1 + src/libtomahawk/network/ControlConnection.cpp | 9 +- src/libtomahawk/network/DbSyncConnection.cpp | 4 +- src/libtomahawk/network/Msg.cpp | 140 ++++++++++++++++++ src/libtomahawk/network/Msg.h | 129 +++++----------- src/libtomahawk/network/MsgProcessor.cpp | 19 +-- src/libtomahawk/network/MsgProcessor.h | 3 +- src/libtomahawk/network/Msg_p.h | 61 ++++++++ src/libtomahawk/network/QTcpSocketExtra.cpp | 4 + src/libtomahawk/network/QTcpSocketExtra.h | 2 +- src/libtomahawk/network/Servent.cpp | 1 + src/libtomahawk/network/Servent.h | 4 +- src/libtomahawk/network/StreamConnection.cpp | 12 +- src/tomahawk/TomahawkApp.cpp | 1 + 18 files changed, 288 insertions(+), 115 deletions(-) create mode 100644 src/libtomahawk/network/Msg.cpp create mode 100644 src/libtomahawk/network/Msg_p.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 82d01f6172..cce9aa59f0 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -313,6 +313,7 @@ list(APPEND libSources filemetadata/MetadataEditor.cpp network/BufferIoDevice.cpp + network/Msg.cpp network/MsgProcessor.cpp network/StreamConnection.cpp network/DbSyncConnection.cpp diff --git a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp index 1ee47c8d13..15f80f1922 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetDynamicPlaylistRevision.cpp @@ -28,6 +28,8 @@ #include "Source.h" #include "TomahawkSqlQuery.h" +#include + #include DatabaseCommand_SetDynamicPlaylistRevision::DatabaseCommand_SetDynamicPlaylistRevision( const Tomahawk::source_ptr& s, diff --git a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp index 94fc9983f2..7dbb52f0d7 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp @@ -18,8 +18,6 @@ #include "DatabaseCommand_SetPlaylistRevision.h" -#include - #include "collection/Collection.h" #include "network/Servent.h" #include "utils/Logger.h" @@ -28,6 +26,11 @@ #include "Source.h" #include "TomahawkSqlQuery.h" +#include +#include + +#include + using namespace Tomahawk; diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 5a74ea6484..03adfb45ee 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -21,12 +21,15 @@ #include "Connection_p.h" #include "network/Servent.h" +#include "network/Msg.h" #include "utils/Logger.h" #include "AclRegistry.h" #include "QTcpSocketExtra.h" #include "Source.h" +#include + #include #include diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index 790d8f5d61..c48c3bcfc2 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -19,6 +19,7 @@ #include "ConnectionManager_p.h" #include "ControlConnection.h" +#include "network/Msg.h" #include "QTcpSocketExtra.h" #include "Servent.h" diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 19bd38a604..3817fc84b2 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -20,17 +20,18 @@ #include "ControlConnection.h" -#include "StreamConnection.h" #include "database/Database.h" #include "database/DatabaseCommand_CollectionStats.h" -#include "DbSyncConnection.h" -#include "SourceList.h" -#include "MsgProcessor.h" #include "network/DbSyncConnection.h" +#include "network/Msg.h" +#include "network/MsgProcessor.h" #include "network/Servent.h" #include "sip/PeerInfo.h" #include "utils/Logger.h" +#include "StreamConnection.h" +#include "SourceList.h" + #define TCP_TIMEOUT 600 using namespace Tomahawk; diff --git a/src/libtomahawk/network/DbSyncConnection.cpp b/src/libtomahawk/network/DbSyncConnection.cpp index 86f30c2130..b2b49369b6 100644 --- a/src/libtomahawk/network/DbSyncConnection.cpp +++ b/src/libtomahawk/network/DbSyncConnection.cpp @@ -35,11 +35,13 @@ #include "database/DatabaseCommand.h" #include "database/DatabaseCommand_CollectionStats.h" #include "database/DatabaseCommand_LoadOps.h" +#include "utils/Logger.h" + +#include "Msg.h" #include "MsgProcessor.h" #include "RemoteCollection.h" #include "Source.h" #include "SourceList.h" -#include "utils/Logger.h" using namespace Tomahawk; diff --git a/src/libtomahawk/network/Msg.cpp b/src/libtomahawk/network/Msg.cpp new file mode 100644 index 0000000000..bdc9bd0c6c --- /dev/null +++ b/src/libtomahawk/network/Msg.cpp @@ -0,0 +1,140 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "Msg_p.h" + +#include + +#include + +Msg::Msg( const QByteArray& ba, char f ) + : d_ptr( new MsgPrivate( this, ba, f ) ) +{ +} + +Msg::Msg( quint32 len, quint8 flags ) + : d_ptr( new MsgPrivate( this, len, flags ) ) +{ +} + + +Msg::~Msg() +{ + delete d_ptr; +} + + +msg_ptr +Msg::factory( const QByteArray& ba, char f ) +{ + return msg_ptr( new Msg( ba, f ) ); +} + + +msg_ptr +Msg::begin( char* headerToParse ) +{ + quint32 lenBE = *( (quint32*) headerToParse ); + quint8 flags = *( (quint8*) (headerToParse+4) ); + return msg_ptr( new Msg( qFromBigEndian(lenBE), flags ) ); +} + + +void +Msg::fill( const QByteArray& ba ) +{ + Q_D( Msg ); + Q_ASSERT( d->incomplete ); + Q_ASSERT( ba.length() == (qint32)d->length ); + d->payload = ba; + d->incomplete = false; +} + + +bool +Msg::write( QIODevice * device ) +{ + Q_D( Msg ); + quint32 size = qToBigEndian( d->length ); + quint8 flags = d->flags; + if( device->write( (const char*) &size, sizeof(quint32) ) != sizeof(quint32) ) return false; + if( device->write( (const char*) &flags, sizeof(quint8) ) != sizeof(quint8) ) return false; + if( device->write( (const char*) d->payload.data(), d->length ) != d->length ) return false; + return true; +} + + +quint8 +Msg::headerSize() +{ + return sizeof(quint32) + sizeof(quint8); +} + + +quint32 +Msg::length() const +{ + Q_D( const Msg ); + + return d->length; +} + + +bool +Msg::is( Flag flag ) +{ + Q_D( Msg ); + + return d->flags & flag; +} + + +const QByteArray& +Msg::payload() const +{ + Q_D( const Msg ); + Q_ASSERT( d->incomplete == false ); + return d->payload; +} + + +QVariant& +Msg::json() +{ + Q_D( Msg ); + Q_ASSERT( is(JSON) ); + Q_ASSERT( !is(COMPRESSED) ); + + if( !d->json_parsed ) + { + QJson::Parser p; + bool ok; + d->json = p.parse( d->payload, &ok ); + d->json_parsed = true; + } + return d->json; +} + + +char +Msg::flags() const +{ + Q_D( const Msg ); + return d->flags; +} diff --git a/src/libtomahawk/network/Msg.h b/src/libtomahawk/network/Msg.h index 77aff75b21..9d1d595919 100644 --- a/src/libtomahawk/network/Msg.h +++ b/src/libtomahawk/network/Msg.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,14 +33,11 @@ #include "Typedefs.h" -#include #include -#include -#include -#include -#include -#include +class MsgPrivate; +class QByteArray; +class QIODevice; class Msg { @@ -58,101 +56,54 @@ class Msg SETUP = 128 // used to handshake/auth the connection prior to handing over to Connection subclass }; - virtual ~Msg() - { - //qDebug() << Q_FUNC_INFO; - } + virtual ~Msg(); - /// constructs new msg you wish to send - static msg_ptr factory( const QByteArray& ba, char f ) - { - return msg_ptr( new Msg( ba, f ) ); - } + /** + * constructs new msg you wish to send + */ + static msg_ptr factory( const QByteArray& ba, char f ); - /// constructs an incomplete new msg that is missing the payload data - static msg_ptr begin( char* headerToParse ) - { - quint32 lenBE = *( (quint32*) headerToParse ); - quint8 flags = *( (quint8*) (headerToParse+4) ); - return msg_ptr( new Msg( qFromBigEndian(lenBE), flags ) ); - } + /** + * constructs an incomplete new msg that is missing the payload data + */ + static msg_ptr begin( char* headerToParse ); - /// completes msg construction by providing payload data - void fill( const QByteArray& ba ) - { - Q_ASSERT( m_incomplete ); - Q_ASSERT( ba.length() == (qint32)m_length ); - m_payload = ba; - m_incomplete = false; - } - - /// frames the msg and writes to the wire: - bool write( QIODevice * device ) - { - quint32 size = qToBigEndian( m_length ); - quint8 flags = m_flags; - if( device->write( (const char*) &size, sizeof(quint32) ) != sizeof(quint32) ) return false; - if( device->write( (const char*) &flags, sizeof(quint8) ) != sizeof(quint8) ) return false; - if( device->write( (const char*) m_payload.data(), m_length ) != m_length ) return false; - return true; - } + /** + * completes msg construction by providing payload data + */ + void fill( const QByteArray& ba ); - // len(4) + flags(1) - static quint8 headerSize() { return sizeof(quint32) + sizeof(quint8); } + /** + * frames the msg and writes to the wire: + */ + bool write( QIODevice * device ); - quint32 length() const { return m_length; } + // len(4) + flags(1) + static quint8 headerSize(); - bool is( Flag flag ) { return m_flags & flag; } + quint32 length() const; - const QByteArray& payload() const - { - Q_ASSERT( m_incomplete == false ); - return m_payload; - } + bool is( Flag flag ); - QVariant& json() - { - Q_ASSERT( is(JSON) ); - Q_ASSERT( !is(COMPRESSED) ); + const QByteArray& payload() const; - if( !m_json_parsed ) - { - QJson::Parser p; - bool ok; - m_json = p.parse( m_payload, &ok ); - m_json_parsed = true; - } - return m_json; - } + QVariant& json(); - char flags() const { return m_flags; } + char flags() const; private: - /// used when constructing Msg you wish to send - Msg( const QByteArray& ba, char f ) - : m_payload( ba ), - m_length( ba.length() ), - m_flags( f ), - m_incomplete( false ), - m_json_parsed( false) - { - } - - /// used when constructung Msg off the wire: - Msg( quint32 len, quint8 flags ) - : m_length( len ), - m_flags( flags ), - m_incomplete( true ), - m_json_parsed( false) - { - } - - QByteArray m_payload; - quint32 m_length; - char m_flags; - bool m_incomplete; - QVariant m_json; - bool m_json_parsed; + /** + * Used when constructing Msg you wish to send + */ + Msg( const QByteArray& ba, char f ); + + /** + * used when constructung Msg off the wire: + */ + Msg( quint32 len, quint8 flags ); + + Q_DECLARE_PRIVATE( Msg ) + MsgPrivate* d_ptr; }; #endif // MSG_H diff --git a/src/libtomahawk/network/MsgProcessor.cpp b/src/libtomahawk/network/MsgProcessor.cpp index 4e6a883b83..22357abc34 100644 --- a/src/libtomahawk/network/MsgProcessor.cpp +++ b/src/libtomahawk/network/MsgProcessor.cpp @@ -18,6 +18,7 @@ #include "MsgProcessor.h" +#include "network/Msg_p.h" #include "network/Servent.h" #include "utils/Logger.h" @@ -114,21 +115,21 @@ MsgProcessor::process( msg_ptr msg, quint32 mode, quint32 threshold ) if( (mode & UNCOMPRESS_ALL) && msg->is( Msg::COMPRESSED ) ) { // qDebug() << "MsgProcessor::UNCOMPRESSING"; - msg->m_payload = qUncompress( msg->payload() ); - msg->m_length = msg->m_payload.length(); - msg->m_flags ^= Msg::COMPRESSED; + msg->d_func()->payload = qUncompress( msg->payload() ); + msg->d_func()->length = msg->d_func()->payload.length(); + msg->d_func()->flags ^= Msg::COMPRESSED; } // parse json payload into qvariant if needed if( (mode & PARSE_JSON) && msg->is( Msg::JSON ) && - msg->m_json_parsed == false ) + msg->d_func()->json_parsed == false ) { // qDebug() << "MsgProcessor::PARSING JSON"; bool ok; QJson::Parser parser; - msg->m_json = parser.parse( msg->payload(), &ok ); - msg->m_json_parsed = true; + msg->d_func()->json = parser.parse( msg->payload(), &ok ); + msg->d_func()->json_parsed = true; } // compress if needed @@ -137,9 +138,9 @@ MsgProcessor::process( msg_ptr msg, quint32 mode, quint32 threshold ) && msg->length() > threshold ) { // qDebug() << "MsgProcessor::COMPRESSING"; - msg->m_payload = qCompress( msg->payload(), 9 ); - msg->m_length = msg->m_payload.length(); - msg->m_flags |= Msg::COMPRESSED; + msg->d_func()->payload = qCompress( msg->payload(), 9 ); + msg->d_func()->length = msg->d_func()->payload.length(); + msg->d_func()->flags |= Msg::COMPRESSED; } return msg; } diff --git a/src/libtomahawk/network/MsgProcessor.h b/src/libtomahawk/network/MsgProcessor.h index ae0892dc90..9539f519b1 100644 --- a/src/libtomahawk/network/MsgProcessor.h +++ b/src/libtomahawk/network/MsgProcessor.h @@ -30,7 +30,8 @@ #ifndef MSGPROCESSOR_H #define MSGPROCESSOR_H -#include "Msg.h" +#include "Typedefs.h" +#include "Msg.h" // Needed because we have msg_ptr in a slot #include diff --git a/src/libtomahawk/network/Msg_p.h b/src/libtomahawk/network/Msg_p.h new file mode 100644 index 0000000000..73b6ce60fb --- /dev/null +++ b/src/libtomahawk/network/Msg_p.h @@ -0,0 +1,61 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef MSG_P_H +#define MSG_P_H + +#include "Msg.h" + +class MsgPrivate +{ + friend class MsgProcessor; + +public: + MsgPrivate( Msg* q, const QByteArray& ba, char f ) + : q_ptr ( q ) + , payload( ba ) + , length( ba.length() ) + , flags( f ) + , incomplete( false ) + , json_parsed( false ) + { + } + + MsgPrivate( Msg* q, quint32 len, quint8 flags ) + : q_ptr( q) + , length( len ) + , flags( flags ) + , incomplete( true ) + , json_parsed( false) + { + } + + Msg* q_ptr; + Q_DECLARE_PUBLIC ( Msg ) + +private: + QByteArray payload; + quint32 length; + char flags; + bool incomplete; + QVariant json; + bool json_parsed; +}; + +#endif // MSG_P_H diff --git a/src/libtomahawk/network/QTcpSocketExtra.cpp b/src/libtomahawk/network/QTcpSocketExtra.cpp index 4c1a51e5f1..6b8906db0e 100644 --- a/src/libtomahawk/network/QTcpSocketExtra.cpp +++ b/src/libtomahawk/network/QTcpSocketExtra.cpp @@ -23,6 +23,10 @@ #include "utils/Logger.h" +#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 ) + #include "Msg.h" +#endif + void QTcpSocketExtra::connectToHost( const QHostAddress& host, quint16 port, OpenMode openMode ) { diff --git a/src/libtomahawk/network/QTcpSocketExtra.h b/src/libtomahawk/network/QTcpSocketExtra.h index 0ca56da1da..1d2cbad675 100644 --- a/src/libtomahawk/network/QTcpSocketExtra.h +++ b/src/libtomahawk/network/QTcpSocketExtra.h @@ -30,8 +30,8 @@ #include #include -#include "Msg.h" #include "DllMacro.h" +#include "Typedefs.h" class Connection; diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 3d6d13b7ca..315035955f 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -24,6 +24,7 @@ #include "accounts/AccountManager.h" #include "database/Database.h" #include "database/DatabaseImpl.h" +#include "network/Msg.h" #include "network/ConnectionManager.h" #include "network/DbSyncConnection.h" #include "sip/SipInfo.h" diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 895dbba5d8..379eedc5c1 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -30,10 +30,8 @@ #include #include -#include "Typedefs.h" -#include "Msg.h" - #include "DllMacro.h" +#include "Typedefs.h" class Connection; class Connector; diff --git a/src/libtomahawk/network/StreamConnection.cpp b/src/libtomahawk/network/StreamConnection.cpp index c204ce3477..b6fcec8068 100644 --- a/src/libtomahawk/network/StreamConnection.cpp +++ b/src/libtomahawk/network/StreamConnection.cpp @@ -20,15 +20,17 @@ #include "StreamConnection.h" -#include "Result.h" -#include "BufferIoDevice.h" -#include "network/ControlConnection.h" -#include "network/Servent.h" #include "database/DatabaseCommand_LoadFiles.h" #include "database/Database.h" +#include "network/ControlConnection.h" +#include "network/Servent.h" +#include "utils/Logger.h" + +#include "BufferIoDevice.h" +#include "Msg.h" #include "MsgProcessor.h" +#include "Result.h" #include "SourceList.h" -#include "utils/Logger.h" #include #include diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index 6bdf8919e5..46a12038db 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -55,6 +55,7 @@ #include "DropJob.h" #include "EchonestCatalogSynchronizer.h" #include "database/DatabaseImpl.h" +#include "network/Msg.h" #include "audio/AudioEngine.h" #include "utils/XspfLoader.h" From 6b69e507319aa82e8c04b8a14c10ebcde7b79913 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 21:33:52 +0200 Subject: [PATCH 463/565] Explicitly hide inactive view pages --- src/libtomahawk/ViewManager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 21aa95b18b..e9db1ee75a 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -691,8 +691,14 @@ ViewManager::setPage( ViewPage* page, bool trackHistory ) connect( obj, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ), Qt::UniqueConnection ); } + QWidget *previousPage = m_stack->currentWidget(); + m_stack->setCurrentWidget( page->widget() ); + //This should save the CPU cycles, especially with pages like the visualizer + if(previousPage && previousPage != page->widget()) + previousPage->hide(); + updateView(); } From a928833373b490a68b0c966e8402764e917a5a18 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 21:34:31 +0200 Subject: [PATCH 464/565] Use XInitThreads() on X11 to enable loading the visualizer module --- src/tomahawk/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tomahawk/main.cpp b/src/tomahawk/main.cpp index bde66a1213..67a37b9876 100644 --- a/src/tomahawk/main.cpp +++ b/src/tomahawk/main.cpp @@ -37,6 +37,10 @@ #ifdef WITH_BREAKPAD #include "breakpad/BreakPad.h" #endif + + #ifdef Q_WS_X11 // This is probably a very bad idea with Qt5 anyway... because (if at all) X lives in a QPA plugin + #include + #endif #endif @@ -138,6 +142,10 @@ main( int argc, char *argv[] ) #endif // Q_WS_MAC #endif //Q_OS_WIN + #ifdef Q_WS_X11 + XInitThreads(); + #endif + TomahawkApp a( argc, argv ); // MUST register StateHash ****before*** initing TomahawkSettingsGui as constructor of settings does upgrade before Gui subclass registers type From 257395a1fe1388f8ef1b629256729ac59ca4853c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 16 Jun 2013 21:37:16 +0200 Subject: [PATCH 465/565] Pimple ControlConnection --- src/libtomahawk/network/ControlConnection.cpp | 143 ++++++++++-------- src/libtomahawk/network/ControlConnection.h | 30 +--- src/libtomahawk/network/ControlConnection_p.h | 63 ++++++++ 3 files changed, 149 insertions(+), 87 deletions(-) create mode 100644 src/libtomahawk/network/ControlConnection_p.h diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 3817fc84b2..93f0460395 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -18,7 +18,7 @@ * along with Tomahawk. If not, see . */ -#include "ControlConnection.h" +#include "ControlConnection_p.h" #include "database/Database.h" #include "database/DatabaseCommand_CollectionStats.h" @@ -39,10 +39,7 @@ using namespace Tomahawk; ControlConnection::ControlConnection( Servent* parent ) : Connection( parent ) - , m_dbsyncconn( 0 ) - , m_registered( false ) - , m_shutdownOnEmptyPeerInfos( true ) - , m_pingtimer( 0 ) + , d_ptr( new ControlConnectionPrivate( this ) ) { qDebug() << "CTOR controlconnection"; setId("ControlConnection()"); @@ -57,34 +54,39 @@ ControlConnection::ControlConnection( Servent* parent ) ControlConnection::~ControlConnection() { - QReadLocker locker( &m_sourceLock ); + Q_D( ControlConnection ); + QReadLocker locker( &d->sourceLock ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << id() << name(); - if ( !m_source.isNull() ) + if ( !d->source.isNull() ) { - m_source->setOffline(); + d->source->setOffline(); } - delete m_pingtimer; + delete d->pingtimer; servent()->unregisterControlConnection( this ); - if ( m_dbsyncconn ) - m_dbsyncconn->deleteLater(); + if ( d->dbsyncconn ) + d->dbsyncconn->deleteLater(); + delete d_ptr; } source_ptr ControlConnection::source() const { + Q_D( const ControlConnection ); // We return a copy of the shared pointer, no need for a longer lock - QReadLocker locker( &m_sourceLock ); - return m_source; + QReadLocker locker( &d->sourceLock ); + return d->source; } + void ControlConnection::unbindFromSource() { - QWriteLocker locker( &m_sourceLock ); - m_source.clear(); + Q_D( ControlConnection ); + QWriteLocker locker( &d->sourceLock ); + d->source.clear(); } @@ -101,10 +103,11 @@ ControlConnection::clone() void ControlConnection::setup() { + Q_D( ControlConnection ); qDebug() << Q_FUNC_INFO << id() << name(); - QWriteLocker sourceLocker( &m_sourceLock ); + QWriteLocker sourceLocker( &d->sourceLock ); - if ( !m_source.isNull() ) + if ( !d->source.isNull() ) { qDebug() << "This source seems to be online already."; Q_ASSERT( false ); @@ -116,24 +119,24 @@ ControlConnection::setup() tDebug() << "Detected name:" << name() << friendlyName; // setup source and remote collection for this peer - m_source = SourceList::instance()->get( id(), friendlyName, true ); - QSharedPointer locker = m_source->acquireLock(); - if ( m_source->setControlConnection( this ) ) + d->source = SourceList::instance()->get( id(), friendlyName, true ); + QSharedPointer locker = d->source->acquireLock(); + if ( d->source->setControlConnection( this ) ) { // We are the new ControlConnection for this source // delay setting up collection/etc until source is synced. // we need it DB synced so it has an ID + exists in DB. - connect( m_source.data(), SIGNAL( syncedWithDatabase() ), + connect( d->source.data(), SIGNAL( syncedWithDatabase() ), SLOT( registerSource() ), Qt::QueuedConnection ); - m_source->setOnline(); + d->source->setOnline(); - m_pingtimer = new QTimer; - m_pingtimer->setInterval( 5000 ); - connect( m_pingtimer, SIGNAL( timeout() ), SLOT( onPingTimer() ) ); - m_pingtimer->start(); - m_pingtimer_mark.start(); + d->pingtimer = new QTimer; + d->pingtimer->setInterval( 5000 ); + connect( d->pingtimer, SIGNAL( timeout() ), SLOT( onPingTimer() ) ); + d->pingtimer->start(); + d->pingtimer_mark.start(); } else { @@ -147,17 +150,18 @@ ControlConnection::setup() void ControlConnection::registerSource() { - QReadLocker sourceLocker( &m_sourceLock ); - QSharedPointer locker = m_source->acquireLock(); + Q_D( ControlConnection ); + QReadLocker sourceLocker( &d->sourceLock ); + QSharedPointer locker = d->source->acquireLock(); // Only continue if we are still the ControlConnection associated with this source. - if ( !m_source.isNull() && m_source->controlConnection() == this ) + if ( !d->source.isNull() && d->source->controlConnection() == this ) { - qDebug() << Q_FUNC_INFO << m_source->id(); + qDebug() << Q_FUNC_INFO << d->source->id(); Source* source = (Source*) sender(); Q_UNUSED( source ) - Q_ASSERT( source == m_source.data() ); + Q_ASSERT( source == d->source.data() ); - m_registered = true; + d->registered = true; setupDbSyncConnection(); } } @@ -166,47 +170,48 @@ ControlConnection::registerSource() void ControlConnection::setupDbSyncConnection( bool ondemand ) { - QReadLocker locker( &m_sourceLock ); - if ( m_source.isNull() ) + Q_D( ControlConnection ); + QReadLocker locker( &d->sourceLock ); + if ( d->source.isNull() ) { // We were unbind from the Source, nothing to do here, just waiting to be deleted. return; } - qDebug() << Q_FUNC_INFO << ondemand << m_source->id() << m_dbconnkey << m_dbsyncconn << m_registered; + qDebug() << Q_FUNC_INFO << ondemand << d->source->id() << d->dbconnkey << d->dbsyncconn << d->registered; - if ( m_dbsyncconn || !m_registered ) + if ( d->dbsyncconn || !d->registered ) return; - Q_ASSERT( m_source->id() > 0 ); + Q_ASSERT( d->source->id() > 0 ); - if ( !m_dbconnkey.isEmpty() ) + if ( !d->dbconnkey.isEmpty() ) { qDebug() << "Connecting to DBSync offer from peer..."; - m_dbsyncconn = new DBSyncConnection( servent(), m_source ); + d->dbsyncconn = new DBSyncConnection( servent(), d->source ); - servent()->createParallelConnection( this, m_dbsyncconn, m_dbconnkey ); - m_dbconnkey.clear(); + servent()->createParallelConnection( this, d->dbsyncconn, d->dbconnkey ); + d->dbconnkey.clear(); } else if ( !outbound() || ondemand ) // only one end makes the offer { qDebug() << "Offering a DBSync key to peer..."; - m_dbsyncconn = new DBSyncConnection( servent(), m_source ); + d->dbsyncconn = new DBSyncConnection( servent(), d->source ); QString key = uuid(); - servent()->registerOffer( key, m_dbsyncconn ); + servent()->registerOffer( key, d->dbsyncconn ); QVariantMap m; m.insert( "method", "dbsync-offer" ); m.insert( "key", key ); sendMsg( m ); } - if ( m_dbsyncconn ) + if ( d->dbsyncconn ) { - connect( m_dbsyncconn, SIGNAL( finished() ), - m_dbsyncconn, SLOT( deleteLater() ) ); + connect( d->dbsyncconn, SIGNAL( finished() ), + d->dbsyncconn, SLOT( deleteLater() ) ); - connect( m_dbsyncconn, SIGNAL( destroyed( QObject* ) ), + connect( d->dbsyncconn, SIGNAL( destroyed( QObject* ) ), SLOT( dbSyncConnFinished( QObject* ) ), Qt::DirectConnection ); } } @@ -215,11 +220,12 @@ ControlConnection::setupDbSyncConnection( bool ondemand ) void ControlConnection::dbSyncConnFinished( QObject* c ) { + Q_D( ControlConnection ); qDebug() << Q_FUNC_INFO << "DBSync connection closed (for now)"; - if ( (DBSyncConnection*)c == m_dbsyncconn ) + if ( (DBSyncConnection*)c == d->dbsyncconn ) { //qDebug() << "Setting m_dbsyncconn to NULL"; - m_dbsyncconn = NULL; + d->dbsyncconn = NULL; } else qDebug() << "Old DbSyncConn destroyed?!"; @@ -229,23 +235,25 @@ ControlConnection::dbSyncConnFinished( QObject* c ) DBSyncConnection* ControlConnection::dbSyncConnection() { - if ( !m_dbsyncconn ) + Q_D( ControlConnection ); + if ( !d->dbsyncconn ) { setupDbSyncConnection( true ); // Q_ASSERT( m_dbsyncconn ); } - return m_dbsyncconn; + return d->dbsyncconn; } void ControlConnection::handleMsg( msg_ptr msg ) { + Q_D( ControlConnection ); if ( msg->is( Msg::PING ) ) { // qDebug() << "Received Connection PING, nice." << m_pingtimer_mark.elapsed(); - m_pingtimer_mark.restart(); + d->pingtimer_mark.restart(); return; } @@ -276,7 +284,7 @@ ControlConnection::handleMsg( msg_ptr msg ) } else if ( m.value( "method" ).toString() == "dbsync-offer" ) { - m_dbconnkey = m.value( "key" ).toString() ; + d->dbconnkey = m.value( "key" ).toString() ; setupDbSyncConnection(); } else if ( m.value( "method" ) == "protovercheckfail" ) @@ -302,7 +310,8 @@ ControlConnection::authCheckTimeout() if ( isReady() ) return; - Servent::instance()->queueForAclResult( bareName(), m_peerInfos ); + Q_D( ControlConnection ); + Servent::instance()->queueForAclResult( bareName(), d->peerInfos ); tDebug( LOGVERBOSE ) << "Closing connection, not authed in time."; shutdown(); @@ -312,10 +321,11 @@ ControlConnection::authCheckTimeout() void ControlConnection::onPingTimer() { - if ( m_pingtimer_mark.elapsed() >= TCP_TIMEOUT * 1000 ) + Q_D( ControlConnection ); + if ( d->pingtimer_mark.elapsed() >= TCP_TIMEOUT * 1000 ) { - QReadLocker locker( &m_sourceLock ); - qDebug() << "Timeout reached! Shutting down connection to" << m_source->friendlyName(); + QReadLocker locker( &d->sourceLock ); + qDebug() << "Timeout reached! Shutting down connection to" << d->source->friendlyName(); shutdown( true ); } @@ -326,23 +336,26 @@ ControlConnection::onPingTimer() void ControlConnection::addPeerInfo( const peerinfo_ptr& peerInfo ) { + Q_D( ControlConnection ); + peerInfo->setControlConnection( this ); - m_peerInfos.insert( peerInfo ); + d->peerInfos.insert( peerInfo ); } void ControlConnection::removePeerInfo( const peerinfo_ptr& peerInfo ) { + Q_D( ControlConnection ); peerInfoDebug( peerInfo ) << "Remove peer from control connection:" << name(); Q_ASSERT( peerInfo->controlConnection() == this ); // TODO: find out why this happens // Q_ASSERT( m_peerInfos.contains( peerInfo ) ); - m_peerInfos.remove( peerInfo ); + d->peerInfos.remove( peerInfo ); - if ( m_peerInfos.isEmpty() && m_shutdownOnEmptyPeerInfos ) + if ( d->peerInfos.isEmpty() && d->shutdownOnEmptyPeerInfos ) { shutdown( true ); } @@ -351,8 +364,9 @@ ControlConnection::removePeerInfo( const peerinfo_ptr& peerInfo ) void ControlConnection::setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos ) { - m_shutdownOnEmptyPeerInfos = shutdownOnEmptyPeerInfos; - if ( m_peerInfos.isEmpty() && m_shutdownOnEmptyPeerInfos ) + Q_D( ControlConnection ); + d->shutdownOnEmptyPeerInfos = shutdownOnEmptyPeerInfos; + if ( d->peerInfos.isEmpty() && d->shutdownOnEmptyPeerInfos ) { shutdown( true ); } @@ -362,5 +376,6 @@ ControlConnection::setShutdownOnEmptyPeerInfos( bool shutdownOnEmptyPeerInfos ) const QSet< peerinfo_ptr > ControlConnection::peerInfos() const { - return m_peerInfos; + Q_D( const ControlConnection ); + return d->peerInfos; } diff --git a/src/libtomahawk/network/ControlConnection.h b/src/libtomahawk/network/ControlConnection.h index c6753eda24..1679178724 100644 --- a/src/libtomahawk/network/ControlConnection.h +++ b/src/libtomahawk/network/ControlConnection.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,17 +28,13 @@ #ifndef CONTROLCONNECTION_H #define CONTROLCONNECTION_H -#include "Typedefs.h" #include "Connection.h" - #include "DllMacro.h" +#include "Typedefs.h" -#include -#include -#include - -class Servent; +class ControlConnectionPrivate; class DBSyncConnection; +class Servent; class DLLEXPORT ControlConnection : public Connection { @@ -75,23 +72,10 @@ private slots: void onPingTimer(); private: - void setupDbSyncConnection( bool ondemand = false ); + Q_DECLARE_PRIVATE( ControlConnection ) + ControlConnectionPrivate* d_ptr; - Tomahawk::source_ptr m_source; - /** - * Lock acces to the source member. A "write" access is only if we change the value of source, not if doing a non-const call. - */ - mutable QReadWriteLock m_sourceLock; - DBSyncConnection* m_dbsyncconn; - - QString m_dbconnkey; - bool m_registered; - bool m_shutdownOnEmptyPeerInfos; - - QTimer* m_pingtimer; - QTime m_pingtimer_mark; - - QSet< Tomahawk::peerinfo_ptr > m_peerInfos; + void setupDbSyncConnection( bool ondemand = false ); }; #endif // CONTROLCONNECTION_H diff --git a/src/libtomahawk/network/ControlConnection_p.h b/src/libtomahawk/network/ControlConnection_p.h new file mode 100644 index 0000000000..d1c70f83ea --- /dev/null +++ b/src/libtomahawk/network/ControlConnection_p.h @@ -0,0 +1,63 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef CONTROLCONNECTION_P_H +#define CONTROLCONNECTION_P_H + +#include "ControlConnection.h" + +#include +#include +#include + +class ControlConnectionPrivate +{ +public: + ControlConnectionPrivate( ControlConnection* q ) + : q_ptr ( q ) + , dbsyncconn( 0 ) + , registered( false ) + , shutdownOnEmptyPeerInfos( true ) + , pingtimer( 0 ) + { + } + ControlConnection* q_ptr; + Q_DECLARE_PUBLIC ( ControlConnection ) + +private: + + Tomahawk::source_ptr source; + /** + * Lock acces to the source member. A "write" access is only if we change the value of source, not if doing a non-const call. + */ + mutable QReadWriteLock sourceLock; + DBSyncConnection* dbsyncconn; + + QString dbconnkey; + bool registered; + bool shutdownOnEmptyPeerInfos; + + QTimer* pingtimer; + QTime pingtimer_mark; + + QSet< Tomahawk::peerinfo_ptr > peerInfos; +}; + +#endif // CONTROLCONNECTION_P_H From 68812eb3a53eefd9b0282f346e31abdfceaa30c9 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 22:05:23 +0200 Subject: [PATCH 466/565] Add icon() method to ViewPage api and use that and title() for sidebar items --- src/libtomahawk/ViewPage.cpp | 5 +++++ src/libtomahawk/ViewPage.h | 1 + src/tomahawk/sourcetree/SourcesModel.cpp | 16 ++++++++++------ src/tomahawk/sourcetree/SourcesModel.h | 7 ++----- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/ViewPage.cpp b/src/libtomahawk/ViewPage.cpp index 9b17044711..5e989745ed 100644 --- a/src/libtomahawk/ViewPage.cpp +++ b/src/libtomahawk/ViewPage.cpp @@ -28,6 +28,11 @@ ViewPage::~ViewPage() tDebug( LOGVERBOSE ) << Q_FUNC_INFO; } +QIcon ViewPage::icon() const +{ + return QIcon(); +} + bool ViewPage::setFilter( const QString& filter ) diff --git a/src/libtomahawk/ViewPage.h b/src/libtomahawk/ViewPage.h index 9608770ba5..5d9530aaf6 100644 --- a/src/libtomahawk/ViewPage.h +++ b/src/libtomahawk/ViewPage.h @@ -49,6 +49,7 @@ class DLLEXPORT ViewPage virtual QWidget* widget() = 0; virtual Tomahawk::playlistinterface_ptr playlistInterface() const = 0; + virtual QIcon icon() const; virtual QString title() const = 0; virtual DescriptionType descriptionType() { return TextType; } diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 9e1d57bd21..237c66c2cd 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -352,13 +352,17 @@ SourcesModel::appendGroups() } void -SourcesModel::appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ) +SourcesModel::appendPageItem( const QString& pageName ) { QModelIndex parentIndex = indexFromItem( m_browse ); beginInsertRows( parentIndex, rowCount( parentIndex ), rowCount( parentIndex ) ); - GenericPageItem* pageItem = new GenericPageItem( this, m_browse, pageTitle, pageIcon, - boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), pageName ), - boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), pageName ) ); + GenericPageItem* pageItem = new GenericPageItem( this, + m_browse, + ViewManager::instance()->dynamicPageWidget( pageName )->title(), + ViewManager::instance()->dynamicPageWidget( pageName )->icon(), + boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), pageName ), + boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), pageName ) ); + pageItem->setSortValue( rowCount( parentIndex ) ); endInsertRows(); @@ -677,9 +681,9 @@ SourcesModel::onWidgetDestroyed( QWidget* w ) void -SourcesModel::onViewPageAdded( const QString& name ) +SourcesModel::onViewPageAdded( const QString& pageName ) { - appendPageItem( ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), name, name); + appendPageItem( pageName ); } diff --git a/src/tomahawk/sourcetree/SourcesModel.h b/src/tomahawk/sourcetree/SourcesModel.h index 48172e57d7..4ba534f286 100644 --- a/src/tomahawk/sourcetree/SourcesModel.h +++ b/src/tomahawk/sourcetree/SourcesModel.h @@ -103,10 +103,7 @@ class SourcesModel : public QAbstractItemModel void appendGroups(); - /* - * pageIcon and pageTitle are visible in the source tree, pageName is the internal name in the ViewManager - */ - void appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ); + void appendPageItem( const QString& pageName ); void appendItem( const Tomahawk::source_ptr& source ); bool removeItem( const Tomahawk::source_ptr& source ); @@ -151,7 +148,7 @@ private slots: void onWidgetDestroyed( QWidget* w ); - void onViewPageAdded( const QString& name ); + void onViewPageAdded( const QString& pageName ); private: SourceTreeItem* itemFromIndex( const QModelIndex& idx ) const; From 3debc109437c10b31fa5e90f6c4abcffb00cb957 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 22:27:55 +0200 Subject: [PATCH 467/565] Clean up --- src/libtomahawk/ViewManager.cpp | 1 - src/libtomahawk/ViewManager.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index e9db1ee75a..5dbadbd667 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -61,7 +61,6 @@ #include "utils/Logger.h" #include -#include #include diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index 84a188350b..c00994b7e9 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -58,7 +58,6 @@ class WhatsHotWidget; class QPushButton; class InboxModel; class NetworkActivityWidget; -class StubWidget; namespace Tomahawk { From be67d85f91e918a4bb629e3f2946d44306681331 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 23:22:33 +0200 Subject: [PATCH 468/565] Revert "Add icon() method to ViewPage api and use that and title() for sidebar items" We can't lazy load the page when we need its members to show the sidebar entry... This reverts commit 1821b24391c33b2ea14fc0d094142395588075d9. --- src/libtomahawk/ViewPage.cpp | 5 ----- src/libtomahawk/ViewPage.h | 1 - src/tomahawk/sourcetree/SourcesModel.cpp | 16 ++++++---------- src/tomahawk/sourcetree/SourcesModel.h | 7 +++++-- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/libtomahawk/ViewPage.cpp b/src/libtomahawk/ViewPage.cpp index 5e989745ed..9b17044711 100644 --- a/src/libtomahawk/ViewPage.cpp +++ b/src/libtomahawk/ViewPage.cpp @@ -28,11 +28,6 @@ ViewPage::~ViewPage() tDebug( LOGVERBOSE ) << Q_FUNC_INFO; } -QIcon ViewPage::icon() const -{ - return QIcon(); -} - bool ViewPage::setFilter( const QString& filter ) diff --git a/src/libtomahawk/ViewPage.h b/src/libtomahawk/ViewPage.h index 5d9530aaf6..9608770ba5 100644 --- a/src/libtomahawk/ViewPage.h +++ b/src/libtomahawk/ViewPage.h @@ -49,7 +49,6 @@ class DLLEXPORT ViewPage virtual QWidget* widget() = 0; virtual Tomahawk::playlistinterface_ptr playlistInterface() const = 0; - virtual QIcon icon() const; virtual QString title() const = 0; virtual DescriptionType descriptionType() { return TextType; } diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 237c66c2cd..9e1d57bd21 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -352,17 +352,13 @@ SourcesModel::appendGroups() } void -SourcesModel::appendPageItem( const QString& pageName ) +SourcesModel::appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ) { QModelIndex parentIndex = indexFromItem( m_browse ); beginInsertRows( parentIndex, rowCount( parentIndex ), rowCount( parentIndex ) ); - GenericPageItem* pageItem = new GenericPageItem( this, - m_browse, - ViewManager::instance()->dynamicPageWidget( pageName )->title(), - ViewManager::instance()->dynamicPageWidget( pageName )->icon(), - boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), pageName ), - boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), pageName ) ); - + GenericPageItem* pageItem = new GenericPageItem( this, m_browse, pageTitle, pageIcon, + boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), pageName ), + boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), pageName ) ); pageItem->setSortValue( rowCount( parentIndex ) ); endInsertRows(); @@ -681,9 +677,9 @@ SourcesModel::onWidgetDestroyed( QWidget* w ) void -SourcesModel::onViewPageAdded( const QString& pageName ) +SourcesModel::onViewPageAdded( const QString& name ) { - appendPageItem( pageName ); + appendPageItem( ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), name, name); } diff --git a/src/tomahawk/sourcetree/SourcesModel.h b/src/tomahawk/sourcetree/SourcesModel.h index 4ba534f286..48172e57d7 100644 --- a/src/tomahawk/sourcetree/SourcesModel.h +++ b/src/tomahawk/sourcetree/SourcesModel.h @@ -103,7 +103,10 @@ class SourcesModel : public QAbstractItemModel void appendGroups(); - void appendPageItem( const QString& pageName ); + /* + * pageIcon and pageTitle are visible in the source tree, pageName is the internal name in the ViewManager + */ + void appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ); void appendItem( const Tomahawk::source_ptr& source ); bool removeItem( const Tomahawk::source_ptr& source ); @@ -148,7 +151,7 @@ private slots: void onWidgetDestroyed( QWidget* w ); - void onViewPageAdded( const QString& pageName ); + void onViewPageAdded( const QString& name ); private: SourceTreeItem* itemFromIndex( const QModelIndex& idx ) const; From a5f1e63ae393f232d49badfa2653525e0b8a6246 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 16 Jun 2013 23:59:17 +0200 Subject: [PATCH 469/565] Allow to set icon and text for externally added generic page items without instantiating them (aka fix lazy loading for vsxu widget) --- src/libtomahawk/ViewManager.cpp | 20 ++++++++++++++++---- src/libtomahawk/ViewManager.h | 16 ++++++++++------ src/tomahawk/sourcetree/SourcesModel.cpp | 17 +++++------------ src/tomahawk/sourcetree/SourcesModel.h | 10 ++++------ 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 5dbadbd667..3c1a210590 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -942,18 +942,18 @@ ViewManager::dynamicPageWidget( const QString& pageName ) const void -ViewManager::addDynamicPage(const QString& pageName, ViewPage* page ) +ViewManager::addDynamicPage( const QString& pageName, const QString& text, const QIcon& icon, boost::function instanceLoader ) { tLog() << Q_FUNC_INFO << "Trying to add " << pageName; - if( dynamicPageWidget( pageName ) ) + if( m_dynamicPages.contains( pageName ) ) { tLog() << "Not adding a second ViewPage with name " << pageName; Q_ASSERT( false ); } - m_dynamicPages.insert( pageName, page ); - emit viewPageAdded( pageName ); + m_dynamicPagesInstanceLoaders.insert( pageName, instanceLoader ); + emit viewPageAdded( pageName, text, icon ); } @@ -962,6 +962,18 @@ ViewManager::showDynamicPage( const QString& pageName ) { tLog() << Q_FUNC_INFO << "pageName: " << pageName; + if( !m_dynamicPages.contains( pageName ) ) + { + if( !m_dynamicPagesInstanceLoaders.contains( pageName ) ) + { + tLog() << "Trying to show a page that does not exist and does not have a registered loader"; + Q_ASSERT(false); + return 0; + } + m_dynamicPages.insert( pageName, m_dynamicPagesInstanceLoaders.value( pageName )() ); + m_dynamicPagesInstanceLoaders.remove( pageName ); + } + return show( dynamicPageWidget( pageName ) ); } diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index c00994b7e9..f01971efb5 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -20,16 +20,19 @@ #ifndef VIEWMANAGER_H #define VIEWMANAGER_H -#include -#include -#include - #include "Artist.h" #include "collection/Collection.h" #include "PlaylistInterface.h" #include "playlist/QueueView.h" #include "ViewPage.h" +#include +#include +#include + +// best regards to you, mr. pimple aka xhochy :) +#include + #include "DllMacro.h" class AnimatedSplitter; @@ -137,7 +140,7 @@ Q_OBJECT void historyBackAvailable( bool avail ); void historyForwardAvailable( bool avail ); - void viewPageAdded( const QString& pageName ); + void viewPageAdded( const QString& pageName, const QString& text, const QIcon& icon ); public slots: Tomahawk::ViewPage* showRadioPage(); @@ -149,7 +152,7 @@ public slots: Tomahawk::ViewPage* showInboxPage(); Tomahawk::ViewPage* showNetworkActivityPage(); - void addDynamicPage( const QString& pageName, Tomahawk::ViewPage* page ); + void addDynamicPage( const QString& pageName, const QString& text, const QIcon& icon, boost::function< Tomahawk::ViewPage*() > instanceLoader ); Tomahawk::ViewPage* showDynamicPage( const QString& pageName ); void showCurrentTrack(); @@ -209,6 +212,7 @@ private slots: NetworkActivityWidget* m_networkActivityWidget; QHash< QString, Tomahawk::ViewPage* > m_dynamicPages; + QHash< QString, boost::function< Tomahawk::ViewPage*() > > m_dynamicPagesInstanceLoaders; QList< Tomahawk::collection_ptr > m_superCollections; diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 9e1d57bd21..91aa121284 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -79,7 +79,7 @@ SourcesModel::SourcesModel( QObject* parent ) this, SLOT( onScriptCollectionRemoved( Tomahawk::collection_ptr ) ) ); - connect( ViewManager::instance(), SIGNAL( viewPageAdded( QString ) ), SLOT( onViewPageAdded( QString ) ) ); + connect( ViewManager::instance(), SIGNAL( viewPageAdded( QString, QString, QIcon ) ), SLOT( appendPageItem( QString, QString, QIcon ) ) ); } @@ -352,13 +352,13 @@ SourcesModel::appendGroups() } void -SourcesModel::appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ) +SourcesModel::appendPageItem( const QString& name, const QString& text, const QIcon& icon ) { QModelIndex parentIndex = indexFromItem( m_browse ); beginInsertRows( parentIndex, rowCount( parentIndex ), rowCount( parentIndex ) ); - GenericPageItem* pageItem = new GenericPageItem( this, m_browse, pageTitle, pageIcon, - boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), pageName ), - boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), pageName ) ); + GenericPageItem* pageItem = new GenericPageItem( this, m_browse, text, icon, + boost::bind( &ViewManager::showDynamicPage, ViewManager::instance(), name ), + boost::bind( &ViewManager::dynamicPageWidget, ViewManager::instance(), name ) ); pageItem->setSortValue( rowCount( parentIndex ) ); endInsertRows(); @@ -676,13 +676,6 @@ SourcesModel::onWidgetDestroyed( QWidget* w ) } -void -SourcesModel::onViewPageAdded( const QString& name ) -{ - appendPageItem( ImageRegistry::instance()->icon( RESPATH "images/new-releases.svg" ), name, name); -} - - void SourcesModel::removeSourceItemLink( SourceTreeItem* item ) { diff --git a/src/tomahawk/sourcetree/SourcesModel.h b/src/tomahawk/sourcetree/SourcesModel.h index 48172e57d7..ab01502898 100644 --- a/src/tomahawk/sourcetree/SourcesModel.h +++ b/src/tomahawk/sourcetree/SourcesModel.h @@ -103,11 +103,6 @@ class SourcesModel : public QAbstractItemModel void appendGroups(); - /* - * pageIcon and pageTitle are visible in the source tree, pageName is the internal name in the ViewManager - */ - void appendPageItem( const QIcon& pageIcon, const QString& pageTitle, const QString& pageName ); - void appendItem( const Tomahawk::source_ptr& source ); bool removeItem( const Tomahawk::source_ptr& source ); @@ -151,7 +146,10 @@ private slots: void onWidgetDestroyed( QWidget* w ); - void onViewPageAdded( const QString& name ); + /* + * pageIcon and pageTitle are visible in the source tree, pageName is the internal name in the ViewManager + */ + void appendPageItem( const QString& name, const QString& text, const QIcon& icon ); private: SourceTreeItem* itemFromIndex( const QModelIndex& idx ) const; From aad713d638e20c500b3bb047ee6d7034599a730c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Mon, 17 Jun 2013 00:51:49 +0200 Subject: [PATCH 470/565] * Fixed compiling Hatchet. --- src/accounts/hatchet/sip/HatchetSip.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/accounts/hatchet/sip/HatchetSip.cpp b/src/accounts/hatchet/sip/HatchetSip.cpp index 4b72509726..c0b4aed6fe 100644 --- a/src/accounts/hatchet/sip/HatchetSip.cpp +++ b/src/accounts/hatchet/sip/HatchetSip.cpp @@ -32,7 +32,11 @@ #include #include +#include +#include + #include +#include #include HatchetSipPlugin::HatchetSipPlugin( Tomahawk::Accounts::Account *account ) From ab882608d80800acc8b4aad1d39562310a7c29f0 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Mon, 17 Jun 2013 02:16:42 +0200 Subject: [PATCH 471/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 236 +++++++++++++++++------------------ lang/tomahawk_bg.ts | 236 +++++++++++++++++------------------ lang/tomahawk_bn_IN.ts | 236 +++++++++++++++++------------------ lang/tomahawk_ca.ts | 236 +++++++++++++++++------------------ lang/tomahawk_ca@valencia.ts | 236 +++++++++++++++++------------------ lang/tomahawk_cs.ts | 236 +++++++++++++++++------------------ lang/tomahawk_da.ts | 236 +++++++++++++++++------------------ lang/tomahawk_de.ts | 236 +++++++++++++++++------------------ lang/tomahawk_el.ts | 236 +++++++++++++++++------------------ lang/tomahawk_en.ts | 236 +++++++++++++++++------------------ lang/tomahawk_es.ts | 236 +++++++++++++++++------------------ lang/tomahawk_fi.ts | 236 +++++++++++++++++------------------ lang/tomahawk_fr.ts | 236 +++++++++++++++++------------------ lang/tomahawk_gl.ts | 236 +++++++++++++++++------------------ lang/tomahawk_hi_IN.ts | 236 +++++++++++++++++------------------ lang/tomahawk_hu.ts | 236 +++++++++++++++++------------------ lang/tomahawk_id.ts | 236 +++++++++++++++++------------------ lang/tomahawk_it.ts | 236 +++++++++++++++++------------------ lang/tomahawk_ja.ts | 236 +++++++++++++++++------------------ lang/tomahawk_lt.ts | 236 +++++++++++++++++------------------ lang/tomahawk_pl.ts | 236 +++++++++++++++++------------------ lang/tomahawk_pt_BR.ts | 236 +++++++++++++++++------------------ lang/tomahawk_ru.ts | 236 +++++++++++++++++------------------ lang/tomahawk_sv.ts | 236 +++++++++++++++++------------------ lang/tomahawk_tr.ts | 236 +++++++++++++++++------------------ lang/tomahawk_zh_CN.ts | 236 +++++++++++++++++------------------ lang/tomahawk_zh_TW.ts | 236 +++++++++++++++++------------------ 27 files changed, 3186 insertions(+), 3186 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 2aee8974b4..a1f711355b 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -91,174 +91,174 @@ connect and stream from you? ActionCollection - + &Listen Along &استمع مع - + Stop &Listening Along توقيف &الاستماع مع - + &Follow in real-time &تابع بنفس الوقت - - + + &Listen Privately &إستمع بخصوصية - - + + &Listen Publicly &إستمع علنا - + &Load Playlist &تحميل قائمة الأغاني - + &Load Station &تحميل إذاعة - + &Rename Playlist &إعادة تسمية قائمة الأغاني - + &Rename Station &إعادة تسمية إذاعة - + &Copy Playlist Link &نسخ رابط قائمة الأغاني - + &Play &إستمع - + &Stop &أوقف الإستماع - + &Previous Track &الأغنية السابقة - + &Next Track &الأغنية التالية - + &Quit &أخرج - + Load &XSPF... تحميل XSPF&... - + U&pdate Collection &تحديث المجموعة - + Fully &Rescan Collection إعادة &مسح المجموعة كاملة - + Show Offline Sources أظهر المصادر الغير متصلة - + &Configure Tomahawk... &تكوين توماهوك... - + Minimize خفض - + Zoom زوم - + Enter Full Screen الدخول إلى وضع ملء الشاشة - + Hide Menu Bar إخفي شريط القائمة - + Diagnostics... تشخيص... - + About &Tomahawk... عن &توماهوك... - + &Legal Information... معلومات &قانونية... - + &View Logfile &عرض ملف السجل - + Check For Updates... تحقق من التحديثات... - + &Controls &ضوابط - + &Settings إ&عدادات - + &Help &مساعدة - + &Window &نافذة - + Main Menu القائمة الرئيسية @@ -895,12 +895,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. ارسلت %1 للفنان %2 إلى %3. - + %1 sent you %2 by %3. %1 أرسل لك %2 للفنان %3. @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. تحذير المحلل النصي: إستدعاء API %1 إرجاع البيانات بشكل متزامن. @@ -1257,108 +1257,108 @@ Password PlayableModel - + Artist فنان - + Title عنوان - + Composer مؤلف - + Album البوم - + Track اغنية - + Duration مدة - + Bitrate معدل البت - + Age عمر - + Year سنة - + Size حجم - + Origin أصل - + Accuracy الدقة - + Perfect match تطابق تام - + Very good match تطابق جيد جدا - + Good match تطابق جيد - + Vague match تطابق مبهم - + Bad match تطابق سيء - + Very bad match تطابق سيء للغاية - + Not available غير متوفر - + Searching... قيد البحث... - - + + Name إسم @@ -1379,31 +1379,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you سمعت %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 سمع %1 - + added %1 e.g. added 3 hours ago أضيفت %1 - + by <b>%1</b> e.g. by SomeArtist للفنان <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum للفنان <b>%1</b> في البوم <b>%2</b> @@ -1548,67 +1548,67 @@ Password QObject - + %n year(s) ago منذ %n سنةمنذ سنة %nمنذ سنتين %nمنذ %n سنواتمنذ %n سنواتمنذ %n سنوات - + %n year(s) منذ %n سنةمنذ سنة %nمنذ سنتين %nمنذ %n سنواتمنذ %n سنواتمنذ %n سنوات - + %n month(s) ago منذ %n شهرمنذ شهر %nمنذ شهرين %nمنذ %n أشهرمنذ %n أشهرمنذ %n أشهر - + %n month(s) منذ %n شهرمنذ شهر %nمنذ شهرين %nمنذ %n أشهرمنذ %n أشهرمنذ %n أشهر - + %n week(s) ago منذ %n أسبوعمنذ أسبوع %nمنذ أسبوعين %nمنذ %n أسابيعمنذ %n أسابيعمنذ %n أسابيع - + %n week(s) منذ %n أسبوعمنذ أسبوع %nمنذ أسبوعين %nمنذ %n أسابيعمنذ %n أسابيعمنذ %n أسابيع - + %n day(s) ago منذ %n يوممنذ يوم %nمنذ يومين %nمنذ %n أياممنذ %n أياممنذ %n أيام - + %n day(s) منذ %n يوممنذ يوم %nمنذ يومين %nمنذ %n أياممنذ %n أياممنذ %n أيام - + %n hour(s) ago منذ %n ساعةمنذ ساعة %nمنذ ساعتين %nمنذ %n ساعاتمنذ %n ساعاتمنذ %n ساعات - + %n hour(s) منذ %n ساعةمنذ ساعة %nمنذ ساعتين %nمنذ %n ساعاتمنذ %n ساعاتمنذ %n ساعات - + %1 minutes ago منذ %1 دقائق - + %1 minutes %1 دقائق - + just now الآن @@ -1712,22 +1712,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 خطأ محلل النصي: %1 %2 %3 %4 - + SSL Error خطأ SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? لقد طلبت من توماهوك الإتصال بشكل أمن إلى <b>%1</b> ولكن لا يمكننا التأكد من أن اتصالك آمن: <br><br><b>%2</b></br></br> هل تريد أن تثق بهذا الإتصال؟ - + Trust certificate شهادة الثقة @@ -2221,82 +2221,82 @@ Password SourcesModel - + Group فئة - + Collection مجموعة - + Playlist قائمة الأغاني - + Automatic Playlist قائمة أغاني أوتوماتيكية - + Station إذاعة - + Browse تصفح - + Search History تاريخ البحث - + My Music موسيقتي الخاصة - + SuperCollection سوبر كولكشن - + Network Activity نشاط الشبكة - + Cloud سحابة - + Dashboard لوحة القيادة - + Recently Played تم الاستماع لها مؤخرا - + Charts الرسوم البيانية - + New Releases جديد الاصدارات - + Friends الأصدقاء @@ -3607,7 +3607,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! مشكلة في جلب معلومات "iTunes" من الشبكة! @@ -3643,13 +3643,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up &إلحق - - + + &Listen Along &استمع مع @@ -3704,43 +3704,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل @@ -3807,7 +3807,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة @@ -4105,7 +4105,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. عذراً، ترشيحك "%1" لم يطابق أي نتائج. @@ -4113,13 +4113,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend من - + to streaming artist - track to friend إلى diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 1005a5d514..448196ba8e 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Слушай заедно с - + Stop &Listening Along Спри &съвместното слушане - + &Follow in real-time &Следвай в реално време - - + + &Listen Privately &Слушай самостоятелно - - + + &Listen Publicly &Слушай публично - + &Load Playlist &Зареди списък - + &Load Station &Зареди станция - + &Rename Playlist &Преименувай списък - + &Rename Station &Преименувай станция - + &Copy Playlist Link &Копирай връзка към списък - + &Play &Изпълни - + &Stop &Спри - + &Previous Track &Предходна песен - + &Next Track &Следваща песен - + &Quit &Изключи приложението - + Load &XSPF... Зареди &XSPF... - + U&pdate Collection О&бнови колекцията - + Fully &Rescan Collection Пълно &сканиране на колекцията - + Show Offline Sources Покажи източници, които не са на линия - + &Configure Tomahawk... &Настройки... - + Minimize Минимизирай - + Zoom Увеличи - + Enter Full Screen Превключи в режим на цял екран - + Hide Menu Bar Скрий лентата на менюто - + Diagnostics... Диагностика... - + About &Tomahawk... За &Tomahawk... - + &Legal Information... &Правна информация - + &View Logfile &Виж лог-файла - + Check For Updates... Провери за обновления - + &Controls &Контрол - + &Settings &Настройки - + &Help &Помощ - + &Window &Прозорец - + Main Menu Главно меню @@ -901,12 +901,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. %1 ти изпрати %2 от %3. - + %1 sent you %2 by %3. %1 ти изпрати %2 от %3. @@ -930,9 +930,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Предупреждение на скриптът за извличане: Заявката към %1 върна данни синхронно. @@ -1264,108 +1264,108 @@ Password PlayableModel - + Artist Изпълнител - + Title Име - + Composer Композитор - + Album Албум - + Track Песен - + Duration Време - + Bitrate Кб/сек - + Age Възраст - + Year Година - + Size Големина - + Origin Източник - + Accuracy Съвпадение - + Perfect match Абсолютно - + Very good match Много добро - + Good match Добро - + Vague match Горе-долу - + Bad match Лошо - + Very bad match Много лошо - + Not available Няма съвпадение - + Searching... Търсене... - - + + Name Име @@ -1386,31 +1386,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you Изпълнена %1 от теб - + played %1 by %2 e.g. played 3 hours ago by SomeSource Изпълнена %1 от %2 - + added %1 e.g. added 3 hours ago Добавена %1 - + by <b>%1</b> e.g. by SomeArtist от <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum от <b>%1</b> на <b>%2</b> @@ -1557,67 +1557,67 @@ Password QObject - + %n year(s) ago преди %n годинапреди %n години - + %n year(s) %n година%n години - + %n month(s) ago преди %n месецпреди %n месеца - + %n month(s) %n месец%n месеца - + %n week(s) ago преди %n седмицапреди %n седмици - + %n week(s) %n седмица%n седмици - + %n day(s) ago преди %n денпреди %n дена - + %n day(s) %n ден%n дена - + %n hour(s) ago преди %n часпреди %n часа - + %n hour(s) %n час %n часа - + %1 minutes ago преди %1 минути - + %1 minutes %1 минути - + just now току-що @@ -1721,22 +1721,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Грешка на скриптът за извличане на данни: %1 %2 %3 %4 - + SSL Error SSL грешка - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Ти пожела Tomahawk да се свърже сигурно към <b>%1</b>, но ние не можем да потвърдим, че връзката е сигурна:<br><br><b>%2</b><br><br>Искаш ли да се довериш на тази връзка? - + Trust certificate Удостовери този сертификат @@ -2234,82 +2234,82 @@ Password SourcesModel - + Group Групирай - + Collection Колекция - + Playlist Списък за изпълнение - + Automatic Playlist Автоматичен списък - + Station Станция - + Browse Разгледай - + Search History Търси в историята - + My Music Моята музика - + SuperCollection Обща колекция - + Network Activity Мрежова активност - + Cloud Облак - + Dashboard Табло - + Recently Played Наскоро изпълнени - + Charts Класации - + New Releases Нови албуми - + Friends Приятели @@ -3622,7 +3622,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Грешка при извличане на информация от iTunes @@ -3659,13 +3659,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up &Последвай - - + + &Listen Along &Слушай заедно @@ -3720,43 +3720,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 изпълнения) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия @@ -3823,7 +3823,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -4123,7 +4123,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Съжалявам, твоят филтър %1 не върна никакъв резултат. @@ -4131,13 +4131,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend от - + to streaming artist - track to friend до diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index a9fba07974..f33bdfd959 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along - + Stop &Listening Along - + &Follow in real-time - - + + &Listen Privately - - + + &Listen Publicly - + &Load Playlist - + &Load Station - + &Rename Playlist - + &Rename Station - + &Copy Playlist Link - + &Play - + &Stop - + &Previous Track - + &Next Track - + &Quit - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist - + Title - + Composer - + Album - + Track - + Duration - + Bitrate - + Age - + Year - + Size - + Origin - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1544,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2217,82 +2217,82 @@ Password SourcesModel - + Group - + Collection - + Playlist - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Network Activity - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! @@ -3629,13 +3629,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up - - + + &Listen Along @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -4090,7 +4090,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4098,13 +4098,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 3704a3fc2f..c8691d00c6 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Escolta a la vegada - + Stop &Listening Along Deixa d'&escoltar a la vegada - + &Follow in real-time &Segueix en temps real - - + + &Listen Privately &Escolta privada - - + + &Listen Publicly &Escolta pública - + &Load Playlist &Carrega la llista de reproducció - + &Load Station - + &Rename Playlist Canvia el nom la llista de &reproducció - + &Rename Station - + &Copy Playlist Link &Copia l'enllaç de la llista de reproducció - + &Play &Reprodueix - + &Stop &Atura - + &Previous Track Cançó &anterior - + &Next Track Cançó &següent - + &Quit &Surt - + Load &XSPF... Carrega un &XSPF... - + U&pdate Collection A&ctualitza la col·lecció - + Fully &Rescan Collection Torna a escaneja&r la col·lecció completament - + Show Offline Sources Mostra els orígens en fora de línia - + &Configure Tomahawk... &Configura el Tomahawk... - + Minimize Minimitza - + Zoom Zoom - + Enter Full Screen Entra en mode de pantalla completa - + Hide Menu Bar Amaga la barra de menú - + Diagnostics... Diagnòstics... - + About &Tomahawk... Quant al &Tomahawk... - + &Legal Information... Informació &legal... - + &View Logfile &Mostra el fitxer de registre - + Check For Updates... Comprova si hi ha actualitzacions... - + &Controls &Controls - + &Settings Paràmetre%&s - + &Help &Ajuda - + &Window &Finestra - + Main Menu Menú principal @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist Artista - + Title Cançó - + Composer Compositor - + Album Àlbum - + Track Pista - + Duration Durada - + Bitrate Bitrate - + Age Edat - + Year Any - + Size Mida - + Origin Origen - + Accuracy Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... - - + + Name Nom @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduït %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduït %1 per %2 - + added %1 e.g. added 3 hours ago afegeix %1 - + by <b>%1</b> e.g. by SomeArtist per <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum per <b>%1</b> el <b>%2</b> @@ -1545,67 +1545,67 @@ Password QObject - + %n year(s) ago fa %n anyfa %n anys - + %n year(s) %n any%n anys - + %n month(s) ago fa %n mesfa %n mesos - + %n month(s) %n mes%n mesos - + %n week(s) ago fa %n setmanafa %n setmanes - + %n week(s) %n setmana%n setmanes - + %n day(s) ago fa %n diafa %n dies - + %n day(s) %n dia%n dies - + %n hour(s) ago fa %n horafa %n hores - + %n hour(s) %n hora%n hores - + %1 minutes ago fa %1 minut - + %1 minutes %1 minuts - + just now ara mateix @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2221,82 +2221,82 @@ i emissores basades en els vostres gusts musicals. SourcesModel - + Group Grup - + Collection Col·lecció - + Playlist Llista de Reproducció - + Automatic Playlist Llista de Reproducció Automàtica - + Station Emissora - + Browse Cerca - + Search History Historial de Cerca - + My Music La Meva Música - + SuperCollection SuperCol·lecció - + Network Activity - + Cloud - + Dashboard Presentació - + Recently Played Escoltades Recentment - + Charts Llistes - + New Releases Nous Llançaments - + Friends Amics @@ -3607,7 +3607,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Error en cercar la informació d'iTunes a través de la xarxa! @@ -3643,13 +3643,13 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::LatchManager - + &Catch Up &Atrapa - - + + &Listen Along &Escolta a la vegada @@ -3704,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia @@ -3807,7 +3807,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció @@ -4105,7 +4105,7 @@ introduïu el PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. @@ -4113,13 +4113,13 @@ introduïu el PIN aquí: TransferStatusItem - + from streaming artist - track from friend de - + to streaming artist - track to friend per diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 53bfb44213..960cbe81f1 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Escolta a la vegada - + Stop &Listening Along Deixa d'&escoltar a la vegada - + &Follow in real-time &Segueix en temps real - - + + &Listen Privately &Escolta privada - - + + &Listen Publicly &Escolta pública - + &Load Playlist &Carrega la llista de reproducció - + &Load Station - + &Rename Playlist Canvia el nom la llista de &reproducció - + &Rename Station - + &Copy Playlist Link &Copia l'enllaç de la llista de reproducció - + &Play &Reprodueix - + &Stop &Atura - + &Previous Track Cançó &anterior - + &Next Track Cançó &següent - + &Quit &Ix - + Load &XSPF... Carrega un &XSPF... - + U&pdate Collection A&ctualitza la col·lecció - + Fully &Rescan Collection Torna a escaneja&r la col·lecció completament - + Show Offline Sources Mostra els orígens en fora de línia - + &Configure Tomahawk... &Configura el Tomahawk... - + Minimize Minimitza - + Zoom Zoom - + Enter Full Screen Entra en mode de pantalla completa - + Hide Menu Bar Amaga la barra de menú - + Diagnostics... Diagnòstics... - + About &Tomahawk... Quant al &Tomahawk... - + &Legal Information... Informació &legal... - + &View Logfile &Mostra el fitxer de registre - + Check For Updates... Comprova si hi ha actualitzacions... - + &Controls &Controls - + &Settings Paràmetre%&s - + &Help &Ajuda - + &Window &Finestra - + Main Menu Menú principal @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist Artista - + Title Cançó - + Composer Compositor - + Album Àlbum - + Track Pista - + Duration Durada - + Bitrate Bitrate - + Age Edat - + Year Any - + Size Mida - + Origin Origen - + Accuracy Precisió - + Perfect match Coincidència perfecta - + Very good match Molt bona coincidència - + Good match Bona coincidència - + Vague match Més o menys coincident - + Bad match Mala coincidència - + Very bad match Coincidència molt dolenta - + Not available No disponible - + Searching... - - + + Name Nom @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduït %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduït %1 per %2 - + added %1 e.g. added 3 hours ago afig %1 - + by <b>%1</b> e.g. by SomeArtist per <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum per <b>%1</b> el <b>%2</b> @@ -1545,67 +1545,67 @@ Password QObject - + %n year(s) ago fa %n anyfa %n anys - + %n year(s) %n any%n anys - + %n month(s) ago fa %n mesfa %n mesos - + %n month(s) %n mes%n mesos - + %n week(s) ago fa %n setmanafa %n setmanes - + %n week(s) %n setmana%n setmanes - + %n day(s) ago fa %n diafa %n dies - + %n day(s) %n dia%n dies - + %n hour(s) ago fa %n horafa %n hores - + %n hour(s) %n hora%n hores - + %1 minutes ago fa %1 minut - + %1 minutes %1 minuts - + just now ara mateix @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2221,82 +2221,82 @@ i emissores basades en els vostres gusts musicals. SourcesModel - + Group Grup - + Collection Col·lecció - + Playlist Llista de Reproducció - + Automatic Playlist Llista de Reproducció Automàtica - + Station Emissora - + Browse Cerca - + Search History Historial de Cerca - + My Music La Meua Música - + SuperCollection SuperCol·lecció - + Network Activity - + Cloud - + Dashboard Presentació - + Recently Played Escoltades Recentment - + Charts Llistes - + New Releases Nous Llançaments - + Friends Amics @@ -3607,7 +3607,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Error en cercar la informació d'iTunes a través de la xarxa! @@ -3643,13 +3643,13 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::LatchManager - + &Catch Up &Atrapa - - + + &Listen Along &Escolta a la vegada @@ -3704,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia @@ -3807,7 +3807,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció @@ -4105,7 +4105,7 @@ introduïu el PIN ací: TrackView - + Sorry, your filter '%1' did not match any results. El filtre '%1' no ha obtingut cap resultat. @@ -4113,13 +4113,13 @@ introduïu el PIN ací: TransferStatusItem - + from streaming artist - track from friend de - + to streaming artist - track to friend per diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index a698668723..d802f9fb79 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -91,174 +91,174 @@ se s vámi spojil? ActionCollection - + &Listen Along &Poslouchat - + Stop &Listening Along Zastavit &poslech - + &Follow in real-time &Sledovat ve skutečném čase - - + + &Listen Privately Zapnout režim &soukromého poslechu - - + + &Listen Publicly Opustit režim sou&kromého poslechu - + &Load Playlist &Nahrát seznam skladeb - + &Load Station &Nahrát stanici - + &Rename Playlist &Přejmenovat seznam skladeb - + &Rename Station &Přejmenovat stanici - + &Copy Playlist Link &Kopírovat odkaz k tomuto seznamu skladeb - + &Play Pře&hrát - + &Stop &Zastavit - + &Previous Track Před&chozí skladba - + &Next Track &Další skladba - + &Quit U&končit - + Load &XSPF... Nahrát &XSPF... - + U&pdate Collection &Aktualizovat sbírku - + Fully &Rescan Collection Sbírku pro&hledat znovu - + Show Offline Sources Ukázat zdroje nepřipojené k internetu - + &Configure Tomahawk... &Nastavit Tomahawk... - + Minimize Zmenšit - + Zoom Zvětšení - + Enter Full Screen Vstoupit do režimu na celou obrazovku - + Hide Menu Bar Skrýt pruh s hlavní nabídkou - + Diagnostics... Diagnostika... - + About &Tomahawk... &O programu Tomahawk... - + &Legal Information... &Právní informace... - + &View Logfile &Zobrazit soubor se zápisem - + Check For Updates... Prověřit, zda již je novější vydání... - + &Controls &Ovládání - + &Settings Na&stavení - + &Help Nápo&věda - + &Window &Okno - + Main Menu Hlavní nabídka @@ -895,12 +895,12 @@ heslo InboxJobItem - + Sent %1 by %2 to %3. Posláno %1 od %2 %3. - + %1 sent you %2 by %3. %1 vám poslán %2 od %3. @@ -924,9 +924,9 @@ heslo JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Varování řešitele skriptu: Volání API %1 vrátilo data synchronně. @@ -1257,108 +1257,108 @@ heslo PlayableModel - + Artist Umělec - + Title Název - + Composer Skladatel - + Album Album - + Track Skladba - + Duration Doba trvání - + Bitrate Datový tok - + Age Stáří - + Year Rok - + Size Velikost - + Origin Původ - + Accuracy Přesnost - + Perfect match Přesná shoda - + Very good match Velmi dobrá shoda - + Good match Dobrá shoda - + Vague match Mlhavá shoda - + Bad match Špatná shoda - + Very bad match Velmi špatná shoda - + Not available Nedostupné - + Searching... Hledá se... - - + + Name Název @@ -1379,31 +1379,31 @@ heslo PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you vyslechnuto %1 vámi - + played %1 by %2 e.g. played 3 hours ago by SomeSource vyslechnuto %1 %2 - + added %1 e.g. added 3 hours ago přidáno %1 - + by <b>%1</b> e.g. by SomeArtist od <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum od <b>%1</b> na <b>%2</b> @@ -1547,67 +1547,67 @@ heslo QObject - + %n year(s) ago před %n rokempřed %n rokypřed %n roky - + %n year(s) %n rok%n roky%n roků - + %n month(s) ago před %n měsícempřed %n měsícipřed %n měsíci - + %n month(s) %n měsíc%n měsíce%n měsíců - + %n week(s) ago před %n týdnempřed %n týdnypřed %n týdny - + %n week(s) %n týden%n týdny%n týdnů - + %n day(s) ago před %n dnempřed %n dnypřed %n dny - + %n day(s) %n den%n dny%n dnů - + %n hour(s) ago před %n hodinoupřed %n hodinamipřed %n hodinami - + %n hour(s) %n hodina%n hodiny%n hodin - + %1 minutes ago před %1 minutami - + %1 minutes %1 minuty - + just now právě teď @@ -1711,22 +1711,22 @@ heslo ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Chyba řešitele skriptu: %1 %2 %3 %4 - + SSL Error Chyba SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Požádal jste Tomahawk o to, aby se bezpečně připojil k <b>%1</b>, ale nelze potvrdit, že je vaše připojení bezpečné:<br><br><b>%2</b><br><br>Chcete tomuto připojení důvěřovat? - + Trust certificate Důvěřovat certifikátu @@ -2220,82 +2220,82 @@ heslo SourcesModel - + Group Skupina - + Collection Sbírka - + Playlist Seznam skladeb - + Automatic Playlist Automatický seznam skladeb - + Station Stanice - + Browse Procházet - + Search History Historie hledání - + My Music Moje hudba - + SuperCollection Supersbírka - + Network Activity Činnost sítě - + Cloud Mračno - + Dashboard Nástěnka - + Recently Played Nedávno poslouchané - + Charts Žebříčky - + New Releases Novinky - + Friends Přátelé @@ -3607,7 +3607,7 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Nepodařilo se nahrát data iTunes. Chyba při natahování informací ze sítě! @@ -3643,13 +3643,13 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::LatchManager - + &Catch Up &Skočit na nynější název - - + + &Listen Along &Poslouchat @@ -3704,43 +3704,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený @@ -3807,7 +3807,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka @@ -4105,7 +4105,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TrackView - + Sorry, your filter '%1' did not match any results. Promiňte, vašemu filtru '%1' se nepodařilo najít žádné výsledky. @@ -4113,13 +4113,13 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: TransferStatusItem - + from streaming artist - track from friend od - + to streaming artist - track to friend komu diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index ee80cef82f..aec353f928 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Lyt med - + Stop &Listening Along Stop &Medlytning - + &Follow in real-time &Følg i realtid - - + + &Listen Privately &Lyt Privat - - + + &Listen Publicly &Lyt Offentligt - + &Load Playlist &Indlæs Spilleliste - + &Load Station - + &Rename Playlist &Omdøb Spilleliste - + &Rename Station - + &Copy Playlist Link &Kopier Spilleliste Link - + &Play &Afspil - + &Stop &Stop - + &Previous Track &Forrige Spor - + &Next Track &Næste Spor - + &Quit &Afslut - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist - + Title - + Composer - + Album - + Track - + Duration - + Bitrate - + Age - + Year - + Size - + Origin - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1545,67 +1545,67 @@ Password QObject - + %n year(s) ago %n år siden%n år siden - + %n year(s) %n år%n år - + %n month(s) ago %n måned siden%n måneder siden - + %n month(s) %n måned%n måneder - + %n week(s) ago %n uge siden%n uger siden - + %n week(s) %n uge%n uger - + %n day(s) ago %n dag siden%n dage siden - + %n day(s) %n dag%n dage - + %n hour(s) ago %n time siden%n timer siden - + %n hour(s) %n time%n timer - + %1 minutes ago %1 minutter siden - + %1 minutes %1 minutter - + just now lige nu @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2218,82 +2218,82 @@ Password SourcesModel - + Group Gruppe - + Collection Samling - + Playlist Spilleliste - + Automatic Playlist Automatisk Spilleliste - + Station Station - + Browse Browse - + Search History Søge historie - + My Music Min Musik - + SuperCollection SuperSamling - + Network Activity - + Cloud - + Dashboard Instrumentbræt - + Recently Played Nyligt Afspillede - + Charts Lister - + New Releases - + Friends Venner @@ -3595,7 +3595,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Fejl i at få iTunes information fra netværket @@ -3631,13 +3631,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up - - + + &Listen Along @@ -3692,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline @@ -3795,7 +3795,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling @@ -4092,7 +4092,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Beklager, din filter '%1' matchede ikke nogle resultater @@ -4100,13 +4100,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 90baf61a29..632f2463eb 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -91,174 +91,174 @@ erlauben sich mit dir zu verbinden? ActionCollection - + &Listen Along &Mithören - + Stop &Listening Along Mithören b&eenden - + &Follow in real-time In Echtzeit &folgen - - + + &Listen Privately &Privat Modus aktivieren - - + + &Listen Publicly &Privat Modus verlassen - + &Load Playlist &Lade Playlist - + &Load Station Sender laden - + &Rename Playlist Playlist &umbenennen - + &Rename Station Sender umbenennen - + &Copy Playlist Link &Kopiere Link zu dieser Playlist - + &Play &Abspielen - + &Stop &Stop - + &Previous Track &Vorheriges Lied - + &Next Track &Nächstes Lied - + &Quit &Verlassen - + Load &XSPF... &XSPF laden... - + U&pdate Collection Samml&ung aktualisieren - + Fully &Rescan Collection Sammlung komplett scannen - + Show Offline Sources Offline Sourcen anzeigen - + &Configure Tomahawk... &Konfiguriere Tomahawk... - + Minimize Minimieren - + Zoom Zoom - + Enter Full Screen Vollbildmodus aktivieren - + Hide Menu Bar Menüleiste ausblenden - + Diagnostics... Diagnose... - + About &Tomahawk... &Über Tomahawk... - + &Legal Information... &Rechtliche Informationen... - + &View Logfile &Log anzeigen - + Check For Updates... Nach Updates suchen... - + &Controls S&teuerung - + &Settings &Einstellungen - + &Help &Hilfe - + &Window &Fenster - + Main Menu Hauptmenü @@ -895,12 +895,12 @@ Passwort InboxJobItem - + Sent %1 by %2 to %3. %1 gesendet an dir %2 von %3. - + %1 sent you %2 by %3. %1 gesendet an dir %2 von %3. @@ -924,9 +924,9 @@ Passwort JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warnung: API aufruf %1 zurückgegebener Daten synchron. @@ -1257,108 +1257,108 @@ Passwort PlayableModel - + Artist Künstler - + Title Titel - + Composer Komponist - + Album Album - + Track Lied - + Duration Dauer - + Bitrate Bitrate - + Age Alter - + Year Jahr - + Size Größe - + Origin Quelle - + Accuracy Treffsicherheit - + Perfect match Perfekt - + Very good match Sehr gut - + Good match Gut - + Vague match Vage - + Bad match Schlecht - + Very bad match Sehr schlecht - + Not available Nicht verfügbar - + Searching... Suche läuft... - - + + Name Name @@ -1379,31 +1379,31 @@ Passwort PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you angehört %1 von dir - + played %1 by %2 e.g. played 3 hours ago by SomeSource angehört %1 von %2 - + added %1 e.g. added 3 hours ago hinzugefügt %1 - + by <b>%1</b> e.g. by SomeArtist von <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum von <b>%1</b> auf <b>%2</b> @@ -1547,67 +1547,67 @@ Passwort QObject - + %n year(s) ago vor %n Jahrvor %n Jahren - + %n year(s) %n Jahr%n Jahre - + %n month(s) ago vor %n Monatvor %n Monaten - + %n month(s) %n Monat%n Monate - + %n week(s) ago vor %n Wochevor %n Wochen - + %n week(s) %n Woche%n Wochen - + %n day(s) ago vor %n Tagvor %n Tagen - + %n day(s) %n Tag%n Tage - + %n hour(s) ago vor %n Stundevor %n Stunden - + %n hour(s) %n Stunde%n Stunden - + %1 minutes ago vor %1 Minuten - + %1 minutes %1 Minuten - + just now gerade eben @@ -1711,22 +1711,22 @@ Passwort ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Fehler: %1 %2 %3 %4 - + SSL Error SSL Fehler - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Sie wurden gebeten, Tomahawk sicher zu <b>%1</b> verbinden, aber wir können nicht bestätigen, dass die Verbindung sicher ist:<br><br><b>%2</b><br><br> Möchten Sie dieser Verbindung vertrauen? - + Trust certificate Zertifikat vertrauen @@ -2220,82 +2220,82 @@ Passwort SourcesModel - + Group Gruppe - + Collection Sammlung - + Playlist Playlist - + Automatic Playlist Automatische Playlist - + Station Station - + Browse Stöbern - + Search History Suchverlauf - + My Music Meine Musik - + SuperCollection Supersammlung - + Network Activity Netzwerk Aktivität - + Cloud Cloud - + Dashboard Dashboard - + Recently Played Kürzlich gehörte Lieder - + Charts Charts - + New Releases Neuerscheinungen - + Friends Freunde @@ -3602,7 +3602,7 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Konnte iTunes-Daten nicht laden! @@ -3638,13 +3638,13 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::LatchManager - + &Catch Up Zum aktuellen Titel &springen - - + + &Listen Along &Mithören @@ -3699,43 +3699,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline @@ -3802,7 +3802,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -4100,7 +4100,7 @@ Tomahawk auf Twitter's Website authentifiziert hast: TrackView - + Sorry, your filter '%1' did not match any results. Entschuldige, dein Filter '%1' erzeugte keine Ergebnisse. @@ -4108,13 +4108,13 @@ Tomahawk auf Twitter's Website authentifiziert hast: TransferStatusItem - + from streaming artist - track from friend von - + to streaming artist - track to friend zu diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 6badcc5253..e043fc8189 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Συνακρόαση - + Stop &Listening Along Διακοπή &Συνακρόασης - + &Follow in real-time &Ακολούθηση σε πραγματικό χρόνο - - + + &Listen Privately &Ιδιωτική Ακρόαση - - + + &Listen Publicly &Δημόσια Ακρόαση - + &Load Playlist &Φόρτωση Λίστας Αναπαραγωγής - + &Load Station &Φόρτωση Σταθμού - + &Rename Playlist &Μετονομασία Λίστας Αναπαραγωγής - + &Rename Station &Μετονομασία Σταθμού - + &Copy Playlist Link &Αντιγραφή Συνδέσμου Λίστας Αναπαραγωγής - + &Play &Αναπαραγωγή - + &Stop &Διακοπή - + &Previous Track &Προηγούμενο Κομμάτι - + &Next Track &Επόμενο Κομμάτι - + &Quit &Έξοδος - + Load &XSPF... Φόρτωση &XSPF - + U&pdate Collection Ε&νημέρωση Συλλογής - + Fully &Rescan Collection Πλήρης &Επανασάρωση Συλλογής - + Show Offline Sources Εμφάνιση Εκτός Σύνδεσης Πηγών - + &Configure Tomahawk... &Ρύθμιση Tomahawk - + Minimize Ελαχιστοποίηση - + Zoom Μεγέθυνση - + Enter Full Screen Είσοδος σε Πλήρη Οθόνη - + Hide Menu Bar Απόκρυψη Γραμμής Μενού - + Diagnostics... Διαγνωστικά - + About &Tomahawk... Σχετικά με το &Tomahawk... - + &Legal Information... &Νομικές Πληροφορίες - + &View Logfile &Εμφάνιση Αρχείου Καταγραφής - + Check For Updates... Έλεγχος Για Ενημερώσεις - + &Controls &Χειριστήρια - + &Settings &Ρυθμίσεις - + &Help &Βοήθεια - + &Window &Παράθυρο - + Main Menu Κεντρικο Μενου @@ -894,12 +894,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. %1 αποστελθηκε σε εσενα %2 απο %3. - + %1 sent you %2 by %3. %1 αποστελθηκε σε εσενα %2 απο %3. @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Σριπτ προειδοποίηση αναλυτή: API κλήση% 1 επέστρεψε δεδομένα συγχρονισμένα. @@ -1256,108 +1256,108 @@ Password PlayableModel - + Artist Καλλιτέχνης - + Title Τίτλος - + Composer Συνθέτης - + Album Άλμπουμ - + Track Κομμάτι - + Duration Διάρκεια - + Bitrate Ρυθμός δεδομένων - + Age Ηλικία - + Year Έτος - + Size Μέγεθος - + Origin Καταγωγή - + Accuracy Ακρίβεια - + Perfect match Τέλειο ταίριασμα - + Very good match Πολύ καλό ταίριασμα - + Good match Καλό ταίριασμα - + Vague match Ασαφές ταίριασμα - + Bad match Κακό ταίριασμα - + Very bad match Πολύ κακό ταίριασμα - + Not available Μη διαθέσιμο - + Searching... Αναζήτηση... - - + + Name Όνομα @@ -1378,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you αναπαράχθηκε το %1 από εσάς - + played %1 by %2 e.g. played 3 hours ago by SomeSource αναπαράχθηκε το%1 από %2 - + added %1 e.g. added 3 hours ago προστέθηκε %1 - + by <b>%1</b> e.g. by SomeArtist από <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum από <b>%1</b> σε <b>%2</b> @@ -1546,67 +1546,67 @@ Password QObject - + %n year(s) ago %n χρόνο πριν%n χρόνια πριν - + %n year(s) %n χρόνο%n χρόνια - + %n month(s) ago %n μήνα πριν%n μήνες πριν - + %n month(s) %n μήνα%n μήνες - + %n week(s) ago %n εβδομάδα πριν%n εβδομάδες πριν - + %n week(s) %n εβδομάδα%n εβδομάδες - + %n day(s) ago %n μέρα πριν%n μέρες πριν - + %n day(s) %n μέρα %n μέρες - + %n hour(s) ago %n ώρα πριν%n ώρες πριν - + %n hour(s) %n ώρα%n ώρες - + %1 minutes ago %1 λεπτά πριν - + %1 minutes %1 λεπτά - + just now μόλις τώρα @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Σφάλμα Script Resolver: %1 %2 %3 %4 - + SSL Error SSL Σφάλμα - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Έχετε ζητήσει το Tomahawk να συνδεθεί με ασφάλεια στο <b>%1</b>, αλλά δεν μπορούμε να επιβεβαιώσουμε ότι η σύνδεσή σας είναι ασφαλής:<br><br><b>%2</b><br><br>Θέλετε να εμπιστεύθειτε αυτή τη σύνδεση; - + Trust certificate Εμπιστοσύνη πιστοποιητικόυ @@ -2220,82 +2220,82 @@ Password SourcesModel - + Group Ομάδα - + Collection Συλλογή - + Playlist Λίστα Αναπαραγωγής - + Automatic Playlist Αυτόματη Λίστα Αναπαραγωγής - + Station Σταθμός - + Browse Εύρεση - + Search History Ιστορικο Αναζητησης - + My Music Η Μουσικη Μου - + SuperCollection ΥπερΣυλλογή - + Network Activity Δραστηριότητα δικτύου - + Cloud Σύννεφο - + Dashboard Πίνακας Ελέγχου - + Recently Played Τελευταίες Αναπαραγωγές - + Charts Γραφήματα - + New Releases Νέες Κυκλοφορίες - + Friends Φίλοι @@ -3608,7 +3608,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Σφάλμα κατά την λήψη πληροφοριών του iTunes από το δίκτυο @@ -3644,13 +3644,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up &Catch Up - - + + &Listen Along &Συνακρόαση @@ -3705,43 +3705,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης @@ -3808,7 +3808,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου @@ -4105,7 +4105,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Συγγνώμη, το φίλτρο «%1» δεν αντιστοίχισε αποτελέσματα. @@ -4113,13 +4113,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend από - + to streaming artist - track to friend προς diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 124b266488..2746611d82 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -91,174 +91,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Listen Along - + Stop &Listening Along Stop &Listening Along - + &Follow in real-time &Follow in real-time - - + + &Listen Privately &Listen Privately - - + + &Listen Publicly &Listen Publicly - + &Load Playlist &Load Playlist - + &Load Station &Load Station - + &Rename Playlist &Rename Playlist - + &Rename Station &Rename Station - + &Copy Playlist Link &Copy Playlist Link - + &Play &Play - + &Stop &Stop - + &Previous Track &Previous Track - + &Next Track &Next Track - + &Quit &Quit - + Load &XSPF... Load &XSPF... - + U&pdate Collection U&pdate Collection - + Fully &Rescan Collection Fully &Rescan Collection - + Show Offline Sources Show Offline Sources - + &Configure Tomahawk... &Configure Tomahawk... - + Minimize Minimize - + Zoom Zoom - + Enter Full Screen Enter Full Screen - + Hide Menu Bar Hide Menu Bar - + Diagnostics... Diagnostics... - + About &Tomahawk... About &Tomahawk... - + &Legal Information... &Legal Information... - + &View Logfile &View Logfile - + Check For Updates... Check For Updates... - + &Controls &Controls - + &Settings &Settings - + &Help &Help - + &Window &Window - + Main Menu Main Menu @@ -895,12 +895,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. Sent %1 by %2 to %3. - + %1 sent you %2 by %3. %1 sent you %2 by %3. @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warning: API call %1 returned data synchronously. @@ -1257,108 +1257,108 @@ Password PlayableModel - + Artist Artist - + Title Title - + Composer Composer - + Album Album - + Track Track - + Duration Duration - + Bitrate Bitrate - + Age Age - + Year Year - + Size Size - + Origin Origin - + Accuracy Accuracy - + Perfect match Perfect match - + Very good match Very good match - + Good match Good match - + Vague match Vague match - + Bad match Bad match - + Very bad match Very bad match - + Not available Not available - + Searching... Searching... - - + + Name Name @@ -1379,31 +1379,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you played %1 by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource played %1 by %2 - + added %1 e.g. added 3 hours ago added %1 - + by <b>%1</b> e.g. by SomeArtist by <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum by <b>%1</b> on <b>%2</b> @@ -1547,67 +1547,67 @@ Password QObject - + %n year(s) ago %n year ago%n years ago - + %n year(s) %n year%n years - + %n month(s) ago %n month ago%n months ago - + %n month(s) %n month%n months - + %n week(s) ago %n week ago%n weeks ago - + %n week(s) %n week%n weeks - + %n day(s) ago %n day ago%n days ago - + %n day(s) %n day%n days - + %n hour(s) ago %n hour ago%n hours ago - + %n hour(s) %n hour%n hours - + %1 minutes ago %1 minutes ago - + %1 minutes %1 minutes - + just now just now @@ -1711,22 +1711,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Error: %1 %2 %3 %4 - + SSL Error SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate Trust certificate @@ -2223,82 +2223,82 @@ Password SourcesModel - + Group Group - + Collection Collection - + Playlist Playlist - + Automatic Playlist Automatic Playlist - + Station Station - + Browse Browse - + Search History Search History - + My Music My Music - + SuperCollection SuperCollection - + Network Activity Network Activity - + Cloud Cloud - + Dashboard Dashboard - + Recently Played Recently Played - + Charts Charts - + New Releases New Releases - + Friends Friends @@ -3610,7 +3610,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Error fetching iTunes information from the network! @@ -3646,13 +3646,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up &Catch Up - - + + &Listen Along &Listen Along @@ -3707,43 +3707,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline @@ -3810,7 +3810,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -4108,7 +4108,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Sorry, your filter '%1' did not match any results. @@ -4116,13 +4116,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend from - + to streaming artist - track to friend to diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 8833ab8a3e..3385af82f0 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -91,174 +91,174 @@ conectarse a usted y transmitir música? ActionCollection - + &Listen Along &Escuchar juntos - + Stop &Listening Along Detener la &reproducción conjunta - + &Follow in real-time &Seguir en tiempo real - - + + &Listen Privately &Escuchar en privado - - + + &Listen Publicly &Escuchar públicamente - + &Load Playlist Cargar &lista de reproducción - + &Load Station &Cargar estación - + &Rename Playlist &Renombrar lista de reproducción - + &Rename Station &Renombrar estación - + &Copy Playlist Link &Copiar enlace de lista de reproducción - + &Play &Reproducir - + &Stop &Detener - + &Previous Track &Pista anterior - + &Next Track Pista siguie&nte - + &Quit &Salir - + Load &XSPF... Cargar &XSPF - + U&pdate Collection Act&ualizar la colección - + Fully &Rescan Collection &Reanalizar toda la colección - + Show Offline Sources Mostrar fuentes sin conexión - + &Configure Tomahawk... &Configurar Tomahawk - + Minimize Minimizar - + Zoom Zoom - + Enter Full Screen Modo a pantalla completa - + Hide Menu Bar Ocultar barra de menús - + Diagnostics... Diagnósticos… - + About &Tomahawk... Acerca de &Tomahawk - + &Legal Information... Información &legal - + &View Logfile &Ver archivo de registro - + Check For Updates... Buscar actualizaciones… - + &Controls &Controles - + &Settings &Configuración - + &Help &Ayuda - + &Window &Ventana - + Main Menu Menú principal @@ -894,12 +894,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1256,108 +1256,108 @@ Password PlayableModel - + Artist Artista - + Title Título - + Composer Compositor - + Album Álbum - + Track Pista - + Duration Duración - + Bitrate Bitrate - + Age Antigüedad - + Year Año - + Size Tamaño - + Origin Origen - + Accuracy Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia muy buena - + Good match Coincidencia buena - + Vague match Coincidencia vaga - + Bad match Mala coincidencia - + Very bad match Muy mala coincidencia - + Not available No disponible - + Searching... - - + + Name Título @@ -1378,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproducido %1 por usted - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproducido %1 de %2 - + added %1 e.g. added 3 hours ago añadido %1 - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum por <b>%1</b> en <b>%2</b> @@ -1546,67 +1546,67 @@ Password QObject - + %n year(s) ago hace %n añohace %n años - + %n year(s) %n año%n años - + %n month(s) ago hace %n meshace %n meses - + %n month(s) %n mes%n meses - + %n week(s) ago hace %n semanahace %n semanas - + %n week(s) %n semana%n semanas - + %n day(s) ago hace %n díahace %n días - + %n day(s) %n día%n días - + %n hour(s) ago hace %n horahace %n horas - + %n hour(s) %n hora%n horas - + %1 minutes ago hace %1 minutos - + %1 minutes %1 minutos - + just now justo ahora @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error del resolutor de script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2222,82 +2222,82 @@ y estaciones basadas en sus gustos personales. SourcesModel - + Group Grupo - + Collection Colección - + Playlist Lista de reproducción - + Automatic Playlist Lista de reproducción automática - + Station Estación - + Browse Navegar - + Search History Historial de búsqueda - + My Music Mi música - + SuperCollection Supercolección - + Network Activity - + Cloud Nube - + Dashboard Panel de inicio - + Recently Played Reproducido recientemente - + Charts Listas - + New Releases Últimas novedades - + Friends Amigos @@ -3608,7 +3608,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Error al buscar la información de iTunes en la red @@ -3644,13 +3644,13 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::LatchManager - + &Catch Up &Actualizar - - + + &Listen Along &Escuchar juntos @@ -3705,43 +3705,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado @@ -3808,7 +3808,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección @@ -4106,7 +4106,7 @@ introduzca su número PIN aquí: TrackView - + Sorry, your filter '%1' did not match any results. Lo siento, tu filtro '%1' no ha encontrado resultados. @@ -4114,13 +4114,13 @@ introduzca su número PIN aquí: TransferStatusItem - + from streaming artist - track from friend de - + to streaming artist - track to friend para diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index e0880d901f..66f21616ef 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -91,174 +91,174 @@ yhdistää ja toistaa sinulta virtaa? ActionCollection - + &Listen Along Kuuntele &mukana - + Stop &Listening Along Lopeta &mukana kuunteleminen - + &Follow in real-time &Seuraa reaaliajassa - - + + &Listen Privately &Kuuntele yksityisesti - - + + &Listen Publicly &Kuuntele julkisesti - + &Load Playlist &Lataa soittolista - + &Load Station &Lataa asema - + &Rename Playlist &Muuta soittolistan nimeä - + &Rename Station &Muuta aseman nimeä - + &Copy Playlist Link &Kopioi soittolistan linkki - + &Play &Soita - + &Stop &Pysäytä - + &Previous Track &Edellinen kappale - + &Next Track S&euraava kappale - + &Quit &Lopeta - + Load &XSPF... Lataa &XSPF... - + U&pdate Collection &Päivitä kokoelma - + Fully &Rescan Collection &Muodosta kokoelma alusta alkaen - + Show Offline Sources Näytä verkottomat lähteet - + &Configure Tomahawk... Tomahawkin &asetukset... - + Minimize Pienennä - + Zoom Zoomaa - + Enter Full Screen Siirry koko näyttöön - + Hide Menu Bar Piilota valikkorivi - + Diagnostics... Diagnostiikka... - + About &Tomahawk... Tietoa &Tomahawkista... - + &Legal Information... Lakitiet&oa... - + &View Logfile &Näytä lokitiedosto - + Check For Updates... Tarkista päivitykset... - + &Controls &Ohjaus - + &Settings &Asetukset - + &Help O&hje - + &Window &Ikkuna - + Main Menu Päävalikko @@ -895,12 +895,12 @@ salasana InboxJobItem - + Sent %1 by %2 to %3. Lähetettiin artistin %2 kappale %1 kaverille %3. - + %1 sent you %2 by %3. %1 lähetti sinulle artistin %3 kappaleen %2. @@ -924,9 +924,9 @@ salasana JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Skriptiselvittimen varoitus: API-kutsu %1 palautti dataa synkronisesti. @@ -1257,108 +1257,108 @@ salasana PlayableModel - + Artist Artisti - + Title Nimi - + Composer Säveltäjä - + Album Albumi - + Track Kappale - + Duration Kesto - + Bitrate Bittinopeus - + Age Ikä - + Year Vuosi - + Size Koko - + Origin Alkuperä - + Accuracy Tarkkuus - + Perfect match Täysosuma - + Very good match Erittäin hyvä osuma - + Good match Hyvä osuma - + Vague match Epämääräinen osuma - + Bad match Kehno osuma - + Very bad match Erittäin kehno osuma - + Not available Ei saatavilla - + Searching... Haetaan... - - + + Name Nimi @@ -1379,31 +1379,31 @@ salasana PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you kuuntelit %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 kuunteli %1 - + added %1 e.g. added 3 hours ago lisätty %1 - + by <b>%1</b> e.g. by SomeArtist artistilta <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum artistilta <b>%1</b> albumilla <b>%2</b> @@ -1547,67 +1547,67 @@ salasana QObject - + %n year(s) ago %n vuosi sitten%n vuotta sitten - + %n year(s) %n vuosi%n vuotta - + %n month(s) ago %n kuukausi sitten%n kuukautta sitten - + %n month(s) %n kuukausi%n kuukautta - + %n week(s) ago %n viikko sitten%n viikkoa sitten - + %n week(s) %n viikko%n viikkoa - + %n day(s) ago %n päivä sitten%n päivää sitten - + %n day(s) %n päivä%n päivää - + %n hour(s) ago %n tunti sitten%n tuntia sitten - + %n hour(s) %n tunti%n tuntia - + %1 minutes ago %1 minuuttia sitten - + %1 minutes %1 minuuttia - + just now juuri nyt @@ -1711,22 +1711,22 @@ salasana ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptiselvittimen virhe: %1 %2 %3 %4 - + SSL Error SSL-virhe - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Olet pyytänyt Tomahawkia yhdistämään turvallisesti palvelimeen <b>%1</b>, mutta yhteyden turvallisuutta ei voida varmistaa:<br><br><b>%2</b><br><br>Haluatko luottaa tähän yhteyteen? - + Trust certificate Luota varmenteeseen @@ -2225,82 +2225,82 @@ käyttäjäradion käyttöönottamiseksi SourcesModel - + Group Ryhmä - + Collection Kokoelma - + Playlist soittolista - + Automatic Playlist automaattinen soittolista - + Station asema - + Browse Selaa - + Search History Hakuhistoria - + My Music Oma musiikki - + SuperCollection Superkokoelma - + Network Activity - + Cloud Pilvi - + Dashboard Kojelauta - + Recently Played Viime aikoina kuunnellut - + Charts Listat - + New Releases Uudet julkaisut - + Friends Kaverit @@ -3613,7 +3613,7 @@ kappaleen %2%4 %3. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! iTunes-tietojen hakeminen verkosta epäonnistui! @@ -3649,13 +3649,13 @@ kappaleen %2%4 %3. Tomahawk::LatchManager - + &Catch Up &Ota kiinni - - + + &Listen Along Kuuntele &mukana @@ -3710,43 +3710,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa @@ -3813,7 +3813,7 @@ kappaleen %2%4 %3. TomahawkApp - + My Collection Oma kokoelma @@ -4111,7 +4111,7 @@ anna siellä näytetty PIN-koodi tähän: TrackView - + Sorry, your filter '%1' did not match any results. Valitettavasti suodattimesi ”%1” ei tuottanut yhtään tuloksia. @@ -4119,13 +4119,13 @@ anna siellä näytetty PIN-koodi tähän: TransferStatusItem - + from streaming artist - track from friend kaverilta - + to streaming artist - track to friend kaverille diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 40081adf7c..aa9af929ff 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -91,174 +91,174 @@ de se connecter et streamer de vous? ActionCollection - + &Listen Along &Ecouter avec - + Stop &Listening Along Arrêter d'&écouter avec - + &Follow in real-time &Suivre en temps réel - - + + &Listen Privately &Ecouter en privé - - + + &Listen Publicly &Ecouter publiquement - + &Load Playlist &Charger une liste de lecture - + &Load Station &Charger la station - + &Rename Playlist &Renommer la liste de lecture - + &Rename Station &Renommer la station - + &Copy Playlist Link &Copier le lien de la piste de lecture - + &Play &Lire - + &Stop &Stop - + &Previous Track Piste &Précédente - + &Next Track Piste &Suivante - + &Quit &Quitter - + Load &XSPF... Charger &XSPF... - + U&pdate Collection Mettre à Jo&ur la Collection - + Fully &Rescan Collection &Rescanner la collection entièrement - + Show Offline Sources Afficher les sources hors ligne - + &Configure Tomahawk... &Configurer Tomahawk... - + Minimize Réduire - + Zoom Zoom - + Enter Full Screen Activer le mode plein écran - + Hide Menu Bar Masquer la barre de menu - + Diagnostics... Diagnostics... - + About &Tomahawk... A propos de &Tomahawk... - + &Legal Information... &Informations Légales... - + &View Logfile &Voir le fichier de log - + Check For Updates... Rechercher une mis à jour... - + &Controls &Contrôles - + &Settings &Paramètres - + &Help &Aide - + &Window &Fenêtre - + Main Menu Menu Principal @@ -894,12 +894,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. %1 vous a envoyé %2 par %3. @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1256,108 +1256,108 @@ Password PlayableModel - + Artist Artiste - + Title Titre - + Composer Compositeur - + Album Album - + Track Piste - + Duration Durée - + Bitrate Bitrate - + Age Age - + Year Année - + Size Taille - + Origin Origine - + Accuracy Précision - + Perfect match Correspondance parfaite - + Very good match Très bonne correspondance - + Good match Bonne correspondance - + Vague match Vague correspondance - + Bad match Mauvaise correspondance - + Very bad match Très mauvaise correspondance - + Not available Indisponible - + Searching... Recherche... - - + + Name Nom @@ -1378,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you joué %1 par vous - + played %1 by %2 e.g. played 3 hours ago by SomeSource joué %1 par %2 - + added %1 e.g. added 3 hours ago ajouté %1 - + by <b>%1</b> e.g. by SomeArtist par <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum par <b>%1</b> sur <b>%2</b> @@ -1546,67 +1546,67 @@ Password QObject - + %n year(s) ago il y a %n anil y a %n ans - + %n year(s) %n an%n ans - + %n month(s) ago il y a %n moisil y a %n mois - + %n month(s) %n mois%n mois - + %n week(s) ago il y a %n semaineil y a %n semaines - + %n week(s) %n semaine%n semaines - + %n day(s) ago il y a %n jouril y a %n jours - + %n day(s) %n jour%n jours - + %n hour(s) ago il y a %n heureil y a %n heures - + %n hour(s) %n heure%n heures - + %1 minutes ago il y a %1 minutes - + %1 minutes %1 minutes - + just now à l'instant @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erreur du script de résolution : %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2219,82 +2219,82 @@ Password SourcesModel - + Group Groupe - + Collection Collection - + Playlist Liste de lecture - + Automatic Playlist Liste de lecture automatique - + Station Station - + Browse Parcourir - + Search History Chercher dans l'historique - + My Music Ma Musique - + SuperCollection SuperCollection - + Network Activity - + Cloud Cloud - + Dashboard Tableau de bord - + Recently Played Joués récemment - + Charts Charts - + New Releases Nouveautés - + Friends Amis @@ -3605,7 +3605,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Échec du chargement des informations iTunes depuis le réseau ! @@ -3641,13 +3641,13 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::LatchManager - + &Catch Up &Rattraper son retard - - + + &Listen Along &Ecouter avec @@ -3702,43 +3702,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne @@ -3805,7 +3805,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection @@ -4103,7 +4103,7 @@ saisissez le numéro PIN ici : TrackView - + Sorry, your filter '%1' did not match any results. Désolé, votre filtre '%1' ne correspond à aucun résultat. @@ -4111,13 +4111,13 @@ saisissez le numéro PIN ici : TransferStatusItem - + from streaming artist - track from friend à partir de - + to streaming artist - track to friend à diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 8c4fa8ba9a..fb6d016c21 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Escoitar todo xunto - + Stop &Listening Along Parar de &escoitar todo xunto - + &Follow in real-time &Seguir en tempo rela - - + + &Listen Privately &Escoitar en privado - - + + &Listen Publicly &Escoitar en público - + &Load Playlist &Cargar a lista de reprodución - + &Load Station - + &Rename Playlist &Renomear a lista de reprodución - + &Rename Station - + &Copy Playlist Link &Copiar a ligazón da lista de reprodución - + &Play &Reproducir - + &Stop &Deter - + &Previous Track &Pista anterior - + &Next Track P&ista seguinte - + &Quit &Saír - + Load &XSPF... Cargar &XSPF... - + U&pdate Collection &Actualizar a colección - + Fully &Rescan Collection &Rescanear a colección completa - + Show Offline Sources Mostrar as fontes fóra de liña - + &Configure Tomahawk... &Configurar Tomahawk... - + Minimize Minimizar - + Zoom Ampliación - + Enter Full Screen Entrar na pantalla ao completo - + Hide Menu Bar Agochar a barra de menú - + Diagnostics... Diagnóstico... - + About &Tomahawk... Acerca de &Tomahawk... - + &Legal Information... &Información legal... - + &View Logfile &Ver o ficheiro de rexistro - + Check For Updates... Buscar actualizacións... - + &Controls &Controis - + &Settings &Configuración - + &Help &Axuda - + &Window &Xanela - + Main Menu Menú principal @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist Artista - + Title Título - + Composer Compositor - + Album Álbum - + Track Pista - + Duration Duración - + Bitrate Taxa de bits - + Age Idade - + Year Ano - + Size Tamaño - + Origin Orixe - + Accuracy Precisión - + Perfect match Coincidencia perfecta - + Very good match Coincidencia moi boa - + Good match Boa coincidencia - + Vague match Parcialmente coincidente - + Bad match Mala coincidencia - + Very bad match Nada coincidentes - + Not available Non está dispoñíbel - + Searching... - - + + Name Nome @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you reproduciu %3 horas atrás por ti - + played %1 by %2 e.g. played 3 hours ago by SomeSource reproduciu % por %2 - + added %1 e.g. added 3 hours ago engadiu %1 - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum by <b>%1</b> on <b>%2</b> @@ -1545,67 +1545,67 @@ Password QObject - + %n year(s) ago %n ano(s) atrás%n ano(s) atrás - + %n year(s) %n ano(s)%n ano(s) - + %n month(s) ago %n mes(es) atrás%n mes(es) atrás - + %n month(s) %n mes(es)%n mes(es) - + %n week(s) ago %n semana(s) atrás%n semana(s) atrás - + %n week(s) %n semana(s)%n semana(s) - + %n day(s) ago %n día(s) atrás%n día(s) atrás - + %n day(s) %n día(s)%n día(s) - + %n hour(s) ago %n hora(s) atrás%n hora(s) atrás - + %n hour(s) %n hora(s)%n hora(s) - + %1 minutes ago Hai %1 minutos - + %1 minutes %1 minutos - + just now só agora @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erro do solucionador de erros: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2219,83 +2219,83 @@ Password SourcesModel - + Group Grupo - + Collection Colección - + Playlist Lista de reprodución - + Automatic Playlist Listas de reprodución automáticas - + Station - + Browse Examinar - + Search History Buscar no historial - + My Music A miña música - + SuperCollection Supercolección - + Network Activity - + Cloud Nube - + Dashboard Taboleiro - + Recently Played Escoitadas recentemente - + Charts Gráficos - + New Releases Novos lanzamentos - + Friends Amizades @@ -3607,7 +3607,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Erro obtendo a información de iTunes da rede! @@ -3643,13 +3643,13 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::LatchManager - + &Catch Up &Chistar - - + + &Listen Along &Escoitar xuntos @@ -3704,43 +3704,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado @@ -3807,7 +3807,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección @@ -4105,7 +4105,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. O filtro «%1» non dá ningún resultado. @@ -4113,13 +4113,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend de - + to streaming artist - track to friend até diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 69062275cd..524e3a269f 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along - + Stop &Listening Along - + &Follow in real-time - - + + &Listen Privately - - + + &Listen Publicly - + &Load Playlist - + &Load Station - + &Rename Playlist - + &Rename Station - + &Copy Playlist Link - + &Play - + &Stop - + &Previous Track - + &Next Track - + &Quit - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist - + Title - + Composer - + Album - + Track - + Duration - + Bitrate - + Age - + Year - + Size - + Origin - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1544,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now अभी @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2217,82 +2217,82 @@ Password SourcesModel - + Group - + Collection - + Playlist गीतसूची - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Network Activity - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends मित्रगण @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! @@ -3629,13 +3629,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up - - + + &Listen Along @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -4090,7 +4090,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4098,13 +4098,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 19532e7be9..50c7a3d8de 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along - + Stop &Listening Along - + &Follow in real-time - - + + &Listen Privately - - + + &Listen Publicly - + &Load Playlist &Lejátszólista betöltése - + &Load Station - + &Rename Playlist Lejátszólista átnevezése - + &Rename Station - + &Copy Playlist Link - + &Play Lejátszás - + &Stop Megállítás - + &Previous Track Előző zeneszám - + &Next Track Következő zeneszám - + &Quit Kilépés - + Load &XSPF... &XSPF betöltése... - + U&pdate Collection Kolelkció frissítése - + Fully &Rescan Collection - + Show Offline Sources Nem elérhető források mutatása - + &Configure Tomahawk... Tomahawk kofigurálása - + Minimize Minimalizálás - + Zoom Zoom - + Enter Full Screen - + Hide Menu Bar Menü bar elrejtése - + Diagnostics... Diagnosztizálás... - + About &Tomahawk... &Tomahawkról - + &Legal Information... Jogi információk - + &View Logfile - + Check For Updates... Frissítések ellenőrzése - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist - + Title - + Composer - + Album - + Track - + Duration - + Bitrate - + Age - + Year - + Size - + Origin - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name Név @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1544,67 +1544,67 @@ Password QObject - + %n year(s) ago %n évvel ezelőtt%n évvel ezelőtt - + %n year(s) %n év%n év - + %n month(s) ago %n hónappal ezelőtt%n hónappal ezelőtt - + %n month(s) %n hónap%n hónap - + %n week(s) ago %n héttel ezelőtt%n héttel ezelőtt - + %n week(s) %n hét%n hét - + %n day(s) ago %n nappal ezelőtt%n nappal ezelőtt - + %n day(s) %n nap%n nap - + %n hour(s) ago %n órával ezelőtt%n órával ezelőtt - + %n hour(s) %n óra%n óra - + %1 minutes ago %1 perccel ezelőtt - + %1 minutes %1 perc - + just now éppen most @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2217,82 +2217,82 @@ Password SourcesModel - + Group Csoport - + Collection Kollekció - + Playlist Lejátszólista - + Automatic Playlist Automatikus lejátszólista - + Station Rádióállomás - + Browse Böngészés - + Search History Keresési előzmények - + My Music Zenéim - + SuperCollection Szuper kollekció - + Network Activity - + Cloud - + Dashboard - + Recently Played Monstanában játszott - + Charts - + New Releases Új kiadások - + Friends Barátok @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! @@ -3629,13 +3629,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up - - + + &Listen Along @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció @@ -4090,7 +4090,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4098,13 +4098,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 205d9fe1c2..ab136bd28e 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along - + Stop &Listening Along - + &Follow in real-time - - + + &Listen Privately - - + + &Listen Publicly - + &Load Playlist - + &Load Station - + &Rename Playlist - + &Rename Station - + &Copy Playlist Link - + &Play - + &Stop - + &Previous Track - + &Next Track - + &Quit - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist - + Title - + Composer - + Album - + Track - + Duration - + Bitrate - + Age - + Year - + Size - + Origin - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1544,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2217,82 +2217,82 @@ Password SourcesModel - + Group - + Collection - + Playlist - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Network Activity - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! @@ -3629,13 +3629,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up - - + + &Listen Along @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -4090,7 +4090,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4098,13 +4098,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 4af3f74224..b4cb11785c 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along Asco&lta insieme - + Stop &Listening Along Non asco&ltare assieme - + &Follow in real-time S&egui in diretta - - + + &Listen Privately Asco&lta in privato - - + + &Listen Publicly Asco&lta pubblicamente - + &Load Playlist C&arica playlist - + &Load Station &Carica Stazione - + &Rename Playlist &Rinomina playlist - + &Rename Station &Rinomina Stazione - + &Copy Playlist Link &Copia link playlist - + &Play Ri&produci - + &Stop &Stop - + &Previous Track &Indietro - + &Next Track Ava&nti - + &Quit Chi&udi - + Load &XSPF... Carica &XSPF... - + U&pdate Collection &Aggiorna collezione - + Fully &Rescan Collection Ri&scansiona completamente la collezione - + Show Offline Sources Mostra risorse non connesse - + &Configure Tomahawk... &Configura Tomahawk... - + Minimize Minimizza - + Zoom Zoom - + Enter Full Screen Modalità schermo intero - + Hide Menu Bar Nascondi barra menu - + Diagnostics... Diagnostica... - + About &Tomahawk... Riguardo &Tomahawk... - + &Legal Information... Informazioni &legali... - + &View Logfile &Mostra file di log - + Check For Updates... Controlla per aggiornamenti... - + &Controls &Controlli - + &Settings &Impostazioni - + &Help &Aiuto - + &Window &Finestra - + Main Menu Menù principale @@ -894,12 +894,12 @@ temporanea InboxJobItem - + Sent %1 by %2 to %3. Spedito %1 da %2 a %3. - + %1 sent you %2 by %3. %1 ti ha spedito %2 di %3. @@ -923,9 +923,9 @@ temporanea JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Avvertimento Script Resolver: chiamata API 1% ha restituito dati sincroni. @@ -1256,108 +1256,108 @@ temporanea PlayableModel - + Artist Artista - + Title Titolo - + Composer Compositore - + Album Album - + Track Traccia - + Duration Durata - + Bitrate Bitrate - + Age Età - + Year Anno - + Size Grandezza - + Origin Origine - + Accuracy Precisione - + Perfect match Abbinamento perfetto - + Very good match Abbinamento molto buono - + Good match Buon abbinamento - + Vague match Vaga corrispondenza - + Bad match Brutto abbinamento - + Very bad match Pessimo abbinamento - + Not available Non disponibile - + Searching... Sto cercando... - - + + Name Nome @@ -1378,31 +1378,31 @@ temporanea PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you riprodotta %1 da te - + played %1 by %2 e.g. played 3 hours ago by SomeSource riprotta %1 da %2 - + added %1 e.g. added 3 hours ago aggiunta %1 - + by <b>%1</b> e.g. by SomeArtist da <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum da <b>%1</b> su <b>%2</b> @@ -1545,67 +1545,67 @@ temporanea QObject - + %n year(s) ago un anno fa %n anni fa - + %n year(s) un anno fa%n anni fa - + %n month(s) ago un mese fa%n mesi fa - + %n month(s) un mese%n mesi - + %n week(s) ago una settimana fa%n settimane fa - + %n week(s) una settimana%n settimane - + %n day(s) ago un giorno fa%n giorni fa - + %n day(s) un giorno%n giorni - + %n hour(s) ago un'ora fa%n ore fa - + %n hour(s) un'ora%n ore - + %1 minutes ago %1 minuti fa - + %1 minutes %1 minuti - + just now proprio ora @@ -1709,22 +1709,22 @@ temporanea ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Errore script resolver: %1 %2 %3 %4 - + SSL Error Errore SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Hai chiesto a Tomahawk di connettersi in modo sicuro a <b>%1</b>, ma non possiamo garantire che la conessione sia sicura: <br><br><b>%2</b><br><br> Vuoi fidarti di questa connessione? - + Trust certificate Certificato di trust @@ -2218,82 +2218,82 @@ temporanea SourcesModel - + Group Gruppo - + Collection Collezione - + Playlist Playlist - + Automatic Playlist Playlist automatica - + Station Stazione - + Browse Esplora - + Search History Cronologia di ricerca - + My Music La mia musica - + SuperCollection Supercollezione - + Network Activity Attività network - + Cloud Cloud - + Dashboard Cruscotto - + Recently Played Ascoltate recentemente - + Charts Classifiche - + New Releases Nuove uscite - + Friends Amici @@ -3595,7 +3595,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Errore nel recuperare informazioni di iTunes dalla rete! @@ -3631,13 +3631,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up &Raggiungilo - - + + &Listen Along &Ascolta assieme @@ -3692,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso @@ -3795,7 +3795,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione @@ -4092,7 +4092,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Spiacente, il tuo filtro %1 non ha trovato nessun risultato. @@ -4100,13 +4100,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend da - + to streaming artist - track to friend a diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index b9dba53ac8..9a74dedb34 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along 共有聴取 - + Stop &Listening Along 共有聴取を中止 - + &Follow in real-time 実時間にフォロー - - + + &Listen Privately 非公開で聴く - - + + &Listen Publicly 公開で聴く - + &Load Playlist プレイリストを読み込み - + &Load Station - + &Rename Playlist プレイリスト名を変更 - + &Rename Station - + &Copy Playlist Link プレイリストリンクをコピー - + &Play 再生 - + &Stop 停止 - + &Previous Track 前のトラック - + &Next Track 次のトラック - + &Quit 終了 - + Load &XSPF... XSPFを読み込み... - + U&pdate Collection コレクションを更新 - + Fully &Rescan Collection すべてのコレクションを再スキャンする - + Show Offline Sources オフラインのソースを表示 - + &Configure Tomahawk... Tomahawkを設定... - + Minimize 最小化 - + Zoom ズーム - + Enter Full Screen - + Hide Menu Bar メニューバーを隠す - + Diagnostics... 診断... - + About &Tomahawk... Tomahawk について... - + &Legal Information... 法定情報... - + &View Logfile - + Check For Updates... 更新を確認... - + &Controls 制御 - + &Settings 設定 - + &Help ヘルプ - + &Window ウインドウ - + Main Menu メインメニュー @@ -894,12 +894,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1256,108 +1256,108 @@ Password PlayableModel - + Artist アーティスト - + Title タイトル - + Composer 作曲者 - + Album アルバム - + Track トラック - + Duration 時間 - + Bitrate ビットレート - + Age 変更日 - + Year - + Size サイズ - + Origin 音源 - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name 名前 @@ -1378,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you %1を再生しました。 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2が%1を再生しました。 - + added %1 e.g. added 3 hours ago %1を追加しました - + by <b>%1</b> e.g. by SomeArtist <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum <b>%1</b>の<b>%2</b> @@ -1546,67 +1546,67 @@ Password QObject - + %n year(s) ago %n年前 - + %n year(s) %n年 - + %n month(s) ago %nヶ月前 - + %n month(s) %nヶ月 - + %n week(s) ago %n週間前 - + %n week(s) %n週間 - + %n day(s) ago %n日前 - + %n day(s) %n日 - + %n hour(s) ago %n時間前 - + %n hour(s) %n時間 - + %1 minutes ago %1分前 - + %1 minutes %1分 - + just now たった今 @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2222,82 +2222,82 @@ Password SourcesModel - + Group グループ - + Collection コレクション - + Playlist プレイリスト - + Automatic Playlist 自動プレイリスト - + Station ステーション - + Browse 閲覧 - + Search History 履歴を検索 - + My Music マイミュージック - + SuperCollection スーパーコレクション - + Network Activity - + Cloud - + Dashboard ダッシュボード - + Recently Played 最近聴いたトラック - + Charts チャート - + New Releases ニューリリース - + Friends 友達 @@ -3605,7 +3605,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! iTunes情報が取得されませんでした! @@ -3641,13 +3641,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up 追いつける - - + + &Listen Along 共有聴取 @@ -3702,43 +3702,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン @@ -3805,7 +3805,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション @@ -4103,7 +4103,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. %1に一致する結果は見つかりませんでした。 @@ -4111,13 +4111,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend から - + to streaming artist - track to friend diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 41c47ed8c6..b40bdd4445 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along K&lausytis kartu - + Stop &Listening Along Stabdyti k&lausymąsi kartu - + &Follow in real-time Se&kti realiu laiku - - + + &Listen Privately K&lausytis privačiai - - + + &Listen Publicly K&lausytis viešai - + &Load Playlist Įke&lti grojaraštį - + &Load Station - + &Rename Playlist Per&vadinti grojaraštį - + &Rename Station - + &Copy Playlist Link &Kopijuoti grojaraščio nuorodą - + &Play &Groti - + &Stop &Sustabdyti - + &Previous Track Ankstesnis takelis - + &Next Track Kitas takelis - + &Quit &Baigti darbą - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist Atlikėjas - + Title Pavadinimas - + Composer Kompozitorius - + Album Albumas - + Track Takelio numeris - + Duration Trukmė - + Bitrate Bitų dažnis - + Age Amžius - + Year Metai - + Size Dydis - + Origin Šaltinis - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name Vardas @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1544,67 +1544,67 @@ Password QObject - + %n year(s) ago prieš %n metusprieš %n metusprieš %n metų - + %n year(s) %n metai%n metai%n metų - + %n month(s) ago prieš %n mėnesįprieš %n mėnesiusprieš %n mėnesių - + %n month(s) %n mėnuo%n mėnesiai%n mėnesių - + %n week(s) ago prieš %n savaitęprieš %n savaitesprieš %n savaičių - + %n week(s) %n savaitė%n savaitės%n savaičių - + %n day(s) ago prieš %n dienąprieš %n dienasprieš %n dienų - + %n day(s) %n diena%n dienos%n dienų - + %n hour(s) ago prieš %n valandąprieš %n valandasprieš %n valandų - + %n hour(s) %n valanda%n valandos%n valandų - + %1 minutes ago - + %1 minutes - + just now ką tik @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2217,82 +2217,82 @@ Password SourcesModel - + Group - + Collection Kolekcija - + Playlist Grojaraštis - + Automatic Playlist Automatinis grojaraštis - + Station Stotis - + Browse Naršyti - + Search History Paieškos istorija - + My Music Mano muzika - + SuperCollection Super Kolekcija - + Network Activity - + Cloud - + Dashboard Skydelis - + Recently Played Neseniai klausyta - + Charts - + New Releases Nauji leidimai - + Friends Draugai @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! @@ -3629,13 +3629,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up &Pasivyti - - + + &Listen Along K&lausytis kartu @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija @@ -4090,7 +4090,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4098,13 +4098,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index f0ff46b561..4cf18ff78d 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -91,174 +91,174 @@ połączyć się i strumieniować od ciebie? ActionCollection - + &Listen Along &Słuchaj razem - + Stop &Listening Along Przestań &Słuchać razem - + &Follow in real-time &Podążaj na bieżąco - - + + &Listen Privately &Słuchaj Prywatnie - - + + &Listen Publicly &Słuchaj Publicznie - + &Load Playlist &Załaduj listę - + &Load Station - + &Rename Playlist &Zmień nazwę listy - + &Rename Station - + &Copy Playlist Link &Skopiuj link listy - + &Play &Odtwarzaj - + &Stop &Zatrzymaj - + &Previous Track &Poprzednia piosenka - + &Next Track &Następna piosenka - + &Quit &Wyjdź - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -894,12 +894,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1256,108 +1256,108 @@ Password PlayableModel - + Artist Wykonawca - + Title Tytuł - + Composer Kompozytor - + Album Album - + Track Utwór - + Duration Czas trwania - + Bitrate Bitrate - + Age Wiek - + Year Rok - + Size Rozmiar - + Origin Źródło - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name @@ -1378,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1546,67 +1546,67 @@ Password QObject - + %n year(s) ago %n rok temu%n lata temu%n lat temu - + %n year(s) %n rok%n lata%n lat - + %n month(s) ago %n miesiąc temu%n miesiące temu%n miesięcy temu - + %n month(s) %n miesiąc%n miesiące%n miesięcy - + %n week(s) ago %n tydzień temu%n tygodnie temu%n tygodni temu - + %n week(s) %n tydzień%n tygodnie%n tygodni - + %n day(s) ago %n dzień temu%n dni temu%n dni temu - + %n day(s) %n dzień%n dni%n dni - + %n hour(s) ago %n godzinę temu%n godziny temu%n godzin temu - + %n hour(s) %n godzinę%n godziny%n godzin - + %1 minutes ago %1 minut temu - + %1 minutes %1 minut - + just now przed chwilą @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2219,82 +2219,82 @@ Password SourcesModel - + Group Grupa - + Collection kolekcja - + Playlist Lista - + Automatic Playlist Automatyczna Lista - + Station Stacja - + Browse Przeglądaj - + Search History Historia wyszukiwania - + My Music Moja Muzyka - + SuperCollection Superkolekcja - + Network Activity - + Cloud - + Dashboard Tablica kontrolna - + Recently Played Ostatnio Odtworzone - + Charts Listy Przebojów - + New Releases Nowe Wydania - + Friends Znajomi @@ -3602,7 +3602,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Błąd podczas pobierania informacji z iTunes! @@ -3638,13 +3638,13 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::LatchManager - + &Catch Up &Dogoń - - + + &Listen Along &Słuchaj razem @@ -3699,43 +3699,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline @@ -3802,7 +3802,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja @@ -4100,7 +4100,7 @@ wprowadź pokazany numer PIN tutaj: TrackView - + Sorry, your filter '%1' did not match any results. Przepraszamy, twój filtr '%1' nie dopasował żadnych wyników. @@ -4108,13 +4108,13 @@ wprowadź pokazany numer PIN tutaj: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 0b3ec9389b..343d16e181 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -91,174 +91,174 @@ se conecte e faça o stream de você? ActionCollection - + &Listen Along &Ouça Junto - + Stop &Listening Along Parar de &Ouvir Junto - + &Follow in real-time &Seguir em tempo real - - + + &Listen Privately &Ouvir Privadamente - - + + &Listen Publicly &Ouvir Publicamente - + &Load Playlist &Carregar Playlist - + &Load Station - + &Rename Playlist &Renomear Playlist - + &Rename Station - + &Copy Playlist Link &Copiar link da Playlist - + &Play &Play - + &Stop &Stop - + &Previous Track &Faixa Anterior - + &Next Track &Próxima Faixa - + &Quit &Sair - + Load &XSPF... Carregar &XSPF... - + U&pdate Collection At&ualizar coleção - + Fully &Rescan Collection Varredura completa de coleção - + Show Offline Sources Mostrar fontes offline - + &Configure Tomahawk... &Configurar o Tomahawk... - + Minimize Minimizar - + Zoom Zoom - + Enter Full Screen - + Hide Menu Bar Esconder barra de menu - + Diagnostics... Diagnósticos... - + About &Tomahawk... Sobre &Tomahawk... - + &Legal Information... Informação &legal... - + &View Logfile - + Check For Updates... Verificar atualizações... - + &Controls &Controles - + &Settings C&onfigurações - + &Help &Ajuda - + &Window &Janela - + Main Menu Menu principal @@ -894,12 +894,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1256,108 +1256,108 @@ Password PlayableModel - + Artist Artista - + Title Título - + Composer Compositor - + Album Álbum - + Track Faixa - + Duration Duração - + Bitrate Taxa de bits - + Age Idade - + Year Ano - + Size Tamanho - + Origin Origem - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name Nome @@ -1378,31 +1378,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist por <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum por <b>%1</b> em <b>%2</b> @@ -1546,67 +1546,67 @@ Password QObject - + %n year(s) ago %n ano atrás%n anos atrás - + %n year(s) %n ano%n anos - + %n month(s) ago %n mês atrás%n meses atrás - + %n month(s) %n mês%n meses - + %n week(s) ago %n semana atrás%n semanas atrás - + %n week(s) %n semana%n semanas - + %n day(s) ago %n dia atrás%n dias atrás - + %n day(s) %n dia%n dias - + %n hour(s) ago %n hora atrás%n horas atrás - + %n hour(s) %n hora%n horas - + %1 minutes ago %1 minutos atrás - + %1 minutes %1 minutos - + just now agora @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2219,82 +2219,82 @@ Password SourcesModel - + Group Grupo - + Collection Coleção - + Playlist Playlist - + Automatic Playlist Playlist Automática - + Station Estação - + Browse Navegar - + Search History Histórico de Busca - + My Music Minhas Músicas - + SuperCollection SuperColeção - + Network Activity - + Cloud - + Dashboard Painel - + Recently Played Ouvidas Recentemente - + Charts Charts - + New Releases Lançamentos - + Friends Amigos @@ -3602,7 +3602,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Erro ao obter informações do iTunes pela rede! @@ -3638,13 +3638,13 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::LatchManager - + &Catch Up &Alcançar - - + + &Listen Along &Ouvir Junto @@ -3699,43 +3699,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline @@ -3802,7 +3802,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção @@ -4100,7 +4100,7 @@ colocar o número PIN mostrado aqui: TrackView - + Sorry, your filter '%1' did not match any results. Desculpe, o seu filtro '%1' não encontreou nenhum resultado. @@ -4108,13 +4108,13 @@ colocar o número PIN mostrado aqui: TransferStatusItem - + from streaming artist - track from friend de - + to streaming artist - track to friend para diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index da7492901b..6f3fac4cb8 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -92,176 +92,176 @@ connect and stream from you? ActionCollection - + &Listen Along &Слушать Вместе - + Stop &Listening Along &Прекратить Слушать Вместе - + &Follow in real-time &Следить в реальном времени - - + + &Listen Privately &Listen Privately - - + + &Listen Publicly &Listen Publicly - + &Load Playlist &Загрузить Плейлист - + &Load Station - + &Rename Playlist &Переименовать Плейлист - + &Rename Station - + &Copy Playlist Link &Скопировать Cсылку Плейлиста - + &Play &Играть - + &Stop &Стоп - + &Previous Track &Предыдущая - + &Next Track &Следующая - + &Quit &Выйти - + Load &XSPF... Загрузить &XSPF... - + U&pdate Collection Обновить Коллекцию - + Fully &Rescan Collection Полное Сканирование Коллекции - + Show Offline Sources Показать содержимое не в сети - + &Configure Tomahawk... &Настроить Tomahawk... - + Minimize Свернуть - + Zoom Увеличить - + Enter Full Screen Переход в полноэкранный режим - + Hide Menu Bar Спрятать Строку Меню - + Diagnostics... Диагностика - + About &Tomahawk... О &Tomahawk... - + &Legal Information... &Юридическая Информация - + &View Logfile &Показать Логи - + Check For Updates... Проверить Обновление... - + &Controls &Управление - + &Settings &Настройки - + &Help &Помощь - + &Window &Окно - + Main Menu Главное меню @@ -897,12 +897,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -926,9 +926,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1259,108 +1259,108 @@ Password PlayableModel - + Artist Исполнитель - + Title Название - + Composer Композитор - + Album Альбом - + Track Песня - + Duration Продолжительность - + Bitrate Битрей - + Age Возраст - + Year Год - + Size Размер - + Origin Расположение - + Accuracy Совпадение - + Perfect match Превосходное - + Very good match Очень Хорошое - + Good match Хорошое - + Vague match Расплывчатое - + Bad match Плохое совпадение - + Very bad match Очень плохое совпадение - + Not available Недоступно - + Searching... - - + + Name Имя @@ -1381,31 +1381,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you Воспроизводилась %1 вами - + played %1 by %2 e.g. played 3 hours ago by SomeSource Воспроизводилась %1 %2 - + added %1 e.g. added 3 hours ago Добавлена %1 - + by <b>%1</b> e.g. by SomeArtist <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum <b>%1</b> на <b>%2</b> @@ -1549,67 +1549,67 @@ Password QObject - + %n year(s) ago %n год назад%n года назад%n лет назад - + %n year(s) %n год%n года%n лет - + %n month(s) ago %n месяц назад%n месяца назад%n месяцей назад - + %n month(s) %n месяц%n месяца%n месяцей - + %n week(s) ago %n неделю назад%n недели назад%n недель назад - + %n week(s) %n неделю%n недели%n недель - + %n day(s) ago %n день назад%n дня назад%n дней назад - + %n day(s) %n день%n дня%n дней - + %n hour(s) ago %n час назад%n часа назад%n часов назад - + %n hour(s) %n час%n часа%n часов - + %1 minutes ago %1 минут(ы) назад - + %1 minutes %1 минут(ы) - + just now только что @@ -1713,22 +1713,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2225,82 +2225,82 @@ Password SourcesModel - + Group Группа - + Collection Коллекция - + Playlist Плейлист - + Automatic Playlist Автоматический плейлист - + Station Станция - + Browse Просмотреть - + Search History История поиска - + My Music Моя Музыка - + SuperCollection Общая Коллекция - + Network Activity - + Cloud Облако - + Dashboard Главная Панель - + Recently Played Последние Воспроизводимые - + Charts Чарты - + New Releases Новые Релизы - + Friends Друзья @@ -3609,7 +3609,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Возникла ошибка при получении информации из iTunes! @@ -3645,13 +3645,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up &Подхватить - - + + &Listen Along &Слушать Вместе @@ -3706,43 +3706,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети @@ -3809,7 +3809,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция @@ -4106,7 +4106,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. Ваш поиск '%1' недал результатов. @@ -4114,13 +4114,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend из - + to streaming artist - track to friend к diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index bbd760ea19..79c7460d48 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -91,174 +91,174 @@ ansluta och strömma från dig? ActionCollection - + &Listen Along &Lyssna med - + Stop &Listening Along Sluta &Lyssna med - + &Follow in real-time &Lyssna med i realtid - - + + &Listen Privately &Lyssna privat - - + + &Listen Publicly &Lyssna publikt - + &Load Playlist &Läs in spellista - + &Load Station &Ladda station - + &Rename Playlist &Byt namn på spellista - + &Rename Station &Döp om station - + &Copy Playlist Link &Kopiera länk till spellista - + &Play Spela &upp - + &Stop &Stoppa - + &Previous Track &Föregående spår - + &Next Track &Nästa spår - + &Quit A&vsluta - + Load &XSPF... Ladda &XSPF... - + U&pdate Collection Upp&datera samling - + Fully &Rescan Collection &Skanna om hela samlingen - + Show Offline Sources Visa frånkopplade källor - + &Configure Tomahawk... &Konfigurera Tomahawk... - + Minimize Minimera - + Zoom Zooma - + Enter Full Screen Fullskärmläge - + Hide Menu Bar Göm Meny - + Diagnostics... Diagnostik... - + About &Tomahawk... Om &Tomahawk... - + &Legal Information... &juridisk information - + &View Logfile &Öppna logfil - + Check For Updates... Leta efter uppdateringar... - + &Controls &Kontroller - + &Settings &Inställningar - + &Help &Hjälp - + &Window &Fönster - + Main Menu Huvudmeny @@ -895,12 +895,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. Skickade %1 av %2 till %3 - + %1 sent you %2 by %3. %1 skickade %2 av %3 till dig @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Resovler-skriptvarning: API-kall %1 returnerade data synkront @@ -1257,108 +1257,108 @@ Password PlayableModel - + Artist Artist - + Title Titel - + Composer Kompositör - + Album Album - + Track Spår - + Duration Längd - + Bitrate Bitrate - + Age Ålder - + Year År - + Size Storlek - + Origin Ursprung - + Accuracy Exakthet - + Perfect match Perfekt matchning - + Very good match Mycket bra matchning - + Good match Bra matchning - + Vague match Svag matchning - + Bad match Dålig matchning - + Very bad match Väldigt dålig matchning - + Not available Inte tillgänglig - + Searching... Söker... - - + + Name Namn @@ -1379,31 +1379,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you spelade %1 av dig - + played %1 by %2 e.g. played 3 hours ago by SomeSource Spelade %1 av %2 - + added %1 e.g. added 3 hours ago La till %1 - + by <b>%1</b> e.g. by SomeArtist av <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum av <b>%1</b> på <b>%2</b> @@ -1547,67 +1547,67 @@ Password QObject - + %n year(s) ago %n år sedan%n år sedan - + %n year(s) %n år sedan%n år sedan - + %n month(s) ago %n månad sedan%n månader sedan - + %n month(s) %n månad%n månader - + %n week(s) ago %n vecka sedan%n veckor sedan - + %n week(s) %n vecka%n veckor - + %n day(s) ago %n dag sedan%n dagar sedan - + %n day(s) %n dag%n dagar - + %n hour(s) ago %n timme sedan%n timmar sedan - + %n hour(s) %n timme%n timmar - + %1 minutes ago %1 minuter sedan - + %1 minutes %1 minuter - + just now precis nyss @@ -1711,22 +1711,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptfel i resolvern: %1 %2 %3 %4 - + SSL Error SSL-Fel - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Du har bett Tomahawk att göra en säker uppkoppling mot <b>%1</b>, men vi kan inte bekräfta att din uppkoppling är säker: <br><br><b>%2</b><br><br>Litar du på denna uppkoppling? - + Trust certificate Tillförlitligt certifikat @@ -2222,82 +2222,82 @@ och radiostationer baserat på din personliga profil SourcesModel - + Group Grupp - + Collection Samling - + Playlist Spellista - + Automatic Playlist Automatisk spellista - + Station Station - + Browse Bläddra - + Search History Sökhistorik - + My Music Min Musik - + SuperCollection Supersamling - + Network Activity Nätverksaktivitet - + Cloud Moln - + Dashboard Dashboard - + Recently Played Nyligen Spelade - + Charts Topplistor - + New Releases Nya släpp - + Friends Vänner @@ -3608,7 +3608,7 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::ItunesParser - + Error fetching iTunes information from the network! Det gick inte ta emot information från iTunes via nätverket! @@ -3644,13 +3644,13 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::LatchManager - + &Catch Up &Häng med - - + + &Listen Along &Spela med @@ -3705,43 +3705,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline @@ -3808,7 +3808,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling @@ -4106,7 +4106,7 @@ anger du PIN-koden här: TrackView - + Sorry, your filter '%1' did not match any results. Tyvärr, ditt filter '%1' gav inga träffar @@ -4114,13 +4114,13 @@ anger du PIN-koden här: TransferStatusItem - + from streaming artist - track from friend från - + to streaming artist - track to friend till diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index ca163ff008..732c95037b 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along &Birlikte Dinle - + Stop &Listening Along &Birlikte Dinlemeyi Durdur - + &Follow in real-time &Gerçek zamanlı takip et - - + + &Listen Privately &Gizli Dinle - - + + &Listen Publicly &Yayınlayarak Dinle - + &Load Playlist &Şarkı Listesini Yükle - + &Load Station - + &Rename Playlist &Şarkı Listesini Yeniden Adlandır - + &Rename Station - + &Copy Playlist Link &Şarkı Listesinin Bağlantısını Kopyala - + &Play &Yürüt - + &Stop &Durdur - + &Previous Track &Önceki Parça - + &Next Track &Sonraki Parça - + &Quit &Çıkış - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist - + Title - + Composer - + Album - + Track - + Duration - + Bitrate - + Age - + Year - + Size - + Origin - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1544,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago - + %1 minutes - + just now @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2217,82 +2217,82 @@ Password SourcesModel - + Group - + Collection - + Playlist - + Automatic Playlist - + Station - + Browse - + Search History - + My Music - + SuperCollection - + Network Activity - + Cloud - + Dashboard - + Recently Played - + Charts - + New Releases - + Friends @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! @@ -3629,13 +3629,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up - - + + &Listen Along @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -4090,7 +4090,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4098,13 +4098,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index e60ca5d1ee..9b59086339 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along 一块儿收听 - + Stop &Listening Along 停止一块儿收听 - + &Follow in real-time 实时跟随收听 - - + + &Listen Privately 私下收听 - - + + &Listen Publicly 公开收听 - + &Load Playlist 载入播放列表 - + &Load Station - + &Rename Playlist 重命名播放列表 - + &Rename Station - + &Copy Playlist Link 复制播放列表链接 - + &Play 播放 - + &Stop 停止 - + &Previous Track 上一首 - + &Next Track 下一首 - + &Quit 退出 - + Load &XSPF... 载入 XSPF... - + U&pdate Collection 更新收藏 - + Fully &Rescan Collection 完整重新扫描收藏 - + Show Offline Sources 显示离线资源 - + &Configure Tomahawk... 配置 Tomahawk... - + Minimize 最小化 - + Zoom 放大 - + Enter Full Screen - + Hide Menu Bar 隐藏菜单栏 - + Diagnostics... 诊断... - + About &Tomahawk... 关于 Tomahawk... - + &Legal Information... 法律信息... - + &View Logfile 查看日志文件 - + Check For Updates... 检查更新... - + &Controls 控制 - + &Settings 设置 - + &Help 帮助 - + &Window 窗口 - + Main Menu 主菜单 @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist 艺术家 - + Title 标题 - + Composer 作曲 - + Album 专辑 - + Track 歌曲 - + Duration 时长 - + Bitrate 比特率 - + Age 已发行 - + Year 发行年 - + Size 文件大小 - + Origin 来源 - + Accuracy 准确度 - + Perfect match 完美匹配 - + Very good match 极高匹配 - + Good match 高匹配 - + Vague match 模糊匹配 - + Bad match 低匹配 - + Very bad match 极低匹配 - + Not available - + Searching... - - + + Name 名字 @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you 你播放于 %1 - + played %1 by %2 e.g. played 3 hours ago by SomeSource %2 播放于 %1 - + added %1 e.g. added 3 hours ago 添加于 %1 - + by <b>%1</b> e.g. by SomeArtist 来自 <b>%1</b> - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum 来自 <b>%1</b> 的专辑 <b>%2</b> @@ -1545,67 +1545,67 @@ Password QObject - + %n year(s) ago %n 年前 - + %n year(s) %n 年 - + %n month(s) ago %n 月前 - + %n month(s) %n 月 - + %n week(s) ago %n 周前 - + %n week(s) %n 周 - + %n day(s) ago %n 天前 - + %n day(s) %n 天 - + %n hour(s) ago %n 小时前 - + %n hour(s) %n 小时 - + %1 minutes ago %1 分钟前 - + %1 minutes %1 分钟 - + just now 刚刚 @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2220,82 +2220,82 @@ Password SourcesModel - + Group 群组 - + Collection 收藏 - + Playlist 播放列表 - + Automatic Playlist 自动播放列表 - + Station 电台 - + Browse 随便看看 - + Search History 搜索历史 - + My Music 我的音乐 - + SuperCollection 超级收藏 - + Network Activity - + Cloud - + Dashboard 概览 - + Recently Played 最近播放 - + Charts 排行榜 - + New Releases 新专辑 - + Friends 朋友们 @@ -3603,7 +3603,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! 从网络获取 iTunes 信息时出现错误! @@ -3639,13 +3639,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up 跟上 - - + + &Listen Along 一起听 @@ -3700,43 +3700,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 @@ -3803,7 +3803,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -4101,7 +4101,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. 抱歉,未找到任何匹配 '%1' 的结果。 @@ -4109,13 +4109,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index a16f8ce7cb..39d705c6b7 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -90,174 +90,174 @@ connect and stream from you? ActionCollection - + &Listen Along - + Stop &Listening Along - + &Follow in real-time 即時跟隨 - - + + &Listen Privately 私下聆聽 - - + + &Listen Publicly 公開聆聽 - + &Load Playlist 載入播放清單 - + &Load Station - + &Rename Playlist 重新命名播放清單 - + &Rename Station - + &Copy Playlist Link 複製播放清單連結 - + &Play 播放 - + &Stop 停止 - + &Previous Track 上一首曲目 - + &Next Track 下一個曲目 - + &Quit 結束 - + Load &XSPF... - + U&pdate Collection - + Fully &Rescan Collection - + Show Offline Sources - + &Configure Tomahawk... - + Minimize - + Zoom - + Enter Full Screen - + Hide Menu Bar - + Diagnostics... - + About &Tomahawk... - + &Legal Information... - + &View Logfile - + Check For Updates... - + &Controls - + &Settings - + &Help - + &Window - + Main Menu @@ -893,12 +893,12 @@ Password InboxJobItem - + Sent %1 by %2 to %3. - + %1 sent you %2 by %3. @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1255,108 +1255,108 @@ Password PlayableModel - + Artist - + Title - + Composer - + Album - + Track - + Duration - + Bitrate - + Age - + Year - + Size - + Origin - + Accuracy - + Perfect match - + Very good match - + Good match - + Vague match - + Bad match - + Very bad match - + Not available - + Searching... - - + + Name @@ -1377,31 +1377,31 @@ Password PlaylistLargeItemDelegate - + played %1 by you e.g. played 3 hours ago by you - + played %1 by %2 e.g. played 3 hours ago by SomeSource - + added %1 e.g. added 3 hours ago - + by <b>%1</b> e.g. by SomeArtist - + by <b>%1</b> on <b>%2</b> e.g. by SomeArtist on SomeAlbum @@ -1544,67 +1544,67 @@ Password QObject - + %n year(s) ago - + %n year(s) - + %n month(s) ago - + %n month(s) - + %n week(s) ago - + %n week(s) - + %n day(s) ago - + %n day(s) - + %n hour(s) ago - + %n hour(s) - + %1 minutes ago %1 分鐘前 - + %1 minutes %1 分鐘 - + just now 剛才 @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate @@ -2217,82 +2217,82 @@ Password SourcesModel - + Group - + Collection 收藏 - + Playlist 播放清單 - + Automatic Playlist 自動播放清單 - + Station - + Browse 瀏覽 - + Search History 搜尋記錄 - + My Music 我的音樂 - + SuperCollection 超級收藏 - + Network Activity - + Cloud - + Dashboard 儀表板 - + Recently Played 最近播放的 - + Charts - + New Releases 新版本 - + Friends 朋友 @@ -3593,7 +3593,7 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::ItunesParser - + Error fetching iTunes information from the network! @@ -3629,13 +3629,13 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::LatchManager - + &Catch Up - - + + &Listen Along @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -4090,7 +4090,7 @@ enter the displayed PIN number here: TrackView - + Sorry, your filter '%1' did not match any results. @@ -4098,13 +4098,13 @@ enter the displayed PIN number here: TransferStatusItem - + from streaming artist - track from friend - + to streaming artist - track to friend From 400271615964d3bc37be7b614abd0e2b6be52447 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 17 Jun 2013 07:58:56 +0200 Subject: [PATCH 472/565] Loosen destructor calls and empty source_ptr instead of deleting it. --- src/libtomahawk/Source.cpp | 3 ++- src/libtomahawk/network/ControlConnection.cpp | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 3d817c6c0a..afb5e24288 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -111,7 +111,8 @@ Source::setControlConnection( ControlConnection* cc ) // Tell the ControlConnection it is not anymore responsible for us. d->cc->unbindFromSource(); // This ControlConnection is not needed anymore, get rid of it! - d->cc->deleteLater(); + // (But decouple the deletion it from the current activity) + QMetaObject::invokeMethod( d->cc.data(), "deleteLater", Qt::QueuedConnection); // Use new ControlConnection d->cc = cc; return true; diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 93f0460395..25596c327e 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -55,12 +55,14 @@ ControlConnection::ControlConnection( Servent* parent ) ControlConnection::~ControlConnection() { Q_D( ControlConnection ); - QReadLocker locker( &d->sourceLock ); tDebug( LOGVERBOSE ) << Q_FUNC_INFO << id() << name(); - if ( !d->source.isNull() ) { - d->source->setOffline(); + QReadLocker locker( &d->sourceLock ); + if ( !d->source.isNull() ) + { + d->source->setOffline(); + } } delete d->pingtimer; @@ -86,7 +88,7 @@ ControlConnection::unbindFromSource() { Q_D( ControlConnection ); QWriteLocker locker( &d->sourceLock ); - d->source.clear(); + d->source = Tomahawk::source_ptr(); } @@ -105,12 +107,14 @@ ControlConnection::setup() { Q_D( ControlConnection ); qDebug() << Q_FUNC_INFO << id() << name(); - QWriteLocker sourceLocker( &d->sourceLock ); + // We need to manually lock, so that we can release before the end of the function + d->sourceLock.lockForWrite(); if ( !d->source.isNull() ) { qDebug() << "This source seems to be online already."; Q_ASSERT( false ); + d->sourceLock.unlock(); return; } @@ -137,9 +141,12 @@ ControlConnection::setup() connect( d->pingtimer, SIGNAL( timeout() ), SLOT( onPingTimer() ) ); d->pingtimer->start(); d->pingtimer_mark.start(); + d->sourceLock.unlock(); } else { + // Unlock before we delete ourselves + d->sourceLock.unlock(); // There is already another ControlConnection in use, we are useless. deleteLater(); } From 97da93afd5bba729c22241897d99c482f844e1dd Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 17 Jun 2013 09:51:49 -0400 Subject: [PATCH 473/565] Fix compiler warning --- src/libtomahawk/Source.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 13f8c647f2..5871dd85e2 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -46,7 +46,7 @@ class MusicScanner; namespace Tomahawk { -class PlaybackLog; +struct PlaybackLog; class Resolver; class SourcePrivate; From c21de815a656b6e7761241f6496406f65cef4301 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 17 Jun 2013 09:52:02 -0400 Subject: [PATCH 474/565] Re-mark proxy changes as needing restart --- src/tomahawk/SettingsDialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tomahawk/SettingsDialog.cpp b/src/tomahawk/SettingsDialog.cpp index e525a52357..31029d1238 100644 --- a/src/tomahawk/SettingsDialog.cpp +++ b/src/tomahawk/SettingsDialog.cpp @@ -249,6 +249,7 @@ SettingsDialog::SettingsDialog(QObject *parent ) connect( m_advancedWidgetUi->staticIpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); connect( m_advancedWidgetUi->upnpRadioButton, SIGNAL( toggled(bool) ), SLOT( toggleRemoteMode() ) ); connect( m_advancedWidgetUi->enableProxyCheckBox, SIGNAL( toggled(bool) ), SLOT( toggleProxyEnabled() ) ); + connect( m_advancedWidgetUi->enableProxyCheckBox, SIGNAL( toggled(bool) ), SLOT( requiresRestart() ) ); connect( m_dialog, SIGNAL( accepted() ), SLOT( saveSettings() ) ); connect( m_dialog, SIGNAL( rejected() ), SLOT( onRejected() ) ); @@ -359,7 +360,10 @@ SettingsDialog::showProxySettings() { m_proxySettings.exec(); if ( m_proxySettings.result() == QDialog::Accepted ) + { + requiresRestart(); m_proxySettings.saveSettings(); + } } From c002eaf11cd129515e5736c717c34fc77cc6cf9b Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Mon, 17 Jun 2013 12:23:55 -0400 Subject: [PATCH 475/565] Try a different label font since Fauna One wasn't so great on Windows --- data/fonts/PathwayGothicOne-Regular.ttf | Bin 0 -> 26112 bytes data/images/view-toggle-inactive-centre.svg | 18 ++++++++++------ data/images/view-toggle-inactive-right.svg | 18 ++++++++++------ data/images/view-toggle-pressed-left.svg | 20 ++++++++++-------- resources.qrc | 1 + src/libtomahawk/widgets/BasicHeader.cpp | 7 ++++-- src/libtomahawk/widgets/Dashboard.cpp | 4 ++-- .../widgets/infowidgets/ArtistInfoWidget.cpp | 6 +++--- .../widgets/infowidgets/TrackInfoWidget.cpp | 2 +- 9 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 data/fonts/PathwayGothicOne-Regular.ttf diff --git a/data/fonts/PathwayGothicOne-Regular.ttf b/data/fonts/PathwayGothicOne-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..36ae84e6b40fd6f8946a8cfe3a5fc2cd643ee763 GIT binary patch literal 26112 zcmeHvd0br8`S&?@U}hNBVK&xbm;r`;nT25=AcTDX)kJOT zZd_|qYpuIBO{=wPty*grty^Q%v_BhLtJJDhGxL6*duL!E%g?@_{`G!dhBNohz2`jV zInQ~v^E~HX#u;O3d#mtRfcCG`{kE<=+t7@e#d|GzU3{E-6%VP-%2LXRs5ly?1&d|Wl!S%bFv#nNYXv20;emSv)`Z> z`lFWOHC9PK@h9V}c{U5it$o6B{BDwtyocF%4K)as(@o49>QV#$-;##*Hk_Nj5`+`p zX2OI$XA*Hj^#tK4-9x*prTV-DM>>C=74aOF&;QEe`A(K8Jj$&6yDW}9h-*PC6GhH1 z$N4r^#O`95>}zJEBR8{TelsgzAL9Pi%#NAPR(T3Vlo9*Ze zaKz&fix-A*e;@RFP^9y~) zX&m7Gs8LYR&>0H=Sk3ci8m@IAP<0`T^t zY{!^hN83#(`_cc4tc?BIdyMeHxm#!w`*Kq~vE3dV1zZ~dyniq)XjR0!(Z7ks@Kuno z^S%G(mCVTFn3kPnD{%}GY3Zl^YP8>g`@10zbbkrPQO^p6NWd|$biS0Cs6PE>r*K}y zGDI3iF$>qSBykMDlb^@(L^?ml44Au6NKOXYhG9PKD5WS)jKL_=PNSyviD2OQPeE`X$}I*R#q`*SzI^k^!@;9rfNR9{_dvIJ@!SI0306-GIQ_~ER~(~o)mx6HGkQMaa;p_Mv=MM3P?R^4k`(xkDw$5@Qcn8z@7Ctwn85T zvj}!E#&aL@@&aDXyLcbp$}i^E3cnQoBzwxFHN}~XCbKEcly53A)tIg@-Ir`mPBRN; zxjE7tZH_Y=%~|GpbFX~{8Dc0b@%@lM_gcozZQbHeL@r!mEt5=BxYz4_-}|)p=iaBh2fRDI8@+?xHgA)+$P;|(U#B$3e}4RD$A5bK z=DVDGLggE0iIs(6I1`$S8HRMyt~sVq)Xs6B3O{rew1vB{j{O zo{^cAos*lFUtqI4oQ1BU;*!#`@(OolRrMlR=DPZZ#-`?$*0%PJ&aNd(yO%9LZ^g=0 z=l87c?PI$yyZnl4uDj)q+wZ*luJ3;D`-kuS!4L1d|3?o#{LmwhJ$mHFKRNpLhJnH1 zW1DXqz2F~{BVX>h;q#4OeLb}4lTZKo`!6n>V)s08$(r?_z3}gub=%+R-?{5Mzj)>k zufFl-Ykz$F)RVt_|D$)`^LYQX<@L9YPw%_*TYInCf8fe*A7t0wbn}f*z4+U)U%&jD zt-pKaCB~lM-pd{cx+-wwT9ytTJjh<-T5jg`d;`Ch-@_l{PxF`g+d`0_7s`YUuzC*& z$Aph%`LbTwxNNWNCfWV6r)2L1l?8PN?GCyx=!Kx;L7xVjgG+*IgL{Lgg7*gB8T>@> zaXFJa`fd`+?{wZ@&3f;6W>ex!l*Z<7)y-T84nwe7@s$O zVEi)4nADzhLDK%DBS|lrm?_&dVA^MT!t`ZwL~>ekMRId;fAV$7N0Q%zE{HSR%&q1f z=DRJ2Apu2Tgsb6;$jJY3$?P(P)2?e!FORHg`<|dX_2Ktckw+-N-}soF5>80zPxum3-zPv}9n~yf9F8EE-DzG@v_D0~41bJmHr=!r7=5*L?dV?aDo8{UhUg)yR z6$Xn{ZZW4BY%Z%D=;-6@`T5_fs12zLHo23n=c|IML-j4)iP_8D4F`+k2Mg2gHq|vo zcMl!z7z%AF%-Pd$UsdC`Tds1}w1zKw!q}8tl}|LM`M*@0e@3%UGf%S)yl6hIXMdBW zf-bBu%~2t<>&y{cA&)ZBSuU^8YLg0WRzre0L>Crqj*qe03QHgQ{GRg*!mE@S)|H+Y zZ|TYosS0-zEID93FTsk|Ya?V9r>%m!3Y}5bT#j=ie?)>76Ax&4>I4IT3H$E({Gonl zcvVPtM&E1PT&z`Ag%=Rb2=BGvl^~|Z539lAQdnJ9mjNSi859QYTozO=>oyM#n!9D? zLCefTL;Slvx7^Z`+uxtpd+1PaUVlGe6tN%hN9Ch=W2){Q2&uU_=2XDk{$X%(2?WU!%|7djnjX1T&z-ng%=;*pO%uO=6F z6{Iarlh+E{+Z%p5;CZJq-`>7rx~8)vx40=3u=0TaTEMb^lWmZnv@GtlS8$vGdxf=v z6MVfWNmiGUKiip=k(X~tud`T#>p~j$wUjJgcA!yNCr`C)OlWac2+k_wW0x_QT~{1Crs z)hDY~P4T4ZHFYbat8DHi)2Z*q?wd~DM|f)RRp^^DXR=Oa(P+5k&W^i<9hGUPE)aH* zM@f9L0d!WgM3#a2exgECLU|Csh*L+3!d4h~&et?9>$<74yT0kF=IYFrO#FwYEK0$@ zpuHm5a&UCXlF{o;=DovhZJvUpI+v@CzWB8%Rr&c<^hG=)h_qaT{JtdRh*3~agCXHRBhUZm-vARu#=2CL7zyPO_#+{C<&6t|8la$&y=lBE>D)%DeI;iHZE>EyyfW~ zIj+Xgp^nA}U;T;(NIdo{!6m$kHBBXBsgn99lVrCUT;O)61G*DJMb^MzbretO(rqus(-E$2oiHRu< z&plaxTjlaA3#`C73gd=)!K`6UlCAI)GwJV}43*1XQFVLJ2u7kiege;GUDTph#xYQDw?!C>O zoy~1+MMZ7G(Y{N*zNGJEo^;^UFGRkfw&iF`_C^O~B`Pet?!f(?uYPa~FF#c&9IfTObOsM-GU?bq;f&oBHHrSXyOP84mAT4ItwO(a+fE(BPHHSN#ew$rZ@1rgTl+SXaMbfUPxt)ZvyqpASpGJdXoIkwh^fxqelt+m zUFoCeG29EoJ;00Sc8o`6kzLg~wX}Pxb?JFa0WhBb8Fq22$H?FF!`p>6AyOL+(4Yog zci|tk!40QsxR?5#vj_hjyDjk<*{V>|9~g~IPkaUe(D($xy`!n4qbYk^OLjKczweUZ zOM1WHFC=~2^AjIF&x?LU6e0l(-mKq!B>fTSOq6n~-B8FcaA(w;RNCe_uR`glO^x6m zesIvX>3pH@k`KtW*vIAMT721Aanm}pG;ZS4Uf>x{-h&@c1*XcP2Bxt7K;6Bgv9qzO zv$bGbMv91S&s6XY8J_oenCDaesd$a-)Cpw0qG0d zSF~NxtZWXtN@r|KFei2GZx3k-%1CXywLPn}@}l9cmO=H-_4#EnrP`FX!rs^t-0lvvBmGBw3x6wKu!F&R2L{@w2d2S?PYCtkuA>qTm{oz~8~m6Y z$}KYDNc_wAWLH<`NPGSAilq(hBm9&{hL#@)F{l1282oZZeXK`Y(j%}05DY-j$cB2( zZ{D+~wZE_Vic5u~Uw!2{?74-@Jg4{?#4Kq{)Q)tQlK9yvb}4(Wb$9Fet6F!RJ?atPJ2e)N2o9m)*o)kF>{y!64JYK@N`mM1-h%akiRfq`U54WRVNcJ1 zY$(FhI&Rn86Mz+izw z7Br`5*jQ%IjW^V#PUl61+D(N;7bhkQLUN*w%NM7Q^p+MSq&?udtSUWy@#^R;nXdTK z+|}0A=*x}1mST++DQamo?6)Y)7PD0WDbbSYhFDr?akV#ov9qYi*{0LyH5iPsg-%0K z3>Qv#K5t*K^}4Mq#(Hs{xnekR$8$RpdY591l zk?>FWo=hWpH^h6iwgD7!j*h8yxo&rJR#sV|IYGEbwNqQ+G4o>;!CO{B++PFu=t+AMA+KN|lcJq<2|0I&`c-KavD#Y~kPd}FD4aeab8@2c3b zWBK-yn5=@DUFOyclgrKK^5n>YoScCiA*(H`E@Z4z9%U~3>f-Kr&451R68ug|dON4a zW~-qu&p$4@i0CBQ{BF=6aw^(9ViYhVE&LQ$wlu^vIb1CTjmSEAzGz!D<@o{MaM5c- z-!im5Aggs-0-|PmoGhj+TDB)}k%+jffYbJsOJ@D78$r?>g8u8V8Kly!wXcr(b0I>$59G zd_-*AEy9UY6#s^;5TZeMa2LTb&|C$MadxPAT1|ViR@Z8Gwd5@pT5@XzAz|r2f4_gw zp1(>%pQTxrpJwymUD=YS)h+Q6EunkR-#m~fL*a9heVmmYtd&G@hmC1$xV!=XE$*r+ zqeMYw}x9SrIcV>4}Hz z=hoFFYIKq2lob%|%Py<%>~%(!>MrBMVtcaRbwUDp>4*&Y-C|gBu)gc7=Da9FqpPSn zKE6DNgrvHork7vtnQ*1|p>ML2wP-tQ!+Tmr?NcYDwiLH&M%y_X4r8E^4Yw|88=F|#vABJF zgOKp!AA3IaeEhSY@o27M-1~3nEouXwL@zgs;ZZl2%=lx)OY`DW<1-_ayIZ=OTK8R1 zlH*A3%oUP6TlwBo*Ghe2ZN*d2-f!2js7AEv!F#@ZHC~P0u>+5T6w#BNOBZmks%_*(_V!`J+X_c7lbop$dKZN|9?b~1U z{3^j}ZQfa75!39f3Mhu;c`nq~nclGmij#jPX&O062RtInQ9OC_+2Z)|Rz}%9zO}0sz zVF!PT=X+l9gwZ$ZWqNyfmT)ifs(GLgX%#ZSSgME=nhW{+0(Rg$Ss`k{VCxO=c5Fb}mSTk8AuU0t9%w}swY?R(mZq9Q)ZO*@^rnousyrn5Q(RtDF zm6^RswmU1Umxc~=A#&Pe)R(7Zk!y~90eg6~-=>m@w4)Sul(v&S744!7aiBl)Kg#Oh zdsztU=-K#!=rsUy3eO~RSAca1bR^!?XBmQQ@`4o($MQV6BS@RBKeg`p3om?rUGmDq zD^?s{$#+`KdGhMQ$|bigsmv`6&rC{pK6ZKcjgyl%($_z_r`N2RrZ3=|&db;b-~&Ca zo)E?L`Li$;Yut6xfp~!8 zW%om-E~0h0xZZLirdEXb`fskG`$4mGq_uM@%OTF2v&VrH3-+ypX)$Y&R>BAf@juR^ zn&*op1XTPK-0meKU6qy3?Ktne9X2RZq9Jl} zzI+2TICzSIn5d?ZIRC)6>*^I}vz z)4s}KyU?HzZloQngxHNwB@AAStzYb-uhfNXr{4G@0=^<$5>Zx^4E|aq$NW3o2Ea zCh8+_%>^c@n6?}cF5bKdYc5(ZHuE;4uV6N5Z8{1L9iecDI0jraQJFtd#Z zK>=e;3;*{zKtSIHE&9a>#Z?!^YFFs5t1eVz2q7Vws039~uxqMeky00KR7L2Nbp_Cf zXdj9>m=%s8x8;vx={^U|B=+3qE206#0a_miatP-kR;7jy|We&r#mi zwva`ZQ~NN;Sh8QnIOUnFE`@a9$$>)Fw@}D}m>qe{N%Sp4+*L=~Tg+lgnDT)1h*R+l zQkGr7o!_#*cYREZyID$AzA265l=J*#p62<`cK(46KLoT=z}@VFYnlg@K{>}3t+}gErk!QtdGV-+$#{=`@`FwI=Kwl=>)+yORcnT`)_z8@T%MhLj zjF68YR52GN7Yicm7*+&8 zxna^=TWg*qUiZVA5@CV;17Vqc-kD8u4+DW3BJ5$h7(;S8j(UAeY5?@4#H2KLf-#NU z5ZDCrL#AZAeYAqq2KGRT6D1JxVVLFsX!(+n_QN?M!qJnr6aeQ;A{q!YI=wy}{{@AG z0g%^36lG)-(N}X^K|x#r*4CecK~kZUB1yX-QjRzkMHMA)Os1CcwDIAk|Kf!|Jap&* z?<$_ zkF}3YBrtylgUrI^1N?^-u8?!oY{OyZt-lZxZx0 zpU)}f*C>V?E_(ckna*Yg)CMUBMdPFXJC_CF`at=W^3xZHykrn_p=a62AS%B3 zyBT&6i=AahgREbi4p2+nC-Dv7lVY3?|2AU9v%nYrCQjoleBw=mxW+mM{-ikPcTwYG z*|YQ_Tjl3q|2U?^eyotcv-PuJJ{EMCv0z@#I==VkW+})=Ly{euqoi~6?pL_;hxro{ zsk4kx_|(U;&~4@Ham0&)iNE2|V4Dsm1yV7XzH(*yvSs4;WTSTAmMjDopEkB#F?{jZWZ;&0ywLDIz@uBANU~0b|!p?17|@k^6Qxe z&|dQE9N+kMi{nGy;53N-*`5VS6TL9kB@E2hNuZaKB4Pf_QYgm&{n@287~X7#NpSea zMY@}EoZ+N}p~+}J4&3BFTlcL|gB-Tfi3JNhxRashBfL4_sxRBWpy7r*mYEqDa}DBYPw~Bl6P! zW#5>AbM!4EMR=~hTg7?u_x+JL?yUb{++fRdbYJgNlIOzUXR|yj(f&;l2ZV^{A{^(U z{Q{+V4rJCXRG{a=YVoWR4f65C2_H`&|M875^PBi{V3bSB^;}qV`{d7ugDg(OA)lfQ z-w1~@dB5kt=cZX__?%c>vCuu1`$*c3QI*fmGZ6zEGXx4f)QLp z+ivZ_v~q@Amvg?o&!}>m3yX>h+kJ^xx5;vO-OE)*v%E&Rh+lfCXU1kTnj^b1GTqDc z%iQOQIob5|6f^B3ck!JX_3>jqtQ?vXULFp zg->UYkMzyp!zL?Ehp&QyI0O7^0)3=L;Uf`$#FYLQ{K-h2mf&ral)xE!;oK+tlAqFu zSgb@R^ErF6`i<9VUFATa&d|?|em_N^l|6ex+%MqIqk`=2mTt1~lu7mP9YFq)JvFZ$ z$med^NH(16N%1ZLo&;krfmrg-@K?8G|+e_kQk=^wX*{KO3i+haq#fegi z^VT;h#X0>uS5I}O-IAQ5sK~0e*b-KIHXquv=g=NtC#~7Kg*UN2H%aS-c^TJ*%Z9)V z?C=5=D+FX@dC#m$4Uh|pw_;T?J2Kk1G&>T}4-3rDj*=-oElVprOTsWGYv+bZVh{*( zADPJ0VYZP3_+fqozg9nGY{1IJlLQ*^_fX`U#=isl>vmw0h=`#0WQ1=GKwhF4JjNOT+hED{w@PaP zT|q-lbYz^vSlX~i8x`v?mb?_)9un@}iSN*=Um%Gu+j?d80qK~t^Pv=@XJ62s6eHgp z1O}IU|2oLF8oiz&b4*+}FMGV!ryK$@$ikWc|NmUDz-{wj?eZy*09gF*K3`eNXMY5| z5ZgUHqfK~$%jSX0n^Q6Y5QX#o`bJy_)Is0SSSf#aTAtkRrpyC-k53T=z~uk(uLXQL z>QAM$(}FCuxFlGZscy%h7i6o2S7^}CHOLn%75)rZw7WHM-xS>X`FksT`=;jatoXC% zGilotwpPe)^kmF!u8{fgq&-<0tbP~f(?jvlf*4|4i0u>xzYyEkrNN8)Td?nia>4$6 zFVNRm|024@X;zh{ecJ&nwnKtLJ1T&(piWczEU#8sH_& z4eWrgjnty0vJ(H;aPvS?p~-U>FG_M@;V#+&Ar|YPSdtWMlSW=itH5I%5EJnM5(o~i zEqIFL)>{qFd6_W>+di9fj9))(n0=sRv~pdKy)sgIrX;e`;h73}s>G+8gc$fuvB-TQ zW(nJG^>3k>@5iOiYHURK=v;`b*=(e`t?QtGMGUCr8|@l9S`# z^_3xRx8X&!`3ry2vj+o+v6<*evHxwSs7#7CoMXdi=L$nhOU$Zs?Hd)Iov_r_S|;ZD zFNIxC5+f7m-T~w5{;V5es-@1)u^&droaNNa6)k#nnUAH(`r+!Q*TTXC29 z<&$`w*MoK;V(Z!6ojx>un>@cGA?4reDSY9_0C^C!Pr=yd+KanLeC?&pBs-<%{yina za({F1^;^ipeT=<(U|J0&$4L@IT}}`C6*}iO@hQ@tjEi-<%U$O9NKIsHoZT*+3JJ$RJa)s=Ng`|enb z(8wOZ+DzQXeWoSX1GDQY;m4<~ooH`s37&{&q&p0>DbbD{58|$yvBjnN`RcgnjM(Qo zN=jYM6nDrv;E^Fra2fnFir_Es42~>t282zsrXRXiSiw7m&qUkIH000XVM}*U_WE@w z!qkqkUK5HmRtpAvE#40S-6TC1KwE^7iH%yN^z zm&q%{hvP(=(Y>bw@7)k^PcXA>z(g{(#}#(;K|tzFAwNd0!wZ5XhZ!{8>wfDbhw2c4BSg! zJ}hS20FMtvJkW-cSHSkPHezKz@*L0oFg_xiAUbpZ23MFRJ~G*~X;agru!Aea`9A$p5s-C9n@+{ez8wAh6g=><0I!emjt|%{T06{pepA}aVab1Fivjugo_u+UOsme+>7P1Q zaEa}j5^;Y@vpN;aw7I#Ubp>rarO1eEIT? z4K7#1LrulmE_GUBV^{1In)=21dYXTV>ipg}N5Bth;HX~=RS)usf@IG{oSX-lIEF7k zTQxE+m@mzlp4;@T)v?wUca8ib*X3I4a^*XE?5@n5oXnt>7GB(`)1FstvsJIq=sGL% z^F4plR#$7Qu^+Ig$XJ9i60JT*KjIh*eS3N+iUx;Jd_FXhTk&uzcB+{_FY2`i7X|6l z^|7)2MT4&Emo3XIwpWH^nM|4O`b)*(9?{g-Yw9t^Fz|RkcpRP=G@l5`zt2sa5%N0& z_Q637Im4_Sd+U{ra#MB2R{UkuTI3DN#w$ClR({Z7+|^&%8S1pUm*hG69pxPrxmEJj zP5ryl9sQt}fcLOu^MAr$2&3Kp(1LSMZlw|nKoodfVVA?PgtkF@K7F9vg$BR$e4j7# z6!U96KjNLr>}<~<-j>6$+n@2A9cLyRybAfhO=w0bQ2MEQkY;#&nhOTzbSE zZ!Ex*j56_=L)xze#-ax{b>!>d;rBEi#l!V!NinImBDFOstbKchEL^v>BtJ1#5zXxh z^v!=$tSsxwiw$-ulM>vQTRpFAS-EI=ta18x6{StjDVCKs9nmb4Z~*^_&tA$%OT_bJ zjX`Gra;N9IosRP!qBOE_bi=7nMLD{K{}k&H=#*g6B`_4C{dQc@R?KfI84!-2G6~1= zER>1hg0H=|@D9{Rc8u$=Ng2xujf^+Gv2*8f&y(Nz4sB+5!1F*QznWhy*2msHalaqg z6WZA(?rFn{o?tn=&4Lm!E9^)EEIc2D2^Ewh_x7)Due@GXSow7~|H89dHcGOR${yxV z3Zx~_b+2Ft^@mY2uV zkw4i~T-@~YrozIe@HaDZ3p(F?)0*w*0vg_vhygS31@tDR6MYFr@$C~*+VK;OHn-em ziI2jAB^8C1c;RBzPLD!#J9qMb<49wp{e8c}xIY11p`kR80{`w|UBG!!oXfwHIABll zyS_ce`{wo*OS!E|;02owS)+GsU`H+$yn6$`i>@dwF0Ji0Z{1r`TvE5pj7OADo~+m> ze);fWzGt4X7eOOE^(;Noj3=KxpJ7^jPd|T;;w{qtMClo6)b~Bvti}ZTo^Dq9pKm_( zp;RAp!qf{8QYIVMvh!{N*E@e4L?G~mF6>md8(i4`3ExNECWIjyi%RyM5<~S7#;};E@Q^rFkV2^y^r4~p#NZ%BLP&U!A~`fJJSH?T zBt1PWF*G(JCNv>DBqSw-2OGIwrQ&)aF<7B8ghYjzLSl9%%0t6d5vnLfOh~9gEtg}% zXB1Z|)m#_E<*`b+N~?@kMTbO(7!wuxkVutA5vx?mwQ@mb;E@VNq!1bwB$I0ts!+8` zrwR|z2X8&^k`*g1Iq%85`wE)dxjrP+AS5Y-kYt4_DlSwJrO_)*QE`z5b*fSy9v_kr z7OG4LmdoWvF6b}{AyF<5iBoDK6P2-%`Y>~pAvj%W2uqAKC@rC(Ax33La7>dz5yGQo zP^@xgNQ^=q79FAqPKX0$k)hE_T}Ze>qmTvbg(#&mk{g18lv;&Si7kAp2)v&t-l)(k zB2}tTxi(BLk5TYoJx0v4&m**l%gYZDBj6n$qtIT`-+K18?;Nomykkwej(1Yr3WD}CqT>}mzQv4s92Se$Ytq>TsBu7)>Jq;rmUnl<{)$-eILoy*uLtfW*Y zh&hBszVl$wd0&xY{79G`Amz(Hf$Ro zSvx#wN?(&@G8 z0X0pmO{4+yRX5d*O%9K&F|~~jGI4-wL6CJod>lUq*KS(Zhm-jv?*zC3-!yH)kf#Es zpadO!lkO#A&h%iS5!xAN zd9-hQd~Azp?fBT_;KasFgQkhT(FxP!whe+q-{>}Q z&VQnz*_qYie+kGOmFM($7pOgic_Q6C?wd!Fymg>3@c?lKSq&3reN>dfah&_-p^G)J zX8djw`Gz#c9OUMC5Qvw~q+LkE1kV5beocTO>4D9_Z$wno{XTx1vs1*QwXBMD;7FE; zlvAK0syst?)U5kI{E zymcXL)jG(_?clJD;5yP(o4|XUA!jGqR=5z`*miai+rge-cd$$FUfiARQua6Y5j(>E zi1+lq&mO>Ad{3}Hv!AfPu>WEA;2qNMvbW%o|A`%EZ?JdR&FmreFnf$W&Yoe9ut)L! z-)Z(VyPW+1>$jh=C)rUh<3T)_%eew8;1I6jp*)O-vn$zc>;`t2-NbIh`=!6ju4T8d z@38}Tf9iMGkJ(e~=j?Zi(f)}I(28MGgX3c(qeFw^BV*&CW21w!=aXAzuZK?#j}Ok? z2^$*QH14k!HZ-z%_D<-;$X5T2@QK0AgQK&*MD6MS6^84xje-_$8XqHmq)ah#J(AD= z()tGxw8#6?%WtOLZPI(xiEkrNux|i=0&2=|2~SpwzoeEb@Mks_^L>+};A*Dk)!{vRS6?;#z67LLpNWs}oluJ>jQFfv1M!5`SkN3D(|5em~8};7@lt}NZOpTI) zUo|MTD0L|HDD7y|fzpZdE*zKOxD>~39GBs^9OZnJO(;8Y?NXF!lwByhQ7%K-gPv~3 z@eY(bQSL&y8|Aww_n>?a?ANd3CvCcvy;G# zusR8>P6Dfw!0IHhIti=@gOkAEBrtdz7`zP(-WD-<9~it33}!&r8DKC23}%4A3^14h z1~b5526USN-DW_y8PIJ8bejR)P6C4&U@!v=W`MyAFqi=bGr(X57|Z~J8DKC23}%4A z3^14h2JZud_kqFtB8R*R9UBZ?sX_^Zt_;Vq631Q?4*eP|-pPR8v_n^trYwU#ByBMb zO-4HIR>Q<6&^v?5AVs(0!yzR< z$CY2=!yzrNLDRm0j||r`#Fm2CQh=R_guJNnfwsmc*axKs9*F`TNj)T%e34Mm6H!6R zI&q~79~FF(B{*M-j|#p?H_n&gqk@mJ9Qcu+5(GbG6~52MM+Ki?6Rz)s6bJjTydAb) z0gL - - view-toggle-inactive-centre + + Slice 1 Created with Sketch (http://www.bohemiancoding.com/sketch) - - - + + + - - + + + + + + \ No newline at end of file diff --git a/data/images/view-toggle-inactive-right.svg b/data/images/view-toggle-inactive-right.svg index 2000ece024..74e111daf2 100644 --- a/data/images/view-toggle-inactive-right.svg +++ b/data/images/view-toggle-inactive-right.svg @@ -1,14 +1,18 @@ - - view-toggle-inactive-right + + Slice 1 Created with Sketch (http://www.bohemiancoding.com/sketch) - - - + + + - - + + + + + + \ No newline at end of file diff --git a/data/images/view-toggle-pressed-left.svg b/data/images/view-toggle-pressed-left.svg index 55734f01f1..dc3cf81373 100644 --- a/data/images/view-toggle-pressed-left.svg +++ b/data/images/view-toggle-pressed-left.svg @@ -1,16 +1,18 @@ - - view-toggle-pressed-left + + Slice 1 Created with Sketch (http://www.bohemiancoding.com/sketch) - - - + + + - - - - + + + + + + \ No newline at end of file diff --git a/resources.qrc b/resources.qrc index c6f3ba2df4..131291a629 100644 --- a/resources.qrc +++ b/resources.qrc @@ -187,5 +187,6 @@ data/fonts/TitilliumWeb-Bold.ttf data/fonts/TitilliumWeb-Regular.ttf data/fonts/FaunaOne-Regular.ttf + data/fonts/PathwayGothicOne-Regular.ttf diff --git a/src/libtomahawk/widgets/BasicHeader.cpp b/src/libtomahawk/widgets/BasicHeader.cpp index be6c41a811..bdc206b5ee 100644 --- a/src/libtomahawk/widgets/BasicHeader.cpp +++ b/src/libtomahawk/widgets/BasicHeader.cpp @@ -67,13 +67,16 @@ BasicHeader::BasicHeader( QWidget* parent ) m_descriptionLabel->setPalette( pal ); QFont font = m_captionLabel->font(); - font.setPointSize( TomahawkUtils::defaultFontSize() + 4 ); + + font.setPointSize( TomahawkUtils::defaultFontSize() + 10 ); font.setBold( true ); + font.setFamily( "Titillium Web" ); + m_captionLabel->setFont( font ); m_captionLabel->setElideMode( Qt::ElideRight ); m_captionLabel->setAlignment( Qt::AlignTop | Qt::AlignLeft ); - font.setPointSize( TomahawkUtils::defaultFontSize() + 1 ); + font.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); font.setBold( false ); m_descriptionLabel->setFont( font ); m_descriptionLabel->setAlignment( Qt::AlignTop | Qt::AlignLeft ); diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index f6ab17bcfc..5baecf1ede 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -122,7 +122,7 @@ Dashboard::Dashboard( QWidget* parent ) { QFont f = ui->label->font(); - f.setFamily( "Fauna One" ); + f.setFamily( "Pathway Gothic One" ); QPalette p = ui->label->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); @@ -135,7 +135,7 @@ Dashboard::Dashboard( QWidget* parent ) { QFont f = ui->playlistLabel->font(); - f.setFamily( "Fauna One" ); + f.setFamily( "Pathway Gothic One" ); QPalette p = ui->playlistLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp index d7021d84a8..9ca56c6dc6 100644 --- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp @@ -171,7 +171,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->label->font(); - f.setFamily( "Fauna One" ); + f.setFamily( "Pathway Gothic One" ); QPalette p = ui->label->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); @@ -184,8 +184,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* { QFont f = ui->albumLabel->font(); - f.setFamily( "Fauna One" ); - + f.setFamily( "Pathway Gothic One" ); + QPalette p = ui->albumLabel->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::HEADER_TEXT ); diff --git a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp index 9eec9f44a4..611578ebd1 100644 --- a/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/TrackInfoWidget.cpp @@ -118,7 +118,7 @@ TrackInfoWidget::TrackInfoWidget( const Tomahawk::query_ptr& query, QWidget* par { QFont f = ui->label->font(); - f.setFamily( "Fauna One" ); + f.setFamily( "Pathway Gothic One" ); QPalette p = ui->label->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); From 47fc81b8c07f5a6c9f8351e62ebfd33047ee5387 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Mon, 17 Jun 2013 13:50:01 -0400 Subject: [PATCH 476/565] Don't forget to replace Fauna font on Album pages too. --- src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp index 3398e8add2..ea5196020d 100644 --- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp +++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp @@ -126,7 +126,7 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par { QFont f = ui->label->font(); - f.setFamily( "Fauna One" ); + f.setFamily( "Pathway Gothic One" ); QPalette p = ui->label->palette(); p.setColor( QPalette::Foreground, TomahawkStyle::PAGE_CAPTION ); From e0bff794abdab1130efd6064dcd3685fc45d6cd1 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 18 Jun 2013 00:02:25 +0200 Subject: [PATCH 477/565] Fix slot/signal namespacing --- src/libtomahawk/network/DbSyncConnection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/DbSyncConnection.cpp b/src/libtomahawk/network/DbSyncConnection.cpp index b2b49369b6..9771fc6f91 100644 --- a/src/libtomahawk/network/DbSyncConnection.cpp +++ b/src/libtomahawk/network/DbSyncConnection.cpp @@ -54,8 +54,9 @@ DBSyncConnection::DBSyncConnection( Servent* s, const source_ptr& src ) { qDebug() << Q_FUNC_INFO << src->id() << thread(); + // Be aware of namespaces in these signals/slots! connect( this, SIGNAL( stateChanged( Tomahawk::DBSyncConnectionState, Tomahawk::DBSyncConnectionState, QString ) ), - m_source.data(), SLOT( onStateChanged( Tomahawk::DBSyncConnectionState, Tomahawk::DBSyncConnectionState, QString ) ) ); + m_source.data(), SLOT( onStateChanged( DBSyncConnectionState, DBSyncConnectionState, QString ) ) ); connect( m_source.data(), SIGNAL( commandsFinished() ), this, SLOT( lastOpApplied() ) ); From 0db80a9a61e2d7450dee05134273df6181a089d9 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Tue, 18 Jun 2013 02:16:44 +0200 Subject: [PATCH 478/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 32 ++++++++++++------------ lang/tomahawk_bg.ts | 32 ++++++++++++------------ lang/tomahawk_bn_IN.ts | 32 ++++++++++++------------ lang/tomahawk_ca.ts | 32 ++++++++++++------------ lang/tomahawk_ca@valencia.ts | 32 ++++++++++++------------ lang/tomahawk_cs.ts | 48 ++++++++++++++++++------------------ lang/tomahawk_da.ts | 32 ++++++++++++------------ lang/tomahawk_de.ts | 32 ++++++++++++------------ lang/tomahawk_el.ts | 32 ++++++++++++------------ lang/tomahawk_en.ts | 32 ++++++++++++------------ lang/tomahawk_es.ts | 32 ++++++++++++------------ lang/tomahawk_fi.ts | 32 ++++++++++++------------ lang/tomahawk_fr.ts | 32 ++++++++++++------------ lang/tomahawk_gl.ts | 32 ++++++++++++------------ lang/tomahawk_hi_IN.ts | 32 ++++++++++++------------ lang/tomahawk_hu.ts | 32 ++++++++++++------------ lang/tomahawk_id.ts | 32 ++++++++++++------------ lang/tomahawk_it.ts | 32 ++++++++++++------------ lang/tomahawk_ja.ts | 32 ++++++++++++------------ lang/tomahawk_lt.ts | 32 ++++++++++++------------ lang/tomahawk_pl.ts | 32 ++++++++++++------------ lang/tomahawk_pt_BR.ts | 32 ++++++++++++------------ lang/tomahawk_ru.ts | 32 ++++++++++++------------ lang/tomahawk_sv.ts | 32 ++++++++++++------------ lang/tomahawk_tr.ts | 32 ++++++++++++------------ lang/tomahawk_zh_CN.ts | 32 ++++++++++++------------ lang/tomahawk_zh_TW.ts | 32 ++++++++++++------------ 27 files changed, 440 insertions(+), 440 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index a1f711355b..ec5f2054c5 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -1791,7 +1791,7 @@ Password الكل - + Some changed settings will not take effect until Tomahawk is restarted بعض الإعدادات التي تم تغييرها لن تصبح نافذة المفعول حتى يتم إعادة تشغيل توماهوك @@ -1821,32 +1821,32 @@ Password تكوين إعدادات توماهوك المتطورة، بما في ذلك إعدادات شبكة الاتصال، وتفاعل المتصفح وما إلى ذلك. - + Install resolver from file تثبيت محلل من ملف - + Tomahawk Resolvers (*.axe *.js);;All files (*) محللي توماهوك (*.axe *.js);;جميع الملفات (*) - + Resolver installation from file %1 failed. تثبيت المحلل من الملف %1 فشل. - + Delete all Access Control entries? حذف كافة بيانات التحكم بالوصول؟ - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. هل فعلا تريد حذف جميع بيانات التحكم بالوصول؟ سوف يطلب منك اتخاذ القرار مجددا لكل ند على اتصال به. - + Information معلومات @@ -3704,43 +3704,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 448196ba8e..bb837ddaed 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -1800,7 +1800,7 @@ Password Всички - + Some changed settings will not take effect until Tomahawk is restarted Някои промени няма да имат ефект, докато програмата не бъде рестартирана. @@ -1830,33 +1830,33 @@ Password Разширени настройки на Tomahawk, включващи настройки на мрежова свързаност, взаимодействие с браузъри и други подобни. - + Install resolver from file Инсталирай услуги за търсене от файл - + Tomahawk Resolvers (*.axe *.js);;All files (*) Модули за извличане на Tomahawk (*.axe *.js);; Всички (*) - + Resolver installation from file %1 failed. Инсталирането на извличащ модул от файл %1 не бе успешно. - + Delete all Access Control entries? Изтриване на всички данни за достъп? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Наистина ли желаеш да изтриеш всички данни за достъп? Ще бъдеш питан отново за даване на достъп за всяка връзка. - + Information Информация @@ -3720,43 +3720,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 изпълнения) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index f33bdfd959..042c2a2419 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -1787,7 +1787,7 @@ Password - + Some changed settings will not take effect until Tomahawk is restarted @@ -1817,32 +1817,32 @@ Password - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index c8691d00c6..1b3c71e67e 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -1788,7 +1788,7 @@ Password Tot - + Some changed settings will not take effect until Tomahawk is restarted Alguns paràmetres no tindran efecte fins que no reinicieu Tomahawk @@ -1818,32 +1818,32 @@ Password Configura els paràmetres avançats del Tomahawk, incloent els paràmetres de connexió de xarxa, interacció del navegador i més. - + Install resolver from file Instal·la un Resolver des d'un fitxer - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Voleu eliminar totes les entrades d'Access Control? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Realment voleu eliminar totes les entrades de l'Access Control? Se us demanarà per una decisió una altra vegada per cada recurs al que us connecteu. - + Information Informació @@ -3704,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 960cbe81f1..ba40933fb0 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -1788,7 +1788,7 @@ Password Tot - + Some changed settings will not take effect until Tomahawk is restarted Alguns paràmetres no tindran efecte fins que no reinicieu Tomahawk @@ -1818,32 +1818,32 @@ Password Configura els paràmetres avançats del Tomahawk, incloent els paràmetres de connexió de xarxa, interacció del navegador i més. - + Install resolver from file Instal·la un Resolver des d'un fitxer - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Voleu eliminar totes les entrades d'Access Control? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Realment voleu eliminar totes les entrades de l'Access Control? Se vos demanarà per una decisió una altra vegada per cada recurs al que vos connecteu. - + Information Informació @@ -3704,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index d802f9fb79..c4cf837805 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -481,7 +481,7 @@ se s vámi spojil? Duration - Doba trvání + Délka @@ -496,7 +496,7 @@ se s vámi spojil? Duration: - Doba trvání: + Délka: @@ -1122,7 +1122,7 @@ heslo Duration: - Doba trvání: + Délka: @@ -1284,7 +1284,7 @@ heslo Duration - Doba trvání + Délka @@ -1658,17 +1658,17 @@ heslo Open Queue - Otevřít čekací řadu + Otevřít řadu Open Queue - %n item(s) - Otevřít čekací řadu - jedna píseňOtevřít čekací řadu - %n písněOtevřít čekací řadu - %n písní + Otevřít řadu - jedna píseňOtevřít řadu - %n písněOtevřít řadu - %n písní Close Queue - Zavřít čekací řadu + Zavřít řadu @@ -1790,7 +1790,7 @@ heslo Vše - + Some changed settings will not take effect until Tomahawk is restarted Některá změněná nastavení se neprojeví, dokud Tomahawk nebude spuštěn znovu @@ -1820,32 +1820,32 @@ heslo Nastavit pokročilá nastavení Tomahawku, jako jsou volby pro síť, interakci prohlížeče a další. - + Install resolver from file Instalovat řešitele ze souboru - + Tomahawk Resolvers (*.axe *.js);;All files (*) Řešitelé Tomahawk (*.axe *.js);;Všechny soubory (*) - + Resolver installation from file %1 failed. Nepodařilo se nainstalovat řešitele ze souboru %1. - + Delete all Access Control entries? Smazat všechna udělená přístupová oprávnění? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Opravdu chcete smazat všechna nastavení přístupových práv? Budete znovu dotazován na nastavení přístupových oprávnění pro každé spojení. - + Information Informace @@ -3271,7 +3271,7 @@ Zkuste vyladit filtry pro nové písně. Duration - Doba trvání + Délka @@ -3704,43 +3704,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index aec353f928..28d8f6bd16 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -1788,7 +1788,7 @@ Password Alle - + Some changed settings will not take effect until Tomahawk is restarted @@ -1818,32 +1818,32 @@ Password - + Install resolver from file Installer resolver fra fil - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Information @@ -3692,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 632f2463eb..8c0947adf3 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -1790,7 +1790,7 @@ Passwort Alle - + Some changed settings will not take effect until Tomahawk is restarted Einige geänderte Einstellungen haben keinen Effekt bis zum nächsten Neustart @@ -1820,32 +1820,32 @@ Passwort Konfiguriere Tomahawk's erweiterte Einstellungen, wie Netzwerk Optionen, Browser Interaktion und mehr. - + Install resolver from file Installiere Resolver Datei - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;Alle Dateien (*) - + Resolver installation from file %1 failed. Die Resolver Installation von der Datei %1 ist fehlgeschlagen - + Delete all Access Control entries? Alle erteilten Zugriffsrechte löschen? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Willst du wirklich alle Zugriffseinstellungen zurücksetzen? Du wirst für alle Verbindungen erneut nach Zugriffseinstellungen gefragt werden. - + Information Information @@ -3699,43 +3699,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index e043fc8189..bf8fb1eaf5 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -1789,7 +1789,7 @@ Password Όλα - + Some changed settings will not take effect until Tomahawk is restarted Μερικές αλλαγμένες ρυθμίσεις δεν θα εφαρμοστούν μέχρι το Tomahawk να επανεκκινηθεί. @@ -1819,32 +1819,32 @@ Password Ρυθμισει των προσαρμοσμενων ρυθμισεων, συμπεριλαμβανομενων και των ρυθμισεων δικτου, περιηγητη και αλλων. - + Install resolver from file Εγκατάσταση επιλυτή από αρχείο - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;Όλα τα αρχεία (*) - + Resolver installation from file %1 failed. Εγκατάσταση Resolver από αρχείο %1 απέτυχε. - + Delete all Access Control entries? Διαγραφή όλων των καταχωρήσεων Ελέγχου Πρόσβασης? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Θέλετε πραγματικά να διαγράψετε όλες τις καταχωρήσεις ελέγχου πρόσβασης? Θα ερωτηθειτε ξανα για κάθε κόμβο που θα συνδεθείτε. - + Information Πληροφορίες @@ -3705,43 +3705,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index 2746611d82..c819bd4dd3 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -1790,7 +1790,7 @@ Password All - + Some changed settings will not take effect until Tomahawk is restarted Some changed settings will not take effect until Tomahawk is restarted @@ -1820,32 +1820,32 @@ Password Configure Tomahawk's advanced settings, including network connectivity settings, browser interaction and more. - + Install resolver from file Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. Resolver installation from file %1 failed. - + Delete all Access Control entries? Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Information @@ -3707,43 +3707,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 3385af82f0..8695f16eec 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -1789,7 +1789,7 @@ Password Todo - + Some changed settings will not take effect until Tomahawk is restarted Algunos cambios no surtirán efecto hasta reiniciar Tomahawk @@ -1819,32 +1819,32 @@ Password Configurar las preferencias avanzadas de Tomahawk, incluyendo la conectividad de red, del navegador y más. - + Install resolver from file Instalar un servicio desde un fichero - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? ¿Eliminar todas las fuentes del Control de acceso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. ¿De verdad quiere borrar todo el Control de acceso de las fuentes? Será preguntado por cada fuente a la que se conecte. - + Information Información @@ -3705,43 +3705,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 66f21616ef..759d3f0f59 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -1790,7 +1790,7 @@ salasana Kaikki - + Some changed settings will not take effect until Tomahawk is restarted Jotkin asetusmuutokset tulevat voimaan vasta, kun Tomahawk käynnistetään uudelleen. @@ -1820,32 +1820,32 @@ salasana Aseta Tomahawkin lisäasetuksia, joihin kuuluu verkkoyhteysasetukset, selainyhteys ja muuta. - + Install resolver from file Asenna selvitin tiedostosta - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk-selvittimet (*.axe *.js);;Kaikki tiedostot (*) - + Resolver installation from file %1 failed. Selvittimen asennus tiedostosta %1 epäonnistui. - + Delete all Access Control entries? Poistetaanko kaikki pääsynvalvontatietueet? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Haluatko varmasti poistaa kaikki pääsynvalvontatietueet? Sinulta tullaan kysymään päätös uudelleen jokaisen sellaisen vertaisen kohdalla, johon yhdistät. - + Information Tiedoksi @@ -3710,43 +3710,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index aa9af929ff..19c897b6fd 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -1789,7 +1789,7 @@ Password Tous - + Some changed settings will not take effect until Tomahawk is restarted Certaines modifications ne prendront effet qu'au prochain démarrage de Tomahawk @@ -1819,32 +1819,32 @@ Password Configurer les paramètres avancés de Tomahawk dont les paramètres de connectivité réseau, les interactions avec le navigateur et autres. - + Install resolver from file Installer un script de résolution depuis un fichier - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Supprimer toutes les entrées de Contrôle d'accès? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Voulez-vous vraiment supprimer toutes les entrées de contrôle d'accès ? On vous demandera de nouveau votre autorisation pour toutes les nouvelles connexions. - + Information Information @@ -3702,43 +3702,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index fb6d016c21..176fc887d4 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -1788,7 +1788,7 @@ Password Todo - + Some changed settings will not take effect until Tomahawk is restarted Algunhas das configuracións que se cambiaron non van a ter efecto ata que se reinicie Tomahawk @@ -1818,32 +1818,32 @@ Password Configurar as propiedades avanzadas de Tomahawk, incluíndo a conectividade a rede, a interacción co navegador e outras. - + Install resolver from file Instalar un resolvedor dende un ficheiro - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Queres borrar tódalas entradas de control de acceso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Seguro que queres borrar tódalas entradas de control de acceso? Preguntaráseche de novo para que o confirmes cada vez que te conectes a un parceiro. - + Information Información @@ -3704,43 +3704,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 524e3a269f..d9e9d8728d 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -1787,7 +1787,7 @@ Password सभी - + Some changed settings will not take effect until Tomahawk is restarted @@ -1817,32 +1817,32 @@ Password - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information सूचना @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 50c7a3d8de..44e0a2c064 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -1787,7 +1787,7 @@ Password Mind - + Some changed settings will not take effect until Tomahawk is restarted @@ -1817,32 +1817,32 @@ Password - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Információ @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index ab136bd28e..c8845e332b 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -1787,7 +1787,7 @@ Password - + Some changed settings will not take effect until Tomahawk is restarted @@ -1817,32 +1817,32 @@ Password - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index b4cb11785c..39a95b7f93 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -1788,7 +1788,7 @@ temporanea Tutte - + Some changed settings will not take effect until Tomahawk is restarted Alcune modifiche non avranno effetto fino al riavvio di Tomahawk @@ -1818,32 +1818,32 @@ temporanea Configura le impostazioni avanzate di Tomahawk, compresa la connessione ad internet, interazione col browser ed altro ancora. - + Install resolver from file Installa i resolver da file - + Tomahawk Resolvers (*.axe *.js);;All files (*) Risolutore Tomahawk (*.axe *.js);;Tutti i file (*) - + Resolver installation from file %1 failed. L'installazione del risolutore dal file %1 è fallita. - + Delete all Access Control entries? Cancellare tutte le voci di controllo di accesso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Vuoi proprio cancellare tutte le voci di controllo accesso? Ti sarà richiesto di decidere ancora tutte le volte che un peer si connetterà con te. - + Information Informazione @@ -3692,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 9a74dedb34..db23d23957 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -1789,7 +1789,7 @@ Password すべて - + Some changed settings will not take effect until Tomahawk is restarted Tomahawkを再起動すると設定変更が反映されます @@ -1819,32 +1819,32 @@ Password ネットワーク接続設定、又はブラウザー設定等の詳細設定を調整する。 - + Install resolver from file ファイルからリゾルバをインストールする - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? 全てのアクセス制御のエントリーを削除しますか? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. 本当に全てのアクセス制御のエントリーを削除しますか?ピア接続に対して、改めて同意を求めます。 - + Information 情報 @@ -3702,43 +3702,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index b40bdd4445..24f023b7dc 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -1787,7 +1787,7 @@ Password Visi - + Some changed settings will not take effect until Tomahawk is restarted @@ -1817,32 +1817,32 @@ Password - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Informacija @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 4cf18ff78d..a2905e4b47 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -1789,7 +1789,7 @@ Password Wszystkie - + Some changed settings will not take effect until Tomahawk is restarted Niektóre zmiany zaczną działać po restarcie Tomahawka @@ -1819,32 +1819,32 @@ Password - + Install resolver from file Zainstaluj usługę z pliku - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information Informacja @@ -3699,43 +3699,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 343d16e181..397728096a 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -1789,7 +1789,7 @@ Password Todos - + Some changed settings will not take effect until Tomahawk is restarted Algumas configurações não terão efeito até que o Tomahawk seja reiniciado @@ -1819,32 +1819,32 @@ Password Configure as configurações avançadas do Tomahawk, incluindo as configurações de conectividade de rede, interação com o navegador e mais. - + Install resolver from file Instalar resolvedor via arquivo - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Excluir todas as entradas de controle de acesso? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Você realmente quer apagar todas as entradas de controle de acesso? Você será solicitado a tomar uma decisão para cada ponto a que você se conectar. - + Information Informação @@ -3699,43 +3699,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 6f3fac4cb8..2516b90f06 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -1792,7 +1792,7 @@ Password Все - + Some changed settings will not take effect until Tomahawk is restarted Некоторые измененные настройки не вступят в силу до перезапуска Tomahawk @@ -1822,32 +1822,32 @@ Password Настройка дополнительных возможносте Tomahawk в том числе подключения к сети, интеграцию с браузером, другие. - + Install resolver from file Установить resolver из файла - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? Удаление всех записей контроля доступа? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Вы действительно хотите удалить все записи Access Control? Вас заново спросят о каждом соединении к которым вы были подключены. - + Information Инофрмация @@ -3706,43 +3706,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 79c7460d48..e5ce13c1bd 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -1790,7 +1790,7 @@ Password Alla - + Some changed settings will not take effect until Tomahawk is restarted Några inställningar kommer inte träda i kraft förrän Tomahawk har starts om @@ -1820,32 +1820,32 @@ Password Konfigurera Tomahawks avancerade inställningar, inklusive nätverkets anslutningsinställningar, webbläsarinteraktion m.m - + Install resolver from file Installera resolver från fil - + Tomahawk Resolvers (*.axe *.js);;All files (*) Tomahawk Resolvers (*.axe *.js);;Alla filer (*) - + Resolver installation from file %1 failed. Resolver-installationen från filen %1 misslyckades - + Delete all Access Control entries? Ta bort alla åtkomstkontrollsposter - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. Vill du verkligen ta bort alla åtkomstkontrollsposter? Du kommer att bli förfrågad igen för varje nod du försöker ansluta till. - + Information Information @@ -3705,43 +3705,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 732c95037b..57acc86eeb 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -1787,7 +1787,7 @@ Password - + Some changed settings will not take effect until Tomahawk is restarted @@ -1817,32 +1817,32 @@ Password - + Install resolver from file - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 9b59086339..3c8ab39520 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -1788,7 +1788,7 @@ Password 所有 - + Some changed settings will not take effect until Tomahawk is restarted 一些设置改动将在 Tomahawk 下次启动时生效。 @@ -1818,32 +1818,32 @@ Password 配置 Tomahawk 的高级设置,包括网络链接设置,浏览交互设置等等。 - + Install resolver from file 从文件安装解析器 - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? 删除所有的访问控制项? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. 你真的想删除所有的访问控制项吗?将在对每个连接的客户端操作后再次询问。 - + Information 信息 @@ -3700,43 +3700,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 39d705c6b7..bce700b1f0 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -1787,7 +1787,7 @@ Password 所有 - + Some changed settings will not take effect until Tomahawk is restarted @@ -1817,32 +1817,32 @@ Password - + Install resolver from file 從檔案安裝解析器 - + Tomahawk Resolvers (*.axe *.js);;All files (*) - + Resolver installation from file %1 failed. - + Delete all Access Control entries? - + Do you really want to delete all Access Control entries? You will be asked for a decision again for each peer that you connect to. - + Information 資訊 @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline From e48b4096b932427b67a0de708a143ce7d1cbdf23 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Wed, 19 Jun 2013 02:17:21 +0200 Subject: [PATCH 479/565] Automatic merge of Transifex translations --- lang/tomahawk_cs.ts | 12 ++++++------ lang/tomahawk_fi.ts | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index c4cf837805..eea10521f5 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -340,7 +340,7 @@ se s vámi spojil? YOUR ARTIST RANK - + POŘADÍ VAŠEHO UMĚLCE @@ -1754,22 +1754,22 @@ heslo Tracks - + Skladby Artists - + Umělci Albums - + Alba Sorry, we could not find any tracks! - + Promiňte, nepodařilo se najít žádné skladby! @@ -4074,7 +4074,7 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: YOUR SONG RANK - + POŘADÍ VAŠÍ PÍSNĚ diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 759d3f0f59..82296f1c7f 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -476,17 +476,17 @@ yhdistää ja toistaa sinulta virtaa? Year - + Vuosi Duration - + Kesto Bitrate - + Bittinopeus @@ -496,17 +496,17 @@ yhdistää ja toistaa sinulta virtaa? Duration: - + Kesto: Bitrate: - + Bittinopeus: Year: - + Vuosi: @@ -597,7 +597,7 @@ yhdistää ja toistaa sinulta virtaa? Recent Additions - + Viimeisimmät lisäykset @@ -607,7 +607,7 @@ yhdistää ja toistaa sinulta virtaa? Dashboard - + Kojelauta @@ -780,7 +780,7 @@ yhdistää ja toistaa sinulta virtaa? This playlist is currently empty. - + Tämä soittolista on parhaillaan tyhjä. From 277edfe8ca1a9480ff12859a1e66e0cca6b4395b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 09:30:21 +0200 Subject: [PATCH 480/565] * Don't reset the resolver's icon if we couldn't retrieve one in ResolverAccount. --- src/libtomahawk/accounts/ResolverAccount.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/accounts/ResolverAccount.cpp b/src/libtomahawk/accounts/ResolverAccount.cpp index 9e8cc1edf1..83204aca70 100644 --- a/src/libtomahawk/accounts/ResolverAccount.cpp +++ b/src/libtomahawk/accounts/ResolverAccount.cpp @@ -552,9 +552,9 @@ AtticaResolverAccount::loadIcon() if ( m_resolver.isNull() ) return; - m_icon = AtticaManager::instance()->iconForResolver( AtticaManager::instance()->resolverForId( m_atticaId ) ); - m_resolver.data()->setIcon( m_icon ); + if ( !m_icon.isNull() ) + m_resolver.data()->setIcon( m_icon ); } From 21beba0881ac53d365b4b6269fcf130639dade9b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 09:31:03 +0200 Subject: [PATCH 481/565] * Style fixes. --- src/libtomahawk/Result.cpp | 8 +++++++- src/libtomahawk/widgets/WhatsHotWidget.cpp | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/Result.cpp b/src/libtomahawk/Result.cpp index e99c815249..c290305cf3 100644 --- a/src/libtomahawk/Result.cpp +++ b/src/libtomahawk/Result.cpp @@ -429,36 +429,42 @@ Result::sourceIcon( TomahawkUtils::ImageMode style, const QSize& desiredSize ) c } } + unsigned int Result::bitrate() const { return m_bitrate; } + unsigned int Result::size() const { return m_size; } + unsigned int Result::modificationTime() const { return m_modtime; } + void Result::setScore( float score ) { m_score = score; } + void -Result::setFileId(unsigned int id) +Result::setFileId( unsigned int id ) { m_fileId = id; } + Tomahawk::Resolver* Result::resolvedBy() const { diff --git a/src/libtomahawk/widgets/WhatsHotWidget.cpp b/src/libtomahawk/widgets/WhatsHotWidget.cpp index 419003ea40..3c012c60fc 100644 --- a/src/libtomahawk/widgets/WhatsHotWidget.cpp +++ b/src/libtomahawk/widgets/WhatsHotWidget.cpp @@ -93,6 +93,8 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) ui->artistsViewLeft->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); ui->artistsViewLeft->header()->setVisible( true ); +// ui->albumsView->setStyleSheet( QString( "QListView { background-color: black; }" ) ); + m_workerThread = new QThread( this ); m_workerThread->start(); @@ -117,7 +119,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent ) ui->stackLeft->setCurrentIndex( 2 ); m_spinner = new AnimatedSpinner( ui->albumsView ); m_spinner->fadeIn(); - } From 7a2d1cadecf345a417c00fea5b54b0add1904248 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 09:31:31 +0200 Subject: [PATCH 482/565] * Make sure JSResolver always has at least the default resolver icon set. --- src/libtomahawk/resolvers/JSResolver.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 9ea63cdf4c..c72ca7ba9a 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -513,7 +513,7 @@ JSResolver::init() bool compressed = m.value( "compressed", "false" ).toString() == "true"; QByteArray icoData = m.value( "icon" ).toByteArray(); - if( compressed ) + if ( compressed ) icoData = qUncompress( QByteArray::fromBase64( icoData ) ); else icoData = QByteArray::fromBase64( icoData ); @@ -533,6 +533,11 @@ JSResolver::init() QString iconPath = QFileInfo( filePath() ).path() + "/" + m.value( "icon" ).toString(); success = m_icon.load( iconPath ); } + // if we still couldn't load the cover, set the default resolver icon + if ( m_icon.isNull() ) + { + m_icon = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultResolver, TomahawkUtils::Original, QSize( 128, 128 ) ); + } // load config widget and apply settings loadUi(); From 42c03fd9ba9d8685ed2d3fe23c71a590b0a0546d Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 09:50:23 +0200 Subject: [PATCH 483/565] * Don't emit Database::ready() just because the FuzzyIndex became ready. --- src/libtomahawk/database/Database.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/database/Database.cpp b/src/libtomahawk/database/Database.cpp index 1d010c8fb2..70761869cb 100644 --- a/src/libtomahawk/database/Database.cpp +++ b/src/libtomahawk/database/Database.cpp @@ -24,6 +24,7 @@ #include "DatabaseWorker.h" #include "IdThreadWorker.h" #include "utils/Logger.h" +#include #define DEFAULT_WORKER_THREADS 4 #define MAX_WORKER_THREADS 16 @@ -56,7 +57,6 @@ Database::Database( const QString& dbname, QObject* parent ) connect( m_impl, SIGNAL( indexReady() ), SLOT( markAsReady() ) ); connect( m_impl, SIGNAL( indexReady() ), SIGNAL( indexReady() ) ); - connect( m_impl, SIGNAL( indexReady() ), SIGNAL( ready() ) ); Q_ASSERT( m_workerRW ); m_workerRW.data()->start(); @@ -196,6 +196,10 @@ Database::impl() void Database::markAsReady() { + if ( m_ready ) + return; + tLog() << Q_FUNC_INFO << "Database is ready now!"; m_ready = true; + emit ready(); } From 776b1c9f2eced175455b3c1adcf717325e79cf16 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 09:50:52 +0200 Subject: [PATCH 484/565] * Pipeline only becomes ready when Database is entirely finished with init. --- src/libtomahawk/Pipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/Pipeline.cpp b/src/libtomahawk/Pipeline.cpp index 2f43262036..7a8b5f66db 100644 --- a/src/libtomahawk/Pipeline.cpp +++ b/src/libtomahawk/Pipeline.cpp @@ -87,7 +87,7 @@ Pipeline::~Pipeline() void Pipeline::databaseReady() { - connect( Database::instance(), SIGNAL( indexReady() ), this, SLOT( start() ), Qt::QueuedConnection ); + connect( Database::instance(), SIGNAL( ready() ), this, SLOT( start() ), Qt::QueuedConnection ); Database::instance()->loadIndex(); } From 8e48956c87b8516a769159d4ce0418fada22009b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 10:00:53 +0200 Subject: [PATCH 485/565] * Result now always returns the actual score regardless of the online state. --- src/libtomahawk/Result.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libtomahawk/Result.cpp b/src/libtomahawk/Result.cpp index c290305cf3..7370f8a47f 100644 --- a/src/libtomahawk/Result.cpp +++ b/src/libtomahawk/Result.cpp @@ -162,10 +162,7 @@ Result::mimetype() const float Result::score() const { - if ( isOnline() ) - return m_score; - else - return 0.0; + return m_score; } From 53ad9739ad7002284250e0eadd995c35549109ea Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 10:01:18 +0200 Subject: [PATCH 486/565] * Query's resoltSorter needs to take into account the result's online state. --- src/libtomahawk/Query.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/Query.cpp b/src/libtomahawk/Query.cpp index bf0bcd0d40..6fbb062d7f 100644 --- a/src/libtomahawk/Query.cpp +++ b/src/libtomahawk/Query.cpp @@ -304,8 +304,8 @@ Query::id() const bool Query::resultSorter( const result_ptr& left, const result_ptr& right ) { - const float ls = left->score(); - const float rs = right->score(); + const float ls = left->isOnline() ? left->score() : 0.0; + const float rs = right->isOnline() ? right->score() : 0.0; if ( ls == rs ) { @@ -368,7 +368,7 @@ Query::checkResults() if ( rp->playable() ) playable = true; - if ( rp->score() > 0.99 ) + if ( rp->isOnline() && rp->score() > 0.99 ) { solved = true; } From 6ed0879c7cc724ba7fbc5c26736b17ba7377d95c Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 10:01:57 +0200 Subject: [PATCH 487/565] * Respect results' online state when displaying. --- src/libtomahawk/playlist/ColumnItemDelegate.cpp | 2 +- src/libtomahawk/playlist/PlayableProxyModel.cpp | 8 ++++---- src/libtomahawk/playlist/TreeItemDelegate.cpp | 2 +- src/libtomahawk/utils/TomahawkUtilsGui.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libtomahawk/playlist/ColumnItemDelegate.cpp b/src/libtomahawk/playlist/ColumnItemDelegate.cpp index 0a54379be8..9f47c906e5 100644 --- a/src/libtomahawk/playlist/ColumnItemDelegate.cpp +++ b/src/libtomahawk/playlist/ColumnItemDelegate.cpp @@ -103,7 +103,7 @@ ColumnItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option } else if ( !item->result().isNull() || !item->query().isNull() ) { - float opacity = item->result().isNull() ? 0.0 : item->result()->score(); + float opacity = item->result() && item->result()->isOnline() ? item->result()->score() : 0.0; opacity = qMax( (float)0.3, opacity ); QColor textColor = TomahawkUtils::alphaBlend( option.palette.color( QPalette::Foreground ), option.palette.color( QPalette::Background ), opacity ); diff --git a/src/libtomahawk/playlist/PlayableProxyModel.cpp b/src/libtomahawk/playlist/PlayableProxyModel.cpp index 2beafc3bc7..b75c102d76 100644 --- a/src/libtomahawk/playlist/PlayableProxyModel.cpp +++ b/src/libtomahawk/playlist/PlayableProxyModel.cpp @@ -321,25 +321,25 @@ PlayableProxyModel::lessThan( int column, const Tomahawk::query_ptr& q1, const T if ( q1->numResults() ) { - const Tomahawk::result_ptr& r = q1->results().at( 0 ); + Tomahawk::result_ptr r = q1->results().first(); bitrate1 = r->bitrate(); duration1 = r->track()->duration(); mtime1 = r->modificationTime(); size1 = r->size(); year1 = r->track()->year(); - score1 = r->score(); + score1 = r->isOnline() ? r->score() : 0.0; origin1 = r->friendlySource().toLower(); id1 = (qint64)&r; } if ( q2->numResults() ) { - const Tomahawk::result_ptr& r = q2->results().at( 0 ); + Tomahawk::result_ptr r = q2->results().first(); bitrate2 = r->bitrate(); duration2 = r->track()->duration(); mtime2 = r->modificationTime(); size2 = r->size(); year2 = r->track()->year(); - score2 = r->score(); + score2 = r->isOnline() ? r->score() : 0.0; origin2 = r->friendlySource().toLower(); id2 = (qint64)&r; } diff --git a/src/libtomahawk/playlist/TreeItemDelegate.cpp b/src/libtomahawk/playlist/TreeItemDelegate.cpp index b980985be1..7cbebf26f6 100644 --- a/src/libtomahawk/playlist/TreeItemDelegate.cpp +++ b/src/libtomahawk/playlist/TreeItemDelegate.cpp @@ -105,7 +105,7 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, } else if ( !item->result().isNull() || !item->query().isNull() ) { - float opacity = item->result().isNull() ? 0.0 : item->result()->score(); + float opacity = item->result() && item->result()->isOnline() ? item->result()->score() : 0.0; opacity = qMax( (float)0.3, opacity ); QColor textColor = TomahawkUtils::alphaBlend( option.palette.color( QPalette::Foreground ), option.palette.color( QPalette::Background ), opacity ); diff --git a/src/libtomahawk/utils/TomahawkUtilsGui.cpp b/src/libtomahawk/utils/TomahawkUtilsGui.cpp index a08a4deb7c..57636517c9 100644 --- a/src/libtomahawk/utils/TomahawkUtilsGui.cpp +++ b/src/libtomahawk/utils/TomahawkUtilsGui.cpp @@ -758,7 +758,7 @@ prepareStyleOption( QStyleOptionViewItemV4* option, const QModelIndex& index, Pl else { float opacity = 0.0; - if ( !item->query()->results().isEmpty() ) + if ( !item->query()->results().isEmpty() && item->query()->results().first()->isOnline() ) opacity = item->query()->results().first()->score(); opacity = qMax( (float)0.3, opacity ); From 61a99449f34509bab8ea0d0dd58e67dd6f1b3db0 Mon Sep 17 00:00:00 2001 From: Jason Herskowitz Date: Thu, 20 Jun 2013 09:34:36 -0400 Subject: [PATCH 488/565] Some dashboard design tweaks --- src/libtomahawk/utils/TomahawkStyle.h | 2 ++ src/libtomahawk/widgets/Dashboard.cpp | 13 ++++++++----- src/libtomahawk/widgets/HeaderLabel.cpp | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index fdb18c84d0..bfca7b43b0 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -89,6 +89,8 @@ namespace TomahawkStyle static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); + static const QColor DASHBOARD_ROUNDFIGURE_KNOCKOUT = QColor( "#DBDBDB" ); + static const QColor DASHBOARD_ROUNDFIGURE_KNOCKOUT_TEXT = QColor( "#292F34" ); static const QColor SIDEBAR_ROUNDFIGURE_BACKGROUND = QColor( 167, 183, 211 ); static const QColor SIDEBAR_ROUNDFIGURE_INBOX_BACKGROUND = QColor( 239, 140, 51 ); diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index 5baecf1ede..b76c89df9d 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -92,6 +92,7 @@ Dashboard::Dashboard( QWidget* parent ) ui->playlistWidget->overlay()->resize( 380, 86 ); ui->playlistWidget->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + QPalette p = ui->playlistWidget->palette(); p.setColor( QPalette::Text, TomahawkStyle::HEADER_TEXT ); p.setColor( QPalette::BrightText, TomahawkStyle::HEADER_TEXT ); @@ -335,15 +336,17 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, font.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); QFont boldFont = font; + boldFont.setFamily( "Titillium Web" ); boldFont.setBold( true ); - boldFont.setPointSize( TomahawkUtils::defaultFontSize() ); + boldFont.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); QFontMetrics boldFontMetrics( boldFont ); QFont figFont = boldFont; + figFont.setFamily( "Titillium Web" ); figFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); QPixmap icon; - QRect pixmapRect = option.rect.adjusted( 10, 14, -option.rect.width() + option.rect.height() - 18, -14 ); + QRect pixmapRect = option.rect.adjusted( 10, 14, -option.rect.width() + option.rect.height() - 27, - 21 ); RecentlyPlayedPlaylistsModel::PlaylistTypes type = (RecentlyPlayedPlaylistsModel::PlaylistTypes)index.data( RecentlyPlayedPlaylistsModel::PlaylistTypeRole ).toInt(); if ( type == RecentlyPlayedPlaylistsModel::StaticPlaylist ) @@ -366,11 +369,11 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, // bottom edge flush with bottom of pixmap QRect rect( pixmapRect.right() - width, 0, width - 8, 0 ); rect.adjust( -2, 0, 0, 0 ); - rect.setTop( pixmapRect.bottom() - painter->fontMetrics().height() - 1 ); + rect.setTop( pixmapRect.bottom() - painter->fontMetrics().height() - 2 ); rect.setBottom( pixmapRect.bottom() + 1 ); - QColor figColor( TomahawkStyle::DASHBOARD_ROUNDFIGURE_BACKGROUND ); - painter->setPen( Qt::white ); + QColor figColor( TomahawkStyle::DASHBOARD_ROUNDFIGURE_KNOCKOUT ); + painter->setPen( TomahawkStyle::DASHBOARD_ROUNDFIGURE_KNOCKOUT_TEXT ); painter->setBrush( figColor ); TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, rect ); diff --git a/src/libtomahawk/widgets/HeaderLabel.cpp b/src/libtomahawk/widgets/HeaderLabel.cpp index 2e3a22561b..9c6f2742a3 100644 --- a/src/libtomahawk/widgets/HeaderLabel.cpp +++ b/src/libtomahawk/widgets/HeaderLabel.cpp @@ -38,7 +38,7 @@ HeaderLabel::HeaderLabel( QWidget* parent ) f.setPointSize( TomahawkUtils::defaultFontSize() ); setFont( f ); - setFixedHeight( TomahawkUtils::defaultFontHeight() * 1.4 ); + setFixedHeight( TomahawkUtils::defaultFontHeight() * 2 ); setMouseTracking( true ); } From 42c0199f534723e785f59648ba0bb8c3713dd439 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 19:38:00 +0200 Subject: [PATCH 489/565] * Fixed incompatible signal/slot types. --- src/libtomahawk/Source.cpp | 2 +- src/libtomahawk/Source.h | 2 +- src/libtomahawk/network/DBSyncConnectionState.h | 1 + src/libtomahawk/network/DbSyncConnection.cpp | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index afb5e24288..09c22f7579 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -508,7 +508,7 @@ Source::scanningFinished( bool updateGUI ) void -Source::onStateChanged( DBSyncConnectionState newstate, DBSyncConnectionState oldstate, const QString& info ) +Source::onStateChanged( Tomahawk::DBSyncConnectionState newstate, Tomahawk::DBSyncConnectionState oldstate, const QString& info ) { Q_D( Source ); diff --git a/src/libtomahawk/Source.h b/src/libtomahawk/Source.h index 5871dd85e2..a1b289a567 100644 --- a/src/libtomahawk/Source.h +++ b/src/libtomahawk/Source.h @@ -144,7 +144,7 @@ private slots: void setOffline(); void setOnline(); - void onStateChanged( DBSyncConnectionState newstate, DBSyncConnectionState oldstate, const QString& info ); + void onStateChanged( Tomahawk::DBSyncConnectionState newstate, Tomahawk::DBSyncConnectionState oldstate, const QString& info ); void onPlaybackStarted( const Tomahawk::track_ptr& track, unsigned int duration ); void onPlaybackFinished( const Tomahawk::track_ptr& track, const Tomahawk::PlaybackLog& log ); diff --git a/src/libtomahawk/network/DBSyncConnectionState.h b/src/libtomahawk/network/DBSyncConnectionState.h index c862c09bf2..17b571b7d9 100644 --- a/src/libtomahawk/network/DBSyncConnectionState.h +++ b/src/libtomahawk/network/DBSyncConnectionState.h @@ -37,5 +37,6 @@ enum DBSyncConnectionState } +Q_DECLARE_METATYPE( Tomahawk::DBSyncConnectionState ) #endif // DBSYNCCONNECTIONSTATE_H diff --git a/src/libtomahawk/network/DbSyncConnection.cpp b/src/libtomahawk/network/DbSyncConnection.cpp index 9771fc6f91..d36e47fe9d 100644 --- a/src/libtomahawk/network/DbSyncConnection.cpp +++ b/src/libtomahawk/network/DbSyncConnection.cpp @@ -56,7 +56,7 @@ DBSyncConnection::DBSyncConnection( Servent* s, const source_ptr& src ) // Be aware of namespaces in these signals/slots! connect( this, SIGNAL( stateChanged( Tomahawk::DBSyncConnectionState, Tomahawk::DBSyncConnectionState, QString ) ), - m_source.data(), SLOT( onStateChanged( DBSyncConnectionState, DBSyncConnectionState, QString ) ) ); + m_source.data(), SLOT( onStateChanged( Tomahawk::DBSyncConnectionState, Tomahawk::DBSyncConnectionState, QString ) ) ); connect( m_source.data(), SIGNAL( commandsFinished() ), this, SLOT( lastOpApplied() ) ); From ef00f9c21ad8236b3bdcc38a132dea900477a5a4 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 20 Jun 2013 19:42:13 +0200 Subject: [PATCH 490/565] This breaks too many things: Revert "Some dashboard design tweaks" This reverts commit 22ce38adbb0ff03a5b41428b584cebbbd6336f8f. --- src/libtomahawk/utils/TomahawkStyle.h | 2 -- src/libtomahawk/widgets/Dashboard.cpp | 13 +++++-------- src/libtomahawk/widgets/HeaderLabel.cpp | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/utils/TomahawkStyle.h b/src/libtomahawk/utils/TomahawkStyle.h index bfca7b43b0..fdb18c84d0 100644 --- a/src/libtomahawk/utils/TomahawkStyle.h +++ b/src/libtomahawk/utils/TomahawkStyle.h @@ -89,8 +89,6 @@ namespace TomahawkStyle static const QColor FOOTNOTES_BACKGROUND = QColor( "#272b2e" ); static const QColor DASHBOARD_ROUNDFIGURE_BACKGROUND = QColor( "#454e59" ); - static const QColor DASHBOARD_ROUNDFIGURE_KNOCKOUT = QColor( "#DBDBDB" ); - static const QColor DASHBOARD_ROUNDFIGURE_KNOCKOUT_TEXT = QColor( "#292F34" ); static const QColor SIDEBAR_ROUNDFIGURE_BACKGROUND = QColor( 167, 183, 211 ); static const QColor SIDEBAR_ROUNDFIGURE_INBOX_BACKGROUND = QColor( 239, 140, 51 ); diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index b76c89df9d..5baecf1ede 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -92,7 +92,6 @@ Dashboard::Dashboard( QWidget* parent ) ui->playlistWidget->overlay()->resize( 380, 86 ); ui->playlistWidget->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); - QPalette p = ui->playlistWidget->palette(); p.setColor( QPalette::Text, TomahawkStyle::HEADER_TEXT ); p.setColor( QPalette::BrightText, TomahawkStyle::HEADER_TEXT ); @@ -336,17 +335,15 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, font.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); QFont boldFont = font; - boldFont.setFamily( "Titillium Web" ); boldFont.setBold( true ); - boldFont.setPointSize( TomahawkUtils::defaultFontSize() + 2 ); + boldFont.setPointSize( TomahawkUtils::defaultFontSize() ); QFontMetrics boldFontMetrics( boldFont ); QFont figFont = boldFont; - figFont.setFamily( "Titillium Web" ); figFont.setPointSize( TomahawkUtils::defaultFontSize() - 1 ); QPixmap icon; - QRect pixmapRect = option.rect.adjusted( 10, 14, -option.rect.width() + option.rect.height() - 27, - 21 ); + QRect pixmapRect = option.rect.adjusted( 10, 14, -option.rect.width() + option.rect.height() - 18, -14 ); RecentlyPlayedPlaylistsModel::PlaylistTypes type = (RecentlyPlayedPlaylistsModel::PlaylistTypes)index.data( RecentlyPlayedPlaylistsModel::PlaylistTypeRole ).toInt(); if ( type == RecentlyPlayedPlaylistsModel::StaticPlaylist ) @@ -369,11 +366,11 @@ PlaylistDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, // bottom edge flush with bottom of pixmap QRect rect( pixmapRect.right() - width, 0, width - 8, 0 ); rect.adjust( -2, 0, 0, 0 ); - rect.setTop( pixmapRect.bottom() - painter->fontMetrics().height() - 2 ); + rect.setTop( pixmapRect.bottom() - painter->fontMetrics().height() - 1 ); rect.setBottom( pixmapRect.bottom() + 1 ); - QColor figColor( TomahawkStyle::DASHBOARD_ROUNDFIGURE_KNOCKOUT ); - painter->setPen( TomahawkStyle::DASHBOARD_ROUNDFIGURE_KNOCKOUT_TEXT ); + QColor figColor( TomahawkStyle::DASHBOARD_ROUNDFIGURE_BACKGROUND ); + painter->setPen( Qt::white ); painter->setBrush( figColor ); TomahawkUtils::drawBackgroundAndNumbers( painter, tracks, rect ); diff --git a/src/libtomahawk/widgets/HeaderLabel.cpp b/src/libtomahawk/widgets/HeaderLabel.cpp index 9c6f2742a3..2e3a22561b 100644 --- a/src/libtomahawk/widgets/HeaderLabel.cpp +++ b/src/libtomahawk/widgets/HeaderLabel.cpp @@ -38,7 +38,7 @@ HeaderLabel::HeaderLabel( QWidget* parent ) f.setPointSize( TomahawkUtils::defaultFontSize() ); setFont( f ); - setFixedHeight( TomahawkUtils::defaultFontHeight() * 2 ); + setFixedHeight( TomahawkUtils::defaultFontHeight() * 1.4 ); setMouseTracking( true ); } From 193d4b6a96d014a1de7f66960fcea633b8b4b79f Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Fri, 21 Jun 2013 02:16:54 +0200 Subject: [PATCH 491/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 14 +++--- lang/tomahawk_bg.ts | 14 +++--- lang/tomahawk_bn_IN.ts | 14 +++--- lang/tomahawk_ca.ts | 14 +++--- lang/tomahawk_ca@valencia.ts | 14 +++--- lang/tomahawk_cs.ts | 14 +++--- lang/tomahawk_da.ts | 14 +++--- lang/tomahawk_de.ts | 14 +++--- lang/tomahawk_el.ts | 14 +++--- lang/tomahawk_en.ts | 14 +++--- lang/tomahawk_es.ts | 14 +++--- lang/tomahawk_fi.ts | 84 ++++++++++++++++++------------------ lang/tomahawk_fr.ts | 14 +++--- lang/tomahawk_gl.ts | 14 +++--- lang/tomahawk_hi_IN.ts | 14 +++--- lang/tomahawk_hu.ts | 14 +++--- lang/tomahawk_id.ts | 14 +++--- lang/tomahawk_it.ts | 14 +++--- lang/tomahawk_ja.ts | 14 +++--- lang/tomahawk_lt.ts | 14 +++--- lang/tomahawk_pl.ts | 14 +++--- lang/tomahawk_pt_BR.ts | 14 +++--- lang/tomahawk_ru.ts | 14 +++--- lang/tomahawk_sv.ts | 14 +++--- lang/tomahawk_tr.ts | 14 +++--- lang/tomahawk_zh_CN.ts | 14 +++--- lang/tomahawk_zh_TW.ts | 14 +++--- 27 files changed, 224 insertions(+), 224 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index ec5f2054c5..86170f3cfe 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. تحذير المحلل النصي: إستدعاء API %1 إرجاع البيانات بشكل متزامن. @@ -1712,22 +1712,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 خطأ محلل النصي: %1 %2 %3 %4 - + SSL Error خطأ SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? لقد طلبت من توماهوك الإتصال بشكل أمن إلى <b>%1</b> ولكن لا يمكننا التأكد من أن اتصالك آمن: <br><br><b>%2</b></br></br> هل تريد أن تثق بهذا الإتصال؟ - + Trust certificate شهادة الثقة diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index bb837ddaed..436b587895 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -930,9 +930,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Предупреждение на скриптът за извличане: Заявката към %1 върна данни синхронно. @@ -1721,22 +1721,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Грешка на скриптът за извличане на данни: %1 %2 %3 %4 - + SSL Error SSL грешка - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Ти пожела Tomahawk да се свърже сигурно към <b>%1</b>, но ние не можем да потвърдим, че връзката е сигурна:<br><br><b>%2</b><br><br>Искаш ли да се довериш на тази връзка? - + Trust certificate Удостовери този сертификат diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 042c2a2419..50e12e7656 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 1b3c71e67e..c6e54f8a42 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index ba40933fb0..1e696321b7 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index eea10521f5..3bd62df7f6 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -924,9 +924,9 @@ heslo JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Varování řešitele skriptu: Volání API %1 vrátilo data synchronně. @@ -1711,22 +1711,22 @@ heslo ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Chyba řešitele skriptu: %1 %2 %3 %4 - + SSL Error Chyba SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Požádal jste Tomahawk o to, aby se bezpečně připojil k <b>%1</b>, ale nelze potvrdit, že je vaše připojení bezpečné:<br><br><b>%2</b><br><br>Chcete tomuto připojení důvěřovat? - + Trust certificate Důvěřovat certifikátu diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 28d8f6bd16..2a4b5d6392 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 8c0947adf3..b0f60892dd 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -924,9 +924,9 @@ Passwort JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warnung: API aufruf %1 zurückgegebener Daten synchron. @@ -1711,22 +1711,22 @@ Passwort ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Fehler: %1 %2 %3 %4 - + SSL Error SSL Fehler - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Sie wurden gebeten, Tomahawk sicher zu <b>%1</b> verbinden, aber wir können nicht bestätigen, dass die Verbindung sicher ist:<br><br><b>%2</b><br><br> Möchten Sie dieser Verbindung vertrauen? - + Trust certificate Zertifikat vertrauen diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index bf8fb1eaf5..efa04b69d8 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Σριπτ προειδοποίηση αναλυτή: API κλήση% 1 επέστρεψε δεδομένα συγχρονισμένα. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Σφάλμα Script Resolver: %1 %2 %3 %4 - + SSL Error SSL Σφάλμα - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Έχετε ζητήσει το Tomahawk να συνδεθεί με ασφάλεια στο <b>%1</b>, αλλά δεν μπορούμε να επιβεβαιώσουμε ότι η σύνδεσή σας είναι ασφαλής:<br><br><b>%2</b><br><br>Θέλετε να εμπιστεύθειτε αυτή τη σύνδεση; - + Trust certificate Εμπιστοσύνη πιστοποιητικόυ diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index c819bd4dd3..e58783f104 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warning: API call %1 returned data synchronously. @@ -1711,22 +1711,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Error: %1 %2 %3 %4 - + SSL Error SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate Trust certificate diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 8695f16eec..99ce7f874a 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error del resolutor de script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 82296f1c7f..f64382db4a 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -340,7 +340,7 @@ yhdistää ja toistaa sinulta virtaa? YOUR ARTIST RANK - + ARTISTIN SIJOITUKSESI @@ -450,7 +450,7 @@ yhdistää ja toistaa sinulta virtaa? Unknown - + Tuntematon @@ -458,7 +458,7 @@ yhdistää ja toistaa sinulta virtaa? Sorry, your filter '%1' did not match any results. - + Valitettavasti suodattimesi ”%1” ei tuottanut yhtään tuloksia. @@ -466,12 +466,12 @@ yhdistää ja toistaa sinulta virtaa? Composer - + Säveltäjä Age - + Ikä @@ -491,7 +491,7 @@ yhdistää ja toistaa sinulta virtaa? Composer: - + Säveltäjä: @@ -511,12 +511,12 @@ yhdistää ja toistaa sinulta virtaa? Age: - + Ikä: %1 kbps - + %1 kbps @@ -592,7 +592,7 @@ yhdistää ja toistaa sinulta virtaa? Recently Played Tracks - + Viime aikoina kuunnellut kappaleet @@ -602,7 +602,7 @@ yhdistää ja toistaa sinulta virtaa? Newest Stations & Playlists - + Uusimmat asemat ja soittolistat @@ -612,12 +612,12 @@ yhdistää ja toistaa sinulta virtaa? An overview of your recent activity - + Yleiskatsaus viimeaikaisesta toiminnastasi No recently created playlists in your network. - + Verkossasi ei ole viime aikoina luotuja soittolistoja. @@ -785,7 +785,7 @@ yhdistää ja toistaa sinulta virtaa? This playlist is currently empty. Add some tracks to it and enjoy the music! - + Tämä soittolista on parhaillaan tyhjä. Lisää kappaleita ja nauti musiikista! @@ -856,32 +856,32 @@ salasana From: - + Päivästä: To: - + Päivään: Recently Played Tracks - + Viime aikoina kuunnellut kappaleet Your recently played tracks - + Viime aikoina kuuntelemasi kappaleet %1's recently played tracks - + Käyttäjän %1 viime aikoina kuuntelemat kappaleet Sorry, we could not find any recent plays! - + Valitettavasti emme löytäneet yhtään viimeaikaisia soittoja! @@ -924,9 +924,9 @@ salasana JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Skriptiselvittimen varoitus: API-kutsu %1 palautti dataa synkronisesti. @@ -1190,32 +1190,32 @@ salasana Charts - + Kaaviot Last Week - + Viime viikko Last Month - + Viime kuukausi Last Year - + Viime vuosi Overall - + Kaikkiaan Network Activity - + Verkkotoiminta @@ -1711,22 +1711,22 @@ salasana ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptiselvittimen virhe: %1 %2 %3 %4 - + SSL Error SSL-virhe - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Olet pyytänyt Tomahawkia yhdistämään turvallisesti palvelimeen <b>%1</b>, mutta yhteyden turvallisuutta ei voida varmistaa:<br><br><b>%2</b><br><br>Haluatko luottaa tähän yhteyteen? - + Trust certificate Luota varmenteeseen @@ -1754,22 +1754,22 @@ salasana Tracks - + Kappaleet Artists - + Artistit Albums - + Albumit Sorry, we could not find any tracks! - + Valitettavasti emme löytäneet yhtään kappaletta! @@ -2272,7 +2272,7 @@ käyttäjäradion käyttöönottamiseksi Network Activity - + Verkkotoiminta @@ -2346,12 +2346,12 @@ napsauttamalla hiiren oikealla. Use this to force Spotify to never announce listening data to Social Networks - + Pakota Spotify olemaan lähettämättä kuuntelutietoja sosiaalisiin verkkoihin Always run in Private Mode - + Ole aina yksityisessä tilassa @@ -2387,7 +2387,7 @@ napsauttamalla hiiren oikealla. out of %1 - + /%1 @@ -4075,12 +4075,12 @@ anna siellä näytetty PIN-koodi tähän: # PLAYS / ARTIST - + SOITTOKERTOJA / ARTISTI YOUR SONG RANK - + KAPPALEEN SIJOITUKSESI diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 19c897b6fd..2b0cf35cdf 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erreur du script de résolution : %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 176fc887d4..9c08a1e8e0 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erro do solucionador de erros: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index d9e9d8728d..2bf108dd58 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 44e0a2c064..c893bb955a 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index c8845e332b..670abda6e5 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 39a95b7f93..7d2f381381 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -923,9 +923,9 @@ temporanea JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Avvertimento Script Resolver: chiamata API 1% ha restituito dati sincroni. @@ -1709,22 +1709,22 @@ temporanea ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Errore script resolver: %1 %2 %3 %4 - + SSL Error Errore SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Hai chiesto a Tomahawk di connettersi in modo sicuro a <b>%1</b>, ma non possiamo garantire che la conessione sia sicura: <br><br><b>%2</b><br><br> Vuoi fidarti di questa connessione? - + Trust certificate Certificato di trust diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index db23d23957..1acd6c5bb1 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 24f023b7dc..7062b6346d 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index a2905e4b47..d049a4fc67 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 397728096a..0bd07993a4 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 2516b90f06..d7f951ac35 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -926,9 +926,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1713,22 +1713,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index e5ce13c1bd..a63ca8a211 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Resovler-skriptvarning: API-kall %1 returnerade data synkront @@ -1711,22 +1711,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptfel i resolvern: %1 %2 %3 %4 - + SSL Error SSL-Fel - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Du har bett Tomahawk att göra en säker uppkoppling mot <b>%1</b>, men vi kan inte bekräfta att din uppkoppling är säker: <br><br><b>%2</b><br><br>Litar du på denna uppkoppling? - + Trust certificate Tillförlitligt certifikat diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 57acc86eeb..85d4cd4088 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 3c8ab39520..06e5eb08ea 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index bce700b1f0..87c4ba26c3 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate From 483fa38f7b701523f354fa4882991485a633aff8 Mon Sep 17 00:00:00 2001 From: Lasse Liehu Date: Fri, 21 Jun 2013 15:12:34 +0300 Subject: [PATCH 492/565] Put "out of %1" under the big number in StatsGauge "[number]\nout of %1" flows better than "out of %1\n[number]". --- src/libtomahawk/widgets/StatsGauge.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index d5d51cb84b..2b7390fc12 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -81,7 +81,7 @@ StatsGauge::paintEvent( QPaintEvent* event ) font.setPixelSize( 44 ); p.setFont( font ); - QRect textRect( 0, gaugeSize.height() / 2 - 14, gaugeSize.width(), 62 ); + QRect textRect( 0, gaugeSize.height() / 2 - 44, gaugeSize.width(), 62 ); p.drawText( textRect, Qt::AlignCenter, value() > 0 ? QString::number( value() ) : "-" ); pen = QPen( TomahawkStyle::HEADER_GAUGE_TEXT.darker() ); @@ -91,7 +91,7 @@ StatsGauge::paintEvent( QPaintEvent* event ) font.setPixelSize( 16 ); p.setFont( font ); - textRect = QRect( 0, gaugeSize.height() / 2 - 32, gaugeSize.width(), 20 ); + textRect = QRect( 0, gaugeSize.height() / 2 + 22, gaugeSize.width(), 20 ); p.drawText( textRect, Qt::AlignCenter, maximum() > 0 ? tr( "out of %1" ).arg( maximum() ) : "-" ); if ( !m_text.isEmpty() ) From 00e8d25da6679ee3e590206ffc09079a5c900294 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 21 Jun 2013 22:33:34 +0200 Subject: [PATCH 493/565] Revert "Put "out of %1" under the big number in StatsGauge" This reverts commit 5634f5a4ca0515d164c76e45bfa689a34ade5cad. --- src/libtomahawk/widgets/StatsGauge.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/widgets/StatsGauge.cpp b/src/libtomahawk/widgets/StatsGauge.cpp index 2b7390fc12..d5d51cb84b 100644 --- a/src/libtomahawk/widgets/StatsGauge.cpp +++ b/src/libtomahawk/widgets/StatsGauge.cpp @@ -81,7 +81,7 @@ StatsGauge::paintEvent( QPaintEvent* event ) font.setPixelSize( 44 ); p.setFont( font ); - QRect textRect( 0, gaugeSize.height() / 2 - 44, gaugeSize.width(), 62 ); + QRect textRect( 0, gaugeSize.height() / 2 - 14, gaugeSize.width(), 62 ); p.drawText( textRect, Qt::AlignCenter, value() > 0 ? QString::number( value() ) : "-" ); pen = QPen( TomahawkStyle::HEADER_GAUGE_TEXT.darker() ); @@ -91,7 +91,7 @@ StatsGauge::paintEvent( QPaintEvent* event ) font.setPixelSize( 16 ); p.setFont( font ); - textRect = QRect( 0, gaugeSize.height() / 2 + 22, gaugeSize.width(), 20 ); + textRect = QRect( 0, gaugeSize.height() / 2 - 32, gaugeSize.width(), 20 ); p.drawText( textRect, Qt::AlignCenter, maximum() > 0 ? tr( "out of %1" ).arg( maximum() ) : "-" ); if ( !m_text.isEmpty() ) From ec17293d3d526fdac61eb89fa12c9ce402d910e2 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 22 Jun 2013 00:22:43 +0200 Subject: [PATCH 494/565] Do not queue ACL result if there are no sipInfos * Temporary fix until nodeId is moved/refactored into PeerInfo out of SipInfo --- src/libtomahawk/network/Servent.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 315035955f..df42a290a6 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -437,9 +437,10 @@ Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) void Servent::queueForAclResult( const QString& username, const QSet& peerInfos ) { - if ( peerInfos.isEmpty() ) + if ( peerInfos.isEmpty() || (*peerInfos.begin())->sipInfos().isEmpty() ) { // If all peerInfos disappeared, do not queue. + // If the peerInfo has not got a sipInfo anymore, do not queue either. return; } From a6f8dc1830e4ccb51ebc906e436816c9b82944f7 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 22 Jun 2013 12:31:39 +0200 Subject: [PATCH 495/565] * Get rid of passing tomahawkLoaded signal through TomahawkWindow and ViewManager. --- src/libtomahawk/ViewManager.cpp | 11 ----------- src/libtomahawk/ViewManager.h | 8 -------- src/tomahawk/TomahawkWindow.cpp | 1 - 3 files changed, 20 deletions(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 3c1a210590..75c9707f29 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -89,7 +89,6 @@ ViewManager::ViewManager( QObject* parent ) , m_radioView( 0 ) , m_networkActivityWidget( 0 ) , m_currentPage( 0 ) - , m_loaded( false ) { s_instance = this; @@ -125,8 +124,6 @@ ViewManager::ViewManager( QObject* parent ) connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) ); connect( m_infobar, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) ); - connect( this, SIGNAL( tomahawkLoaded() ), m_dashboard, SLOT( loadData() ) ); - /* connect( m_infobar, SIGNAL( flatMode() ), SLOT( setTableMode() ) ); connect( m_infobar, SIGNAL( artistMode() ), SLOT( setTreeMode() ) ); connect( m_infobar, SIGNAL( albumMode() ), SLOT( setAlbumMode() ) );*/ @@ -784,14 +781,6 @@ ViewManager::onWidgetDestroyed( QWidget* widget ) } -void -ViewManager::setTomahawkLoaded() -{ - m_loaded = true; - emit tomahawkLoaded(); -} - - ViewPage* ViewManager::pageForDynPlaylist(const dynplaylist_ptr& pl) const { diff --git a/src/libtomahawk/ViewManager.h b/src/libtomahawk/ViewManager.h index f01971efb5..7bdd4db48d 100644 --- a/src/libtomahawk/ViewManager.h +++ b/src/libtomahawk/ViewManager.h @@ -119,8 +119,6 @@ Q_OBJECT FlexibleView* createPageForList( const QString& title, const QList< Tomahawk::query_ptr >& queries ); - bool isTomahawkLoaded() const { return m_loaded; } - signals: void filterAvailable( bool b ); @@ -135,8 +133,6 @@ Q_OBJECT void showQueueRequested(); void hideQueueRequested(); - void tomahawkLoaded(); - void historyBackAvailable( bool avail ); void historyForwardAvailable( bool avail ); @@ -178,8 +174,6 @@ public slots: void playlistInterfaceChanged( Tomahawk::playlistinterface_ptr ); - void setTomahawkLoaded(); - private slots: void setFilter( const QString& filter ); void applyFilter(); @@ -233,8 +227,6 @@ private slots: QTimer m_filterTimer; QString m_filter; - bool m_loaded; - static ViewManager* s_instance; }; diff --git a/src/tomahawk/TomahawkWindow.cpp b/src/tomahawk/TomahawkWindow.cpp index 699a562abe..f3155971d8 100644 --- a/src/tomahawk/TomahawkWindow.cpp +++ b/src/tomahawk/TomahawkWindow.cpp @@ -121,7 +121,6 @@ TomahawkWindow::TomahawkWindow( QWidget* parent ) connect( vm, SIGNAL( showQueueRequested() ), SLOT( showQueue() ) ); connect( vm, SIGNAL( hideQueueRequested() ), SLOT( hideQueue() ) ); - connect( APP, SIGNAL( tomahawkLoaded() ), vm, SLOT( setTomahawkLoaded() ) ); // Pass loaded signal into libtomahawk so components in there can connect to ViewManager #ifdef Q_OS_WIN connect( AudioEngine::instance(), SIGNAL( stateChanged( AudioState, AudioState) ), SLOT( audioStateChanged( AudioState, AudioState) ) ); From a4505930b5f99daf91dc85e0cbe01c0012b12410 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 22 Jun 2013 12:32:02 +0200 Subject: [PATCH 496/565] * Changed tomahawkLoaded handling. --- src/tomahawk/TomahawkApp.cpp | 16 ++++------------ src/tomahawk/TomahawkApp.h | 4 +--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index 46a12038db..e510bbd27a 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -146,7 +146,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) , m_mainwindow( 0 ) #endif , m_headless( false ) - , m_loaded( false ) { if ( arguments().contains( "--help" ) || arguments().contains( "-h" ) ) { @@ -158,7 +157,6 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) setOrganizationDomain( QLatin1String( TOMAHAWK_ORGANIZATION_DOMAIN ) ); setApplicationName( QLatin1String( TOMAHAWK_APPLICATION_NAME ) ); setApplicationVersion( QLatin1String( TOMAHAWK_VERSION ) ); - connect( this, SIGNAL( tomahawkLoaded() ), SLOT( initEnergyEventHandler() ) ); registerMetaTypes(); TomahawkUtils::installTranslator( this ); @@ -566,6 +564,7 @@ TomahawkApp::initFactoriesForAccountManager() Tomahawk::Accounts::AccountManager::instance()->loadFromConfig(); } + void TomahawkApp::initEnergyEventHandler() { @@ -590,9 +589,6 @@ TomahawkApp::initSIP() tDebug( LOGINFO ) << "Connecting SIP classes"; Accounts::AccountManager::instance()->initSIP(); } - - m_loaded = true; - emit tomahawkLoaded(); } @@ -684,6 +680,9 @@ TomahawkApp::onInfoSystemReady() // Make sure to do this after main window is inited Tomahawk::enableFullscreen( m_mainwindow ); #endif + + initEnergyEventHandler(); + emit tomahawkLoaded(); } @@ -812,10 +811,3 @@ TomahawkApp::mainWindow() const { return m_mainwindow; } - - -bool -TomahawkApp::isTomahawkLoaded() const -{ - return m_loaded; -} diff --git a/src/tomahawk/TomahawkApp.h b/src/tomahawk/TomahawkApp.h index 952ebfe5ba..3286e58f7a 100644 --- a/src/tomahawk/TomahawkApp.h +++ b/src/tomahawk/TomahawkApp.h @@ -95,8 +95,6 @@ Q_OBJECT // PlatformInterface virtual bool loadUrl( const QString& url ); - bool isTomahawkLoaded() const; - // reimplemented from QApplication/QCoreApplication virtual bool notify( QObject* receiver, QEvent* e ); @@ -144,7 +142,7 @@ private slots: TomahawkWindow* m_mainwindow; #endif - bool m_headless, m_loaded; + bool m_headless; QPointer< QxtHttpServerConnector > m_connector; QPointer< QxtHttpSessionManager > m_session; From 1ecfc8aa9ed52077f6f654501773c2e1b5aa8260 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 22 Jun 2013 12:32:25 +0200 Subject: [PATCH 497/565] * Load recent albums when SourceList is ready. --- src/libtomahawk/widgets/Dashboard.cpp | 9 ++------- src/libtomahawk/widgets/Dashboard.h | 2 -- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/libtomahawk/widgets/Dashboard.cpp b/src/libtomahawk/widgets/Dashboard.cpp index 5baecf1ede..71abcfeffd 100644 --- a/src/libtomahawk/widgets/Dashboard.cpp +++ b/src/libtomahawk/widgets/Dashboard.cpp @@ -186,13 +186,6 @@ Dashboard::~Dashboard() } -void -Dashboard::loadData() -{ - m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime, true ); -} - - Tomahawk::playlistinterface_ptr Dashboard::playlistInterface() const { @@ -228,6 +221,8 @@ Dashboard::onSourcesReady() { foreach ( const source_ptr& source, SourceList::instance()->sources() ) onSourceAdded( source ); + + updateRecentAdditions(); } diff --git a/src/libtomahawk/widgets/Dashboard.h b/src/libtomahawk/widgets/Dashboard.h index 74f4e14b65..bf74219871 100644 --- a/src/libtomahawk/widgets/Dashboard.h +++ b/src/libtomahawk/widgets/Dashboard.h @@ -111,8 +111,6 @@ public slots: void updatePlaylists(); void updateRecentAdditions(); - void loadData(); - private slots: void onSourcesReady(); void onSourceAdded( const Tomahawk::source_ptr& source ); From d829389e4e513d2205b1be935cb2f16ed6723a6b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 22 Jun 2013 12:38:30 +0200 Subject: [PATCH 498/565] * Filter out tracks with bogus mtime when trying to load recent albums. --- .../database/DatabaseCommand_AllAlbums.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp b/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp index 7077383ffc..4399516512 100644 --- a/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp +++ b/src/libtomahawk/database/DatabaseCommand_AllAlbums.cpp @@ -27,7 +27,7 @@ #include "utils/Logger.h" -DatabaseCommand_AllAlbums::DatabaseCommand_AllAlbums( const Tomahawk::collection_ptr &collection, const Tomahawk::artist_ptr &artist, QObject *parent ) +DatabaseCommand_AllAlbums::DatabaseCommand_AllAlbums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist, QObject* parent ) : DatabaseCommand( parent ) , m_collection( collection ) , m_artist( artist ) @@ -39,11 +39,12 @@ DatabaseCommand_AllAlbums::DatabaseCommand_AllAlbums( const Tomahawk::collection DatabaseCommand_AllAlbums::~DatabaseCommand_AllAlbums() -{} +{ +} void -DatabaseCommand_AllAlbums::setArtist( const Tomahawk::artist_ptr &artist ) +DatabaseCommand_AllAlbums::setArtist( const Tomahawk::artist_ptr& artist ) { m_artist = artist; } @@ -54,7 +55,7 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi ) { TomahawkSqlQuery query = dbi->newquery(); QList al; - QString orderToken, sourceToken, filterToken, tables; + QString orderToken, sourceToken, filterToken, timeToken, tables; switch ( m_sortOrder ) { @@ -63,10 +64,11 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi ) case ModificationTime: orderToken = "file.mtime"; + timeToken = QString( "AND file.mtime <= %1" ).arg( QDateTime::currentDateTimeUtc().toTime_t() ); } if ( !m_collection.isNull() ) - sourceToken = QString( "AND file.source %1 " ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) ); + sourceToken = QString( "AND file.source %1" ).arg( m_collection->source()->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_collection->source()->id() ) ); if ( !m_filter.isEmpty() ) { @@ -89,10 +91,11 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi ) "LEFT OUTER JOIN album ON file_join.album = album.id " "WHERE file.id = file_join.file " "AND file_join.artist = %2 " - "%3 %4 %5 %6 %7" + "%3 %4 %5 %6 %7 %8" ).arg( tables ) .arg( m_artist->id() ) .arg( sourceToken ) + .arg( timeToken ) .arg( filterToken ) .arg( m_sortOrder > 0 ? QString( "ORDER BY %1" ).arg( orderToken ) : QString() ) .arg( m_sortDescending ? "DESC" : QString() ) From 3adeee41408ab108175a3628a206b94103eeb85a Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 20 Jun 2013 15:22:59 +0200 Subject: [PATCH 499/565] Debug spam++ --- src/libtomahawk/TomahawkSettings.cpp | 1 + src/libtomahawk/accounts/CredentialsManager.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index ea9812e819..e3449b8d84 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -622,6 +622,7 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) tDebug() << "beginGroup" << QString( "accounts/%1" ).arg( account ); beginGroup( QString( "accounts/%1" ).arg( account ) ); const QVariantHash creds = value( "credentials" ).toHash(); + tDebug() << creds; if ( !creds.isEmpty() ) { diff --git a/src/libtomahawk/accounts/CredentialsManager.cpp b/src/libtomahawk/accounts/CredentialsManager.cpp index a7745cd4d8..38ee1f6df7 100644 --- a/src/libtomahawk/accounts/CredentialsManager.cpp +++ b/src/libtomahawk/accounts/CredentialsManager.cpp @@ -141,7 +141,7 @@ CredentialsManager::keychainJobFinished( QKeychain::Job* j ) } else { - tDebug() << "QtKeychain readJob finished with error:" << j->error() << j->errorString(); + tDebug() << "QtKeychain readJob for" << readJob->key() << "finished with error:" << j->error() << j->errorString(); } m_readJobs.removeOne( readJob ); From 56dd8e2572a81b81473afb35a41beff88c52b44c Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Thu, 20 Jun 2013 18:06:45 +0200 Subject: [PATCH 500/565] Stay insecure if there's no other way. --- src/libtomahawk/TomahawkSettings.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index e3449b8d84..1c6c325123 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -629,7 +629,9 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) QKeychain::WritePasswordJob* j = new QKeychain::WritePasswordJob( QLatin1String( "Tomahawk" ), this ); j->setKey( account ); j->setAutoDelete( true ); - +#if defined( Q_OS_UNIX ) && !defined( Q_OS_MAC ) + j->setInsecureFallback( true ); +#endif QByteArray data; QDataStream ds( &data, QIODevice::WriteOnly ); ds << creds; From 612d8874dfab21d7d68297347c569bcbe735279f Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Sat, 22 Jun 2013 12:47:07 +0200 Subject: [PATCH 501/565] Don't output secrets in log. --- src/libtomahawk/TomahawkSettings.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/TomahawkSettings.cpp b/src/libtomahawk/TomahawkSettings.cpp index 1c6c325123..e7f14bae2f 100644 --- a/src/libtomahawk/TomahawkSettings.cpp +++ b/src/libtomahawk/TomahawkSettings.cpp @@ -622,7 +622,8 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion ) tDebug() << "beginGroup" << QString( "accounts/%1" ).arg( account ); beginGroup( QString( "accounts/%1" ).arg( account ) ); const QVariantHash creds = value( "credentials" ).toHash(); - tDebug() << creds; + tDebug() << creds[ "username" ] + << ( creds[ "password" ].isNull() ? ", no password" : ", has password" ); if ( !creds.isEmpty() ) { From 1c62ec33587d87ff7eef4ee8f6a0c8685af827ac Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 22 Jun 2013 20:08:11 +0200 Subject: [PATCH 502/565] Delete AudioEngine after MainWindow --- src/tomahawk/TomahawkApp.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index e510bbd27a..b5239a051f 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -285,9 +285,6 @@ TomahawkApp::~TomahawkApp() if ( !m_scanManager.isNull() ) delete m_scanManager.data(); - if ( !m_audioEngine.isNull() ) - delete m_audioEngine.data(); - delete Tomahawk::Accounts::AccountManager::instance(); #ifndef ENABLE_HEADLESS @@ -295,6 +292,10 @@ TomahawkApp::~TomahawkApp() delete m_mainwindow; #endif + // Main Window uses the AudioEngine, so delete it later. + if ( !m_audioEngine.isNull() ) + delete m_audioEngine.data(); + if ( !m_database.isNull() ) delete m_database.data(); From 10712c7efb03490e6ab602c62eae057fac249aca Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Sun, 23 Jun 2013 02:16:17 +0200 Subject: [PATCH 503/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 100 ++++++++++++++++----------------- lang/tomahawk_bg.ts | 100 ++++++++++++++++----------------- lang/tomahawk_bn_IN.ts | 100 ++++++++++++++++----------------- lang/tomahawk_ca.ts | 100 ++++++++++++++++----------------- lang/tomahawk_ca@valencia.ts | 100 ++++++++++++++++----------------- lang/tomahawk_cs.ts | 100 ++++++++++++++++----------------- lang/tomahawk_da.ts | 100 ++++++++++++++++----------------- lang/tomahawk_de.ts | 100 ++++++++++++++++----------------- lang/tomahawk_el.ts | 100 ++++++++++++++++----------------- lang/tomahawk_en.ts | 100 ++++++++++++++++----------------- lang/tomahawk_es.ts | 100 ++++++++++++++++----------------- lang/tomahawk_fi.ts | 106 +++++++++++++++++------------------ lang/tomahawk_fr.ts | 100 ++++++++++++++++----------------- lang/tomahawk_gl.ts | 100 ++++++++++++++++----------------- lang/tomahawk_hi_IN.ts | 100 ++++++++++++++++----------------- lang/tomahawk_hu.ts | 100 ++++++++++++++++----------------- lang/tomahawk_id.ts | 100 ++++++++++++++++----------------- lang/tomahawk_it.ts | 100 ++++++++++++++++----------------- lang/tomahawk_ja.ts | 100 ++++++++++++++++----------------- lang/tomahawk_lt.ts | 100 ++++++++++++++++----------------- lang/tomahawk_pl.ts | 100 ++++++++++++++++----------------- lang/tomahawk_pt_BR.ts | 100 ++++++++++++++++----------------- lang/tomahawk_ru.ts | 100 ++++++++++++++++----------------- lang/tomahawk_sv.ts | 100 ++++++++++++++++----------------- lang/tomahawk_tr.ts | 100 ++++++++++++++++----------------- lang/tomahawk_zh_CN.ts | 100 ++++++++++++++++----------------- lang/tomahawk_zh_TW.ts | 100 ++++++++++++++++----------------- 27 files changed, 1353 insertions(+), 1353 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 86170f3cfe..136ec53912 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -615,7 +615,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -623,7 +623,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown مجهول @@ -1188,27 +1188,27 @@ Password NetworkActivityWidget - + Charts الرسوم البيانية - + Last Week الأسبوع الماضي - + Last Month الشهر الماضي - + Last Year العام الماضي - + Overall @@ -3807,7 +3807,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection مجموعتي الخاصة @@ -3892,156 +3892,156 @@ enter the displayed PIN number here: توماهوك - + Back إلى الوراء - + Go back one page العودة صفحة واحدة إلى الوراء - + Forward تقدم - + Go forward one page تقدم صفحة واحدة - - + + Hide Menu Bar إخفي شريط القائمة - - + + Show Menu Bar أظهر شريط القائمة - + Search for any artist, album or song... ابحث عن أي ألبوم، فنان أو أغنية... - + &Main Menu ال&قائمة الرئيسية - + Exit Full Screen الخروج من وضع ملء الشاشة - + Enter Full Screen الدخول إلى وضع ملء الشاشة - + XSPF Error خطأ XSPF - + This is not a valid XSPF playlist. قائمة الأغاني XSPF هذه ليست صالحة. - + Failed to save tracks فشل في حفظ الأغاني - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. بعض الأغاني في قائمة الأغاني لا تحتوي على إسم الفنان أو إسم الأغنية. هذه الأغاني سوف تتجاهل. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. تأكد أن لديك خلفية فونون المناسبة والإضافات المطلوبة مثبتة. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. عذرا، هناك مشكلة في الوصول إلى جهاز الصوت أو الأغنية المطلوب، سوف يتم تخطي الأغنية الحالية. - + Station إذاعة - + Create New Station إنشاء قائمة أغاني جديدة - + Name: الاسم: - + Playlist قائمة الأغاني - + Automatic Playlist قائمة أغاني أوتوماتيكية - + Pause تعليق - + &Play &إستمع - + %1 by %2 track, artist name %1 من قبل %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 حق النشر ٢٠١٠ - ٢٠١٣ - + Thanks to: شكر لكل من: - + About Tomahawk عن توماهوك @@ -4233,48 +4233,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox البريد الوارد - + Listening suggestions from your friends إقتراحات للإستماع من قبل اصدقاءك - - + + This playlist is empty! هذه المجموعة فارغة! - + SuperCollection سوبر كولكشن - + Combined libraries of all your online friends مكتبات مجمعة لكل اصحابك المتصلين - + Recently Played Tracks الأغاني التي إستمعت إليها مؤخرا - + Recently played tracks from all your friends جميع الأغاني التي استمع إليها أصدقائك مؤخرا - + Sorry, we could not find any recent plays! نعتذر، لم نستطيع إيجاد أغاني مسموعة مؤخرا! - + No listening suggestions here. لا إقتراحات للإستماع هنا. diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 436b587895..6344f126fd 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -620,7 +620,7 @@ Tomahawk създаде доклад относно това и изпращай Изглед на скорошната ти активност - + No recently created playlists in your network. Няма наскоро създавани списъци в твоята мрежа @@ -628,7 +628,7 @@ Tomahawk създаде доклад относно това и изпращай DatabaseCommand_AllAlbums - + Unknown Неизвестно @@ -1194,27 +1194,27 @@ Password NetworkActivityWidget - + Charts Класации - + Last Week Миналата седмица - + Last Month Миналия месец - + Last Year Миналата година - + Overall Изцяло @@ -3823,7 +3823,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -3907,159 +3907,159 @@ enter the displayed PIN number here: Tomahawk - + Back Назад - + Go back one page Една страница назад - + Forward Напред - + Go forward one page Една страница напред - - + + Hide Menu Bar Скрий лентата с менюто - - + + Show Menu Bar Покажи лентата с менюто - + Search for any artist, album or song... Търси всеки изпълнител, албум или песен... - + &Main Menu &Основно меню - + Exit Full Screen Излез от режим на цял екран - + Enter Full Screen Превключи в режим на цял екран - + XSPF Error XSPF Грешка - + This is not a valid XSPF playlist. Това не е валиден XSPF списък - + Failed to save tracks Не успях да запазя селектираните изпълнения - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Някои от изпълнения в този списък нямат изпълнител и заглавие. Те ще бъдат игнорирани. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Съжалявам. Има проблем с достъпа до твоето аудио-устройство или до избраната песен - тя ще бъде прескочена. Моля, увери се, че са инсталирани подходящ Phonon и приставки. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Съжалявам. Има проблем с достъпа до твоето аудио устройство или избраната песен. Тя ще бъде пропусната. - + Station Станция - + Create New Station Създай нова станция - + Name: Име: - + Playlist Списък - + Automatic Playlist Автоматично-генериран списък - + Pause Пауза - + &Play &Възпроизвеждане - + %1 by %2 track, artist name %1 от %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Всички права - запазени 2010 - 2013 - + Thanks to: Благодарности на: - + About Tomahawk Относно Tomahawk @@ -4251,49 +4251,49 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Входяща кутия - + Listening suggestions from your friends Предложения за слушане от приятелите ти - - + + This playlist is empty! Списъка е празен! - + SuperCollection Обща колекция /Сборен изглед от локалните и наличните в колекциите на приятелите ти изпълнения/ - + Combined libraries of all your online friends Обща колекция с всичките ми приятели на линия - + Recently Played Tracks Наскоро изпълени - + Recently played tracks from all your friends Наскоро изпълнени от всичките ти приятели - + Sorry, we could not find any recent plays! Съжалявам, но не откривам скорошни списъци - + No listening suggestions here. Няма предложения. diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index 50e12e7656..f00b26afc2 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3877,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4213,48 +4213,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index c6e54f8a42..64fb0ff3a5 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Desconegut @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3807,7 +3807,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meva Col·lecció @@ -3892,156 +3892,156 @@ introduïu el PIN aquí: Tomahawk - + Back Enrere - + Go back one page Retrocedeix una pàgina - + Forward Endavant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Surt de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en desar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4233,48 +4233,48 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! La llista de reproducció és buida - + SuperCollection SuperCol·lecció - + Combined libraries of all your online friends Biblioteques combinades de tots els amis en línia - + Recently Played Tracks Cançons Escoltades Recentment - + Recently played tracks from all your friends Cançons escoltades recentment pels amics - + Sorry, we could not find any recent plays! No s'ha trobat cap reproducció recent - + No listening suggestions here. diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 1e696321b7..96d70d093a 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Desconegut @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3807,7 +3807,7 @@ Intenteu ajustar els filtres per reproduir noves cançons. TomahawkApp - + My Collection La meua Col·lecció @@ -3892,156 +3892,156 @@ introduïu el PIN ací: Tomahawk - + Back Arrere - + Go back one page Retrocedeix una pàgina - + Forward Avant - + Go forward one page Avança una pàgina - - + + Hide Menu Bar Amaga la barra del menú - - + + Show Menu Bar Mostra la barra del menú - + Search for any artist, album or song... Cerca per qualsevol artista, àlbum o cançó... - + &Main Menu &Menú principal - + Exit Full Screen Ix de la pantalla completa - + Enter Full Screen Entra en mode de pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. No és una llista XSPF vàlida. - + Failed to save tracks Error en alçar les cançons - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunes cançons de la llista no contenen ni artista ni titol i s'han ignorat. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hi ha un problema per accedir al dispositiu de so o a la cançó. La cançó actual s'ha saltat. Assegureu-vos que teniu un back.end de Phonon adequant i els plugins necessaris instal·lats. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hi ha un problema per accedir al dispositiu de so o a la cançó, la cançó actual s'ha saltat. - + Station Emissora - + Create New Station Crea una Nova Emissora - + Name: Nom: - + Playlist Llista - + Automatic Playlist Llista Automàtica - + Pause Pausa - + &Play &Reprodueix - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gràcies a: - + About Tomahawk Quant a Tomahawk @@ -4233,48 +4233,48 @@ Podeu reenviar un missatge de sincronisme en qualsevol moment simplement enviant ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! La llista de reproducció és buida - + SuperCollection SuperCol·lecció - + Combined libraries of all your online friends Biblioteques combinades de tots els amis en línia - + Recently Played Tracks Cançons Escoltades Recentment - + Recently played tracks from all your friends Cançons escoltades recentment pels amics - + Sorry, we could not find any recent plays! No s'ha trobat cap reproducció recent - + No listening suggestions here. diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index 3bd62df7f6..a746fe522f 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -615,7 +615,7 @@ se s vámi spojil? Přehled vaší poslední činnosti - + No recently created playlists in your network. Ve vaší síti nejsou žádné nedávno vytvořené seznamy skladeb. @@ -623,7 +623,7 @@ se s vámi spojil? DatabaseCommand_AllAlbums - + Unknown Neznámý @@ -1188,27 +1188,27 @@ heslo NetworkActivityWidget - + Charts Žebříčky - + Last Week Poslední týden - + Last Month Poslední měsíc - + Last Year Poslední rok - + Overall Celkový @@ -3807,7 +3807,7 @@ Zkuste vyladit filtry pro nové písně. TomahawkApp - + My Collection Moje sbírka @@ -3892,156 +3892,156 @@ služby Twitter zde zadejte tam zobrazené číslo PIN: Tomahawk - + Back Zpět - + Go back one page Jít o jednu stranu zpět - + Forward Vpřed - + Go forward one page Jít o jednu stranu vpřed - - + + Hide Menu Bar Skrýt pruh s hlavní nabídkou - - + + Show Menu Bar Ukázat pruh s hlavní nabídkou - + Search for any artist, album or song... Hledat umělce, album nebo píseň... - + &Main Menu Hlavní &nabídka - + Exit Full Screen Ukončit režim na celou obrazovku - + Enter Full Screen Vstoupit do režimu na celou obrazovku - + XSPF Error Chyba XSPF - + This is not a valid XSPF playlist. Toto není platný seznam skladeb XSPF. - + Failed to save tracks Nepodařilo se uložit skladby - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Některé skladby v seznamu skladeb neobsahují ani umělce ani název. Tyto budou přehlíženy. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. Ujistěte se, že máte nainstalováno vhodné jádro Phonona potřebné přídavné moduly. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Je nám to líto, ale Tomahawk nemůže přistupovat k vašemu zvukovému zařízení nebo k žádané skladbě, a proto se nynější skladba přeskakuje. - + Station Stanice - + Create New Station Vytvořit novou stanici - + Name: Název: - + Playlist Seznam skladeb - + Automatic Playlist Automatický seznam skladeb - + Pause Pozastavit - + &Play &Přehrát - + %1 by %2 track, artist name %1 od %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorské právo 2010 - 2013 - + Thanks to: Poděkování: - + About Tomahawk O Tomahawku @@ -4232,48 +4232,48 @@ Kdykoli můžete odeslat novou seřizovací zprávu. ViewManager - + Inbox Doručené - + Listening suggestions from your friends Sledování návrhů od vašich přátel - - + + This playlist is empty! Tento seznam skladeb je prázdný! - + SuperCollection Supersbírka - + Combined libraries of all your online friends Spojená sbírka všech vašich přátel - + Recently Played Tracks Nedávno poslouchané skladby - + Recently played tracks from all your friends Naposledy poslouchané skladby všech vašich přátel - + Sorry, we could not find any recent plays! Promiňte, ale nepodařilo se najít žádné nedávno poslouchané skladby! - + No listening suggestions here. Žádné sledování návrhů. diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 2a4b5d6392..f1308b204c 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Ukendt @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3795,7 +3795,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Min Samling @@ -3879,156 +3879,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF Fejl - + This is not a valid XSPF playlist. Dette er ikke en gyldig XSPF spilleliste - + Failed to save tracks Fejlede i at gemme numrene - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station Lav Ny Station - + Name: Navn: - + Playlist - + Automatic Playlist - + Pause Pause - + &Play - + %1 by %2 track, artist name %1 af %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4215,48 +4215,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection SuperSamling - + Combined libraries of all your online friends Alle dine venners kombineret bibliotek - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index b0f60892dd..514ce88757 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -615,7 +615,7 @@ erlauben sich mit dir zu verbinden? Ein Überblick über Ihre aktuellen Aktivitäten - + No recently created playlists in your network. Es gibt keine kürzlich erstellten Wiedergabelisten in deinem Netzwerk. @@ -623,7 +623,7 @@ erlauben sich mit dir zu verbinden? DatabaseCommand_AllAlbums - + Unknown Unbekannt @@ -1188,27 +1188,27 @@ Passwort NetworkActivityWidget - + Charts Charts - + Last Week Letzte Woche - + Last Month Letzter Monat - + Last Year Letztes Jahr - + Overall Allgemein @@ -3802,7 +3802,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -3887,156 +3887,156 @@ Tomahawk auf Twitter's Website authentifiziert hast: Tomahawk - + Back Zurück - + Go back one page Gehe eine Seite zurück - + Forward Vorwärts - + Go forward one page Gehe eine Seite vorwärts - - + + Hide Menu Bar Menüleiste ausblenden - - + + Show Menu Bar Menüleiste einblenden - + Search for any artist, album or song... Suche nach Künstler, Album oder Lied... - + &Main Menu Haupt&menü - + Exit Full Screen Vollbildmodus deaktivieren - + Enter Full Screen Vollbildmodus aktivieren - + XSPF Error XSPF-Fehler - + This is not a valid XSPF playlist. Dies ist keine gültige XSPF-Playlist. - + Failed to save tracks Konnte Stücke nicht abspeichern - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Einige Stücke in der Playlist enthalten weder Künstler noch Titel. Diese werden ignoriert. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. Vergewisser dich, dass ein geignetes Phonon-Backend mitsamt benötigten Plugins installiert ist. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Es tut uns leid, Tomahawk kann auf dein Audio-Gerät oder das gewünschte Stück nicht zugreifen und überspringt es deshalb. - + Station Station - + Create New Station Neue Station erstellen - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatische Playlist - + Pause Pause - + &Play Abs&pielen - + %1 by %2 track, artist name %1 von %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Danke an: - + About Tomahawk Über Tomahawk @@ -4227,48 +4227,48 @@ Du kannst jederzeit eine neue Sync-Nachricht abschicken. ViewManager - + Inbox Posteingang - + Listening suggestions from your friends Hören sie sich die Vorschläge ihrer Freunde an - - + + This playlist is empty! Diese Playlist ist leer! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Kombinierte Sammlung all deiner Freunde - + Recently Played Tracks Zuletzt gehörte Lieder - + Recently played tracks from all your friends Zuletzt gehörte Lieder all deiner Freunde - + Sorry, we could not find any recent plays! Es konnten keine zuletzt gehörten Songs gefunden werden! - + No listening suggestions here. Es gibt keine Vorschläge hier. diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index efa04b69d8..e2e43d012a 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -614,7 +614,7 @@ connect and stream from you? Μια επισκόπηση των πρόσφατων δραστηριοτήτων σας - + No recently created playlists in your network. Δεν δημιουργηθηκαν πρόσφατα λίστες αναπαραγωγής στο δίκτυό σας. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Άγνωστο @@ -1187,27 +1187,27 @@ Password NetworkActivityWidget - + Charts Γραφήματα - + Last Week Τελευταία εβδομάδα - + Last Month Τελευταίος μήνας - + Last Year Τελευταίο ετος - + Overall Συνολικά @@ -3808,7 +3808,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Η Συλλογή μου @@ -3892,156 +3892,156 @@ enter the displayed PIN number here: Tomahawk - + Back Πίσω - + Go back one page Πήγαινε πίσω μία σελίδα - + Forward Μπροστά - + Go forward one page Πήγαινε μπροστά μία σελίδα - - + + Hide Menu Bar Απόκρυψη Γραμμής Μενού - - + + Show Menu Bar Εμφανιση του κεντρικου μενου - + Search for any artist, album or song... Αναζήτηση για οποιονδήποτε καλλιτέχνη, άλμπουμ ή τραγούδι... - + &Main Menu &Κεντρικο Μενου - + Exit Full Screen Έξοδος από Πλήρη Οθόνη - + Enter Full Screen Εισαγωγή σε Πλήρη Οθόνη - + XSPF Error Σφάλμα XSPF - + This is not a valid XSPF playlist. Αυτή δεν είναι μια έγκυρη λίστα αναπαραγωγής XSPF. - + Failed to save tracks Αποτυχία αποθήκευσης κομματιών. - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Μερικά κομμάτια στην λίστα αναπαραγωγής δεν περιέχουν έναν καλλιτέχνη ή έναν τίτλο. Θα αγνοηθούν. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. Σιγουρευτείτε ότι έχετε εγκαταστήσει ένα κατάλληλο Phonon backend και τα απαιτούμενα πρόσθετα. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Συγγνώμη, υπάρχει ένα πρόβλημα πρόσβασης στην συσκευή ήχου ή στο επιθυμητό κομμάτι, το τρέχον κομμάτι θα παραλειφθεί. - + Station Σταθμός - + Create New Station Δημιουργία Νέου Σταθμού - + Name: Όνομα: - + Playlist Λίστας Αναπαραγωγής - + Automatic Playlist Αυτόματη Λίστα Αναπαραγωγής - + Pause Παύση - + &Play &Αναπαραγωγή - + %1 by %2 track, artist name %1 από %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Χάρη στους: - + About Tomahawk Σχετικά με το Tomahawk @@ -4228,48 +4228,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Εισερχόμενα - + Listening suggestions from your friends Προτάσεις προς ακρόαση από τους φίλους σας - - + + This playlist is empty! Αυτη η λιστα αναπαραγωγης ειναι αδεια. - + SuperCollection ΥπερΣυλλογή - + Combined libraries of all your online friends Συνδυασμένες βιβλιοθήκες όλων των online φίλων σας - + Recently Played Tracks Τελευταίες Αναπαραγωγές Κομματιών - + Recently played tracks from all your friends Τελευταίες αναπαραγωγές από όλους τους φίλους σας - + Sorry, we could not find any recent plays! Συγγνωμη, δεν βρεθηκαν προσφατες αναπαραγωγες! - + No listening suggestions here. Δεν υπάρχουν προτάσεις για ακρόαση εδώ. diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index e58783f104..b8570b9855 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -615,7 +615,7 @@ connect and stream from you? An overview of your recent activity - + No recently created playlists in your network. No recently created playlists in your network. @@ -623,7 +623,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Unknown @@ -1188,27 +1188,27 @@ Password NetworkActivityWidget - + Charts Charts - + Last Week Last Week - + Last Month Last Month - + Last Year Last Year - + Overall Overall @@ -3810,7 +3810,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -3895,156 +3895,156 @@ enter the displayed PIN number here: Tomahawk - + Back Back - + Go back one page Go back one page - + Forward Forward - + Go forward one page Go forward one page - - + + Hide Menu Bar Hide Menu Bar - - + + Show Menu Bar Show Menu Bar - + Search for any artist, album or song... Search for any artist, album or song... - + &Main Menu &Main Menu - + Exit Full Screen Exit Full Screen - + Enter Full Screen Enter Full Screen - + XSPF Error XSPF Error - + This is not a valid XSPF playlist. This is not a valid XSPF playlist. - + Failed to save tracks Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Station - + Create New Station Create New Station - + Name: Name: - + Playlist Playlist - + Automatic Playlist Automatic Playlist - + Pause Pause - + &Play &Play - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk About Tomahawk @@ -4236,48 +4236,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Inbox - + Listening suggestions from your friends Listening suggestions from your friends - - + + This playlist is empty! This playlist is empty! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Combined libraries of all your online friends - + Recently Played Tracks Recently Played Tracks - + Recently played tracks from all your friends Recently played tracks from all your friends - + Sorry, we could not find any recent plays! Sorry, we could not find any recent plays! - + No listening suggestions here. No listening suggestions here. diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 99ce7f874a..9e5841834c 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -615,7 +615,7 @@ conectarse a usted y transmitir música? - + No recently created playlists in your network. @@ -623,7 +623,7 @@ conectarse a usted y transmitir música? DatabaseCommand_AllAlbums - + Unknown Desconocido @@ -1187,27 +1187,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3808,7 +3808,7 @@ Intente ajustar los filtros para reproducir nuevas canciones. TomahawkApp - + My Collection Mi colección @@ -3893,156 +3893,156 @@ introduzca su número PIN aquí: Tomahawk - + Back Atrás - + Go back one page Ir una página hacia atrás - + Forward Adelante - + Go forward one page Ir una página hacia adelante - - + + Hide Menu Bar Ocultar barra de menús - - + + Show Menu Bar Mostrar barra de menús - + Search for any artist, album or song... Buscar un artista, álbum o pista… - + &Main Menu &Menú principal - + Exit Full Screen Salir de pantalla completa - + Enter Full Screen Modo a pantalla completa - + XSPF Error Error XSPF - + This is not a valid XSPF playlist. Esta no es una lista de reproducción XSPF válida. - + Failed to save tracks Fallo al guardar pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunas pistas en la lista de reproducción no contienen artista ni título. Serán ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Se ha producido un error al acceder al dispostivo de audio o a la pista deseada. Asegúrese de que ha instalado un backend de Phonon adecuado y los plugins necesarios. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Se ha producido un error al acceder al dispostivo de audio o a la pista deseado y se va saltar. - + Station Estación - + Create New Station Crear estación nueva - + Name: Nombre: - + Playlist Lista de reproducción - + Automatic Playlist Lista de reproducción automática - + Pause Pausar - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Gracias a: - + About Tomahawk Acerca de Tomahawk @@ -4234,48 +4234,48 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Lista de reproducción vacía - + SuperCollection Supercolección - + Combined libraries of all your online friends Colecciones combinadas de todos sus amigos - + Recently Played Tracks Pistas reproducidas recientemente - + Recently played tracks from all your friends Temas escuchados recientemente por mis amigos - + Sorry, we could not find any recent plays! No hay reproducciones recientes - + No listening suggestions here. diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index f64382db4a..4611652930 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -340,7 +340,7 @@ yhdistää ja toistaa sinulta virtaa? YOUR ARTIST RANK - ARTISTIN SIJOITUKSESI + ARTISTIN SIJOITUS @@ -615,7 +615,7 @@ yhdistää ja toistaa sinulta virtaa? Yleiskatsaus viimeaikaisesta toiminnastasi - + No recently created playlists in your network. Verkossasi ei ole viime aikoina luotuja soittolistoja. @@ -623,7 +623,7 @@ yhdistää ja toistaa sinulta virtaa? DatabaseCommand_AllAlbums - + Unknown Tuntematon @@ -1188,27 +1188,27 @@ salasana NetworkActivityWidget - + Charts Kaaviot - + Last Week Viime viikko - + Last Month Viime kuukausi - + Last Year Viime vuosi - + Overall Kaikkiaan @@ -3813,7 +3813,7 @@ kappaleen %2%4 %3. TomahawkApp - + My Collection Oma kokoelma @@ -3898,156 +3898,156 @@ anna siellä näytetty PIN-koodi tähän: Tomahawk - + Back Takaisin - + Go back one page Mene yksi sivu takaisin - + Forward Eteenpäin - + Go forward one page Mene yksi sivu eteenpäin - - + + Hide Menu Bar Piilota valikkorivi - - + + Show Menu Bar Näytä valikkorivi - + Search for any artist, album or song... Hae artistia, albumia tai kappaletta... - + &Main Menu &Päävalikko - + Exit Full Screen Poistu koko näytöstä - + Enter Full Screen Siirry koko näyttöön - + XSPF Error XSPF-virhe - + This is not a valid XSPF playlist. Tämä ei ole kelvollinen XSPF-soittolista. - + Failed to save tracks Kappaleiden tallentaminen epäonnistui - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Joillakin soittolistan kappaleilla ei ole artistia ja nimeä. Ne jätetään huomiotta. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. Varmista, että sopiva Phononin taustaosa ja vaaditut liitännäiset on asennettu. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Valitettavasti äänilaitteen tai halutun kappaleen kanssa on ongelmia ja nykyinen kappale ohitetaan. - + Station Asema - + Create New Station Luo uusi asema - + Name: Nimi: - + Playlist Soittolista - + Automatic Playlist Automaattinen soittolista - + Pause Tauko - + &Play &Soita - + %1 by %2 track, artist name %1 artistilta %2 - + %1 - %2 current track, some window title %1 – %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010–2013 - + Thanks to: Kiitokset: - + About Tomahawk Tietoa Tomahawkista @@ -4075,12 +4075,12 @@ anna siellä näytetty PIN-koodi tähän: # PLAYS / ARTIST - SOITTOKERTOJA / ARTISTI + SOITTOJA / ARTISTI YOUR SONG RANK - KAPPALEEN SIJOITUKSESI + KAPPALEEN SIJOITUS @@ -4239,48 +4239,48 @@ Voit lähettää synkronointiviestin uudelleen millä hetkellä hyvänsä lähet ViewManager - + Inbox Saapuneet - + Listening suggestions from your friends Kaveriesi lähettämät kuunteluehdotukset - - + + This playlist is empty! Tämä soittolista on tyhjä! - + SuperCollection Superkokoelma - + Combined libraries of all your online friends Kaikkien verkkokaveriesi yhdistetyt kirjastot - + Recently Played Tracks Viime aikoina kuunnellut kappaleet - + Recently played tracks from all your friends Kaikkien kaveriesi viime aikoina kuuntelemat kappaleet - + Sorry, we could not find any recent plays! Valitettavasti emme löytäneet yhtään viimeaikaisia soittoja! - + No listening suggestions here. Ei kuunteluehdotuksia. diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 2b0cf35cdf..4cff41d15f 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -615,7 +615,7 @@ de se connecter et streamer de vous? - + No recently created playlists in your network. @@ -623,7 +623,7 @@ de se connecter et streamer de vous? DatabaseCommand_AllAlbums - + Unknown Inconnu @@ -1187,27 +1187,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3805,7 +3805,7 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. TomahawkApp - + My Collection Ma Collection @@ -3890,156 +3890,156 @@ saisissez le numéro PIN ici : Tomahawk - + Back Retour - + Go back one page Reculer d'une page - + Forward Avancer - + Go forward one page Avancer d'une page - - + + Hide Menu Bar Masquer la barre de menu - - + + Show Menu Bar Afficher la barre de menu - + Search for any artist, album or song... Chercher un artiste, un album, ou un morceau... - + &Main Menu &Menu Principal - + Exit Full Screen Quitter le mode plein écran - + Enter Full Screen Activer le mode plein écran - + XSPF Error Erreur XSPF - + This is not a valid XSPF playlist. Ceci n'est pas une liste de lecture XSPF valide. - + Failed to save tracks Échec de la sauvegarde des pistes - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Certaines pistes dans la liste de lecture ne contiennent pas d'artiste ou de titre. Elles seront ignorées. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sautée. Vérifiez que vous avez un backend Phonon et les plugins requis installés. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours , celle-ci va être sautée. - + Station Station - + Create New Station Créer une nouvelle station - + Name: Nom : - + Playlist Liste de lecture - + Automatic Playlist Liste de lecture automatique - + Pause Pause - + &Play &Lire - + %1 by %2 track, artist name %1 par %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Droit d'auteur 2010 - 2013 - + Thanks to: Merci a: - + About Tomahawk A propos de Tomahawk @@ -4231,48 +4231,48 @@ Vous pouvez envoyer un message de synchronisation quand vous le souhaitez en env ViewManager - + Inbox Boîte de réception - + Listening suggestions from your friends Suggestions d'écoute de vos amis - - + + This playlist is empty! Cette liste de lecture est vide! - + SuperCollection SuperCollection - + Combined libraries of all your online friends Collections regroupant toutes celles de vos amis en ligne - + Recently Played Tracks Derniers titres joués - + Recently played tracks from all your friends Derniers titres joués par vos amis - + Sorry, we could not find any recent plays! Désolé, aucune piste récemment jouée n'a pu être trouvée ! - + No listening suggestions here. Aucune suggestion d'écoute. diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 9c08a1e8e0..0347b98040 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Descoñecido @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3807,7 +3807,7 @@ Proba a trocar os filtros para ter outra lista música para escoitar. TomahawkApp - + My Collection A miña colección @@ -3891,157 +3891,157 @@ enter the displayed PIN number here: Tomahawk - + Back Atrás - + Go back one page Ir unha páxina atrás - + Forward Adiante - + Go forward one page Ir unha páxina adiante - - + + Hide Menu Bar Agochar a barra de menú - - + + Show Menu Bar Mostrar a barra de menú - + Search for any artist, album or song... Buscar a calquera artista, álbum ou canción... - + &Main Menu Menú &principal - + Exit Full Screen Saír da pantalla ao completo - + Enter Full Screen Entrar na pantalla ao completo - + XSPF Error Erro XSPF - + This is not a valid XSPF playlist. Esta non é unha lista de XSPF válida. - + Failed to save tracks Fallou o gardado de pistas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algunhas pistas na lista de reprodución non indican nin artista nin o título. Ignoraranse. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. Asegúrate de ter o motor Phonon e os engadidos necesarios instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Hai un problema accedendo ao teu dispositivo de son ou a pista que quere así que se omitirá. - + Station - + Create New Station Crear unha nova emisión - + Name: Nome: - + Playlist Lista de reprodución - + Automatic Playlist Lista de reprodución automática - + Pause Pausa - + &Play &Reproducir - + %1 by %2 track, artist name %1 por %2 - + %1 - %2 current track, some window title %1.- %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecementos: - + About Tomahawk Acerca de Tomahawk @@ -4233,48 +4233,48 @@ Podes reenviar e sincronizar as mensaxes en calquera momento simplemente enviand ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Esta lista de reprodución está baleira! - + SuperCollection Supercolección - + Combined libraries of all your online friends Bibliotecas combinadas de todas as túas amizades - + Recently Played Tracks Pistas recentemente reproducidas - + Recently played tracks from all your friends Pistas escoitadas recentemente polas túas amizades - + Sorry, we could not find any recent plays! Non se atoparan reproducións recentes! - + No listening suggestions here. diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 2bf108dd58..36ff6e9f00 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown अज्ञात @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3877,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4213,48 +4213,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index c893bb955a..0d412ec20d 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Ismeretlen @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Saját kollekció @@ -3877,156 +3877,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF hiba - + This is not a valid XSPF playlist. Nem érvényes XSPF lejátszólista. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Rádióállomás - + Create New Station - + Name: - + Playlist Lejátszólista - + Automatic Playlist Automatikus lejátszólista - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk Tomahawkról @@ -4213,48 +4213,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Ez a lejátszólista üres! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks Mostanában játszott zeneszámok - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 670abda6e5..67376350d8 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3877,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4213,48 +4213,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 7d2f381381..141163ae65 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Sconosciuto @@ -1187,27 +1187,27 @@ temporanea NetworkActivityWidget - + Charts Classifiche - + Last Week Ultima settimana - + Last Month Ultimo mese - + Last Year Ultimo anno - + Overall @@ -3795,7 +3795,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection La mia collezione @@ -3879,156 +3879,156 @@ enter the displayed PIN number here: Tomahawk - + Back Indietro - + Go back one page Vai indietro di una pagina - + Forward Avanti - + Go forward one page Vai avanti di una pagina - - + + Hide Menu Bar Nascondi barra menu - - + + Show Menu Bar Mostra barra menu - + Search for any artist, album or song... Cerca qualunque artista, album o canzone... - + &Main Menu &Menu principale - + Exit Full Screen Esci da schermo intero - + Enter Full Screen Modalità schermo intero - + XSPF Error Errore XSPF - + This is not a valid XSPF playlist. Questa non è una valida playlist XSPF. - + Failed to save tracks Errore nel salvare le tracce - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Alcune tracce nella playlist non contengono l'artista e il titolo. Verrano ignorate. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. Assicurati di avere le giuste librerie Phonon e i plugin necessari installati. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Spiacente, c'è un problema nell'accedere al tuo dispositivo audio o alla traccia desiderata, questa traccia verrà saltata. - + Station Stazione - + Create New Station Crea una nuova stazione - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist automatica - + Pause Pausa - + &Play Ri&produci - + %1 by %2 track, artist name %1 di %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Grazie a: - + About Tomahawk Info su Tomahawk @@ -4215,48 +4215,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox Inbox - + Listening suggestions from your friends Suggerimenti per l'ascolto dai tuoi amici. - - + + This playlist is empty! Questa playlist è vuota! - + SuperCollection Supercollezione - + Combined libraries of all your online friends Collezioni combinate di tutti i tuoi amici online - + Recently Played Tracks Tracce ascoltate di recente - + Recently played tracks from all your friends Tracce ascoltate di recente dai tuoi amici - + Sorry, we could not find any recent plays! Spiacente, non sono riuscito a trovare nessuna riproduzione recente! - + No listening suggestions here. Nessun suggerimento musicale qui. diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 1acd6c5bb1..7b1fd489d6 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -615,7 +615,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -623,7 +623,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown 不明 @@ -1187,27 +1187,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3805,7 +3805,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection マイコレクション @@ -3890,156 +3890,156 @@ enter the displayed PIN number here: Tomahawk - + Back プレイリスト - + Go back one page 前のページ - + Forward 次へ - + Go forward one page 次のページ - - + + Hide Menu Bar メニューバーを隠す - - + + Show Menu Bar メニューバーを表示 - + Search for any artist, album or song... アーティスト、又はアルバムや曲で検索して下さい - + &Main Menu メインメニュー - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPFエラー - + This is not a valid XSPF playlist. このプレイリストは有利なXSPFプレイリストではありません。 - + Failed to save tracks トラックの保存に失敗しました。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. プレイリストにアーティストもタイトルの無いトラックが見つかりました。この項目は無視されます。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。適しているPhononのバックエンドを確認の上、必須プラグインのインストールを確認して下さい。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. オーディオデバイス、又は要求トラックをアクセスすることができませんでしたので、このトラックは無視されます。 - + Station ステーション - + Create New Station 新規ステーションを作成 - + Name: 名前: - + Playlist プレイリスト - + Automatic Playlist 自動プレイリスト - + Pause 一時停止 - + &Play 再生 - + %1 by %2 track, artist name %1 by %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Thanks to: - + About Tomahawk Tomahawkについて @@ -4231,48 +4231,48 @@ Twitterを使っている友達にTomahawkを接続したいなら、ツイー ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! このプレイリストには何も入っていません。 - + SuperCollection スーパーコレクション - + Combined libraries of all your online friends オンラインの友達全員のライブラリ - + Recently Played Tracks 最近再生したトラック - + Recently played tracks from all your friends 友達の最近再生したトラック - + Sorry, we could not find any recent plays! 最近の再生した項目が見つかりませんでした。 - + No listening suggestions here. diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index 7062b6346d..adbbb46d17 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mano kolekcija @@ -3877,156 +3877,156 @@ enter the displayed PIN number here: Tomahawk - + Back Atgal - + Go back one page Grįžti vienu puslapiu atgal - + Forward Pirmyn - + Go forward one page Eiti vienu puslapiu pirmyn - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF klaida - + This is not a valid XSPF playlist. - + Failed to save tracks Nepavyko išsaugoti takelių - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station Stotis - + Create New Station Sukurti naują stotį - + Name: Pavadinimas: - + Playlist Grojaraštis - + Automatic Playlist Automatinis grojaraštis - + Pause Pristabdyti - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Autorinės teisės 2010 - 2013 - + Thanks to: Dėkojame: - + About Tomahawk Apie Tomahawk @@ -4213,48 +4213,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection Super kolekcija - + Combined libraries of all your online friends Jungtinė visų Jūsų prisijungusių draugų kolekcija - + Recently Played Tracks Neseniai groti takeliai - + Recently played tracks from all your friends Visų Jūsų draugų neseniai groti takeliai - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index d049a4fc67..97ac174a52 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -615,7 +615,7 @@ połączyć się i strumieniować od ciebie? - + No recently created playlists in your network. @@ -623,7 +623,7 @@ połączyć się i strumieniować od ciebie? DatabaseCommand_AllAlbums - + Unknown Nieznany @@ -1187,27 +1187,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3802,7 +3802,7 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. TomahawkApp - + My Collection Moja Kolekcja @@ -3887,156 +3887,156 @@ wprowadź pokazany numer PIN tutaj: Tomahawk - + Back Wstecz - + Go back one page Cofnij o jedną stronę - + Forward Naprzód - + Go forward one page Przejdź naprzód o jedną stronę - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error Błąd XSPF - + This is not a valid XSPF playlist. To nie jest poprawna lista XSPF. - + Failed to save tracks Nie udało się zapisać utworów - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Niektóre utwory na liście nie zawierają artysty i tytułu. Zostaną one zignorowane. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Przepraszamy, wystąpił problem z połączeniem z twoim urządzeniem audio lub z żądanym utworem, zostanie on pominięty. - + Station - + Create New Station Utwórz Nową Stację - + Name: Nazwa: - + Playlist - + Automatic Playlist - + Pause Pauza - + &Play - + %1 by %2 track, artist name %1 wykonawcy %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Podziękowania dla: - + About Tomahawk O Tomahawku @@ -4228,48 +4228,48 @@ Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyśl ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection SuperKolekcja - + Combined libraries of all your online friends Połączone biblioteki wszystkich twoich znajomych online - + Recently Played Tracks Ostatnio odtwarzane utwory - + Recently played tracks from all your friends Utwory ostatnio odtwarzane przez twoich znajomych - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 0bd07993a4..a7cbadb233 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -615,7 +615,7 @@ se conecte e faça o stream de você? - + No recently created playlists in your network. @@ -623,7 +623,7 @@ se conecte e faça o stream de você? DatabaseCommand_AllAlbums - + Unknown Desconhecido @@ -1187,27 +1187,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3802,7 +3802,7 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. TomahawkApp - + My Collection Minha Coleção @@ -3887,156 +3887,156 @@ colocar o número PIN mostrado aqui: Tomahawk - + Back Voltar - + Go back one page Voltar uma página - + Forward Avançar - + Go forward one page Avançar uma página - - + + Hide Menu Bar Esconder barra de menu - - + + Show Menu Bar Mostrar barra de menu - + Search for any artist, album or song... Pesquisar por qualquer artista, álbum ou música... - + &Main Menu &Menu principal - + Exit Full Screen - + Enter Full Screen - + XSPF Error Erro de XSPF - + This is not a valid XSPF playlist. Esta não é uma lista de reprodução XSPF válida. - + Failed to save tracks Falha ao salvar faixas - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Algumas faixas da lista de reprodução não contem artista e título. Estas serão ignoradas. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. Certifique-se de ter um backend do Phonon adequado e os plugins necessários instalados. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Desculpe, há um problema ao acessar sua placa de áudio ou a faixa desejada, a faixa atual será ignorada. - + Station Estação - + Create New Station Criar uma nova estação - + Name: Nome: - + Playlist Playlist - + Automatic Playlist Playlist Automática - + Pause PIN do Twitter - + &Play Re&produzir - + %1 by %2 track, artist name %1 de %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Agradecimentos: - + About Tomahawk Sobre o Tomahawk @@ -4228,48 +4228,48 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Essa lista de reprodução está vazia! - + SuperCollection SuperColeção - + Combined libraries of all your online friends Bibliotecas combinadas de todos os seus amigos online - + Recently Played Tracks Faixas Reproduzidas Recentemente - + Recently played tracks from all your friends Faixas reproduzidas recentemente por todos os seus amigos - + Sorry, we could not find any recent plays! Desculpe, não foi possível encontrar reproduções recentes! - + No listening suggestions here. diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index d7f951ac35..7fb6223361 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -618,7 +618,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -626,7 +626,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Неизвестный @@ -1190,27 +1190,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3809,7 +3809,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя Коллекция @@ -3893,156 +3893,156 @@ enter the displayed PIN number here: Tomahawk - + Back Назад - + Go back one page Перейти на предыдущую страницу - + Forward Вперед - + Go forward one page Перейдите на следующую страницу - - + + Hide Menu Bar Спрятать Строку Меню - - + + Show Menu Bar Показать Строку Меню - + Search for any artist, album or song... Поиск любого исполнителя, альбома или песни ... - + &Main Menu &Главное меню - + Exit Full Screen Выход из полноэкранного режима - + Enter Full Screen Переход в полноэкранный режим - + XSPF Error Ошибка XSPF - + This is not a valid XSPF playlist. Это не является допустимым XSPF плейлистом. - + Failed to save tracks Не удалось сохранить песни - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Некоторые песни в плейлисте не содержат исполнителя и название. Они будут проигнорированы. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. Убедитесь, что у вас есть подходящий Phonon backend и необходимые плагины установлены. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. К сожалению, есть проблемы с доступом к аудио устройству или данной песне, текущая песня будет пропущена. - + Station Станция - + Create New Station Создать Новую Станцию - + Name: Имя: - + Playlist Плейлист - + Automatic Playlist Автоматический Плейлист - + Pause Пауза - + &Play &Играть - + %1 by %2 track, artist name %1 %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Авторское право 2010 - 2013 - + Thanks to: Благодарность - + About Tomahawk О Tomahawk @@ -4234,48 +4234,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! Плейлист пуст. - + SuperCollection Общая Коллекция - + Combined libraries of all your online friends Комбинированная библиотека всех ваших друзей онлайн - + Recently Played Tracks Последние Воспроизводимые Песни - + Recently played tracks from all your friends Последние воспроизводимые песни все ваших друзей - + Sorry, we could not find any recent plays! К сожалению, мы не смогли найти никаких воспроизвидений песен! - + No listening suggestions here. diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index a63ca8a211..7bab23b63c 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -615,7 +615,7 @@ ansluta och strömma från dig? Överblick över din senaste aktivitet - + No recently created playlists in your network. Det finns inga nyligen skapade spellistor på ditt nätverk @@ -623,7 +623,7 @@ ansluta och strömma från dig? DatabaseCommand_AllAlbums - + Unknown Okänt @@ -1188,27 +1188,27 @@ Password NetworkActivityWidget - + Charts Listor - + Last Week Förra veckan - + Last Month Förra månaden - + Last Year Förra året - + Overall Generell @@ -3808,7 +3808,7 @@ Försök att ändra i filtrerna för att få en ny låtlista TomahawkApp - + My Collection Min samling @@ -3893,156 +3893,156 @@ anger du PIN-koden här: Tomahawk - + Back Tillbaka - + Go back one page Gå tillbaks en sida - + Forward Framåt - + Go forward one page Gå framåt en sida - - + + Hide Menu Bar Göm Menyrad - - + + Show Menu Bar Visa Menyrad - + Search for any artist, album or song... Sök efter valfri artist, album eller låt... - + &Main Menu &Huvudmeny - + Exit Full Screen Gå ur fullskärmsläge - + Enter Full Screen Fullskärmsläge - + XSPF Error XSPF-fel - + This is not a valid XSPF playlist. Detta är inte en giltig XSPF-spellista. - + Failed to save tracks Misslyckades med att spara spår - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. Några spår i spellistan innehåller inte någon artist och titel. De kommer att ignoreras. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. Tyvärr! Det uppstod ett problem med kontakten till ditt ljudkort eller det önskade spåret. Nuvarande spår kommer att hoppas över. Kontrollera att du har en lämplig Phonon-backend och alla nödvändiga plugins installerade - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Tyvärr blev det problem att hitta din ljudenhet eller den valda låten! Nuvarande låt kommer att hoppas över - + Station Station - + Create New Station Skapa ny station - + Name: Namn: - + Playlist Spellista - + Automatic Playlist Automatisk spellista - + Pause Paus - + &Play &Spela - + %1 by %2 track, artist name %1 av %2 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 Copyright 2010 - 2013 - + Thanks to: Tack till: - + About Tomahawk Om Tomahawk @@ -4234,48 +4234,48 @@ Du kan skicka om ett synkat meddelande när som helst genom att skicka ett tweet ViewManager - + Inbox Inkorg - + Listening suggestions from your friends Lyssnar-rekommendationer från dina vänner - - + + This playlist is empty! Spellistan är tom! - + SuperCollection Supersamling - + Combined libraries of all your online friends Kombinerat bibliotek av alla dina vänner online - + Recently Played Tracks Senast spelade spår - + Recently played tracks from all your friends Alla dina vänners senast spelade spår - + Sorry, we could not find any recent plays! Tyvärr! Det gick inte hitta några nyligen spelade spår - + No listening suggestions here. Det finns inga rekommendationer här. diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 85d4cd4088..378ba46a59 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown Bilinmeyen @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3877,156 +3877,156 @@ enter the displayed PIN number here: - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error - + This is not a valid XSPF playlist. - + Failed to save tracks - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: - + Playlist - + Automatic Playlist - + Pause - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4213,48 +4213,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection - + Combined libraries of all your online friends - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 06e5eb08ea..9f960f0ea0 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown 未知 @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3803,7 +3803,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3888,156 +3888,156 @@ enter the displayed PIN number here: Tomahawk - + Back 后退 - + Go back one page 转向上一页 - + Forward 下一个 - + Go forward one page 转向下一页 - - + + Hide Menu Bar 隐藏菜单栏 - - + + Show Menu Bar 显示菜单栏 - + Search for any artist, album or song... 搜索任意艺人,专辑,或歌曲... - + &Main Menu 主菜单 - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 错误 - + This is not a valid XSPF playlist. 这不是一个合法的 XSPF 播放列表。 - + Failed to save tracks 保存歌曲失败。 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. 播放列表中的一些歌曲缺失艺术家和标题,它们将被忽略。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. 抱歉,访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。请确认你正在使用合适的 Phonon 后端并安装了必要的插件。 - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. 抱歉,在访问音频设备或者指定的歌曲时出错。当前歌曲将被跳过。 - + Station 电台 - + Create New Station 创建新电台 - + Name: 名字: - + Playlist 播放列表 - + Automatic Playlist 自动播放列表 - + Pause 暂停 - + &Play 播放 - + %1 by %2 track, artist name %2 的 %1 - + %1 - %2 current track, some window title %1 - %2 - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 版权所有 2010 - 2013 - + Thanks to: 感谢: - + About Tomahawk 关于 Tomahawk @@ -4229,48 +4229,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! 当前播放列表为空! - + SuperCollection 超级收藏 - + Combined libraries of all your online friends 当前在线的朋友的音乐库集合 - + Recently Played Tracks 最近播放歌曲 - + Recently played tracks from all your friends 所有朋友最近播放的歌曲 - + Sorry, we could not find any recent plays! 对不起,找不到任何最近播放项目! - + No listening suggestions here. diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 87c4ba26c3..9c6466745e 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -614,7 +614,7 @@ connect and stream from you? - + No recently created playlists in your network. @@ -622,7 +622,7 @@ connect and stream from you? DatabaseCommand_AllAlbums - + Unknown 未知 @@ -1186,27 +1186,27 @@ Password NetworkActivityWidget - + Charts - + Last Week - + Last Month - + Last Year - + Overall @@ -3793,7 +3793,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection 我的收藏 @@ -3877,156 +3877,156 @@ enter the displayed PIN number here: Tomahawk - + Back - + Go back one page - + Forward - + Go forward one page - - + + Hide Menu Bar - - + + Show Menu Bar - + Search for any artist, album or song... - + &Main Menu - + Exit Full Screen - + Enter Full Screen - + XSPF Error XSPF 錯誤 - + This is not a valid XSPF playlist. - + Failed to save tracks 無法儲存曲目 - + Some tracks in the playlist do not contain an artist and a title. They will be ignored. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. - + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. - + Station - + Create New Station - + Name: 名稱: - + Playlist - + Automatic Playlist - + Pause 暫停 - + &Play - + %1 by %2 track, artist name - + %1 - %2 current track, some window title - + <h2><b>Tomahawk %1<br/>(%2)</h2> <h2><b>Tomahawk %1<br/>(%2)</h2> - + <h2><b>Tomahawk %1</h2> <h2><b>Tomahawk %1</h2> - + Copyright 2010 - 2013 - + Thanks to: - + About Tomahawk @@ -4213,48 +4213,48 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + Inbox - + Listening suggestions from your friends - - + + This playlist is empty! - + SuperCollection 超級收藏 - + Combined libraries of all your online friends 聯合您所有線上朋友的音樂庫 - + Recently Played Tracks - + Recently played tracks from all your friends - + Sorry, we could not find any recent plays! - + No listening suggestions here. From 0403301c9a003e849f7a64d762221b802d2da708 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 10:58:12 +0200 Subject: [PATCH 504/565] Move ScriptEngine into its own files --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/resolvers/JSResolver.cpp | 95 +-------------- src/libtomahawk/resolvers/JSResolver.h | 28 +---- src/libtomahawk/resolvers/ScriptEngine.cpp | 128 +++++++++++++++++++++ src/libtomahawk/resolvers/ScriptEngine.h | 58 ++++++++++ 5 files changed, 190 insertions(+), 120 deletions(-) create mode 100644 src/libtomahawk/resolvers/ScriptEngine.cpp create mode 100644 src/libtomahawk/resolvers/ScriptEngine.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index cce9aa59f0..51ec98f371 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -99,6 +99,7 @@ set( libGuiSources resolvers/ExternalResolverGui.cpp resolvers/ScriptResolver.cpp resolvers/JSResolver.cpp + resolvers/ScriptEngine.cpp utils/ImageRegistry.cpp utils/WidgetDragFilter.cpp diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index c72ca7ba9a..4edc8c4a6d 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -33,6 +33,7 @@ #include "Pipeline.h" #include "Result.h" #include "ScriptCollection.h" +#include "ScriptEngine.h" #include "SourceList.h" #include "TomahawkSettings.h" #include "TomahawkVersion.h" @@ -1100,97 +1101,3 @@ JSResolver::resolverCollections() // Then when there's callbacks from a resolver, it sends source name, collection id // + data. } - - -ScriptEngine::ScriptEngine( JSResolver* parent ) - : QWebPage( (QObject*) parent ) - , m_parent( parent ) -{ - settings()->setAttribute( QWebSettings::OfflineStorageDatabaseEnabled, true ); - settings()->setOfflineStoragePath( TomahawkUtils::appDataDir().path() ); - settings()->setAttribute(QWebSettings::LocalStorageEnabled, true ); - settings()->setLocalStoragePath( TomahawkUtils::appDataDir().path() ); - settings()->setAttribute( QWebSettings::LocalStorageDatabaseEnabled, true ); - settings()->setAttribute( QWebSettings::LocalContentCanAccessFileUrls, true ); - settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true ); - - // Tomahawk is not a user agent - m_header = QWebPage::userAgentForUrl( QUrl() ).replace( QString( "%1/%2" ) - .arg( TOMAHAWK_APPLICATION_NAME ) - .arg( TOMAHAWK_VERSION ) - ,""); - tLog( LOGVERBOSE ) << "JSResolver Using header" << m_header; - - connect( networkAccessManager(), SIGNAL( sslErrors( QNetworkReply*, QList ) ), - SLOT( sslErrorHandler( QNetworkReply*, QList ) ) ); -} - - -void -ScriptEngine::javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID ) -{ - tLog() << "JAVASCRIPT:" << m_scriptPath << message << lineNumber << sourceID; - #ifndef DEBUG_BUILD - JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Script Resolver Error: %1 %2 %3 %4" ).arg( m_scriptPath ).arg( message ).arg( lineNumber ).arg( sourceID ) ) ); - #endif -} - - -void -ScriptEngine::sslErrorHandler( QNetworkReply* qnr, const QList& errlist ) -{ - tDebug() << Q_FUNC_INFO; - - QByteArray digest = errlist.first().certificate().digest(); - int result = -1; - - if ( !TomahawkSettings::instance()->isSslCertKnown( digest ) ) - { - foreach ( const QSslError& err, errlist ) - tDebug() << Q_FUNC_INFO << "SSL error:" << err; - - QMessageBox question( TomahawkUtils::tomahawkWindow() ); - question.setWindowTitle( tr( "SSL Error" ) ); - question.setText( tr( "You have asked Tomahawk to connect securely to %1, but we can't confirm that your connection is secure:

    " - "%2

    " - "Do you want to trust this connection?" ) - .arg( qnr->url().host() ) - .arg( errlist.first().errorString() ) ); - - question.setStandardButtons( QMessageBox::No ); - question.addButton( tr( "Trust certificate" ), QMessageBox::AcceptRole ); - - result = question.exec(); - - //FIXME: discuss whether we want to store rejects, too (needs settings management to remove the decision?) - if ( result == QMessageBox::AcceptRole ) - TomahawkSettings::instance()->setSslCertTrusted( digest, result == QMessageBox::AcceptRole ); - } - - if ( TomahawkSettings::instance()->isSslCertTrusted( digest ) ) - { - qnr->ignoreSslErrors(); - } -} - - -QString -ScriptEngine::userAgentForUrl( const QUrl& url ) const -{ - Q_UNUSED( url ); - return m_header; -} - - -void -ScriptEngine::setScriptPath( const QString& scriptPath ) -{ - m_scriptPath = scriptPath; -} - - -bool -ScriptEngine::shouldInterruptJavaScript() -{ - return true; -} diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index 461f1649f9..c430003ade 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Leo Franchi * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +37,7 @@ #include "DllMacro.h" class JSResolver; +class ScriptEngine; class DLLEXPORT JSResolverHelper : public QObject { @@ -86,32 +88,6 @@ public slots: JSResolver* m_resolver; }; -class DLLEXPORT ScriptEngine : public QWebPage -{ -Q_OBJECT - -public: - explicit ScriptEngine( JSResolver* parent ); - - QString userAgentForUrl( const QUrl& url ) const; - void setScriptPath( const QString& scriptPath ); - -public slots: - bool shouldInterruptJavaScript(); - -protected: - virtual void javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID ); - -private slots: - void sslErrorHandler( QNetworkReply* qnr, const QList& errlist ); - -private: - JSResolver* m_parent; - QString m_scriptPath; - QString m_header; -}; - - class DLLEXPORT JSResolver : public Tomahawk::ExternalResolverGui { Q_OBJECT diff --git a/src/libtomahawk/resolvers/ScriptEngine.cpp b/src/libtomahawk/resolvers/ScriptEngine.cpp new file mode 100644 index 0000000000..dd610a3048 --- /dev/null +++ b/src/libtomahawk/resolvers/ScriptEngine.cpp @@ -0,0 +1,128 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "ScriptEngine.h" + +#include "jobview/ErrorStatusMessage.h" +#include "jobview/JobStatusModel.h" +#include "jobview/JobStatusView.h" +#include "utils/Logger.h" +#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" +#include "TomahawkSettings.h" +#include "TomahawkVersion.h" + +#include +#include + +ScriptEngine::ScriptEngine( JSResolver* parent ) + : QWebPage( (QObject*) parent ) + , m_parent( parent ) +{ + settings()->setAttribute( QWebSettings::OfflineStorageDatabaseEnabled, true ); + settings()->setOfflineStoragePath( TomahawkUtils::appDataDir().path() ); + settings()->setAttribute(QWebSettings::LocalStorageEnabled, true ); + settings()->setLocalStoragePath( TomahawkUtils::appDataDir().path() ); + settings()->setAttribute( QWebSettings::LocalStorageDatabaseEnabled, true ); + settings()->setAttribute( QWebSettings::LocalContentCanAccessFileUrls, true ); + settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true ); + + // Tomahawk is not a user agent + m_header = QWebPage::userAgentForUrl( QUrl() ).replace( QString( "%1/%2" ) + .arg( TOMAHAWK_APPLICATION_NAME ) + .arg( TOMAHAWK_VERSION ) + ,""); + tLog( LOGVERBOSE ) << "JSResolver Using header" << m_header; + + connect( networkAccessManager(), SIGNAL( sslErrors( QNetworkReply*, QList ) ), + SLOT( sslErrorHandler( QNetworkReply*, QList ) ) ); +} + + +void +ScriptEngine::javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID ) +{ + tLog() << "JAVASCRIPT:" << m_scriptPath << message << lineNumber << sourceID; + #ifndef DEBUG_BUILD + JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Script Resolver Error: %1 %2 %3 %4" ).arg( m_scriptPath ).arg( message ).arg( lineNumber ).arg( sourceID ) ) ); + #endif +} + + +void +ScriptEngine::sslErrorHandler( QNetworkReply* qnr, const QList& errlist ) +{ + tDebug() << Q_FUNC_INFO; + + QByteArray digest = errlist.first().certificate().digest(); + int result = -1; + + if ( !TomahawkSettings::instance()->isSslCertKnown( digest ) ) + { + foreach ( const QSslError& err, errlist ) + tDebug() << Q_FUNC_INFO << "SSL error:" << err; + + QMessageBox question( TomahawkUtils::tomahawkWindow() ); + question.setWindowTitle( tr( "SSL Error" ) ); + question.setText( tr( "You have asked Tomahawk to connect securely to %1, but we can't confirm that your connection is secure:

    " + "%2

    " + "Do you want to trust this connection?" ) + .arg( qnr->url().host() ) + .arg( errlist.first().errorString() ) ); + + question.setStandardButtons( QMessageBox::No ); + question.addButton( tr( "Trust certificate" ), QMessageBox::AcceptRole ); + + result = question.exec(); + + //FIXME: discuss whether we want to store rejects, too (needs settings management to remove the decision?) + if ( result == QMessageBox::AcceptRole ) + TomahawkSettings::instance()->setSslCertTrusted( digest, result == QMessageBox::AcceptRole ); + } + + if ( TomahawkSettings::instance()->isSslCertTrusted( digest ) ) + { + qnr->ignoreSslErrors(); + } +} + + +QString +ScriptEngine::userAgentForUrl( const QUrl& url ) const +{ + Q_UNUSED( url ); + return m_header; +} + + +void +ScriptEngine::setScriptPath( const QString& scriptPath ) +{ + m_scriptPath = scriptPath; +} + + +bool +ScriptEngine::shouldInterruptJavaScript() +{ + return true; +} + diff --git a/src/libtomahawk/resolvers/ScriptEngine.h b/src/libtomahawk/resolvers/ScriptEngine.h new file mode 100644 index 0000000000..c90377527a --- /dev/null +++ b/src/libtomahawk/resolvers/ScriptEngine.h @@ -0,0 +1,58 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef SCRIPTENGINE_H +#define SCRIPTENGINE_H + +#include "DllMacro.h" + +#include +#include + +class JSResolver; +class QNetworkReply; + +class DLLEXPORT ScriptEngine : public QWebPage +{ +Q_OBJECT + +public: + explicit ScriptEngine( JSResolver* parent ); + + QString userAgentForUrl( const QUrl& url ) const; + void setScriptPath( const QString& scriptPath ); + +public slots: + bool shouldInterruptJavaScript(); + +protected: + virtual void javaScriptConsoleMessage( const QString& message, int lineNumber, const QString& sourceID ); + +private slots: + void sslErrorHandler( QNetworkReply* qnr, const QList& errlist ); + +private: + JSResolver* m_parent; + QString m_scriptPath; + QString m_header; +}; + +#endif // SCRIPTENGINE_H From 402a7ed6d426b76fcd57baae72e5f826a8901ab7 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 11:11:37 +0200 Subject: [PATCH 505/565] Move JSResolverHelper to its own files --- src/libtomahawk/CMakeLists.txt | 1 + src/libtomahawk/resolvers/JSResolver.cpp | 330 +--------------- src/libtomahawk/resolvers/JSResolver.h | 51 +-- .../resolvers/JSResolverHelper.cpp | 359 ++++++++++++++++++ src/libtomahawk/resolvers/JSResolverHelper.h | 83 ++++ 5 files changed, 445 insertions(+), 379 deletions(-) create mode 100644 src/libtomahawk/resolvers/JSResolverHelper.cpp create mode 100644 src/libtomahawk/resolvers/JSResolverHelper.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 51ec98f371..b34700d85c 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -99,6 +99,7 @@ set( libGuiSources resolvers/ExternalResolverGui.cpp resolvers/ScriptResolver.cpp resolvers/JSResolver.cpp + resolvers/JSResolverHelper.cpp resolvers/ScriptEngine.cpp utils/ImageRegistry.cpp diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 4edc8c4a6d..9952cdf89e 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -30,6 +30,7 @@ #include "Artist.h" #include "Album.h" #include "config.h" +#include "JSResolverHelper.h" #include "Pipeline.h" #include "Result.h" #include "ScriptCollection.h" @@ -60,335 +61,6 @@ #define RESOLVER_LEGACY_CODE2 "var resolver = Tomahawk.resolver.instance ? Tomahawk.resolver.instance : window;" -JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* parent ) - : QObject( parent ) - , m_urlCallbackIsAsync( false ) -{ - m_scriptPath = scriptPath; - m_resolver = parent; -} - - -QByteArray -JSResolverHelper::readRaw( const QString& fileName ) -{ - QString path = QFileInfo( m_scriptPath ).absolutePath(); - // remove directories - QString cleanedFileName = QFileInfo( fileName ).fileName(); - QString absoluteFilePath = path.append( "/" ).append( cleanedFileName ); - - QFile file( absoluteFilePath ); - if ( !file.exists() ) - { - Q_ASSERT(false); - return QByteArray(); - } - - file.open( QIODevice::ReadOnly ); - return file.readAll(); -} - - -QString -JSResolverHelper::compress( const QString& data ) -{ - QByteArray comp = qCompress( data.toLatin1(), 9 ); - return comp.toBase64(); -} - - -QString -JSResolverHelper::readCompressed( const QString& fileName ) -{ - return compress( readRaw( fileName ) ); -} - - -QString -JSResolverHelper::readBase64( const QString& fileName ) -{ - return readRaw( fileName ).toBase64(); -} - - -QVariantMap -JSResolverHelper::resolverData() -{ - QVariantMap resolver; - resolver["config"] = m_resolverConfig; - resolver["scriptPath"] = m_scriptPath; - return resolver; -} - - -void -JSResolverHelper::log( const QString& message ) -{ - tLog() << m_scriptPath << ":" << message; -} - - -void -JSResolverHelper::addTrackResults( const QVariantMap& results ) -{ - qDebug() << "Resolver reporting results:" << results; - QList< Tomahawk::result_ptr > tracks = m_resolver->parseResultVariantList( results.value("results").toList() ); - - QString qid = results.value("qid").toString(); - - Tomahawk::Pipeline::instance()->reportResults( qid, tracks ); -} - - -void -JSResolverHelper::addArtistResults( const QVariantMap& results ) -{ - qDebug() << "Resolver reporting artists:" << results; - QList< Tomahawk::artist_ptr > artists = m_resolver->parseArtistVariantList( results.value( "artists" ).toList() ); - - QString qid = results.value("qid").toString(); - - Tomahawk::collection_ptr collection = Tomahawk::collection_ptr(); - foreach ( const Tomahawk::collection_ptr& coll, m_resolver->collections() ) - { - if ( coll->name() == qid ) - { - collection = coll; - } - } - if ( collection.isNull() ) - return; - - tDebug() << Q_FUNC_INFO << "about to push" << artists.count() << "artists"; - foreach( const Tomahawk::artist_ptr& artist, artists) - tDebug() << artist->name(); - - emit m_resolver->artistsFound( artists ); -} - - -void -JSResolverHelper::addAlbumResults( const QVariantMap& results ) -{ - qDebug() << "Resolver reporting albums:" << results; - QString artistName = results.value( "artist" ).toString(); - if ( artistName.trimmed().isEmpty() ) - return; - Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistName, false ); - QList< Tomahawk::album_ptr > albums = m_resolver->parseAlbumVariantList( artist, results.value( "albums" ).toList() ); - - QString qid = results.value("qid").toString(); - - Tomahawk::collection_ptr collection = Tomahawk::collection_ptr(); - foreach ( const Tomahawk::collection_ptr& coll, m_resolver->collections() ) - { - if ( coll->name() == qid ) - { - collection = coll; - } - } - if ( collection.isNull() ) - return; - - tDebug() << Q_FUNC_INFO << "about to push" << albums.count() << "albums"; - foreach( const Tomahawk::album_ptr& album, albums) - tDebug() << album->name(); - - emit m_resolver->albumsFound( albums ); -} - - -void -JSResolverHelper::addAlbumTrackResults( const QVariantMap& results ) -{ - qDebug() << "Resolver reporting album tracks:" << results; - QString artistName = results.value( "artist" ).toString(); - if ( artistName.trimmed().isEmpty() ) - return; - QString albumName = results.value( "album" ).toString(); - if ( albumName.trimmed().isEmpty() ) - return; - - Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistName, false ); - Tomahawk::album_ptr album = Tomahawk::Album::get( artist, albumName, false ); - - QList< Tomahawk::result_ptr > tracks = m_resolver->parseResultVariantList( results.value("results").toList() ); - - QString qid = results.value("qid").toString(); - - Tomahawk::collection_ptr collection = Tomahawk::collection_ptr(); - foreach ( const Tomahawk::collection_ptr& coll, m_resolver->collections() ) - { - if ( coll->name() == qid ) - { - collection = coll; - } - } - if ( collection.isNull() ) - return; - - QList< Tomahawk::query_ptr > queries; - foreach ( const Tomahawk::result_ptr& result, tracks ) - { - result->setScore( 1.0 ); - queries.append( result->toQuery() ); - } - - tDebug() << Q_FUNC_INFO << "about to push" << tracks.count() << "tracks"; - - emit m_resolver->tracksFound( queries ); -} - - -void -JSResolverHelper::reportCapabilities( const QVariant& v ) -{ - bool ok = 0; - int intCap = v.toInt( &ok ); - Tomahawk::ExternalResolver::Capabilities capabilities; - if ( !ok ) - capabilities = Tomahawk::ExternalResolver::NullCapability; - else - capabilities = static_cast< Tomahawk::ExternalResolver::Capabilities >( intCap ); - - m_resolver->onCapabilitiesChanged( capabilities ); -} - - -void -JSResolverHelper::setResolverConfig( const QVariantMap& config ) -{ - m_resolverConfig = config; -} - - -QString -JSResolverHelper::hmac( const QByteArray& key, const QByteArray &input ) -{ -#ifdef QCA2_FOUND - if ( !QCA::isSupported( "hmac(md5)" ) ) - { - tLog() << "HMAC(md5) not supported with qca-ossl plugin, or qca-ossl plugin is not installed! Unable to generate signature!"; - return QByteArray(); - } - - QCA::MessageAuthenticationCode md5hmac1( "hmac(md5)", QCA::SecureArray() ); - QCA::SymmetricKey keyObject( key ); - md5hmac1.setup( keyObject ); - - md5hmac1.update( QCA::SecureArray( input ) ); - QCA::SecureArray resultArray = md5hmac1.final(); - - QString result = QCA::arrayToHex( resultArray.toByteArray() ); - return result.toUtf8(); -#else - tLog() << "Tomahawk compiled without QCA support, cannot generate HMAC signature"; - return QString(); -#endif -} - - -QString -JSResolverHelper::md5( const QByteArray& input ) -{ - QByteArray const digest = QCryptographicHash::hash( input, QCryptographicHash::Md5 ); - return QString::fromLatin1( digest.toHex() ); -} - - -void -JSResolverHelper::addCustomUrlHandler( const QString& protocol, - const QString& callbackFuncName, - const QString& isAsynchronous ) -{ - m_urlCallbackIsAsync = ( isAsynchronous.toLower() == "true" ) ? true : false; - - boost::function< void( const Tomahawk::result_ptr&, - boost::function< void( QSharedPointer< QIODevice >& ) > )> fac = - boost::bind( &JSResolverHelper::customIODeviceFactory, this, _1, _2 ); - Servent::instance()->registerIODeviceFactory( protocol, fac ); - - m_urlCallback = callbackFuncName; -} - - -QByteArray -JSResolverHelper::base64Encode( const QByteArray& input ) -{ - return input.toBase64(); -} - - -QByteArray -JSResolverHelper::base64Decode( const QByteArray& input ) -{ - return QByteArray::fromBase64( input ); -} - - -void -JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result, - boost::function< void( QSharedPointer< QIODevice >& ) > callback ) -{ - //can be sync or async - QString origResultUrl = QString( QUrl( result->url() ).toEncoded() ); - - if ( m_urlCallbackIsAsync ) - { - QString qid = uuid(); - QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2', '%3' );" ).arg( m_urlCallback ) - .arg( qid ) - .arg( origResultUrl ); - - m_streamCallbacks.insert( qid, callback ); - m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ); - } - else - { - QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback ) - .arg( origResultUrl ); - - QString urlStr = m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ).toString(); - - returnStreamUrl( urlStr, callback ); - } -} - - -void -JSResolverHelper::reportStreamUrl( const QString& qid, - const QString& streamUrl ) -{ - if ( !m_streamCallbacks.contains( qid ) ) - return; - - boost::function< void( QSharedPointer< QIODevice >& ) > callback = m_streamCallbacks.take( qid ); - - returnStreamUrl( streamUrl, callback ); -} - - -void -JSResolverHelper::returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ) -{ - QSharedPointer< QIODevice > sp; - if ( streamUrl.isEmpty() ) - { - callback( sp ); - return; - } - - QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() ); - QNetworkRequest req( url ); - tDebug() << "Creating a QNetowrkReply with url:" << req.url().toString(); - QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - - //boost::functions cannot accept temporaries as parameters - sp = QSharedPointer< QIODevice >( reply, &QObject::deleteLater ); - callback( sp ); -} - - JSResolver::JSResolver( const QString& scriptPath, const QStringList& additionalScriptPaths ) : Tomahawk::ExternalResolverGui( scriptPath ) , m_ready( false ) diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index c430003ade..a21fb2e89f 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -36,58 +36,9 @@ #include "DllMacro.h" -class JSResolver; +class JSResolverHelper; class ScriptEngine; -class DLLEXPORT JSResolverHelper : public QObject -{ -Q_OBJECT - -public: - JSResolverHelper( const QString& scriptPath, JSResolver* parent ); - void setResolverConfig( const QVariantMap& config ); - - // Return a HMAC (md5) signature of the input text with the desired key - Q_INVOKABLE QString hmac( const QByteArray& key, const QByteArray& input ); - Q_INVOKABLE QString md5( const QByteArray& input ); - - Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName, const QString& isAsynchronous = "false" ); - Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl ); - - Q_INVOKABLE QByteArray base64Encode( const QByteArray& input ); - Q_INVOKABLE QByteArray base64Decode( const QByteArray& input ); - - void customIODeviceFactory( const Tomahawk::result_ptr& result, - boost::function< void( QSharedPointer< QIODevice >& ) > callback ); // async - -public slots: - QByteArray readRaw( const QString& fileName ); - QString readBase64( const QString& fileName ); - QString readCompressed( const QString& fileName ); - - QString compress( const QString& data ); - QVariantMap resolverData(); - - void log( const QString& message ); - bool fakeEnv() { return false; } - - void addTrackResults( const QVariantMap& results ); - - void addArtistResults( const QVariantMap& results ); - void addAlbumResults( const QVariantMap& results ); - void addAlbumTrackResults( const QVariantMap& results ); - - void reportCapabilities( const QVariant& capabilities ); - -private: - void returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ); - QString m_scriptPath, m_urlCallback; - QHash< QString, boost::function< void( QSharedPointer< QIODevice >& ) > > m_streamCallbacks; - bool m_urlCallbackIsAsync; - QVariantMap m_resolverConfig; - JSResolver* m_resolver; -}; - class DLLEXPORT JSResolver : public Tomahawk::ExternalResolverGui { Q_OBJECT diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp new file mode 100644 index 0000000000..28f49b0813 --- /dev/null +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -0,0 +1,359 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "JSResolverHelper.h" + +#include "resolvers/ScriptEngine.h" +#include "network/Servent.h" +#include "config.h" +#include "JSResolver.h" +#include "Pipeline.h" +#include "Result.h" + +#include +#include + +JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* parent ) + : QObject( parent ) + , m_urlCallbackIsAsync( false ) +{ + m_scriptPath = scriptPath; + m_resolver = parent; +} + + +QByteArray +JSResolverHelper::readRaw( const QString& fileName ) +{ + QString path = QFileInfo( m_scriptPath ).absolutePath(); + // remove directories + QString cleanedFileName = QFileInfo( fileName ).fileName(); + QString absoluteFilePath = path.append( "/" ).append( cleanedFileName ); + + QFile file( absoluteFilePath ); + if ( !file.exists() ) + { + Q_ASSERT(false); + return QByteArray(); + } + + file.open( QIODevice::ReadOnly ); + return file.readAll(); +} + + +QString +JSResolverHelper::compress( const QString& data ) +{ + QByteArray comp = qCompress( data.toLatin1(), 9 ); + return comp.toBase64(); +} + + +QString +JSResolverHelper::readCompressed( const QString& fileName ) +{ + return compress( readRaw( fileName ) ); +} + + +QString +JSResolverHelper::readBase64( const QString& fileName ) +{ + return readRaw( fileName ).toBase64(); +} + + +QVariantMap +JSResolverHelper::resolverData() +{ + QVariantMap resolver; + resolver["config"] = m_resolverConfig; + resolver["scriptPath"] = m_scriptPath; + return resolver; +} + + +void +JSResolverHelper::log( const QString& message ) +{ + tLog() << m_scriptPath << ":" << message; +} + + +void +JSResolverHelper::addTrackResults( const QVariantMap& results ) +{ + qDebug() << "Resolver reporting results:" << results; + QList< Tomahawk::result_ptr > tracks = m_resolver->parseResultVariantList( results.value("results").toList() ); + + QString qid = results.value("qid").toString(); + + Tomahawk::Pipeline::instance()->reportResults( qid, tracks ); +} + + +void +JSResolverHelper::addArtistResults( const QVariantMap& results ) +{ + qDebug() << "Resolver reporting artists:" << results; + QList< Tomahawk::artist_ptr > artists = m_resolver->parseArtistVariantList( results.value( "artists" ).toList() ); + + QString qid = results.value("qid").toString(); + + Tomahawk::collection_ptr collection = Tomahawk::collection_ptr(); + foreach ( const Tomahawk::collection_ptr& coll, m_resolver->collections() ) + { + if ( coll->name() == qid ) + { + collection = coll; + } + } + if ( collection.isNull() ) + return; + + tDebug() << Q_FUNC_INFO << "about to push" << artists.count() << "artists"; + foreach( const Tomahawk::artist_ptr& artist, artists) + tDebug() << artist->name(); + + emit m_resolver->artistsFound( artists ); +} + + +void +JSResolverHelper::addAlbumResults( const QVariantMap& results ) +{ + qDebug() << "Resolver reporting albums:" << results; + QString artistName = results.value( "artist" ).toString(); + if ( artistName.trimmed().isEmpty() ) + return; + Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistName, false ); + QList< Tomahawk::album_ptr > albums = m_resolver->parseAlbumVariantList( artist, results.value( "albums" ).toList() ); + + QString qid = results.value("qid").toString(); + + Tomahawk::collection_ptr collection = Tomahawk::collection_ptr(); + foreach ( const Tomahawk::collection_ptr& coll, m_resolver->collections() ) + { + if ( coll->name() == qid ) + { + collection = coll; + } + } + if ( collection.isNull() ) + return; + + tDebug() << Q_FUNC_INFO << "about to push" << albums.count() << "albums"; + foreach( const Tomahawk::album_ptr& album, albums) + tDebug() << album->name(); + + emit m_resolver->albumsFound( albums ); +} + + +void +JSResolverHelper::addAlbumTrackResults( const QVariantMap& results ) +{ + qDebug() << "Resolver reporting album tracks:" << results; + QString artistName = results.value( "artist" ).toString(); + if ( artistName.trimmed().isEmpty() ) + return; + QString albumName = results.value( "album" ).toString(); + if ( albumName.trimmed().isEmpty() ) + return; + + Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistName, false ); + Tomahawk::album_ptr album = Tomahawk::Album::get( artist, albumName, false ); + + QList< Tomahawk::result_ptr > tracks = m_resolver->parseResultVariantList( results.value("results").toList() ); + + QString qid = results.value("qid").toString(); + + Tomahawk::collection_ptr collection = Tomahawk::collection_ptr(); + foreach ( const Tomahawk::collection_ptr& coll, m_resolver->collections() ) + { + if ( coll->name() == qid ) + { + collection = coll; + } + } + if ( collection.isNull() ) + return; + + QList< Tomahawk::query_ptr > queries; + foreach ( const Tomahawk::result_ptr& result, tracks ) + { + result->setScore( 1.0 ); + queries.append( result->toQuery() ); + } + + tDebug() << Q_FUNC_INFO << "about to push" << tracks.count() << "tracks"; + + emit m_resolver->tracksFound( queries ); +} + + +void +JSResolverHelper::reportCapabilities( const QVariant& v ) +{ + bool ok = 0; + int intCap = v.toInt( &ok ); + Tomahawk::ExternalResolver::Capabilities capabilities; + if ( !ok ) + capabilities = Tomahawk::ExternalResolver::NullCapability; + else + capabilities = static_cast< Tomahawk::ExternalResolver::Capabilities >( intCap ); + + m_resolver->onCapabilitiesChanged( capabilities ); +} + + +void +JSResolverHelper::setResolverConfig( const QVariantMap& config ) +{ + m_resolverConfig = config; +} + + +QString +JSResolverHelper::hmac( const QByteArray& key, const QByteArray &input ) +{ +#ifdef QCA2_FOUND + if ( !QCA::isSupported( "hmac(md5)" ) ) + { + tLog() << "HMAC(md5) not supported with qca-ossl plugin, or qca-ossl plugin is not installed! Unable to generate signature!"; + return QByteArray(); + } + + QCA::MessageAuthenticationCode md5hmac1( "hmac(md5)", QCA::SecureArray() ); + QCA::SymmetricKey keyObject( key ); + md5hmac1.setup( keyObject ); + + md5hmac1.update( QCA::SecureArray( input ) ); + QCA::SecureArray resultArray = md5hmac1.final(); + + QString result = QCA::arrayToHex( resultArray.toByteArray() ); + return result.toUtf8(); +#else + tLog() << "Tomahawk compiled without QCA support, cannot generate HMAC signature"; + return QString(); +#endif +} + + +QString +JSResolverHelper::md5( const QByteArray& input ) +{ + QByteArray const digest = QCryptographicHash::hash( input, QCryptographicHash::Md5 ); + return QString::fromLatin1( digest.toHex() ); +} + +void +JSResolverHelper::addCustomUrlHandler( const QString& protocol, + const QString& callbackFuncName, + const QString& isAsynchronous ) +{ + m_urlCallbackIsAsync = ( isAsynchronous.toLower() == "true" ) ? true : false; + + boost::function< void( const Tomahawk::result_ptr&, + boost::function< void( QSharedPointer< QIODevice >& ) > )> fac = + boost::bind( &JSResolverHelper::customIODeviceFactory, this, _1, _2 ); + Servent::instance()->registerIODeviceFactory( protocol, fac ); + + m_urlCallback = callbackFuncName; +} + + +QByteArray +JSResolverHelper::base64Encode( const QByteArray& input ) +{ + return input.toBase64(); +} + + +QByteArray +JSResolverHelper::base64Decode( const QByteArray& input ) +{ + return QByteArray::fromBase64( input ); +} + + +void +JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result, + boost::function< void( QSharedPointer< QIODevice >& ) > callback ) +{ + //can be sync or async + QString origResultUrl = QString( QUrl( result->url() ).toEncoded() ); + + if ( m_urlCallbackIsAsync ) + { + QString qid = uuid(); + QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2', '%3' );" ).arg( m_urlCallback ) + .arg( qid ) + .arg( origResultUrl ); + + m_streamCallbacks.insert( qid, callback ); + m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ); + } + else + { + QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback ) + .arg( origResultUrl ); + + QString urlStr = m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ).toString(); + + returnStreamUrl( urlStr, callback ); + } +} + + +void +JSResolverHelper::reportStreamUrl( const QString& qid, + const QString& streamUrl ) +{ + if ( !m_streamCallbacks.contains( qid ) ) + return; + + boost::function< void( QSharedPointer< QIODevice >& ) > callback = m_streamCallbacks.take( qid ); + + returnStreamUrl( streamUrl, callback ); +} + + +void +JSResolverHelper::returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ) +{ + QSharedPointer< QIODevice > sp; + if ( streamUrl.isEmpty() ) + { + callback( sp ); + return; + } + + QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() ); + QNetworkRequest req( url ); + tDebug() << "Creating a QNetowrkReply with url:" << req.url().toString(); + QNetworkReply* reply = TomahawkUtils::nam()->get( req ); + + //boost::functions cannot accept temporaries as parameters + sp = QSharedPointer< QIODevice >( reply, &QObject::deleteLater ); + callback( sp ); +} diff --git a/src/libtomahawk/resolvers/JSResolverHelper.h b/src/libtomahawk/resolvers/JSResolverHelper.h new file mode 100644 index 0000000000..b1fd5d7e82 --- /dev/null +++ b/src/libtomahawk/resolvers/JSResolverHelper.h @@ -0,0 +1,83 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef JSRESOLVERHELPER_H +#define JSRESOLVERHELPER_H + +#include "DllMacro.h" +#include "Typedefs.h" + +#include + +#include +#include + +class JSResolver; + +class DLLEXPORT JSResolverHelper : public QObject +{ +Q_OBJECT + +public: + JSResolverHelper( const QString& scriptPath, JSResolver* parent ); + void setResolverConfig( const QVariantMap& config ); + + // Return a HMAC (md5) signature of the input text with the desired key + Q_INVOKABLE QString hmac( const QByteArray& key, const QByteArray& input ); + Q_INVOKABLE QString md5( const QByteArray& input ); + + Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName, const QString& isAsynchronous = "false" ); + Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl ); + + Q_INVOKABLE QByteArray base64Encode( const QByteArray& input ); + Q_INVOKABLE QByteArray base64Decode( const QByteArray& input ); + + void customIODeviceFactory( const Tomahawk::result_ptr& result, + boost::function< void( QSharedPointer< QIODevice >& ) > callback ); // async + +public slots: + QByteArray readRaw( const QString& fileName ); + QString readBase64( const QString& fileName ); + QString readCompressed( const QString& fileName ); + + QString compress( const QString& data ); + QVariantMap resolverData(); + + void log( const QString& message ); + bool fakeEnv() { return false; } + + void addTrackResults( const QVariantMap& results ); + + void addArtistResults( const QVariantMap& results ); + void addAlbumResults( const QVariantMap& results ); + void addAlbumTrackResults( const QVariantMap& results ); + + void reportCapabilities( const QVariant& capabilities ); + +private: + void returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ); + QString m_scriptPath, m_urlCallback; + QHash< QString, boost::function< void( QSharedPointer< QIODevice >& ) > > m_streamCallbacks; + bool m_urlCallbackIsAsync; + QVariantMap m_resolverConfig; + JSResolver* m_resolver; +}; +#endif // JSRESOLVERHELPER_H From 2dac747a90f11b0883869b11c2d34aed5d0bbb55 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 11:29:10 +0200 Subject: [PATCH 506/565] Remove unneed includes out of JSResolver.h --- src/libtomahawk/resolvers/JSResolver.cpp | 4 ++++ src/libtomahawk/resolvers/JSResolver.h | 13 ++----------- src/libtomahawk/resolvers/JSResolverHelper.cpp | 4 ++++ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 9952cdf89e..1438c9f51a 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -25,6 +25,7 @@ #include "jobview/JobStatusView.h" #include "jobview/JobStatusModel.h" #include "jobview/ErrorStatusMessage.h" +#include "utils/Logger.h" #include "utils/TomahawkUtilsGui.h" #include "Artist.h" @@ -39,6 +40,8 @@ #include "TomahawkSettings.h" #include "TomahawkVersion.h" +#include +#include #include #include #include @@ -46,6 +49,7 @@ #include #include #include +#include #ifdef QCA2_FOUND #include diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index a21fb2e89f..4185993cbf 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -22,19 +22,10 @@ #ifndef JSRESOLVER_H #define JSRESOLVER_H -#include "ExternalResolverGui.h" -#include "Query.h" -#include "utils/TomahawkUtils.h" #include "config.h" -#include "utils/Logger.h" - -#include -#include -#include -#include -#include - #include "DllMacro.h" +#include "ExternalResolverGui.h" +#include "Typedefs.h" class JSResolverHelper; class ScriptEngine; diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index 28f49b0813..f46a7566d7 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -23,13 +23,17 @@ #include "resolvers/ScriptEngine.h" #include "network/Servent.h" +#include "utils/Logger.h" #include "config.h" #include "JSResolver.h" #include "Pipeline.h" #include "Result.h" #include +#include +#include #include +#include JSResolverHelper::JSResolverHelper( const QString& scriptPath, JSResolver* parent ) : QObject( parent ) From 7b6987418c0fe7d354b2d966e5ce3463c2451fcc Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 11:33:21 +0200 Subject: [PATCH 507/565] Move implementations out of the header --- src/libtomahawk/resolvers/JSResolver.cpp | 41 ++++++++++++++++++++++++ src/libtomahawk/resolvers/JSResolver.h | 12 +++---- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 1438c9f51a..8838e095d1 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Leo Franchi * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -117,6 +118,39 @@ Tomahawk::ExternalResolver* JSResolver::factory( const QString& scriptPath, cons } +Tomahawk::ExternalResolver::Capabilities +JSResolver::capabilities() const +{ + return m_capabilities; +} + + +QString +JSResolver::name() const +{ + return m_name; +} + + +QPixmap +JSResolver::icon() const +{ + return m_icon; +} + +unsigned int +JSResolver::weight() const +{ + return m_weight; +} + +unsigned int +JSResolver::timeout() const +{ + return m_timeout; +} + + bool JSResolver::running() const { @@ -138,6 +172,13 @@ JSResolver::reload() } +void +JSResolver::setIcon( const QPixmap &icon ) +{ + m_icon = icon; +} + + void JSResolver::init() { diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index 4185993cbf..82816eac43 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -41,12 +41,12 @@ friend class ::JSResolverHelper; virtual ~JSResolver(); static ExternalResolver* factory( const QString& scriptPath, const QStringList& additionalScriptPaths = QStringList() ); - virtual Capabilities capabilities() const { return m_capabilities; } + virtual Capabilities capabilities() const; - virtual QString name() const { return m_name; } - virtual QPixmap icon() const { return m_icon; } - virtual unsigned int weight() const { return m_weight; } - virtual unsigned int timeout() const { return m_timeout; } + virtual QString name() const; + virtual QPixmap icon() const; + virtual unsigned int weight() const; + virtual unsigned int timeout() const; virtual AccountConfigWidget* configUI() const; virtual void saveConfig(); @@ -55,7 +55,7 @@ friend class ::JSResolverHelper; virtual bool running() const; virtual void reload(); - virtual void setIcon( const QPixmap& icon ) { m_icon = icon; } + virtual void setIcon( const QPixmap& icon ); public slots: virtual void resolve( const Tomahawk::query_ptr& query ); From 9fa387e10268706302f09f11cda63fb412d5423b Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 11:58:26 +0200 Subject: [PATCH 508/565] Dpointer JSResolver --- src/libtomahawk/resolvers/JSResolver.cpp | 179 +++++++++++------- src/libtomahawk/resolvers/JSResolver.h | 16 +- .../resolvers/JSResolverHelper.cpp | 6 +- src/libtomahawk/resolvers/JSResolver_p.h | 64 +++++++ 4 files changed, 185 insertions(+), 80 deletions(-) create mode 100644 src/libtomahawk/resolvers/JSResolver_p.h diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 8838e095d1..bad7a7b8d2 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -19,7 +19,7 @@ * along with Tomahawk. If not, see . */ -#include "JSResolver.h" +#include "JSResolver_p.h" #include "accounts/AccountConfigWidget.h" #include "network/Servent.h" @@ -68,24 +68,21 @@ JSResolver::JSResolver( const QString& scriptPath, const QStringList& additionalScriptPaths ) : Tomahawk::ExternalResolverGui( scriptPath ) - , m_ready( false ) - , m_stopped( true ) - , m_error( Tomahawk::ExternalResolver::NoError ) - , m_resolverHelper( new JSResolverHelper( scriptPath, this ) ) - , m_requiredScriptPaths( additionalScriptPaths ) + , d_ptr( new JSResolverPrivate( this, scriptPath, additionalScriptPaths ) ) { + Q_D( JSResolver ); tLog() << Q_FUNC_INFO << "Loading JS resolver:" << scriptPath; - m_engine = new ScriptEngine( this ); - m_name = QFileInfo( filePath() ).baseName(); + d->engine = new ScriptEngine( this ); + d->name = QFileInfo( filePath() ).baseName(); // set the icon, if we launch properly we'll get the icon the resolver reports - m_icon = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultResolver, TomahawkUtils::Original, QSize( 128, 128 ) ); + d->icon = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultResolver, TomahawkUtils::Original, QSize( 128, 128 ) ); if ( !QFile::exists( filePath() ) ) { tLog() << Q_FUNC_INFO << "Failed loading JavaScript resolver:" << scriptPath; - m_error = Tomahawk::ExternalResolver::FileNotFound; + d->error = Tomahawk::ExternalResolver::FileNotFound; } else { @@ -96,10 +93,12 @@ JSResolver::JSResolver( const QString& scriptPath, const QStringList& additional JSResolver::~JSResolver() { - if ( !m_stopped ) + Q_D( JSResolver ); + if ( !d->stopped ) stop(); - delete m_engine; + delete d->engine; + delete d_ptr; } @@ -121,53 +120,67 @@ Tomahawk::ExternalResolver* JSResolver::factory( const QString& scriptPath, cons Tomahawk::ExternalResolver::Capabilities JSResolver::capabilities() const { - return m_capabilities; + Q_D( const JSResolver ); + + return d->capabilities; } QString JSResolver::name() const { - return m_name; + Q_D( const JSResolver ); + + return d->name; } QPixmap JSResolver::icon() const { - return m_icon; + Q_D( const JSResolver ); + + return d->icon; } unsigned int JSResolver::weight() const { - return m_weight; + Q_D( const JSResolver ); + + return d->weight; } unsigned int JSResolver::timeout() const { - return m_timeout; + Q_D( const JSResolver ); + + return d->timeout; } bool JSResolver::running() const { - return m_ready && !m_stopped; + Q_D( const JSResolver ); + + return d->ready && !d->stopped; } void JSResolver::reload() { + Q_D( JSResolver ); + if ( QFile::exists( filePath() ) ) { init(); - m_error = Tomahawk::ExternalResolver::NoError; + d->error = Tomahawk::ExternalResolver::NoError; } else { - m_error = Tomahawk::ExternalResolver::FileNotFound; + d->error = Tomahawk::ExternalResolver::FileNotFound; } } @@ -175,13 +188,17 @@ JSResolver::reload() void JSResolver::setIcon( const QPixmap &icon ) { - m_icon = icon; + Q_D( JSResolver ); + + d->icon = icon; } void JSResolver::init() { + Q_D( JSResolver ); + QFile scriptFile( filePath() ); if( !scriptFile.open( QIODevice::ReadOnly ) ) { @@ -190,20 +207,20 @@ JSResolver::init() } const QByteArray scriptContents = scriptFile.readAll(); - m_engine->mainFrame()->setHtml( "", QUrl( "file:///invalid/file/for/security/policy" ) ); + d->engine->mainFrame()->setHtml( "", QUrl( "file:///invalid/file/for/security/policy" ) ); // add c++ part of tomahawk javascript library - m_engine->mainFrame()->addToJavaScriptWindowObject( "Tomahawk", m_resolverHelper ); + d->engine->mainFrame()->addToJavaScriptWindowObject( "Tomahawk", d->resolverHelper ); // add rest of it - m_engine->setScriptPath( "tomahawk.js" ); + d->engine->setScriptPath( "tomahawk.js" ); QFile jslib( RESPATH "js/tomahawk.js" ); jslib.open( QIODevice::ReadOnly ); - m_engine->mainFrame()->evaluateJavaScript( jslib.readAll() ); + d->engine->mainFrame()->evaluateJavaScript( jslib.readAll() ); jslib.close(); // add resolver dependencies, if any - foreach ( QString s, m_requiredScriptPaths ) + foreach ( QString s, d->requiredScriptPaths ) { QFile reqFile( s ); if( !reqFile.open( QIODevice::ReadOnly ) ) @@ -213,21 +230,21 @@ JSResolver::init() } const QByteArray reqContents = reqFile.readAll(); - m_engine->setScriptPath( s ); - m_engine->mainFrame()->evaluateJavaScript( reqContents ); + d->engine->setScriptPath( s ); + d->engine->mainFrame()->evaluateJavaScript( reqContents ); } // add resolver - m_engine->setScriptPath( filePath() ); - m_engine->mainFrame()->evaluateJavaScript( scriptContents ); + d->engine->setScriptPath( filePath() ); + d->engine->mainFrame()->evaluateJavaScript( scriptContents ); // init resolver resolverInit(); QVariantMap m = resolverSettings(); - m_name = m.value( "name" ).toString(); - m_weight = m.value( "weight", 0 ).toUInt(); - m_timeout = m.value( "timeout", 25 ).toUInt() * 1000; + d->name = m.value( "name" ).toString(); + d->weight = m.value( "weight", 0 ).toUInt(); + d->timeout = m.value( "timeout", 25 ).toUInt() * 1000; bool compressed = m.value( "compressed", "false" ).toString() == "true"; QByteArray icoData = m.value( "icon" ).toByteArray(); @@ -241,7 +258,7 @@ JSResolver::init() bool success = false; if ( !ico.isNull() ) { - m_icon = ico.scaled( m_icon.size(), Qt::IgnoreAspectRatio ); + d->icon = ico.scaled( d->icon.size(), Qt::IgnoreAspectRatio ); success = true; } // see if the resolver sent an icon path to not break the old (unofficial) api. @@ -249,12 +266,12 @@ JSResolver::init() if ( !success ) { QString iconPath = QFileInfo( filePath() ).path() + "/" + m.value( "icon" ).toString(); - success = m_icon.load( iconPath ); + success = d->icon.load( iconPath ); } // if we still couldn't load the cover, set the default resolver icon - if ( m_icon.isNull() ) + if ( d->icon.isNull() ) { - m_icon = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultResolver, TomahawkUtils::Original, QSize( 128, 128 ) ); + d->icon = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultResolver, TomahawkUtils::Original, QSize( 128, 128 ) ); } // load config widget and apply settings @@ -262,17 +279,19 @@ JSResolver::init() QVariantMap config = resolverUserConfig(); fillDataInWidgets( config ); - qDebug() << "JS" << filePath() << "READY," << "name" << m_name << "weight" << m_weight << "timeout" << m_timeout << "icon received" << success; + qDebug() << "JS" << filePath() << "READY," << "name" << d->name << "weight" << d->weight << "timeout" << d->timeout << "icon received" << success; - m_ready = true; + d->ready = true; } void JSResolver::start() { - m_stopped = false; - if ( m_ready ) + Q_D( JSResolver ); + + d->stopped = false; + if ( d->ready ) Tomahawk::Pipeline::instance()->addResolver( this ); else init(); @@ -282,6 +301,8 @@ JSResolver::start() void JSResolver::artists( const Tomahawk::collection_ptr& collection ) { + Q_D( JSResolver ); + if ( QThread::currentThread() != thread() ) { QMetaObject::invokeMethod( this, "artists", Qt::QueuedConnection, Q_ARG( Tomahawk::collection_ptr, collection ) ); @@ -298,7 +319,7 @@ JSResolver::artists( const Tomahawk::collection_ptr& collection ) QString eval = QString( "resolver.artists( '%1' );" ) .arg( collection->name().replace( "'", "\\'" ) ); - QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); + QVariantMap m = d->engine->mainFrame()->evaluateJavaScript( eval ).toMap(); if ( m.isEmpty() ) { // if the resolver doesn't return anything, async api is used @@ -314,6 +335,8 @@ JSResolver::artists( const Tomahawk::collection_ptr& collection ) void JSResolver::albums( const Tomahawk::collection_ptr& collection, const Tomahawk::artist_ptr& artist ) { + Q_D( JSResolver ); + if ( QThread::currentThread() != thread() ) { QMetaObject::invokeMethod( this, "albums", Qt::QueuedConnection, @@ -333,7 +356,7 @@ JSResolver::albums( const Tomahawk::collection_ptr& collection, const Tomahawk:: .arg( collection->name().replace( "'", "\\'" ) ) .arg( artist->name().replace( "'", "\\'" ) ); - QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); + QVariantMap m = d->engine->mainFrame()->evaluateJavaScript( eval ).toMap(); if ( m.isEmpty() ) { // if the resolver doesn't return anything, async api is used @@ -349,6 +372,8 @@ JSResolver::albums( const Tomahawk::collection_ptr& collection, const Tomahawk:: void JSResolver::tracks( const Tomahawk::collection_ptr& collection, const Tomahawk::album_ptr& album ) { + Q_D( JSResolver ); + if ( QThread::currentThread() != thread() ) { QMetaObject::invokeMethod( this, "tracks", Qt::QueuedConnection, @@ -369,7 +394,7 @@ JSResolver::tracks( const Tomahawk::collection_ptr& collection, const Tomahawk:: .arg( album->artist()->name().replace( "'", "\\'" ) ) .arg( album->name().replace( "'", "\\'" ) ); - QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); + QVariantMap m = d->engine->mainFrame()->evaluateJavaScript( eval ).toMap(); if ( m.isEmpty() ) { // if the resolver doesn't return anything, async api is used @@ -385,13 +410,17 @@ JSResolver::tracks( const Tomahawk::collection_ptr& collection, const Tomahawk:: Tomahawk::ExternalResolver::ErrorState JSResolver::error() const { - return m_error; + Q_D( const JSResolver ); + + return d->error; } void JSResolver::resolve( const Tomahawk::query_ptr& query ) { + Q_D( JSResolver ); + if ( QThread::currentThread() != thread() ) { QMetaObject::invokeMethod( this, "resolve", Qt::QueuedConnection, Q_ARG(Tomahawk::query_ptr, query) ); @@ -419,7 +448,7 @@ JSResolver::resolve( const Tomahawk::query_ptr& query ) .arg( query->fullTextQuery().replace( "'", "\\'" ) ); } - QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( eval ).toMap(); + QVariantMap m = d->engine->mainFrame()->evaluateJavaScript( eval ).toMap(); if ( m.isEmpty() ) { // if the resolver doesn't return anything, async api is used @@ -545,7 +574,9 @@ JSResolver::parseAlbumVariantList( const Tomahawk::artist_ptr& artist, const QVa void JSResolver::stop() { - m_stopped = true; + Q_D( JSResolver ); + + d->stopped = true; foreach ( const Tomahawk::collection_ptr& collection, m_collections ) { @@ -560,8 +591,10 @@ JSResolver::stop() void JSResolver::loadUi() { - QVariantMap m = m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getConfigUi();" ).toMap(); - m_dataWidgets = m["fields"].toList(); + Q_D( JSResolver ); + + QVariantMap m = d->engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getConfigUi();" ).toMap(); + d->dataWidgets = m["fields"].toList(); bool compressed = m.value( "compressed", "false" ).toBool(); qDebug() << "Resolver has a preferences widget! compressed?" << compressed; @@ -584,7 +617,7 @@ JSResolver::loadUi() if( m.contains( "images" ) ) uiData = fixDataImagePaths( uiData, compressed, images ); - m_configWidget = QPointer< AccountConfigWidget >( widgetFromData( uiData, 0 ) ); + d->configWidget = QPointer< AccountConfigWidget >( widgetFromData( uiData, 0 ) ); emit changed(); } @@ -593,21 +626,25 @@ JSResolver::loadUi() AccountConfigWidget* JSResolver::configUI() const { - if( m_configWidget.isNull() ) + Q_D( const JSResolver ); + + if( d->configWidget.isNull() ) return 0; else - return m_configWidget.data(); + return d->configWidget.data(); } void JSResolver::saveConfig() { + Q_D( JSResolver ); + QVariant saveData = loadDataFromWidgets(); // qDebug() << Q_FUNC_INFO << saveData; - m_resolverHelper->setResolverConfig( saveData.toMap() ); - m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.saveUserConfig();" ); + d->resolverHelper->setResolverConfig( saveData.toMap() ); + d->engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.saveUserConfig();" ); } @@ -643,13 +680,15 @@ JSResolver::setWidgetData(const QVariant& value, QWidget* widget, const QString& QVariantMap JSResolver::loadDataFromWidgets() { + Q_D( JSResolver ); + QVariantMap saveData; - foreach( const QVariant& dataWidget, m_dataWidgets ) + foreach( const QVariant& dataWidget, d->dataWidgets ) { QVariantMap data = dataWidget.toMap(); QString widgetName = data["widget"].toString(); - QWidget* widget= m_configWidget.data()->findChild( widgetName ); + QWidget* widget= d->configWidget.data()->findChild( widgetName ); QVariant value = widgetData( widget, data["property"].toString() ); @@ -663,10 +702,12 @@ JSResolver::loadDataFromWidgets() void JSResolver::fillDataInWidgets( const QVariantMap& data ) { - foreach(const QVariant& dataWidget, m_dataWidgets) + Q_D( JSResolver ); + + foreach(const QVariant& dataWidget, d->dataWidgets) { QString widgetName = dataWidget.toMap()["widget"].toString(); - QWidget* widget= m_configWidget.data()->findChild( widgetName ); + QWidget* widget= d->configWidget.data()->findChild( widgetName ); if( !widget ) { tLog() << Q_FUNC_INFO << "Widget specified in resolver was not found:" << widgetName; @@ -685,7 +726,9 @@ JSResolver::fillDataInWidgets( const QVariantMap& data ) void JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capabilities ) { - m_capabilities = capabilities; + Q_D( JSResolver ); + + d->capabilities = capabilities; loadCollections(); } @@ -693,9 +736,11 @@ JSResolver::onCapabilitiesChanged( Tomahawk::ExternalResolver::Capabilities capa void JSResolver::loadCollections() { - if ( m_capabilities.testFlag( Browsable ) ) + Q_D( JSResolver ); + + if ( d->capabilities.testFlag( Browsable ) ) { - QVariantMap collectionInfo = m_engine->mainFrame()->evaluateJavaScript( "resolver.collection();" ).toMap(); + QVariantMap collectionInfo = d->engine->mainFrame()->evaluateJavaScript( "resolver.collection();" ).toMap(); if ( collectionInfo.isEmpty() || !collectionInfo.contains( "prettyname" ) || !collectionInfo.contains( "description" ) ) @@ -791,21 +836,27 @@ JSResolver::onCollectionIconFetched() QVariantMap JSResolver::resolverSettings() { - return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "if(resolver.settings) resolver.settings; else getSettings(); " ).toMap(); + Q_D( JSResolver ); + + return d->engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "if(resolver.settings) resolver.settings; else getSettings(); " ).toMap(); } QVariantMap JSResolver::resolverUserConfig() { - return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getUserConfig();" ).toMap(); + Q_D( JSResolver ); + + return d->engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.getUserConfig();" ).toMap(); } QVariantMap JSResolver::resolverInit() { - return m_engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.init();" ).toMap(); + Q_D( JSResolver ); + + return d->engine->mainFrame()->evaluateJavaScript( RESOLVER_LEGACY_CODE "resolver.init();" ).toMap(); } diff --git a/src/libtomahawk/resolvers/JSResolver.h b/src/libtomahawk/resolvers/JSResolver.h index 82816eac43..5b7ce8769a 100644 --- a/src/libtomahawk/resolvers/JSResolver.h +++ b/src/libtomahawk/resolvers/JSResolver.h @@ -28,6 +28,7 @@ #include "Typedefs.h" class JSResolverHelper; +class JSResolverPrivate; class ScriptEngine; class DLLEXPORT JSResolver : public Tomahawk::ExternalResolverGui @@ -95,20 +96,9 @@ private slots: QList< Tomahawk::album_ptr > parseAlbumVariantList( const Tomahawk::artist_ptr& artist, const QVariantList& reslist ); - ScriptEngine* m_engine; + Q_DECLARE_PRIVATE( JSResolver ) + JSResolverPrivate* d_ptr; - QString m_name; - QPixmap m_icon; - unsigned int m_weight, m_timeout; - Capabilities m_capabilities; - - bool m_ready, m_stopped; - ExternalResolver::ErrorState m_error; - - JSResolverHelper* m_resolverHelper; - QPointer< AccountConfigWidget > m_configWidget; - QList< QVariant > m_dataWidgets; - QStringList m_requiredScriptPaths; }; #endif // JSRESOLVER_H diff --git a/src/libtomahawk/resolvers/JSResolverHelper.cpp b/src/libtomahawk/resolvers/JSResolverHelper.cpp index f46a7566d7..cb1fe5e463 100644 --- a/src/libtomahawk/resolvers/JSResolverHelper.cpp +++ b/src/libtomahawk/resolvers/JSResolverHelper.cpp @@ -25,7 +25,7 @@ #include "network/Servent.h" #include "utils/Logger.h" #include "config.h" -#include "JSResolver.h" +#include "JSResolver_p.h" #include "Pipeline.h" #include "Result.h" @@ -315,14 +315,14 @@ JSResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result, .arg( origResultUrl ); m_streamCallbacks.insert( qid, callback ); - m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ); + m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( getUrl ); } else { QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback ) .arg( origResultUrl ); - QString urlStr = m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ).toString(); + QString urlStr = m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( getUrl ).toString(); returnStreamUrl( urlStr, callback ); } diff --git a/src/libtomahawk/resolvers/JSResolver_p.h b/src/libtomahawk/resolvers/JSResolver_p.h new file mode 100644 index 0000000000..8eb37fcecf --- /dev/null +++ b/src/libtomahawk/resolvers/JSResolver_p.h @@ -0,0 +1,64 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef JSRESOLVER_P_H +#define JSRESOLVER_P_H + +#include "JSResolver.h" + +#include "JSResolverHelper.h" + +class JSResolverPrivate +{ + friend class ::JSResolverHelper; +public: + JSResolverPrivate( JSResolver* q, const QString& scriptPath, const QStringList& additionalScriptPaths ) + : q_ptr ( q ) + , ready( false ) + , stopped( true ) + , error( Tomahawk::ExternalResolver::NoError ) + , resolverHelper( new JSResolverHelper( scriptPath, q ) ) + , requiredScriptPaths( additionalScriptPaths ) + { + } + JSResolver* q_ptr; + Q_DECLARE_PUBLIC ( JSResolver ) + +private: + ScriptEngine* engine; + + QString name; + QPixmap icon; + unsigned int weight, timeout; + Tomahawk::ExternalResolverGui::Capabilities capabilities; + + bool ready; + bool stopped; + Tomahawk::ExternalResolver::ErrorState error; + + JSResolverHelper* resolverHelper; + QPointer< AccountConfigWidget > configWidget; + QList< QVariant > dataWidgets; + QStringList requiredScriptPaths; +}; + + +#endif // JSRESOLVER_P_H From 8d80b1e1133c02577775126ad65554115b1db4a7 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 12:07:14 +0200 Subject: [PATCH 509/565] Improve includes in Resolver.h --- src/libtomahawk/resolvers/Resolver.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/resolvers/Resolver.h b/src/libtomahawk/resolvers/Resolver.h index 96fd9bcbf4..5ce76d05bd 100644 --- a/src/libtomahawk/resolvers/Resolver.h +++ b/src/libtomahawk/resolvers/Resolver.h @@ -21,9 +21,9 @@ #include -#include "Query.h" - #include "DllMacro.h" +#include "Typedefs.h" + // implement this if you can resolve queries to content @@ -52,6 +52,6 @@ public slots: virtual void resolve( const Tomahawk::query_ptr& query ) = 0; }; -}; //ns +} //ns #endif // RESOLVER_H From 1b1b88551d11eb5cac768de53def78af87a51f5f Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 12:25:26 +0200 Subject: [PATCH 510/565] Improve Includes in ExternalResolvers.h --- src/libtomahawk/resolvers/ExternalResolver.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/resolvers/ExternalResolver.h b/src/libtomahawk/resolvers/ExternalResolver.h index 400ba12f0a..c60d3c627a 100644 --- a/src/libtomahawk/resolvers/ExternalResolver.h +++ b/src/libtomahawk/resolvers/ExternalResolver.h @@ -21,16 +21,14 @@ #ifndef EXTERNALRESOLVER_H #define EXTERNALRESOLVER_H -#include "Artist.h" -#include "Album.h" #include "Source.h" -#include "Query.h" #include "DllMacro.h" #include "Resolver.h" #include "ScriptCommandQueue.h" #include "ScriptCommand_AllArtists.h" #include "ScriptCommand_AllAlbums.h" #include "ScriptCommand_AllTracks.h" +#include "Typedefs.h" #include @@ -119,6 +117,6 @@ public slots: Q_DECLARE_OPERATORS_FOR_FLAGS( ExternalResolver::Capabilities ) -}; //ns +} //ns #endif // EXTERNALESOLVER_H From be364d582ad7099fc5a30206edade5c05c47b0fb Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 23 Jun 2013 22:40:28 +0200 Subject: [PATCH 511/565] * Added xhochy to AUTHORS / about-dialog. --- AUTHORS | 1 + src/tomahawk/TomahawkWindow.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 996bc73adc..d7e66fde03 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,6 +13,7 @@ Contributors include: * Teo Mrnjavac * Michael Zanetti * Christopher Reichert +* Uwe L. Korn Thanks to: diff --git a/src/tomahawk/TomahawkWindow.cpp b/src/tomahawk/TomahawkWindow.cpp index f3155971d8..cc074b76a4 100644 --- a/src/tomahawk/TomahawkWindow.cpp +++ b/src/tomahawk/TomahawkWindow.cpp @@ -1207,7 +1207,7 @@ TomahawkWindow::showAboutTomahawk() const QString thanksto( tr( "Thanks to:" ) ); desc = QString( "%1
    Christian Muehlhaeuser <muesli@tomahawk-player.org>

    " - "%2 Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Syd Lawrence, Michael Zanetti, Teo Mrnjavac, Christopher Reichert, Harald Sitter" ) + "%2 Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindström, Michael Zanetti, Teo Mrnjavac, Christopher Reichert, Uwe L. Korn, Harald Sitter, Syd Lawrence" ) .arg( copyright ) .arg( thanksto ); From b4de7d6e908033cdd7f601b834a01ad54014c4e6 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Mon, 24 Jun 2013 02:16:14 +0200 Subject: [PATCH 512/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 14 +++++++------- lang/tomahawk_bg.ts | 14 +++++++------- lang/tomahawk_bn_IN.ts | 14 +++++++------- lang/tomahawk_ca.ts | 14 +++++++------- lang/tomahawk_ca@valencia.ts | 14 +++++++------- lang/tomahawk_cs.ts | 14 +++++++------- lang/tomahawk_da.ts | 14 +++++++------- lang/tomahawk_de.ts | 14 +++++++------- lang/tomahawk_el.ts | 14 +++++++------- lang/tomahawk_en.ts | 14 +++++++------- lang/tomahawk_es.ts | 14 +++++++------- lang/tomahawk_fi.ts | 14 +++++++------- lang/tomahawk_fr.ts | 14 +++++++------- lang/tomahawk_gl.ts | 14 +++++++------- lang/tomahawk_hi_IN.ts | 14 +++++++------- lang/tomahawk_hu.ts | 14 +++++++------- lang/tomahawk_id.ts | 14 +++++++------- lang/tomahawk_it.ts | 14 +++++++------- lang/tomahawk_ja.ts | 14 +++++++------- lang/tomahawk_lt.ts | 14 +++++++------- lang/tomahawk_pl.ts | 14 +++++++------- lang/tomahawk_pt_BR.ts | 14 +++++++------- lang/tomahawk_ru.ts | 14 +++++++------- lang/tomahawk_sv.ts | 14 +++++++------- lang/tomahawk_tr.ts | 14 +++++++------- lang/tomahawk_zh_CN.ts | 14 +++++++------- lang/tomahawk_zh_TW.ts | 14 +++++++------- 27 files changed, 189 insertions(+), 189 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index 136ec53912..a4b068e995 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. تحذير المحلل النصي: إستدعاء API %1 إرجاع البيانات بشكل متزامن. @@ -1712,22 +1712,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 خطأ محلل النصي: %1 %2 %3 %4 - + SSL Error خطأ SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? لقد طلبت من توماهوك الإتصال بشكل أمن إلى <b>%1</b> ولكن لا يمكننا التأكد من أن اتصالك آمن: <br><br><b>%2</b></br></br> هل تريد أن تثق بهذا الإتصال؟ - + Trust certificate شهادة الثقة diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index 6344f126fd..a514e8e5b2 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -930,9 +930,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Предупреждение на скриптът за извличане: Заявката към %1 върна данни синхронно. @@ -1721,22 +1721,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Грешка на скриптът за извличане на данни: %1 %2 %3 %4 - + SSL Error SSL грешка - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Ти пожела Tomahawk да се свърже сигурно към <b>%1</b>, но ние не можем да потвърдим, че връзката е сигурна:<br><br><b>%2</b><br><br>Искаш ли да се довериш на тази връзка? - + Trust certificate Удостовери този сертификат diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index f00b26afc2..da83604f51 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 64fb0ff3a5..473d2590f9 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 96d70d093a..182f18e50c 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error de resolució de l'script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index a746fe522f..b491125bac 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -924,9 +924,9 @@ heslo
    JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Varování řešitele skriptu: Volání API %1 vrátilo data synchronně. @@ -1711,22 +1711,22 @@ heslo
    ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Chyba řešitele skriptu: %1 %2 %3 %4 - + SSL Error Chyba SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Požádal jste Tomahawk o to, aby se bezpečně připojil k <b>%1</b>, ale nelze potvrdit, že je vaše připojení bezpečné:<br><br><b>%2</b><br><br>Chcete tomuto připojení důvěřovat? - + Trust certificate Důvěřovat certifikátu diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index f1308b204c..5c090ca0dd 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index 514ce88757..c508b3d639 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -924,9 +924,9 @@ Passwort
    JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warnung: API aufruf %1 zurückgegebener Daten synchron. @@ -1711,22 +1711,22 @@ Passwort
    ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Fehler: %1 %2 %3 %4 - + SSL Error SSL Fehler - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Sie wurden gebeten, Tomahawk sicher zu <b>%1</b> verbinden, aber wir können nicht bestätigen, dass die Verbindung sicher ist:<br><br><b>%2</b><br><br> Möchten Sie dieser Verbindung vertrauen? - + Trust certificate Zertifikat vertrauen diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index e2e43d012a..50214a1639 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Σριπτ προειδοποίηση αναλυτή: API κλήση% 1 επέστρεψε δεδομένα συγχρονισμένα. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Σφάλμα Script Resolver: %1 %2 %3 %4 - + SSL Error SSL Σφάλμα - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Έχετε ζητήσει το Tomahawk να συνδεθεί με ασφάλεια στο <b>%1</b>, αλλά δεν μπορούμε να επιβεβαιώσουμε ότι η σύνδεσή σας είναι ασφαλής:<br><br><b>%2</b><br><br>Θέλετε να εμπιστεύθειτε αυτή τη σύνδεση; - + Trust certificate Εμπιστοσύνη πιστοποιητικόυ diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index b8570b9855..a494a879cc 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Script Resolver Warning: API call %1 returned data synchronously. @@ -1711,22 +1711,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Script Resolver Error: %1 %2 %3 %4 - + SSL Error SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate Trust certificate diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 9e5841834c..7d33d274f0 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Error del resolutor de script: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index 4611652930..ea3e8b3ad6 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -924,9 +924,9 @@ salasana JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Skriptiselvittimen varoitus: API-kutsu %1 palautti dataa synkronisesti. @@ -1711,22 +1711,22 @@ salasana ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptiselvittimen virhe: %1 %2 %3 %4 - + SSL Error SSL-virhe - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Olet pyytänyt Tomahawkia yhdistämään turvallisesti palvelimeen <b>%1</b>, mutta yhteyden turvallisuutta ei voida varmistaa:<br><br><b>%2</b><br><br>Haluatko luottaa tähän yhteyteen? - + Trust certificate Luota varmenteeseen diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 4cff41d15f..410695e41a 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erreur du script de résolution : %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index 0347b98040..f1341ee086 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Erro do solucionador de erros: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index 36ff6e9f00..ba349b64b1 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index 0d412ec20d..aff78499fa 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index 67376350d8..e94d28c191 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index 141163ae65..f345e72ddc 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -923,9 +923,9 @@ temporanea JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Avvertimento Script Resolver: chiamata API 1% ha restituito dati sincroni. @@ -1709,22 +1709,22 @@ temporanea ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Errore script resolver: %1 %2 %3 %4 - + SSL Error Errore SSL - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Hai chiesto a Tomahawk di connettersi in modo sicuro a <b>%1</b>, ma non possiamo garantire che la conessione sia sicura: <br><br><b>%2</b><br><br> Vuoi fidarti di questa connessione? - + Trust certificate Certificato di trust diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index 7b1fd489d6..a0d79e2803 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index adbbb46d17..af2324f63e 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 97ac174a52..2cbcffde21 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index a7cbadb233..6564fde3c5 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -923,9 +923,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1710,22 +1710,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 7fb6223361..9138554f6f 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -926,9 +926,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1713,22 +1713,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 7bab23b63c..646fbc4129 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -924,9 +924,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. Resovler-skriptvarning: API-kall %1 returnerade data synkront @@ -1711,22 +1711,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 Skriptfel i resolvern: %1 %2 %3 %4 - + SSL Error SSL-Fel - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? Du har bett Tomahawk att göra en säker uppkoppling mot <b>%1</b>, men vi kan inte bekräfta att din uppkoppling är säker: <br><br><b>%2</b><br><br>Litar du på denna uppkoppling? - + Trust certificate Tillförlitligt certifikat diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 378ba46a59..5b85043d21 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index 9f960f0ea0..f1191a4002 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1709,22 +1709,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 9c6466745e..2180547ca0 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -922,9 +922,9 @@ Password JSResolver - - - + + + Script Resolver Warning: API call %1 returned data synchronously. @@ -1708,22 +1708,22 @@ Password ScriptEngine - + Script Resolver Error: %1 %2 %3 %4 - + SSL Error - + You have asked Tomahawk to connect securely to <b>%1</b>, but we can't confirm that your connection is secure:<br><br><b>%2</b><br><br>Do you want to trust this connection? - + Trust certificate From 09026a33fea4616068e520f564149ba881603e27 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 24 Jun 2013 15:18:55 +0200 Subject: [PATCH 513/565] Debug spam++ --- src/libtomahawk/network/Servent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index df42a290a6..d011a0f9f9 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -1031,6 +1031,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString if ( !d_func()->noAuth && peer != QHostAddress::Any && !isIPWhitelisted( peer ) ) { bool authed = false; + tDebug() << Q_FUNC_INFO << "Checking for ControlConnection with IP" << peer; foreach ( ControlConnection* cc, d_func()->controlconnections ) { tDebug() << Q_FUNC_INFO << "Probing:" << cc->name(); From cde07bc2b857f6df7a2fce88baeff55af91d0341 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 24 Jun 2013 22:17:08 +0200 Subject: [PATCH 514/565] Write --verbose output to Tomahawk.log --- src/libtomahawk/utils/Logger.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/libtomahawk/utils/Logger.cpp b/src/libtomahawk/utils/Logger.cpp index 457b881bd0..9df870a166 100644 --- a/src/libtomahawk/utils/Logger.cpp +++ b/src/libtomahawk/utils/Logger.cpp @@ -60,13 +60,8 @@ log( const char *msg, unsigned int debugLevel, bool toDisk = true ) #endif } - #ifdef QT_NO_DEBUG - if ( debugLevel > RELEASE_LEVEL_THRESHOLD ) + if ( debugLevel > LOGTHIRDPARTY ) toDisk = false; - #else - if ( debugLevel > DEBUG_LEVEL_THRESHOLD ) - toDisk = false; - #endif #ifdef LOG_SQL_QUERIES if ( debugLevel == LOGSQL ) From 546f54f1965589410e4a787bc0811ca804ccb375 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 12:01:38 +0200 Subject: [PATCH 515/565] Make WeakPeerHash more generic --- src/libtomahawk/CMakeLists.txt | 3 +- src/libtomahawk/sip/PeerInfo.cpp | 11 ++- src/libtomahawk/sip/PeerInfo.h | 2 - src/libtomahawk/sip/PeerInfo_p.h | 4 + src/libtomahawk/sip/WeakPeerHash.cpp | 57 ------------ src/libtomahawk/sip/WeakPeerHash.h | 47 ---------- .../WeakObjectHash.cpp} | 39 +++++---- src/libtomahawk/utils/WeakObjectHash.h | 86 +++++++++++++++++++ 8 files changed, 121 insertions(+), 128 deletions(-) delete mode 100644 src/libtomahawk/sip/WeakPeerHash.cpp delete mode 100644 src/libtomahawk/sip/WeakPeerHash.h rename src/libtomahawk/{sip/WeakPeerHash_p.h => utils/WeakObjectHash.cpp} (61%) create mode 100644 src/libtomahawk/utils/WeakObjectHash.h diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index b34700d85c..54c07c6fc2 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -349,7 +349,6 @@ list(APPEND libSources sip/SipInfo.cpp sip/PeerInfo.cpp sip/SipStatusMessage.cpp - sip/WeakPeerHash.cpp utils/TomahawkUtils.cpp utils/Logger.cpp @@ -357,6 +356,8 @@ list(APPEND libSources utils/XspfLoader.cpp utils/TomahawkCache.cpp utils/GuiHelpers.cpp + utils/WeakObjectHash.cpp + thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp thirdparty/kdsingleapplicationguard/kdsharedmemorylocker.cpp diff --git a/src/libtomahawk/sip/PeerInfo.cpp b/src/libtomahawk/sip/PeerInfo.cpp index 1e7c3a524f..9003eb9afb 100644 --- a/src/libtomahawk/sip/PeerInfo.cpp +++ b/src/libtomahawk/sip/PeerInfo.cpp @@ -28,7 +28,6 @@ #include "SipInfo.h" #include "SipPlugin.h" -#include "WeakPeerHash.h" #include #include @@ -36,7 +35,7 @@ namespace Tomahawk { -WeakPeerHash PeerInfo::s_peersByCacheKey = WeakPeerHash(); +Tomahawk::Utils::WeakObjectHash< PeerInfo > PeerInfoPrivate::s_peersByCacheKey = Tomahawk::Utils::WeakObjectHash< PeerInfo >(); QHash< SipPlugin*, peerinfo_ptr > PeerInfo::s_selfPeersBySipPlugin = QHash< SipPlugin*, peerinfo_ptr >(); @@ -83,9 +82,9 @@ Tomahawk::peerinfo_ptr PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options ) { const QString key = peerCacheKey( parent, id ); - if ( s_peersByCacheKey.hash().contains( key ) && !s_peersByCacheKey.hash().value( key ).isNull() ) + if ( PeerInfoPrivate::s_peersByCacheKey.hash().contains( key ) && !PeerInfoPrivate::s_peersByCacheKey.hash().value( key ).isNull() ) { - return s_peersByCacheKey.hash().value( key ).toStrongRef(); + return PeerInfoPrivate::s_peersByCacheKey.hash().value( key ).toStrongRef(); } // if AutoCreate isn't enabled nothing to do here @@ -96,7 +95,7 @@ PeerInfo::get( SipPlugin* parent, const QString& id, GetOptions options ) peerinfo_ptr peerInfo( new PeerInfo( parent, id ), &QObject::deleteLater ); peerInfo->setWeakRef( peerInfo.toWeakRef() ); - s_peersByCacheKey.insert( key, peerInfo ); + PeerInfoPrivate::s_peersByCacheKey.insert( key, peerInfo ); return peerInfo; } @@ -106,7 +105,7 @@ QList< Tomahawk::peerinfo_ptr > PeerInfo::getAll() { QList< Tomahawk::peerinfo_ptr > strongRefs; - foreach ( Tomahawk::peerinfo_wptr wptr, s_peersByCacheKey.hash().values() ) + foreach ( Tomahawk::peerinfo_wptr wptr, PeerInfoPrivate::s_peersByCacheKey.hash().values() ) { if ( !wptr.isNull() ) strongRefs << wptr.toStrongRef(); diff --git a/src/libtomahawk/sip/PeerInfo.h b/src/libtomahawk/sip/PeerInfo.h index 44be6847db..9aee964f39 100644 --- a/src/libtomahawk/sip/PeerInfo.h +++ b/src/libtomahawk/sip/PeerInfo.h @@ -30,7 +30,6 @@ class ControlConnection; class SipPlugin; class SipInfo; -class WeakPeerHash; namespace Tomahawk { @@ -130,7 +129,6 @@ Q_OBJECT Q_DECLARE_PRIVATE( Tomahawk::PeerInfo ) Tomahawk::PeerInfoPrivate* d_ptr; - static WeakPeerHash s_peersByCacheKey; static QHash< SipPlugin*, peerinfo_ptr > s_selfPeersBySipPlugin; mutable QPixmap* m_avatar; diff --git a/src/libtomahawk/sip/PeerInfo_p.h b/src/libtomahawk/sip/PeerInfo_p.h index 78187672e5..7e8730e2a9 100644 --- a/src/libtomahawk/sip/PeerInfo_p.h +++ b/src/libtomahawk/sip/PeerInfo_p.h @@ -22,6 +22,8 @@ #include "PeerInfo.h" +#include "utils/WeakObjectHash.h" + namespace Tomahawk { @@ -40,6 +42,8 @@ class PeerInfoPrivate PeerInfo* q_ptr; Q_DECLARE_PUBLIC ( PeerInfo ) + static Tomahawk::Utils::WeakObjectHash s_peersByCacheKey; + private: QWeakPointer< Tomahawk::PeerInfo > ownRef; QPointer< ControlConnection > controlConnection; diff --git a/src/libtomahawk/sip/WeakPeerHash.cpp b/src/libtomahawk/sip/WeakPeerHash.cpp deleted file mode 100644 index 52a04e8a76..0000000000 --- a/src/libtomahawk/sip/WeakPeerHash.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2013, Uwe L. Korn - * - * Tomahawk 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 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 Tomahawk. If not, see . - */ - -#include "WeakPeerHash_p.h" - -#include "PeerInfo.h" -#include "utils/Closure.h" - -#define WEAKPEERHASH_KEY "WeakPeerHashKey" - -WeakPeerHash::WeakPeerHash( QObject *parent ) - : QObject( parent ) - , d_ptr( new WeakPeerHashPrivate( this ) ) -{ -} - -WeakPeerHash::WeakPeerHash( const WeakPeerHash &hash ) - : QObject( hash.parent() ) - , d_ptr( new WeakPeerHashPrivate( this ) ) -{ - d_func()->hash = hash.d_func()->hash; -} - -void -WeakPeerHash::insert( const QString &key, const Tomahawk::peerinfo_ptr &value ) -{ - _detail::Closure* cl = NewClosure( value, SIGNAL( destroyed( QObject* ) ), this, SLOT( remove( QString ) ), key ); - cl->setAutoDelete( true ); - d_func()->hash.insert( key, value.toWeakRef() ); -} - -const QHash& -WeakPeerHash::hash() -{ - return d_func()->hash; -} - -void -WeakPeerHash::remove( const QString& key ) -{ - d_func()->hash.remove( key ); -} diff --git a/src/libtomahawk/sip/WeakPeerHash.h b/src/libtomahawk/sip/WeakPeerHash.h deleted file mode 100644 index b165d8eb39..0000000000 --- a/src/libtomahawk/sip/WeakPeerHash.h +++ /dev/null @@ -1,47 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2013, Uwe L. Korn - * - * Tomahawk 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 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 Tomahawk. If not, see . - */ - -#ifndef WEAKPEERHASH_H -#define WEAKPEERHASH_H - -#include "Typedefs.h" - -#include - -class WeakPeerHashPrivate; - -class WeakPeerHash : public QObject -{ - Q_OBJECT -public: - WeakPeerHash( QObject *parent = 0 ); - WeakPeerHash( const WeakPeerHash& hash ); - void insert( const QString& key, const Tomahawk::peerinfo_ptr& value ); - const QHash< QString, Tomahawk::peerinfo_wptr>& hash(); - -signals: - -private slots: - void remove( const QString& key ); -private: - Q_DECLARE_PRIVATE( WeakPeerHash ) - WeakPeerHashPrivate* d_ptr; - -}; - -#endif // WEAKPEERHASH_H diff --git a/src/libtomahawk/sip/WeakPeerHash_p.h b/src/libtomahawk/utils/WeakObjectHash.cpp similarity index 61% rename from src/libtomahawk/sip/WeakPeerHash_p.h rename to src/libtomahawk/utils/WeakObjectHash.cpp index ee4e80e07a..7d3de67ad8 100644 --- a/src/libtomahawk/sip/WeakPeerHash_p.h +++ b/src/libtomahawk/utils/WeakObjectHash.cpp @@ -16,24 +16,33 @@ * along with Tomahawk. If not, see . */ -#ifndef WEAKPEERHASH_P_H -#define WEAKPEERHASH_P_H +#include "WeakObjectHash.h" -#include "WeakPeerHash.h" -class WeakPeerHashPrivate +Tomahawk::Utils::WeakObjectHashPrivate::WeakObjectHashPrivate(Tomahawk::Utils::WeakObjectHashBase *parent) + : QObject( 0 ) + , m_parent( parent ) { -public: - WeakPeerHashPrivate( WeakPeerHash* q ) - : q_ptr ( q ) - { - } - WeakPeerHash* q_ptr; - Q_DECLARE_PUBLIC ( WeakPeerHash ) +} -private: - QHash< QString, Tomahawk::peerinfo_wptr > hash; -}; + +void +Tomahawk::Utils::WeakObjectHashPrivate::remove( const QString& key ) +{ + m_parent->remove( key ); +} + + +void +Tomahawk::Utils::WeakObjectHashBase::remove( const QString& key ) +{ + Q_UNUSED( key ); + // Does nothing but needs to be implemented for linking +} + + +Tomahawk::Utils::WeakObjectHashBase::~WeakObjectHashBase() +{ +} -#endif // WEAKPEERHASH_P_H diff --git a/src/libtomahawk/utils/WeakObjectHash.h b/src/libtomahawk/utils/WeakObjectHash.h new file mode 100644 index 0000000000..0845bc133f --- /dev/null +++ b/src/libtomahawk/utils/WeakObjectHash.h @@ -0,0 +1,86 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef WEAKPEERHASH_H +#define WEAKPEERHASH_H + +#include "Typedefs.h" +#include "utils/Closure.h" + +#include + +namespace Tomahawk +{ + +namespace Utils +{ + +class WeakObjectHashBase +{ +public: + virtual void remove( const QString& key ); + virtual ~WeakObjectHashBase(); +protected: + WeakObjectHashBase() {} +}; + +class WeakObjectHashPrivate : public QObject +{ + Q_OBJECT +public: + WeakObjectHashPrivate( WeakObjectHashBase* parent ); + +public slots: + void remove( const QString& key ); + +private: + WeakObjectHashBase* m_parent; +}; + +template +class WeakObjectHash : public WeakObjectHashBase +{ +public: + WeakObjectHash() : m_private( this ) {} + + WeakObjectHash( const WeakObjectHash& hash ) + : m_hash( hash.m_hash ) + , m_private( this ) + { + } + + void insert( const QString& key, const QSharedPointer& value ) + { + _detail::Closure* cl = NewClosure( value.data(), SIGNAL( destroyed( QObject* ) ), &m_private, SLOT( remove( QString ) ), key ); + cl->setAutoDelete( true ); + m_hash.insert( key, value.toWeakRef() ); + } + + const QHash< QString, QWeakPointer >& hash() { return m_hash; } + virtual void remove( const QString& key ) { m_hash.remove( key ); } + +private: + QHash< QString, QWeakPointer > m_hash; + WeakObjectHashPrivate m_private; +}; + +} // namespace Utils + +} // namespace Tomahawk + +#endif // WEAKPEERHASH_H From 7daebc655af6b2f902d0322174273341401fd382 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 12:03:11 +0200 Subject: [PATCH 516/565] Performance++, Memory-- --- src/libtomahawk/network/ConnectionManager.cpp | 11 ++++++----- src/libtomahawk/network/ConnectionManager.h | 2 +- src/libtomahawk/network/Servent.cpp | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index c48c3bcfc2..a844bc28c5 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -28,6 +28,7 @@ #include "sip/SipInfo.h" #include "sip/SipPlugin.h" #include "utils/Logger.h" +#include "utils/WeakObjectHash.h" #include #include @@ -35,21 +36,21 @@ /* Management of ConnectionManagers */ static QMutex nodeMapMutex; -static QMap< QString, QWeakPointer< ConnectionManager > > connectionManagers; -static QMap< QString, QSharedPointer< ConnectionManager > > activeConnectionManagers; +static Tomahawk::Utils::WeakObjectHash< ConnectionManager > connectionManagers; +static QHash< QString, QSharedPointer< ConnectionManager > > activeConnectionManagers; QSharedPointer ConnectionManager::getManagerForNodeId( const QString &nodeid ) { QMutexLocker locker( &nodeMapMutex ); - if ( connectionManagers.contains( nodeid ) && !connectionManagers.value( nodeid ).isNull() ) { - return connectionManagers.value( nodeid ).toStrongRef(); + if ( connectionManagers.hash().contains( nodeid ) && !connectionManagers.hash().value( nodeid ).isNull() ) { + return connectionManagers.hash().value( nodeid ).toStrongRef(); } // There exists no connection for this nodeid QSharedPointer< ConnectionManager > manager( new ConnectionManager( nodeid ) ); manager->setWeakRef( manager.toWeakRef() ); - connectionManagers[nodeid] = manager->weakRef(); + connectionManagers.insert( nodeid, manager.toWeakRef() ); return manager; } diff --git a/src/libtomahawk/network/ConnectionManager.h b/src/libtomahawk/network/ConnectionManager.h index ce95e4016b..f4c10e400e 100644 --- a/src/libtomahawk/network/ConnectionManager.h +++ b/src/libtomahawk/network/ConnectionManager.h @@ -49,7 +49,7 @@ class DLLEXPORT ConnectionManager : public QObject */ static void setActive( bool active, const QString& nodeid, const QSharedPointer& manager ); - ~ConnectionManager(); + virtual ~ConnectionManager(); /** * Receive incoming SipInfos and start a new thread to connect to this peer. diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index d011a0f9f9..724616375f 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -492,7 +492,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) peerInfoDebug(peerInfo) << "we need to establish the connection now... thinking"; if ( !connectedToSession( peerInfo->nodeId() ) ) { - ConnectionManager::getManagerForNodeId( peerInfo->nodeId() )->handleSipInfo( peerInfo ); + handleSipInfo( peerInfo ); } else { @@ -556,7 +556,8 @@ Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) if ( peerInfo->sipInfos().isEmpty() ) return; - ConnectionManager::getManagerForNodeId( peerInfo->nodeId() )->handleSipInfo( peerInfo ); + QSharedPointer manager = ConnectionManager::getManagerForNodeId( peerInfo->nodeId() ); + manager->handleSipInfo( peerInfo ); } From 04e5b338c455d8996c84f5b2cc9573c99925f478 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 12:03:24 +0200 Subject: [PATCH 517/565] Add comment to prevent others from doing the same mistake --- src/libtomahawk/utils/WeakObjectHash.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libtomahawk/utils/WeakObjectHash.h b/src/libtomahawk/utils/WeakObjectHash.h index 0845bc133f..0de08e6078 100644 --- a/src/libtomahawk/utils/WeakObjectHash.h +++ b/src/libtomahawk/utils/WeakObjectHash.h @@ -66,6 +66,7 @@ class WeakObjectHash : public WeakObjectHashBase void insert( const QString& key, const QSharedPointer& value ) { + // Do not pass the QSharedPointer to the closure as this will prevent the object from being destroyed. _detail::Closure* cl = NewClosure( value.data(), SIGNAL( destroyed( QObject* ) ), &m_private, SLOT( remove( QString ) ), key ); cl->setAutoDelete( true ); m_hash.insert( key, value.toWeakRef() ); From a381c5e3f1c6939837f0458623966eb38b76ce82 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 12:10:00 +0200 Subject: [PATCH 518/565] Use strongRef where strongRef requested --- src/libtomahawk/network/ConnectionManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index a844bc28c5..fe97a04d8d 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -50,7 +50,7 @@ ConnectionManager::getManagerForNodeId( const QString &nodeid ) // There exists no connection for this nodeid QSharedPointer< ConnectionManager > manager( new ConnectionManager( nodeid ) ); manager->setWeakRef( manager.toWeakRef() ); - connectionManagers.insert( nodeid, manager.toWeakRef() ); + connectionManagers.insert( nodeid, manager ); return manager; } From 419a3f1cd2a7b3262d744e200aad5488d9f36daa Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 19:04:05 +0200 Subject: [PATCH 519/565] Prefix HTTP API v1.0 members --- src/tomahawk/TomahawkApp.cpp | 46 ++++++++++++++++++------------------ src/tomahawk/TomahawkApp.h | 4 ++-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index b5239a051f..0f17c6ca84 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -269,10 +269,10 @@ TomahawkApp::~TomahawkApp() { tDebug( LOGVERBOSE ) << "Shutting down Tomahawk..."; - if ( !m_session.isNull() ) - delete m_session.data(); - if ( !m_connector.isNull() ) - delete m_connector.data(); + if ( !m_httpv1_session.isNull() ) + delete m_httpv1_session.data(); + if ( !m_httpv1_connector.isNull() ) + delete m_httpv1_connector.data(); if ( Pipeline::instance() ) Pipeline::instance()->stop(); @@ -468,40 +468,40 @@ TomahawkApp::initHTTP() if ( !TomahawkSettings::instance()->httpEnabled() ) { tLog() << "Stopping HTTPd, not enabled"; - if ( !m_session.isNull() ) - delete m_session.data(); - if ( !m_connector.isNull() ) - delete m_connector.data(); + if ( !m_httpv1_session.isNull() ) + delete m_httpv1_session.data(); + if ( !m_httpv1_connector.isNull() ) + delete m_httpv1_connector.data(); return; } - if ( m_session ) + if ( m_httpv1_session ) { tLog() << "HTTPd session already exists, returning"; return; } - m_session = QPointer< QxtHttpSessionManager >( new QxtHttpSessionManager() ); - m_connector = QPointer< QxtHttpServerConnector >( new QxtHttpServerConnector ); - if ( m_session.isNull() || m_connector.isNull() ) + m_httpv1_session = QPointer< QxtHttpSessionManager >( new QxtHttpSessionManager() ); + m_httpv1_connector = QPointer< QxtHttpServerConnector >( new QxtHttpServerConnector ); + if ( m_httpv1_session.isNull() || m_httpv1_connector.isNull() ) { - if ( !m_session.isNull() ) - delete m_session.data(); - if ( !m_connector.isNull() ) - delete m_connector.data(); + if ( !m_httpv1_session.isNull() ) + delete m_httpv1_session.data(); + if ( !m_httpv1_connector.isNull() ) + delete m_httpv1_connector.data(); tLog() << "Failed to start HTTPd, could not create object"; return; } - m_session.data()->setPort( 60210 ); //TODO config - m_session.data()->setListenInterface( QHostAddress::LocalHost ); - m_session.data()->setConnector( m_connector.data() ); + m_httpv1_session.data()->setPort( 60210 ); //TODO config + m_httpv1_session.data()->setListenInterface( QHostAddress::LocalHost ); + m_httpv1_session.data()->setConnector( m_httpv1_connector.data() ); - Api_v1* api = new Api_v1( m_session.data() ); - m_session.data()->setStaticContentService( api ); + Api_v1* api = new Api_v1( m_httpv1_session.data() ); + m_httpv1_session.data()->setStaticContentService( api ); - tLog() << "Starting HTTPd on" << m_session.data()->listenInterface().toString() << m_session.data()->port(); - m_session.data()->start(); + tLog() << "Starting HTTPd on" << m_httpv1_session.data()->listenInterface().toString() << m_httpv1_session.data()->port(); + m_httpv1_session.data()->start(); } diff --git a/src/tomahawk/TomahawkApp.h b/src/tomahawk/TomahawkApp.h index 3286e58f7a..39611b4c74 100644 --- a/src/tomahawk/TomahawkApp.h +++ b/src/tomahawk/TomahawkApp.h @@ -144,8 +144,8 @@ private slots: bool m_headless; - QPointer< QxtHttpServerConnector > m_connector; - QPointer< QxtHttpSessionManager > m_session; + QPointer< QxtHttpServerConnector > m_httpv1_connector; + QPointer< QxtHttpSessionManager > m_httpv1_session; }; Q_DECLARE_METATYPE( PairList ) From f742066a7e8c270dbc901bb37578e3103eee79d4 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 20:02:17 +0200 Subject: [PATCH 520/565] Ensure that Servent still runs on all IPs with Qt 5.0 --- src/tomahawk/TomahawkApp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tomahawk/TomahawkApp.cpp b/src/tomahawk/TomahawkApp.cpp index 0f17c6ca84..9a84baf0d6 100644 --- a/src/tomahawk/TomahawkApp.cpp +++ b/src/tomahawk/TomahawkApp.cpp @@ -542,7 +542,11 @@ TomahawkApp::initServent() bool upnp = !arguments().contains( "--noupnp" ); int port = TomahawkSettings::instance()->externalPort(); +#if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) + if ( !Servent::instance()->startListening( QHostAddress( QHostAddress::Any ), upnp, port ) ) +#else if ( !Servent::instance()->startListening( QHostAddress( QHostAddress::AnyIPv6 ), upnp, port ) ) +#endif { tLog() << "Failed to start listening with servent"; exit( 1 ); From d41a9d8dce2376896554af65f1b2818c3f5070fb Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 14 Jun 2013 20:29:59 +0200 Subject: [PATCH 521/565] Update Qxt --- thirdparty/qxt/qxtweb-standalone/web/qxtwebslotservice.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/thirdparty/qxt/qxtweb-standalone/web/qxtwebslotservice.cpp b/thirdparty/qxt/qxtweb-standalone/web/qxtwebslotservice.cpp index bca88a58b4..37120d05d0 100644 --- a/thirdparty/qxt/qxtweb-standalone/web/qxtwebslotservice.cpp +++ b/thirdparty/qxt/qxtweb-standalone/web/qxtwebslotservice.cpp @@ -107,7 +107,9 @@ void QxtWebSlotService::pageRequestedEvent(QxtWebRequestEvent* event) QByteArray action = "index"; if (args.count()) { - action = args.at(0).toUtf8(); + // Substitute hyphens for underscores to be able + // to dispatch hyphenated paths. + action = args.at(0).toUtf8().replace('-', '_'); if (action.trimmed().isEmpty()) action = "index"; args.removeFirst(); From b0e352a5f5c6ea58c4dea9f0423654cf3b6f3f2d Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Tue, 18 Jun 2013 10:23:20 +0200 Subject: [PATCH 522/565] Generate QxtSslServer fancy header --- thirdparty/qxt/qxtweb-standalone/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt b/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt index 699c7bfc40..cc79bb0afe 100644 --- a/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt +++ b/thirdparty/qxt/qxtweb-standalone/CMakeLists.txt @@ -24,6 +24,12 @@ macro(create_qxtweb_fancy_header simpleHeader fancyHeader) file(WRITE ${CMAKE_BINARY_DIR}/QxtWeb/${fancyHeader} "#include \"${simpleHeader}\"" ) endmacro() +macro(create_qxtnetwork_fancy_header simpleHeader fancyHeader) + file(WRITE ${CMAKE_BINARY_DIR}/QxtNetwork/${fancyHeader} "#include \"${simpleHeader}\"" ) +endmacro() + +create_qxtnetwork_fancy_header("qxtsslserver.h" "QxtSslServer") + create_qxtweb_fancy_header("qxtabstracthttpconnector.h" "QxtHttpServerConnector") create_qxtweb_fancy_header("qxthttpsessionmanager.h" "HttpSessionManager") create_qxtweb_fancy_header("qxthttpsessionmanager.h" "QxtHttpSessionManager") From 22aa75f4bbe094fbc94150242300bae69fe79bb3 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 23 Jun 2013 21:18:01 +0200 Subject: [PATCH 523/565] Export JobStatusView --- src/libtomahawk/jobview/JobStatusItem.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/jobview/JobStatusItem.h b/src/libtomahawk/jobview/JobStatusItem.h index d03f31f056..71d323df3f 100644 --- a/src/libtomahawk/jobview/JobStatusItem.h +++ b/src/libtomahawk/jobview/JobStatusItem.h @@ -19,6 +19,8 @@ #ifndef JOB_STATUS_ITEM #define JOB_STATUS_ITEM +#include "DllMacro.h" + #include #include @@ -36,7 +38,7 @@ class QPixmap; * The right column may be empty. * */ -class JobStatusItem : public QObject +class DLLEXPORT JobStatusItem : public QObject { Q_OBJECT public: From 179dac3442a7bbd66c36cc8c898f974ce1da18fa Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 14:31:02 +0200 Subject: [PATCH 524/565] Only display filename in JS resolver errors/as scriptname * The paths are internal ones * Shortens error messages for JS resolvers so that they will most likely not overflow anymore --- src/libtomahawk/resolvers/JSResolver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index bad7a7b8d2..6f37888d5c 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -230,12 +230,12 @@ JSResolver::init() } const QByteArray reqContents = reqFile.readAll(); - d->engine->setScriptPath( s ); + d->engine->setScriptPath( QFileInfo( s ).fileName() ); d->engine->mainFrame()->evaluateJavaScript( reqContents ); } // add resolver - d->engine->setScriptPath( filePath() ); + d->engine->setScriptPath( QFileInfo( filePath() ).fileName() ); d->engine->mainFrame()->evaluateJavaScript( scriptContents ); // init resolver From 4998852e5a9774ebeb588816de7aba018f5e6aa3 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 14:41:19 +0200 Subject: [PATCH 525/565] Revert "Only display filename in JS resolver errors/as scriptname" This reverts commit a6459f28237fe3fc3889e914f3d3c8c49d651d60. --- src/libtomahawk/resolvers/JSResolver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/resolvers/JSResolver.cpp b/src/libtomahawk/resolvers/JSResolver.cpp index 6f37888d5c..bad7a7b8d2 100644 --- a/src/libtomahawk/resolvers/JSResolver.cpp +++ b/src/libtomahawk/resolvers/JSResolver.cpp @@ -230,12 +230,12 @@ JSResolver::init() } const QByteArray reqContents = reqFile.readAll(); - d->engine->setScriptPath( QFileInfo( s ).fileName() ); + d->engine->setScriptPath( s ); d->engine->mainFrame()->evaluateJavaScript( reqContents ); } // add resolver - d->engine->setScriptPath( QFileInfo( filePath() ).fileName() ); + d->engine->setScriptPath( filePath() ); d->engine->mainFrame()->evaluateJavaScript( scriptContents ); // init resolver From 0be198fc49edc0d94d595aa44149609011436c0c Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 19:00:01 +0200 Subject: [PATCH 526/565] Make selection of ControlConnection symmetric --- src/libtomahawk/Source.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/Source.cpp b/src/libtomahawk/Source.cpp index 09c22f7579..f0b09e9851 100644 --- a/src/libtomahawk/Source.cpp +++ b/src/libtomahawk/Source.cpp @@ -106,7 +106,8 @@ Source::setControlConnection( ControlConnection* cc ) { const QString& nodeid = Database::instance()->impl()->dbid(); peerInfoDebug( (*cc->peerInfos().begin()) ) << Q_FUNC_INFO << "Comparing" << cc->id() << "and" << nodeid << "to detect duplicate connection, outbound:" << cc->outbound(); - if ( cc->id() < nodeid && d->cc->outbound() ) + // If our nodeid is "higher" than the other, we prefer inbound connection, else outbound. + if ( ( cc->id() < nodeid && d->cc->outbound() ) || ( cc->id() > nodeid && !d->cc->outbound() ) ) { // Tell the ControlConnection it is not anymore responsible for us. d->cc->unbindFromSource(); From fbc8803c371ae8c3771ebc494b97d875d47c2c22 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Wed, 26 Jun 2013 19:36:14 +0200 Subject: [PATCH 527/565] Don't try to acquire lock if we do not have a source anymore. --- src/libtomahawk/network/ControlConnection.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 25596c327e..36e701fb10 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -159,9 +159,15 @@ ControlConnection::registerSource() { Q_D( ControlConnection ); QReadLocker sourceLocker( &d->sourceLock ); + if ( d->source.isNull() ) + { + // Not connected to a source anymore, nothing to do. + return; + } + QSharedPointer locker = d->source->acquireLock(); // Only continue if we are still the ControlConnection associated with this source. - if ( !d->source.isNull() && d->source->controlConnection() == this ) + if ( d->source->controlConnection() == this ) { qDebug() << Q_FUNC_INFO << d->source->id(); Source* source = (Source*) sender(); From 6ad4a8ce765295bbf5a87c0beadea74568cbc919 Mon Sep 17 00:00:00 2001 From: Tomahawk CI Date: Thu, 27 Jun 2013 02:16:38 +0200 Subject: [PATCH 528/565] Automatic merge of Transifex translations --- lang/tomahawk_ar.ts | 18 +++++++++--------- lang/tomahawk_bg.ts | 18 +++++++++--------- lang/tomahawk_bn_IN.ts | 18 +++++++++--------- lang/tomahawk_ca.ts | 18 +++++++++--------- lang/tomahawk_ca@valencia.ts | 18 +++++++++--------- lang/tomahawk_cs.ts | 18 +++++++++--------- lang/tomahawk_da.ts | 18 +++++++++--------- lang/tomahawk_de.ts | 18 +++++++++--------- lang/tomahawk_el.ts | 18 +++++++++--------- lang/tomahawk_en.ts | 18 +++++++++--------- lang/tomahawk_es.ts | 18 +++++++++--------- lang/tomahawk_fi.ts | 18 +++++++++--------- lang/tomahawk_fr.ts | 18 +++++++++--------- lang/tomahawk_gl.ts | 18 +++++++++--------- lang/tomahawk_hi_IN.ts | 18 +++++++++--------- lang/tomahawk_hu.ts | 18 +++++++++--------- lang/tomahawk_id.ts | 18 +++++++++--------- lang/tomahawk_it.ts | 18 +++++++++--------- lang/tomahawk_ja.ts | 18 +++++++++--------- lang/tomahawk_lt.ts | 18 +++++++++--------- lang/tomahawk_pl.ts | 18 +++++++++--------- lang/tomahawk_pt_BR.ts | 18 +++++++++--------- lang/tomahawk_ru.ts | 18 +++++++++--------- lang/tomahawk_sv.ts | 18 +++++++++--------- lang/tomahawk_tr.ts | 18 +++++++++--------- lang/tomahawk_zh_CN.ts | 18 +++++++++--------- lang/tomahawk_zh_TW.ts | 18 +++++++++--------- 27 files changed, 243 insertions(+), 243 deletions(-) diff --git a/lang/tomahawk_ar.ts b/lang/tomahawk_ar.ts index a4b068e995..af59581c7d 100644 --- a/lang/tomahawk_ar.ts +++ b/lang/tomahawk_ar.ts @@ -3704,43 +3704,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) يجري مسح (%L1 أغنية) - + Scanning مسح - + Checking فحص - + Syncing مزامنة - + Importing استيراد - + Saving (%1%) تحفيظ(%1%) - + Online متصل - + Offline غير متصل diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index a514e8e5b2..b365cd9d0f 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -3720,43 +3720,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканиране (%L1 изпълнения) - + Scanning Сканирам - + Checking Проверявам - + Syncing Синхронизиране - + Importing Импортиране - + Saving (%1%) Запазвам (%1%) - + Online На линия - + Offline Извън линия diff --git a/lang/tomahawk_bn_IN.ts b/lang/tomahawk_bn_IN.ts index da83604f51..53ccf1af31 100644 --- a/lang/tomahawk_bn_IN.ts +++ b/lang/tomahawk_bn_IN.ts @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_ca.ts b/lang/tomahawk_ca.ts index 473d2590f9..0d59d6f50f 100644 --- a/lang/tomahawk_ca.ts +++ b/lang/tomahawk_ca.ts @@ -3704,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Desant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_ca@valencia.ts b/lang/tomahawk_ca@valencia.ts index 182f18e50c..a29f5fc86e 100644 --- a/lang/tomahawk_ca@valencia.ts +++ b/lang/tomahawk_ca@valencia.ts @@ -3704,43 +3704,43 @@ Intenteu ajustar els filtres per reproduir noves cançons. Tomahawk::Source - - + + Scanning (%L1 tracks) Escanejant (%L1 cançons) - + Scanning Escanejant - + Checking Comprovant - + Syncing S'està sincronitzant - + Importing S'està important - + Saving (%1%) Alçant (%1%) - + Online En línia - + Offline Fora de línia diff --git a/lang/tomahawk_cs.ts b/lang/tomahawk_cs.ts index b491125bac..0bf98ab8c1 100644 --- a/lang/tomahawk_cs.ts +++ b/lang/tomahawk_cs.ts @@ -3704,43 +3704,43 @@ Zkuste vyladit filtry pro nové písně. Tomahawk::Source - - + + Scanning (%L1 tracks) Prohledává se (%L1 skladeb) - + Scanning Prohledává se - + Checking Přezkušuje se - + Syncing Seřizuje se - + Importing Zavádí se - + Saving (%1%) Ukládá se (%1%) - + Online Připojený - + Offline Nepřipojený diff --git a/lang/tomahawk_da.ts b/lang/tomahawk_da.ts index 5c090ca0dd..4465605722 100644 --- a/lang/tomahawk_da.ts +++ b/lang/tomahawk_da.ts @@ -3692,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanner (%L1 numre) - + Scanning Scanner - + Checking Checker - + Syncing - + Importing - + Saving (%1%) Gemmer (%1%) - + Online - + Offline diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index c508b3d639..e83dd481f1 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -3699,43 +3699,43 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanne (%L1 Stücke) - + Scanning Scanne - + Checking Überprüfe - + Syncing Synchronisiere - + Importing Importiere - + Saving (%1%) Speichere (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_el.ts b/lang/tomahawk_el.ts index 50214a1639..451043738c 100644 --- a/lang/tomahawk_el.ts +++ b/lang/tomahawk_el.ts @@ -3705,43 +3705,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Σάρωση (%L1 τραγούδια) - + Scanning Σάρωση - + Checking Έλεγχος - + Syncing Συγχρονισμος - + Importing Εισαγωγη - + Saving (%1%) Αποθήκευση (%1%) - + Online Συνδεδεμένος - + Offline Εκτός Σύνδεσης diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index a494a879cc..52b60b905e 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -3707,43 +3707,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scanning (%L1 tracks) - + Scanning Scanning - + Checking Checking - + Syncing Syncing - + Importing Importing - + Saving (%1%) Saving (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 7d33d274f0..540eb2428c 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -3705,43 +3705,43 @@ Intente ajustar los filtros para reproducir nuevas canciones. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Guardando (%1%) - + Online En línea - + Offline Desconectado diff --git a/lang/tomahawk_fi.ts b/lang/tomahawk_fi.ts index ea3e8b3ad6..80bfe96f1f 100644 --- a/lang/tomahawk_fi.ts +++ b/lang/tomahawk_fi.ts @@ -3710,43 +3710,43 @@ kappaleen %2%4 %3. Tomahawk::Source - - + + Scanning (%L1 tracks) Etsitään (%L1 kappaletta) - + Scanning Etsitään - + Checking Tarkistetaan - + Syncing Synkronoidaan - + Importing Tuodaan - + Saving (%1%) Tallennetaan (%1 %) - + Online Verkossa - + Offline Ei verkossa diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts index 410695e41a..5703329135 100644 --- a/lang/tomahawk_fr.ts +++ b/lang/tomahawk_fr.ts @@ -3702,43 +3702,43 @@ Essayez de changer les filtres pour avoir de nouveaux morceaux à jouer. Tomahawk::Source - - + + Scanning (%L1 tracks) Scan en cours (%L1 titres) - + Scanning Scan en cours - + Checking Vérification - + Syncing Synchronisation - + Importing Importation - + Saving (%1%) Enregistrement (%1%) - + Online En Ligne - + Offline Hors ligne diff --git a/lang/tomahawk_gl.ts b/lang/tomahawk_gl.ts index f1341ee086..b55fd7b5ab 100644 --- a/lang/tomahawk_gl.ts +++ b/lang/tomahawk_gl.ts @@ -3704,43 +3704,43 @@ Proba a trocar os filtros para ter outra lista música para escoitar. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 pistas) - + Scanning Escaneando - + Checking Comprobando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Gardando (%1%) - + Online Conectado - + Offline Desconectado diff --git a/lang/tomahawk_hi_IN.ts b/lang/tomahawk_hi_IN.ts index ba349b64b1..59eecb2aaf 100644 --- a/lang/tomahawk_hi_IN.ts +++ b/lang/tomahawk_hi_IN.ts @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_hu.ts b/lang/tomahawk_hu.ts index aff78499fa..bc338025e1 100644 --- a/lang/tomahawk_hu.ts +++ b/lang/tomahawk_hu.ts @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning Szkennelés - + Checking Ellenőrzés - + Syncing Szinkronizálás - + Importing Importálás - + Saving (%1%) Mentés (%1%) - + Online Elérhető - + Offline Nem elérhető diff --git a/lang/tomahawk_id.ts b/lang/tomahawk_id.ts index e94d28c191..767589723d 100644 --- a/lang/tomahawk_id.ts +++ b/lang/tomahawk_id.ts @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_it.ts b/lang/tomahawk_it.ts index f345e72ddc..440c5b9193 100644 --- a/lang/tomahawk_it.ts +++ b/lang/tomahawk_it.ts @@ -3692,43 +3692,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Scansionando (%L1 tracce) - + Scanning Scansionando - + Checking Controllando - + Syncing Sto sincronizzando - + Importing Sto importando - + Saving (%1%) Salvando (%1%) - + Online Connesso - + Offline Disconnesso diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index a0d79e2803..6df77ca177 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -3702,43 +3702,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) スキャン中(%1L1トラック) - + Scanning 走査中 - + Checking 検査中 - + Syncing 同期中 - + Importing インポート中 - + Saving (%1%) 保存中(%1%) - + Online オンライン - + Offline オフライン diff --git a/lang/tomahawk_lt.ts b/lang/tomahawk_lt.ts index af2324f63e..d732646aa8 100644 --- a/lang/tomahawk_lt.ts +++ b/lang/tomahawk_lt.ts @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Peržvelgiama (%L1 takeliai) - + Scanning Peržvelgiama - + Checking Tikrinama - + Syncing Sinchronizuojama - + Importing Įkeliama - + Saving (%1%) Išsaugoma (%1%) - + Online Prisijungęs - + Offline Atsijungęs diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 2cbcffde21..15bf08d8d5 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -3699,43 +3699,43 @@ Spróbuj poprawić filtry aby uzyskać nowy zestaw piosenek do odtworzenia. Tomahawk::Source - - + + Scanning (%L1 tracks) Skanowanie (%L1 utworów) - + Scanning Skanowanie - + Checking Sprawdzanie - + Syncing - + Importing - + Saving (%1%) Zapisywanie (%1%) - + Online - + Offline diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 6564fde3c5..2e2567e4d4 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -3699,43 +3699,43 @@ Tente ajustar os filtros para ouvir um novo conjunto de músicas. Tomahawk::Source - - + + Scanning (%L1 tracks) Escaneando (%L1 faixas) - + Scanning Escaneando - + Checking Verificando - + Syncing Sincronizando - + Importing Importando - + Saving (%1%) Salvando (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 9138554f6f..b9456c7f3a 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -3706,43 +3706,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) Сканирование (%L1 песни) - + Scanning Сканирую - + Checking Проверяю - + Syncing Синхронизация - + Importing Импортирование - + Saving (%1%) Сохраняю (%1%) - + Online В сети - + Offline Не в сети diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 646fbc4129..f5545c5020 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -3705,43 +3705,43 @@ Försök att ändra i filtrerna för att få en ny låtlista Tomahawk::Source - - + + Scanning (%L1 tracks) Söker igenom (%L1 spår) - + Scanning Skannar - + Checking Kontrollerar - + Syncing Synkroniserar - + Importing Importerar - + Saving (%1%) Sparar (%1%) - + Online Online - + Offline Offline diff --git a/lang/tomahawk_tr.ts b/lang/tomahawk_tr.ts index 5b85043d21..5b86b6fb45 100644 --- a/lang/tomahawk_tr.ts +++ b/lang/tomahawk_tr.ts @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline diff --git a/lang/tomahawk_zh_CN.ts b/lang/tomahawk_zh_CN.ts index f1191a4002..80adda3acf 100644 --- a/lang/tomahawk_zh_CN.ts +++ b/lang/tomahawk_zh_CN.ts @@ -3700,43 +3700,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) 扫描中 (%L1 首歌曲) - + Scanning 扫描中 - + Checking 检查 - + Syncing 同步中 - + Importing 导入中 - + Saving (%1%) 保存中 (%1%) - + Online 在线 - + Offline 离线 diff --git a/lang/tomahawk_zh_TW.ts b/lang/tomahawk_zh_TW.ts index 2180547ca0..ea7f9fa060 100644 --- a/lang/tomahawk_zh_TW.ts +++ b/lang/tomahawk_zh_TW.ts @@ -3690,43 +3690,43 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::Source - - + + Scanning (%L1 tracks) - + Scanning - + Checking - + Syncing - + Importing - + Saving (%1%) - + Online - + Offline From cb3889f37e9ae0399df16bc7f1c3c6f9352532a1 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 27 Jun 2013 15:32:07 +0200 Subject: [PATCH 529/565] Few minor fixes/improvements for Windows installer --- CMakeModules/NSIS.template.in | 6 ++---- thirdparty/breakpad/CMakeLists.txt | 6 +----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CMakeModules/NSIS.template.in b/CMakeModules/NSIS.template.in index b50e0c38f9..08ee16a95c 100644 --- a/CMakeModules/NSIS.template.in +++ b/CMakeModules/NSIS.template.in @@ -274,7 +274,6 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${INSTALL_PATH}\bin\tomahawk.exe" File "${INSTALL_PATH}\bin\tomahawk_crash_reporter.exe" - File "${INSTALL_PATH}\bin\libtomahawk_breakpad.dll" File "${INSTALL_PATH}\bin\libtomahawk.dll" ; plugins @@ -285,7 +284,6 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${BUILD_PATH}\tomahawk.exe" File "${BUILD_PATH}\tomahawk_crash_reporter.exe" - File "${BUILD_PATH}\libtomahawk_breakpad.dll" File "${BUILD_PATH}\libtomahawk.dll" ; plugins @@ -361,8 +359,8 @@ Section "Tomahawk Player" SEC_TOMAHAWK_PLAYER File "${MING_BIN}\libssl-8.dll" File "${MING_BIN}\libcrypto-8.dll" - File "${MING_LIB}\libclucene-core.dll" - File "${MING_LIB}\libclucene-shared.dll" + File "${MING_BIN}\libclucene-core.dll" + File "${MING_BIN}\libclucene-shared.dll" File "${MING_BIN}\libqtsparkle.dll" File "${MING_BIN}\libattica.dll" diff --git a/thirdparty/breakpad/CMakeLists.txt b/thirdparty/breakpad/CMakeLists.txt index 446175f3c4..a9761dcc93 100644 --- a/thirdparty/breakpad/CMakeLists.txt +++ b/thirdparty/breakpad/CMakeLists.txt @@ -80,9 +80,5 @@ ENDIF(WIN32) INCLUDE_DIRECTORIES(.) ADD_DEFINITIONS( -fPIC ) -IF(WIN32) - ADD_LIBRARY( tomahawk_breakpad SHARED ${breakpadSources} ) -ELSE() - ADD_LIBRARY( tomahawk_breakpad STATIC ${breakpadSources} ) -ENDIF() +ADD_LIBRARY( tomahawk_breakpad STATIC ${breakpadSources} ) TARGET_LINK_LIBRARIES( tomahawk_breakpad ) From 7219f1035a93e971ab2da673ecdc0889fd59fa5e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Thu, 27 Jun 2013 16:13:50 +0200 Subject: [PATCH 530/565] Add Network Activity to ChangeLog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index b8fb6973ef..0f2ac440b6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ Version 0.8.0: * You will now be asked whether you want to trust invalid SSL certificates. * Improved connecting between Tomahawk peers and support having multiple IPs (including IPv6) + * Added Network Activity showing you the most played tracks of your friends. Version 0.7.1: * Heavily reduced memory footprint during and after indexing the database. From 1554b638f6a31a274fe1c30c8d1aaf2f2108bce2 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Thu, 27 Jun 2013 21:39:21 +0200 Subject: [PATCH 531/565] * More debug output in DbCmd_SetPlaylistRevision. --- .../database/DatabaseCommand_SetPlaylistRevision.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp index 7dbb52f0d7..46969b719d 100644 --- a/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp +++ b/src/libtomahawk/database/DatabaseCommand_SetPlaylistRevision.cpp @@ -137,7 +137,7 @@ DatabaseCommand_SetPlaylistRevision::exec( DatabaseImpl* lib ) if ( chkq.exec() && chkq.next() ) { currentRevision = chkq.value( 0 ).toString(); - tDebug() << Q_FUNC_INFO << "pl guid" << m_playlistguid << "- curr rev" << currentRevision; + tDebug() << Q_FUNC_INFO << "pl guid" << m_playlistguid << "- curr rev" << currentRevision << source()->friendlyName() << source()->id(); } else { From 15704d8fbfc47bdb305bd9df593f00c548dda510 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 28 Jun 2013 13:27:08 +0200 Subject: [PATCH 532/565] Unbind from source if we are not responsible for it --- src/libtomahawk/network/ControlConnection.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index 36e701fb10..02272582db 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -145,6 +145,8 @@ ControlConnection::setup() } else { + // We are not responsible for this source anymore, so do not keep a reference. + d->source = Tomahawk::source_ptr(); // Unlock before we delete ourselves d->sourceLock.unlock(); // There is already another ControlConnection in use, we are useless. From 7f8bb31e23f537bc58d757041db94f63f65fa688 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 28 Jun 2013 14:23:14 +0200 Subject: [PATCH 533/565] Set nodeId on lazy offers --- src/libtomahawk/network/Servent.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 724616375f..975c56a44e 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -1090,6 +1090,13 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString conn->addPeerInfo( d_func()->lazyoffers.value( key ).first ); conn->setId( d_func()->lazyoffers.value( key ).second ); + if ( !nodeid.isEmpty() ) + { + // Used by the connection for the ACL check + // If there isn't a nodeid it's not the first connection and will already have been stopped + conn->setNodeId( nodeid ); + } + // Register as non-lazy offer d_func()->lazyoffers.remove( key ); registerOffer( key, conn ); From 8e0dcc69ed06f84b8baeb83589dacedd4d2fcbbd Mon Sep 17 00:00:00 2001 From: Teo Mrnjavac Date: Fri, 28 Jun 2013 21:52:33 +0200 Subject: [PATCH 534/565] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 723063a583..c9871e2087 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Required dependencies: * Attica 0.4.0 - ftp://ftp.kde.org/pub/kde/stable/attica/ * QuaZip 0.4.3 - http://quazip.sourceforge.net/ * liblastfm 1.0.1 - https://github.com/lastfm/liblastfm/ +* QtKeychain 0.1 - https://github.com/frankosterfeld/qtkeychain/ The following dependencies are optional, but recommended: From 33cca96f00e86a63dfadaf8e1343082882b096d8 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 28 Jun 2013 15:44:11 +0200 Subject: [PATCH 535/565] Connections should be started in the thread they live --- src/libtomahawk/network/ConnectionManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp index fe97a04d8d..5ab72f7be5 100644 --- a/src/libtomahawk/network/ConnectionManager.cpp +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -380,7 +380,7 @@ ConnectionManager::handoverSocket( QTcpSocketExtra* sock ) d_func()->controlConnection->setOutbound( sock->_outbound ); d_func()->controlConnection->setPeerPort( sock->peerPort() ); - d_func()->controlConnection->start( sock ); + QMetaObject::invokeMethod( d_func()->controlConnection, "start", Qt::QueuedConnection, Q_ARG( QTcpSocket*, sock ) ); // ControlConntection is now connected, now it can be destroyed if the PeerInfos disappear d_func()->controlConnection->setShutdownOnEmptyPeerInfos( true ); d_func()->currentPeerInfo.clear(); From 45519110b9300fd1d04833674cedb206489ecdd6 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Fri, 28 Jun 2013 17:34:29 +0200 Subject: [PATCH 536/565] Only match duplicates on inbound Connections --- src/libtomahawk/network/Servent.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 975c56a44e..e4fae72352 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -648,7 +648,8 @@ Servent::readyRead() Q_ASSERT( con ); tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Known connection:" << con->id(); - if ( con->id() == nodeid ) + // Only check for known inboud ControlConnections + if ( con->id() == nodeid && !con->outbound() ) { dupe = true; break; @@ -671,7 +672,8 @@ Servent::readyRead() { Q_ASSERT( keepConnection ); - if ( keepConnection->id() == nodeid ) + // Only check for known inboud ControlConnections + if ( keepConnection->id() == nodeid && !keepConnection->outbound() ) { tDebug() << "Keep connection" << keepConnection->name() << "with following peers"; foreach ( const peerinfo_ptr& currentPeerInfo, keepConnection->peerInfos() ) @@ -694,7 +696,8 @@ Servent::readyRead() { Q_ASSERT( con ); - if ( con->id() == controlid ) + // Only check for known inboud ControlConnections + if ( con->id() == controlid && !con->outbound() ) { cc = con; break; From cf93741079dec596c48163613c55187d36074e87 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 29 Jun 2013 09:02:28 +0200 Subject: [PATCH 537/565] Keep lazy offers lazy --- src/libtomahawk/network/Servent.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index e4fae72352..8a7d1b6e27 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -1100,10 +1100,6 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString conn->setNodeId( nodeid ); } - // Register as non-lazy offer - d_func()->lazyoffers.remove( key ); - registerOffer( key, conn ); - return conn; } else if ( d_func()->offers.contains( key ) ) From ff8e90559905da42f8951b8d7fbef95936d0df9e Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sat, 29 Jun 2013 09:03:03 +0200 Subject: [PATCH 538/565] Only setup a Connection once --- src/libtomahawk/network/Connection.cpp | 58 +++++++++++++++----------- src/libtomahawk/network/Connection_p.h | 2 + 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/libtomahawk/network/Connection.cpp b/src/libtomahawk/network/Connection.cpp index 03adfb45ee..4981dae72c 100644 --- a/src/libtomahawk/network/Connection.cpp +++ b/src/libtomahawk/network/Connection.cpp @@ -400,7 +400,7 @@ Connection::doSetup() { Q_D( Connection ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << thread(); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << thread() << d->id; /* New connections can be created from other thread contexts, such as when AudioEngine calls getIODevice.. - we need to ensure that connections @@ -414,39 +414,49 @@ Connection::doSetup() moveToThread( d->servent->thread() ); } - //stats timer calculates BW used by this connection - d->statstimer = new QTimer; - d->statstimer->moveToThread( this->thread() ); - d->statstimer->setInterval( 1000 ); - connect( d->statstimer, SIGNAL( timeout() ), SLOT( calcStats() ) ); - d->statstimer->start(); - d->statstimer_mark.start(); + if ( !d->setup ) + { + // We only want to setup this connection once + d->setup = true; - d->sock->moveToThread( thread() ); + //stats timer calculates BW used by this connection + d->statstimer = new QTimer; + d->statstimer->moveToThread( this->thread() ); + d->statstimer->setInterval( 1000 ); + connect( d->statstimer, SIGNAL( timeout() ), SLOT( calcStats() ) ); + d->statstimer->start(); + d->statstimer_mark.start(); - connect( d->sock.data(), SIGNAL( bytesWritten( qint64 ) ), - SLOT( bytesWritten( qint64 ) ), Qt::QueuedConnection ); + d->sock->moveToThread( thread() ); - connect( d->sock.data(), SIGNAL( disconnected() ), - SLOT( socketDisconnected() ), Qt::QueuedConnection ); + connect( d->sock.data(), SIGNAL( bytesWritten( qint64 ) ), + SLOT( bytesWritten( qint64 ) ), Qt::QueuedConnection ); - connect( d->sock.data(), SIGNAL( error( QAbstractSocket::SocketError ) ), - SLOT( socketDisconnectedError( QAbstractSocket::SocketError ) ), Qt::QueuedConnection ); + connect( d->sock.data(), SIGNAL( disconnected() ), + SLOT( socketDisconnected() ), Qt::QueuedConnection ); - connect( d->sock.data(), SIGNAL( readyRead() ), - SLOT( readyRead() ), Qt::QueuedConnection ); + connect( d->sock.data(), SIGNAL( error( QAbstractSocket::SocketError ) ), + SLOT( socketDisconnectedError( QAbstractSocket::SocketError ) ), Qt::QueuedConnection ); - // if connection not authed/setup fast enough, kill it: - QTimer::singleShot( AUTH_TIMEOUT, this, SLOT( authCheckTimeout() ) ); + connect( d->sock.data(), SIGNAL( readyRead() ), + SLOT( readyRead() ), Qt::QueuedConnection ); - if ( outbound() ) - { - Q_ASSERT( !d->firstmsg.isNull() ); - sendMsg( d->firstmsg ); + // if connection not authed/setup fast enough, kill it: + QTimer::singleShot( AUTH_TIMEOUT, this, SLOT( authCheckTimeout() ) ); + + if ( outbound() ) + { + Q_ASSERT( !d->firstmsg.isNull() ); + sendMsg( d->firstmsg ); + } + else + { + sendMsg( Msg::factory( PROTOVER, Msg::SETUP ) ); + } } else { - sendMsg( Msg::factory( PROTOVER, Msg::SETUP ) ); + tLog() << Q_FUNC_INFO << QThread::currentThread() << d->id << "Duplicate doSetup call"; } // call readyRead incase we missed the signal in between the servent disconnecting and us diff --git a/src/libtomahawk/network/Connection_p.h b/src/libtomahawk/network/Connection_p.h index 6e0d519faf..8baccaace2 100644 --- a/src/libtomahawk/network/Connection_p.h +++ b/src/libtomahawk/network/Connection_p.h @@ -41,6 +41,7 @@ class ConnectionPrivate , peer_disconnected( false ) , ready( false ) , onceonly( true ) + , setup( false ) , tx_bytes( 0 ) , tx_bytes_requested( 0 ) , rx_bytes( 0 ) @@ -66,6 +67,7 @@ class ConnectionPrivate bool outbound; bool ready; bool onceonly; + bool setup; qint64 tx_bytes; qint64 tx_bytes_requested; qint64 rx_bytes; From 1252b6618dc302e8f33bc8c6998043b0d3bc47df Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 17:06:54 +0200 Subject: [PATCH 539/565] fix merge --- src/tomahawk/sourcetree/SourcesModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tomahawk/sourcetree/SourcesModel.cpp b/src/tomahawk/sourcetree/SourcesModel.cpp index 91aa121284..c298dc0117 100644 --- a/src/tomahawk/sourcetree/SourcesModel.cpp +++ b/src/tomahawk/sourcetree/SourcesModel.cpp @@ -313,7 +313,7 @@ SourcesModel::appendGroups() sc->setSortValue( 1 ); // browse section - GenericPageItem* radio = new GenericPageItem( this, browse, tr( "Radio" ), ImageRegistry::instance()->icon( RESPATH "images/station.svg" ), + GenericPageItem* radio = new GenericPageItem( this, m_browse, tr( "Radio" ), ImageRegistry::instance()->icon( RESPATH "images/station.svg" ), boost::bind( &ViewManager::showRadioPage, ViewManager::instance() ), boost::bind( &ViewManager::recentPlaysWidget, ViewManager::instance() ) ); radio->setSortValue( 2 ); From b402347beef5899f2395b6f81d30588eec5aa6d9 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 18:48:18 +0200 Subject: [PATCH 540/565] fix playlist generation and clean up dynamicqmlwidget a bit --- .../dynamic/widgets/DynamicQmlWidget.cpp | 42 ++++++------------- .../dynamic/widgets/DynamicQmlWidget.h | 2 - 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 3c783d3ca7..a645eec91a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -26,8 +26,6 @@ namespace Tomahawk DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* parent ) : DeclarativeView( parent ) , m_playlist( playlist ) - , m_runningOnDemand( false ) - , m_activePlaylist( false ) , m_playNextResolved( false ) { m_model = new DynamicModel( this ); @@ -62,8 +60,12 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) ); - // m_playlist->generator()->generate( 20 ); - loadArtistCharts(); + if (configured()) { + m_playlist->generator()->generate( 20 ); + } else { + // TODO: only load if needed, i.e. the user clicks on start station by artist + loadArtistCharts(); + } } @@ -212,10 +214,7 @@ void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track) void DynamicQmlWidget::error(const QString &title, const QString &body) { - qDebug() << "got a generator error:" << title << body; - -// m_playlist->generator()->fetchNext(); - + tDebug() << "got a generator error:" << title << body; } void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision) @@ -226,9 +225,9 @@ void DynamicQmlWidget::onRevisionLoaded(DynamicPlaylistRevision) void DynamicQmlWidget::resolvingFinished(bool hasResults) { Q_UNUSED(hasResults) - qDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row(); + tDebug() << "next track generated" << m_proxyModel->rowCount() << m_proxyModel->currentIndex().row(); if( m_proxyModel->rowCount() <= m_proxyModel->currentIndex().row() + 8 ) { - qDebug() << "fetching next one"; + tDebug() << "fetching next one"; m_playlist->generator()->fetchNext(); } @@ -240,28 +239,14 @@ void DynamicQmlWidget::resolvingFinished(bool hasResults) void DynamicQmlWidget::trackStarted() { - if ( m_activePlaylist && !m_playlist.isNull() && - m_playlist->mode() == OnDemand && !m_runningOnDemand ) - { - - startStation(); - } + startStation(); } void DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl ) { - if ( pl == m_proxyModel->playlistInterface() ) // same playlist - m_activePlaylist = true; - else - { - m_activePlaylist = false; - - // user started playing something somewhere else, so give it a rest - if ( m_runningOnDemand ) - { - stopStation( false ); - } + if ( pl != m_proxyModel->playlistInterface() ) { + stopStation( false ); } } @@ -269,14 +254,11 @@ void DynamicQmlWidget::stopStation( bool stopPlaying ) { m_model->stopOnDemand( stopPlaying ); - m_runningOnDemand = false; - } void DynamicQmlWidget::startStation() { - m_runningOnDemand = true; m_model->startOnDemand(); } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index bf77e4b78c..ce2d59f40e 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -102,8 +102,6 @@ private slots: PlayableModel* m_artistChartsModel; - bool m_runningOnDemand; - bool m_activePlaylist; bool m_playNextResolved; }; From 6ab35323e89334acb891cd2536d9112f2fcae426 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 18:55:29 +0200 Subject: [PATCH 541/565] more cleanup --- .../dynamic/widgets/DynamicQmlWidget.cpp | 29 ------------------- .../dynamic/widgets/DynamicQmlWidget.h | 5 ---- 2 files changed, 34 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index a645eec91a..deba5107d7 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -57,9 +57,6 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) ); connect( m_playlist->generator().data(), SIGNAL( error( QString, QString )), SLOT( error(QString,QString) ) ); - connect( AudioEngine::instance(), SIGNAL( started( Tomahawk::result_ptr ) ), this, SLOT( trackStarted() ) ); - connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ), this, SLOT( playlistChanged( Tomahawk::playlistinterface_ptr ) ) ); - if (configured()) { m_playlist->generator()->generate( 20 ); } else { @@ -237,32 +234,6 @@ void DynamicQmlWidget::resolvingFinished(bool hasResults) } } -void DynamicQmlWidget::trackStarted() -{ - startStation(); -} - -void -DynamicQmlWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl ) -{ - if ( pl != m_proxyModel->playlistInterface() ) { - stopStation( false ); - } -} - -void -DynamicQmlWidget::stopStation( bool stopPlaying ) -{ - m_model->stopOnDemand( stopPlaying ); -} - -void -DynamicQmlWidget::startStation() -{ - m_model->startOnDemand(); -} - - void DynamicQmlWidget::loadArtistCharts() { diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h index ce2d59f40e..397ee47430 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.h @@ -84,14 +84,9 @@ private slots: void error( const QString& title, const QString& body); void onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ); - void playlistChanged( Tomahawk::playlistinterface_ptr pl ); void resolvingFinished( bool hasResults ); - void trackStarted(); - void startStation(); - void stopStation( bool stopPlaying ); - void loadArtistCharts(); void onArtistCharts( const QList< Tomahawk::artist_ptr >& artists ); From a7152f83f4b48cd554e9d7c5eec51c1dc96e580b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:53:30 +0200 Subject: [PATCH 542/565] * Style fix. --- src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 486774f4b7..2df06574e2 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -276,7 +276,8 @@ DynamicPlaylist::loadRevision( const QString& rev ) setBusy( true ); DatabaseCommand_LoadDynamicPlaylistEntries* cmd = new DatabaseCommand_LoadDynamicPlaylistEntries( rev.isEmpty() ? currentrevision() : rev ); - if ( m_generator->mode() == OnDemand ) { + if ( m_generator->mode() == OnDemand ) + { connect( cmd, SIGNAL( done( QString, bool, QString, @@ -287,7 +288,9 @@ DynamicPlaylist::loadRevision( const QString& rev ) QString, QVariantList, bool) ) ); - } else if ( m_generator->mode() == Static ) { + } + else if ( m_generator->mode() == Static ) + { connect( cmd, SIGNAL( done( QString, QList< QString >, QList< QString >, @@ -407,6 +410,7 @@ DynamicPlaylist::setRevision( const QString& rev, m_generator = GeneratorFactory::create( type ); } + tDebug() << Q_FUNC_INFO << controls; m_generator->setControls( controls ); m_generator->setMode( Static ); @@ -452,6 +456,7 @@ DynamicPlaylist::setRevision( const QString& rev, m_generator = geninterface_ptr( GeneratorFactory::create( type ) ); } + tDebug() << Q_FUNC_INFO << controls; m_generator->setControls( controls ); m_generator->setMode( OnDemand ); From 75a1fce2d860f0d963d8b28017940d74fc28ba3b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:56:08 +0200 Subject: [PATCH 543/565] * Create dynamic playlist when needed - for temporary ones. --- .../dynamic/widgets/DynamicQmlWidget.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index deba5107d7..6ad8a743e6 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -10,6 +10,7 @@ #include "SourceList.h" #include "audio/AudioEngine.h" #include "database/Database.h" +#include "database/DatabaseCommand_CreateDynamicPlaylist.h" #include "database/DatabaseCommand_PlaybackCharts.h" #include "widgets/DeclarativeCoverArtProvider.h" #include "utils/TomahawkUtilsGui.h" @@ -89,14 +90,23 @@ DynamicQmlWidget::title() const void -DynamicQmlWidget::setTitle(const QString &title) +DynamicQmlWidget::setTitle( const QString& title ) { m_model->setTitle( title ); m_playlist->setTitle( title ); m_model->playlist()->setTitle( title ); - m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); - m_playlist->reportCreated( m_playlist ); - emit titleChanged(); + + if ( !m_playlist->loaded() ) + { + DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( SourceList::instance()->getLocal(), m_playlist, true ); +// connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); + Database::instance()->enqueue( QSharedPointer(cmd) ); + m_playlist->reportCreated( m_playlist ); + + m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); +// m_playlist->reportCreated( m_playlist ); + emit titleChanged(); + } } @@ -134,6 +144,7 @@ bool DynamicQmlWidget::loading() bool DynamicQmlWidget::configured() { +// return true; return !m_playlist->generator()->controls().isEmpty(); } @@ -202,6 +213,7 @@ DynamicQmlWidget::tracksGenerated( const QList< query_ptr >& queries ) void DynamicQmlWidget::nextTrackGenerated(const query_ptr &track) { + tDebug() << Q_FUNC_INFO << track->toString(); m_model->tracksGenerated( QList() << track ); m_playlist->resolve(); From 02f8eac1b380c35c6f7102a72d68b0819811e3af Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:56:26 +0200 Subject: [PATCH 544/565] * Radio mode needs to initialize a temporary station. --- src/libtomahawk/ViewManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/ViewManager.cpp b/src/libtomahawk/ViewManager.cpp index 75c9707f29..4899b9e6f1 100644 --- a/src/libtomahawk/ViewManager.cpp +++ b/src/libtomahawk/ViewManager.cpp @@ -394,7 +394,7 @@ ViewManager::showRadioPage() { if ( !m_radioView ) { - dynplaylist_ptr playlist = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false, QString(), false, false ); + dynplaylist_ptr playlist = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false, QString(), false, true ); playlist->setMode( OnDemand ); m_radioView = new Tomahawk::DynamicQmlWidget( playlist, m_stack ); From c946bd9b8dbf798170445ba44bb0537d0a8a41e0 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 18:57:57 +0200 Subject: [PATCH 545/565] * Fixed displaying of back / save button. --- data/qml/StationView.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 3ce7639c6d..33eb10e1a6 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -22,9 +22,9 @@ Rectangle { subtitle: ""//generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: !mainView.configured && stationListView.currentIndex == 2 + showNextButton: !mainView.configured nextButtonText: "Save" - backButtonText: mainView.configured ? "Configure" : "Back" + backButtonText: "Back" z: 1 //cover albumcovers that may leave their area From 59003b88e93e0fdfc208d989f3af7c529e42aeea Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:46:16 +0200 Subject: [PATCH 546/565] * Show save button when we have a configured station. --- data/qml/StationView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 33eb10e1a6..bea98e94d7 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -22,7 +22,7 @@ Rectangle { subtitle: ""//generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: !mainView.configured + showNextButton: mainView.configured nextButtonText: "Save" backButtonText: "Back" From 6378a1658d4d33caacd03927fa9f1aec34c2130e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:46:31 +0200 Subject: [PATCH 547/565] * Fixed invoke. --- src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp index 2df06574e2..bdac2772fe 100644 --- a/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp +++ b/src/libtomahawk/playlist/dynamic/DynamicPlaylist.cpp @@ -445,7 +445,7 @@ DynamicPlaylist::setRevision( const QString& rev, Q_ARG( QString, rev ), Q_ARG( bool, is_newest_rev ), Q_ARG( QString, type ), - QGenericArgument( "QList< Tomahawk::dyncontrol_ptr >" , (const void*)&controls ), + Q_ARG( QVariantList, controls ), Q_ARG( bool, applied ) ); return; } From a4fee407a474b67413b404aaf662961197f70a48 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:46:55 +0200 Subject: [PATCH 548/565] * Create those darned controls manually. --- .../dynamic/echonest/EchonestGenerator.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 056a4b526e..ffcbbd9820 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -274,6 +274,22 @@ EchonestGenerator::startFromArtist( const Tomahawk::artist_ptr& artist ) Echonest::DynamicPlaylist::PlaylistParams params; params << data; + +/* Q_PROPERTY( QString type READ type WRITE setType ) // the generator type associated with this control + Q_PROPERTY( QString id READ id WRITE setId ) + Q_PROPERTY( QString selectedType READ selectedType WRITE setSelectedType ) + Q_PROPERTY( QString match READ match WRITE setMatch ) + Q_PROPERTY( QString input READ input WRITE setInput ) + Q_PROPERTY( QString summary READ summary ) // a summary of the control in phrase form*/ + + QVariantMap controlsList; + controlsList[ "id" ] = uuid(); + controlsList[ "selectedType" ] = "echonest"; + controlsList[ "match" ] = QString::number( data.first ); + controlsList[ "input" ] = data.second; + controlsList[ "summary" ] = ""; + setControls( QVariantList() << controlsList ); + // params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); emit paramsGenerated( params ); From 490f736a934f0aa6d2841912e39b02df3ac53185 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 19:47:12 +0200 Subject: [PATCH 549/565] * Print out controls when creating a station. --- .../playlist/dynamic/widgets/DynamicQmlWidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 6ad8a743e6..20f564610a 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -98,13 +98,13 @@ DynamicQmlWidget::setTitle( const QString& title ) if ( !m_playlist->loaded() ) { + tDebug() << "CONTROLS ARE SAVED:" << m_playlist->generator()->controls(); DatabaseCommand_CreateDynamicPlaylist* cmd = new DatabaseCommand_CreateDynamicPlaylist( SourceList::instance()->getLocal(), m_playlist, true ); -// connect( cmd, SIGNAL(finished()), dynplaylist.data(), SIGNAL(created()) ); Database::instance()->enqueue( QSharedPointer(cmd) ); - m_playlist->reportCreated( m_playlist ); + m_playlist->reportCreated( m_playlist ); m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_playlist->type(), m_playlist->generator()->controls() ); -// m_playlist->reportCreated( m_playlist ); + emit titleChanged(); } } From 952120a2600648fcac6c8c5c7e2dd49904607a97 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 21:15:51 +0200 Subject: [PATCH 550/565] * Create EchonestParams out of the variant list. --- .../dynamic/echonest/EchonestGenerator.cpp | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index ffcbbd9820..87527c6afa 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -419,22 +419,40 @@ EchonestGenerator::staticFinished() void EchonestGenerator::getParams() throw( std::runtime_error ) { + /*Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = Echonest::DynamicPlaylist::Artist; + data.second = artist->name(); + Echonest::DynamicPlaylist::PlaylistParams params; -/* foreach( const dyncontrol_ptr& control, m_controls ) { - params.append( control.dynamicCast()->toENParam() ); + params << data; + */ + + Echonest::DynamicPlaylist::PlaylistParams params; + foreach( const QVariant& control, m_controls ) + { + QVariantMap controlMap = control.toMap(); + Echonest::DynamicPlaylist::PlaylistParamData data; + data.first = (Echonest::DynamicPlaylist::PlaylistParam)controlMap[ "match" ].toUInt(); + data.second = controlMap[ "input" ].toString(); + + params.append( data ); } - if( appendRadioType( params ) == Echonest::DynamicPlaylist::SongRadioType ) { + if ( appendRadioType( params ) == Echonest::DynamicPlaylist::SongRadioType ) + { // we need to do another pass, converting all song queries to song-ids. m_storedParams = params; qDeleteAll( m_waiting ); m_waiting.clear(); // one query per track - for( int i = 0; i < params.count(); i++ ) { + for( int i = 0; i < params.count(); i++ ) + { const Echonest::DynamicPlaylist::PlaylistParamData param = params.value( i ); - if( param.first == Echonest::DynamicPlaylist::SongId ) { // this is a song type enum + if ( param.first == Echonest::DynamicPlaylist::SongId ) + { + // this is a song type enum QString text = param.second.toString(); Echonest::Song::SearchParams q; @@ -448,14 +466,16 @@ EchonestGenerator::getParams() throw( std::runtime_error ) } } - if( m_waiting.isEmpty() ) { + if ( m_waiting.isEmpty() ) + { m_storedParams.clear(); emit paramsGenerated( params ); } - - } else { + } + else + { emit paramsGenerated( params ); - }*/ + } } From 53ba4aae9324027fae2bb4367f4a340c0fa7e5f3 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 21:07:13 +0200 Subject: [PATCH 551/565] generate does the real thing again --- .../dynamic/echonest/EchonestGenerator.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 87527c6afa..087c413761 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -188,10 +188,7 @@ void EchonestGenerator::generate( int number ) { // convert to an echonest query, and fire it off -/* qDebug() << Q_FUNC_INFO; - qDebug() << "Generating playlist with" << m_controls.size(); - foreach( const dyncontrol_ptr& ctrl, m_controls ) - qDebug() << ctrl->selectedType() << ctrl->match() << ctrl->input(); + tDebug() << "Generating playlist with" << m_controls.size(); setProperty( "number", number ); //HACK @@ -202,16 +199,7 @@ EchonestGenerator::generate( int number ) } catch( std::runtime_error& e ) { qWarning() << "Got invalid controls!" << e.what(); emit error( "Filters are not valid", e.what() ); - }*/ - - QList< query_ptr > queries; - queries << Query::get("Colour Haze", "All", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Sun", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Zen", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Outside", QString(), uuid(), true); - queries << Query::get("Colour Haze", "Dirt", QString(), uuid(), true); - - emit generated( queries ); + } } From 6bb9ec7de3e43fd8763835fa749dfc5034fbabe7 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 29 Jun 2013 21:51:10 +0200 Subject: [PATCH 552/565] * Fixed startFromGenre. --- .../dynamic/echonest/EchonestGenerator.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 087c413761..1c0e78ba32 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -307,7 +307,24 @@ EchonestGenerator::startFromGenre( const QString& genre ) Echonest::DynamicPlaylist::PlaylistParams params; params << data; + + QVariantList controlsList; + QVariantMap controlsMap; + + controlsMap[ "id" ] = uuid(); + controlsMap[ "selectedType" ] = "echonest"; + controlsMap[ "match" ] = QString::number( data.first ); + controlsMap[ "input" ] = data.second; + controlsMap[ "summary" ] = ""; + controlsList << controlsMap; + params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); + controlsMap[ "id" ] = uuid(); + controlsMap[ "match" ] = QString::number( Echonest::DynamicPlaylist::Type ); + controlsMap[ "input" ] = QString::number( Echonest::DynamicPlaylist::ArtistDescriptionType ); + controlsList << controlsMap; + + setControls( controlsList ); emit paramsGenerated( params ); return true; From 1d9b76efbc0a4307392bf441b1901047ef0743c8 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 21:42:11 +0200 Subject: [PATCH 553/565] station summary does something again, at least for stations by artist --- data/qml/StationView.qml | 6 +----- .../playlist/dynamic/echonest/EchonestGenerator.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index bea98e94d7..ed032e4ab9 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -19,7 +19,7 @@ Rectangle { width: parent.width icon: "../images/station.svg" title: mainView.title - subtitle: ""//generator.summary + subtitle: generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 showNextButton: mainView.configured @@ -29,10 +29,6 @@ Rectangle { z: 1 //cover albumcovers that may leave their area onBackPressed: { - if(mainView.configured) { - return; - } - inputBubble.opacity = 0 stationListView.decrementCurrentIndex() if(stationListView.currentIndex == 1) { diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 1c0e78ba32..4ee1ade16e 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -275,7 +275,7 @@ EchonestGenerator::startFromArtist( const Tomahawk::artist_ptr& artist ) controlsList[ "selectedType" ] = "echonest"; controlsList[ "match" ] = QString::number( data.first ); controlsList[ "input" ] = data.second; - controlsList[ "summary" ] = ""; + controlsList[ "summary" ] = tr("Songs from %1").arg(data.second.toString()); setControls( QVariantList() << controlsList ); // params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::SongRadioType ) ); @@ -666,6 +666,9 @@ EchonestGenerator::sentenceSummary() * NOTE / TODO: In order for the sentence to be grammatically correct, we must follow the EN API rules. That means we can't have multiple of some types of filters, * and all Artist types must be the same. The filters aren't checked at the moment until Generate / Play is pressed. Consider doing a check on hide as well. */ + + // Keeping this for now to make stuff backwards compatible + /* QList< dyncontrol_ptr > allcontrols = m_controls; QString sentence = "Songs "; @@ -751,7 +754,10 @@ EchonestGenerator::sentenceSummary() return sentence;*/ - return "This is a station!"; + if (m_controls.isEmpty()) { + return ""; + } + return m_controls.first().toMap().value("summary").toString(); } void From c161d5dc3d86c8a979a861013de9dd223e2cc19a Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 22:00:01 +0200 Subject: [PATCH 554/565] also add a description for byStation --- src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 4ee1ade16e..07965385de 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -315,7 +315,7 @@ EchonestGenerator::startFromGenre( const QString& genre ) controlsMap[ "selectedType" ] = "echonest"; controlsMap[ "match" ] = QString::number( data.first ); controlsMap[ "input" ] = data.second; - controlsMap[ "summary" ] = ""; + controlsMap[ "summary" ] = data.second; controlsList << controlsMap; params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); From d65de018478845ccc291f6a8a6d921880ae71a5d Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 22:00:10 +0200 Subject: [PATCH 555/565] fix loading state --- src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 20f564610a..8fede25e52 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -50,8 +50,6 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa setSource( QUrl( "qrc" RESPATH "qml/StationView.qml" ) ); connect( m_model, SIGNAL( currentIndexChanged()), SLOT( currentIndexChanged() ) ); - connect( m_model, SIGNAL( loadingStarted() ), SIGNAL(loadingChanged() ) ); - connect( m_model, SIGNAL( loadingFinished() ), SIGNAL(loadingChanged() ) ); connect( m_model, SIGNAL( changed() ), SIGNAL( titleChanged() ) ); connect( m_playlist->generator().data(), SIGNAL( generated( QList ) ), this, SLOT( tracksGenerated( QList ) ) ); connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( nextTrackGenerated( Tomahawk::query_ptr ) ) ); @@ -241,6 +239,7 @@ void DynamicQmlWidget::resolvingFinished(bool hasResults) } if( m_playNextResolved && m_proxyModel->rowCount() > 0 ) { + emit loadingChanged(); playItem( 0 ); m_playNextResolved = false; } From cee26a77c34eed6066d1fc1806118fa65897d319 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 29 Jun 2013 22:24:50 +0200 Subject: [PATCH 556/565] adjust station summaries to look the same everywhere --- data/qml/StationView.qml | 6 +++--- data/qml/stations/CreateByArtist.qml | 2 +- data/qml/stations/CreateByGenre.qml | 2 +- data/qml/stations/CreateByYear.qml | 2 +- .../playlist/dynamic/echonest/EchonestGenerator.cpp | 2 +- .../playlist/dynamic/widgets/DynamicQmlWidget.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index ed032e4ab9..009f7b6ac0 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -48,9 +48,9 @@ Rectangle { ListModel { id: modeModel - ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml"; headerSubtitle: "by" } - ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml"; headerSubtitle: "like" } - ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "stations/CreateByYear.qml"; headerSubtitle: "from" } + ListElement { label: "By Artist"; image: "../../images/station-artist.svg"; creatorContent: "stations/CreateByArtist.qml"; headerSubtitle: "Songs from" } + ListElement { label: "By Genre"; image: "../../images/station-genre.svg"; creatorContent: "stations/CreateByGenre.qml"; headerSubtitle: "Songs of genre" } + ListElement { label: "By Year"; image: "../../images/station-year.svg"; creatorContent: "stations/CreateByYear.qml"; headerSubtitle: "Songs from" } } VisualItemModel { diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml index 359b353f78..60bb5b4b6a 100644 --- a/data/qml/stations/CreateByArtist.qml +++ b/data/qml/stations/CreateByArtist.qml @@ -23,7 +23,7 @@ Item { HeaderLabel { id: headerText - text: "Create station by artist..." + text: "Enter or pick an artist" } Row { diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml index 1d89641833..017c0cf020 100644 --- a/data/qml/stations/CreateByGenre.qml +++ b/data/qml/stations/CreateByGenre.qml @@ -46,7 +46,7 @@ Item { HeaderLabel { id: headerText anchors.horizontalCenter: parent.horizontalCenter - text: "Create station by genre..." + text: "Enter or pick a genre" } Row { diff --git a/data/qml/stations/CreateByYear.qml b/data/qml/stations/CreateByYear.qml index ae9f15a69b..ebd0a8c3a3 100644 --- a/data/qml/stations/CreateByYear.qml +++ b/data/qml/stations/CreateByYear.qml @@ -28,7 +28,7 @@ Item { HeaderLabel { id: headerText - text: "Create station by year..." + text: "Enter a year or pick a range" } Row { diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp index 07965385de..8a703458f9 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestGenerator.cpp @@ -315,7 +315,7 @@ EchonestGenerator::startFromGenre( const QString& genre ) controlsMap[ "selectedType" ] = "echonest"; controlsMap[ "match" ] = QString::number( data.first ); controlsMap[ "input" ] = data.second; - controlsMap[ "summary" ] = data.second; + controlsMap[ "summary" ] = tr("Songs of genre %1").arg(data.second.toString()); controlsList << controlsMap; params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index 8fede25e52..fc60eb447b 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -83,7 +83,7 @@ DynamicQmlWidget::title() const if ( !m_playlist->title().isEmpty() ) { return m_playlist->title(); } - return "Listen to radio..."; + return "Listen to radio"; } From 26f581dc97af47c18c50abbdff3384ca65e5cf0b Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 1 Jul 2013 21:03:33 +0200 Subject: [PATCH 557/565] some more work on saving stations --- data/qml/StationView.qml | 73 +++++----------- data/qml/tomahawkimports/Button.qml | 37 +++++--- data/qml/tomahawkimports/InputBubble.qml | 104 +++++++++++++++++++++++ resources.qrc | 1 + 4 files changed, 153 insertions(+), 62 deletions(-) create mode 100644 data/qml/tomahawkimports/InputBubble.qml diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 009f7b6ac0..01ae10474e 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -2,6 +2,7 @@ import QtQuick 1.1 import tomahawk 1.0 import "tomahawkimports" import "stations" + Rectangle { id: scene color: "black" @@ -22,7 +23,7 @@ Rectangle { subtitle: generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: mainView.configured + showNextButton: mainView.configured && stationListView.currentIndex > 0 nextButtonText: "Save" backButtonText: "Back" @@ -41,7 +42,7 @@ Rectangle { // In our case the next button is the save button onNextPressed: { inputBubble.opacity = 1 - saveNameInput.forceActiveFocus(); + inputBubble.forceActiveFocus(); } } @@ -137,55 +138,25 @@ Rectangle { onModelChanged: print("ccccccccccccc", mainView.configured) } - Rectangle { - id: inputBubble - color: "black" - border.width: 2 - border.color: "white" - height: defaultFontHeight * 3 - width: height * 6 - radius: defaultFontHeight / 2 - anchors.top: header.bottom - anchors.right: parent.right - anchors.rightMargin: defaultFontHeight / 2 - anchors.topMargin: -defaultFontHeight / 2 - z: 2 - opacity: 0 - Behavior on opacity { - NumberAnimation { duration: 200 } - } - - Row { - anchors.centerIn: parent - width: parent.width - defaultFontHeight - spacing: defaultFontHeight / 2 - - function saveStation(name) { - mainView.title = name - inputBubble.opacity = 0 - header.showNextButton = false - header.backButtonText = "Configure" - } - - Text { - id: nameText - color: "white" - text: "Name:" - anchors.verticalCenter: parent.verticalCenter - } - InputField { - id: saveNameInput - width: parent.width - nameText.width - saveOkButton.width - parent.spacing * 2 - placeholderText: "Station" - onAccepted: parent.saveStation(text); - } - PushButton { - id: saveOkButton - text: "OK" - onClicked: parent.saveStation(saveNameInput.text) - } - } - + InputBubble { + id: inputBubble + text: "Station name:" + width: defaultFontHeight * 18 + anchors.top: header.bottom + anchors.right: parent.right + anchors.rightMargin: defaultFontHeight / 2 + anchors.topMargin: -defaultFontHeight / 2 + z: 2 + opacity: 0 + + onAccepted: { + mainView.title = inputBubble.inputText + inputBubble.opacity = 0 + header.showNextButton = false + header.showBackButton = false + inputBubble.opacity = 0 } + onRejected: inputBubble.opacity = 0 + } } diff --git a/data/qml/tomahawkimports/Button.qml b/data/qml/tomahawkimports/Button.qml index 1c73d7c8c8..344b1403d8 100644 --- a/data/qml/tomahawkimports/Button.qml +++ b/data/qml/tomahawkimports/Button.qml @@ -2,28 +2,43 @@ import QtQuick 1.1 Rectangle { id: root - color: buttonMouseArea.containsMouse ? "blue" : "gray" - border.width: 2 - border.color: "white" - radius: height/2 - height: buttonText.height * 1.2 - width: buttonText.width * 1.5 + height: contentRow.height + defaultFontHeight / 2 + width: contentRow.width + defaultFontHeight / 2 property alias text: buttonText.text - property color textColor: "white" + property alias imageSource: image.source + property bool enabled: true + + color: "transparent" + border.width: defaultFontHeight / 16 + border.color: buttonMouseArea.containsMouse ? "lightblue" : "transparent" + radius: defaultFontHeight / 4 signal clicked() - Text { - id: buttonText + Row { + id: contentRow + spacing: defaultFontHeight / 4 + width: childrenRect.width + height: childrenRect.height anchors.centerIn: parent - color: root.textColor + Image { + id: image + height: defaultFontHeight + width: source.length == 0 ? 0 : height + } + + Text { + id: buttonText + color: root.enabled ? "black" : "grey" + } } MouseArea { id: buttonMouseArea anchors.fill: parent - hoverEnabled: true + hoverEnabled: root.enabled + enabled: root.enabled onClicked: root.clicked(); } } diff --git a/data/qml/tomahawkimports/InputBubble.qml b/data/qml/tomahawkimports/InputBubble.qml new file mode 100644 index 0000000000..efaa4d2496 --- /dev/null +++ b/data/qml/tomahawkimports/InputBubble.qml @@ -0,0 +1,104 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +FocusScope { + id: root + + property alias text: messageText.text + property alias inputText: inputField.text + + property real arrowPosition: 1 + + height: contentColumn.height + defaultFontHeight * 2 + + signal accepted() + signal rejected() + + onFocusChanged: { + if (focus) { + inputField.forceActiveFocus() + } + } + + MouseArea { + anchors.fill: parent + anchors.margins: -999999999 + hoverEnabled: root.opacity > 0 + enabled: root.opacity > 0 + } + + Item { + id: backgroundItem + anchors.fill: parent + opacity: 0.9 + Rectangle { + id: arrowRect + height: defaultFontHeight / 1.8 + width: height + rotation: 45 + color: "black" + anchors.top: backgroundItem.top + x: defaultFontHeight - arrowRect.width/2 + (root.width - defaultFontHeight*2) * arrowPosition + } + Rectangle { + id: background + anchors.fill: parent + color: "white" + border.color: "black" + border.width: defaultFontHeight / 8 + radius: defaultFontHeight / 2 + anchors.topMargin: defaultFontHeight / 4 + } + Rectangle { + height: defaultFontHeight / 2 + width: height + rotation: 45 + color: "white" + anchors.centerIn: arrowRect + } + } + + Column { + id: contentColumn + width: parent.width - defaultFontHeight + height: childrenRect.height + anchors.centerIn: parent + anchors.verticalCenterOffset: defaultFontHeight / 4 + spacing: defaultFontHeight / 2 + + Row { + width: parent.width + height: childrenRect.height + spacing: defaultFontHeight / 2 + Text { + id: messageText + wrapMode: Text.WordWrap + anchors.verticalCenter: parent.verticalCenter + } + InputField { + id: inputField + width: parent.width - x + anchors.verticalCenter: parent.verticalCenter + } + } + Row { + height: childrenRect.height + anchors.right: parent.right + spacing: defaultFontHeight + Button { + text: "OK" + imageSource: "qrc:///data/images/ok.svg" + enabled: inputField.text.length > 0 + onClicked: root.accepted() + } + Button { + text: "Cancel" + imageSource: "qrc:///data/images/cancel.svg" + onClicked: { + inputField.text = "" + root.rejected() + } + } + } + } +} diff --git a/resources.qrc b/resources.qrc index 131291a629..5fd4cea3bb 100644 --- a/resources.qrc +++ b/resources.qrc @@ -102,6 +102,7 @@ data/qml/tomahawkimports/PushButton.qml data/qml/tomahawkimports/CoverFlip.qml data/qml/tomahawkimports/BusyIndicator.qml + data/qml/tomahawkimports/InputBubble.qml data/qml/StationView.qml data/qml/stations/StationItem.qml data/qml/stations/StationCreatorPage1.qml From 0b4c3cde67510260e5c1847ddfe2c2dd702e56e0 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 18:13:52 +0200 Subject: [PATCH 558/565] Station is now a CoverFlow instead of a CoverFlip --- data/qml/stations/StationItem.qml | 41 +------------ data/qml/tomahawkimports/CoverFlow.qml | 84 ++++++++++++++++++++++++++ resources.qrc | 1 + 3 files changed, 87 insertions(+), 39 deletions(-) create mode 100644 data/qml/tomahawkimports/CoverFlow.qml diff --git a/data/qml/stations/StationItem.qml b/data/qml/stations/StationItem.qml index 77bec8c46d..16021e37bb 100644 --- a/data/qml/stations/StationItem.qml +++ b/data/qml/stations/StationItem.qml @@ -5,13 +5,10 @@ import "../tomahawkimports" Item { id: stationItem - CoverFlip { + CoverFlow { id: coverView - anchors.right: parent.right - anchors.top: parent.top - height: parent.height - width: parent.width interactive: false + anchors.fill: parent backgroundColor: scene.color @@ -25,40 +22,6 @@ Item { onItemClicked: { mainView.playItem(index) } - - states: [ - State { - name: "empty"; when: mainView.loading - PropertyChanges { - target: coverView - anchors.rightMargin: -coverView.width - anchors.topMargin: - coverView.height - scale: 0 - } - } - ] - transitions: [ - Transition { - from: "empty" - to: "*" - NumberAnimation { - properties: "anchors.topMargin,anchors.rightMargin,scale" - duration: 1000 - easing.type: Easing.OutQuad - } - } - - ] -// Behavior on anchors.topMargin { -// NumberAnimation { duration: 500 } -// } -// Behavior on anchors.rightMargin { -// NumberAnimation { duration: 500 } -// } -// Behavior on scale { -// NumberAnimation { duration: 500 } -// } - } BusyIndicator { id: busyIndicator diff --git a/data/qml/tomahawkimports/CoverFlow.qml b/data/qml/tomahawkimports/CoverFlow.qml new file mode 100644 index 0000000000..7f9fa00469 --- /dev/null +++ b/data/qml/tomahawkimports/CoverFlow.qml @@ -0,0 +1,84 @@ +import QtQuick 1.1 +import tomahawk 1.0 + +ListView { + id: coverView + + property color backgroundColor: "black" + property int coverSize: height / 2 + + // emitted when a cover is clicked + signal itemClicked(int index) + + // emitted when a cover is clicked + signal itemPlayPauseClicked(int index) + + preferredHighlightBegin: (width / 2) - (coverSize / 4) + preferredHighlightEnd: preferredHighlightBegin + coverSize / 2 + snapMode: ListView.SnapToItem + highlightRangeMode: ListView.StrictlyEnforceRange + highlightMoveDuration: 200 + + property bool itemHovered: false + orientation: ListView.Horizontal + + delegate: Item { + id: delegateItem + height: parent.height + width: coverView.coverSize / 2 + anchors.verticalCenter: ListView.view.verticalCenter + + property real distanceFromLeftEdge: -coverView.contentX + index*width + property real distanceFromRightEdge: coverView.contentX + coverView.width - (index+1)*width + property real distanceFromEdge: Math.max(distanceFromLeftEdge, distanceFromRightEdge) + + scale: 2 - (distanceFromEdge / (coverView.width)) + + property double itemBrightness: (1.3 - (distanceFromEdge / (coverView.width))) - ((coverView.itemHovered && !coverDelegate.containsMouse) ? .4 : 0) + property double itemOpacity: coverView.itemHovered && !coverDelegate.containsMouse ? 0.4 : 1 + property int _origZ + + z: -Math.abs(currentIndex - index) + + CoverImage { + id: coverDelegate + height: coverView.coverSize / 2 + width: parent.width + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + } + + showLabels: true + showMirror: false + artistName: model.artistName + trackName: model.trackName + artworkId: model.coverID + showPlayButton: true + currentlyPlaying: isPlaying + smooth: true + + itemBrightness: coverDelegate.containsMouse ? 1 : parent.itemBrightness * (coverView.itemHovered ? .5 : 1) + opacity: parent.itemOpacity + z: coverView.width - x + + onPlayClicked: { + console.log("***************") + coverView.itemPlayPauseClicked(index) + } + + onClicked: { + coverView.itemClicked(index) + } + + onContainsMouseChanged: { + if (containsMouse) { + delegateItem._origZ = delegateItem.z; + coverView.itemHovered = true + } else { + coverView.itemHovered = false + } + } + } + } +} diff --git a/resources.qrc b/resources.qrc index 5fd4cea3bb..9c29f523a5 100644 --- a/resources.qrc +++ b/resources.qrc @@ -101,6 +101,7 @@ data/qml/tomahawkimports/RoundedButton.qml data/qml/tomahawkimports/PushButton.qml data/qml/tomahawkimports/CoverFlip.qml + data/qml/tomahawkimports/CoverFlow.qml data/qml/tomahawkimports/BusyIndicator.qml data/qml/tomahawkimports/InputBubble.qml data/qml/StationView.qml From ec6ec7833ff7498b7803cb496b9affd19ef586b1 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 18:32:46 +0200 Subject: [PATCH 559/565] fix inputbubble --- data/qml/StationView.qml | 1 + data/qml/tomahawkimports/InputBubble.qml | 35 ++++++++++++++---------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index 01ae10474e..fca3a53ed8 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -148,6 +148,7 @@ Rectangle { anchors.topMargin: -defaultFontHeight / 2 z: 2 opacity: 0 + arrowPosition: 0.95 onAccepted: { mainView.title = inputBubble.inputText diff --git a/data/qml/tomahawkimports/InputBubble.qml b/data/qml/tomahawkimports/InputBubble.qml index efaa4d2496..4b68d3f34f 100644 --- a/data/qml/tomahawkimports/InputBubble.qml +++ b/data/qml/tomahawkimports/InputBubble.qml @@ -31,30 +31,35 @@ FocusScope { id: backgroundItem anchors.fill: parent opacity: 0.9 - Rectangle { - id: arrowRect - height: defaultFontHeight / 1.8 - width: height - rotation: 45 - color: "black" - anchors.top: backgroundItem.top - x: defaultFontHeight - arrowRect.width/2 + (root.width - defaultFontHeight*2) * arrowPosition - } + Rectangle { id: background anchors.fill: parent color: "white" border.color: "black" - border.width: defaultFontHeight / 8 + border.width: defaultFontHeight / 10 radius: defaultFontHeight / 2 anchors.topMargin: defaultFontHeight / 4 } - Rectangle { - height: defaultFontHeight / 2 + + Item { + clip: true + anchors.bottom: backgroundItem.top + anchors.bottomMargin: -background.border.width*3 + height: defaultFontHeight width: height - rotation: 45 - color: "white" - anchors.centerIn: arrowRect + x: defaultFontHeight - arrowRect.width/2 + (root.width - defaultFontHeight*2) * arrowPosition + Rectangle { + id: arrowRect + height: defaultFontHeight / 1.8 + width: height + rotation: 45 + color: "white" + anchors.centerIn: parent + anchors.verticalCenterOffset: parent.height/2 + border.color: "black" + border.width: defaultFontHeight / 10 + } } } From 9886a15760a4955466b876944534d29007357502 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 20:41:29 +0200 Subject: [PATCH 560/565] change artist selection to according to design mockup --- data/qml/stations/CreateByArtist.qml | 56 +++++++++++++------------ data/qml/tomahawkimports/ArtistView.qml | 35 +++++----------- 2 files changed, 41 insertions(+), 50 deletions(-) diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml index 60bb5b4b6a..897cfe6a29 100644 --- a/data/qml/stations/CreateByArtist.qml +++ b/data/qml/stations/CreateByArtist.qml @@ -15,15 +15,39 @@ Item { Column { id: upperColumn - anchors.horizontalCenter: parent.horizontalCenter - height: parent.height - width: defaultFontHeight * 30 - anchors.bottomMargin: defaultFontHeight + anchors.fill: parent + anchors.margins: defaultFontHeight spacing: defaultFontHeight HeaderLabel { id: headerText - text: "Enter or pick an artist" + text: "Pick one of your top artists," + } + + Item { + height: parent.height - headerText.height*2 - artistInputField.height - parent.spacing * 3 + width: parent.width + ArtistView { + id: artistView + height: parent.height + width: parent.width + model: artistChartsModel + clip: true + cellWidth: defaultFontHeight * 12 + cellHeight: defaultFontHeight * 12 + spacing: defaultFontHeight / 2 + + onItemClicked: { + createStation(artistChartsModel.itemFromIndex(index).artistName); + } + } + ScrollBar { + listView: artistView + } + } + + HeaderLabel { + text: "Or enter an artist name" } Row { @@ -40,30 +64,10 @@ Item { PushButton { id: createFromInputButton - text: "Go!" + text: "Create station" enabled: artistInputField.text.length > 2 onClicked: createStation(artistInputField.text) } } - - Item { - height: parent.height - headerText.height - artistInputField.height - parent.spacing * 3 - width: parent.width - ArtistView { - id: artistView - height: parent.height - width: parent.width - model: artistChartsModel - clip: true - delegateHeight: defaultFontHeight * 6 - - onItemClicked: { - createStation(artistChartsModel.itemFromIndex(index).artistName); - } - } - ScrollBar { - listView: artistView - } - } } } diff --git a/data/qml/tomahawkimports/ArtistView.qml b/data/qml/tomahawkimports/ArtistView.qml index a35c9b4284..6c0d764270 100644 --- a/data/qml/tomahawkimports/ArtistView.qml +++ b/data/qml/tomahawkimports/ArtistView.qml @@ -1,16 +1,14 @@ import QtQuick 1.1 import tomahawk 1.0 -ListView { +GridView { id: root - - property int delegateHeight: defaultFontHeight * 3 - signal itemClicked(int index) + property int spacing delegate: Item { - width: parent.width - height: root.delegateHeight + width: root.cellWidth - root.spacing / 2 + height: root.cellHeight - root.spacing / 2 Rectangle { id: background @@ -41,24 +39,13 @@ ListView { ] } - Row { - anchors.fill: parent - spacing: defaultFontHeight - - CoverImage { - id: coverImage - height: parent.height - width: height - showLabels: false - artworkId: model.coverID - } - Text { - text: model.artistName - color: "white" - anchors.verticalCenter: parent.verticalCenter - width: parent.width - coverImage.width - parent.spacing - elide: Text.ElideRight - } + CoverImage { + id: coverImage + height: parent.height + width: height + showLabels: true + artworkId: model.coverID + artistName: model.artistName } MouseArea { From 6198d559401a4b844fd899bf6e6685cee6079ef2 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 20:41:48 +0200 Subject: [PATCH 561/565] tweak input bubble a little --- data/qml/tomahawkimports/InputBubble.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data/qml/tomahawkimports/InputBubble.qml b/data/qml/tomahawkimports/InputBubble.qml index 4b68d3f34f..188de2245a 100644 --- a/data/qml/tomahawkimports/InputBubble.qml +++ b/data/qml/tomahawkimports/InputBubble.qml @@ -20,11 +20,16 @@ FocusScope { } } + Behavior on opacity { + NumberAnimation { duration: 200 } + } + MouseArea { anchors.fill: parent anchors.margins: -999999999 hoverEnabled: root.opacity > 0 enabled: root.opacity > 0 + onClicked: root.rejected(); } Item { From 745bbb8afbe6a492ba0fcd6d33b45dbd842e5cc9 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 20:44:31 +0200 Subject: [PATCH 562/565] don't show save button while on page 1 --- data/qml/StationView.qml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/data/qml/StationView.qml b/data/qml/StationView.qml index fca3a53ed8..4d772bae80 100644 --- a/data/qml/StationView.qml +++ b/data/qml/StationView.qml @@ -23,7 +23,7 @@ Rectangle { subtitle: generator.summary showSearchField: false showBackButton: stationListView.currentIndex > 0 - showNextButton: mainView.configured && stationListView.currentIndex > 0 + showNextButton: mainView.configured && stationListView.currentIndex == 2 nextButtonText: "Save" backButtonText: "Back" @@ -32,11 +32,13 @@ Rectangle { onBackPressed: { inputBubble.opacity = 0 stationListView.decrementCurrentIndex() - if(stationListView.currentIndex == 1) { - subtitle = modeModel.get(stationCreator.modeIndex).headerSubtitle + "..." - } - if(stationListView.currentIndex == 0) { + switch (stationListView.currentIndex) { + case 0: subtitle = "" + break; + case 1: + subtitle = modeModel.get(stationCreator.modeIndex).headerSubtitle + "..." + break; } } // In our case the next button is the save button From 439730ee8ab47555a626c7588e338f60e21954bc Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 21:38:44 +0200 Subject: [PATCH 563/565] changed startByGenre to according to the mockup --- data/qml/stations/CreateByArtist.qml | 3 ++- data/qml/stations/CreateByGenre.qml | 16 ++++++++++------ data/qml/tomahawkimports/TagCloud.qml | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/data/qml/stations/CreateByArtist.qml b/data/qml/stations/CreateByArtist.qml index 897cfe6a29..78bb338530 100644 --- a/data/qml/stations/CreateByArtist.qml +++ b/data/qml/stations/CreateByArtist.qml @@ -52,8 +52,9 @@ Item { Row { height: artistInputField.height - width: parent.width + width: Math.min(defaultFontHeight * 30, parent.width) spacing: defaultFontHeight * 0.5 + anchors.horizontalCenter: parent.horizontalCenter InputField { id: artistInputField diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml index 017c0cf020..87be16b161 100644 --- a/data/qml/stations/CreateByGenre.qml +++ b/data/qml/stations/CreateByGenre.qml @@ -45,13 +45,12 @@ Item { HeaderLabel { id: headerText - anchors.horizontalCenter: parent.horizontalCenter - text: "Enter or pick a genre" + text: "Enter a genre," } Row { - width: defaultFontHeight * 30 - height: genreInputField.height + width: Math.min(defaultFontHeight * 30, parent.width) + height: parent.height * 0.2 spacing: defaultFontHeight * 0.5 anchors.horizontalCenter: parent.horizontalCenter @@ -64,16 +63,21 @@ Item { PushButton { id: createFromInputButton - text: "Go!" + text: "Create station" height: genreInputField.height enabled: genreInputField.text.length > 2 onClicked: createStation(genreInputField.text) } } + HeaderLabel { + text: "Or, pick one of your most listened genres" + } + Item { - height: parent.height - headerText.height - genreInputField.height + height: parent.height - y width: parent.width + clip: true TagCloud { anchors.fill: parent anchors.margins: parent.width / 6 diff --git a/data/qml/tomahawkimports/TagCloud.qml b/data/qml/tomahawkimports/TagCloud.qml index ccdc82cd30..3ab6828aea 100644 --- a/data/qml/tomahawkimports/TagCloud.qml +++ b/data/qml/tomahawkimports/TagCloud.qml @@ -33,7 +33,7 @@ Item { color: "gray" //text: controlModel.controlAt( index ).summary text: modelData - font.pointSize: 16 + font.pixelSize: defaultFontHeight * 1.8 anchors.verticalCenter: parent.verticalCenter //anchors.verticalCenterOffset: tagCloud.randomNumber(0, 15) From 1131840a23d3c758166acaeb59d36354dbe86122 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 22:27:53 +0200 Subject: [PATCH 564/565] added a completion list to InputField, use it for all genres input field --- data/qml/stations/CreateByGenre.qml | 4 +- data/qml/tomahawkimports/InputField.qml | 70 ++++++++++++++++++- data/qml/tomahawkimports/ScrollBar.qml | 6 +- .../dynamic/widgets/DynamicQmlWidget.cpp | 3 + 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml index 87be16b161..2eb9d85df7 100644 --- a/data/qml/stations/CreateByGenre.qml +++ b/data/qml/stations/CreateByGenre.qml @@ -53,10 +53,12 @@ Item { height: parent.height * 0.2 spacing: defaultFontHeight * 0.5 anchors.horizontalCenter: parent.horizontalCenter + z: 2 InputField { id: genreInputField width: parent.width - createFromInputButton.width - parent.spacing + completionModel: allGenres onAccepted: createStation(text); } @@ -81,7 +83,7 @@ Item { TagCloud { anchors.fill: parent anchors.margins: parent.width / 6 - model: styleModel + model: allGenres onTagClicked: { root.createStation(tag); diff --git a/data/qml/tomahawkimports/InputField.qml b/data/qml/tomahawkimports/InputField.qml index 29159ad23b..1212f239ad 100644 --- a/data/qml/tomahawkimports/InputField.qml +++ b/data/qml/tomahawkimports/InputField.qml @@ -13,6 +13,7 @@ Rectangle { property bool showSearchIcon: false property string text: "" property string placeholderText: "" + property variant completionModel property int spacing: defaultFontHeight * 0.2 signal accepted( string text ) @@ -54,7 +55,15 @@ Rectangle { font.pointSize: defaultFontSize onAccepted: root.accepted( text ); - onTextChanged: root.text = text; + onTextChanged: { + root.text = text; + realCompletionListModel.clear(); + for (var i in completionModel) { + if (completionModel[i].indexOf(text) == 0) { + realCompletionListModel.append({modelData: completionModel[i]}) + } + } + } } Text { width: parent.width @@ -91,4 +100,63 @@ Rectangle { anchors.margins: root.radius * 0.1 clip: true } + + Rectangle { + anchors { + top: parent.bottom + left: parent.left + right: parent.right + } + height: Math.min(completionListView.count, 10) * completionListView.delegateHeight + color: "white" + ListView { + id: completionListView + anchors.fill: parent + anchors.rightMargin: scrollBar.width + scrollBar.margin + clip: true + model: ListModel { + id: realCompletionListModel + } + + property int delegateHeight: defaultFontHeight * 1.25 + delegate: Rectangle { + height: completionListView.delegateHeight + color: delegateMouseArea.containsMouse ? "lightblue" : "transparent" + width: parent.width + Text { + anchors { + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + margins: defaultFontHeight / 4 + } + text: modelData + } + MouseArea { + id: delegateMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + textInput.text = modelData + realCompletionListModel.clear(); + } + } + } + } + ScrollBar { + id: scrollBar + listView: completionListView + color: "black" + margin: 0 + } + } + MouseArea { + anchors.fill: parent + anchors.margins: -99999999 + z: -1 + enabled: completionListView.count > 0 + onClicked: { + realCompletionListModel.clear(); + } + } } diff --git a/data/qml/tomahawkimports/ScrollBar.qml b/data/qml/tomahawkimports/ScrollBar.qml index 351e378030..83867b53f9 100644 --- a/data/qml/tomahawkimports/ScrollBar.qml +++ b/data/qml/tomahawkimports/ScrollBar.qml @@ -11,6 +11,8 @@ Item { property int margin: defaultFontHeight * 0.25 + property color color: "white" + states: [ State { name: "hidden"; when: !listView.moving @@ -48,7 +50,7 @@ Item { id: background anchors.fill: parent radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1) - color: "white" + color: scrollBar.color opacity: 0.2 clip: true // Size the bar to the required size, depending upon the orientation. @@ -61,7 +63,7 @@ Item { width: orientation == Qt.Vertical ? (parent.width-2) : (pageSize * (scrollBar.width-2)) height: orientation == Qt.Vertical ? (pageSize * (scrollBar.height-2)) : (parent.height-2) radius: orientation == Qt.Vertical ? (width/2 - 1) : (height/2 - 1) - color: "white" + color: scrollBar.color opacity: 1 } } diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp index fc60eb447b..95fbca06a0 100644 --- a/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp +++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicQmlWidget.cpp @@ -15,6 +15,7 @@ #include "widgets/DeclarativeCoverArtProvider.h" #include "utils/TomahawkUtilsGui.h" #include "utils/Logger.h" +#include "utils/TomahawkCache.h" #include #include @@ -61,6 +62,8 @@ DynamicQmlWidget::DynamicQmlWidget( const dynplaylist_ptr& playlist, QWidget* pa } else { // TODO: only load if needed, i.e. the user clicks on start station by artist loadArtistCharts(); + + rootContext()->setContextProperty("allGenres", TomahawkUtils::Cache::instance()->getData( "EchonesGenerator", "genres")); } } From 23b477da19fad24ffccb715afe11dd79b7efc33c Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 4 Aug 2013 22:29:44 +0200 Subject: [PATCH 565/565] revert back to static model for genre tag cloud --- data/qml/stations/CreateByGenre.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/qml/stations/CreateByGenre.qml b/data/qml/stations/CreateByGenre.qml index 2eb9d85df7..2ba9ac8318 100644 --- a/data/qml/stations/CreateByGenre.qml +++ b/data/qml/stations/CreateByGenre.qml @@ -83,7 +83,7 @@ Item { TagCloud { anchors.fill: parent anchors.margins: parent.width / 6 - model: allGenres + model: styleModel onTagClicked: { root.createStation(tag);

    z*E!#0t3d~n}pn0!2qryC6U{5?{Cl$_qrE0G- zV@wgMqoAeqBw)Rzef!%_hqa3wt*eSa>0&u#^ann7>`FIP#Yfv?ei=3_!u)h%ntyrf zW!*O)*&{yvH~fs=n0&WHY1pU+Ia+jBy-2+!@?xi^W%FX`=#55?3w>G8n~5pyQE{`^ zw%kN^zPKf`$1bsMkwRZwdV*5uRJjH+;)RqndJEa0gM7KY!<4Bc?uSVCIs zYbx2Oe(f}68$rdN%Y{U5gPa_rSZ^F)*u`a5G*vi`i(Yv`NNIeHH>T@z7bAmnVIkl50Bl-b))|Y!}?jnZXmzvAVlQqV}0|q*nQNacs?^9~e_!L#=XE~j_kFpr|DnuaR=)~S zIUwdIP@H1}6q|c9Q)7;Ae>B_!YmHs4lA9Pd!wQ7mE`$c zR@%;*YlZ^-QoWaU5+bjzh~2Uf16)8-g9jyeCx9P>wqY6RXRnFq=Y2peWvAb(Oik>z z6SqdiF-hiI)Y-VxL>14fPCrdE9H*;3*L|&cM~EhkyyWiHmTt+Ims2SdzHc>zjtxE) z#NR(KOpo08M#V4fzsc`KA-cvbpm0^d_bnTTgF|9u^=h-hVY_z&k_dU8-t2Udj`TNB-5AVa58H^e*cfKchbIj$eZ_`e zk00d3{JOJ8qn)XBU*8kE$rVugY@>%-JlmYqRdc@f;{kTYQl{htNn`?Q<8&&mZEN~E?Ajw-U8Es>RK_kgs$cAeR=Xp%_6T=! z)2*Qs;`n#vzRS~`dk@Bi9e6U_86h;XbY}$233lHdWgE{#Qfp7ReB6))4l05B-A=oU z2>lNm??HHSB2VdiNE`v=gHaROrpKcbSy(#U8Lp%rlXcL=ZBGxx;)}VxiJPn))+{sc z*KZ-CjjNeWXG`yZO>t2k-=y3N4?;&X+dZ=~)3N;3Doc4k6_)|rt#w4Y;?|OJNUR24y>DjJh za7ZXGzQCcuZ-r5`N_a7IhrK`fydVk1-oyp+;Mi!}wD&ddK^?Zt`SPo@paQ6vm~i5F zdjfj$`Khg0Y4z(gqf0hhX05$^l>x_%K#Eb1z4yd$-Ihp!Hv)glE`XRh&LHq}w9ky1 z`uMPjz@G~zg@I_Mzj*2KPlwF$wj-QYcAa#1n)95wxOPf*aV<7rhCR@{fTtfgNXpLo zxN$H^6d)fi8A6Vd8jR}^)%=3!JMMmfkSB4vDEX!K4Km*;6VpmzoS?E)0HTi=VH z-ul#4kv<8dX7AawhAZ4=_z9H*^5$}7x#Ad2KC`1=(g<-YY#0g)Lb~qQ15&vH)9_R*ympxNM7ZtTRLS=Oly=wcy*5wQen} zvpu)e1Ly^!`O{F{Ql68e#1#7lp9A(=Kj9$sFc~bOff$Vw=Y^BUQbv4WE1<5*P5T3uyUR(Ka_Bhn2&iRqg z!RiCIoB0HXl?`Es;>h0ICbGgpR*FtID>s|fGl54-#W|mWRRLJ?$t~0JfakopBMnMw zMl+@Elfnx!daO!QA?|Qx7^RTI(tkVflBf8G3Ym!hub54YYO*3=@a(ySx?R@uMP$Sg z{pHeRY;~N7)jYyi*W|s+)PT3!!uP{2?}o*QnL$r-?EaxCH8oOi&mm22fyC!>*p$zQz)I*ukMA^=uj3oP}_^eq`<^kOJ3mV%N}D%|eq zefEYiD_W#>4jW#fju1{pXd|G7AOV6+-bQW!)q&>qY`3ud)`4+7NSf+5Qv3Q`PIJ4< zAAqR-Fr|N!x=nX@hK++4*mB%F0v={Uas0_aYotaBuhw-qw_1e zEa0B-3jGcfQ#>Fb(S_2Ka$>$#xCC^a&7$Kn(MP?YFzsjg=CsaZ#jE=dvrUs z{1|t^WaKM8q=UuZE!~OJ*5>^QR)7fH@TK3#%Hvyy*jtZ>@2C5;-A6DoI&|euX4&Il z5u=FjlRAY9RZBOEhOo?cHDhts$79VAH*?0Hg=fuu`l@@;_wT15xfFJtLu@B`(eJ|B zr)~tjRI4z>J*{o=P_uXF#ZJUh`ZCO0K4^v9>V>nt-uwfN3{slUIH zW_n~eg;8gizCPzc1V%llRE+XGQ0(;26%wE^0TGpkuN%u$YbQKU6dzT6wBl5`Vha9i zYgh(Nn|h~o+u0+p@)P=kBgv6uq@l~WPh{~xdI5A{SZy#gRKgT>dqK%BF2Y07L1O;N zUuT+Mo+k_|G2lI6$vooSe9Iccm8H>bUr{Sh744rU<5(SPd>UKle&03(@t6Ju z{vahd06*p`Jy%HlsqS#+qLMM|Qp?r)RbBs5CmN{2^^VOSmv0Qn0@JRNVF3jAmQM(p zSbmG0#XE}XOuF-9D4v>twtYmKJt{G7xDC^4zvHE5D{*=phP>^poI9Ua zm162XU#d`sC#IzvZf3|=j2h%7aDzAo=S~>6v2{Q{3j`FWNe5R)hFn+QN-l<@oGv}H zf^zHG`#dsY+z)oM1+?y(bvMW7%|`XR)Q7T%3E;#T&zU5;&a9d&R%3}d?bBQ?oEmky z4*4+FtkD6$F96OlRKF26M1JjBA<0yu6A#tt)e8ekqZ9lLyRse}67DL?F|l=crGn1- zh6VUJGIp_<(!!T~be4E^Tp*)DJi6gh6Mrr*lZ#WFMv+%em|Fvv!L#WmKJW2#w{<n64gW^xsC4NMz!&{_;Tga;=np|8!hkM7ZDHc;wAY9lqwd+B`@{*vu9g~lWw;4 zEbU}Hx4Ew-fB8nBNjTPD)uusTdD)%5`L_cicIhj>T z=d=qY?|$c=Gjm4G+_RFT+_!tLdz^$w+!2w1_0T|7U<)L%jhw@s*JrPYY^)-DUMV0c&< z{ciddv*bzZ84a1&Sm~74$lQ*wS7N{jWjdW`m*T5($hrFYyOV~<2F=YI;NDjYOYr3Z zPAZ4EF2y^T5e%RIm-d(%nVGegrFuYPwXDQ|FqC9rm*Tu-{PiUr8>zh4>Pg&zTG2mF`e|!6v4qcrddu-+Xgct*54}S9_pmtS&%eR z&Ho1EV1a;}taL&a={KnVI^)^ND)EG!^REz4Rg}nM26z%{OWf zwijKQX5nVJzY^{OZ|oN6fJ1LQ+xlLZhsWRT6q!MF68<56FZp{i9UE1{x~N}+I7LY_ z6KwuEw_&oewy3Knl-+d$KMKq6y?CS!Ieq|p)L(_M!N=01hG}0*FL3jZ#Q2Nc3nGqj z`bzgMlJa35L^*>Yzw>yFpB1nuVZqg;PR<*OPmwQqJDO3Wyo+@GI;9w}#Lv=yroQ&B zUm&%--Uzo8;2nJTrY>pPZMsTtK0@9Drr!J5k9I>QeS31T<_Aqmb>_0GJlqV&w<)D=Yx7W za!R`tDxxk(c;3IqI?Tf8#l<>n1P^|ItxH#-I$yqFcK-Gq^&x|z#&Ci>im7rHDY!UI zEyeumFMXspw#Sgyo2D+xg7S;}G`uXA}Z8)Qk~^B-8^(NMFu5pVLf~^|j|_bExoC zGqH%@Q<9JjgAXJytpo{05N(!w0%$P&%pObOsLFFbz+w6|L~WP#T_uF)#(s8%;KxFw zctg_=7Bbx1*O#C+AMWxptooDRf9MuK;|pAfxB{JVM;Qgdl_^e@EmE4s zqqo+n^M5~c%>$P2OaXJDomTR%TA3*)$xNmz)=3si;Q?0(Q4HU&|Ay=Zmq7Ub*etBt z?^wRWCI{R#+vYX?do)!+k$vRCO~(Go`uWp!Eqro@f4PB5CTg1Pik{B+CGHkt_(eH8 zpkl_F<#gs@VJTFA(MAqS`f0F4YsWYKJxZ?I4qFwO-fY*VUD(Hrb2~TYW`P8Qy@2B1UH@ zgvCFxH6t|p?Ra7E)WHqFu;sGn_@w&5n6hyJbSqo#uP^q;r8}-h9ya`M?E>?(Y`AZr z1bJ0f(4)z_@(Mu@z!Agj)FM*?ADZI}=COos3#MN2I8kA^DQW#3Tz>rYjaYZpC#fN_ z53JmZUzstz6EQxeO~2`C{!6g+R!{#8cLo)O*uxCXC)q>B!>BJ4mU-}t0*JHivY_48 zX3Hx0%;i+4j&U2ynm!K;RAasdYaM4e| zrXBF2%iM7FjE82-{1(Pd#?;$G-r6#(GuE{c0h5lGcw4Fs&o6ER40rJ z)Xb|&nZw!-kCX&NQ@?D)D_MZ2uM#%M-dWuA?2B9aZFi*_(D^Uu!r_N6yxpvV(F4j3 zU=}H3ados)!fmR|BoB$s#^tlrN2uGS{yj}8hrN@vpGazLyL3Ab_i5r=;wF`kG#Jm) zd$a>z?53DC1Q7()r((Z)INe;duWd&>CKzW7mtI=qV~(otT>nuM)nAvZywv>usBo)w z%2V^HecSP`sbdlQR9?^918>YOk6t!%n!#PG7~ExW82~$kA%$r9C@pF^E($R)z=@tU zbwc-M^PC6A_OcgsaHo8;yRt(aI^Us(@{Q7tJJe|nw-odFMghq;>L-Pag7s^Px-%O&H+KDQZu-Q@VY=gM z+JcLe58e`NDGe|EC=iy|fY&U})p+=oqmAM{M8)%x;mHf$-M*ARL+Rh9n%Bd&J1Y7d zcSH_4pA3azb~0;?XYtnhZb}|4FQ#{=CBgmki6Je%N$uHCs^1l|n53kC>wcp_r+noM z>%Lwm&lRZxzgYe7Z3D~H|A(F-5Mq;Er4tH3O4wz+5BTTTexj&1i)C=|cFNo2-%kBC zA~X^x=v`~93}#9PpFK_5SR2pPAh$LuLe|r*j7A`@rX;f|mzDW5qFje7!Vj0Ktd|UX z=H+XamaxCr%vFM;&Tg-jKgc~w_4NJ1znp?mifbWwu;_wIe0Xh@+O&&!eHc6eG`^pB zz)}KFa;r%1RUzFQ?SFUu{e@#VJ8mTQuetZO@4c970WnzV1$Jm5hUg5@oJyBy3j8_b z4G~?l7J#v^j&xd%k5d5$L9N*&YvDW1)}Y0AZ4D;Gr?+c#w*b9;-S;&Uea$)Qz>nh? zH?;(RyCV6R@c(9;&)Y-4zB4Xqb|q4fvllJih$vH#lrZxoh==zc`|rv7BP5+EsHOHfdm$@wl_Uj6@cG2{rj z=6VGwFZiSWg(v(2Ff0%GXPt8|tim_klK+;1=X1nkZmQ0wVVRV`TnAbO+bc?jCQ7{h zF4HR2Wd?c32r{y&(Ph_6q_lg-*#_$@YhAKn3)Ug~9RF|JVBYZIm06y)(~pkd=-{g( zyRLub;aZjKr^w={<#>;pd&DQnleFz_^66!gL)-go^E5Dz+HVRDJUAJfcP)=_&}BN< zk%|iMdDvi^6?S9K5km}@1Mh;u@<^Jmjbl3z5zyudpysW?Y)g!_8)(QNYUJ!{HnR& zQHv~i&&+9-`jD3CT=s_WGJd4=07bO(O(q`0BV<2*G~PO@hMu5Mu~$3r=VYpHY%wDZ z3H)hxSQ=xBdyP{HhE~tsnM~vPV$_r4MItZf?zFSJOQE~RWqXyB= zQiJXg15hy5F=HBO(Y!>(&DA!$*XF#@a&QBrQwv8}feph6gNg=(EppuUF;r-R977ZY z%$m=#F~C}am}Oi((7K$Rbg$;wUlgUm@W|$^`suo!jakXgLKoC{l~)M9dHF4?_}SUM zH<(XiW7DnzjqzvaGUb2Z)?@yRC#!EuH13$0H=L-&tX|?caJ0H1j%~~$^)3m#C@WijY6F+RB`It;fTM>!lPBV+Euo|Ema-`rm} zxh2?NNNa|NJv*{yv_B@r3>C8FbD+$&2-hLE5M%|KyjB@xb`F8NA`BX>P)j{cbBwDD z@9t9H-upKo2yLw=YQ9aTMA@9eSrs=^43!;ZjmV7`eU^S)6gwF;BCfVkbe1Kum;rL0 zy$3twJ`Jf;PEj1VgoCH{_DNm=;gB3WTugdP^QNrj3tnwN|6KEKS>|Z2>^Q>v;G%83!$y z*C^rFcXGq0zZsgKhX1YLZx!vot&)%jHtC=WJWyO2+H)-hms(S)npSOxOcBt<0%ec& zsi{z`vx^=UQ)j{?JQthA0MR`ke&m!avWvF|8MY9}{>{Qzx6R9thq_Ot0`qH_77tpB z3kmM}-otx$j8f=LhV@J1Q$Emyi3JCgtzE(lSc-+K4i43}`(9;Z4dL&l7pt>T=pL4O(m2b3hAnaJ^a`!r`@_U1Kk@xarC?$NMBBZ zp)w)KVZ@z-?((Xy)XsW6{oip&egOwfF`CcNY}$sW5LUO1;%&KOJfC^$A>y-KkY`1uwvIqVWl7&&9vb89Xk2IQeX9@zonKALGspfi=6{Rm{pA=Hir=rYGcqWDM$K~plLBlQ z?b=}}po!$yVA>QYI!(k#dGyG29;>AaHig01HGu@6C7H{vtG?5gufp1CCs}p)1N0<# zof8=wBx#(%uZ_sxJd2%wV<1)gOe+1#iQfSB=UYtX^tj!(=Y$mgELV9EYJ(;mer#-M_0wHF8)IyM9^skxmaOMR&!FYL9AG?mvm!RloZqjAXlD=^c@vS`b#=;MvS$>nTYC0(< z#hqHEBOv^dW2@SDSX^qRJHLv-iql1#Cu5T#N(W^k?~<}V$%&}U1UtKredOMUlkJaU z^fdDoqjJ}rb5h5A6bn|DMBcxw-Fp4R-S+Lq`NS39z6YYj@_kgz(s5>uqeZB28rWyA zX#h)UGj-nfouIEpwQdB%ki!Np4zo}|K-;)+alEeHb?0&CGtCK3zC`>dyYHkb51E!V z8R1uZsM`+>c5-0dH^z&dfBcW}%M$NfUD;R7dsRa-O!FI_0&C@45c58s+eDf4#91Q2 zqVw*dDgFWSU=;@8t_(i^UnuciKO$I$g9@Xw3$@*lspP)!!l7SE51|oYi`jvkLKxe6 zY_X-qL%b~;(>q^!6nprIJ;g}BSo$DBi{OGO;nX0Ta-?7ZRc>2)TQ`HAwsE^F`QR{F z2fx|R&Vw414oBrc(-yq^PDiZv1qRf*OEPagek(5X1Z%2lQSS)gvi?NRRs?ZBeV`Xr z*R*Ymk(Le6K16sF3-_Y4S6KM8 zO3Hg>_~=D?0XgHGtxi>_VY9OYG9@m(ZOjr?bon@n;ssz^UV|>e%iJJfQN9aWEq@(~ zb<&Q=Rt7f4k;EFbR9Y7-UbYx<WRveHL55s)c<`27p8X~5bl8uHZ4v?PjGyUmH+X9{3j^zwSH z(57;0KY4%ihS$TZcokyu2E(I-aXSe97lgOf%xji6rBn=!lmLk&(y&4r4~8yRZE<`d z6)6`gtihsT>&Vwc<#{HjlzWEJ6jQ*Lki%VNL3G1ZVgH}Aye&m**#??XhWktC#PwV=V<=+9%I`W-re-vN=FM@A_6_;u zp<7jQA8XbQIoRI(bliXipz}?6YS>e{IW=zWrk~kJ?~A}AFQS4yRNM2W~F`nvNa*m3cS z-5n*ZN4#cxiDV@jVkDy3MusW*(HyOR1RgpLQu6CMN=vKw*-W`KOM6R)xRF^PV>^$IH-jOhRVjkCtnvyO; zyfU@0W@CfSKI*h_xoK|GbH*htTIkWSzNb`o*8YszS&6CFwRI*)&R`6qgK|W*45|M( zpeau&s^e-A@N0GUfHQ5^eD5>np=uPIIUoYw#OU-A;vFmfN0JFvsoU^1FA4^vluvDY z0}%5a`p)t+5K&>@J!g1)DpqVpT&s+t>g8Ha!-7;e*Z3CyPD(SdlQdKyDAy6{6N8^qo2t zi0_wpUHeNBXwo|JW#5qscQ!@wLy|tQX<>5;KjV*Y%72Wj)8Ol9S0N;kTg}^x*@nZWdkbuHQQ7da5Nyy?*gVD@x-xw z1HBg1r!~tVN@5Qq#$+%E6Z~k@ejSZ*bqgN5(7pUFBbtWc;rme&VPc9~;?B6ybdf&9 zyDwo^@D=%MfWu9PYdBqV``)$l1(MrQD-p(Tei(V)wQ!e z1fmuq1%xvAoYRBSA~$L6ydKf72CK#45!p%lGl?HnpqF+(=mvf?S^MMGKAO@5SXoiV zr$=MOqcC5w`!{6~Zsqp6-4d&*_Vc{=T9@Wp=eISj@f8!fPY|>j77B25FhP<*pwywa zr?pzI^QhHL#MD205&vw3`#x>B4s0y?QUFR-wRlk!)ha#5xIal1P&p-l9`(X*t%!SM zxBu#v{=LN8KW@C3x;zci7J^hLd;m&Yx(zhQCt8k1ojdW?Sg&O%g@@Sg|2aHfA5>D> zlybs?EBlGJ(EFaPoZC;iFo9kP>qtKZVVY40c5Zh>dbdqlI4UX(vocDB?^o)G=@8dyWafEM*E zHxx@C!cQ#DIX@;r)JBKJJ4At-R(8|;dky|yb}C5GQ@O(u#Ix^yi}bUXmK%TEP#%M3 zjM`{71*w#uX#`94N!RK-)fwJRch}S^PLMHWH@Ev-ngN2>WI70(W0GeM?}iI#eihSamqOz&Ute&Y2RB&@_w3J zX=M5D8XV(Q7-ND&0v(wW*;bT#uYl_4&&03-nkr8c;X8f9D|;toc`&q7F>kAQ_bv9$ z7mmREZdkp($En6(`j>tw(XOW>hEX|2C8zTKmnIkplq@cgPte$5}fQ* zF$N7(@nbSwpf45~QSn)uKd&}(Mktc$m~v=ZC8jsk7)HaR_z?&8$i3{G%k)Q}v&d8Z zcgu`&a>KH%qXb*lQRcO#x!mJk*bzXgn;BYbssLb?j=wPxcH*hE4$4W)(BQrQz}*_v zO%cyFmE?lvwj{oI9HUldDO7|9;fr0QJ1!|l@N zNOU2A0gJACKI4$oa;0WlYb{Onz)4DHn_PLykKo|Js`iT=ZTh@o{)V)Rn%8re@6AdE zkP{c@{xmygjgDC7zp_Mi@Tnf|>t;;gmz83CH7^?1f*tp2Uz%f_{?a~~Sdm-*3a-5tr~G}6^<5~7x6Mq_-f0!$pLz8R~7 zV?aQkr}$GeYFf1n{X@+K5DTjXNoW!^`r|&VLoG&L-Zs({%GP-`?X=1g#1XOM$qtzo zW-8zlR2QV^oJgZi?oTZs&xY_EAX1j%|9NEoB-)7VD4s!%*vF>< z8ig+4&V`wQ%Jxbn39jcK)yKXDX_qOmk(xm-2Jh@cZ5Oj!Eg;{ylB0piS=zt~M9nTil&vfAsBZttoE z!}Ypm=)Xgr0`7&OdtV$>k#DD;P0qYQ_lwLZ?mcL%Y@zPK(T~k#D&e=n*wf5#5)R$sn1sQ;K0hI^k1<;vDJ>Ay(Z1O2!j|rJ{3e5^!gYS$(sp4 zjyra#A>Pm)Nt2H@gQJ{8!b2)S;SE!1vHf5|H(xh+qgQD~Es8P0_*&IJ$edjjr2%S=( zC?*Fb%qa?@!Al&M+{)MksYF$V8qTima-;I>MF%T&jJ3@(pQyjy`8>NAJfn>_Z*nI@ z#;e6C1Mkk*a%?l*x1WwBB04TcS3Cb;?vB}Xt=cSH#l?6!MXfpfwqiGC){yi{zps^= zQK=VSaF&1Sh0H4pzkWFhzNE5R`*b{#=am6V}d1mfrrk?w)+y#mYt) z+zRR3NP@w*`*Qmc0pCFVvQ7R>=TI_RL!E7%kESS$KK?cfQ24iJo#^ye(P_x7{wJB+ z&Rq|z{s()Uy)muRTyX()=Kp=Br2at^t=cz8$HPf4;@a0T(}fQ;LSvtleJdt+z^`vc2$Yx5dcT{c#p{T31mt2G8|unP5jtPny=rUwv1d(}c=LF~x{`666LQi3PcUW3+-obH5`9zDGLLG5qT#Y&YbzUvedA70W z2bFI5>z=PLjn9xIYRVgC8A{GSh-;rOsD_^ohXIhA*tC73V)oa5B%_S;Z?e_0dTY(w%_#0IT1c%zOq^; z!&0Mg6+U$9d1$A=Ko#qryA}-~==Bed3FL%CBElE)b`Y*yi_0Lw6K`e$vqnVCa4+`Y z1XQUf`{&i<$V(q!)1(i}d5>&7yJGbKlv<2B?;nBNwA=02(0h7OT%HQ}L=Ol+@T|6S zx8ua)>`we{*Q9*seR-Nf;Q5tccie?(ur^lNJtf{?E@edAb!Q#b+8u25wtUC zvu*OszGj9nDMg4I(Ki?@7p*)DknctR_E(DT=%5Rk(4K!GcTDBzmHXL-Em`R z>k$`lqmHDTux+@DuKk{JchZP%m+)Ep+~)Q&DUw zwEehiiPX{Jv`k)D+iV?^J8i(3U8DD*_-C)c(VL{&&`J-;+|)%!E}u@N^W7gO4^HVn zIlITm8UksJm56zu>ycEqL_U!k9U~p9s)IbflXUts`y+CD(jhsbAt%EZaMpTRFD_zq z^6tl~HQ^9||6S{4f&z3D(wzw<(P?VBUs5-6uHDA%1*^q_BZCb}lxHHT%l*`=$o52}BF|8T+m!FEoe$ z_Y8@#Nmj-Z^#JZ1YP<5EY6W)6D#5Vh_s5tFr|4rnk%m_e^=2M4S6(T7@ILio{kkP9Mz# ztB9IM4v{>7^^gW>`RBXE$*VhEMrSefU8`FXp2-bypWTEwh@r;WnfQ0VcNHE~7)$!I zn7|D=6?+YVl6PIFj<-GRzA)%&T~SYg!D#e`1I@8@DI+~B6z+oLHPoVPU z{2gtj_dq^Pe-ib}8<_P-06*wIER0MjjkXef{oh|!qgRdyo34UPUdWF5gahN!hu3 ztED$jSybEz5jyTjDO>$6+RzWDsJ9*=<=S1iC*6F$;1a4D+nRtvXo&$0OMM3QVXAvg z>_^dz-yAz36I9N*BiQVAh2vBL2KVKKXKnx0)bSwSzWpQYm8-p0gtJQ7!&sq<3Ng_!qV?~^F#Vu0=+mf7CbiM2F+qsy7Fd~ep zDPdEOg<{iRJiIZ{K1QkY#H3b7F%enl-kyJ><_)|fHqq@$Xw8?=&S zgd&JUjpR7Q9azv@`*LZrJ(LcJ5DclH)1h^PAY)W-`;?o0kIwsmt&`)zhmHWVb2#xg z=61FF3@AWv_M&0TM)_buKQX{bJHt1QZWQlOHE*O#RE-i-pA zt)6Lja1Iy9CrGbx;(?LZ_}5Q%IvtHQ^w&i<4h*ek0T_o^$nGmF?T z15o_^+WkbE`~D&xrh5NvVbu!L_s$0>qfb}7oFZ%)(q`36Fnh~@k*bu5v4|xoh>$1U zvIUL!8&kK)G3XZtfYYGo_7rLhJ${hr$9SjFyF!TjIMU$&M(Y+y5^f@xX_Oj$K>IMX zgI7NcWsu16gl}rQg{nJ&PK1;;G9e~gP?8k<)smGmt1OHpdqC~u)978{(w*9LdG-qw z_9);2yPh`7!>Y)6CZbRgqYRJbTWjXDM$GBY%*x!h@3bQ>Rw{Mu!b-F&k*gXqL!$#h zKot}t?e#SCRqe`8gG)ayWCo=%T*jMn2pyGhkwPXj(HYT&xGj79IaB>X360N5(SFWr zZQOa>^yl|?I2Sx1KUip|aYp?46cR_}mq`=j;&t@$J?iN3&9LWXr?>*m{QL~F-yh2; zClFV~YHG6s8o9XjqY_=z)tTRN*OH~S1P&Fj{NBGNZ%@Yts@Oq3LXv}`yVlHn@?;@W zP1#!b3JdIjprji<^UxFth{mCrbgy`H%s86|{GFuiC+qT5G^t7eY@lIisev~qq(<`% zVJhDoW^^%6fK5uDG{yvb@`+TV1Dzb+2oD1!ay3vz+~pwmYUmLW?!YRH(X@b|Av<`| zC)giPIvW`tk43Ao8s$rusfg%}f2oz+m#unw2s@q_`T)>zF2U>+IgR` zXyuLj=uU6M9pe@3Anyr%iUD@4;?RDB;xB*p2rwp8YyAk;b{UsH*rwBQO`|YOHVQ2l zy(8_}izOi*V@J{_GBQlp`}B9Qchcw{5z@t3ze1Sl@z_q7s3|o)PrZ+VGp8TMr9cDK zg?^r|x->yz>QoyMhCG&OVf!ef)?A};le}+vmU!^93gAKz$QI4UCT9ogh~E!mO0EKd zZ<9~n&23AMe;%V%_NJ)8s6l$V46pVWud(e_sb=spDpW%V{G;bMa1K2hlSaS&0uejD zzE6=ehNFN536nTtlx%yX|rByjW|^&`^akk;n9iJ)8J4=aLQQ zA(_I5(S93HL<#L1;Pl7rxtTDIbfCV%%#e0OdV76YF{udB<}oVPeAALz_yOVHhkire zpIpn~CAn{G3qy?BPi{x1z^xF}HBV3!*E^E~<`Sy}dFhm*0=lqWIICWn_amwuyt4sl zx7>;pCg)YIz6A8Mxd?m7WyRACq#->n*^g(}(BOQY@fl$Fp0O5Kob6C83|a-*D?b^+r= zJo!Q9|U#^10MlHr5rsuO2GuEgCmJ?l1;Uxyy!RyazBA9RG<{n z?sQ7{O>j7GUqWjBE=^S8ezsL&*rq6Q#UwC$(y&owc>GQ`h~b;{%p>lhT09>GRW z$ISk7b@7zULNX*}FsOo`8z(S}D^E>8>bce@&oCCGIfpBXD8&`khYg2FC&`66fvM5{ zg8VS$)qT1x07=fHqfJFbNK~rPG5))W$^U;7%MvK0L^K$(IVW1+R#2Ux1CL;@S7z)P zak!GMNnsE?8GJ+k5ebhKN{3z~o-`b$8MDZ!&j#+oij|T!;zL_;#Q;39e#;NeDrMCo zPWw--$R%L%3UF}e#aHMC`BBW+O;1reo_u_wT9I=N1#p+bxSu6nuz)|L06BGgqC>@3 z(fb%E7~S~g@$gFJ4`k76<8EoW?91EsnO6$cB+1(8b-}7>Qbu1;wxPrXa0BhI4ifkE z#ECF}IVeR^2)jEVVdoWL%q=BX@wPvcm@Ct7NHM#KM}Z^41L;7<1uYeiqfF&;O814U z;Az={P3!2_FIXx1?|$dQzl%yb2A9e4RPtzZb8OLY zB>H%JURYfA1^^)$+;GuxD5XPC7RnhU7Pnw;nqK1-)QS@HDl8=;*vKcP?Mo4C-3(?p zva(H#GxbSCJ{+=5sBd#{Vor^s;x>G49QJS|Uhvl-n@Y(-wJwtcOEm?=^r;8$tAz;W zhHY}V(lwS1%LU4(dVQ|!+zMr{zlHO#7rIlZ35$IXbe~bEU%x(E+S zZ&7*Ix#xSXI#Cl*JJ93=-VF#H-}nzK6W2aw3G4$&6FUdso)w zpPnuA%YXBFVlE5h#yZ_$9KNe0@C(qQ4~z^lWaClPd$}v$w%svwlD4;q%TW$*8+T}w zRW)oco0i9M_E~4jeSdQD+tH(nxzwzAE^1YR*;eeyYJ_WOhit4@)|7jc4$cafS@Vm_ z#k|d**e$R&~_h^{18AKr~NsOT?c#42S3D`*P!z^YA4qUA5dG-0(XFXN8F(W0*ya zD(mB#FY?I@EW@g3Lfq6obK2hf1W;W=*D)>LU!wAFD(f=2rRs(5;ZM_c3Gqh^QAGO~{slW!F zu1g|BoXw0#r{zbhWgPzDG$CRVSJDI5p5YVE!yn^ZfrOi5C*>Ba%|VB}SX%AnRbYX3 zelJHtEsa8iq*uPpJasj}A%~yOS?l|JDvE*x@dUQpYKB<{Am)TDO$&1eIyp;gEr8B4 zZJ@~8B1{Tb>pgkCR-&qBvKcmFB<>|&l0U}=G^P;pEmH#HK1>!{9hXLg`O&wK(Mj@e z5wZ9m)|1Yo=_Xi>Ob6+*dC)5tqsWBLar4(BE<-fKNdU;)7?L$kNx==v1)Z3TqR?I~~#TCZo6a5AH` zyu7=Pa16Bg$z#a`E`2^ETL#qk(xMPonrWoYy}I$T;!osO#hA(M1!n(6NOOz+5Blmc zFB`6f=A6nmY+j#spd{zQ?(^4+v-FGMeXL}(sxEOGVZvO>GvaaImiBm`E%eb)E z$*Z%W?^ALfCn^;=TkJm`zfMg}KZQP-2W2mv@IPv-7JS`k0hJ4JG6lC{G!UJ*e?s#p z6ha1-OMg$IGD*yjp-z?5pi|+Fxibm7__~08yGXt`zWnLsE1r}*f;5T+oY`A{U)GH` z`7&_CmOdX?ASomP$OVssg}p?X;t_Ncq1*_#1WF&!mp5Sohr*yv~tursJ83Z7$eZH}Jw3_`~s}LTD)M?&z6L_M{!;?m&RklGi%zOawt~ zfoz_`iz$Daq|HdOn?0jLzGUr0X`y7{MhS*Z`P34rAAybo-5miVS*CZA%NoWY z+;GG9X-|X4xkdM`ID1c=gN}X#y_3*KrJWs{>7IBQnyMXwc>Jf=gV;|QdSm*)#>0z&JqM%;oR@SU zEoz>=xdcx;g{3XRFX7ketxubK#yI3f89V4T>iE~ri2;gp6(dg!#DZpfWK|&dI(J~ zyn?|yI>azC>S2=Y6<`;oZqSjJ^&%<41lukB^fF0Fw^iBS0`lgg z4^4&>`l z&#l>#7ZW`V?LBGm1yG(5y4mtnyKdvMpC%lJ3{h*gRpJO@$bw@Vxo?*`;8>c(`0ra6 zD@qZS)Qg5`YoskXS{XqP+6jrXb2GB63AF?ZAwd;;*VmrLd)8(5?gyI#e<^+ zM=;+t82T!9%m5zB$^5;- zR}ig#h)6#g&qEs-t%wxou?arK`E#IA_(bw(vi|h0?VvA})@&Ea(p>VJj4dhxZ8>GO zB^+5aE9pHKLdnHF2QnS5P`o3((%Sy2K)3zN@2qNZ{19VkXN$YHH$uL^A@)5Vr7H{U zDX;O+I|o}a#PtYUvQ){WW(lpt71Q{7#Fot zfyF1eWszV1G;k_lnDLFy7b@C;MZUf?o*_LA3pIW~>~?OipKHzyj=UHLNuVute}wAtU~XHTH|i74mLWWcRcRz5&w3L zA5F{X|FQzw`z69!wco0BHo*i!#oh_my9Xeq3;)~u?e4e4LzT~28hNK z27#supV{qA0E}yM`*&wP7dLz3yx-NeHo{i%LR4mdR zyP3tjEdUXycxk>{d!RCS)a9Ms!EGA03pfdfq$!%O?*Cc@yEt|D5h}N}jW)i5YM9uT z$B)000k#k6T3!beqY{--I%+LRvKzH>gXUNES`sPWqpV`ya;y*y39BBgP_X%tK!0v^ zimx2jFANRezu&ks%}P~5+st#xBD2IzCUpLqEQB*CgozZ7DMaB?5cXz3S9IIpYRL<5 z-Y?LDh7*G?3R`sw^YEwVzZ5BO8TIN5*|?(8RnJAD#5m}t0va22t%W;g+933%Fn#ZH zY>OE}BrPf@IlXK4k(D)5vHiI752jrmsZ6^Wog1{$guVOIkycDrN}RUD;2A#2TQsiD z(JL$5waaeX?7L~Ya!`j_gy7veCz!dUWp`j1Nf^hR!rcB-6*uRSHD!11F=FbnqDg2e zC*q%)@I_6u+I%q%fBPcc7;8K|emtX@WMk?!7w$Ob=v?&;M9oD@3IKImi7y99kt z?Vx!mdLPd@{p=xkqY39TM}p245kHpZ2kbSSySHk&HR0HdyPDGNLDL90#b}EBDum{cKD6Gs{`eA)W1IPEs?Gz|kh{OGv-~$C&YUf1@xd|YNbOMKO<0Lnprz1Y=zh<`7J+f7*~f zo269I1>$GHY*|?=sv92Vl>-V1$l{})_>H{(Rk2nUx67#E!n?FdR@-&jkEz)KKayR5VE%|^Oei!`SzrrB1JowdMe2b`AOHP*S`?5yW6C<<#((o?Lw zK~GB+x{&R{kN$HH{DYyix2G{FIh!>8-S76~PHzhl4KGw#FHwDzuSdiDYaR}U(UB$0VM?BoIbm2O8zIaC}QzE{7yF>)l{kA73 z!)&Ka{t+e}$Sq2I{I^11Hmf;8!ls$z?vl&@;JxyN>|S3oba$qC3z5d?!aaKtB;A6a z9k@6VM$H-x)Mf)CVt0Q-;snD^xn&d%UC#o5dD^a73d_ea?+(DSJYV` zVnnua-a!}ySUJwFEKJFhtbI=ku8jH;L_FAxA-=QQcXuG25%8LX*B|dT$uwZW(StJ6 zer$93Gp=T(IDoqWRS)-YPbU-Xt)TQ*z_d-m`QJI_;2jGWL-U;429E^L`7WISQlL|3 zfP_h|XyR-Gs}p(|Q*PWA4BDuPFYDOXqRRU|Ox_s(!F9e0xlBb?f2A&9KQ+GLPdI-N zbK#kGUt`=HL2iv-TuQ>w12nZ1JH41830L@uynRx6AFoz40zArcClBkkZ}c%@(qDo) zY18~^30Is%<)gEiUT&T3w z5$_CbAaRpQw#df(#E3>jVwjhUaESi57?obq!D>_e5Uc@3_r!wtpe*WC2btGAZ6@sn zJy$@>e~u}-2lGVJB~On3Nkn08oZ+XA&CBKYp%x!@k_L*BO#Ru8h{$bslc9d4Q?opw zL19|w#aPj8d@NHe=sb?O86c0kcs}`7BqYxQ_Xnz);z{E=GXi@KEyBHG<9(BZaW2%F z_M6ChrTu(mVFhY)t}p)UXxMjY)4S0?2Y;qNiN^1`kS#n`C=fWd38rb?(0Vu5y}b{HF6J;kg^wRzgE%N$BZJalWJ0!KzJD!Q%h45GtL9$?;{x^~-I6y^Ggf=aA1j zD?)B)rs)wqqq20CMFqk3de5RMWX6^ueCf4W`ds86Z$z^(39O1ZbIh6{JxAJ(fXD1N zC&D^K8~O~!FT1lkoSp_~u}*pdnPMh4}{DXq&pJ#OS|h}~}8xVc=GY93I?$OJ0Q81=QtAQsBU zVftVx%I8uL#@^=s@Vj zder1Z-&7YKq@6yKmW*7@LIwHsF~P8oFITa;O@xsHnI8PYh=Sy@A z=HXD!$vOyM{B^jMt2JC!0A5%ojcMNGxh-TuVZk~(273&iIhu|O9SQT2u4m7cbGX&$ zF$Qy)XiD_2j4lq<{@bm&-l|cRUhA7{0Ux(Hqb2DXpFKvhgVF&z1hoSP7Z4;yKeTYN zK}*_cx^H3i^C#v4d}FbhMr#+Zo%nAY1FTvvfMGq_$#}lO5x?YK@Yxr6)3fstZ?m5I z3JBE#HkeD!+eTaMUZ;=}j~Iy^L&00*kC9`Oa)D(h1^rx2uT#aj+n<15u{iKgQ49AG z^$>^u$R`#C!dya4LYII@=NF%^fD17XQ$B7{?6of(czx#$U+5ex-+t;yio8AYs)YzJ zkCrRt7gHN=+s&8X&LOHd&3qIft{!)G;?(2UD?Kjzn4hAFsg2g3@@H`Cg-1Rr3(g@@F0ob;YG%_oln3H5B;Ivn%hHrmCwVWD7B zwB5+n+R>m3M2W;1&@+0lvcUXo17U3KfH362^w>7=Ej|s?Va-Or z>3_k@A&)Af_WK4`UmUaUo7%qb^Yq?qy@l<3^Xem=2!|cDdK-zOI4gL>%TVEzYpnx} zK?UMJ-EvxL{1tHi)Rg~0&B1EE%|j8&z4f_gdf{I7;UzXHTB!#0)uxGTGNa9W z1g?k@i8gGL7pGwGB5+gTQlRk32|m~ezqn+kmGBB*b5J$IC=|wfw}1Xt`B#w=-`+`e zsrY^F%1~sA(`3x$%-NmAWE4VjJ3cMn%NKz1h<^p4rV#Xcv@y*xqz&JhA*T`+;1Nx*tMz7(nL10pQ%#kUF96C~(^0i}5Ja#Zfg$xZcNS2T$Jc zcUNRQl7$&6rZhV@evz?K56|2r%d}1Ih5x2nA5+dq0iC@X{9yl)JmW|d+n4wIk9B?l zy%H=kE+!)hn&(3|4T8r7kH!0z-yA)!WBVlD_|HCk>dI9YbUQnJj-%3v0k4c07V17L zmLAwLDDu+^%i?+9bv@&5`?v84{1aNp<^ zhTsPO0;t-f^GZKFWAK-*9rk_aFHp5SF;T=i!d)r?Rbv!#%M2@OM+aOTbPuK(CPfs7 zhMGl@dw{K<;;!(L@6?A}P;I&&I`(V)z5Y`gLQFAlAX24W^;MUiuRMPZFXIPl2aU(X zb|nUH$&TvQ$oBA+@KRG~|pd?f_v@9R#MYFZI=Y3q~V++6dh} z(32be)*aD@r-%ZFmV2C_CMV9+<)?L@gwLq0Ub_x^HDAKaVbNO zL=6Qy(KP&C0K)=e=cpBm@^44oZQcJK?nH}!jhzA!#_?FhC+{M4j(Vo?a87icp@8K{HF60-^>W9f%7b1v1=sK zuZd^mp$@xIcizx~DTSAzAO%f32MKREK4jKNuI3*^SKEq zR^7TszjP03;;q7s+C~~q6R*fxHO|f7%sTHwCM%&Z*rebMup#<2`gdo2cTtr?n#4>+ zT-zxwW1iK8uYh1hGR+dCIVlnKn&n^WRkcFM9eZjDdQh5NjU5Wf|8U1_)(8Rjx?k6) zd=J!q82%Dh`wHco$qn13h@Ayrt*2@uu5^3N>toPBbP*tHQqJhs$^k*yS)xm4Mm=x3 zlP7&6Rz_N)SiwF`i*gs@I8R^uydeJ5-DQ5+rjNt(LA7P~d^B%ZLpEHdDl*-#)*Zer zyR2kpP4}pOyus-cc|ynNoJ;_gt0m!(z@MH>KXi z9?YEBk(@)-odDr;!E&xh|*~cN{+Bo{NK8FpRgog84GA`(T^J%kk@p z=*O7cTky;T4+k)48_R8nDhMu&jOUvUs^^yX9iAe%<{k|CJ|!wJW?v8moBRngr+H04 z3VV;VaJm2) zY#XrMm5cRDKdP<&srapc~7TdyJ_8_`!gv;PeM9+x&A4GbUT!xluLobv2{2nX^02K?k1W`#RHk7OLM3=Gb2{8qQzuOCHos&M1Jh$W_+h!8e%|Kl9u+l>{Z$=C>RPb=RsJ(R zQ%PoI*FZ(Mvk==4xk)Y%RhKhLq>i22seMl8N2|4fCriAKbCis{iWq(N2E6U(reAQY z#wIJT=<_4TB$G5-tjv*oDUP=Biy{fSm~Gi}hwbrCqE7NK9%I3jE$Nc2i=CIKl-NeE z^i?LgudNc^*L=yv09{AKC-jFVEbOV^C!z+R3~zN*I7P??M*=C1r1ahp-qL{V-S4H{ zlg+}1h1~+-kREY$ax0wG1oi|Q`yV&{ve?y-CtMvvHrX*kfn9bn@I?quRNcrKO?&EF_YXDZd4j7yV zvmePUEqg~zdq*p}QR=HJje@k@NdKErEgCa@j`bco*h)HuA)xTFg>F)*tr%#gM8(i->h(YL z;a7a>SXE#F=MJ$!K#6q#zD(OQ%!kGizXoirQbQG@ST^JuEk=lM+t6YKf=E^l!RPOg zNB+i`Ru8-FQD8N2`IvBH+9urF)JLyc+$R)1Dr+sF!$1|bv$!@;BxZn zyT=avw>zozl&(tEeQWHp)A zh*LK*8xo%aW&;PoYtiP!WVxyi4ihrTp82I#*3+FV_Y=Q3Npv?k`}|Y-hsaiLNVqUV z-ttCS{B2Dm|1c%-t6r5GM~(A;ZB=sje@13L8XrP^K{?w1r+fKdtr8zsA{PcM-#LHD zt7iwsnV(f-Mw$1jnpz4zwo_bMWd>bTXyiY^B~lT*jd2%d9+dX``27!o?$i8VpV0Fu z=O1Nl0fvUe?(Jyb-Xk)ie&!P3wM=fFBre=9Y*|0I zKoV>@e;~EY2g=nnnl`L1iYT^esh%C?c}gJmTn?m0f6+BAHb9VlpHkq|+b4c89OD}yP;BV`>7OQxZ^ zoWUL*)7hJq5|oPH`DiEj3p*5cU_V}b{MV2nZCiFcPgd{e3jk2O#9c6Wvcqgqt)(O_ zuo+e&Wj1tthnD(O)rx9EDV`}XRft;Fi*qGQLT+j#-+cZqp?g zM^1x0#z~BCZl>T@yJDR!08J;R8iNsurOuG|A%@&mx9O$06E|YFuV;Rl@+k_?qj@L9 z5y}hSNMWIB&;puAuDjTGjM!B~>U#l_WARTO2;E~iRSzEcn`hXIpBF|5@RVLm*`@s% z=yAEw+|jJ%VkY&BlaEKGMqM5UYWm%4e1)jL&he3L=@WNwu;A9QF#6#-F~j7Z7NV-Z zF2gF;HV)_p`zDIUm-YN%dqk2A&W4NLbUa0dYXqAZY=D{j`(bWZ1&yu2-N+c8I?&&s zgo)ajbOAAPaTRWWa&nmGlHA^~Vl1L!GE5BYkawl^n-6_n;P{1&=d9&!H|6YgdD40N z-R$Gh{QTBEo6ssD7XD84{PJ~Ez6^Nn0Dff8BCe9(+yK(YdR8jO3wDZDvrZ$a*iECG z**E2;9pg^LbaY$$x?d{zaH+8+Qy5^w(e@?KC6tt2@Jd@&xd7HkI7M3j?I&2XN~{oP zk+HGgh$gCVlyKjscPE~g6Sh?Y=Ojy#Q zo1+Q|rgU`gaHf||tez-^6*J7vDHxlr-ooCBe*N#ZOnh1Sk2kVGDFMQ7vBW80GLU!r z`S>?aOR19Ws_$}lth4@wTsK)bsbS!W`zbk`)3QlU4#2KS&kl{Td3jW@0I!L^C0*>x z7e*zXANZVVez9!o7=F)t!Xx#$@h$l{XMrSMd17a7$tf*>ASDuN%Nd=C_J;?W?wh!* z;`7Ex`dsnqnb%EOKi01nb1ki?PcI!kkoG$&gwMg1fZM`tuPIJrx64I0d8E+h9_^2o zr37fwyvdUjPaMjctc0;j|Eo$rY3cQocRpP$|94GE8;To)D+z;TDYR$Wo`c(wy{k-0 z0CfMk=G~eWs{<-I0)@KyMkIs&Z|kCPMTNoOL)&y`w6We}oa6af!qxdEl?**?FhJt1 z?@lV!Sh}dp`RARQ8Pc2P7i%Zy(c4jX$^-#{vR|MNo2qr_8B@_iQ{n4`|Cd_K$v2*xKUbzAXx^E3-P5tO;#k-5CDPbf(lXG(s|RicZR+7rcS6tS zTS;-pGb+rZX`er?z4*dX83O@y)k!e4DGLa+{R=jvb4ae~qM5N$`mgPdg!+)FXK~2> zOUG&;a%8Y-L&+;Om2?(uBpi<^#{5!dv7LS21DBKxsZYUO#m}H&Ym0%|7|SBvuyfz} z*&YxZfYT_1Lj<|Mo@Z7IfYP%p;Ynt1{phtS?Q%PokS%aq?)-JduJ%K+zceWMC^Kjk ztEDvNO~o^XC%Ge4RLiT5tTWbT@53m|D$oiV^(Uw2F#$Hy&EFGYm^(vr4uQzK+s8gj z{*nC_1QmAq2yrAYR3QM<@y^OP{v z?oC|Yj&uh{+@UY0|6(`hr%cy2Plu_2s+@uUD7Na@8@v=W+avC8q^2~_4Nk) zz*Ty=^HVHdP}4-XmR7_j(OrcBsFSK^meZ7TkdL?Kw2k zH&Xk+uq5JlAX3NKnyUG_Ei+gA;^!ScEVZaAtN6tzHqV4dZ?I%i{GxFq`k{wMGjO z_m?l22Eng6clPs-oAjl-NWQ5whkDaie8x<3PUyEis2Z|J7MQ)fXl|B~QfY{${zD8s zL^P0vlb&6onQvMj8I`n+KOotmlfZHyXAl1pN8Dw2zpM&?4h5M%RQJy31twO%JR$pU zU1Dph(~|BEMucgUd281(4o&g&^CYORkHg&wb(zaGjig3E69gZZrLoB|<%^;$^L3$} ztlpHrR8<@i@*(%u&<_3SMs5kjb&jGLb|*ddJtbgM0ZN}IL>xg;eRm+v$~@@*se460Roo%_H% z8^>L~xW$DZKpNITJM4sZThq*}XZTJ*o_BW;cQ=^J`rFnw44Q}oL&$vF_P26;xYICi zlDNC=fAx=bgybKsW+g4v-WRep%O3yDF0ghd?{jan5w2%!$+_!Dn-;=2!sDYD-X>I`IYgE=*#CVpeTKc(;*DjogLf!{ zKNFDsa01`r+F`7l+FB8;kla3@AC+l)1d#_}=d^{1oIg1d4BM6nz2-eHeek^>(6{!O zDvL-@C7teZe80#Bb!7fk?nKt$*|39U-j;->$_nLQDZirLKG!=D;MNBuM@r%}OUP6N zJMV$zdXs4L{x1uFllJK#2~0uV{P~yh?}+d?W}Q*9QQB0hUEEVrL|$I5(crbs)!LU2 z{wJL#f&<)R82Cn$)<^1p6XqZN|Fz&*kwV+_Ki~K^nV^n;dBQOCcqEbtx)|xX1e$VN zWoM9C}2#bWMCf^mfBY*uc=h*Q*X< zIP7AoAYPwn$}mi45xR>cl3l<%tPZH;`s=V1k~m<5{Yzz{|M zC@l9n%}a*O8|)sch@H*-hQ)v72Pha6cyq$(yV@g)7V_rrq(K>`4j!yGt#@&e)H_Pf z(PxyBxlO&XrFAUDXS>kQ)bMnut#8Is zsW7%(f8p@7#?b!9)CmNkWDOU0lFq>1uIwgWoP4$K?N-A%oBwuTQKQD4!CH%~1Z9bs z?(mni?vw~|Q|umImuUH3DEef%aJFDS4Za|Y&XV^DKd=7ss2s+gqAs$=KU3!CG?Cr- zxqQF*zubW=XR7wZT!L|+Twx-9nedFc+GO}nc1}-S+OCf=BOhMaHOy@%-GI*(VkB7e zg(f^4fS_SE=Yj!^@FCdh$=6yi-?g7R_8(2KBrZEWK;Py8g%dZQWz-%MnOI6r24+XR zN~~X@K7whWU!Q5)`RcM78ZQoz8W+ zCZ(SmG0AuF?;Bl><|U6Y+gS{(8fGV`-KJ!h@J6@pri zgV>&%_YN0v7|Z~zhM8tNX;b)YyDQPW@52^l12>7yXUKs?BZ=7Aea5v(#$pb~Q?i{6 zzbZi&??!e?GdXNLe{SR^h-7VacAT%YaQ{goRWjcGVLk#GXLWiS6nB`yyR+_mDf5#D zuW;himfMGcg-F9Myth@ZZYPrF=sA$0KXhfY#X*;tOJP5X>LGzRERGKs_miIMJXf(y_Y@N_ zCOX?N+DqlIKC)B_#@wWz2mgG(v$iB4+#**{bA2`eM{|&nuI(uhBba*dwcSyx*T%kZ zqxrb5RI4R_^*ZvCKUFNnwLr=)2e`fbOBhLDUG!B3KMU~jEFm=mNra4x2nPldebDDC zlYyY;WzJE+BY~mhvQ6@658=2pZU`=`59~Mx`9s75c_{na(eBdB7CLGRf&E&0CXq zfX!jgcxFu+-wTVu?dLLZ2N?5v@jV6VxWysnEtU}xG{CV`rXk}@T+DQxZxEN=Zvf zcMly(i%1S3senWG(2X?GFf<}1-6fqvcQ*_jLk#hb|FzyPFl)_y?z;D!eSUji5h)Q` zJ&zG`DFXiye9pEt9{0`!r1{&(@inP}+q7X~m{MW~a_;_CIu8T+DRH0Nap~nL8&&p+ zq>$aI?YUCC?@cub>WegGg=-Nkqjm+erKX9~U`;YX!F*Xs76v3V>st(o>LCf!pa2+q z{r2hn$k(}rqQVPV1{+AvYUCE-z)*Yrka(t}NI{`PJ=E-q2j@y8Nc`k2u&#`xb%>%I ztebJ*%Vrq70yLA*ez>$qg82!YgW;NSod*E7=G%ciFS`iA>;WG^SaxZqOUXs)P1qL9 zDu+Kat_x$;^jt977j%NZJF?_i}Yz)~$V+lJXV)RvY2VmtTF68avPL_8fv!B!r;PrnO(_BSlU{ z?1bCRthANtHreF^Z_`5tln9(w42 z+>4qtBY|i|eL<{(9P%+USRM#(|CamME#rD2$xjU4tY^Jff_mk> zFX`$cY5WpZxf3z3(EBq*UuuY$lg$|g#6FJ4`Z;q(YGk{57{C!K6@c9#Pq>p#$2-L) z)3&5#HlOs#pFn)Dg8x|uhrf&M`I5y$2;-<~-lI^jQDHjm!CUL*-PdBFi$M5(TFt`C zJsz61{L$Ep$`{(15%L)o1MIK;%P{VKT^L6yqxG^RD90ZIV(x4dm7(Fp*BFuGTf9g3 z`vBQe8V11|Ti5e=wXZ!ab{lNh+%etX)u%09o0V6>eM&N)v+sWV;b5&kVyE6Pl|v8? z4#07Kg(sKlxE0blj50DLvK1Vve@sXJH;=Og6Z$kLVd%L+T;<4ksm+Vr6dyD4{sF>D zZ+PQTs?^tj{;2-nAMWvX)YaQaLT~pcM2NQH{WXQvgQ&JH356X(kZ+Wb$>bv}vx=C< zx-x8K{&n}5o|&h|FZ}KTniG*$0k(y%PWIhJo!#l(>7&P6`;VT%ZwKcYRwAd^K#pS| zK2N=83re*7U&Ah}cVe24)F2NZ(Ig&YZNJ3#Gpl_tq6I8Gju2IXvhnoD;(4Dd-tMv8SE>@(WEwV}S%H z>U!>Pm=olt|5~L2u*EAP#ZJp(lG;fadqK%=*d0b|m;wyg1^2KyOk1oL0y~h}RM=Vc zKRud0H5gSo432szgDZ6}1o1R`JYIaVS?^E2!YOu+-J$iv<<*D_S1Z0qo*>!_4om@I zXaUwua}+HTGERMNFH-H{a?DMz?i?`yoz1%0JlrsiSDa>8hg+r`l|;hPkvuJg?gfeD|~& zr(q17Df)*aP5zD$#rh-OtjE5(mwP~~6ufXF4(%C+%1_9SM4$vl_Svx93tH1UO}eBz z6(ss=AN%r)kF2{*TOIR7{8KE(-A0+2tdw3O@oicF4{s`|D6_oa9it-Xnpx_}-L@DUkV<8*egzLlA zk2R*o`I+c4tVv+L_q*=Fb5I%TQriAEom`98ehVz5dU1j_Hu5Dl6)@2I{y2A7UFL1f zY4&inN&=x&h{4skWaVIgInk0=MlPiBvbd~F(-Yqq*% z)K|_p=_I`lBI`(1_3&5lc|Lr&5R>4*v1-5{037heeUilKlU_GV&SZPrRdXMB@6{%% zydz>)j}$0QjO%^xF3GzL{%Cr&9=e@S{y1T^27!2w$e)AHO-v65<2f-x*yTQV=S}zK z2|XXl!cVo~a6uBBP#o5OMWuKp!3K~>PyX8%Dfv$_jv3FG9mfE^@Zt8+)$=$=&we%J zS!YMKyQ;eNoK9)XbAM`eYSgL}LVxPbYgwW_xWMPG%P$WAOS7!2i*_}l2BJM#tEs-h zOE*w1X}T1M$?=XFTN~%{suuWiFLA5~$-W@44*{(u#Cw$|6&QDy*q1EV>L0R&j=p~N zC0s7{&3tNdh-n)AAm?Q(ehAVnj=Rz2YW$*@?Pl=_M6?I{_+B+Tbg8Hzx%sh~u=K^P zU5lRSKDuhAl=0yOo%<`svGkMc0#b4ORYbmV_ja=32u1*QlJ=yIz(jbZ_e-<)Rm5`t z&ervz!2c?0G#2c{0=+O=G`;Q!28*)`g4GAti)6^XfHzM&O)R-CZqm`Q^&R=1Y1Bg@ zl8ZMcy$-KP-trhm>2+#tQZibs#n)SZGm2%U|mwHv^i^c_}W-Z<8f5Sm`OI9Ss3(%7?-tAQq=!AJqm#Bzf#sL zXr(@TE&u_;Ae7@4n*(TiO>D!aQ?ak9*ap2mClaRBKxuxDpFYr9|^v)iSs zQQIQ*{c$o`drVF$CPgjC;}UfohvOXm=Tk`U8((gJYI}h5zRF1p$CaRb)aA4Q4#UHp z;d1@Zu-g4-J>2tTVS# zuvYK;p{cPn>Yg2)dz;+Qp@D7x0S#^wWY?MSvYRmLv4M0=to@$x(+KA(^J#g9-=KHS zIp|sQfBm>$gVMjf@zPxW^XP}vskiNyq?e}^4fh4tE3QviN%#e1XbC220jY#?6GAvH zj2Ig~#q60Q>q5lcXlcE%;}b0mzQpeWYy_G7SNN~naDCo9nGCZpUSfE%IoWNEUWz=ckCQr~&@$7qQ$%Zcw|hi4mi~G2;Xx=JGp|i__NFKc<3-=XHT2lmIx7sEHNLEq@P5hQ*s9^mVUCZ4S8mVyn8O@O z&#nG--;`E)8Z6PALD)4=1T6{Lv4~Toq3b-s3ZfYC^fIzA*yH`u^PPTNGc}@QiPN*x z0t9qrmeDIdjSYfI@=k5X<8LT6Pyd`CGeFSeShQkJ&Ixffd@dWXT?C_0<$b{n?W$dH z@y(BQVn=+*E9tp8@**xxw$=0A_|aPC|7X%gm#LO%oXQ#pkxvnMbQ4Waaz`S29WSu@ zy4i+nx2ooa;Ke?Zi`ld2!%8Nt=MLa%2;!T21*Ea?{Qc%hcS1D|pnY-0-oRT1dxq|+ z&(-&Pw0g#U(Ykl8yH6(}d|aQzG9-f&K{hHdAGI<5}Nfr#}oE~qx#_n;Urr1WAxv$WABni zH*NKYx7sLSJtLc-)a|9!ElPE1OYzE{T$0OB9F?jVga&t1v&!;X!aau9DoTp=Tr%oZ zC1B@{9uEz-cS|o5Lyw}$30D<+a|FhUb8ya^vgR$b>m`=xOB}ix@Mp@u+t8rfq}4AZ zw1v7zz>D0({Rzo3i3Ds^et~11xWDttMv<3yk5s+flTn35$=KI>_9O+ zzcB6!IbPV|i@2b*+V}`E+-=m`F?ha1TZX4;--f;}g3UD6?UV>&cs``%5d5V$QkO#m zgMTQNR9fo{^JDtsoB9!;R%?}}+sD;d3^E-uAlp*e^FRQFpZ`;!O<@-?rB6CdR zM4;{R+h=F~M`d&Oi~6hzfRknD!x8$XCMl5Jd$jKM`e&a*79`&$>+3~Ge#MR5^-r9d zeSmqB@-V;WJ+m11JxclA#0fGJZHvpud$xKT6_sPaiJo4MSoh^5QABc~z&Vnu%yIaQ z1aO$8tAi(6A%T#_TfRxZF*oYyM)D4)4Ac5Ej;s?*0OvjYWDRu5{e z7jLJ=D9u{B{yGz*|6IHLHIuK_3OU?T_!%?#ozj^rx~?ynxn4cyug_`fP#v^gAG82N z6;?irwH#jz9A?<5*|VaZQk}S}$ZVxLANL=7VWh~Xwd86C9q&QqrZS{H#7(y!}S3|HtPn_va(~aUVADbDRJ-`1v zIG333wZBZx-YK;cwp5imT1tR0DKb9kerk1SQS#L3X5*eejLbuUb{;j7_2QGea-p!d zh9z5SY^nKbb|TZDrckR<>&OK;z3u@$b6UW3)XBu3)Lm8+%=oL1Cirlv58H+yO#N1c zfKsQT-*^Sx`X4wN?bdE4nDM>PrPz?E3%c~rKh_nC69B6dP%UCV9*wtQE%zWlkhEt` zsAZvgCynjkV2tcBMbSm>h%s5nI^SrCwU&>%^>kiOQQf`$phn^|oUgx*U@_H2oI!U_ zJTq>*yQy^MCS-#Sv-=6|lUTm5AU+c%BbylHw3rwRq`Ax5BFXZuca7<$xf(3&b~#D* z$cC2VhATUhCX|w-qHg@}YJqRVF4Zk$^`0WbqB9zsmXFq~<5iT5lPG1Yk8SIUM+D)x zkPTbq;vQ8{=|BlCbpU#7R!6RzAYR2)AtN;ES|5+FrYdc-Y{HZ+|$t8IrY6EqxKLTm|f^Iq2g!X1eTz9PGEC8GqDI3eiJnEYRmqYdu(eM?+x2^5(%^nPoZ0_f_K~pE>;)+;Q zYRl$RNb!H*1~_d95k(d}z4mCWsy+Y$?l-$6lfJL<7of%OFN=xx%(7IGlf2Ik)<*a& zvLO{dTea@T0u>wbQ=hT~YbdK@1u3*yUQwMT{<}bOB~Tds*BS^VAB4{Su(jVZtAh_7 zuSug>M>=0%X*Vz!TG5W04pWkkV1nqove$aE>y8gDtDtY=cgJ7#J7Z=G;y-Q(srr95 zL|b;R(0QKQ*m@tKH|eG$0)L4Hzu2ICo(G$yd|!^qI^aap8tWp$$5^KoZN^Nj14+lm z4J9J4!|?oFpP2?9(LAEzCg~g(VItO?K_$2lGUp@mZ}U;DvgVEW<3LfFA#3PpFHDpv zR96#@Do3!nED;C64bXRg(gwNViNx2?DzOqvkCYv?;y}s#G~EV^>rsYW*m^ zh(^hOboEo_o_M8F$S_9!-7K;`8cZH!DZ7e5%df-rY4@<#=>o{>12>{CU#%(jOU0xLNIHwhW=NfQ>29X~ zcBMKID2kLZVPqFzY7PejMWP2!dP zGpt}7yBpczZta+t7A5u?4g8BqzsDUdJw$NI3pdGF`9gYCLGFrH;I%)%b;rXi+h%6R zf;ijlr~TR6)lpTnW%=Y^IYthd;_6?ES}I6ovW4RC@l=&&W0Ff1bqZg%hqOp1P;i2F z8Vqezw)}0Cukb>Qcvpf|u4&<|g%cN2%_o$GTyf)o!Ul}HrsX#u2sR1#mURw1_YMvI zS~y7{-BWe5rAbmR*uKvKnfi4?RDI0%0?Y4Ga4O$BQw+CJFiR*QcaRyyv8~=sL!rtY z(J*qZlZ%^3Oh)b~C=B81zGy!eXgH4t^(R$ah5a`xDZfxeb}}23^=R^eWAem5A9%~m zlH~x-vtlD+-+egF$xV~cvqf+%6X*o*KInFH?QVSD@GC3dF}CrN9&99J(i~`_o}kOu z{55g{%a40nx~B+*BZns8+dEey3zX?wGxGVJ_I=>&BVR|X^txXx=E#}=@w3IS~!Dn%25AGllQTz{{V|Ay1tiz$dw%izTlY$A-WrP9zCO+H{AiBt{c@$zY*a zZ;kCjQYu9b93SwYl?i-VE+ZwUwK0!YEi9{5xUA-1{mmteKC`^qefMuejoJ}+$i)&f zcl1TSH|iC|K=Hi_e=-NSBMcl;M(CKjaC>a*vqk1kS@2|px-RH&oZYGxdOPu)bu#<- z<Z+zyEtb=d zrNnzBwQu622y71x7^ys0KfagBMi6rEexSP0Yz)Jw>_!r@QH^3mj_cdFpI!6!^%-)= zhp%wI(x7Dq*ws~hFF4fzLWjS-C)HU@NRuK+u6W~blnl`dPxq_$DEyEe+EQ#vEJnsf zo+*YQiW8wza6vVEOUIoVS|oXAys=PX^cNWsgtT=ev&nbx@vSv&^stO*AXnk*p9m== z7cGtEzc$A+P^_}CjDR|?8%V;RJ8fF6M1)%Q*nr62kDo^GoYKYjxyJwHO;D{8>ErHL zRKaw4*->{vpDRV44<_u!Ay_E);M)555Z1rI?CqQJCi(|Q``8kHq6Ytc-nK8PucQJh zXtUidNC-PrqqP`j6>_D%>T~keCvB%_>-MycCCO(>auINCzt~*$7Cc+^(|8Gs&Uq_f zv>1{s{_96n_0v|B+rX{=NTV5>$b8DutaCcNF9dVnS`6U)!v)yu zk{e}?D}Go>ilJde>8ytL?ID|Qp+?3KpyI!t?=RrR(csWi3g_bim+x7z7>zIvr_Ggv z-h~$@Cf}E^fe-XxZ)X11JQ9Tr<^#KvqcFu}ogm!H^5N13fzjF7DXZlWCcChFr?#uo zQlt=2pSJsBEc8WNLAc@Pcyv3Z5xQ?R(If44cl9&+OeCw)3#}^Db?oxQb(psZ_>ld` zhfn9;;tkytb@NT~;Kf*Jmnl+EiSLrzdnjtpw{qs6k1yb=3J7r2#=;r4D$SyVirKik z)fyS2VaP%r?A)T_b=~x>CsR`i2Ms;?vK2u^4BMlAx`~9+(92onJ6t^3ZG-QHWU`}} z3cQD5YseZtQ1WRFWOy$o$v}_@({}}v%JvLNVE89FXtIoI?h*4NPZG?R<9yP5IlxB5 zW5P~EpU+8gGj#(FpBV}tmKpvQxg&Op$Cc>2p%gRfAK0n-h1}HU=>KN{45mu8d9}tn z@g{^ahgG{lcH5@l_*gkrK_gVjO3C^17Q4F2akm+awN=DCyozvfQ~mby=t z>?sVFGQ@h+6Ey;lmXZ6cYJF>^&QrMV73*=*-w0li;mVA5WD+a|fbN-Bz3#Fmneo&J zNnbD^^BAP$91S78Y_E2?|IH+Yi#SlQ=ZXy4PJy?Om3w{4QRpkGow&d-lMWLyGG4!o z&|wr4X*Chc_)c1yrOG!<9Ri(dzG05>z%iAJaTBe)wlWcOrOeG>z|}sq36bF9%N$GVG@?L?9OAX92B`36_^uSV9~1jN*D;1?I#VWQ`fZi$+xw^deS5fVP$^*42bET1ViWV79T z+)U6~|43_cD2#a)FpfQ=GhauBEcG&u$^RZPo$pE})30+#-xItI1YD@7W$To`uJVm^ zC={7gi3Q|KTJ^E)(zBbO{yMA%O{)y2rtd}K~arS3QC;HN}~IPIAv(?B}5M)rVUx`V7m!MSgDMol4c zIlXPm`!PgP&ZVg^ip816OUAKz-e3Jh7FqRJu9kL2Ipp`p0?@9ma=U(U(4y8G3xnYg zZRxU%VPy5U7>5!x&5B^m1y@`<#FD=hh~h)0v3Y9w3d=@TW$;6;Li4KCqG*MRcI{Dl z+_5||(WPzoo|w3>Xuaj<$g^+xji0tUmnG;UvS3Cf2#)yKmiRa)wHL-EUj_cbPJ=mSIyYADcof$pUtRPzB^$a2h_M`%67dt9KYn7v6<8yaDpg{Oo=jg zJzj_B!j=(^6r!VKCB@~rK(3ker6`18E64jfN}(Z>f)g1Ed^bppoiCRSXs{gTR=`$DNGordK2>*|$JtUvz| z?$J5}6Ux%c#R5^T0q)7S{>sMQ2hmudVAFsc?YH%XQ1j(s^YZZbJMNH@-nyeJ1Djl~ zFbpM{{(}5jnW{~l%{+$rUBxakm06T&(vN!05{ua`KwK{fG%# z1Y=g+Hr*ae$zjp{HYb%5Ta8NB1+-GzSSNkl17G``smWU4;`<-U3|E@P%hDn||FXVP zTnwHh;(Xpi0y6RYL$I*Oh@A6c|HutfR){rB0+AChJMEZ1Vm;mPiVORqq>^p5yffz2 zt`cwQ4jpRkPTf+a2@86LVU}!<16+MPyZ&jzZHX56sfPmnTb1Q(4=%>(B?}dVZJ*2x(^Cr6@(bRr zPkj2;6HEpwi(7F9b}Lh!vK~#a|Mo%5ahwR(j)9#SMvu3$OPr_gK~+&z;|$46mAfkR zi<=AP{7qT7Mt=(c=9e~%r}L7h zx18;J19B*@EzTlToApGskp?uzf5ST4D}19QyHx*fDHCi2?jcF2&}-_sqT?s8i#lP7>u+}(sm9O z7@YS!R9a10Ko}tNGULw!OUTHItcN|u$uJDhL#M9V@YBm249~Is*-7-=llqT>owk#c zZPbufRVgH&5wjB_V^*%%44JT`Y2IbC8MS@)@V;J507HMy<0!3CvDQDL&nncjY6cg{!qgm1>Q~h}by4@!HV~XvFZP8!?w-rzn>$Wjq+oHTeI?snH^vEpy7vYmn`hT66>d%Nn|2YsBMN4Qv=ao&L5 zP}yw^Y7y_LI@vGhbrB!bluoxcstc{2&JU+0S&MNi5UUWo(4_uxaTABlrq?)@Kz@Fy zxP^*ledjXY7VAUyN|07j1_?9vhtYRA)G*Z1PdLsBA+kJkbW$BDo9w*i1ubOkQfi?n zM{6EB54dgcap)$agBk*Ne){g~^PZQ%NVTX(5VTe$uB>^HVTm!IY!##=^|T7~6@G#l zJQ5@Cq>RAx^PiT2CVYYG9^5$bqr%$QB;TIY#v}$`sIb9WL;-H8WzWuYP^Hk&T0f*A ze{h;TsgT>xE8tl)H_fU>!S7W;=`RWQwie6foyat#gDMW5Lxsi1{kLNXoSd4?+WDBy zZT)v%X@e$X7Ee0Rh_kON7WT7>*yJ1dkrAhg|~x zR*95|GsS&pubtecW*U67XC6x*buu6V%Dr>kR(tb?ZKlGg&yq^TPqmV947{6+%saVV zTU5xdAFyG(5Z*Ri@TTSSXE+vkAPS)iIn^6JpPv{I4?;~?YcBU@&4M?Wk*|xA7>enH zAf@!Ce`xvK9nesY_0uauV!|+tdw9CB*i$I96+QLvmEH_LmgeWg?|Q;B4%ybLDhFM>UIt3liz~Mt8;HHo5>9sEFiK3?1NaKs2n$73NIsU%`>s1i{rKsM zub$m(JWv~?JPBG}wQ~CFtjc3g>YVae?>S@$rh<&Yn^>xA*fXRX zbJ{Y|BHRht^~=EOr4-4xPBJR5SQlx7SDkdO8eI(9)?@bz{HyeH|E*NYIp#B7x|FI> z56$25w6lShvobr|ZogzQ&{5hqx458^&QQpAVZS#)eR9xiywHe&W3SQCxW#9-%U?Hd z9z4gCJwRo49!BF}clT1O)a)D6vo<^$5Py-}E;Cbd0;B!NlTX%?Bfw%vf&doqgNu$<;zx?Ycm;@xkDR@FZk zD(y7Ig9mka(Njuw^VlKM{T+WgXN};7;wNbki=2%sd%YQCR8lv4{7Q{llT2mne=B&2 z52UJ5&ZT@k(Q@-qv+0?fEs+zMEuH2AJD@JaQ$8rGf+7v%>4v3Si-d`N7+6Of^{>q;&gKdC zrg(ZC?rCPLIA(aS*lEU@Ai5wX)nsv<4R1r`IFb+91azCDzIHCQq-Jp7+s$kjy6dol zP*s9v;C)e{2Y-9c!1$$PlHtGM;KenSaqJIDoXatAT3Y#;2My1!a2o>uHg7*}O|(o^ z&538+v~Nu;htCK{^In{Wy{PE+;_}?=^1Y&1;-Z%ic!SL{=RA*f#wkZd7%bCjgyaWS zWjP)d5o@g*_XQgjq*-qrq_bp=;LQy;tkAnktYeQ;1{Hj4&~8_yJp78c;~j*AANjvjC}=1;ZPG|xR8us*z59@_l+ z!N9OS3{ltsVE-9!V&0L044+GtihY}KVL!9g0E>ilPtv%bfz^fmU!ZKzZzm>;J=0eMLlA5$nqVf!%E%PFbiJGg$zPNfC6#?-2I2^xXJQR zkLif<=9V6cqe5}anJNKw7(GqitP~#9V%EIj+l{fwu!b2Jc#;8L0Tb@sXX9R<3s#ZN zh&e^6X>#THF4?A*mKD=9tHtp7%VJ&=b<;xeD}!;S634Gt?4wsaG`YQVJFIp+uat!8 zzqy+YN*lj>l-9aUp1up67vh^sYfRWHKX*ElO@LvgT0$foe#G8&zq{0oUqlp3Y?*Xc z(V(YRb+SaXec{8N+iE~`#;i}P`<$V>LE2bJrIg@9I<`N(v{>q>^#97|q+H@Zla9FC zDR^R#ldED6z;4>TRNX<2B~MN@9#N1sgbZ{?pFG(vD!gqbo@ITU82Kob=ueSC+9MYo z8cnt1mY@WseZ8T6Mnjp5x$=*UIRelUn1vQHzN%X(<&{u1lyqyL3a7+kk!r)VAi<*(DKF|g+o2k?Wu8$XK;Nz;2NG9u5Wn-pOtzm-3GDFz zST>@2X>z3*e~hn2s; zH^{2Hx-JXPy1R^3>Mf$auKjsR{^Yz}YwMh2D^V9m8H6jgYaH}+J5bS=$JN+0ELVhU zLBNL$n#vq2GI`#USZU2mPx#J=u`oAn0f63-c@5!je_HpAslK7-B|LwEw7SKl9X1`H z`+BW@Ta4F#jyuPshZ{17d0|g!#KsnBs=)o;eQUi(MISw(1LH>@n_34~0BLE^W1GJ& znWe5Cp%)q|T3uj{WJWjjK+?-z3U)pm6K>D>B>gHzvAQOM-jD4JF0;#Cj}6zKmwz|K zI30^zYic8ylM9YdN`5;*=e;1U*Ymseu87)jI zh8;O2KzodT89e3OXLWw!|PnW9lz#pbuT<2v-zgIhf_c}NTBnx`hzo( zU)7TEI$6*&^$|!12z;sSemD`>h&p?l%Q9-hZOh2+|D9Wf%+1d-@e0=>nBGRg(N9*Q zL&6F)n};w8E~)_FS=YsBeNYmRmU%w0U*hJ#QJsDAYolWkv+p|%5g`EINBcatL2WVk zH^UC5U=8u{8`|I)E_RU_Mt5pY0Aekk#>`vVOglP$m@IH58?tpvRzrtK2*Wo>;9+rY z(hL_UcU5eSE-(gqMO^Maev0Zl5uCoe3HhVg_THI#T%i;iIyBN<{PpQo%t>1OWZ+R~ zjTboOORv7R#Gk>ljT^Kqb1yWUpz2x`e$?Fo=k?nSze{9R_Su{3N5W%(TM0^c~l^AR%SYSM;ujy=(nURHm~(p zoaxh!d(~cXFg;;nLTo2aRUiZ~D?) z;~9I?cwRsO0Ob0*^^u5ub>m?f$YMCt5@SlA@X=pxI>X`UrU}*_BR;Erj-}P%oMT^{ zxD)+JP@)LbZE92;AHsUMvU5OZn5oW~x0}s)BG9X}|IO81p`1De#P-_|p``QkdkOBJiV)9(X{TjO-%idhIgR=)MulZ4715PqP0rS+)*`her13h9X;GuMk878Q5Q|VfiRP(*h z7ni~Zy%ss?T!8^6xc(cR2wb7(Q7T_ZX7eP<9I8K+OKzsr#u43|E7RLCHTISfg6=qT zrg?N)1FrsPe!1+`Z1OMZJ(Y3zfjhf~ij(9DVCX(#MOZe4{O* zf;Nls>F!gqfzX}66wAdKh`8m%U$M54p2p7cPpkuRZ9`VGu;+T@TsBW)TcVkBJK_Te+9aY2IWp6&& zSDBh9gM=j3p19qtO5{2*1q~06>1U0Kk0lvsL?72saRCG<9;9 zzer?+z9+38$L{c_USs(BJoufIqGGp@_i}rN@7kfA3HJoUvjM)0zrl9uGAd`KwCtJL z30a}Ba>`FQM&Y7ab!@$rJ5{%|7ER}?)Ilz)S&HfFd!BX3Pi7&RZO7&qPxS^m2cJ;n z6Sn&3DGni_(n)xRoiyJBwy{#AX;k@j_BoOJ`%{%EyB^Ek}0 z7R1fm!(&?X;4(OH!ESBuRM^B!(Bx%Gd2{h?SB%!fsI72Wt3Ypf>(;Xa`B24NThb!R znP+d^trt{MdswqW)7GWh+TB8F_vwOfk`j*1dMrg7kQs+aM}v8(qYmtHj*E1eDJ zZzzgGL?gCkx6&sR)BNy_w^qIcIB`CUFA{T5_UWilg!@(N7~kWDeEpjiMuDrPso=+d zo%0@5f+08IcYRiy-m%2RcNQx2eQg#-6Qt#^-gobkD5jD^sZQiOFwiXezO+QJ?#&T0 z2 zYH1ktV1BFK1`XWO_2%mWmzZ4g670tM#k)J5>$lYAWoa|^tT5Tqp=k&=$O4aUtIvae2L+!{Ycu_QYoS|)z%sV}jtFP#dmsZ^)rTn7E zg!c!AR<0q}pHKT@zGcvfP?{AU|MI4HPnE=vMEq6{a}EWk0!NZ7+Df8A6|+2KFn_1 zlof#hzN_=sQu|~?gi?GgRwJuHj5e>D={&|AP$OWr2ai2cg;Dg>xq-Xbudtu`jWFj>-@`>R}G)w)edcF;(W zE0QT0U+B+LpW^tINkb6j>_Q4lxY4&sHhcACKGPN(Epuv3zHTc9Y@U6uf(6orw7jWg)Yy-0Pq3L zbQ>E8fPDH}1&q&)1_CeNbtwJN3VvsvHz;I(1o_DDGg9ZwZL+XGYI!scJJ|AHDa9MN9UOP1h9}E9+uH!sL?ct#Ltz9%S;{^W-MBWAIjiC^HmjTNwzbD3u?wsw;5= zKildZ^DKJ(?yRF+7Yblla# zrV+_R1hzunJ3JY^1>e~#g-^hjC7KYM?`hVvB4fwrt0WgH;e!?juQ&sKR9PtC%17{@ z*u-58q3DvaD#P94AEF6Qmu!r~p6t(bK<>Lj`_C>!|D9Asdzf7)4e0#Mar_0rP<0J$ z35>IWw}hD*no`CNFMtTurz6Rx%Y9`6|2RJv4%QYDDeYU3mNn(UB6rnj=Sf^IANknbeucvJr<$}l3s^KQd=vmvlPJL&MU#(`vyC%}@jT;3;jCKRLdS|w!O0OFE zH%yJ09}eq9%7t&5(^qMHxHk?02ZF4-OH>?k`?B3BWN?3CAWMOdvEnZvzFij%nNy$s z&jQ%@K^_jy>b)zB&@8NOpYM&oWMo#8Lalx75@;$%hSk(L#)lnRTCmlKrZ0!j{Ql>w zd}k#Pz{P&q_$h*>!G32#*5spo=uzDDoj7|bT5C*)r_MRoVxm&ogl^w=Owo|XtLh{= zry|(&7q$(LN^`IBRU${p+twI@99Tj?TSA^ zn8!`Ng0BrEY>oZD=Kc~j2159E<)Po3iwTJ=vCG$-c4>N7-3JJa?k|`n?-rggx$bm6 z%(qf$tK!fy5|)nt>f|o0FuSwjz6{Pi7hmMaLE+h+cPh9)!M^NW+z;9HUMB!N&LtGl zi7KK6_>tmZ%|5>@W*7srjm=Ummg5U*e_|03>0LsIJHLqD#G^(2wXcuf!Ya*JEfepL z4ZAAJU3BEOE)BI`cc^x5X-HGLr6`lCZbiH1su~?^jg_HEl&h&WqGg5!T>F_4<4ojX z)cY1OLc7)!_KD0|5lP`=yKakoX4={HM<=5v#ux1h=Ok3W3?q0#w6ptkSLq6xjuEr0 zge63YKCkzdFZ*WO&qsv#wVL?7C^>)b1*nRWLV8cMDv%FCw7$)%l(lAT0{vhi9^%sJ zfqne$$=3L!fYw8q+jp7)YojFaJzh2PJvX={N(|$zm`RJIzs~ z;Bu45k&p-H-URn((V5P5UBJebAX{DCmMk8T7*jZ+z1Mhbr@O6okGBh1)oI6f(my7v2vf!SAOy)5XnX}^-U zc9y7jjjdJQRRmUW32b@S4OD-Wn9i#P@nqXNgYw_3e5!%WNn@r3?wc$>7JZ;)Xv8g~ z{fxc6w9k*ObUN3AIBS6-RypsN6P#6DRTMnvsE7S|$a*h*%g~lh_9nwoEHXIc{61(l z=o@x|FV7zwhrn3w_xtMGk7_&7&!L@v)8D7mMqrwzns2lyKl7|V!9fJvFo&5^&alPE z5*c+8g`>B^yktSIf*f6xFRwV~+E}fRXNdP)Miji9wu*>eB@vLeBq)#I#J>X{O88xg zU;fbaM-um!Y)}i7eY-~=<+SV25#0a@j?=mGm#{_&hWMo;+3jm#!Pmf+*-i-AokqR& zy93@N7f-U(ehLDKXr}f~MQR~7=Pu_y#l?pb#KXHWu(^l=b^bZumzxudH&Quf5#CbN z;#Ln7Z&JCt!p+J3M=&pLJxhpm84+&u4aemFBqVuIM|Fi40JWP|JI@}uAc5N1Z*W^Z zTjrm57n0biWS(b;(gVD1r9Jw96=w(-A)Ucff)kBLvzX;N%t!Eb; zYy9i*)}u`zJifSLv&+^MxYZPkrCruSU+R@M`xe;dy*t4{mcQPsxKVgLIHWV=R7WTM z*=5CrhOC^zD`U%AlRUGP-D9PMGugcO*9V$Dna~ycJ@`^)zTSFi`_I*NQ)NLS0enFa zHVvowW5_vAk)v4w$ml^MbG;u*ALWIOHKaICvv@in{;;$%Q%jL!!Ve>XG5`Cg#;=I#ssxrPYZjU)i93Wg|P1ZQT6W}E9q-qAIlV_f1Ek- zwts{-`(P(;;b7w=?w~O2mTpO-cK|3ICcwaBY_&^_gk7^SY88-E{FynujGvZ|wqCMb zc5TY}&6g;{q6zvzLbWxoSr{XCLhFVe~nq9wihfI}9*cENlK_cD%hN(Lmw zubAVV>t-wFZHQh5+TXck<@82#S-xW2)8$Dx_4bu_yq?4wvcC zivoL6HXmju71Iy$Gk_)d)SX9Ji9(N#2S4ae3X(Ran`bLv6k$Z+Q(bV^UD4M0M%s}n za1GwIES~Fs-`Q)HOnNie=wX&duZb+6WVMe)bV-i#8$ zAq@@7yIy?Y@3A74$n+?XuzDT`^_xK}$UUBV{Ew~Itk!BKgAddsv5sv#+(;E|K>mJ5 z;tAKPeP?&4VU!mGv&;IM&Hn&JLAt&i=mtYL?f}h74MTyR-dw=ZdHiwh$6VUDcSVtgPdN7bJv!XZ~$l-G*!L)9U}iX zvBS?pS7M(Ny5StD{WlNoThq$xzfH2x#)ZAknZC~VI+Xx+mBV#^0_bVV+OSO=i|-nv zPlKV0y&1Eq>-fE8_W9Otb~{J|<){B; z5qI`(Gx}aD*5%<|Y5kZPswtk5i};Oue+cI;omMRVp+pHd1X+I1VCZ?TGZ+jVQKD_p zJ3uo)r3eoXHQTk%UA$l3de3WcH9ZejK=Cw~x&68>Sp{Ke@Y#=}w=yEcYEZSE0df35 zNFoN%95gO>KG`a{-2Fs}KR;;K&!}H>!YVDm0P;+-3~U;whofE{CL*wgf5uc38}1q_ zSWnVh(tH~s`GL4=QhWQtl`6SxJg|(oWjGa3v%1+bjFohZvB$*w{9Ta0q4ok_D!!)i zh~qvmtna$bep`6V5+k-+rxqGZ7q%@1F6NCDczXUc-hST?o+CEeH@=%MfRAHM{x-EdB@WYsJV-&fb@vd<=~q4MuLu1(&{ zV&SQi9J$NQI#(<8mI!f8o;%H-&1-mAj?-(iXKokGq+Z;q|s{ZkyquKLj75M z1HR|QKZj>u_X0w8(JN<^H5d#-#@7ae!D$1`{+IPO&(a~Jz^NnRvzH%~Q+K@@SFSt- zB7*Q02njf^yh_oWJU6T+N{Ux3CrBcV%lkcQ*GPw2pRV8Rx;Ax{=TlCe`>sJXZ433L zA60tan_cnIMT%SP?D9DYva0j0$@BhcP3kKE)mchiFa6uzxXb&y`x-Oi5J|~k&(@dz zIUVE`FPAYoR-(u8Uqbf=4x5`j2(arad|mzM)ioa8{>nskdNOu1hz^rRSf8ovGux71 z;GFwBt<_`b46HLeCFd8F=uoIyZGpkC)BC%@uilkl16RjE7u%I?qRrSU$8Q3MC4I}-zBqAH;y@pcpPYKM z4?b6nb?anhi0jI`Se;!y5U}d=-L;`pwRqz^-*CDf0LI?n!Du@|(BaRL(2>%0|*xqB3^OnUz>Csb8+qd;ls9k5Oh+Vo@L zcMmdY8HLA~vRvF!+$R5uh@e|ie6cbwB%orpm60Y{9ziA3gs10E;_Y|88n1lu=@i$& z0w;r^FTFGlbc3O3;{vHd0pk3>lz`*_q$f%BP>D`vWMe`Aedh9MdB@$a!6kVDEbWa# zla~EWc|3ymcp0m&W-9kIi+Jx2IF^p+kWD>BR@JfFJ; zrBH8O@5+(h`o3Db?D_muZ^t!vzL#^=_(^MXN>RoK<*d5XabNU}r=@3^YW1H)oc+FR zF1hpbN(aB9JI6c+$?y8}S8>R9*=skRC)EEZsq{O1$FqKLS@+y=t#3c}-zVmrA~$;3 z^s=z`o{+6yf@8;y;UB)}O?dY4=g$;K?*PsIUs5@#1V9B@|29hi8Vm!+-v+~;1aMjM z{4n#;i)wQ@Ne4JoqLbMsO(4*ZzKf3E@{xazC+8P7HbTR;u790_LfD18PDpk46t6&r zCeMeMx2di-_wZfo$qo1Sv}z`Xt{?ST?-;zK#er%4_sU2bC7!F=eNHWTzTe0W(4{w7 z*TMB^82dBWm^8*`sMp{7tZeX{dEOM8@njIVw(T>}6&lFETXi{!acyci6@zKa=6gP}o1_z=L!y!spn=XDvo1kUHmcSuzQaa+)?EAtmFbKQey*T#Bv z`b_Lsflfrh_P*c1UIvv~vQEo}6+PjB~*%xS4EXD zbEmm>^E0$|FLV7a=~=c~<)cK4vo25T?_aKO_c`^Y`slj?y-t4HD$t|p`^XjOo8zLW z6i#DQp!>w0Q>;KQ+xspR=qq$r1bor8&&NHt|LXDY{t;aHdO!)9s-2b?3=v{TOQ0JJ zO+NeLn$sbO4|2B>Me-cbgI}U+zx1!Y{L9j^Z3G^7%=dly$eOD>*M8;9~J)2d8)bif7 ztN7fHopJp<^37%kB+xB+9?$k0b`RZ>=)03ZFOn)g6dfT6^o{X3_$o0dU79P;sb&6j z-XyAB6)svM9GO0nwn=}Fm#P0ErD(@xTp#fRFyK? z%q)3M^pdyYnpeO02QEJLkhS$^+oc5h(2bea9E{rJ$L9O0DhT7-8K2K;O`xyquV=&7^(sHs zcym9UFxW?0EqQKeOQ`Tba?1lfkEbS5we6_<{Fn)J2Y+RlCWh29f8`rY@e=4|Y2rw0 z8z+I@{nNT|d#_e%kDoxVpMmaG=&nBB6cgy7`dF?;AKQbl1bRyXodtvhlHc@Pc@C0A zK_7|Yxi`EJ_kQA6Zg}J0(+i;Na-UVA8w^wS5DkVc24yTOM3h&aE8#;*GXQw$2XWo& z^5nT9MS^6pi4iG;=go-@5}V+O8?=z*d6$57(z>A2Tc19X=S=}m+{9l0+waiH^LiPn z8;7Ef&ph_nyYiBE4Q^b0Doc|;qJYeI1fa_=657xTC|pW`=*j2;*_B%XX_w@8SEBtH7I#Y4gZ zW`nz2y2|Es?d>48ugz9C*fJk`6DzqI*5i&U?SGL(cb|9Ki-5UmKBiEW>nnR4Vecx- zZ@I~4)oXkgjk8w~Q?9>uVE@g=WbtyhNF#x&X1A7@aPG;E;kqyXfeYXF7P^)IbG8hI zpt#Z~TnvUL|CtWU4MRcn(huUg?>heS^Jg!A6odqvQkF0$0SiNjL8Ks2lyA^hRi3vs z3E58ymu?C4z8Z#xIF^;X=Z?Xv-xn20>Z+zs0RfeSJ%2fnsk&;*qdV|YDZ^FGD?9Pt zSH36g+O?0&H;uWuS(Mu~-n!C!@h?n-bA!Te>L>^dsyG@R4(yt z@h(ufG}?F)B$^=23&1!f2wlZc$2*fh=jXT zZ5+*!66lc~W7qK%0n&P)6x0OOJ;Vh2CM`UghK8&^kvct3U6OA7s&ieUfO`l)cx@bw z*R5OUbD5y!O1+@Q&&}pg8|H6w45|udhD(KeXae2qeI;FUp*~Q`63ij;?P}l4ayV?# zSOTQeCCM$81UGcwy;p1>N0#Mg*%pfHpLLDJs(eS+&rbl~ebm2lTuW?r{bn((StlW@v`+VN;Nn^_I#yUG3 zs(8ssV1m8#0ajxIT|M+Pu6&0i(%WKHrTF?AtfxMC0p*SHuDqYJTk(uJNEy$2&Nt)l zfB8=`m6lqwlS?LK%|(so#b9vXpA3fm0;z)Xf1q<{wqYDBdXX;^?4-cu({H%?;N!m! zK#-DO18YkZOj;0Z3L=MScI~pinQe3M-LF0OyGBii5AG&&>kx>Nlf(l14 zgWxJ2KKlW@`+=K9^M0b5!BpD35suP=uiCuUOpYg>WV}0UE zOaHEVW&4XyrS#-Nj~%)FO}f2);Lh7d?XK$~F;l;`UZT#$l#}Omd4VEt;PU>(oTW=% ze5GeXNpSa>+wks(Zcb6O8ptcp_r9C1!Qf<5jKamR-zqR$ZW_7UUNj8NInM!=cb|Tv z-gD-5C<~4WL6IVL+`v{GlC=tjViTNdvYb0`Om=l4QvbeePA3vT z=K59FM6Grn3%#c96fRwC{<;kIBWqm~3+(mzR()=WiKPwX9%Q?3kE89GS~QBfy|2Qg`9nBIwRmWo`?#7ut$hXC zhryF?!v3j z2lX#|q*m!8DcClLV{D2M>Zf z%kkuP;;yuR>%OUjeRHLA>0__enreF@mvK?;_}ZVxvUYv@RUA$Ct28xkImR~aW!=}N zUy7veR=9s|`>l=RN;!w@079XfLXl!d03iXT30P)e1vHrP$H0~ zFc=I`_A7&7=WqyO0_f<75|HXmCvwhnJ;C>!eXrj3u{Y#SJA#!3Qiml?$+r0OKIP=O1UtlIRTssk!Ra?xuB+yru>Eow+(fE0`%RvQ zguuI%a4_s!7{ngdY~oKdvBXcGht@GFsQtzl_8?QUe}(_O=;^Zd>J|%3xmZYgmBZ;e zITE0UIyqSmjT_tMVHb*^hzN-$P*pHJ1JD^nmYnDXkGd)WIzvK=_dM_~anHG1HRo}$ z6GMZc(N5T17=xh*mKK~}_ndvN-uA%FNSOgiL6qddNzN(91cA10v`Nb^>;eaRSOQ%e zozitDDRvsG>MCd#zij0($_ez5Inb*DOjrJMe@C(#_j&y_O^sMHBvE~Tc`u<+Zu>Oj4P;$EM{l@6n5K);UE*REFhDa!4K&g#J%6N?W$`<(p4 zL@I0~)%tdudlqesuRbQ$)_u-@o%^xM?Vr2KfgY2?JDbN4Pt9G++Qz;9S>ma?K3v&Z zrEj+$C4FuqhQK@@t+2#qhj03$wepD4Hr7H>bvf1W5nO?}p zOpoDjz3@lyHP89>1W>y=Z!pxxo0dQ~?7o=uA%KHyyH_G2%>|zTQbOr(i5|WDK>F(+ z{(b<@1fd%R7!d&^U}*_}SXL4&qt6u8UvaH+IzmtCslx}jxRkzZjH}L|PvRyEu`4Uj zV^poSBx%4!8$s8`p-$tg(c!wL&>QpXo5xz4$CwG^m7qyO zQ#3x3W7I8%ap0F=k!X4lyD(z4L8^4p`PI*9wr?xVu^VjM%IB?pR$YQEB$2K}UVDT( z?o;dIa*vCb345tH?ru#03pLK~RI*nhva+7;{+s_|$4sERd8lrYM3sEBJ%8Q!>fZ0F zpFuH%a_JCCJ?u~0!Bkir)~%sv;6YYPJgd}Q-g?u;vLDxyUuZf=&1h*Ai7 zDXu1hV=3eN{^~E|*~eeN#hkn!l-y{X=mtYj{A@665r{#mC!wMQLgs(dA&3Fs1cG7VNFRxzNq4i7j=!o;z(&TV3DjDfM&WUYdTb1M+ zfhuToeqtoNb(A@q;#vi@-f^IBE-79qbwXxnv zlix36VtF%W3ggyJ^X!vE?z30bkoIs_^&=@!x)kd=OJ_(4_|1D>hjSNC3n)8CxDh(1 z#nl^P5X`AF>@_TbZrEuYW`2U+1WFE-0Hp*_ISCaI=oB;shymhrmmbvLxaU=PYJLH8 z?UvwOY*-$Sap&ewTfMQ{&6nK>8YJjBrs0HFkIN-c=u3=aHSk5`^8f%K07*naRMRS2 zEpg3jDl$o`gC^4VHK9BFK4Sr{Vb>uO(Y44na&aNA+fLEge`Tb(>C<=Ol^u8Ye9ocv z&SCt$;n&D+`}ZZ6`{iTA$`=$=E-6y5GC*?(XY z4kk3lcX*2Ac^8MWe}m;Wral{FwFNS5&QbR!Q2Vn%#o$7pW)s{geAn>8)F{6bto9qS z$?xi36O!mLx6!aI{)XoMhKwNB67f0u*=J&XW-&hUaA<>@dOHn$xbYMcv`f~pl7gpH zMeU{o(s?fX)kME>?NZH8x45O>3E&|46ffUu7vPgSSlH)E>M8;B86YG`RiI2@N--1Q zvRuM{`se{(@>aSIr22kEqIu5b)X{+@&<*?TU)rgtVfUpR1f_&X(*={8E?feA{k!P; zuXx@o&OdhPesHQtXM{w6lmYVWp|O6QwT+`}YDt|or#L3(xo?xWA(H350p;Rx>eexG z6H)K+IdU{^@>;vH7fPqK8Y=P24o-iQ9CjaP7zcW+`I88`Q}tbitf&fz>UhF|_GWAG z{Eu4O6rw{tbD($Uj_)U>5ZemdO(Vk;u*xAmlx|3Luf;cBYvVvCByOMW;ioP>$HDhe z8e;A1{=RzeQtHNm9VAE z^6^K11W#IHQG+2ot~MAZ4&epliSKc`rNv*pm-r&fY9bnx`z@ z$@7K7vLn2P@pB*I%23+qxlWIfdxweI~a;z~E65ZXmltIQCYp3hOSDuvb+c9p*o?#dtxSQ<9?Qyg9=l^SK zysopt0+j?rgj5NV0tqOibI}B8{?DVA9>5#F?PV8#=2vlDZuQK$GweMqfo|AosHz?= z{y<5QJV(YPqW}0GzTwi@%lAVhJAD<&E5}KpaBm{&dwq4%dv4FPBzEg1p5s0CT-I@G zZwnLXMn&F;0S-^t7OcONnyYoDnmlzHmt(SMwDENeD7X7L42G%8;~o^|wb;dKihzAs z`~LRwDIBT~$$`G>`%crLgkk^U;r<_Bd04WpAF2vYvcfjv6lpSt5QAlgL^A7>kb+5Z z?(vV~uYB_x&M!aLlIVv0#>}R`u+y6#K$0-M(&p|R!jpd3i!QygdXJj~K{+2#25PX=S1p$-vT z6UJbvHyCz(&xxPz%jk5ROigcQlWP@iS}Gn67Ep-4I$Y|_S?^X^@I+ zOkLNb-V2ispH<>Uh8vAo>(42+SJ(bY-hBcB#--H$-RqQWow&JciToqPA0>asU(XS= zmfbomHOG+EcD{=pagUG3%AurjV@+P6$U8*w{_|uzYnDV803UkzZTRrH zTT}@+1Th_Ic5ZoqyaTB!$f|PtMO5~+^4_nL-WvL(U!v(!%aDLNR-uP9JdZKc^|sD` zEj#P^j_;Y-79dC|1=BMiBzQ^{zkC1B;rM62Pyi%)0EK43LY7Jg-J!%cLgzM%hFygn z1R8c93RNXK`yCI=Z#n(586P8#L05Lm&9;Z);gs+EMyg@S^J&eIVz>&U{sZR5pZ7TJ z(@ZJ2(YLr(e38{qL6r)EKutheXy}sYxVP`Q}mVRjl}<^f_I0a5=uX`+rxi%pb(^y=bwIRPZic+fR>cAqu72LJ0<{XD+#`sZ_2g-%S+tUgh#53XT% zVHN0x-3EbP@)o-G-4Ff@F3&Fl;Ox2@69j4ZkKKVp!4N0yA>58rN}jJcAT;fRbysJO zysKrW%zZ9fS3SZ5>Cq7OAsgCiB~tA1_OiC+3iB>{U3y zshTqHdHK1@j(Q(gYkk*dlC&NZ*mQ^6c+1HGw)_@S2=#%f_FH6n0zQ&kqRl11tB}s=ZZvmCbSdiQP?vaHF1uucPa& z&~X(=I{VUnJ=YlW?HeEOY9gI6hk$cS#!G)sXDIG~aH=?a=^=dO^ImrTSKo?fLh~k~ zC$0M2u>1Rl!LYOV$Itoc^N&9HQK%A9UT*S?1f&fY_#&D9^{&|KFN0yKL1vtdW4sEe zwyUVSnfO!0=cOd)dECe3xj)+Ee?rNymI~Cm^)_li!**BuKc{Jiu!7qXEVO?PWngE z+uE*wE_bfhQb)Ookzjzcj!YvdVlYg_y~NO-Wu)8(?>s?x^axg=ngAC^C3 z4~GS++R0beo=s7)rmJ&SKacCC*?w+fPM&c?CVMAxp}k%q+x4ehp&b$hsuc) znik#e4nd3~(B1#!w)E~Zw{4$7?&)?igdy6-#87c%uAW2L(A7D#`7{`|*ESjq+omI7 z{#~1w6rqtXiLI>#-kbB|!R>8+)&B0kLp;a6CatEJ8z2P|$qGVrCJ+|fefAdIeeS&q z%KypA4OO%A2r822WM;y!?K){wVA%34n*rjZQ^Z6>2c-Jo-~9dgtsi@1%Z}5HLSolA zU$aIaOCGoNS2u_^hhy8%1M4V&9XaUk5}br{^hs~;8EXR?R)=Lnmiv0{_@BGvbyXGg zLlF~qU8irKWq?)ezP}j^hS>ViUt&5Mw{nlkb0S*a+MK?Zg)X-hUH9j{ddP53GS}ui zjtjEgUy<)gyn6~X zhK?`#IRO@D`VAKY6oa;9yPeUo*I2w<(O3!0_KJJI1mP4Ds~q{Wcwl*%q)rW3K^bbV zty0fmn7DU2IURMYLU;DRbYQ=;;Fh1tc0o-P3_8g%hWc-P`j7s8R*i&5|F3($ z9+$ammcK2HITp_p_AD`C!@e804qZhZ-Bt(M;uI+WMhXQZK_atk-kEUQ{r?v}bMdsi zACxSzAqLIBuhWVm5bF!b-@a+JT>o;Q#@YTCK8^ans+-ejMwYa!(jJp*e!{9U@?JHP4W7oM75fKXn!!0}wkzOJC|Qt~{u>po^65bGLdY^QWej?DInED*a@#;yI}%s*lK(WXa)dZ@Wub)@iUiC z=bqgR%7jFkw`8_6(5SA?`oyS+ zW*lHs+!E>**|Rk>I2mTk4fPz40CW}#O%2Nw>p z6=$%AprSm6J}Tn5JOkA7^G;5^b{v=6&-!7l^Kd3Y*SvT?3Cfh+3%R1^;JVz9wfOI* zCavBc+Yt@KCacVrEG`Hr)&msZGlG3Tor$=s-%kP1othsp}gv}pd!?o-_L$l zCx2D)Rt~Vc&Lz2O{=(t!`FbuTUSq-Qp<66DPl;^fB5xkm=@8a!O_wd>DJD0!jb2zfP zM%>~%yZOoOx=ZeIjaC14;c3L&%asQ|{d1Ls`fd!LZLZy!iKcrcfOAlid&hx>xEByK=mR^SbT&xs&p4F`r>dHbS!E z8zeJ86z@KLGd}smhZRsEfS?3|>K&T#p`W6xIP&O-5~u~02D4KJLx@9=HJ-@+Km-th zaPIQ=rtiG(CvYVJNN3qeXu?2}HL?)YRX0pf3Dx-6V;iWDL?kIDOJUGdeI7ILp6bTn z)AVxR0>dfE-;ncYhc^a;VQiaNhs}#CF?|be-OIOe&ZaRYpC*|55rp^Qd-d(p#nUj^ zJDtb|Cf#om2E<{OoZ7a0fmE!2f*Bzzo2M&a1>W(olX&dn>HG;)6wn)?^FtIB=fo#Y zXbR&%il)P)qF#VsEq9K0XbJ@@+WzHt8OGx>%hP$r}*U}mT!0L?+l!NEk* z_nCf=iCQA+-6`!t;nKfj6xJwcC4nC6D&*QayN++2CZjEZ*?r9=XpNR*?3cpD_1T=~ zw-{efdLBx#(sz*7c168S{`wfNkN#2Lkwo4=a6#b`-9Gk5;j-01yTPzU@Q-d%rRDcnb__B&Vg&UgG0L4TFp zXcs%l-}-xE$1Q>SmyNGqUqd>LtKTv8LE+NNIydYEO=BmaL%#iB=kX*Av`rO)gB824Wb4)7?>-ViinP^0Mt2h8>^gNkYMr zQ?>kx=N?x*TUQQA+g$sRzu@n)^L&HBu*GmomRMfE_J%!LJ4Zh)TjSU_G|I2cPhI-0 zCcneQNBvGfH-FpgaPy*v&5sg;a11FiQe4drEOds)pZYBRr{ga>PbXC~W&zc#8l8w1 zhQX5OhH1kR=!S_$Ba|qhhagrc-TJ`ISI<8AAXu|1b0G$C-q1#7P|N^ks1lT$6FyBz z^leU_596?jw*xv<@;qqmNG40)(iWh{SZ{`H3S(@nV;p*Qm6x*o#Vp{l1j+bG4THfj z6o&URw*AKORQHHJiZ7;~J<>heK4SUBTkGQl#zjkCV_He{Y+TZ-UD$d*G31t~C9 z<`ATLoO1;r!K0Txfm=`CEYSQS0D(Y?1I>yi4-4vEgJDPFe1lpg$?Uw>$|4I7$AOA(9xRQyjXwE5@LTC=UNg=8#%$KMt*1EnF>%JngUq9082Pi*Wb$DDJ`@!ee<4kQ1d}?kQA<5*S2P;%ki~8T+!&V zuHog7h}|?)BI!mwwUe$G8UjTe>OI_Zqu2kuJ;MLJl~Vgh=gs zgt7+OuH!5Jw8M-%H4|Q;+)}FCD{DWE>OYIMs(D;T&#mkSc=+6X zAKVt>sLmjBkDQFGe%bwwO^m%V@;4l)baYEC~I4W>N2?3;ZNH6^$ zUH|{w{{}oQ=Wz@vQly}H0!b7^$@VGSo}O0?VQ983DWtTlG*MO9VP;(C&gL^rmt6HJ z#v#-cljl>noUVI`CCd$l;q2x%j48%Ah4y=0&90hXHnv^awwr%L=*+$*wRj6@TLlD8 z1W(Cj-1flF;PvmK>!IMIqh)f30J;M-1A#Q-Nmomr8>Std84N>hsvrQwN{0l6;zZ7e zg#-Qm&;N<@XD*)x5n(Rbm7s)*t`pK|n!B7u?^<=9Ql6;Q>Q|R7&v!Frho1-5L7?yE zG<~dzYrrv0NR+kTr$ok1+wcIM$1d-iGN0y@WzY1&VAwr?a zgr@lnvl4Lb2pPqT7U#A!gEbj7j0_V%!??~U4mqQ2QGuQm5a5wJX)NTZm+oaI{+Imak*VvXno*D z6C2NPcG+Ms79?Ett<{H2Z9|fI@wbX4~L&@II z@ndMwQ;gU$)ILV_Z>N}JSo68#&yj#gK{*xS$It&M-uKX(1a!!UAkOka&=WUhzd!(G zVxx1iCZmRtVF`4@$T+E>LoQAeNr983de@nEr+@tEQy^goJ)IlbY=f9lnX@hy3tIJt z5o=T1jX*@U=8E#J1aEsCyzaPrH1ztq&F2Ison-5%TkM*;!C=@0`!s~_I^~-?>m1%I zKcm%G_zll~eTcED(D%a!l*#F?`M$p6kzQnYNI8z4Z&>rc!7w6bHU)-Jol_7&RX|`Uu|nn13lHi0KKg${ zB|*YhpmZ#2qJpdml28f|z!Hy#k%)Rro*U}7)X^wDrkE$(3V%Qb`3~3hacxGmuWMb* zWBs?$+FW|tZ|YB%lmOa>Ais{EbfU6pw{;ALes#+Z&~_v~SzE{TX3y72wRqAM9QhU_v|L#|ve)#%DzGT7E=T3Pe-&uqDWrh?!7 z_%Gq-k3TT~qU&D3P{Kjhe3r8MbCpa{)vlx(Muk73(`hd zQEU?*|5SCW&US5sp4j*d27}pw-I%AQ15yjjcT3KQ;`!n+;;(Yze3}y`qsxXwkk3^r z7d!lnxpvDuj8y0dgLIZhPBVZ6%7m-wx~eZL|hfDSdQLbsJL zj0sDi8^$%n1&nXh{Mb5ETCQ<_&d&M>DGk|(7^07e3&)}&)w9br2*Htb}e zW{&q{0?(bXm1LIGU@&a)zG!o5u=$JYBRL%24Canbpf}C76l86Eh!A#jRp{la2rg4>P)vtRyyx6a7DDE;q$`ekSv7A>0Y*Frne+b_XMJ67SmbgQ zva5b9?SspjmFHx*iYI5;9g6>48N|9)F}eLJiU0s007*naRO4>RR29rzQ#IC2FRDK) zu$TG)bfVP^FE%)9F*m|G9kp&yCM^@y$wW(&gZ_{GPgT zmgi8!uMdz8)C98Y=f+HjmFIFXXBsx{)e2z_I$W2Mywt~H>@}AXL64jKdVy|KNq4!i zTKh~f;ven4E?w2Z*DsdNdPPC{;1Zw5whQH=3zmLsWK(pda&aHmH~z)Hk8l05SF#c) zn=q9{?#O6d3?srS&<#C9g$R`GB}9s>87P&qHU!|_v+t2V{PYn>o&nXY9G%3((~Qub z#O5syacnxC*yA(!2Qbb&g8h%q7Rc<4!C)}NwKE=DOL3??hHcvR%Frn{VYR(6uI$-5 zTgWvA|mFqG#W-!k2U$mm_M%>V@j42$aX2|MrQ5>O);2|N$ZC{c89yY zyD&*KS+pS6U@%P6s}i$O>;9nG_1@<(j?cA!1*hAsF)3LCRY;ZBU8AnU&mZeptpTax3v}TwaUh^`K&_g z&f2gPE_HL>KZQ%TryVK?@}qF6et+N1ZG9+QhMYGJ(5d=)n-+yju-!8l_Rf(l>0OKd zj7#CNv=P-5F52mPx=^@u_V82(p>XjTj|=76rvE(*g-h4Z?<*Z$^sMSk-&5lc7u24e zHNt_y#b+KRq8_iyFKKBWs|ss8{IgyuTq5XSHol(eR6C7p`zTzZu5YA0p)-YxGL!|R zf*YRs7xAZ``OWKo@$cwmfS5>CjR)N@A}oqB^olz`Go{1K)aA6lcP!+N_(#9=HJ8p` zehkXb0AUU=A<=o(k-5W`SDe1OBtfj2WE%}i_x+M|dRKLauWl0W@zsxu7fzI6H(*!6 zcYAFO2E#Oy5eCD)CIG7TruzL#BF{kil~uV@PB=QMxuGe<3q!_Nx)-v(TTZ3)yk=SQ zT#51c)1St-e%Wg;5rK}5auOGVVMNSq3JiVXM(7*>(IGf%9jdqym;=D?J@l{BhtB^t zm|eltM$pfT}UQOEg3vi@;qqmX!=(`U8@oLZm`?@{?*mqtVG}C za8q{3K2UAoVhPvqB!IzS*x|&%WRvRLvQPOmNZwCWRgn5tp_2|v!Bz#w}g=XblE~VfWBhP0j$)>Fqr<&BW0* zR)kKzLfQ6lgTc@#)@2ZmfssOa4h7@32X4kIzxtsR$1O!<7*jrN^D=}vKvZ-?K02c0 z#0gEazXKJGLIL0=P;!bCFTInl{d>oM@cd^lejLhIuzrT)?@%`<_v)(BsrW%)1&y#c ze`hR*vuVDn8qstO%6uJi?yzcpv)?61$S5i*E6`(9Ppy5XZ&M#;w`OE3+=tp_2Bp(F zxmT%mn@esfA}BxqdUd|bT*s^0;f=ZE0KJW;6YJmOr}O2&n;IIP#SG4Un)uQ{*NVC} z4{g_vnDu>u%|mw};bJgs6QNFj%1U(d~Fq8{|3JdYG%9^JgY@>jXaU$g!?$VJZ- zRKBvlI=+5<<{_Jjw#DP?Kkw6?R+% z*qYbgoj)clypu5HUPxE0M$p$Wp&!Nv*EZ$rh3YNlfyYHy){1bYuh8Wb)|8XY* za-lMrp{$NHH)5!UrV_2j5z3!lJis$Q@3NaNA3bk!1IypxrK%uZk>*a!)UKIZ`KV6u z2VjaFfGBsQUl059dQG0DxhJD07cmd>d?;moZ0SE^mOr<#;#57X@~^qKTDj4x2r^Dj zFD5>yCr;G`7>}D@sxEJKg*T5Nwpap`38%9mnNb!J9XN9xqW#7;3%RF~A<%chga}CO zR7NkW#;vQ-Rcjq$;KA>)f{;!Nf}@Wc>aWvZ^qFxLL!jEzj{kTikqB(uy(R4o8=hV& zMnn0zWo}0jqzEkCC9+R7v-h9wyAeZ*{@|HH)TR>(@l?cKe5p=n!$PB!f(F2427De0 z+%7*oe>$JM_!yL8i;x`U+on)*Be!>tR3jNb)Hk#-d}gG<(TXJJzBC3rb42a&gb0_4 z@<131?s5A_51h%&d)bMx{*Kg1*l-{~!j^liznICT|Ek+BJ?u5M*AmS?`rXX7uju(Q`y3n=+Of3%le!jNh)s8Q#9p6>uz0YMhytVjAIh?Z?E0Syw{$TVa z!~+wzu8SwPa{@-onb3lAl?VoSq&RR^9^^wvje21)=f2@$^Nxq-D)qyo1#HDP4fSGw zP$*{8i^C`BL6g3*FQWPE&W7XX8JAM%X&h3>J0G1w>zrRwOM_RnlE61vvyJJAXK zvX#QPp-pk(OSxGp2~G!U!Khw%h93^hI1jXat0)I6BvvDpMTqznbp*VBmMU z^9(B`?-M2eDX98AR#vl0aJl)~b(NV_GNLidw48feuWmHFK5_eD?48$stp`d|y>HV^ zq3vo9R2=3e5y(l1r3k^W%9gOP(3D7?eEaS7UuT=YQ-|P;GEB2&i@Q$^72^=HSCN0# z6eHV?=(fuHvQGI|`JB#=v$DEwTAhze(yw19PDOjwpBYeolvbrm+#_H*508E8er47h zFoi7y&xrYd;#o8uI|#AXNqly|(ZcKrGrc;;3b2D(_vpyzITy2fSLcQPi0moS?K3Gv z&+g@@$IQB+uw7WC7YfQ22sU{{IQvy36G0G~%&Z(nei5?%y}4eUs(xRSZaR==pq`9m z;nJVAzd1;ag){|KBKdTV`eAB}31{)k3`GT0veUZ!>&C$tb&_X zQZgx%JN|1KJ`&U+5D9c|9|*B^gmG=W!Es36aom_XQ=HRK%F;Zdgyd+n6US{9I(DI? ztcA{eu@=LUv#PJHD&J|=4hgo=-OK|)4c*KR(JUI#7Q7Zi0*)qyzL*gNP!!OKJHzGJ z*Mx?jWl`EY|BR$aU=-+t^(ZEXfx~D5tn4^%53Ibvz&EwaEu|la)SvnB&QTX`e%1e(YWH!?-z#aYF|shr zg9zTBUoE3=h`*okqMl)Oao85Bn_QDrdm~}2JQ*_3$orE+1t06(mr z-y=;JHuubgkM^u#7LBG8Tmzt4U6Q8*PLWrCB;JMO_~O&Fiq-$FK$E;}LRBYS=Q%gq zjMU4Q9kK#DEFD7d226^7jvT;}h7UKa?#AIUnRod<7;w?dPxr7|67HGD!jB^=W9}P!^rSleazmbDOF2Ai+jq8*&Q+0mW5U zsF(Yp*NBFbn-p5`zgaBuL!r#94{HP!(04vCf0I}n6J|&i%Jx(kI*b1C%eb2dc1x%D z+6Cogx##4!cw9zXQ&_;Ne9P>2A9IKQHuDVeFUM~xTP6b^R+hy^tJl*}vn`kj&-;M>9Eob+(Uf(OvynQ_0=(gYJgaZ0+wxrzrK9J(%AFrkByPT=&<%OkTHKM1jq@x24kCMnxOJ5K~sfCl5k>XI{uq6ByxN zB*T$h@kR=?!Y(Cr7RN}$Z+o5nz11Dm0>GOY7n5Lh6Wku=dRpd&GhaESnSbt>D<7!K(@T$5CzZq;31$ zLh&iKT3Fz6Es)gQPhd_0_qN|>aXM-QPyBaBqC z29drl1q&%)4g3HZc;NLihNxjm@^_du#~UjE}e(51fDZ7Kg58Jm>< z$3BbiBG+4=gm68yY6*I-SMnFagB{^q{5%(M8MAc-GXx?uo>WH&78+wp=w*zy{qv1b zigsL)4*KUA#xI&{?r$;FpeUe5AstTMnYvSTb*Hxr9;{Wk7-45$D4RpW0RzRdERO+Z zD9RM)8%#? zv0vB$W^O-+0R%t*UVExl=_3x@5WH?PzLUqf^OvY&r|gsI`aN2F|4#hLr9?^LjTDKo zb@n3XUU8`7=Rnq64pXo?;(m)I#X@`GBG!EC*nN~zoyYS5_Dx1hKMadz5;-E*Hx8F- z6t5PGKX&H{BTg6p{Ed5XGd*G!=^FD=km=?IJ^H=Ld*1aNM_0&2iNBCS^hE~Sc1t}X zpEIMliruCZO%;%r*bG#;y8>Guv5&P4-CtnAxvBGp&;<90RK2|djsDf84p~2eiU;g4 zfqB4?#;EaE;SBgs}Bln%0B57V)ZGPJG>kB-20(d z&b31(jHC{cMX?9(Hr@umWZ_Ul5JYO`vz6fx44@;KipWs=c7Hu9XK?q2NMIxof@X^*>zmceJPpAps?0%_GSGJO_~IzvSYzq>%pN8r6(tytiNYD10<;ZWww5*gh_<9}e1>Z1}7j z25h!JRy?k4{{Eq8wZ=VHHn|F|`rj`8?{xD%m-LI<;cX0!Tu!1hu$j0vD`E>n}am# z?U^@c9-9wzRv{Opq4d55s9uT_yA%u3gdoBc=tP%tJLfn9X-E>AXxg}je3!xZ$Ezl83$CYlX zX!IGitOW$bq#FZ)4k6iIS&+)RcHra+rq{jO_kR3=I!s=o-lEA~i#*1BxK=)#lrU2P zG8s|2WY2@#6ARAE_aUP*9C2~79c3oGF%*sj1&mjX_hL`au{VT7ii|5ab6*qfZ-H_} z_XoZYaQT1FZoBeS`o2X=T~v|&uZ5i4NPlgNjFFc*e|dP0b0EdUbcLySw}M4>FR_&T zP)SEkj=^0joj54t9By)V#P*zqNs4*Rez!QdYh+y(nYZG z$!F(!WQFWt{)K5Rh~SZw>3VT4tApY3INxV{L^q>R`?;QCblEptL8hP8@UbhQESSd- zg6p-T&7f0$e~lB|XmQBmEIl*)8&{oDAgO|{XJ{z*e1wZ~*Rdn3UU{%O=u))y%cW)I z-+^aKL1$?`>$Bqi`09YVU017!|Fs`Dc z$qK0LSGoKi+ox#2m_I{&g)7#X9qZn*K4V^Y@k-kqgi#$A_H79o7NVT?eHGrL|94Yl z{Lav263T{MLX1`Ou^NjuP{n^7>?~ei)e|Qt-Dalc1~o4kEs+SA96@FQU}ZIdYe7y7YsJe zu}T?uY=-~PP8<&;t=#OmG*0f1@S~b8kJS}~rC6_Kx(=F{`zm<5$F!Z5(*+M`25SMB zfx-41aofnFE=u`S2&C~uNhsHY8@C?S+3hzJX&9oW(fPb?laP^R(`_C!yefTIM-8lk zvU+~Dm;6<-Oc8FS;uooe_-$p8HEDv)bJa0HTIMx6@eHbwyexzUcWiE=Z9Zs(Y~6Jg^+(7Q-VRN*ix z_@;zYH%gs1={_nkaeHD)q;j`;`}!>;LEAhzr}*gixu}1K*qmH;_Dwnq$soL=qYz5z*)VCVg~@P zJ!h_5wA1&Y>F##&t-XeFQ2+cvla0GR$=q5@8NIm`=I=%AYIfpn6hX$rfQk)|^4fI0 z+C076d#f3L*~Dn@vqnM-eaT3gdDbg42)8(Ab)tGK?KfWAKC(O{=+_$Dv&;Mupb$#QL!D;bQX%g7!!aZ0B=sy(5TkIW4l&?O>5y8 zmn?W{ab<5rvv_%O6IOh6dGI$U;{%iBQfL08rn<{wM<>XX7E?1$GwW3UW-hX5jE!Z5 z3IRK&fNmc#kmfzQEaml^iyvXtHzv0nNgH2D-rzMW81o=1Qu#qnf5-X8Hmh-3fX*KA zLiFqjwzJx*lr?$=r#|xYY18ax)Ih=GoBsOqw~m1BH(4{sKYOCLtvrsJ&26=Jz-D0s zIrJs0pU~t&J+B)dn_KOUACufE1!+YP!2FsE2Cvrr8iYH0*eo@$W?f(jIw3)O;pe6c z>Q4$lwg01L3BOl|fFy;kg5$Vpoc-E%h3RU5*~oFgVS5Vqz-bN20?!wgJt`Gh{4jF` zW2oolJDG=4Ggpz#@fVLphxCPio6Fn9^?NPdHFAx-%sDl3#T@tZixch>bwB90nu!0N zQ|u)k%0>bBwy#l4S3fi9bQzc_C8KugQ|dJKy;{TNiw@qk{PVNEMR4VNH_}fGB2p+} zmWLr2i<-Wbv!U+X_onV4$|%s@!58{5CrNRb*fZO4R6zQjY66ZBWiI>*i4fjn0^T=| z!E{7WJ5)wAfyy!ca}&yC8GQgeg3HoXJJ)ggsnTxf6l+X8&0G(6s2Lmo1OM-?#!3y% z!4?|H6?56Mc43}*H4k3fZq2p)ngSNG;zhS*4W1pZt|~21&*!a>?KDvR_Z$KSI#?9)5UooF1`y9HTh$cS?0&kFvK&*T1Gcr6e;9*C8WQ$w zJchoaRWi`tdT@(-9a#Q9h!c4g|(- z;h_BnIQ)!2=y*)=*d5%w9=*PX1M5Hod_;Y9YKABG5F?Hsg)Cl(A9sPc|Tvw@4iJ1 zSZQMt>5o8oFhE_~8hcKAzc;pZ@;ek0^*+Iiju@P}Qqzm8DbUoSB7n zE3t?$OFm$?twim$>b~;H2AC_l>?@8b*%NB3J66pYp3QzVG*(AlR96~Y^b9bO6Qyza zFIx@?p)&bG2M=)8sYwX#UhYf`{3~1Jw*7~m?Wsfjx}ou1F~45m|6>7OJqJh!d+_7^ z)6>};zI|mgm$2M~iigS!6BbztQsDo70+sY8T5EM_P0|Z@4Za?2Ac}c1h3_-K1naR6cUGFcbbRFAC9P=YzL~lh(=`=a0;C zwGRuz(L1lQ1sv&PUyXOyHX~u13X;-)3zo2I@4@)|S?Kq4Vo^LZsfIxtQbdPC#u+@> z>On-SvDy(i`TD=d5&2)EyPX&Q+_h$p39;Lu{-L)?+(3dBs>N^7a(Gvn_hFoMlmEJB zKUnGjeOAY5(^e*YDcgzl-ln}dTh*)JL2zXLsO28aLMTKu#xF~co2oIHSSeh+f?qPr z(Nr_2wt34&a7jnFc(HKMs*$VNUfsWTU_W`qh@*NfGHC>y0&ARv=?4PKh_r5-~neLo~Smjbh;>D90~yN`BdBar&`Y+0TPutXxoPCtB!$()$iX&T4^|1 zyFhPDnJ79_u0o(UFN^@k7d;cm_KANQeum008RMODWk6o_{CDfuwfnC}=NX`dysVWsptoWiW*Q1upFi> z%jU@|AZC%C>4m`jV>J&llJLsQdBGx)zQaM93<-cCc*_@qQ6ZC9in>2 za&~2^;VL>s11PtRGn8&`x>u@VJ(opvqRuIxryiHr?IiAb&@+VTu`6_MimV@(?kyD1WD#oI`Uk^&{SidbUlTov2l3}1Nl%~ZL3i&V?ZyhEq*u8&k6Z) zY0QS|(h1Rj2zUN)^n?oxb;3^>$2ZuMx)8Uda}*)pL2e45Z}4HGABSB)a?Uz5O59E4 z{gB>dIpKVfvYj-_BS({1JDlbG@S{w>z>^~vrtW-rGMv_A$_|=w3v|!zA;J13WA&#?#rVrNj-;pC0@yA5O_GW1^wl)+5i*$i4t6N7aN6tx=JAr)2T2SxmdT&~#V6OyZu zC?uspF~**8`v=DI)Auio>dZR_acj=05b z&UGJ}Q*t!4kYT+wZ%JYn8pax;*g7RCEJMotHByrLYy|zkrTZ^>HvHMuY#Quh%`V#T zz2p0gGBFjZ-^sDfF`&aQkf_b6C;{>sEJpd78D4+zc_;BeI4MY$MkrU_G}EGJfL;n2@i(y#ryVH~bUiSAIq7Euc%c!f z!Hog)rLJeEz-V<1`1p+(Fr}ceU#^C_ot3fdm<>2m{;!0kz;F{h)@ks`LP7@T4%JwK zX3%j1?08xWNvNc1CC2bHfODlwWNzAfKIrA1Y7&WnP&h6Wg?|O8sZZ*8%6(it=LF5* z{3Hx-JsL-MSE6+#Cc@%rgubvaJJ+^vFXWHq%$CpVY9$b%(9ZGE$8Fs{=K9TwGp2x# zRDN@e#Obe|?J=93JV%ocgH2f)oEsPmKoLm%0JEg;Y#;`FSIF`|v@*;qmI}oP6cv$y zvu0DFggxr}{=>x>{!X5YU4#~~2DXPNDezTDoU8qF^5ZbRZX>AA(sF0d%a=JQEXk~P z@42ovdY2Zf1kbL$Voacqm*S88geDsMQX2kWbd%OWFqS1(@vJLg(cgHJ=gwgS-G*Ov zZFPQZ!e{{pUQD}dCvS(u;pZg(wFtj6;h+#oJ0zusRi7?2M3DNSt2U<{l3al!n942m zVgev|g)QiMymf_H^3vs{0CWc^OJxtXwb|(S79fcvZ~ivXLILRr7|(%Wx|NcDBOAOg z2t2A#v8Z8?K=OeVS?!!iYm)_UQS6_r6=8O6yV#=(v74i4@gw{e_gA4wueV7AT&|k^Z{qyF z_7O4g1tN|+ierdp^5L0BG$1nr4vJ8Fp0oF#(xY*`4gA7OpX}Ok@v6yC7=IEra@kQ8 zCbV;cdF?@e*vxEP-Z4Z{GuBqQ{7*4%wZH7F$bxIk1K}Q9epC)&y{QIIguaj6IbZvI z2yW%;*Iwp;3X&! zPzk->HokTu1)qFKJ9oV!L7tP^kd!Tm&yjuWT`@(alj#zh6rjPbCKS#!qDcZ)@CC*? zOU}Welx+)$Iin%LAYvfNqPTFH&o8Utjc7L8C*dPeVZ($x_i_4z3;)iL10)XsAR2TU z!2B1&Qry+bHV@dAK`ir?8vM|ahA41YCc+6FwMd&3_Qz|5phx@q_dj57ZYrbDH^aA= z5F@Hz-ZWpda6|>I2jZ!wp@G3P35hfa+jXexmAk1GEmdWdmU_iiz3IpUr@(`Ib1A&>UDgGeXTO<9C@98hHh#@dSFg_c z!MGE@j+#Fs3-Z2YCGa_x*n-9v*>evUe6_J^7QN}!%WKjNTjG0*2=MIBDnau`I19Xj z8qmrXF^Lqdr9P{@s)Ajt=!W0d=P#Eg>fX0YK84Bp6iS;$bm(R14QblGpH?g?!C?t4 zUfF$_ydJ`Sekh!eno5t|tfN`3n_RIs*1y%Ni+!v+v&dH;v{Fy<&Wwg(iq4_U7y0cG zH*1|9xp?WNkKSSy+8;orQPMiaq!tvG^GVThc@sCXURWW$&cm`ZkPnIpl@K~bfy@#_ zhIEAt?b{aa^I^nj4Qrj`zrQttmx*?RjU)`DI?%$`IGA#rU18$^t1V>lISCBPyJaZi zBiTBpaz&R7?adpV&JRrPNQLC-f_2;D6ou~VrBn_Vgk61hnDB!+!bC6IP?fQbjEKWW zpd;X&%BI4y{fvAcMrHx>05@gKWgJm=fbq9XpuQor2}&$943tOEuZ8?Rrs01`pdh{< zD=uUL8A@lByo*LS3AGA~Z&XSxID|3XzGnEH>L=6YalQGqILohT)+@lZHup4?}` z^Q&Nra(XzuZ%W|eSBS@{Ov2&yWyH4;Hali1PP%J5Y1dwoemfA8-Km?{fMa<^oG$6a zpT2+QH{y8MHQfAnXCd4k_k1r9T~NxDt=-`3;B+l`^b-&)N_ODbe(8_d^C}6r23NmY zf(7HacbOp)C)6EIORhL8cUqnm?wFC9${gCs|DjAeQbU7_yLOZVSZMg1Xm!c%lL?71 zx;di{iT!)4I}?YB-4=Yw>`|f%@1BIL*)4Ztt#`Z^8-0L4Q#6oaj->!Ci1@!`RDGbFte6v z?BW6hH4X}fCj)4viAvGcgl_woc3<`ow%~!N)nB6la2QTs!_lQOkv9f-t!>dO{)aWY z5)n_{#6_}&PN<}zh%lcwTQFj-kXzJ=wqBy?ZACl+^EN7hxO?ueIeZTyP$5Z1s~uWp z*urd}sJ@UuE4yx`6A67z?yD;LqA5UsRYCir#=*k49H7a;J-WLq6T?7Hg6P1sILN>{ z6_ou)&jYi1P*#pJ@y~8$60v3xhNI z5dy zp^Bb=h+S`_KMfDxHx_^2tL+r7)yx;DIQ4&LwDvVz^W3m}o>BDz&QIJK?qBEfc>VSy zG+z+bDApXmEWFf>6{zF@{V)_EWu~PXTe=Sx`Yisyew)%48c?G75?^K`RTjpd^`o#_ zh+$_L*w>@*`bI1y%H{X5`bjMXq4rhV%F-odb;}Z5wevsirNI}GD=BF3i>Sm;P%K~HvZ3euEd5&rxiA$x>gXOu5 zRjzCZj%`tI(H0Hbf`bWFg0lM**FOUE{_(a%MX?5 z_{!XI(?9H;xp;KznDeZLA$kw2^lriQ#q&oVw;A?GToB8N7#Y08YxTdAL{Ra?hCeG;_))3|E9Ou=o^_jk;8zO) zVY(wiO!(PwYW{$rSnEQqt(tc2q>;orjT3Q^LyQHW78Yt1@>+K8lzr-jK=;4YZv&7K zH6oBjO1WsVMz$Z8z3bx(SGBaiRSwu1Oc>2?ecQ05Qu&1*4(P zaI*WDE$)=2Xp^hQ=~K5+qc@ap%7otIxEAp>$Lq#Xf~6|t_A8qEjSN?!_-4upUfx?2 z$EOa5YuCqatJUN0*fhd!2STK@P%b<3bR$z|>h>}A9!ogH0^%Uq zHo$$Xk2Ut*TwH}%k2%fNNp3P99dK9j( zGetwYos{m=gdtWS=C)_1N1x&YK25PZ5hCeyA|Q4I%7vSTMrv$IGTUwn1#i{{yYX7% z%em3w4q%`%oPW;8?x0Yfv5)EI{K%*EUvA~V0T~IB%Or5;l1m3zW;oprR#K2~wp-vB zg!l5GT5P;D>^=YGwK1VaN1_j}XpLYRF+r^F#kyo%UvbEIjs}x!U;dc$^#!^^#+@pu z*C2E}orQo?w@pEW7%_D?8+mcAzBcv>9>LG^xSs{&7;{1)Tk~My;1&9yMkfjjprDN` z81hpA_`%$EW8}2VJC&dWHtYJvFSgHO2I2DGG&qnTW|%EF?c#G3jX8cXcL7bHYa#uA z#uLF_1G90(e{OzGY{Dk(^tEVxoxTq{4hk|Q?t9LnGl8V}c>iBf?W3mv&E3dY7(*ll z^J>3s%8ejNo&7c3gT)s1lQKD^{D9w5>1HI>=ZD^IPDU(8e98npml{Ct#^#WM%O#pV zM$&zQ?RImx)n4njT8eOnCax$(W&HO?233~@bxdf@7l%@o9KBQAdQ8si=9HLvl7H!r z=~DE)&1;dwF<>>gn28d1r&f4O8TAVx-QVgj!Kl@Bo83*VO=s`s)rJB3oT+>Hbz4`9$hv&hXl1dd#-|%d1uJ^J(Ekt8`hFOKqZBYlfGgA($=KH|vJGEZd)? zUyY!J=C2k-SbsBHi$W&cnM##KpLL~4=gbc);#;){zl^?x?T_>rTkTk*6U%(P3FA_4 zHt*V`F~{Ldt|*}t7r|Waswmflx;+cMWw9YBQ6+BSL2j38+ozl5S;3i?bbB&yp&`=3 z1)e{iwFvhdS4`(aJG^5vf?|r0N)e%v7|pL9$Dq&il+UXh^%h`19KmpbFgH(Y!4uKlrfFiBPfA)NeN z`NGfYGA>n_jFGLZL+!XbNj(e@h234!V@%12R=D?Bw~XN;*NEqL#6YWjlC#o%RgueI zG^C$#l9))`V!Y>-Kb77GP1-Osh<({N7vbanBf@k3Pi_(+`7Nq0r`$mplJdhd6Yh99 zz=4%ZKa0P@lzR7ZA*OiO2RWx8xiZRJ3w78I-F3f7vC^^ZqITIEgkKD~`q!^(9`rbM1 zsE|d~v7IFp@N8S{i$(4n=qaqPbhIbZgLZTNOoWYd+(GVc$ zgL3m_uM?fsp$XNdQD*vArYK%m_4)ijfy3``QkbzXy4 z@kkiBNLSv2YxbUBps6^QL6t5`h{HGY8sC5TXlFj>KJR%QUm}Bw+9q+UZi_1Ww=162 zr`>RoDt+74v85^S9XTmS({3(`eY}#SGBU*tlZdtTjq~RI))!$&VESDTv;(txPAMp+ zheNhVWS-8N{2@N?{pgAnzHSEOAdsRp{XEf(IZ^rMCFrW<%Jzol3z(Ot2(r}{VlV- zi)s99*LN%9aY{p6zQdWHf=S^$?}Y`M(MulKhZpK_d6 zM{@?tA=#cTXY%LEB!xZG>GGJ0MCaJn_y}PqC3Sx92al_jX>EuHvQ{jBVJKApunQ}- z_ZBlk>ird+I3@|?`$mp0oM}6{xGe53!WzgCq?o74c7!bqv?jG(GdV<1Iz5c7A!W@& zME>8$PKtNdr@j`B5soB=RS0F8yX8@_Su~PQzSSVkG(VzD1=h4pfi>t>8J}Uo*FmVo z7~7nGoSigDu+^#m^^4VKn$3k}%8~2+NIK zV~v=qy)_-%C811a#|z!G&i+4UUv<%okU5@$V^2{LC*-a_THR7;%Bx$qIP^(`)DsNt z_lw8zteXUjf?b{aFSkQA=}w_qkCw}+h!am6ZOqp61kg}wvNG8ytKT%bGCcg*I1O?*z7HQ(YzdH;(s5w0oaxD4)IKDe8G_Y&($nuV{A~+_iMnuAcs{pd1 z0Uh8+CV#Q;f+)!qEkYp!;9Ai?*x~Iiu@f!`6&d~~Al((;vQOHuAe)&Tfzl@x6W;N-=c%<+-#U$t7B)CTp|({O{n0+B ziLwCs2+t*(#xnaRICS7d*;yJ0LHGJ)Q@QTC5RFCxmQU?tB<^EnKjidKcqz!Kq)X#R zh&p&MO{Wi}<}U{Vx6c_F)`HD6Zlc9@wK#Az>Znq0{j>vm6AoZPSctXk99hbZ3E7i( zwvM3R6a(&q=(R_%-VtJq)Q>{KBzWjO&O&~jLE~Y=1o}~u1=0{)<$+&xM{oIgImLhh z8A$-##pXbo|57VnmseTucsf`kEpp1K7J869nQ#PySfoK80@DlGX2xu1Nnp%s#B1I7 zI7(7-wiD0R85lDT?PNgi!?7Y~8*WpP*cz}#?ETtDrg=|@wgz6w4m(9j&=C55u4rXeG5)!diPjhnw)>12HEA2W8>=xl|Ft~0d^lq+u z8B9)MwXzwu0v2_HLA+{W?>3JR=uPSGIS2qhh>`{EM^soN*)O?1lahtwhnNA6aB@B- zn8>EM@H?k?yU>s52i{4r+4&`0M34e=A8*Ah!P?Hv9|&E*U?^AMuMiTaJU&lE|0Xsy zUD>cAzH+v+L$0ur3^$uyE9MHNxZkM8OG>WooSMn}lY(Kx8F@b6I{qly=hql`=RKxP zVEr@>C#?%k%DV!5Wz-W4V-kGKr3p0%=M)xeVmoj}w&qQPyysyg=%DF*xbKX--aAo2 z{>DZP(cm`nK0 zd75Q2s^LC9XJCoEXnE+YC+RN@Q~!HMr>Z{9z5JcPwQ`(NJ4WBD z`^6`v}HF3+z;0|`@S9p#lW z4(%#Yi}O1Ejtfi-FVVN1Vczj=zc(DlpHovf{ib!8Vw1z!BONUSH8rm_4|x5C9GgG) zX<)2f9oM1^4G2c7&ho@<8|wvsXn!)B2A1uG-CS-f>=#?f)~h+`k0qDE8dH1wLv$_e zXI#x*zDQy_@O>Qcsg$EJkXhyA`9PwDsC}Jr?UgYCi{8P;AWT!`MoMzi!IGI3I2Sux12>vYaV#&Fp+&7xn zLx{xf$c1Kdl>Ik{aMkS?Jf3T15ixI5jo(oR%jyueY~Dz$CpY#-A~8 zX|CI-G2+qU|6>7;uzKaretb@5R%(#f0*mts`ukOrfB98@)2;nqy7!~Ko0))zjEGYG zyaMG6(2o<~dhvL5)~yLG16=>A#4RYI)K7!~IHzuy1dT_hBdHTO_Yd#=f`jPl^nB@6 zA`)3!f*Dc@{^l?t$Lfi4w_gc?4~r;+WNUuOCwo?z^QNetUtdIyY>$okNMp()c1@#|$)Kwc4J#1d033-bLHd7|2EYZM2!_K0(coT{>GVmd zjbQ@j!Jkf27%qCxlN)e1V|ia>s|e{mM>zQi;)X~m(K*AgzvH(r3E{|rRT*1mZp6ArCh$T9=Yy6%2ank20$z;>yL^D$3*t`J^wwlroxv^3Q** z-DthsJAX%v5@Uv2Z9PKrjsj6RwkaFZubFY|2$!4xYee?9fGQ)=F_oer3wV4=&};T)d-LD*8V{gx zMk_K-s^_up)ho5M6y@pJcP9JK1KX@lF!!PkxwYg6#qlag#Mr#FTF!qv*Du6E%HY!V zmNeWcJ>lKz$fbYfL2HqUbGh&vcm3`$E`IyhvBi1w?HM_vQsjLN#znshd*olrp@`cu zQgaqDdBdB0Lxn*4%_yB8w?6*)yKV0l*DT0^h*nf&z1)1W8IFqJxJ`BffqSbHuF1O>LqJ*4irm#vv{2F&(l zFASSwnwQ{1BpcOB2{Qn`8%0{_X+aPMi9D@?|C17dobY2zhdCO~G8#iKd|+9bQX#UM zI#@YN1`Oy2JIm3;YTqwFSgfu#nlEiY6jwgZ1_v}P^K$TEuaJZ^N~tG=T<*VIHM?_W zSg29HQ8hnzZKbkb()>iX&MHB2E~4SjNtM=%vdI2a7)l>7`1uhgvwr~0D$YN|HJ-DLjl@zgr0nwj}KP; z1+Ey;Nlu`I=m5o##!c+Ih8Ajn@3o$dQfE?NB-xNdS3Wjv2zl`XLh^n%6?iF3*b#@i z*R3?_-ORlGN}|t}>%_OHirdds+_-gXRtzy-HS5U0sazHqeHeCLrs5f1Sn^-J(qmdz z&8$A#tbfPl)hDlTDpR6*+jhz6N`1QF3wk9&1pZ3N)ukM}Jt2HmE>JQ-OA{TdkP#{o zx_KvVb8>RUQUdh_HPA?XSK>lIPD;t`l8>kk$y-+t_|5M=JzcjK`5UEo046^}@W8xt zk}7U>*}S}&E$svEk6-=Xb3P9zpSc#r?Y;dj?@Hgdzq>AYl8zR>inyDOJTH@UX^X;8 zze38w$Op8bu~8>?u1@x=GpV=)NRTizL#7;x zCAlh`fuIhCqQ>Gv5E$*GQhNH9-w@u~k(k#W>!?J>~mr8*LX%Kl+2na1wKmEdW_#g5TDk=-^a~xP7xR zdo-Uo5AULJ#ZBeoj`o=fqI=vg_G~kpgG+LlW9JopWc9gBCmn`>hGWw*vpAA72|wRp2L?~Ja zTp^wpf-*rN2^e%zG0yr=Ld*~^5L2nhuPFTLlTGoX$9^gvS`pM=ZW*NVHw26wFm_U4 zq8IkBHBim|r1Mft7yo}WePci+?h|gCZEv>QY}>ZY&9-gZZLSTw*|yD7ZLX7zdw&0W z@3+33>dZUu%rnnB^9*4*G6PoL?%@YH#a~V?b_#{}r)#!Zf2`D=={B>2M%l0Y0M7?Z5tf$*(Q>R7 zwi?@X5hsAgErZFljOB-n!?d}PMgN~_QGggQJ1aDu;)Sr0s)0YFOb-X{3^5L ztdVA9frrv-xjLcp{$-KwRig0tL~v5^O#X{e>lB%I+8yAiqT$Iseela4QkH&4rBYm} zmp&k)Hnn&W>vLk) z9R@XlVWgqU>>OQzyQZ4w9I_&Px;UgW>mng_6rj7zZvT97e|(olvp#Wi@7ioFR!wEx zysP#2zHU|UFtOw4F-R(h(meL56v)Ml$X{vhN~U1DxWxVM#DL7F>*9}WqO8@5u0CzO z=;^Eqln;{)Q(8}jungM+-5b-O{zsz@(ccrlAo0lcE!0)I~ zYI`d2-CFwrMBPV(n$>qXNwn{;Kc(by;5-y%F;CHv%Tr-^0p+el ze)Tjwd#0m}9I%L6#w~|jXyM`5Za;v7Ig~ji^fPrdSgRF1%UeXdkcGQKa+xa2pQl!# z&uV4kq}*Sy-3d)gC|y+0zj7=?%Tm6duRo~2ci!sn%4qaYsVy<gmb8_ci6TWz38xN%Cp5ZnVxe zprn!T%KI~%?bUxqVCJf3w7a1_z#sS9IBW=w6)cKLWb*Nl$NR9*L+^g%)jNuQ8CGd7 z>CBh?34|nIfiLQ*NEH4R#^}=v^PX0mgEyh4`}ag9e9fq?B?#63QP!kfK2c4nElgHd zLWhTAgbr8JEzi|Q50B#~gt7UXlDpSEYECB8ls!g?6?#yr!t^6CbpgPJQ8ag$5+&?( zy>oDK0y~!vMS875#lkwwk@C(&R!KYmWfmnpRBD(p2kNqKZZlD3z8T4ejGy(}nMOpq zdpg0{vU}Gj626Z2@F2)4JyBX@QH>KR!;Qyv5bx(4;eVzurnlYsH9oM0!E&U;AUDFFmU z0UcSC1j#UVxHAJEYO{qM5Bc;_-Hg!TIc zCN#fGQPXFA6ZmHAk@|MJbo%T-kt2k`_$||eGCGLm$Jo~+N)tk4(90(~^*GGvmpc&caOy;v}+&ImY zKgkeH^;t)jIw!6o{Pty_*wYY*`gROUoE|- z2`b3GecgS@aqap)83i4~Nd{nYjK(Cq;>)d|gH!$Yo9>Z-P^pdLGOm&1-0F6t8i3U3 z$U+j{fT657juNhHaZVD7f#zuO3WTWzzNFB=N+=>!VX4OG=K4 z@3Ft8-;XwdX=fB)FTBWZ3G-B$zpM87r1$--3c0$CANXW2W>wo>`K$jh2uybUst7R^ zTjAg3hd-D`7-g@QFLWTt;j|$n{+3EZB4c10-NW~3-?icwA!_VHPNL8x=i=ex$wjd* zc1y6f4#M1CY(odna8|N|$_Rg7niTKM5B{r1BB&(8zTt;bk%w&{z-^aD3G+r!!(jJo z8NL4W+gmQ|fU1U>hZ$Bq{s6VgZX%QHxfNpiny?QhC}jjjJr`RMU!w>;H98S!;~7J{ zslpz;dbY(Tcz(Sf$EHH_FW)m7Wv{)ixw_@Ec4TqBpG}hO+K2+0rWmt(GMH&6UK07f z$ZRN3+=Wt?o2OS2T5=BlxfIF$vohd;@saG|mSx56G5LGMT>E7i9napbb=ZNlTwOe& zs}Zs;ZB0QH%@|!%GWg+ilziKQDND;`mzP8(U$jp~7gHIM2e|{0N{$nQm*(UJ85QPS zAkR+~nO0kMzKR)!h6!#Nt<5Dlzi8pzQ~tv<#qnMmb3R=;VlY`{MvaK;7~v}c185S* z%3pPeK9O+28|zMg%5d~n30IcD`jpOzGChegtNn#&=nc_t^RZGl+pl8k_lj#ryqs-w z%loF!1iIj<*Xa2aNqJe+_w?-k(_R5qi73aM;tTFnB>NQWv+~M#5*5t1tJ6kCB4;kt zaEe>F`7X+l^YxLgnE7Sb+|GjztSb(XagtSj;k$g&T{ftRp^5|D60AhM%-839eJ`{D zUb5U*1_Pu*SWDBti(~!%+SH%86udlRN_+fdY?3&|eQS4Zvu{5i2%K#YvURT`Ri(ym zpFKZGsMkieXn(wufq9jw+G;3x>n2h|TDP0tWZiEj zdi&16_CXnc^UaqHHj*a*ru1Sj!QTiB@ z3aw^^c)tW2G3M{3E$>80(akex@|;Lyy~QNJiGA`I6lL+6s1a!>s^P$B!!d#z8Q?6u z)(_n2RR&cd4FkaB3F>pjSplAO_~@G@!cC|9VBffTq|pvV_e2!Y>z``02IuuPQ1lbI zP6k|!{M-q)wMgbwV8aHzE{I@?z)g*xw7m>H`mU~6f~ZJt*VZk#xnz|r#d9si*FV9e zU*WA=g42W~lA-^W0lH&f3DQ0V0`$ZKQ+ud8h#yE+!jLQ4UXqkFWhFJos++m`8T3pt zP3TFrE#cJXdVet^od=VuS=R0EsAG|vc&Yk54DXK2cAe%$ju50-ioZor&gsd;)13;^ zDtd2aTEfASP%cLnzVhf75QI%|OTuciE{V564PAcVvJ(XYe3X zp)oTRtVXQHPIH2bS{jX9&CnuMxL|3sL7U~0ycz&dQub($7-dBR8M!K&qLl9BhMN)^ zFmNp>7l?L|I6|-|t5Q1h1DOsJUti8mUs-D2StQ|vzGOlZiUOb6d?a#@#889)CKxe3 z)#R|m+)P?q&BcYJIzj^z-yosXHx5WD7?1AAD~Ox!;@eQN#k^<;0YM{zKf^{ z{R3{=(}|!puMMGf$IS}FOF`I>pcRcetzJ3~Hg4wv{D)#0Atl#vxt%YN+7ABu{-Ryo zHwnkS5-RA0LcT8f*B(KZuF2!+#0YEX{eefIXc)`AV8C8)a>%5BeEr(rd>sI8iNooI zdU&?5jC1|^Cv&gw^c0c+uwQ=H=JpS(62XpY2ZT3R?Z*7sEK)`xnFZ}^M*Aoht%Sv$ zK5Sdigu@2utMus>^W_MCT~h zIcv~zIft_k!cjX+;lFBhKw523#GIU{l-raGp-kw=v=p;Et7fF-G&wyUF1>b$aunY| zg-vlUCN^iAy7vn0yh1h~j57u*?&={c5$_$#`!Em%PO^k%+4{u!eOkm$D9ZT)TvbD3 z1LmCQaPNfj<67$z;rB(~B3c+OIi+w3Nj$eK4a1G0t4P2DdE`U3c(uSPD~*24s9#5= zAQyb5EnEqwVNX$Xe%=`z8hqS6do?8mI;lHX z?qIU}BanK=DWTw0{{ZC^Zina4Ec&@BiUO@7*#@R%kU%({iM4E9UpD#Le&6-lb~~D9 z58yJ0JBSL`1jlnLfZ1WKo$~G@taa;Q(3miMsDL#O+_C$dFqk!B-yC1cM@E!`u_^lG zSwLO2f1=nntJEN=Qpa*lKI)i-TxvoM?}{DJLD;P@P%G^^T^9R!!4|Dnc9%2fC+*O4 z=O)7c?-{1=Oc2JI>1aR_Wk#z%tv5!oG#6e2O%afj|Kug5V zxxZcu4{4&%b@6_w>gFEyPS8>!Qm|B3O}PvZ5>s!5#!(YUspR@79x9cFdH9iq|A^rn zL!DSKn;;j6+MIs)<#>ALb1n>67=k%*sEr>e2c|ny3*n-&FY) zN&=enb#$P^CtLE^yGGDKXKnYpgYf{5ZGkXc?tAyj$QN>`u(B#Q&F8D{7xPxY1-YU# zdQg3im`f>Wd*IIpUrPuj=M=D#)L@i+qSqCObjNS)L-+5fO$sP=@xZR%+eiDD^887N4$@Z(`X9zAqfo6e0|fay(kMEOyiOZ` zf3M&J+(W_OF_nU*lRP=)M$%%5z=f~|#LZnoq zVMlRerNbP@H1QJBbZR+2kK&taihswFvO_PzvO*JiUzyDT zHy0Nx6mR{X)~BBEzqFxzCP5SO5S4VKDe9zTyuSZrysvAB!O%cz4mDsGWK8R6Ovc#? z;<5cQ=|CfWux+N`9Z#r^-wf|FmZb3-MaQZ52J}?gP_G2pP4M?8cZ~FN2eOBA%BpM{ zWmOTgDs4Y-=$65M#uS3>V7zCsWP_zPnzEu*#{I~a-=k&;bd~KC7N-q|ZK06KG*OJI z9$aikoJMF@jxV=3@2ovFy6vQSQN&0}0CR79L)wisE5!f{+H~mn-nw(m2^M0;$F+C0 z*~_dx9kXB0*Mlv}+})^r${&GtJ-AtY1XpL#c%q4$9RKY8F` z(e7DZOlD%eHQTOPI|*uAH>O2rdZP2uq@z1Px1Z?9sQ3*2w$+%NLX13@^Y`I^r+MzI zq9ZD0+z+sX5iRG>2WsI9K(zDcas|s{CIE{&Pk(^EK?tf1PTk{Xi{5C``H$MrLxH`E z3Y}+l>YOjNtv-HE>aVU_fTnvjsr`GiamTA`hpE4hQ`CN#r|fD|^LMwaMhrlJ6gdo+ zY8%>~{*ERV#*u}O`QM*>Cb=S4ebP-2_8t{ooh{EYsp1}J0L#ipR~QBQ*WW4WC#QAKVMbl zLC>(DqlWa^89x;zYG=nqTF#P^0gy{GgqLPlu5_0f z&L7>d`S?fCYE zAH!8p{aGp)$_2OZ5qf(vHHs_|l+WcRFaGO7K5%u#By2{&_ZMOgl8-NsVqL1#ON}Kj z&13|z-d(N;XZ1P>{e&mi8qN(wVdNn1BtZHId@NH08|ER%6`@RuGVlfONERY<1Q!Gv zv3Xac_x}9@6@R1v3mKdwhfG*Qch!BF|11Z5=Iffje}o89X=_=%?t;Ohpih6E3U7Gc zZM)a@sm10io{1D)XlNIU{{OuIP_;q3@i}#M*E4$XUR$9K$v2?EtO!7ev)6X$GT^!c zoOb|ph8h%bw|ia|U*BQsbkWCbZ7h^jfQr;bGyp-(bc!})1^&^Xgd+6M&y~&{kVMYG zkU`tOk=CH->ygb~7-|oPZ?p&Xn>z_+oz$!f%(2sMF@E#T;JiPn=n&FPmW!B5+=Z4x>lnUY(%c3vM3pPssIwp~Xdvi26zwVVJOI#K9MM*eUx zTC<`oBlI{0gMeoa8iE3WFYDV29l@*XSvNJ#`n5tuHyN1}vJbxwnJlXHY%#X|^VJcs zpmVL!cBG`|j?#J9QUerAW2@2Nd{e8;{@S&5_xKo;hzQ>3A0TGsMDfK*Fxch?F7#25 z9D#lR%iNue%=JZh!~Rr1F@l4CBoA`;h>&LRPCre^W4bkS(&#=$;5*mh3qqnK>7#v# zDFG z&LQ;(in~?^2)>x-Otz8YW2CGOYT>4Ml5^<3pBB-_Wogw7+YWU0F}KQPOw#B?qV4cQ z6b+T^ov(Q%hX}tChs&q$2TCFHu{6{~F+Poi!Hu|Z>H40dNJ?;td6U4TB7>qL!n~K% znc27bA>Gm_geNb|)}6bXVkY_w6lDe~?7P`qm*}<+o==cK0sJY*8PT!pqtn^Xjm?2z zJB|<7hC|WWcK!$)zRAyBfL~(VTLTgFHS#54Uj#1@d4ZQYUib(5%dTx+mjp#|$1_6E z!)Q!~0*SGiv!P>RMQqi5<@d*?X3Mv;X>UyyJ$2^Rg$aZxK%Rhs;3W_pgoA$7sq>{Q zTtfLsFNaq;{((tb(A15&@HQqGDgT0EAt-T$xRlpcYg6tzLrgSroAM~ZpTCQ-`~*Bu zGg%C{BfGiHhS#G*6Rf5xQYh4@k#N@MYpVH6lTW7ayBxfz)IG9RYxpjMB8xuOKFHbUb{Ubk0I}COQC|ZLkT{U)59{` zTLTL`55?c^N60>}b|Rpj>m4=V3??v~oKuLTdUT;-A!;W!TKBdUr1sJcaY3)5AO3kU zEcFmR+6!KiA7<$B%b~KN`3+-05jf~k*Gkny^v;3gPH?JIIJvoDFB-%)9(q9lYI_uG zce=NmxrfXQ3)Lzn@xbGaLj3wIYh3^XEnPeSOVm%Z;h!0WK+qPj96WSz;`97|=maBm zki?C1W9*@v4`%G;^&?UEOqivC2d;>3-c_bag>0W~d31fK;|P2{OGrSWK^p?*ZVcX@i014F2>k)B;TE9W-$9L|(u5$N}C!O%$ApTX`h?yxif%I~WPXvPd z*9f=cTDM*xgjFAi1HO(wr%N6R_R?1iN0S4waF_~5!)uPgpbLbaX!^nsGL1&c&u7If zaEeN$twz3=_M0Sd$uw`BoxZW3tXZ3}DkfQ^+7f7u(mQ!Eo{SCJ0fq+78(4sgtwvZ(7P6cEEmC#Zp3qb|4&*o0{V!2hj4Drh(1qQ5PBZYFyJ!L>1gk~#H%%yOkJ~b zPv9C3p-6Y39El-#M{r=Td#?{e{bt-MP+&jvUPYY64@T=UX>?^^%X3hF`^O&{fo&j( zB2k45M9>lxNbwV1PX=KeEX2GzS`|ORT1f?t4n98d+3xp~R3s^BfY(oOv_2zZ6IQfm z8NZyq@G#!aN>S3Zs?HNDGMFe+NEk0DZITk>1R$}Ds3gUfYn$dhL5{bfyD|6wPURtj zYd_=rP6(ev0D{L0=a&EIrZDS3w`VWLKfnFO2YOn@aIp3vZ^5~hs4qV~ufL$NPFsO6 zW%v9``o2@R#!A_^lBE5a&ED^Qo(GJc=J;=)r-j1uNabM2qd3=R;Xq+&r_To~r=NbH zSXwyx&#qzbY@V{q<=T9tAPh;2I~*3`0#N$h+3EC@3cSm*Qct>XJyj^?O~`|Vzy+8C2(y0a{)s)dqO!S zYR~|oqS0^ewv2N5lvYBsv19uO|L*t&NOZKZxql93ALIT8x+g_+IoJ!bpA-avOWU4c zG^C$&234$3tExqx(!EeL!*`%FgdKrYaKg3KuXU?L^SiuWm~m*37*49*JbRs7Uo*e8 z6>zlTp`bTe?+#1$1d*30fud(LW<&ldBw&%^95#8VQVUM3G9)x$rpjshsLaO4PF^R) z=w9P2>;3b^#;lrN#2#DeJjJCZ&(=5vr3#Np{3uw*h%5WfFBES{GCtx;RPe_H;-Z7f zmfySw^mgBof5$liw}H@iXjdICN(V!YxlYG*j-%1rxpxp$YGtE%^+5C)`)_Ht1Uolx zVkr^9;Qnk9e*K#M)<1Axr7{+cSo!sn9b{i&mG{5Hn&Q{5S;nDbNum<$f`(ikMm!-P zx9&!sNG@ZORNSk*KGR3RsJ%{?t(^Q_g0zqeMjpnY2=sX(W3`xspK0b>(dQ$e6n${~ zUH=k?gmr5VY5W7zE~ZdnvjPv>vxXtV7+W)A_2BM;u2d98Od<%Us+cjc@`NcRL^+1# z>+G|ZYV>8eA0wGrqYRV1ubqbbAf;(fBxn%eDT{u5xl^^np0=nty0h+Vt6WnWH2p7G zY6l;SO8B;WZxU4#h2<;z&wz(VRzo-;&Jyt_IQG2$KKuUkd{4Xo2Bx9G95lP!w7{DoPNfwvZWG+KfgBM)}@*C)A^hd`~xHzHMBC4B8c7IJY4L<0F>7 ztp_f!yN5CoT##HvMR2kx)z|*3%O%|RAkUs+dJh)FWT(3c-VsGS#oVY!D@Rf5cb@e( zg$@qFWl54oBZVnQ54C&bFPOXl6)*(gDu_vC4V|QIpACZi7AjLkK)@CVPJgh&^?@oK z>%*IQta?CuTFkSx&6Xihxw=$oqTQTh{aIkW{1eoh zVDnA)Z}An`7qzA(+C#J?&r^%2@8^-xZ>+phl-wgE6eDNuo)|5|K>SrI7_XOoF6YZ@ z*apLQ?0O=DA$k}kF`f;O>cRc5Yc!~g&Cps50Q?J4@}q4t z0`&7yD%^MiNxopue4g2Zyr*Fv&ktJxxwe!qz9DXAd$N{0;H$hK%)r{2pkXpZcT9Xn zyFf(s%l_q2kuJLx!O2Ll;cGkNkuZ>pO|^^bK$!U`qS~p*Go2KV0j9%+2A3q{nP#>R zKPS2xhW20u!xPZ!4-|4Pi~8$f_Q{n5g0LX{sn&GIO1fYo-&3~R*@AcDJ#lj+sSlel zcSYwVx#xA2g*u(PjJ8d(=ds2^Jk~<0S=ab&c1SC-kelSa6V&Siv=OPk`DEd)kdrSf z;B+WOXZ_vGE)R32E`E2=36?ec+?q@fSep*I%-F4TNv*{QIYNuQS_T&KB07&p93em= zAk3uWY50h;8t2w{r#l+s-BT; zSKw~68~AUswzj)Ev-(a#%1y)K$d?(|3EXk*dD{jqsgy+WEZ03{luGOueo~nqj&J@| z=G5H+6WS;a*GD!A7l7)!flnPTchaeua#z_$EP$Pw=3z#SF*Qj31>bX{wN zEOWEnaOj$3+;}RAn+k9>+2$jkZ^UCk$*_>iNyK&>s=KSOl?{85LYf3Hy`&kla3q&IO#Lm6H@x9gyBO@ zml)WzR8Gppc2~VP_Xs|=6Z9yh`Xi9Bn0eH4NjgEj`V10(dwyy7c=^HO%S4HVrwvmU z!LZu1S_SUkoPG?YAKc^RYls4WA7IMUE*Jmc}~UGi-*xMQ_f= z?mbWt=v|PRV>iJf!PZTp)`{zh!t7J%{Qu{&Kq z?|*^Ev&N>aHQMb`foftRqFAb~{J1mQ{}Fh4!%jH*I^w_BV+W7v0TX(qq~%Ykn8!zu zV3*zgFk}}xTGrob2j7`_#ocxNP;U9RfTiozu#{p2hPbqeJ4gDq{gHA;si1h-JF+ph z!P(}BAk%6zD^GB;JUbd!h$=lZ9Jdr+hry7LLZ^2vWLry4!mPWoExj|wmV>eeDo0GV zj%dvG2m4k{(WOqAVLyRWH{BeUs6X=R&S#nDXwV}+3B6j8<4|Z@!MTx+<}tjRRuRg3 z9{n6wHmNhz>p-kwH+Xkj!quC*!ez_)!RCffdPbwnVSLkVdz_{VUT4J9&FRyZ&opZ6 zpcMn1ZuLB&T?qZ$sz4tCbHaz>^K`bec+<(C^|uyi&PW+o44D;$SJ|Fl`)$7n=SJ_( zVKy+1BKcDe>by(0(Qh>+?RxUzokt*z7}Laoq60<1<3laG@m_vxg7^bEs&O((G5NLD zH=4nCm4pl5wB%Q!$E)`ETq#G3;}gRvTbVyZ(E6TSYn4>(WO9(x0ume5g*wu64253S z|FW>iq+T1uP8nHcciVlxJh=%HYBI{wCoZa=UR2Fe9^moKjk z1ZovAuZqG_-BLK!&hpXd9Inp>K1ltv4Ir2@rA8(cRTvjluh*X}vTeUZ(ycNi4kk+9 zp*O&r;`CkCiUA2^XuoM}R`>jxCRkQQdC*coIUFkK7 zV$Bb27*y%eeIc5(Nfb;)=GLxws5p)+<9|4U?n;4{?p?Yd5jx=K`~iQ%ee~@gw~RPm zX=&OhZPb zBk0(3@Zx_Bm;B-o<5)&xQRrm$#W7bXKRslO{^>Z<|Bx|a{FHU zuPisW8A@keyD2Gk!;knrK8tw8ujkeqE^65O{nrtr-z&tI2>i|h*2#G!{aj|E(nVFp z95qp^69Y2OpD}JyOETuXuR152t9*5FML66)41=Q5)v}HQjH{GG8m|fQA{1@DttVL3 zol%OEuqQp#uZ_^)cnBJk%Lh0@|Nb?Kk?kMs8!E!$UFw{)K;(Ou3($ut%Gbx$%Ef72 zimUhKWF0EsJaJB{0cLqEc{o@?bbPFJVL8J3e!dY|wDp9F`CeH73;1yBq~6jU$ac01 zh_|OL`aIo4w*esQYZ1r4qDDG;M-AR8^JwYGRvm3-dimaJ>=gLN(k~w4b$X@9H9NB0 z%-FQ1;*MtnSL}f(=26KPbA;zX7$=jS!i|Y~KL*9ce^ZB$aBzv&PA`5{MBcuIZM{7SX+K}Mhs3c`as}jbwQ)aTW281X{TJBl<1nIL36}9q ze}X#~>uH$v+cvu;;A=}0U8dMuiQ++oz2G;ydaE{$dh2t{;j*5^3wM()+2X|)dpFe@ zM&q#lR`#ig*lF-5ckW0v>Z`8=Qe`mrG#b|RvVL;^$0`nNt@ z&f;JlyVdDbAXu->{vt^I{kK6(6q6P*`#s9qzjm=EW!jyJbhCeiq(EUiO`B<$4MteT zeulwUghzC`>L)jX#jG-FG9mRC1IutLo?wy*=>w|A%br*W->dzj`A~ulR}^2*-EpS_ zDj$S#EDF&I-|cAq-{O^0q9uHEZvLJyf08lIkU?LkZNmFBq>X22*iND6NHu#$lY2K8 z&(V;3#?LCI(Q{{Spj<1!0QsPhkEvEydIJW{Cfgynw^$0Fg)^CqP<-DY2E}Q=gQXS$ImpywU>&kxU7$M?Fgj zvZu2<%Wuwv5gwTd2>`*E=~2D^#6y9%5EDsm-(@R5IQSF6`-}@X2Y9CCESvW~f(q&c zSMMuEs)nxVmnz2v+i6qGVR;zsb{k39ZwkIW>~7IvN%d@yW)4(W^0AQdrYnjCXCTDK zp6X)Itc>mZCux{$@3iwF$s^-$1EUGBi7wFne=bTupQvVSwp5x~<32`lAZPW-f%4Hm z%Idup@{=ny4k-fTgkIR~8mqBwTSO4S+dij#VQ@OdLpdLCC`Vf58>;sGB2|LAXKji zgE@~#u3S2@v)$V4I~ss|*_B8hNn|>E{qtg@5hsLdG+puCkL(HIOP%&ot0nSBRJa7C z0~d87LLz!nij#DikJ5aL1?G{K68rvt;YG=RT>ee>uglp=4Kwu-SsbJwFStCQ)J_wiMRj!`f^b0h! z$V#^>H{|QAj-3G)yjk7|aepoNI6h}23Z<&C$)^?a@YJ>*rN0or_l z2k=1_xzedNv2gQTCkKf_CPHuNsW0j6uJcAT>&Qd6JkZHxqEQ16JEiU6awzql2sjF> ztu)bB*i7H~Nw9?MaS$@kIL{r3(@~doE;Cq#aIB}{@-Pg_xEAS-m=iBJLzxh zdIVU%uN?A+FLkheFVCKQ{_`1oW(F&ZIU$s%AqLg1NI|{KIkvB-jUM&muAP_J821yG zolF02?Ft8XCfCo)RUXe%ET-u^6Z=~Ws$Ygy_5PH@q@?-nm5nLx4dI-k*fQ=qSyEX# zTp9CiceOjpIY^bu@kglaIW{I;O5P3$;DmRH$(o7%l)W9j;+gzsXk==I}l*@EWZ~bv0QpnIA7{HwcHFN$4 zNW*D;WKu{;O8(Vi9dkf4aN-P0VhkE!lTuy|zzf`Yy%5%n4pmVBlB?&K(ua> zwOeu6|NDXBviS)!*^U|fGGj5q)H87!eIO4qye;S>W}_^Z#>L3^sFOj~6_ygtx7Da5 ziif8_o}&LyW%I4BM!asFWZk#;Ulgi@;x%DeO)PG;2R>BpAdE2Rj5Oi{id9L&U?i~8 z`LV86-X`y(u8(inW$wcfg4y+z*)3~Bu-DB-FRmsX9B5ca^!g@xbqnqS{qwe%P2Kq> z0#;WVMVW!=<*6wOj@UXzLG_by%o?f&oQIuZO+PhDz}h<0OMadq!#93=vMa?zoTM1G z2;nxS9$(-p>TIcVyUdkm58xD6x>o*cP#c}p%6fF1Hlts}kvU%g#9l?8eoKS?wyb;4 zy1cKO8Zg!3H)5JV@a6Fq?Ah#l=;5&Gp7z1DJ9+uG8>(H$uyIP!@?UBu*yI^ZU|A^7 zh8v+aGPA#>9H}rodOxI>S5Pt@o?)S{{nrt27flp)>AR>v z^4UX#3Nfo~&M-oOYFwdS<4c8!mu59xh-0kW7oTldE zPf<3NgL|%dRup{eIc>W(lWvyT4O}ao&^bI1f%^@;UqaRCjtRf)sIdAg+s7wWK{fd; zd0Pk+g)OU5*0cU|LmfgR#_RrcQ)up>v%zs5GZ4g^bqRn7m(sXD&ja=YF7A27Sj-x=~UJ|6q#fG^l5 z5ZP0D{BIG1`3DEr%-S$UUgtf!)kqKAiln&mq;QS-uk>Mggk+y|ek~2&pT&#FQ&Zw0 zgf*lazj&66E7yy5rfiT~$7>U^P4IohMFhHplQ1#X$-+FVS=7qGVxYc}*%i0`(LM;hgufTLJb}MIqvg^GCVF+qdBpA>xP=cUDy%Wp zFdRb%Fm9Vg3u5p|v@8y`{*G{?ebe>FOL3!oKz}GH z$@#7S$;o>5<6_sH3&v4!qTk@@9+pbhrTapudn2R3&YiO3e;%6f?T>^hS^j<|MNk(g zJ3SAl#wqnow97HyJszIx=LqITo$~T%j$rGmctsMhhi)|;b`nWIu=LoHr=W?(xi`sM zBg5DtmGe-rxg{JjOt zb;}QOveD7NR)OVDA+>LX3_nfW%0tW`I`#f!)zlbs64Q-`Zb37m+L{gC_S9r3RsA-jjKuEiKXNdX4WU!Tj!~6zq*%cBU%&#)36k*)Q%C@S_s99< zZ)-eFiM|dW(s*K1Ge&^-e_D9H$B;Noy(4RU)I5PV@zR==#v-H$kg=T?>P-tr<+Z++ zA=PLhCWKj?d1w@6S(0P6lBHGbQbjwk>^ULUofHtlK9{-_WrPt1w|xw{|0Gz#R8bc+ zlEoi9L4Ui=@EMFa6}mOKbrODKl>M?VhSe35YV+93WWPDs-&sePVJ52aJ*yqwACEh@ zr-UfVc;v?MQ-L8e362AioIWP9LBl~kYRzQR!I zKAttFA-yPEo=(1ZCJw#-0JxcDt22sSeZ!~DBNd820!}p)69WlFQYZ}|n)$H^>r(X) zM3JV`wr+7TzL%F9?4Vil{a`bbnMzV=fU#A;#gC9=Q?8N2B*_Y~h&uYWZ0sylWs+{% z;GM}1q#e7O3I>_*bL3dNwg0Xy{BqyyvFTDR>4dT(>2BcVD>3rXKQO2DV8^p!MhlvH zF7w=LUf#&C^WE?B97rJ5g!o4hzt4qujNp4_8+W~T=ab|5AA%z*V~3TKDY#2bWKy|+ z!VJtq{BF+JpU$?Ba@DkKAuXjw-l(>8aZ4cleryPeX<37O!2RofDH~1?<8R`zSf;d} zlqtO=M~kmcbYyhPq<$nOg4U@gMP+OMnF)FH5jiI2qCJYGL&#HwauH_)ci75d`w6!7 z8_N3V1Wt@;`7yN-VC!%)4>W3k?V^4bg^#VeKpBEHxuQi$m3|4!f|K~Tp zpbCcwdCQNzD&Gh0B7{GY!rT4aP&M~^<1d!7S&yGa9R(#U&hhM>#sAI`rOER7LA)R6 zK8oP!!HPF)FVi4OV>^|#iIx?zuLkG*k(R~jm+}Ml7bW|M9Vz9(>m?FlABjV3zbBvqoOFX2+a1jk5?g z|A!tOk?v#nzYySTu=t(yN?ZAo2{G4wTZdse-}k64a4_GuMCaka`OQa#XbW$Ka!*1$ z^C#(H{P+Q2wdbalMBBpGIG4Ys^krhh@f;h?AU8Y~2kp`kMepFjU>V>wiG?f$zhaJX{a(IKK@4Ej_#HXZhskaZ=^j&aH)5x4pKKA+x5c&J)2}V}C5>is4NI zI3Ii@>xe$)GU*x4B*eR;PHb;AnaMyuauo>Z^cXR268}|3n@^9U-TWI>>3&}QAargS zK?aE-4_@eXZPo7oNY?qnWF)5MjPM(9rYDHCP3DciEeCp;&^& z%^q)pm|7;Ac_WTnmJgA0ZJk7Ow4<0)>$F}oXH{B}x@I1=!wIcg^8s!?Qp|<_c1o$+ zVmn{QCg^w?Y59I3^901ElPH20a&21h&C0wSABxZ5+n{WyztT;Z@w; zce;#7*)7e=!`Q6)w;V7sZ;C>S*uRXwvLKt}OD^Tuey}q%3Rj8D8HN-ZM^Tg*-kKL% zL5#a6m*up|>g%Bg4L@LW7A@2n462IntdPvbc6tA?nbjnp%k0HB(E852DyUe5@=Bza zDr`jWXwrlX-Uj@P?3pxV7Qi00N*Vjf2(#$k3g{?Ip3V~-bOE2^hAA2O$t5tAC~yxd z=@-2Avz!h|N^oQSP`6)4?r?{7 z+n%HCgS-E{YX|QxBuSK!J)sKD6$D_fok(%UV?9!;W zK7f@#g2&J2&0FS=C@Y$AcdTd0oJwP0`RR^~XzWijl$rk2J%Sk$VfGdqJdZO~~3N2K$hnx{%ELr#$Nrg|3hGjytR=AF4%tiMu^E{IbFc z`Y~yhFZ<794n~X&7jW71vSoHEc5xR0ps=r^wF3PmPca(`)dMSM)ivfx<10gBV{XCz(~u=0@GnTHYjVYxy5MlN&8vGITD5_!}XHbq<{ zGbAHMTSPhMA%Cj}DB`gDL;?vFUUljkr_?=l>dMUOaSCZgWk|A$+~1_$TK)Sd791&E zngnuE#Rj!DG$zTYip`s{DBIY(XkI->Y>t4%R-xoFR)gY}QbZLslOs;?$)d4|+b&Ud z`!zaU`DHg8_dmR|hFI1v9ZnK#HTxlTQBasLa%u>0*0J}6>WGSZU>GFR-tzWz{;a!3 z2z-aTt&Nx$RX_ueNiuUjA9NY*1%S%p$TqtUMXjQzeBdi+_fBi3!v&bQ%^<52!JdIo+>n6J=`OgQkRcVv*?o0gH#yF+1=GO!y-grQb}+#fN#>&(H^I)ZS+M zP$N`IPNx%Is(A>KFzLGO2lJvP6=YGU8tpd6rAgnCjVhPZi%m*-%Ge_z{&b`R&quO# zlpKuxZ)!F2#U}FmfeF2K_j9G7ObMlMjLlSHTud^POpV2d4JP<@E7W*!ER;)i$>2cDF0lkl zwK+6kVO9}z+p#co0_o%2b175wgcC~lPN`6Pp+}~p?uvmrelj(9(Xx<(v$5#yU3S)N z{m;LF)J=6?OlAoIPh^8&`@bvqOmwyNM0&OMM6?w16pWTq=)DY+3hzy{eBM}jExMU; zb^ISc_#d{Qw*u1+h3!=Jr5b8u*r<2=66E;!kG#GedBMrb;vYY4Jci3Tc|O!&GWhWI z%rqGmg%*wb!tgG|W6v3xi<&10k!-emQca@P9G|QfzaCiA&9F{#XSe#Jd`{QIM?IG> zY;@}EW8?(xf3TJsf3Bri)KDn04iDKuc9>F%4T~Nghyr~37O=O|TiR$JrPsy~{?iel zy5y(Cgi57EFo@dC@5?D9_{o<=lazS|`G2_j%BZ;3rr99D-Q6X@HMqM&Ah^2(*TLQ0 z-8Hxe4-UaSIKh2z8SGBZd*oYptpWZqkL>QQuCDG))emV!zvZ@Rd@z-20tIngr^dkjZ zoQIJ?4gcgiF-<09Tc0cqHWp_;Ra6M3I8HI)KN#Pkidy7^aX_EC7&ZZO5akDCn{0!> z%zOSh3-=fj{ao;FBg;p8$jl40mZp~v^5ulGeg zYp;D71{8=0-_l*t$cl~G=~P0x*AO;hYJ`Cm6r1;l)wiJbfO`7rj)T=)gd5Q>B#1%# z7lCcXzM&~tYj?hX46Tm-POy%C=f+3Z zFUKyk7EDvQ2e%PB*(83s5o4RCr#Y%JF*36y?(V#I!=YwO>Jchm3c&UkTC^wEWh7AV z>PIJ-j-=fK>0|h#-O{C-h0X1KJGd&xs|Z8{r@@@B9OQ|@jM*<4xO2@xhnRf4g# zvKN&B7@wf=9a&5?rsao8m{70#YgXRKX?mq;+SeXirF_KCSjg0uMw^`EMM5{nsQS4; zc}j}iMGdq<+Nm`8l0SA_8i zIOwBukR}Bl{mUFg0`HUi=A<6%#_){J#uPE^rdXq!YT98c1w_;>t7!#fo)170$o>Tj zb;2vQzI}e+FLWQ)&&rLYGY-Ryz9A!TE3im+gV>=VJIoONty25!agkb4$vdcCZgYEn zK!Zo%b;6W$#6L-kLDycZLYcX9f$^A?qCk+yk|U%5yPGVL{Iw5qEb!9lBg!CXv$6d|>tlredX9_0ayu4ZF?c*C zswyvnma?GtQ{3gRggj*cygSIoo^DqSahsTy)k(QA=Ag(6zyf^W*Ls zp0QR1izZ@#;045HC-65kZzZZjs`XTJ>y-7Rvk&Is`bibcfgW~Ld|A&SMc$lj^*a+g z0&`|DFUcbpNZBUvcooQcYInZv0Z;hX0@aH&ckur{j8_+8MPt`BeKg4oMrXcdRhC#7lEw zQPtk`dl>K%oA~k;2{4W;y3_iwj-c@-y02y`gCvIpY67(;d$Z!44mPBCJTK7Cd(QZ# z8|psW8-1XBRK-bK(eVHIw;}%42|yruA76CU9W;F2O^G zN8se&gHg=4-){t?&KQ||1C48qnRj%C0CxtK9a!KaU#I)qy@h5rFA{pO=6l1{)wJ&+ zh@V+@Lyd&)PxD$U7NO(s`Q^cOZ9CFK01c8%I#_sWKf^v$N83@)ZHBw+KuNKhR#;>N z@Uie!kL#-QHnOI-8sE;c#q--bV^5>Cq9g>E2TvOnw9;Uo=>uQLz?iO?lRSgwl0;ZrV{07(aZ zr%jH4*~4+Ok1Ug{Drke2l!-IENX;s)l;ODN0Qj=~h5L>0T@p>bvG-8kaA- zINMGdOHT=GRv*GHsYLDrbN_}(u?iDph*`-YWdqV;%3 zvrIO(ZX;?PJhvtG`BeARWk0s~(UdujCZk<_ZrjKOPFusXQK!RC6h!}4u|y$^q46g# zEH6DlEbh~V3Ar`?F4*Zo9NYV7f%nXHoii9qSjQKot3lNsVyVg)k`e_UX}oI+gL|;K zUE-cQ3G8_6hb*KhOl^@6u%Z}dp`C689Aavyjh`+VJnO`VSwcXV#eH1_ z#`>2c_kCX7+;K45n6k(?XopN`m>8gS!^`abGiX+M=Uj($rx$z-lVYg#Uc1V&?4nxF zsFh&Dj$+Qftd6~Ats8F#}? zD+=|<^1}xq=R}fgv!CMD?rTsH)3pk?BqZ{3)2qvmk9$=5fOxI-R&F&~F5^=;s1SCTbb5!qg`K1MgZC4}SV# zxBpdVzy?&`8=uSFl9ql*OHpE|Qw$QU_|-KMICZ`XzF0X5H(ya!3*pq|;h zHJ3xBcAjIy#M6HPF?&8P=?0Jl?e%?=5Z~TbX=>{;z9nC|cGwGK6`E_dho`d)_Zg=` zKTAuq}TfcQQ4UusbEQ{(Px#y`*O<; zA$Pue9VaS|Mx&*-K+=&s!{BOH7rpEJ5BVT%* z?xHt-wr;-lcRt+>e+RgbO1GVi{S$c|z6!7D*DkfaEe?BZEylgeGqwg1)yDbdsN#0_ z$ywwYed>|_=``{7Ib6hhm{)I`R(WM8 z+%$ZDAm&B1)m+7!boMCndzGj!VhTmYABX<^6%E!4oKfk#S>4z3{gx4rlX$GJn;5;Z z%@@XHLo3i0^x|@pkl6*P`KOj%{qTN^)dHTGKq{H^HC$11PJGrx(yY z;%bli-m_lx8(ctBHx^dPkNMRAdR#UtRX84e{5CkrFak-tzNhn7Uy`mTukSXSl$W`( zOugKb(bw0FQvR+|||?x};V5SacAOU~qFFqk?azr;~-ftF^FG&9U@XqO|0q z3+7##{!}5CP+KD*1q{p0(_Qgu&UV|9dYX4OU+}I2_x4N7T4m5@4wqGHQj=4e0tJOG z+nyU~-&iRm;R58XgMYEmUvu>Yz~3x5w}LT<8HP3}SC15jDFq^6vC7F2Nwf^_d5-g& zAx-q6{WdtJ?st&9I@)bG%C%#hwS;^D#$X=)9ZFG#Lwi4G+rlIwk4rIh9qeW>J0JIH zc=&Ad(9jt4BL(7REEIav?B;*QA*lv^3o^2vC+t%w;sh(YV#q0JsVD?!lJ$&zl<$5~ zr$i*vboj`+*LBG|>r&R{8h$;uW?|h^L2Vw1=&|PlCivTJ;Hsll-3J{z{ef z!4+aAF31~Zto8a?XBB7j^Z59-JSaFq9Mk)x^ZAZ+W%Z1Gp5u{E?$RHYjZOYaQ|-p{ z)!HWgbinq2$n346KxE_q3Ye|B(kMPfUy)EIDlQs?O7!CUIxm)IcpnRe2SbrSTIy*F(c@YvRbthhx(MRMR><0IOi|-VUgr2cZ(!eX3tU3d zPCg5QNBi>H>vRPYvMF~b6vN90SL&e2e^js~H*D)yU zL9q5lY6;;LZZiqRG9owrcut8PjUXsUqRbp5<8moCS!)COc)x|c^1|K|F)lmwvJ@kQ zlpuw2Vk043R@Vssw=-^ETq-SU>w3)jZW@+zWsVHbF8IQ2WI7VK-q@Y>AS+Ad!eTI27LEAHowLA2bqgUL#-xT_~h0XgH>IZ}W*j_+*ilh6zd(7P-8yOSx z^Biu}T++M!`3FbE?ptbo(l>;iq}pNmc6n%qWh|F#{zm1|s9^2&a#Yw*sg(WjffIy% z947j90Ew&!F)M7B*fT%9{CrB z=EK#k5>b}do4OyQo-3hNJFeaYcdv(quBF0o${^B+gzyT#$Dz`t1?kf&(t^8(`Ck&g zKW6!rGMU2|^n3dTu#xT&n>P6>$eaf#jgx2I`1RDGOc|V>XJtORPmOU*5o#_;QYgAn zm2^dMR-s~1Ff!USo`t}C8?dyV*7HTEV}|E^7SLLC7~Oto!vz!2z2%`X3c6bXlPwzL znlCRa53kKCXF80a4)ihxoX}5EpXfoqW#Ml$pXv^nHL98QF)4gF=@Gxh5A9j5W?km` zknZ?(J^{_mY-MuQl1%PQ8%BIFW=xD*7U1u|!6}VEvrLB{>**$`VP_HeIyol0*eWVe z9-!`lLpzO2oUZPiQ`t=iE?VboIS0vax5%-HIGa5;q3y?@NGdoXnH9Grk-m;>?yrZD z%8`rw_;TSR*8YzcVE14D>?W$o@x;Qe*m)ZU<}^7Kb6+UIqEPJXGDGRoV4`3mO3LB# z2ub?%?Q0687-&g1II({GOjltf5lq(fQwut}xzD}$UqwjPia^PhN*~oH4#4G>NL&ZV zt~W34fOBZj{{7%dhOh>!HneprHMS>_4tvns=QsO~?q}NXeX8O7cE91pV+BIQ6B;ZD z5^{ul>U+ooOBBo##G5P>UlLz7GW?WU0!60aZ*Je?T4&imDf}EXZuqUA6yDzIIXq&Ba;1p_j)H1BGqI~Vwvca_DPd? z9Ixkwx7Of+j=A#=Fxlk{xlrR1t<|7LMu)f1Zt8(H$WS)JA?Z#aMk!eh69U&|O{_va zB@sp1>^;HI)|5#j;ngAf^~gV4eJN*$DaXAwMi|v*{LVA(H@MDpzAh_$W22BynDx=A zLk0e3TN@l+cC+bi@!urnsrR-nmy>DVkHH_2HJU+|E4qDGC$MN%`}*@pZlH1#_M6)th{h!?6M?H9akVxrP0(cOu2 z$j7BF>vaD(1mo_sove4y9k_2x4sKU~Kyy_G)?%`kK4;GVm#x4w7#%^t76h=s!# zDQ=#{=6>&U=2dGQ8L;cqSt|*ok56zp<{{zSTN*&>7XH_}KidTx`MXyr+G*Gv=Md=j z@OYgtdFx$sQvzHD=ZN$7tw7)Oo`)?V4RUo%#+cS@;Z501Hal8wu9wdbOT&JVnKCz~ zN#5XrobWd>_#0p=>13=$2*~Y!Tw-&wQE7xBh9_!&6cl`{_mgexF6!hn2dtQXw0zv_ z3I~s$`5%E*PdsC@{`t9HNJ?6W5D!4LOkF%>4i)z_nZl~i^Mo|jbGI6;&@BsFRB8x_ zElhWb*vh3SFfT_CM^8J=mB=4A>;~!c0+#!@V^z0` zRQ=#xS%bd`IY}bJNf=W=Pnen{-HxfOB4u#??v>fz_^lr`aNJ){B`dD|J94n@8V~Q1CKyc z`@n$T3&!F5(Y90>yED^^mC+-@2;o}rE3der+tEj6?_05&7SWw1^S-RiK@^A1{9Y75 zz0l|Z=CLK8gkM}6 zTuu#0nMFS@K5$4~U$K+>ONVP&VMt~wlkelEULhTIuH4_({E|+aSkg$;LS>>j#$g)E z1!jVecoeRxY@!w=U}JONW)HPi3an|jSiF9&E>%KlZwct|`P6Apd%?rV&?b-js2^Z% zLOZPVycpZz8@){XGq6yJ&h4vQ<ywUB$fn;J?sl;;WMWbj6X+fI`q0HX zFc0OLkA~0>Kl7r%uz_Xx&ax+;*X%Hzb{4zbFPFLxEG#+PO6@b$vDXU+|IK`LsRz7U zWZ&WB9#k+9CnyU!-dT0Kf>1pktA$K!IHP7_B_tQ>rYCkJ7!w7|FDb>cb{5bnA%jT}S!#q?j&(^R!EE#eDF}<^eU%kUq+U8`9A2 zXe?~qPm_7OVqqk4Ko40qlGaY^qlPc_j10PH4{a7g=o`6yNxaPsL2d(J%bYJ*2lKQm zE=3HwkYqSQn zq`I~Y7%HS~JX6q43HG;cJYzJTSv65@LG=ccvEpJhYJP7TpK%U#C*X5KxGfM(U@9pj z{?N4|N5P{tt>#{Nup6N>k~)zKUdnFw&L19KE@v1V@dV1~DW+5Y$VEXxJ>SgD>-Tq) zAcx1{Zy5dORGEL(c0O?VBQ^S4-_XptJi&5}(-F?X-vofm(3hXq;UERxSx1@$#1k3` z?wqmnkk?{TIXvulJnr7VsvgG}%^yS8b9!$5>o`${hxWv}1|<(e)0*97Ssy{^NEf4? z!SB&?gd2(2185Mz`I6@!4-#Mr!VllgPl*9pCAw@Qp14 zmAC8GS6|=l93fG-e8~~)g@#bvPdhW~vzVsJN2Q53DexR`R)SFR&14U4;jdR{Ev zpe8^l1KVEjo%h=Qag4veONJMFEc~QiDMqWD#t&DIxK@jwzhYX>)%QIDhyIDio~hAB zCHAH6=#VNkX`Hsk3W>{XQR$B+T@d|60mOXdHUmPDrs;dotaZ_%ziV#vqrCE&o%q4A z{)VcmYa&OO)FYSBU)Vs2&kj7>3_SRR7#7&|xcZpj&c${L|NaCmDxw|oMRG{ZX3h6? zSqL`Ks36FaaZwBFq~f@%&PaVZ9xQnZ{!^?xlGOT+#T!%MXlBl4NU=7Lx5w;vn&;?o zFR*T0Afu^rQ&2P6_pcFg*HB~d*ovc+9!KQ*GP=bfVhlQ_(uv4y4J?-d9w#gwK+9zr z_6n2*A86=&c&d$Y7`5ct!Ai)kj~13--7T8vJGr*5_vDpS!74dSQTs=gqgCL@v-6lO z><;NualJ+c4a#p>lcxrSYJJB`hfa?6zdS;HrRyLFV;lHdnRM{3x%%~eT$~TnhD4)u z;QbY~|WdDmwbRa|@YQF_yv}xD|Jgvwlm9Dvad&E$g|^4W1BHK$1oGcm!9^fSg61 zxu-9Js#rBlDs7;2l7(kJyM-=T4dJZ=mM-EgXUq|y%pU``gW7S}Pr&bq0H>Iep)&5U z31&l=^;6fs61d-$vSHN-_at0`Ymq8hnK@)sQw_!q9Al~>_IkyCZlnf`wZPrU8>NJM z0g7<)ov9)$^2tKC8+lt^B(HLMgFM(J0BzRf;&X@p6QI6(et=5{zh*1z;_l$Xi~bSf zrbhAC&)QW3Z$Z6s7Cqw;>~Aq?ll=YiHlOxlqE3D|7p}{haMzjOBE=F1UMA%)$tcrYAP(@w8Vr!98vup zV&82chwL^Ow2!&tCiYo*mq$whUS#J^0)Cghuf+Yh98c_4<#pTr6$(5mINMVQ%)tL0 zyjSVnj}s+ArGJF;%>a3xZP&aW_|mPEu4glZb_cQyc6T$%;CoqCN`49)r>qErS;Y)` z2QyyEO8ovJhdHKcPvMf#J^Vrd)nc`zVyrdQ4uWfIsCFlgUO9Jj^1-lfPnWV4xVO9o z>6@;;P^&xTg|~UACZp_w!YHKoOW#4;Jr3d(mG!Wv#=BMw0E*5~A9doP5``xcA_Ya- z{!~cb%TCV^>!rK$d(Y;x9YQYcfbNEBCv(U| z%6Zx5AoS^Ya%=f)j4K{EI`1nJp?*Ct(f~C>TAZc-_8of`jfUt?K}O6lj}`;ib+O6( z^Hev|<{E<~34!<&x5=>g73S5*5nGd)EDiJ)4>Z$ARB*=ehysz$(>0()EGk1B88`N2 zmM0v|41`5S-P9*RjYf@{vI$q!6=$9r%Bm$}!=xvfY5$OBH;gXpmn-edy0MlRhF!7B zO$8)z@cXN+3uS&XKhMMZfdeAcOIHORw-|xFod=tboaT3LqFwetK_l~-n6*>X$rp^$ zobSBb4r}@0!GL=U>Fz6Qd4teN@6$`tHlWlWa@tWTWv?7>HD9vsUzg;NjMr(RgMNet z-=JOSid8gWxkE!D0Al^~LsK>kI6B3B1a3Yk>1Gr6Gl(_C;JGjaF^I`0#3xN5Y7Cv| z=E8Bf3t!XVt)Lf@)qe2(2D2L+FV7)&sTZn6G2=OZh5uF9mO0FXuc#f$%=Lry2k;A^ zN`w`#5Qzg9RmL7c0|$?XXMP_!y+w?)!l~LVIp)4!F0AT^1~8G`y&v6G(#|~%+dasA zxiS|JFE8H`%Mth4lm7NOIl^FJgQwf- z#>XX@{49Je8cn7In`VATrJrCz{B5f{t4DA97sg4nU2~fm;)vQ@i+|yL#>h48tMBPU zVk35|p$GbmEgp2}AtyD)$ZEh?5@&MuJ<;n2px`qeG?aq=5ryW}ecx z9Q>|+(t>JqtQC7VcR*Y168Q6_)sOFf^Yv`J>kWZ+l7FIA@eP7G=Y|)4dFK+XB_88th<5rtI+PeRgZJ+VgO;+*XR~?HD!9sqzR<%bpO6q( z@mjm5>cKuUpJ3n}%`~h6Ry&GWYt@xGmNm={c*;`O4nxN`uPNZl!N#|mX^{#Yh&o6W ze~K0eBf?8)$ZX}l-vVEwV+2Uc?&UFlT}!UpZyf9Z?x;3?8GrkV6l8y*YHm$fyIJfygMu{+#$9V(_dgng^x)uCBAvG>0A?^sA(>BG=rrQq1@=Nu>l#$mIwu!I-vKkV(}dTbG9P zZ2rXf>}`fVoYON@&jfwrlAVGzeW1#<3UszI2u?IZSv#+P<{PV70^YSByzvV?2j->q zp%hG8hcH7YEghWb$#0^O94>sj4|}^}_cR8fNkWmIJi<6Y_*i8*<4#-9<7VCCXYQG; zgdAP|xP_gw<`Xq%{V}Ey9`S*^vClOR~W?Z$){GQsFFE5hbeo#(rNRgDHSC;V9 z7ak3&oW@dk!2SFY&+=Vj6vk;l^yAmG#wjcj1M%;zH~!PC8VjEsyH46V`~`>awI4yd zkRXiI62wEf&##Es(^F@BablZq5nnAX&)o3c(v*v3^ntc-Ll?rE5{6~Vlv2RC9y_7| zON?UFFa2kIIj(vgVv41KR!yKhZkV?B`d)jsNK3l>?nJ|me|@yt;0YXoUbpp;G`ClB zzTx!Dujr*$n5Tg4YyKu6$sMU5Ka4~|$~FM2HY@!-jUvL1Nq~0s(DWxW%SI8nj}Jdt z5QF0jW`8s(!WCQIn9^nqFeQB5F*|c6vRHR`zG3yfJAC0ekbx<6!#%}7B&04l480J3 zqxkG4a9Bq2()GL=CKMzi+!PS3Jyoel5+F!O5tr}ge2)=NZYW(Vc0`nTZSSqcg;-i093fuDCw5j^ z9ni;E(T7kL(|IeND>YsT?b<=OdjIF3Ra{Dk(6BF^UeyJW@FayclVJipR}ZxD73|XCn+cF?$MDxOjb`O>m_rjNR!ThLT9(_#LlF<1eQFmfR1k#> zaHm%%r{3RCe|!r;ILr@!|0>g6x2MYWYcyS4%@o&Ri5GXuC`ddX%KNOXr^PE5B*u1? z%tKf_4upu)_*pXdYRZ|Tq@zb}$RyX@IXy;r3UfrK!l2{BuJyZ^IM?$}0> zI=k!XNz=TP$99NaWrb%e7SCfcFOoM7uegr24G{P&|sgFV=8C#e{|Z5+a7 ziMkH3y(xIZ#|tfA%XwNp!-G@mQ1ejBe2qL$-08Fy9(M?z7N?9_f4g1CG9?7s9@tvI zp6&BPr7v;g*6Zs(o`st}yqP1My;G7Esh**mX+kRo?O=aYOmEgEj|7k|@FX|^73aq! zNQ#u=Ux8+cAxbud+`vtU%xU5wXJ{#h(o9pTg>_gGA(hX3b*=aJp}-*R$MLhX~QKO`$&EeQk53D;RkBh zZrSJ9(^cduR)ywxv&-L=7 zw%+wZR2*6KM#w&sLQ8;#Y=OuYp-3v_MTveG)tsX=YRVa6z%hGa5RNFrI~oepE6I== zLf#fi+`bm`ElhKZB3$cP$P7ng4p#D&9b!Z*Nn2lx!BFIL>B*ogg>&r?lOJ)pWcCyn zi2rueB3zVN{Fg)M0nTTK>J%Xh$6F;17N5@^gVzSbEfcFclw71_|81a+he-NklSUb^ z+CXg8S=2D!p1syZ0CQwFvDGkzM zV1K=Cy`v~1-QW9Q`hZon!TVjHk%-DDJfU)@UfbkYQPvVd84cFJ_d9B?F9aMtg8y6) zH=#`*>EO-SR8OXkfe&rZV@ups64&!5Tw5P>3Q3aWcj4wGzicyE1=fe_#dyN*-VaH# zebt7;vy2Qy<)QhAvO!Qz)8l517r?L8h6lh$qNNbfS>>G^G6FQGDk|7hGAd^*z;leU zba%Z0m%n_n=~(RYK&i?@3hvmpmRPplbPU^ryQ&V#Tj-gA4}=~d;P_H5u~P@AeOJ9K z=t8`%T|QN!iiXgkP3i=HxNhE?*L2-X z5b<1c-wR$jlB|KF8G?v4tMM0tc-Rp(7L#pp!)Fi4w|rKQNe-xf{8D@jqy?Jbs%#Z& zRe1dLJww;x?OxsWmvfcF*hZpz5KKo;F*@#@8c$BnZfa28&ya9gyUWu2-14rraE3jsO~pTIj@Hwq8Fm+)W)S&Q71N`kTY?JO{JSD{9FJ-mD~2 zD_FSyw~Jra#PkqhY=Y)QG@K#${amf#Foyd2iL~>q@%dNfE8{8*Q(skpcBmNyXFtQR zIIESDC*8rrYjgsw(X(dkzF|nR@k}ALX$VEUEAreo$y_h2cIUh4iioz?SwdDoWqiT6 zQvpYznJ3-8p$E5<5Ixp>A`!Gkd$QZ5vy=(28Un)@Qk|CTGtTPaE%oPBmIa4L_2N!) z&9Pl%%THT-%nt>3Kb%qDIsCg?uOzRFzu#a>)M^B&L*2G(QQRKsu{H2{Z$!PzS3s+CMR^&+|)=!|ye%wENG4 zftFSdXH~dtl_F^J*8IM^g1bOd)i!+XU7^%e9aMxm<#C#zk6;O8L zq;pdf%3m7AYoH~sK<{S|&eTwcsSiLd($;X*AH+^=_Qe5hL|QjEC%=6?n7zJlASw=x zA8wI_v~NG_j?LWUc`yb+U&hrj6!$3 zZF$YS&+w#RW?6Ka{uM8zR;QlCERG=V)V~y-=X&hW`D^7}E9>~iUh{3d=z zc6q%xheh~8dYsc^@SH7Z?5HfWMBm4Ykup|xua4?BxEt5sj>e7^eec>5m<>Z?qWuhZ zdgKK~$S74=W_}ff8{)wA;xrQ+aN7jzf86;gXD`hqijs)FRIVrd4_t#he$4tu3oyK_ zj78W!sO^```g0Ujj~DVivUt-Cm*3-{P~R9c2wjpE)e%UUXF8RXXY|dH;`|q_Sk$2l z`jvUoC1pT_2`5ZwOn@`N{E6$gdatuoCcA@eZaL2}d$P)63Q}s35k_S(ZjSo2eoUob z!(od(!X0;CxIcEp6NHj;?UNZz{Z>j#8wRg2+b`ZJWKP)q*C4F#zE<32damVcv7e~W|%a&cAX1TT)74$1`*VDnyc{>;{Wmd0XlCJ*Vt z<#9b&^h4I1n;O}nMX9(o`(Z*@2PsO6ohFVml|zKXCWoWD%A_|y)Yl-u)4xd=chvAW zS~&aA=YmML*52STH{Jd}r|#!9W^fs5vpH(`rtE1j-d3_})?}5t#Pcc9(UMf2xOT=8 z0VA`7v^_kz*RT{)y;8ggR-XD53%!*j;GCN<^DuzKy*F4{N?F{nsVy%mZf&VtGU$AR z@oZwA*YFJSkfxy#?wV^H?eYAwwzx9M3)yuMfM`P8T968rx8LNn9BVVyGuG-+@h{ro z&Y2tQ!=ZRA)SwiNLVl*`;}OHX3{86>B5i-^aay$z9BB)}CHs(3(q@%fL@?;Y`FlWx z`^shL65I}N6*IGs?Z_XjELlLQ5DgQa3NKTknZ_I2$og>wT7*@VLE2g>SW3~m{;b)3 zO$B>WVJRqHUk5d9iz$eC$a$gl4LwN6s(9C?H6Hmbb6)FePWb(qi^T+n@Y&Q zuOx@{&WF|S_B{jK&f?XJFoPcv%Np{>Ry%kU516V_m$X1gI{Ol_5;RW80z=L=CAlFI z1O8(SxZ>l}vF)5bhJ>Ww_v}CGDMFFpPX6mav)Pr$c}vnaydrhTsXonPOuyGUfqz@{hHJ$ay&6LAy#s?0 za|H%l8K>+R|8V8zPh9Migx%Sq9QIy09dG{`1iNLb;Gr3r2Z++ng=yg0^yKp-BQ`Xo zYl|&!oR0prPUn4U$K$2O3(YD_>#%SgzZ?!D0-cJBE#5FH42poQJkeA=KN(J;gW|@g zG+T)xzSu_egeF6D(eRj90*tF)+jyq}m+!lc_!`x-gqs8En%`a)gHTh*0xtrTSUYdC z35Ty3C>x1VGSH{Zc7W)IT|PqQ^gSCFa5s)Ck-P8eLsu!3t%DcOLv4ou!ZX}9$GK{?GgwM03+3jR_+E4Bm=TgJfVEScVNdBFLeV=m9mp@q;rAd2o%nh+Cfag%Kp!-Qth(;lRG=MPq|w7 z)MOjDO|n*^hO``n{BTX{i7@Cq@;kuYeCTmpH?t4mr_-Zbp^3Io;C3D7zDsu478WKX zN4P4i-s+}kD~DVB=z)`mi-b8S+UVWY2!bA{9b8^(87H@=$7z-uElg2Wo~LT24wc?w zwtpS>0I^_3_3<1nHygYyK{%#}S#Nrr+b3FnO|MQ$G8meiT7IW_>QAr|9u)pvQ+#9m zp*yPf>gr$I>bMAk0>6(RfmGR46aq4E*D~<<>}dc9GJ7 zywaj@!(ooTe8?!gG2={Ud(=8oy>MMOo`igVGy1UBJ>KT8H!c;n&5tUtk%_hb)qvV< z*Fq6s;>=M{%t7g0V#xp|`M~_l@2bgdqUTqggK)Ug_=RNtv#^AB08G(yDCqyfQOvjj zBOS{D(%IG`uqWyMZD4)PWnpSA4>=Id5+$)a%53S;YA_yMFGd)1=Vfk z|FaH6%wK4>n&a9=MTMpxmh?b4z9+Ws*K=I2OlkTq#0AlG8dxKv0ntNEte+@|ybC8Q z>b0M<`Q?n;jRwM9iX$qDH+}3*xleNBh2M-JMc;AdI}7bhmVm>SS?@%+xSV23u* zF=Y&)LinCT~dn5(fWant+Y;x&BU!;52!r5yU~yg7`Eep0zMPz+*IKgyFiObsDUrRQc5>ITHxt8Wwi$0VW688;KG;ze7DSW6^YGP#jIrs}QblKQ!;>o>z-p*4|LA)|l?Z=Ry8rj8#Cgo*y ze^?rl#w67+<%k%E+(c<<<$;D9Y@9pJeUmtfhL$9CEd3R896o}qT7?M=2FdUiUk10- zlbqzaB>!X~IbWzuLx^NFE^TnnO)K!fwJ%~g#xf5onN8BA_?1jsLc?8VIEOLspMeqA z=Hs%GX_#IGcuE~y2&dkzNmt_0XQ8su3)h3ZSWoaHlY+OU&kEK2(TVJ`==GFKprVQv z5o29vEd~7VAtf|?rJpt+-5=6;h6MKP?W}oEFwbd_iZ=3B2(b1EaP?3NUYTflFV>GP`lLje|Q0l7wfh#9q&; ziF!#{>kSL+IHP?2)eJOSy^u)8_=0w|n-GhzBgKXygq_X}W6r9r7r^j=6LUcOb7Fc5 z-|zwf>w#vu(Y}ba?NZ;$n``!7txKj$?Jo>|r<*ajK4N!Grjs8-W()6K(9s9vg@55+ z(?gwZFfIp-(%y*gIADx9|M-cCQo~eJ{bX2V`=Z6l+YnWoqciv4y{AUw`4Oz_|A6wz z0-U9^UTo;RM(WsmpdKI_z{xzsl3PTki=gaK2J}CHcmQf5$j#cIfp-Kf4YQ%x;Ty-o zxd(edMMT`G)tOH+W4OAhnYyA*xDkj(m}RKWTe~I%Z*GWbZG_6~MIXx31DadBT20+M zeaZy94;zn4TfkVnT^tM^J%LS;A+SmF(!>fY3ci#0JJoiQkC$AwTMQ2BkXHJjc3xVAro5R(I{77R<7-^JLa*R04YTaV2 z9byY#j~uPf3`LrfjqxUc$^I~*v_u+d+v>d6eMOhQ?++0`@!JufBnoFqws-$yyS;rqC8@+IN}WGlCA^Ozn_1w zgZE7V-_h{^2&2+GqcSzrHHS1F8d)56(9vH2YXCL4f@Io!ByO!oe|Fdl9$c>y|~pGECRJnIfC8~ zM^>a&hB5>)S$u~iqW>livwq<8paK>J0|n#H!~=+shse~s{j(Z(rmL<4TU)=2u;u0# zLFxhXTmA{$641=0boNXK_dEt)YJMi-_<)57#4Jf?s7IPjSv#;Oz9~f|O6Bodg6JI1 zfq;l%m_^^jz=bD%@HR)b8E_rpb6S-CZlB{T0Y?;p-v^V$X#VNO3SA)1tOFE%@d#}7 z;l|&jrDNRN{%baNZ1C?8U=?Bs9qC9>{;2J82{+ZGYC2vo*dAAFyi-+d-tl*#Ek>zA z;eA{Xe>nb(jrL=L=P?e&LaM{D*j9gQF_4+%?wNl>VgL=gxLis#+N;eRaOfq*} z1eAlUZ_fzc@LZTb-aY725L7(gxql36S+M>T>mJu_;B8*9qUny-!1*>F57P_E)FOX3 z5-=e;>9D9H287B7WDqR%k&u4@_V-V|>+=8iro?`i5VUgKIAFW`)Z*>Kt?5c~V$+kR zCBU-LoXF%4Jp@KO=&(FR*|=W?kVGI2rj({X6+i_E2=>(nMFfM5S*;oJ=Vaz_%@QyW zW&;BYu+S@Iqj~xx=D;1J*s{K@hft<#dZ1T#f9w1i*eYYhApUKiRvI6-BaIBUJtfYr z)SgxOi*8_I&$NtXF1P6$S#+KwHu`@JI~-x&=)>>-uR2b7+PX#D{9N5KSX;#9uXjZR z%F|};14)pocfCh0tEDPz(MhBK`=izwA@H2+GO)+SiN` zsT(pw9{0;WC^JV5oeRK@fF5kcX`AUj#JADtEF|h^O4OR4B%@0s1D8{b#Ar;k%l*Hx z7zaEYN%gN{tx5D?uvK+y#M71|TT9Pn3y0mdV6A9|(ZmG3LZ*z#xqi%dxx?(tk%&>r zy*q{kiycRO1!{Deg!~LH55FoHgG11n`2h9UaqNjyl=(AB!+{m4E>4tYs>cy|JS*N zL>5!YRMjgnez)3yMV2`UwS7ji-EFP`KXo6Z?w49fu1o}kDZx75lkK-hqmjYiW<>cb z5ylLS+rx{$xjmt{W{Yg}pz+@_s<)fufSLBSh<*labdVxX2rq8Jwy<(5f+4!zCOwTF zCfp3mGg=mw-7mXy{;LKB*KNs+>+Y6X3vHP>IDxF@|GRO>lz|jh;)G_u*F8+pHsqs* zX)f>aj`0s+)p~;9*In88fQ1J=pa)Y}fk`=eJ(>W>Ph#}Ni@}wdz!sIW8)y-o%7UJc zV>Xix>A>)7*zpJC&87-F^No(HUtG5{A9e@n_vO)5aY2HR)t<;wgul)}DN2kN=g*e7 z5AtN{D(s+ZR(&2m{?b=a;ceTtAa0 z9X=hVzxw%no{)ii7{&yWbB2M6#R3ok1x^O$6$uL@8XYKod5yf(lk-}B({fQ7m8F3+ zf(vSBC4?D`XL8WIzG*_CgNFj=CGey+V-trkvpG30c}s$Hla}U<2Mk{GVp>gp77u0G z+cs+6@oJauN&dk)e|2Vx2vIzk)RI)xwt{mAMsyImeeNsDf)$z3oubf=&JVBmiN#l7 zOTj2ix>5cQat@Rt5XLNny)mkJh5gs$>OS=0gBRM9g9Ks-z%bKu5aD={(fbx+5*v@! z5|~g|rZ+-Pd;Uc9eE6=0Yj%M=?|%R61N?r&^_0M>r9gMQs~45-1X7KmuT|QgbnPc4 zNkoDG(`a7!<}%c#%V8}o#ACBQVu07#H}v~v7o-5+1K8wPyl$zh*{IB~?X#{@L{63?|%VKZS5uLL+9WnPdcdLu&LQC1v zDwh90NK2M39E33Ph^CG!vqsyscj0|@68|vG@4i2qsrI{3CKjefW@s395yOh{zQ*2j zLG+;oA##Ce&T&<~O<)_iFy|Eam0ZLN{S)5DFSo%kY2g6`2l)Hoj1}Urp5O>TBbj=$y`98q0rQDIty+X?TO^#{Hls%!Gdub*8dOI;NY2mid3mw2%yPt^qzq| zXh-kP*m6J1bG&1@Dz4%62kVz-gw&U5%wY1RroW|@jy3uHOm3_Th;0CPF%0)>jxC7;p^VKEL_)%o1KY?m)V08Z{Rg5a)wN1aQ8|1$*Y}36NKJ5K z-6OvN5fsQO+p3M`srGKxBWcLQwGQ!%+cwVOwtX}AE$h4@sp}1pOzh*#M*#ZN3sTNO z<|*b_ZIbDl)gPD1^f<(P*B7l09I1?tpr+Ej3HJ_~>e&B)_Dt}_EOE4tu1S6DT2TB) z%3Sp7_=l-W8>4Tw9~g9<*OtjRumS#r7#TmYlExBB&$2NBIxR+LMda%Y=aoLy7p62S z>CK+boEE3EDxc~e6*tjPWgJ;XVFDr1wmZbSlP~AgG2A*osDaZ@8Qqn?DFtt82BgDT zIekXRN<X6ZG9v*pjTye}6z&$)n;!9I31YGzR8TQ2k z3zGW`P)H}ufolc4tyEgW$NkI+qItokvzrqYB)Om|$+2x;u)01<>+6-6(`U@ZXn=Sut?^NbN z_RihQU>4s*$JTpZ+x|M0IfgzakzlUy$Y>G^9FL08Zg>J4Y^83rr@Y}`b3)ah;oXd6 zwlk;C>x|_?CG@_h5ahDB^*Mtu098JT!$@iei8fy|s9D#EF0m^y3nW2RSy9kR_u`tp z%1Jbmew1A98v#G{UUbWko5e}&>{+*ESTmV2jThiuaa>a52>;LY`4CeVO4(gQUe zIzaDC-10ulyH*0CD^4xBQvJ#Nv8JMn*6}PUNn%W*xrimQtVSs9>NrxF@oGO=M0^Jj zykWK*r|YJMuBwon5D=2PhP(T8be#7W3|Hj({#GKDGXoNa{v> z%f)=*$>OIOzeZ+nMXIQ&d1M7rDS(KGJNvDyl+a;i(+$}@ z4J+q{_+SAFn$wnp=KR(a(Sjyly=sqp7h4M&=K)Clg-a3HstK++JhON-K5@qPmbz@( z9;%WN3+-R$0jOutDuY5TnwfCKckCQuIW+!#uyw+J;yZf zuW_H4c5R1g7(m7h$AXN7k-2mQ?6GA5_D5?1KHQN31XM=QUls$Xf=iw5u};kfXm0IL zEXqa5LY}5F)-%Z_JrbmvO~o7**-8+B5$}h34Bk7?wm40dq3TSwCZpexGLxCsNmC(z zGbN56stbuHm8WQeL~11*UGXV*#Tp14E<;zu4HAiES6W^jv>T&48yx9~R%_J!Zdksh ziS4L;#qlrbj(Q$ETPd+YB+ty-Uv8!$3HVNvwp&VkOH;uBIJD~+RP@j)=bO7L9QjUq}c zLSlEgm5@CehA0R<3=C#JW~vfExa|y>-x)dXPeGm_$nLB3J@n-<&d%hW7%2bFe4)&} zrOYZ?q@%8z2x5@tAjf26zU>|Qv)M?qUm5~rKGW$jgb+RSrz<_cjZjdJ`YzvJ{}&kI zylhg442+#8$j8GS#QwnpGV**$*|@$p?;6&i6IS>_9bMeaeZs|Xvc|CcqAPx*#$sbml32mNh&-i9 z%qiXY>QoU8E;{^l_X7SEXvh0$;TREtK45@A}CoE{>CM_?eh!-sU<-y z7yUp!lC7_}e(sF)wP$_Tx*WjNF?hc~H-pmWey zZ#!mmfS3CkCnCVe)E~=mjsml#=lyH@MXUO8v}upsDjY>acSGB3fnujPuSG+7Vk5LE z`0mt7iKMo1;2Akd&QWuCEZ`4Ov;-Mb2mKW5n=}p35KA>^YqF)z>0QgdWX69 zl|j}>^Qf;%&8VbGS$}bOyTl3eln=c!j#|_P$p0{&+KWzrt4iCTmm4{vvpDz|p^|Xa zp#um@wdd8+8BPlRPxR-+c+)I^N(O)Ho`ATBMkQm)c2slbCRGn zqyVqDfVblCXG<9q^f;cB5{0FFpX5ScARUe@&c;AuUVYLS)YC;Fq(U~=YG=K9rI1m8 zV1WZ7S9)u#J4kRc-14%5MRA^1^07ZgZWkve1vSsphnS7b^}(~=Xu$+wuIcxHM;l~; z>EJq5g03_j%1{D%U*|H1dcP1#Z~<55nbmTEK}Eb66?nO9FoP!XAFzBXQ?M#!jC=)J zQ&XJ|ZBMUtPgD1K=G~z5%fn8V1lrs3bhEz zOy-#1)NCVN{s$v6;Ai3jy%HXeFP*6=5m7~snDYuTFpd;3`=B#P8>81I<-C8YJ?+nN zVion1h(fabj<6sAYFF(5Uyr-v99K_(znR51z=Bq!%z*RNgD`?I>zT%NHHSwvZx~vy zbbqS7fc{p=9ikuOu)i;~Um#$MSycKsYo0wMoMDM~w7=hwPVpqKi98)m{y(ID+#)gh-Ok(9Gb>k*gW-#EfEar3ziCRRpadip-$P`qqIcDzQor1aN z&q!J(E@e!|i$6x;#3W!IxS*+UCQ+*={-%>l>=Eg%HwChb`MTUZVs+>(qh&0qQcK5M z)Oca>q2oP$dWXiTt2YlBb)bMcW`<)Y$}IQ9VCu0>V3U+u|3*9AqzFE$wZzrs`*ux* zSWcWdr^Xdl6_f@dieQxg$sKxj1eC;)W_P=zqIxI*QGKFt{!gJ$)URjpb}%8$I@I>m zk+%&E|IR^1)8AtLf-cX%nnz{L3to0`2d4#mk-)nzaKQ-Beea$HeYF(JuMmz=5H<&8 z3He+#-Ml;P(Lp9Q(uk-6AtK^}ZG%?Y%})G4jyuWYxu$Xwow0P3)%z1%n1kHcMwaiS zs;=>lWZ;7B?gGlTg zIyVg|zczG;j)yM!I_nH0Z)MzdS1;SCC%T4j-SebfEMQNMvtA@t3l#Q!~XsLc`qXmG{+QmPBgi3T)7==*X>S=Rl^yB?Q z?JFD{Ze0%0SVVIGIW}3g@croATpk;NvHl)&W|k#b{&4j~nQG77$-qaKEyjqJLAmmS zg{kt{6S6ivuG{Fsi2=H65+1ZF9~#vYYc20&?vTyT+HYFXqkt|Vy?f-wO{-S2j=ruK zv>l0B(_?sR@4@Sd#cv|E7hQ7vyv*mZS<#El*rd~iWavg*jx@=GRp*ld}=L3f%%cSXZ1$ew*J6mbL#t|M59H0r3=tzYfrp@=*f>Xdikx` zc&2qXhW@PFxbk|JW2jd_z5vga(6V2I^PFTRl!TgD$2uD?0HhKv#Fv!)KSWN__l;6o zDL{bZ|M0#<-F z41>`%j>+9)85>wdLBY(eC|zSfEaG6`l!7UK|CAVZ21B(L6{zRns>6*%kfz|x`rh^< z0cp}>epcJqi}YPs3IgD$0-jEnJ1ryV@4dUNC=Hln=KJ;F)qWVAPpR_+>hCC(=Ucd| zv@Z-K;j!}qB5y*B8oSRMHs2Mv98c=%u}OuGKa+imm%7K=JXuzWn=G?jmDS+W>n-i~ z$~$Y=fg#W*A`bVf;wDil9wsi*eQ4x`(_w~hUIcT;Al9jOx6rnSGHUN|WRb~hritLz z`Pwg!qs4pFgFG0ViBPVhc2d*dGcASN5$Ulc!r@c8Nl7h)8Ft~4ErUG>#(V@!g`ff5*6~%}(-IWS#{ZhY%(npWPpz?A>y5NL zpN<_Xf?J(SC7zU}IERDzY$?(h{RFITYJD88rStao{vqIIDd|=4Di+ZBFYiHqy*^tn z6mu`->Eg<$g4s^J=by3O(1 zQ??BemKnY4pq^$(@>*5`F=iBBCgtc(6Hy%-{AggK-#z5(wP&PT?T?{0eI~zX%+SWt z_>g=|hyZi8^2pDj5*J&3p#e)a9{uLhh4d}X3RUfO2I?;oZLx2nV<_whRE=I zZrQ&vzJ|n|P8j2VX@A$6Zr18-)Llc`Dl)6L;0ML+{&8`o5S?%X&QRrGy}KsOH1%BG zADzPsk-_Nr4#KQd%Nc;fwEHM)L!c;ip$nI4U}{`AK=HiKWb5K+QBs`j(2A%iI6*PQ zf-qWGyL9-GQ>aHBwy4iN@+Kk%_eqxfj^CBi#Y28th61h~A9Qk+U6 z{3U<%F?jBEv&RCc4^ZJxHW48ni#yvfAcM?>iC)(AY`k@hu6e{p0Td>uSwVaRjyUsZ z_H#CCsHh9+RQZ=UsQm*cwH>r2L42xjbt?Un5*)^&Kl@%u%_C2cXgA-XY$428VMiMF ziu~>bpKR2Qu5*qNQkV)6NrK75YC$i6M~$ zxb0G<{?!vzU(^d2iX9{oc~baUOcKZSP{&bEkyj#L8WTi=hBRbH+?UjMaVh8kt%q8s zj+l}y#)v&(Y9W!AQFPz<3R-WaTY)&@Imxa~aWvYh1rWpp;K6#mRweWV;T6`P(dE;{ zY}CN>w#PVUo1fTK+&3&QsUc70Niz| zH`{nq8aGOYsj%50a)QxMGtX}WeEKA4sq64>ZQq@0B>e@SXf&cuTRfHQ?eMW)^Wi4? zZH~KE+KIBm<;ItMRkNr8joa|aoy(;0;!gN1CeV$hSA>*QI9ngP!t2cv=s2|Xloe~EK0p?)!z0x3T?V1DtN|XVXAS4N zV=Nsy8ryx4jt0g;?yD8}zR?0Mt~hg}W*vl8*?r zwkzQdEyLP}1EgKg9-Qx2MQK)PrV)=!cd8tJ-j?h10TN)kv9D1c#HY9LO+$aDK6xgd zN27IQcU(=m7NLb93z8*zuD1+i%@Y0gZpjZvvDhc|TuT$TMUDX1jQ4wNDr)aU*@z$k zTl7rCa7HadY&36lgrmMQd?sd?7(Q8=sYS1`fw zVE4bHuhr#alV7Y2E^al^YnM23&}cV?m~f}3m!TCczSqm&D58^+yp^2RK~<#An(^TF z8y~hdq`zYqbZkf?)y2$#tt6l43QF;cBVMWGJw63X2pgKtduEB^vUv{l7KoS z;#nh5fWPG?QY*DTGsU?s)J&)N`s^)x&2mq&LdB5c#ii!?x4r1^M<+f{GupVf=?Y7t4XBJWm+}GSctcZ<_Ah(31{;R)101JS2+r&rH=*0D z;JToW2hY+ph3SaLO^l$AyH2eR;Qqo@Cl`%w>TI_OuZe0Cr+Rc#q%%i; z`VG_4JLMXr!tbNRgipWQgsaYOW7XpPO4(P5+4$)gu? zgXUm=7mtMHN}fiYRp|#4ryFw9*M(pY(EM{Imx(_|^ID($4$tJgL;H?_I?rz@PPz!Y zg|6wbFN)zEQ_RTqZTDwz0d93hFPNNBH1&2|{jr@*+X0(z+RN>{&6*N|6;3g>PH}^p zPLHS0fQ$P-iBH;U$0g%_DdkaDWj%OSf%6n0_bE35X{c+O2vt`1boj6S7hEqc^fJ2^ zc8JF|=;+|IfP9FWBirh~>1&Fu&9A0lNv2KztmymthfkJjmBTP&dOX{tbOXLq zr9W!8X|nVY#zu^#&@ps`;pvJ-Pg`n~tLYtw$-wv>ge+?hCvSat^yC9rTK*k5##3%w zC{0$_o^kx0*(WT#`V$+tk*)C7X$b0Bkvj4ReA4Pbx}o~}Iiuv@59i?wrwL6ckWt59 zP>>?nHuom|*=+Yjp>NGh%01yWS-OILU*EVwt8K1^txexH76X-UbsC=xx?q}rePpS; zR$$tktxKo?lTCI*l%L0YgZJ&{7bG=tDdgW0-{n_OVs+g5PL{l1=$=Qpd9D7uaU3z6V*aKHi29}gK+rezFbV8ah{j05B?(nwdv)8i#VE%7e&gw2;F;BwcMG*?l&&yk`t zt#{vdDlp3Oh7Y@Ot#REEzc*E7^LdkMSfrp90YofwWMHavRFn&+E$-TF@+QqsoUnBa z=lXO_cGQlWgT{fcUmO*+%b4osw;yVq8NJ;!-shWJChZ#hf39-oI8Dk--SoKv^4SEg2W zl60Sz#vaIJqz?RCNT<%WF-!M|7l=njbJGmGKEXod-J{F*2C}kz{RJ~u@nII!{I#uB zdel^1Lt0NV<|2ra#4oQUWlov;iWWi*C=t9$<|%@$8N@gSq{IL!#i{yxsxxGc{%dOo zn;XT)rG_4TJc%*j*uWHHWB7J6_hCppx2TRplT2Lq*^O>gpyxO?uV!^4np+a7G>mZ} z-cNS7V-suK)@Yd(dUWgVSeX@sBN%PoC|qeO&vnyA!4#6pLFSVBZ>&sM8{Y|l^KDa` zqbero$tjwIZ*e@md7F1v7(6_ZdDe!jSTNQsQ(K#1p(m|(Y9v4B9_aaQfZH?LNI-gt zHePCBy9%_FM|&FI!@;irbe(HX$|lq3z9Ey9BReXB{H4E{5VvjQU;JM$UU}4Xii>-! z&l$)=DX0MShWI1U*9->`pYGv{7E(lKnT^p~mN~D)|U?+NUK` z^J#Q5=G;Iw9Hy8VY|e!0vE3 zHS&N(G#cNAUQxz2e7jzNdD59k{fuhWKR#f9(#7KMS{JoTNo{LfL8e5HU#kMNuK=gp z_Sntv8IH@Gz~0tphF=<{Ke&cUhY7$&Vsgb6x8WcGTdmd|%v?fiP6h&e**jcL5jLpA>e*n-gI zi*#@OxaYubKCl*MZTGiD`kRS?2|{Q-MS7$=ZbSD`8^@K6ib}t_7StC=>XC>(bDSd~ zs36NG=lDL)`xwXjclkspT9$8kWYn&p&>^V9^n~a2tKsQT!BuC;%+mx84aneK;Jd`zFf5qbd--72Im3d9QP!M&SUB{ew5mqW@aI_~L^ zvtf3p3y5;sZ6DMWk{Sy5)#Jg#Vwb{(d5Jn74@5J2X_SC^{)H`3^3nCi2UGNg;&LUT`aTVh6q({rD^|~n;3xY zv9+Lkh48t_<(qSH=0_;-M0_r`as*z-^ad1hgd4=?{7C43642S~$akTV8HrJ0 z3v_ap%4gGp@sZn(=C@m`eIV7ywFlMN)voaDbP{(}UnYM~>H124v-^l2#19Sl;fd+d zovgdl*JOm#^N4a()Ajd#w<5d86^2g1QN#>EXN>BvQc9VW?j)`s(!+QbzEw@aLqR*Y za;BNr+}p1yiO+gZo;wmGlE||0iaG2j*O`bVj(Y*5;~b2GKwW7Kuvf|M0f_s{?;E0t zozFgdt)}~1`MY5PMbTh?>3i?ChzB;)A-(jsw}h0&1vU!IHB*k_WFvql1g)c8uc`J!=)9EYf50m z2-x( zQI7-P7oGU_;B!8CvY}DyBht8+&yTB$KV!Iv=6ueeY6Xg{Bhu=s`RTC61yp7tt~|53 zFq!n)FRSgWV!uwizOp-7y13YN$`P|Yl&GWtLs$kn9yp^==YsvLDX)1fi!SKcSts== zn)ADNFIP3|+@m2O=(^>fSO-NvPUKnt%!rwiv^%f%7fI~@hV|}D z?!*P&E;jt-C*o8pWliRxA3>ay?5=kU6PK7n;RNEPULUMvwZ{AHMT+J2-(=k;Q9;(~S<>)ODb>{*%xRPfRL+osLy^5WTZei*Qu!RMsEW zc^w#`d+7(>=^n!re{p;4Gw{9)$Q|`;wnMX8t9Ftpv`e48@>C`7IS+~h9n=+0a)g7xI0>6Ys={ZeG&Y`bQ1J37YHb)&MxJZ1 z6jHR*soT{9p=>?&zK|?2cHa|9?i+V5`2?u)p|JEs;SII8LXls-g5T9J1%+UP0%Ad> zfd9$uEO*6Lh(U2jg+y1DDQ7D$RxuTWW!pVx3wa zY6hk-qf45Z12*P|*w?F_05>P10#`k@WtSt-BoUcGuKl zxKF6l)O5a_TGy`Os)|6ps84^s+aSA|R*6a4vAczo}ZLrB^U?%$OgDgxJS z&ANFCm;+%rymunm6Zy&#UEA7$+lWo9cRz*r5+K$)Jz~W2J`{LgWxg`CmM&nmrk4Js z!0thluP~e%?oAzY%`eC@6aFAiOE|3)3Hh6XP2Ot1_s@q82Y@!xV()|XOA!XC%3l{I z70?hKNFaDdNhA^RQ5}sRvSjHUwt^y$C1Y{Ch!;lJQSAoU!F89@78D#bE|Qh?I4vw9 z4qAh+fw}UgRWa{Z1$HHam8?D3?5VLED3%=}L1JA75@3zt&NfOaLVobnAO4OfvcBryf9c=Ut8(+g=b2E&6ns6Gx7SZ%0V{&MO_0?!4yum zV+hPiLLvM&+1KFs@4m5lUb#ZU_`_b)znQF-UA<6KvLI5HpS}-ZI1ANC$%S>@3jxog zZ)Z+VSnmrFcfW33fEh7G&gX;t9r`Gj-=PdzOkCS~WR5bx7KyeB*)hrfWWR)5tBd0HVI6DgFFhFeFWpSHW2DuB3zlO6~H2(>;EB2~Us+D=|Y}KAt2bKA%4N+9) z+QC}r`%7V}&7!%kO5so&czOP*0|fS>wb|V&oBde{o5R^FaA?Z_9^2G*T&`f0u`Sgw z`W%aQIK>I&KS^EZd0W5X&QGPS%{j>L11Q$}5|ZSeSO4K^uu%f=XC6my(qEG4`<;AW zRrKtOM52wwCng-YzMjtvP;u(s+0@z^?4N#D7Qs`kfhkEiCSV3oeceWFbv3GYiT{FI zNDc-gfhEjmL8Re)8GB5(ipjyI z=y%hO{%EbeW;8?Z9*U@tb9!7gprz{+R6&P+B#J<_+kt4aYH!hzE~6??YF5fuETfOh ztAKM6FoKtG;Piy9gaPd`?diy2qkQ^kuWkDv2_XA1d0C22@@jcjnIOzITZaMiC=O^2 zXDZ3TIR6Dg<>Xsp0K25(P7bSb(rpWKx2(&3`h(m8(BcC3utJ>gAU5m(1MF7Jf$o znO|T7RZF%q(hTd?;B*PnZ0nA!1@u$b3t5~P*rAA9xH4B<0Aa?jr_}&mjboizob~!f%hbxpB1#LOMe|Wa025DJ}?H9?kMnOzgI?3 zoL3VUe%api>&voziC4*w?)}|`N+RWY-zO2u8tlj?M&}7-`ZUIyC$kP6wvRM z9APVj*?WL@W^?DW083)WcMZFI(><&jxg&6d#^^zE2G+#9xuP}3$+}~rV9W%ZJpl> z0FH(CS_G2ul20qCd|r?69j!hjd5`1r=?5_nrR#AHMvR96w;i#wTkm1eYj6dUP@G|Z zsUQKlI?&^^-vXUg*UM-K8X}f!SIguWk`~`L1bLhBFnCols zK{5WK!9o?F(^_5-j<{Bio@C9vS)-yysruv#JQ_Xj?|L~A5YL7O9IhQ9q za`~QUuOKEV`J*7xaf@baY^(FDG%?!mvL#vy67_gD_@nGk8g)(_`WHva{ywrFk23q1 zbfqy2^pZz~JTpdU3^6*MnePqCmhV%X(|CIal($`(>K_vdTGndCkr8yRdM$A{olA8V z&Lr(LStdx_RMI&?GOxq|zcb`vPp6C`eYZi zs6Jr$f>?R0-p>@popLxhw9?k$?5$A41Fs_5taAb8t%=Ry>NfC|=1<<{svfQZKCC&} zQN_z<-e4?;P5#`Pk^Hx3mAXL-V9SD*b>w=33;+JD=j9*|s2v`X#l~%AYUYSZ7i19q zolYD~1~qJN0eYICjQ2LYH4J~TTMax#K7dwMUIk<&&Up6%)baL|;VTN0ONd*ovB9&$6Mu1f1+RDJys@e+j7;~2hoC+B#3n`Dl%LOT^?e&NK;axt&)hu>~% z3a$pUE9Ya)v(eH?aqQ5R9)o7sPM8WHHaYzW-CxabcY0O?l*u)Qpt-6^ttCjH-G7~ph7o9sv(g8kf16E<60%gSZ}%Wwb*(iuPtp1Urxd^>@6<6 zy$Dt+H&_AxY)SU`2Zy`>)}Oh5NXMzuTzbCm`iOmkww%Y0b~Qyppw1FX+_fA2EByJUdkeAl9=mvJxRA2Q-mc<(#fY@$I~zKm)A`mu zh#XO%+;QrDhGdE@})}k= zA@3QphRfMe;@n9Q(V;;MIceY)Dv`JYv=6C&&`ngYOer`F&kHmv&D;I@oQMO3T)XmbYB(5c#R%vg+X8kRheys> z_>}J8Q%VnkQps1XcUN~)OFjc9-#D+6O9EmE${@tN%->D=h)S45bY zC1$L_k1!>_zSgm}5H}hH@Kw1 z5BSz#uebjH>J(UK@+H1Quy!Sri1-0{R3!9hgFjcfGxs*T0mWlu09zJxz8 z(r#nl{#PK1O&M#&N;#Djx%Kl(5r?0SPk)|Qu78G*h! zN6?Bl=tul9=>)p^1Y`*`L7gIASYO%1|N8FVO2Xs${-PWWhCrQXdNV2_a^mU{|L?0; z@+B%HQQOYGmJu!QCHW zZDlss-tNT#P23!3TIT{c{3VW^{W7O+`~|OFJjm6X=fXH=`sGOl?|n$@;zJ|F;dK#V z260uKf@*kJp|0jkLZb;msDx9?V?=&UdZk$I@F};6qfT^BNYkwC#~N%W@H7|0@cZlZ@w3LJn9-rtMtDBR-M6M7^agADM(7H4x}1M;c&v^OUM5M2Ty&9ci!Td@PZRTbqSX}0rr5CLi4U62WUpt^>>U%m_%=P{%xJ~e;#T<<#IyL#@=A)M*=5p z+q_BKgD%zFM?n%v^w_5*#*ZT4eK=94s-Y+#&kmD3kqm^o=BL84#_CQ5WC@joD>qLt zzjT5Z&WFF6N#@wG=>cw^dn?=5y^)=B4}vB@6~ub<7z~DKc2Kd8S;oUT$6tHr5A*fc z9_QNpybCqUE-Q~`r=*2RxO8#-3|r&2M+t{EX4b{>>J-nGwXfq_?)dL`(=8v0 z@*_fBlc(ZQAC@EahSe&uVtaN$qu)=@Whr8s7t?%ziHyBeBITBHLML++dN6Bz1G!zMwSQ_wK4TZ<_c%|L9(u6?bp*Mr zV*JrbENBF}VIuZti*?*mo<-e?{$LdB+%ypA#W=yIO2xTcD%6W-=iJ-aJ@*i|ZG4Db za}UJ6JmaU6k0)d|O zQWuA)qUfcYr}W?c#rLD6Er}K*(2YRPuiFUpF6O(>1bUo@G;a`<1~Q${u4&E8X71VY zF5b5Ld)dDJp2$RN&B;+TyaoMqFHr-$mBB7Hi0ULki5i$Wpwtua_FP+jJei+|^(6#r>340i*#)#LC&vObcR-z+hoBD3nnf*6lfkvoKZxRVDtI9AK z42B{2hiIdM7J5P|gh$36KJ~?q@s(qrVaHucQi=H|35X{}sb~^;(EX`;L$(e`qh+;TRBN{5^U(*J|%cOiK{@Wq~eiE zf=@k(rolOTVTKvbUOU2>>mTO}FMgc6w!Mctwmih1&F^Ewx^1|OJl8;M3)N_TinOfD zlcLaY%0XVizPyT@n81YBXx-1HPQ5*=4ln#2o~3kw7cYE)7X=j(i4~?Q+&Z_HU9)fH&aLlY*W8;~mu#-v!P)u=k}LIk zlG1dY6^Zch8t;8wReAmItDqHBmN+&4HD0@Xh*#$ia^mt+C<(|CP6YLWNIVWKq2Y=r z&F@e3sE@DIJG@KleXD3t z9?WD~bOyw;#%*NR=C`qX_HDd=%e&aPc6Y%w`_KnR>SNJm!KuMuFbosRs-MCGz^@+r z48Q)wPqKT@2Atf4_;693AgQ$GM@+({tGuwZ_`o#KqfZx*lvISoI}p#Nbvya7cYMkz zXeQYrAo~&7R}z6e>-bT|2^|v%i5$Vbj{%*tPjBY+m;|?%enmX4l=u#N zM!D9YQ^g~=yn}c!IEDNq2wUQ$pj9fc=&y6(%3;o4Kf>i3CpbC(Rn9IPLtQ02wUjE5 zAaXNLmVzeB3`h#<5Cn${RZR#aOW+fHJSwcdf7#J!$y?MOgj!pyE+Z~6qH%oI`(Ty? zBfzhWaz(AdzzFV-e*T@I!?9JVb;!|L5k`mvfo8R78{U+#KnOoK|{Qt!=_r3QwdMHLlwLD z8Me>e&U&|vjq7$Ww|)oWXGlEk*z%UpT{%vbmq6)XT=)hz(i?cGvbc1U3)c>#PB?Mt zt9a*8nZc>gkFbf>&$*C2ZLh}lmLP?uDfxcVq)?Hbl@+Do-h5L3o*nMH;*-`!60x24 z8cYRx{T^>Frqk|vowN#cS9d?FHWe+gp=kEwe)lLFqZ=S;2d7lyexu9n&*+OI5i86vdSZSqN zkZDoS1l5#VHtc2R+=JY??fvYY{ia%x2Pue2xEKtEVIq_~dC~?+sah%2FMaNx@ZzOE zW9v?#Dk;u~o?j}}))}5vpm*)FtOC8+=d_N$>T`9+l2&of;k=NFClR4aD8l@{`+t(X zw|u;!Jd&2xIJAgL?*p5a#|Jk4v14DUOTNpz+Kjj>{KV{ILl8flO1^p$X{v(x7DGLZ z!qeP{iPnPtu4gt@U)QZOx9oRXY@|euRE`~n3)dy}2GWP7OSmP@U3&?53FaFv;zXP4 zKYQ9P!LW*EmEn{v2S=u*^(|BR$B)E03ZNK zL_t(pfZB{m_fjswKB^6L`%L}OI{(`oOD#IhmN5g|GB~% zlos}Rg$#VPeb+LnD{;>*(_o+ClIB*nrRIyI7Pn=bu~_5nY4>9L{zTu zdtcX-blK|+27{rE{AhVUhMN8Omb*X9bB}+CR0O9WF2zg8%_n_a4C9_Zr6f^0Tw~&t zs^-$bM=n2&-jb2$AO~=|F9ri85bC8h5slof{@T@-sXg^7H`=qK#Rfxr`$RSBr`VP; z^u=)*2EVa&q!<)u&#$K*MFLU^$Ak0V7YOjC@!JXyAuZN3w6*?PkgCfFb{*eS6KY#x z;dvxFTm{vA znQBTo)#xP<=%ImVrqb|JmkuH>JS|sE5J`L7;31i6+PKePFf`wlwqGWK92gBEsa^xU zAMZOolRBPy=w(-%w_5j^4V%h+UpzrF1~zI{e0SNU2NZQU-oI# zouc!FqL)xu7uRUBhmXz*WDEvF4?IBv9^QDzdpNQ9B`Py1q7@|Zc<;?W8pay0Ts#zY zVLq3@2Ye75*V3qr5(sq7mBnAV{G|UCDyUWw=$SlUzS(Uv8^*{v4VfovXTR8C&|xnU zKb7{)2(liGN)suUK@tmtB+{pSjg&9PG~ne*($+k$_;WK6hBj@*N-J=j_q^J?4&1+W zc7Z_)z2YP%oT^$yMDV@}k|3cw5N8*@!TIZl`O3+E$NFS5w{3ejcW!(;w{3kNYu(0B zP3~%QF8I9XS6$`01Zf%zp9}^=&v2eM-|=3KedBS~Ziw=xUZR~bBV!m}q~dt#(jouL zTRxD4a*6|PKl_1l0*jI)T*Ms^^E-7u`=cp8#G%{AGw zAM{v-p8f5R&h!tm8r}9tZJ@P@Um5iW4Bj_yu3T(LWH5BUK{B}u9sB*An{?X}L-A?d zz>$JL)hi;E*xt?z zvJSBS9`Pw&6jkBu_2)Tv<1kO2`ZeZewsG60Z|3z| zKEQ1o-@;5~BdRGvk~cUP!N_xi!B9%(zjbyOn`U-^oI!mBCqCF4YvOJgV~7`Cx%@Qm z)bABUy(kjZ)~X}YMD&2be)$}@Q&*3HQ=EFdCUNWa9thb{Zi5jbE5SvbM0-$W=*Rk6 zzTWIyQ5TN9pb_1V%KMCbnN%-nF~I1q>u%<9r3WL$@2xuB(9T*~ZM~Sy81Fe9`D=9v z>SIGtl9-Zva1v5;aR?P)5wwCsP^sd*;+#X&;U()Crii9oT|CG0=O5#_bB`ekcW!?d zyJp|cUEAKzY~>c5Bt@MUgTY|v3Z78tDgt|Mc?YlXSj_SgypiXIu}@Gur>-600I*L* z6!oB`kmmr>bFdPnk}fWs^8fZLf19dvR8Ue4I&I|7-n&o0R70@QGa2u7gl2HEY4NSz*Zu>*a-H(1#OLMP)`9>Ljr!;j%r6qiGe1<5wwnBfd%9rb!Y znOzQcuY_?aIqWKHz4ZRz$Ms}?TEBab^C;1OQQu@y%378xb(M>5fNpKjGA>4jE#J?> z9+OT;%avly;_Dm#)+(S3tFAvUZQF=ZB-ra70GN4O)lT#ay;YzW9t5f&8mi4jB*?Yk zAg1k^@fI9DyehZl;%do&_tntck19|>J-9?sUt#;~UUtoW6ZhWoSJ}RPuPqjX!O%nI zT|3D7)uW%`v#)%NbsGeiDn60cwz%KvqCy%n#shtD6h7P`r~Gzs9PwCU`--)Xm4qV8`R z0`ZRH!4~mDGcEQy8*2o9ZwYjV1q#d7h`Lp)%*$SXL*2{MzLbWL>-iqXU$O`&Z%ZP% zNfzST)%ponG>-`~gPp~{LGL`t({m9)lJI+Rp7RUObMD%6eErmKFf+53J)1tj?OWc* zo!j0S@(-=l`GoN9)mQ~zj2mNtTkI34nifbAlZG)ErU8PFr)1!DTlV0OICOm#?`GnI zyb*Iy4e@|s%GYm0{N7kj=cu9zr>-4=^*a$&)kh`aVYCb)y#xst^&sxVm0!j=hi2EL zV#$ZWU>HtPFOwlNvQn@x+#teaTnse=!WxQ>kiC%cYGBFf>en zp9fCQzl7?0Mc5AyqloGO6}NBL8t4ZQhbV7&=Eq1iIA(`OMQryO3o5(H7ygu&FFnrZU;Y@|*WJtB zTmKvG+WdYttlf!7fJLjCmgyc&v!1op~PEe3ZSMw8sZXUHV`3HR4M(MX4Yz#d~lB85R`}T4DQ! zJGgE3TiAQ+cW}$>UhuJRO4MV|QsazdHy8|4Vt=Cv3NoLb-+c1_;>E?^4OQvPv_9cC z=={1t!o@Vur&|MkrTffzXLYDkNFD$5+x{rYy=Uy*sXda)y-T==>goBT{ui7gg@JYI z(8SwGe}lm=*q}fNM^}yf7`sHen}EV3L=1-Mg!il4c$c&wxV#yUp2C0We0)270BjG* z42I?f6iOA53POr{C5h#ADk-WCVUcqSFL3_)i#&7gKQUL?#ogOJ$evq1$o38QkX56* ztO7ljo%^WMZadFlm|&Zd+ZF)FwhcQuc0*7E#3Yyv;zyXzO840(q_)n~F_?UL#O^DPoMq-8`kaQbsHbzjxF!u4O`!rHxvYq(|CSdObgv$ zm=2qqvSagIaM6>vq-Ak}!7vU!(}|F#o|6kl*fAFmU8t~vw;M{_Oo z!~S);F1r0Zi%ZHK-)67vRzrPM=SIy0mA)s-@<&5i3nkxbkZvjDPF_fygrIJWgxCF&>?dB;C-}o%gpZ^SNl8xN4<=eP>%lml4)_2z$j@oiH7^cD| zizKXNGpIB2+%OSvQo(DLM1&Jpzk#Z%#Gq442t;}&&;`|By!bzXhRS$}N5Djavo;tE zhQXi@8OuloIKxb5j6@&XwKF7b(?M6G5BlC<{O)huNc3(<*Rt%Yh*$w}N%PlEU!HdJ z86(dPo!aSfT?@?%j2uL5ViixSE>?VI2eW3|v+hgb?Z{$(tICeuav}*X<49gS~c-# zSXbSWKg}A6w)0+f6_rh1-g@1+HujG%ys|f0U0bu!<16?%zAIC~q- zsKAEzl~xiAW5l+0qOII3#)UX<+Pr9$^*L(O-S2s9eE;TT)oO1r<}q%cn}BsPf9<|@ zlunw|H3_w+EuF{uWW5O47+ouk4I{a05uH_12wMPyn|X6 zYlVBg;ji%gxli-_xlgku*~p#S-@{#-KENBcerrQ~m$i<|eipu0&3eeyeqOVsR)}FR zj8>3U^JKGY<_bSfR%{|#<4vZ>e~sV+@?L#vvj?=zdTq3hlSdPYDybx>R5`J56bRDqW-{?qEO%8%kC%C$t%c56RkFbs!qu%qYGG0&q3 zOOA%SFm$9My-BV@pVTBlY`!`eFtc|C!zg0pG|#&*tiz7wm#SAea_0AW{>&e-E?LK2 zTR*^^Ti?l@TfQymt!L!8hS*OeBjN>cSx?e}opdNBw`MSmTHcoL$aDshrC@t(n@(p~ zyNcf#CyptNe#VPO)bYy2r_$GNd%($loT~O*{nC;^m%=k`1x3RDa{yroc_%dah(;x#>t<3N2TGOW4im;}mNA1oWX zOEks-Hora2uMOYG?GZm|bJD{D@y76>)3?&7g`QAl$zSCg=ReK!XCGr-Wi5AXdp~d7 z{vmeFJw(lnL}?gRkOB)(Y-XKRysNYQV=#=Et-f{Z?VP>xk|{_`Av!q)I2N(5OTeeW zFKLyN*N^b}M;>%MtUf5I&|e7`%};;F^W}=}M;!YAQ91SeOQb3!;t@&k2$063A4cjK z43-oRD;k)*>cDX#Y(PlI8=aF+5F@{UQ7A{c!# zx%Qycmww-qz2;hJ%k_N!txC`)oFN^gE_yH5HV)%Awn6l?ClQ9W*SAh?A9Ay66E>!U zhzl#JqN-tE$P%K8P$Bh8kR*s-;_A)QJagugJbmuh**LR^Q?IczkG@?UXa~F9GoI!&=JIq1FU?@O}jH zT%3eTbQIOksU+vV@WIePR2>zG4NJV$of{0J=q0VFcm;Rb|E{q~0TTTy*Mv`1t2cLLx#6 zNa{Fy?Rh?cRQ@ECruU521iBLYc8h!TQG|zs9~I!GOHVC5&Obn;5|8VrbsMq-jdp{< z&=E{FY0{mWksjSA*kc(J_K`#%20uRxO>~2ypNXfzVE%4+_c?LV^oTsoQJ;cT(72G) zgHwk{g0hs4kcg1V5>g4(<*Bc5cHt=JUivujv)s1j9o)11uW0Yn zyun}?L_kwelCaZ6(;UNQb3e|UgU8`C#c6_fDIVpQFFmy+TXCwrpEc~rd&RQ%_^1kZ z>iA*QR}c}rOF;!uP#h}pqOB2u!C)}-p3IE2LmTY}H(8QBdZa>|<2GR=dRcQ}50nfB zgQ2;+)Iy7<;6O(%9?*5BuFuOo&&J-=TTShWgp z8-Je|I97>UAOy5n%tA`+8&J{1+ap@Tg5K-VkkuG z67>B2b)}sst>15$!G4DZVpRO?q`)^>IK?Gl<7^m}xV9-TIibsQ5dN-CH+Nhs6Mf9;Vof56c*f6UCx z4EJsOF!$~FYiwJ0H$pX3yVu2zvC*i85Je<5AcaDc(#BZRhcT4IL}CnvR{J~Vs2ZF4 z3JI!;Dv2Jo=cdsCV;z+zlopSu@i`m`xOi;V_ z201hk6+!kX{?SJfM7gr~n%N(NVRX_a8-fYLYEEpWq;+(XSzC{W7V(W`xCVp4Fp^j; zmKb(|Jt@9JRSOTYC*L88gyOoTr6ry|^BX+!weRPVXZ{w4&i)rJ-#mjTi2Cqvc0x}A zc}^tLMFDlF)Jj0|@gzvhSpOq~p^Y3(f*f?%Yt=xci&*|<7<*Ktg5nWLaM9OZS$vHH zQqJOT{U&n2)f_zv;vRk&J)puXXTLVU0M0V)ae@&eqnGd)#y8>Uv>IvH>tLFNyHxs< z2RA0!T@x6hBh{GH%i#S*Mq1R77lu;TzsmA)wsAM~b007JlK$Caizyk(J|2d@7=}MB zYg~uF{+maGN($mha9p}^oG-odZ}{ZX-^+hJ{P%hO+#j%%UL~t654D0mbYqi92MCC| ztRO!Bto&AO&l?PFphDK^5Uo%p!E1H2vN&~vJvnZenAk2DS&t%cNqgshR?cFcRN?KQv>M0}f zA0;k*n52l=%>M0x)xHb{TVEp_DwQlFGR{6?B_CRLz%YE-mi|S7!OsRc7ZF9g0uB)& z(HR_Y>dI4m?$}TBYfpcK&mH|3$L9}%^Qa~uMYTB}V)#^aHK1dPF8XlWy;#uFal z`|}IWBP5_B(BM~mUA{h!ak>?Y3~~9m;BPbq=N#TwsU|6>uDwt#@M98E_USA zpoWf~S$F}T3W=SQGSs2g6tA0Lx2FGRBiU=>V`yAlZlcKZ(QwLcRU1(Dz1q6Yv}cX< z?a#Q{`Z5^C#>d%qow9giQU`ZB7ILG14K;ZoE?1WDI1~j}Wih?ZvllIdk3@MJV#_Hb)4y#@&46f^mT@s3$M zFoPs1>ILU2c&eP5f03QDdwPEVHcE!Y?JsU4MUeyU8fvLe@SlRwW2_#ZP1TP6b1#w{? zjzcayj`_P`P!N!%d@YCOxQ^2cM*-=XplIU_g8G;$xs(gKpOe=PBbp9u5r&0m5wpQy z2HWOzMR(DKUbc>d4%+zUU$FMs93eDUR<Dy zr*R4K1cyC9X+$^bDO?JXa^vOe`9{MCtu@`XKpM7I67!0Fq`^G)_FT?f@dmOU- z)|5)i&JVNy@_2Qj5I?S&L?o)DyPSZ)DG60l0s>BCZxbPq^aC;s*MQj*rM0qeTAihfC9QcH@^GC^w z!^`nxD<|xcwnsqy0mPkOIR50#^vb|ap5>P?#6(ymXhI=A3e%5PfIC_m!M>b@KY$7X zhYvxVlw0Z2?#awz6TdP}bCx;(ek9g*?@w>*j(iWRn|JoP@k_n5bwqBZit^Pe&AX{s zU%vLrkzfhCtGJeYkLw}oTjtl->TB4RuqH>5*=g1GB`S*Z+-wb~XXWN&ofx3vR}>r{mY`nn!E@w2sLfAgYLJ{@Z4Mr5fmPr!M4e>cY>Z!s&&heD1}M@gEO<4}Ws( zW1P8uB$DSL-qgnU(?{#0#h5fks*3p*!?bvsT-ZT@bJva{qNGwmm|5wN*xVZo?S*NjCi&yE##Ja?_NF8Rvc2t4xr?=YCm5P>pgp zQbn}Fjr1zdUHDy|zxWw$S-+RJ?EDCKZF?_kl8t~1h2}zDah|=GSF1laOc4amU3(=y zP%;ADFj)u%-7c7-XSl^7szGtJwPR{Ypl2++)RdC^<;DNOj-MF}BM*nw zfLRhe8tX6wWEPh7fBs9WL?6{4NJnRn8JRxj`=u4ZZuZ0m$mZDqy_4r^vMpt=JEsKN zLlkx3J&1FmxLkvJy0V0mHQ=k9yMB~Ee(_(h_T^dLu>Ji!u=68q+i*`bW3dul((s*B zY=R6^!V|!i8)vwYUd>INJsdZTzkf*!jihTu-y*_=8?R*lEs-q``f9NPEjuv2+WWVq zfet|%1{D$1{p6JwafvIViK(H_!HFgKhOy3BOe?bOeLju*eH^7+`n^v|uS6d)jGC;A zt-r}dt7dY=c^{W*_F`ge1o`yF45JiZjePaF9aYKasSKZ6)|yLQ6(@pL6GVlIcv3GU z2#BZZ7IA(ps#Vk_;Z&|Fi+Y3S&ioF~pZ$Gq+w>Ohzva8Q_m&Su3MJAaYWB}CJvejy zIZ{bu(?zR7H;g}A7F#q(r+M|9yLyUmMrKrXDRSICvM6nn+ijjuQokP&P=8|ng(``s zYI|#B0%0Q<4daMTauQqT{Ygkf>%Ua$zb5_u$9@myaa(!-03ZNKL_t(V0%$FvR}i5mnqOaj~g#5a47qs#h~_dHc!3ISDE)N>QFC;QxH$8 z3T}z2+@y-&dX!KJou?<>qO#m4@l?1^NHL zD_6c6PIO3O17nytBr#V`?c&BU%hdX zRKdB5>4O;z27{pubmuLGmEezSVqZ+q`#E;w_JGA;7)U}Yc5Md3IPF*47vl?2EgsNE zBtg_6>TpQjxIIx%;zJy&0ysf60US;p;*}eEiN84h%Y5R{NBR9D{|6`L58415rUu6^ zJsb9nDpq}Nm=F(-hyo7p(`9VLj!5;7um39G z5naTS;B2D}H-K2>=y)f`%8Reny6JEAmn*jdba;Fh#94kVGJr<4@%>jkme^%C>imdo zi#?d%>xdRG`nvfn*RX}eMcNogDHy}vs&}$vNz`>cx22APm%4S5FgoIwH!YnS_B`Hc!6IRZ+ALwN%H&e_PoH*XDSHdo6FZ4oVuNC=ubhJTKgAp0l5c^!N}$ znpd`GKW-%x(=rBWInI`Vl!LQ|g0h zm5%pky?^S$Cw1VLiwD7rbK#J-_G5l~Ub~9l%0zd*)i)UzSK3+(98TBZFQ>o>sb<x>D zRa6ocO@Hm+K}0KfSH*cuBh0X>@#|)Nn0C9N+l0t4UT(KsvS7SN!=qi3=(b;K0kbm` zVK5k`BpzBRR-SnBydNl0>%6}B!!YVGWHVe|Jk6iJ@Y8&Wb=<$}zva!l{sx<8Zl_rL zqZ0m8Gz~;Nm3aO?Hgu2IsOs_HkQ=EE`MeG5OVUls; z!gEoxNUQt7?NgDgXq+~*(A(G@;SB^$0WL0`#!&@Lg6^2X(63-w&3GI7nSD<7`Wj{O z)c56nWgtn_SgiriischsKcvhA^9`%+J$u+^Fc^mE=gkiF7c2Tul{VXuwdZO_yamWm z{bE1Qk%qQ%ODwX$!Bd~)*Pr@+e(&gyb9~_t8e>iYR8x?o7K4h9WV$b`mD>Bd!YMw3 zVQBl2B>;|I{5;N8!g|$8D3TS+qYRUc3yZICK!T@M_LHffM(10G2717LL`31r_0u`= zq~4yN8Vu7Guo^}wgY(_XzYH1uNqojT==1&3S!j}N_iPtNGy}}^>n%Jn7z`8b{$tOJ zvG8pZSbaXR&Aa=Ca^tgpi!h2y<2A89a%&VVXMFFk=L#d$5PS0m63lZ`9a&$3?;5clY#h<2D1x7;BD z!Xx7Lq5kEI&-j!S%_`5$2^g9#q23Lz5x?cmPgAW{8(+hi+J{ND8$PORmN_Pt*bfKU zdNhA#Fc=1DYeq@7Wu(dTGEY=RA8WS0E4pFy@=%|k;=mu*PpPQ+&dnNB8R9g`h@@A$6!%%G>oLM-=jipQZ!&t$C6YFni=*IPp zSFY_+UcPv!itOj%kE)MC>4Epw)|CnEd6m+i- z$7ZmCYD$8Vpg0m#z$e0u>NURn>M!w$XCCGcUi=uR7mnuFV#8w?%iOtrW1nzw@ikyyCdTDO^6nNb4RqU{M?~b};wivI zXP6yZG8ha7gJE(5fk}`9F%o?==D;e_4F*GBZ2R)et?)HkHgK{gU~($Y`xWnui+2IMl_iqgG&M z7&59zSf^42JP86xijrD2BwP%JF-WY&y%NUIeX=qwb8qW>Wk~FdQokb6`GUyw^(xk0Ob~YYHgh1bmv$uc$|LhD3twUP=W~$I1DpI6i-fO*6ZA z+v|Ra`)~a#ID$@ku8Xd2@&hEpNaN)TPq46b6?F;X9g;e#m77%j3~KzIVd9hHCC!oW$i{fj~>I{h&Xz!s%kn1g@&AiTOakGSyqK=X6L;3c<-C8sdnB!d!Oa& zwXCDFj%M%bx?JatK%EMOh3od@O@-Jn|n>V`;_2C+q`PY*Cd)fU^1|9RL zN>B1FeuW$)eDyzX4s+=cNow=zGF|jIH}T(m?wyjx*k^w66rVf#FZiXee3&mE|7C9Y zg}5GFfuIa`Q#&Epc=s#)YcRChKpK}*6Dy+d)P>K)(-qaQCfy9e z6{hN|$c%tooIet}QA;dbm(&c@n7}F`36-F*%^p+kE*M?``UVQ z79|_ngNsc;z0@@KHPGpomrnEAnaAFLSpvKFbx}o^YbroV80~$_hrY` z+OH08>qXxzT0MRBs82~qNP<2R)ga?CPQj;f$OgkiB$1N>8Q_?W?zp*f663ME?-AHb zer3nl^+VTwmi?8K)Q+;Qy{%b;!C*F_9|2cqW4NLZj3>AZ1M=C+;zBnT`&85+B2=U7 z%XNR1ubulnKKbO|<`0kkH0Q1!3#7RQ@-l=@kx&N|7ry{W3a=gAF))nKdJKyaTsTSL zSpoq;c=Gt8R3$}ZEh&n!6xOT@8tCIuVQw(=#yxlkKl~4mEbR*oL{-Dl=xVJ}ydCCZ zRi}W?r&sV&MOE-rNN~6@d;&VnBzhJM+Hvu*jOBX_>?UjB_jUc6_S-}`$nxZ7hU2dW z(xj!y2lTwZWc2;ZD3j^tpA81XsM&#LvRGr$WV;K0)O76o?2N~~G26JuT&2z>;go(N z4t#>(5vrsD&z<`{9{KtY@?W3*F;2{Xy)gGaFrQKlLX}GRJB`AXB2kpd@)Scmb}wr0 zD>!CQRc@s7eB0T$JN}`+H zHW;QX6dBPX7eDQxgi2py>G5~LMw-{f+2?CVFFd2?ugu)>`-96P+)wN~5chB*Ud+%E^Ush&SW>#fY-GeAh zbynuf_wI7;Ip6*6cOa4#A5)Hr-2n{B!{R=1#Z-?{aHad_E;`3Tv|gPghk}XTYmQG| z|93N%MwuQQmSe9vrXrXTM0YmHvM!T9hoeP{c39{2fdzq_INp0+t!mJ zoytJ;pV9ibJvV47FSOECeEFTvf+az&c}|XTPUMRhlN`EJ>(Qchn*m6$)LH;uAg@X8 zfO8r^VRzt_=>FF2yb?7lAjGgSfr}O`TC@Y3tPhfRmVe5}i^mgR@kBlUksKd;ctkzM z=sWA5c$zF)w8LXjR&m@7tYZPsqwR-{J~r%(MLOf%VPi}p1=MLvt5FCUz-C}!5G79U zTnvN_tfvUCrg!lB*M1J4`N~ziw)i^*4>;(ft2)0Cr6!~G^?%X@1HO3Y*Rd{ZC=@FE zri>6&8Xz=aKp>dde$fuKi6?_EZQTK`LW!JbRd-mU?+Va=MIsQOa`(Y&AX0a%!&ixB zjz@?N3@8#U+L(gSXweTuJHRn^35eYffHs2Y(V|6*7Hv0c$2ixI&JB*bH8Wa_Gkdjx zC;`-PW5y~-GB7pVxW$&!J7)!yk>mirD?GZ|x{cqv{&V=%mwyz0@%AraOV=DNl423j zXoKhbqOKS%AZTan_>0^BFN^@f4mc;)T=Seb1=K=SVJOb!Xoo%db$Ir@^=|;AzJMr7 zZ1g@KA7sJ%zSro)4=sNGs*r-E0in!Etg9Kgkf>Cq^zMU@>PK9i>b~FYKa*G3gEjJse%IS|}wyAS6_hub9&&C<2wzE^tHBi`i?Z9iz6hU8#0Ynco+ z4!W1-i3wRpme5RW%;3l2j-NBk6OyhzA4Qv~stfwZmQYIVIFljApu1lg;0m&{wPnx4 zmmYQeEZY*!p&dG$3x%wKW?0*ZhmMX1vTjUQ+|%( zGsE5cpzf@kS9ktCwtf}LPs|LL-)Vl&P8N!H=dbc6AI_iV?r%U6SLgT34p)ruenY@- zG(Lao&%Q?=C?VHD@5e$Ph=Kn9%)Pj3)d1S?yPpgwAr|S47VRKe45Fe%i#9}n6m4Sr zDfU&gXmOrKi)&xZT@HtlG8$w+VzFV@@u#;wjbDBFZ{g2x{h!#9H4pEwZyr~d&W+ka zxRr{_= zpI0)=TX#X^3Jiz{3<#_YVmU&M95o7~#iSrw91DAG<6C9ohEm@#BIrrQ*fo1=RJyc# zG4`nVIY@Czv}n=dygUj%ZOjkwIRIj`4{uDN31q#RBBqDz<0e)o&OuZ@}(ccmu`Iu zsjT_&EFhF(eg&X_Di`eZ0HQ}b^!dI4@cQxq0zv)0ALHhOFQb73Bo-MdSevnVq8(`? zGR{p?hLzR=RLLOUI`6-Q-gkBSI8+uN-10g(j)AVKunH*C5oqAJO*^auj~2&uv_lRO zL=gS(_H8aq7k$P-1JOHwqD70gck?ZR=La*8iN125#$arMYI~?-3qw;dX#tF58l;Y4 zzNO3f)3^UGeEQ`d!I$3oB(`KDXP^V@hBUeQaRhNdJm7TGqaE&;9YEV|;CHV5oIfd| zATj_XaMPnjJJLqv&i34X@FoDw0AlE10#^fgP~uHpb`^R<0Yp*|HK2O5CtwV+q8&^- zJb_c(2$-zgbMzSNc*kl476+SO5kxN~wFgb&AX>C&F&2rwtbYK5!3&s0+wVN(@Wbr} z1ciknfvCE|fk>d5K}kTG0AxVQXlGma;@h9bXI}hI@ugdzavJIIN-YjChiZ^>Nuz|z zkywiapL^q1chd7}9|*}y6QVvs6815J*;Zz8DPTKX!)0;FjJX6K&F_pmF} z>*s*_$>=YmMT_%klKtDBe*Gb3p7)ANLfkS@_qwB;@n~?>Efi1#QUb%k3A6<8oRP5r zYQaQMq-dx`YFF{+w?2u_y!26g`Igg2ciO({)zAfmM&v>q>ipgni+yeB3wZV2-vkRg z271C!3os{Ojkrv-qds2jW*OKsGOTho}`*iB+RkcJQ)&4;M zZSVYvuP?u7KmFDJ8YJo&bpj7XsB zrTIf6`2NuZedBBWaDTL;&*bxSRQ=T1?|Zy!uO!>Hz_9)zw3YL#RsXOXKQUUrv5gNI zW8itv*-hm(AGH7K=S)#(DvXD&-?{ql>bNz&qBu}(z26*k{&%)l@6C~-eQc>6P0x8+=xtWV;f_5K4NxX*G~;W4rkGIU+6;cwb3w+GSq$6e zy<%aJ(dO$t+jGc#g8&E-lm*9V7T^8Y-^csregy2aU^>#Qa&b|olTICY!6^(!RG{wApuR8Tn` z-Lwz#gef}CXwjk_goHR+v^Z`Cd+xetCkBY!W;xiEo&rwX7VY1^L= z(mzW3Bu*TLqjtH)Z<#yY9N`25{x4`nhngwC#1ef1(|9zg|Z!iR;4z+lY_5#XgPg zcIb^21Pd06O`GELZ~Y>E<>im!8+U&TfedL0PzW@Dmp+c85^bvjZspNJWH9f$OAPS{ z6I@vCVw2C`{Aaki@?vbD!{m1({5tyi!wRvK6H2$#>!ADWv#)#g&M_BF*V5`pM0EGT zt>Vi>!O9@zJ!fJ@+vNbESXDk+41y1CfV6kxVKT==0IUK4R=hgCIZ^$tgBWP-G}sD2 zQuqmgx5u9yaIM9^A?g?rfsO0|>wLW_Gm%R`b|(l@2CwRV&+jE?GqckK4WK;4eXj@3 zdmY19)#91J4(wNgMFBcnU#?5Ysf6}|PAf2K9}Qs_GdU&?iGoC7K>nmmp+&p>T&m-UxF69(7e|Pe?eW|rDuGRC17YXgz|2ls5`7rD8bj{ZTUc0HI( z|9a5;K|vH~0BD0@u-sa}AH4NX@Tr&o7GAsiZ(x928peeM7Mk~kREY;ZG9vf_rqL#3 zZVJ?c=U>0~JNVMuzn*V|M2ogp>)b)a!I%@5E3o43>g&J`B7>3#EI#zY3uLZdb-wps z|K%;0gpfj&U`pqH1hG8iK^rXb6hX8CV3485L-YaB_9Ivv*VwE2NX9`1$-*h!egH!! z@aX?i`kVBWaDft38RS{sDgekyM5&V7mzQqfJTKZt=w>-FAP(sU5$6T46q#p0srw88 z>>0&{6iG?yN&%ZofN{|n{$5lq2uc2tyk?97)+(|rgUa?vV!GLzFT9 zScJ&<{^@4_6EPh7J-ik+O_29vr?g6E3{&@e6@Zt@^@@9BxNfojecW-KaBRPKn@5W_ ziFT;K@*!F0wP5ESX8MPVHMn#6)Isux=vT)%0NJ;*f|wxcbkr4s_4YgX{cHa#Uc2}E z_~0Xd2ale3-Y>x>S9GC3#-#Fps;@=c?>+{UT&}XX@diHk=Kl^u7C?f7xb+*!eFrnw zJh1VSWRB1zisjZqIS(N1GZ>S)H(tF84S@P;`qPz!M@4;N8@*?=y?3f{+)Z9Ue5gV6 z&XYE@znbV{$x-8+>hd*5rA_K3HS&>B@eiF^-l_^E2cC;6C`hO%puzwtMAIBJCM{`- zlg;N3cd&%113$@GPehKj4Th>gYb!;e*=-;eux$_AYXRz}X$ot_&s73Z;u+R}ZPz_p zkR~x0egmab8l?;@H$cPEASo+7YXE{v8y?(GL(t}cv3uU~28k~mU+Ix%N1HS&*J36l z4S?C?dnbSc-;=}v`$c;Qm-&R70_5p*_cp^8>lhMS?2cb>e920+P(4W2wGAu#>S=?Ox zGH$HCgvU-kgYSCe@8Qvt&qH9r?BbuYq)Q&sP)UC~+Wz$~ilVZsTles5U;7x^*#^`w zP$4%&ix#arl4lTGh`5|=rL}~g1@XtBQ05(Sr_*?2%C zTC`{-NMREELELEBc_MJ|5~lGp!$$?83++K6o)PtbD70AskOKJPvJ%=OB#s1toE~{T zRzv{~#W&$4N|Fj_PGkTjWEmq%1qKWeAZ>fSYBf+Lq^X5imm&}~kflJD*nmRjQn&)38n(H%XOZ+;|2y^Y8$0o4Jb-7cg1ZjDwIlJEgo0UL2Lk5^cCXJf4KVl1 zmByswHH(5d?lw5mHuCYqZ1Ms@Md9ko7Kvwqi|FqA`?wjB#8sr+2ZEvE4a1#5Ahx6eh7c%@t?r)#+(P- z>-tElZ1iaR<3j;}1Z}&8fA!6u!G>K0h(Q8J6oHM66fIhx{LH|>s>RaQO?(2XA6FtG z^ktw^_3!Qww$F?4B~bEPfA|CKpmhldSQ*MrJCk<+V$^YVc2m5EdFQOz5Lo8HgzU1y ztg6>8)q;sSrfRt33Iy`|-{E%+7wk&cqCRM_-x&ZpaHe#kmJ?WSh|?V-MelogZaC zw;##uAikn=J#hann7id-lHD~m5+_CCWjfkh?o~sCv@Nii(=@f2BFhwFz215`5%V%1 z81hxOCtwzU=C z6E;K!@$vQFTi*^zgJF!*={H8vXc_&i0yHPe??r)s2QrU8xYkzw z`yiSgT(pmCf|Pvn001BWNklE4B+D$yGY-dXMUOK%C;jj?k5qbVGN6+dc zSV$8v6i@?b=H=BAkl*4VG7tfn4Ad&f5dQMc@8FGlU&Ql|d=&4y@Ha4PP9vP&bH&^( zKqgLzeYAG^0KfL?$FQ*RCK3Rm1Y!X+gL3q7htBsRZeESnmwWk0IH^&qz?}#GB*Tf% z4%EzvugOLDM4?EhKKr_6S0UmOolv#QLc!LZ?C!;`8g!W)1heT(G{vHF%zU6Asn=LTUN6jS7#{~wRKQtx>(mj`U%hDMQXQTi*hWSk4HwFQlwjcMo57`g>^FH5^ z-~KL7uyj6uufE^?m?-?;aAY#>as9%#?4~#R?^tt5Cp!GQkjjwSPZY8pHXjG>TRs7--V*(0q;^@ zf$?Dd4wlz%LpAf)3*V{v&^63Cd#}8FlS)ZTzv^og45m=THVCS7E*r!|P%kSZMI~5- z!l4RsY^)Al_yCea$#3u!e#%W*cWNTbGKh7oLk`dFrGr#TbCccYW@cS+HfA81$^dPk zF_pm>1CU~7HjH0Ck2FE&+00@+CMeo|lPIZrEE1@glHAZ>z)sgG53l!MEB#Z7zlwOL z!yl1!tWR9Jw(YY{a1ReWOe{lVnL(3glfX0&W5V`ppMAd9AVxYayt5n^O-doPQL)Z@ z<;lZ>$I>UJ8$|{YHh*ac1F$Z}Eyr4wTwb{nckeddF8t@1^1Be5Nmx^StRB zYX|EiShsGrWDS3M^Ox|_+n>dUF8?IHG#adS=z&j@#w_y+S0H3lD!UMb1nEV#G9LuS=feLj+2|s$IiFAPzLF?LM7o0YHkM zrVOZn)ZV-K>(P%zn|O-_Zlc9;vpd^v6QLI%bC_ieDnWn;L|t-#LTlR>7t~6i*}aTY z-KJyZs9Nc$WNrB20BWaQNgj08Tw)~zdszzD43JEbwG$9$*j#to(_#&>tjP5ocd{K&Tr-+s{qbJO-{&@a%j|RpvA6_F2+Z(+cmfH} zLIQ~T`zt_y@|)hq5F`^En>mMbXC4M&22g@QiqpsEaPs&?uo|eiK0AGU9w(2_IRFru z(?iD;PR(AxiR0(}hNM8@bRx;K+^eI2OFV81LZdYP3t+Z*Un$@PPZ(XqCo=xFFP;CO z+tlcvfp;Wf+hQ6PCRuAG5DZd1l}ve)xZJa7>D(`)vF{AV5L zCdyzF!2h7ru8!QG0!0I)Zc}G-BLiUk7!Mlz4L{y}dvSs!G2mt!P+)r2jk!kCf{lV1 zXikZjBQn1IGpH0yeP{wAL3w|!9u!ASQPl8X$pQFsu~hBOq#zVFpezhZvQAx_yWv{e z^Hf7;;xfp(+<&d%L%EjMX;@E+OiO+rF@!o~b~H7uV=k~=L~PKKHGJ;c|BNqR|0I6k z>Hh(b9DfE%83+lKeF1qTKYYS=wA)#Zr#m762hb=0Xs#W~ zr?84zAwBQ3LP}R~%^jE9R)mnYTu|KJaNu}*!+oaRDi@P*Y+%(4L0g(!1u0NqgXYKC z$?HvL?oNq)F-8YYG8tG=0EuHfsAu%%g+$dec)2Ru652u?_->dyNGTwHTzF>sG|rvA z1mi*X`BUG9spc#o1fmAcpM4a^rcZdl-r(Y?%b1=zhE6Tkg5_$u+D%J|NtyNDjTSpV znaSkL<<4}bBym;zLDG@Ii z%9qbSQ@T#@Lv_h}U|hL>udXj*dF@WVxwW1y(m=&}y$?@%}X^wV^VF2kY-( z<-r0dkzCO*6kb(Tke`Dpj*adlgEAP?fCzy}JzGz`P@@4;0+kd@Zl0+>4Z-BYXX8Nn za8lB$`eLnagCR`?NZoU#8@?Tx-Mm2nwGF0bobPE)GeCsK6krm7nUGAoteQGbXjVb- zqUE~%WpT=*{$!%>tB{n7!1xW4iZAHuuQDLz>J{~#2hwLfDxX~34kSNZLA}JVYxqR( z@P6H%mnTt36Me@A4{KPI<-n8G%ReZU80eg5%|gY@aaw59^ZHzqM%p%Dt#}qWLsNq^_5Zex zu^l8-+{ARB4?wksK-tSdxc>0eXEh~|Ij~$Dds?h}E@pNN7G*FuAezC#I(9M(V4DD0 z$EdY_1HrSgPnZF$Jvt?p&2mKJcGHjPe5UjNCS^v`~U)2ohJvCBhXc# z3{sW8DZ8e;eZ@1zOA?O|x(k>0qDo8vzGa`Vc?8)2W z`+x>WaJhDd6&puX0l-}%s5SC_Tv}I(b}Gg!*MGNXz>}X#r;g3z)Uo->{k->)kJQMM z_(#hb+zG(I%{yO(AcFoZKd_VNc2kHAV_o$z1)wB&ckNAl_SKJJLs!wT z2`rZiZ<4rEqX@EwHa2UtXxq&?1AkJQz2sGK74^=yG@)a{o}qSCNGt zEgJeHztL*_Mg-1}z=L-eD-8^su0^sWUtzRc)c zNZ6$KhB8223A7n176b-S21t;ja4cyyPSac&02wI4JthuJga=dE>vn4cpe?{Q@WjRE zz}mocV+wQUzYP$@$>Z}ldF%oj+{CGwa}L0)Xzz1P@);O4>MExwmzXu>L6RgwTKATS=k@B#l!0?4!O_<84;$)Ce*I=y}8Yp|3-NpN%F>wvM?*xJD2{nwxb zwA&k4Sb7~qDWDC&gWZmWo_XXm=+26t8%{0>@XU2e^G%$f&##c8$ZIYqUjW?%!q%EX zDZ$o;fGEN0eFePK5K)5ZSqGa>PbW|aW{))>+A08cqS9%XaQ*J~aNkM!iIeUap>Cr^ zcCqFJaDI?Dg>e)#MCyR^Xgz|o*giw85r?7ecHlx{{96P5g$x1z=p96Nm%;|6jfLZ}B_d z{CP0BlVQSANV|DSxv*hgsYeuZ$!O8)$E6fGTZn5P*kuf=f0X2PHq`x6mVH^-d^fgm zv}nNRL8i1MRIb1mVI9M8( zKX(aJ$qCFfPvHF7%P=ntAY7tNeY?ax-L9@A#fbf?q zfKH_fgSgt4WpK9q8kaW1xn82B^LHxgb9%TuFtSNYqTr&QR z0P;@8Mq6<@!dC99U?KM_Qp_do?CR!%8wR~TFO_Qq@VsD#bLliixF^%`-8QF@hZ6wB zxT$J)7yUz}2j>o8zN}Bre#lT|9F|${!5*FaK;Gw1JoLfR22p6g%x|71j zFeph#0R!x~i0C+T%xKXjcl{HviZ6)HSlwC*SvdnLeeeQ@ockDE%OCnjpZ{xG7u2!B z7kV21?qcmxJdz>4SF1_Kc8xQFXB*}yA+hGibBIUdgt|eBoq$V2EyepXJ#$<&|qdUqw(I;0(cJ-XS{8O*jJF9c|`@ZsZ_OAV8;BhFW_A?+~ zh{u!y(9{(m)C6hE=29zJ3Z!WUyOs3rk{yoHa(f0aijd{3&j8?x64g;#bPR4O3m{!)Y_Q5_m z{psD*Q11df2kPN&ZE`m@sYcVelXcy#_3VIhlXr5bgvi8rzaj5OYsRv6j&ad$%6Z-{ zK|&~K^K)r@D7sHCbgC;_XeqU3r6g>ui5uL zRoeDm#@#-0;7>sC*!yuWbkCJ`mv?vjc-`CiGJz*r%WcUZWTapvkg6k;Fo29eocX24 z0AyVqk;GvB`1|pZOaBn(XWuhGEOq3)e>EloLE)qU1%kEKJ^bs}eg^l}-vEn+vOz`` zMh&0}B^OuaF|By!I;mr)?*Krr#W;o>pN=I@`{vh1;Y)TszlYesP$7Syb^j$}2l0tS zG&I7evOoEgg@S}2K9*+{=wJQ3`FJ1j-1$D2L^8pPd#X!URyW?U|Jz^u80wlO0_ej7 zR0CmR5kMc>B7i=4|BPxbM*w{vY)AmT^RvBP9|oYGQ~*8rL)HZCmW8#1wC#1s+YO{! z?XCdE6{lo??Y8~l*rnTO1$T{9cR$PtrW#GmpZN|n8#9m*$qWaUp>>}ZRO>*q%=Alz%G1IZ4bvlp{pO00W# z-R`m0eT|z7Ux6CM;=A9(X1b1*jeA&HzlqKEI_@sL39zjN$Ah*xn7Vv<{yftCdgpr- zPWr}OE6BINf$E0PoK+a3Xfz2)Ltzq>=K3T_8BHjBG6Z-&s<>v{$@N&jU&f!qZ9m&1 zfIfKUx3>Vg3l(-B5BG|JM>=0796)!mE_t@I2WtJgtit}i-rq9;HUlF8MDg^+AHrXI z{HKtZW~qJ*9f??JN9k#~E|8ByEP&S+|1Ey+#xJ04H(`}vC@}y1+R8yP($z7_LGuzu zwv%}s0rdLuzw7xO0rZ_e^B(&J3xaA2R)!GgvH4GZ_pjj8%v|3aNdVx57eIOy;*Wq5 zNcG+OFS_HUT0hamM=r^Ms}$jOQg@Ru?tO4bwLsLk65>G}%kF@%Zixlfoh1_e^)=dnj| z_Sig5%$}<-oQDq_d7Bwg){&+G#s}}{_wscs``|>o7^_vVd$+T@_%5!kqe)cPHg)aG z69Z|s$V&~taB+q;K?CNEX_9{QC0ICu1 z+i|QvZrW6@J|Uukf`e-8THOpE#hlx2{wOA(&Soi(>D886B2}mc0z-GePNH5P-G$uW zxbwSsbLk8C?#KQPo}2rq7al8gx~rSYQu5Xzr#U#AuDyMcucOnMH;LQaE;jCq;;S37 zj?caRkMY{Of07d?O*!8wh3N{5Tv|=3f=3MGyG!{ikHgjLc|tJ{&6;Lz%!L5K3U6SZdwGshPR{?lWhg6IAmaBL2p`FWP_c^`Gf` zfn>}YSEFa3V_*P%lndz419_JW)4Jyw_PF_|nCN=}pqp`rC_@71eL*ALTW2x~0DbtS zY#a&L2(LB40QxwR?#^*g2cXyan7VtYzE_ZV34$)I*Px-O+#?;UV>%ZsnIL&$Ta5Nr z3dyFBwt@DBmi(V%!V)Au`UqtxXJATPaO%PCT!KOheQ;3bitw>QELWJ9h8?r{iMbD= znM`B;{F9hUj^p98--c!~i;HKT$~Ck*XG>(~M2i+}7+FZ8J%U~QD8VvUz-$VQ7F^@l?@vb2g4WRF0 z?B>_eDsgA0Z8%B*{ctnT9cG|PPP&B+NGv}3fq#r|Klix1Z#;5GR zyY=b5jTZs*JsQX(0?>PbU^{ESpk(@J5JvkV+LzdAK*kTfhzBg6D`0&W0FBOok_I;c)a zz-nsNs%iosmPx8KPMN7kJ=V(M8R(M^pdT1rdZn>QY7EjohnabIFaHCaYCKZPU$`XeCjKXx5Q_dza-(j&_nfF- z-pH-wknZxny{j=w}P4&3dVHzJgw&Q6> z+fUSKMpnRduMr-WAtEXU*TGUAgs4CL7K-vk~Eke6xq3sklOY+aD zWL4msGheumK}@dRA#8qacQ2vVgfD8T3Z@2B6v`Gv86KbeE}S`b0q2fChPhKuVY)ek zOBbKZWxUu*mHrp#$Sh|jt_1jq9xBxiND}uhe*G>m{ zUu-=vum10ZtxXGT0z^|-1pskAdTJIhhS6v;8q+|dDPZG1!_x4DpGpr(00NU<6(+3F z1fXVvH7Bv@t#WzDm?k7c#=?e zNI_U`-@%{X_#|Gv`&&>}G^FVy(nLXq{O98`aHN;OSbTkq^H%{22(d4h;Z9pR{Cj{R zqHrv9^^=~|N5r3b`KR>S$}8x%w;UPhySV>d-Sp@g=(}&fVZJ#s&SsB(UtQBlMNRVZUYzxRzf=C#n z0+2w(I2jhQ5D^eHkh55{2{ozHW(52Ga-g}0fDHTZPQV~sJaY-tjnjDI;r!mzy?iWibfmGFkoIP8)#4-t*!bByYE9kQ$0yfd2McaK`6;9+-UITl-Rh7J| z6v-7T7RHL?iT@{sqNi1j;CGkb#74G?n+vaCJzc@V{nxO#@;WwKTa{wJ06?5UC2_t_ z7{ua1^#mdSGw09XWM9aiI+FZJ^0>k%np1>EgJ7moG#UoYDFZfTo%QfAbrLVz)&0<2 z7hc;<@_W>afcC$^?T@g>BB8@Y<)i9<*{3XE6b$t8SaLBW?cMyE020n90ouH7M*eT{ zq#1T{h?GG!15tvenZfgOAI1A0{wQYn7>J7X$`zZJd>uFl>u=rMB40<4I*ODpwHEN= z%}?W%yT1!)>i$Lz-#(65Bw_Pmn?8yR^uy0UALf1{1ATk5(0mLPZDw%g~|J5l>>X<3wZR*dvWr_Lzp}LBnp;D0F1oPg!lqIjDnr$ih%}cAavE1@P(IZi)M0B zs4p)DRI0dM@z-%7jutIiue3b`@LXGrv|5bK7ceHc%}00!JJ-jEL_s1Z@l3)(ii${a z?(}7xC&FXrpY@+{AURlYku!aB?=HVoer!4tfU}v zr-X`ANUrFqio4GsttFPQwE?sUP)+jtVM9n7z|=I*oB~V&G^QGUlUYlNb_tBvGV8oE zMu>7-`PQk0KL&ob2YXA(VcoBz-7?A(8XDRTlKzm_TE;B-N`hLKx4!uvL#5`9eNL$fv0j(_{Z41(M>UpnWEEaz&F&c9j z7*l{lL8=8U00xiEKZ|q69>J;Ei@0?0S)8AJ1gDPAxuDb&WfZ;j@e3|ZzoY$CX6B%6 z9yc_jMT<6^gDyMFZhy3W99GD_R2b>rR})diSNhYf_1~fH6{0G?yUfB(1V z+`RJ=Hro$y>;4;fcljDx>9Pk*$ro4?zqnUxXegBnMt85jZnLx+cQ>a9jcG;FuxL&@ z=DL%X)zWjLrXl+;?y*SdxNfXIzUcMZ7}M)R?%3wn zJ~Plq-Iiqrda>bEEFh#omP7$&zlvC>GMZ)v?>YYi_|8ZEbDVC@c~5*WWVO2QO&k=b z4S;}aOMi?n-To}zdGHEUtt*yo-Dz88>!i&z1yLg}!mVm^iOXJn6&dJ}fxg{!A_IMZ z_{{>!>I#V>gHQuAW(xn^hkmm^1HJ2&t1e>LvTcvWM+Qc;Xh$X=j|`t^9SdWxfS_}% zI8ei_7b1c*O<^|^WG(f8xdN6ut{V~-%Jpyy5J!t*b0O%?zbj}A3ND;^59ZH5i8IIM zae3}poSA(br;eZNC_*Gf!7M6Xr<&wEXJFO@W!r)0g;g2sz1orJ08~ke7A@KYJLtmq zQl4ycAOBQ-Pw>ZuG*tymr_w2!?GjZIaM<7sz7(FD;|s(Q2xn#=#+liNap|FF3f8(B zw6b+9+`op!<=3&iehZ6B*Kqg#TUbw*^Y0EU#%i(kDQmw7WtE*ZM%uD-uVrDeevJmu@leY+b{euo;vekB&ON5$vJ6YdKXC%78@H6 z@XFF};I+ly#`4x}C^Y~Om<-fru$1IAqQX{PsP&|SnC!^#VFa(GzMz*Zd!cU zL)6{L)_4LhhlO^Gg}q>xgzwCYrAf8*G^w1}agrqdC_ZbKZ{mu6p=Sg7Y`t*e%8UdgIxql653TdPdj5kekHBLc2QuKd%g~WN#?nvyn3$uQA0b^*)#^R& zx5jxnKd8x7b%cG@seeA~MpwxFE}#28Jazi}arxZ$Vw#Q@xKO8$?0|!+CrG((Mp-$n zU-sW9NIcy+R`psd?c2Ea?w{c6i@%Gztye)}YR7PBmqCl9DvimX4?8~V@Q+f?ws$AA zc%*)1PzHCW!o#p@c31!IvVZoDflfoMpZUojp<1Pkac`1^G0KV@uW)0Q@n3z!^^C=@rvCZtn25-_7h8$|#_z96O7%yxd! z0A%xJ&N283UBRM4d^oPpP?D0bC8%buun!fiaBG2d-7(7T8X;{LQY!AVQYx6~q+FPb z;KB;PY;vg}_udqN{m8<2;^FVasbdf0;WJNR?(9=|V($GQBtHK2Kt}BSXwjlYJG3^{ zn8KyGXK`unS+B?L)`+!@dstb!iMQ^)g!`+DSXliQ7MI=#dR!nC7-4@jSA8+mzzz&~ zgN48VFb)SG#m1^)Yt1v+VZf}InkG!mD3ZWbpK^(o3PY$Uc&dT0pC=fuaGr|#q(l8x zeWEA|8#Hgcl4d;dyT{>E%s+;<$k#6FAx+$PswLJOocvoz@y5~@aBb<|!@N$obpEg6 zk>l^frBffo`PnBsq~wx3Tb6!ojnPk_kk zSI5FY-HqisjCHR^>pwsyST{^!7she32hVY2jKoQupeW#@%AV!mYbda~au=$8u}ECi z8`S8V!-A-R_NGO;sYo{s+ASx;l8HM7L_XjY5fG^_3`$;l#oZ4BASEZK>x*XwJYlMF z0vFG{2bbrb!Rgs~JaYC5=FUD{EyP(i21FT5K_4*!4$-1Ti*_iNG&KA=4$nAu82QO# z=W+7bSv+##c@PDbpkVRcx3IEx8w<;?;l}M(u(Y{=l?MwT7Kj>uop36u&{x-RDw2&q zapiSAMXE@4hC?AyRxChMmQM5U$0NQXTg$Q`_-sfGSx4 zS~MFc@c8lX!ntEt@W|=sac<^GOq&x0S;%ir(zc_wQ_9TyUGj1>a2ul_cfG~6*YWP= z8@Rji4cuG1hMTKj=}5s8Oa?-Lutc)v5%aSVmppj=Mnr%i$AV`8G=oYLN;Cb}7j@MO zBuKabDu49GXY_M7Kjp!Y{VgIIn3WY;KkEo2NREbtOTQw3!?od{Ar|jj*cByQh6|5J zxgbmi)H$AojyjL`?bXpuCP1{i$gqT?Rn{32jGv#Nx^|+*o)S_cv~0ap@Wsm#t(cxwa6`a`iW62^98pFNrdMlB71u4* z03kDWkZOO!G1~Td#v$QyxG;Mj>-dln-_MAL%TVKBT!2jVg$W^ebmYF{DIQvm+(+p7 zNx@oZsk^$Q3^ss>E%^$@OmYfmW*)=onM*j{xPVjB7jb<0JV>T+a{2;JOg&UD$)XIU z3=7L&cV(JbaC_+`w3)HI{wD6XZ{gnR>wsa12#i0qYyCxnT$rks3R`mR42lvid+!H# z>Q{Cm;WCN-V=pCKMp1lqza(5NwA5Cn^xY}``eQ$Vk6iijeiAP3;Gy5JQ&nO$p9GOD z1&;d}V08~54Wc!>93bJi)lss}b{bl%Y2Y^ORlq79Z8{jidE7$Z(R|qJfLX0S0v>`E zAJkL|ZkFacIaUP0vE|ZDSKza~1*BUHn-NF|YdM$z?>per;_fDQE9&R1TmHXmOu@Z2wNM##u{P8gDac>)3b_3lQ1;{G@1gKkT5cc zn(ny2PuvO}aIO^7KC}T7Mh}?p6ex){>UkP1+6ZwA702if(v51s>?9&HA+Yzg;(hX3 zx{M83!8;GWmgg@3{ApRq0bP}0=zA+V}2{s_FPVs`2j=FUEaOBbKP!>6Cd#WPRh?D31G zDn8vuCAHHB5W6?$th)?ENiO-~%%GEv7%f_~XweR{`Sqi+p7?dHx)A!eYp#V3W-n=0 zTqk_yv5U{*F+2<4hkQ%`+`WGd@2q?a%j>ss{mx6cz4T4A(=E?>B)EKqf< zb(e%0S7$^8nhG|S3$`b-qB#XLXBEvU@p|eef5sraMqa#^4(krF(!S)CM+rRNyY`K7 z*ufeYqh+6udz=|!SPQ5TN3M||3#l;VFl;BIK=~Tr-vSWjdgc;}Gkc&{nkclmX!wN{)|u8>nvli~<4V7-QV0-~&tBeR69O<-%YC{AY@gho>_JqyoL zS1{Aaj=t-pL_Q_dF0vC3UamX=!^Vwa-Tv>Djt zp`^%xZ-gW3jy<7J%%6E0^XIPM;;HxI^2HBe{_GQB*{Cu0$LT`|++F*is_R-X0x3TV zS^k{l0dZcG%?IRE`h2u#(V|5=jO!cxrYdTDJ$J;qUSg%AUPb`U%Q3NAhfvz&g+IC* zK9}U!bcICX3e?|I}y_^^NP;{9v5yYwyGT6qI^?|%a~Zhr-8tYem78fQ-bcfBC#bLXLuPipc@#IYFqjey}n!QW)Z{`6O{# zMT>R-%-7dsC)=c26quI*>U)x_R_p|6iHH=6pZnt^Ox-J~m0@Qi!vKF-K$l=CsxKGCZ784z3C{w4OKg{^y z9ew-GKeM~`?r#4<5z$zEG*r?;_~abzW8MZpM(5qH22;hCxad_(4g_rxGBO) z+vg#2;0ad!BH!}38E;$5t|Iu+^zW`eUjjAD<8@S<2Yu(jeisof<+;0U%euKfZf@mn z@;(`||LXJH{q4VT6%Q5vT&yD+U!}2LzSr?@B=Elam%6^$DLzv9{66}NUcPes_8U!+ z?{OCq?d+4f0=LC`TujwyH1IRu``he_A81y0-6V{n7hZr-R~8=aKYRdUcg!U+0HQ^U zcI5Kn9W5Xs&-x-9Q- zybPQdz&p8H0C^G5pdGA3$4Akk?d$ZU{V7&^6my|y(V{$EWmFtmuf?T6(c;cv#i2-X zcP;Mj#ogTOmR~c&(0RLCyTcx`Dco|PC~zQ@jQQZKoMc4k&O6$H!A7B)1scQ8j(r0M(L-KbZEW4ZQu zTbWKRb@K>WtE&yTZ1~Z8_QG<8{^rf(DB$)?$N>wR%UZynM=0_l(d^Ode=hET4aP5} zVS_~>-(~vPLquQM9vd>hc)75u-xt%y^mX4p-skFsGo&FCx!lwBYa4FQ{MRdM_Le&z zEBrKWI9WM5{5tuRn5p1=-H7aZijf0Wo{k{Pl}A|@76TvSsqDsQV!u6NZ9llD4Hlpq zCO8vy85@Mh8o%86_Q5buL9@9R4v5rH8{4iS-|9VnPmgJ>I*~TXjYE^e?eIIAXX1hU zYnpf5Ll+?4@ovdy-}~ox(&=wxisa-BJ~Z%C`tAYa^JP|c^j(EHo{wF3*9_=_7q&|q z@@_!veqvC4*_a%H|HjccSjIpL;n|!sP2`s;I)7|f>|i6HREpt68;)Xggu7i?B^9uc zLb`5Jzkpc55TJ3t`}=`6NqAm3!I^Mka?Qb1KKFN;4dKvQ%UG--|2uD`j(epDXZsXI z)d{dUi1>eAmmfo?=~8RscCOz?;HKB#J9^@7y-lZ~=|}JzYCn`oBegl-eQ$*{>*>ON z;1&j3RB#63mpMYrGV&}N92Gi{gR}B2;&b>w4r?W<0e8}po}d>Id5!wxv=aQ((8R|S zpXe>vf@DoR30W^ih+8FKoD`sFEdEtvz>TUePZYwZ-J%yf1=H21fI2jWoI2S_W%bvC zn;5Gd@B=}2`L~&8Tl=DAr0nDqiYgE?=^9}VFBC|BsL#kXa7UqH)=4!jA0^EQ(-yfrMaG^ojumM2Dw*I8_3Q z*55cOU6ZXSdKh4#66PsKUU`jMf*euiD4y3Gkcs}hIl=x33YNhgO8JJh)zUYL1jp?F zRRVM0^7*g~#BqZp9>w8Hx40aOZR3aYmc9CF`bX1UR%(bXh{dh zgh72IOJhBP1S1?5jgnM4-jVYs4D{OeuHLS^DHt6f%T9uBXV*O4~u-yKSoH` zzZGhC2R$QGMu7L9#}@8&Qmv1%9fXjz}tZ(cuv|G834R}4wF##DI<7Gaorl*CSw=gGx1JlO}-j8uRBzbxqQn<0WJIa{BpA7Gq+KZR0$ZI3H?@u=`s zW5{m4kp%zQPH(}Tkq6=D6 zXI5r>WOb5dVK;`{#XUavjm$6fm`S%rLN19Swkb6J{(k*G_O)u)h2^7EKdk{-?xyb3 z*7o$7|LW=@q|EYQv^ptj4B`0j!zEh0a70}bKeQ@zcK|O2CqDSQjxy%WpDWOnme;^q zfU0l-%Z2kB5aUrIsM;t6EHHkz1~(xGmXR9LY5paOD%Tfe zH7?qD$EHnOU25sL7o!1IlfrIQKfOonK_t)M(&4W~2>9_%w=rKK6p39Oxsn5n-B=iUA_c%44u&gAibX6w#{y^u#ikr>CiKU^KscP6%q+@ z2cW;O8a)3DLC`Z-Z)BCh;*uXCUI59Vn%;zvLc&fUV9=mG!@!anv0&#d8tPMT5$55G zbES6YoK-QXCE3fPsY)pzUBZYV!wdgSC^tuS%gbb-?$Z(u#`*@Uiy)6v-c51!qeDNR zMQmgpUGQOF$E*go!6vqW-?(5@Z#4y;Y!=iTv|qNk_8}|yVpxL*#a!t;ZY1NiMX#`c zMB(n6!~&B@?50kt*Wt5K$!O(}6uaU^RCPO-m4^Q9DBFOy5QF=>Y8EQl*e5^MKqu;x zNINKhG3%h}hC#_iyhfXV%&Gw~X+fxaiM(_?s3hv)^6v3D7G4&RuTJ{Atq%7)Au<2E z=%>qbds8u%HD>V=ziZ{26kICt|HZ4tF8gnvfaj{!ED}1%BQ9H+kJFl}9&foG{)u$EzO#_j5w$~|@FJz0@n+K3fNCkL7_a4;M?KUji8NA>PY@=DKE1)zj1A&*Df4{Lfu zVEH?fsO*)%(t4dIy%T5-Jz)IUAYoXM_s7R{;v>cWNW?u91BN3F-8smsSrD-fh@kB^ zSi;yufcr-}hHA?H7mxft({2~!^OnT2d&OGNJc})72&#&N*v*lkC^BmQ3%5ZY`v=b4 zrhV=kYr*Q|mH^gAVV8006H1HJT1oE_^X= ziGygl_FZcrYD3>Z{Wuoaus{xwpjk+v9808$Cl&h%fKciUa!1E)H-lrFW#gM&pH=mk z^leVp)SRDRSJnOd^8jahmTz4(IUVHl23m8fY>qgoY7IK6Y7aZ9>I_OReVnX?jTiS| zirwV`e>xC#1*~6cb=p&~6o1Wn*!mv4uDw(~vltY)WbfL&A`0+c@#*nBV%cSTKu%;~ z1Zi~YMNY!}5+;rM`HokDKM^H-lW$KruP!y+Q$fB_U3QNTM*geyRuG6zK zc6}B%fL^g{6VnuKPVR4!a#Vf{n30d%lMaMK4w?KWA)~GNTo^>NaYM>Bd5Obc_9fC~ zaGD==L5{O+{K4_nt&RO<)Nj!w*-t=T(D5#q!wZd9Ci)R0YRVvzO;jz7cNN!bN#B%{ zeqqjF86W#TIG)MEHw%F^OV6DtLs^(DB_W^MNbTeMZiFQxSMy4nAE(btQU{?Ol(pf5 zyg6`#K0lv*ZXW8Dqp&qjbr&*_HRsy2I8>$wY}OvcOjdhKf??)!r3X2UE00AOCYBw* zU3U4NK}JDKmD$;CWkm-tirh2eMd}C3{HF!TB|iy&*BN6|H_BafA?f4Q<|}4!IW^U& zU(Y+|K5Xq&q&74%%5_^%)jhMFW0|5PIj0v$Swkntg83vyAF4O95G*r>h?B}%D+86{ zZ;~sFfM|k8F06JdG>f}K33`&Q?`ex`&mpL64_n=EB9ZvGskkA9UL%+5rqRut?Wyuo z&|__ZSGM`ryoAMeBJFF@i#o_ab@HMLwc$TAS5E_bP415s)DPPu)b4kGKwrJKQRs1v zq)BtBG{SQ%GDtmev1ik6=Pby{gAK;V5od3h*^LV=brve%amK174j`B_8ra1_bJqz7u>_MgR(05 zliIcu`SR6@BUmC!5sw z|HdRsH4n_~Qg&i&YO?skWJ_?N9ayQ`Uq3;UP;B-0Wc8LScE`jK)QO!AYElz2ho zg|sYcDhjzM_Fo2jmOUMv5FfOoK2zB68TNHW8->`KWx;>6sx63Ob%N}$y|Sce``sSE ze^xz$Gj;NvEr~qWu2f>l7HdkR!ey>%fBNKLoxd_#UoebSJ#$pUdCm~V8NvIJ!Y`iG zEY4T8x5iirjj1=gxSzQbZaAVS^_C7}U7S)dpit+EXp7 z*=eA|OZ_DkvmYt|871_Op+@}@VQv5*M^RXC__t%tM-SV>RJEGGXwT+SEfUEKgSqCccEezwNJ zoQzE5h;D&3bS0iS>fVsGYazguYl$;9Cgnc&Xal?XvZd!&_SF1GMlQW7AugY{$I}lY zzSy(8EbT5?Ba3Y|;uMVW=`Os2)iZNZ@dym^`2NXABQ5KLKq0`dE=pt6=1U*q zbo(37=5vGJt6U^B>`+wo)gL{wW;JOtk%P4>JL0rATu2ZyJKO_P72c?`{mxd~9gn3_ z)S1M8*5P*+wEBLGv=DZc($rF(>|&72VCEr7%dlqQ%poN!TdPtFe$*=)_FbqJTiZ~I zs?e^us;`}_*j$>nSyz3n$t^>%k1t*uWz?k}J-A2i1XNQqfXDaCMz^NM3(=p!@l&hy4aM7%q%<6q!( z$aJhR#|;tuq;8Ztp*ym^_zrN>d&fZ!!aUI1YT@jhWUJzz)Hc`a)}W9SCRW8_hqe^E zaF*L`Wf^O>-;kW-f=ET3aex*oX~powDn^= zUXxg*U7Bry<3I;4uGUYA#;Shvv;AuR9_tchTbZH_VOqb*+K+ft5GU<8PS3V=w14F4 zZJgwj*gtS3G>ezXFLs-^Dg>8;2I6hlS+6W2LyJV%6&fDb(3a-%`Ala0S^UF{D1E#| zpmb>HgXPT;eN+2$-c4q5{AP)j!~B0@gF*BfHZ_xpz?kE6LJ_qPVSI!{aV5p{brM0{4#_;@sxh|ZnSl_D?` z+in+HVK&45mHxGQ@K#opuqiSiGxxXP0-Kr5b_hS>w`&?+AV~K4H_OIlwkb>v5M)HK zvXnNw-H$or`h~I5ciY21O zuK)Zu8-daG%qDyOLCbznnp*6NCh>qW+Na^*Sv$>W2R#7%FDYg;sEYUi#3o%3+4VQ881 z2Q4>;&}$C2kPj^G)>o$4UT4<4PA?~!8s5Y;wDIgJut>s~l%$fs3XN!lQ%vcZ@W;%H z6Y59C%bA_J!Kwj34eO6$SV@GIcw*1;#rrn*^WN?A3qQTEpW8c+G((K5dCS{`|?+hgy+Xi!#~Ymc0pj}ERz_TwE<9cPbFl4O!R zzG}z^P?`ys${_%jNv7VA*-%r5t#2_Ke+K zHtuiA8`V7a8>+5GOQ}8X=LO7KT(Uz4c?Gef$G-*k6SAvusni?X3HJ_AiUqQ;*e`;ESMcd-+fxqbgG{Br7E%IIqY)*-lknfj|zK z?0utvjJbQc#_oN3-Zg2n_y%i6aH9*dArj9CM-yJT(-}ZFhWy@x63){rA?RfRb#f~G)0(&UGE-K*MB~jX zIb&Nm{~T5-0R>0zlaegV*lunZL<(YR%J%6`e>r1UL^d)+L^B3#eE5f9A_3s*1>ZvD zCaX@=9%5p{nhaJU#ezp0U$ERtw8B69ZY3sC_Mp0+~(wk&a*MlcFv+V&JNa&ZDs^AkxLikSgn<1EX_-03v62=4#QpTd9X9tlChD-`t z`DUX0a)@uF2+YM zySHezMX=ttCLZbV7u+>ds-H${%aB-%R}K$P!xVv?w=m8$b=?aVb2KUBJpC`>NsP%X zCGds-fLXWJmvB)*HsaH*L^f zzI%d62O2DbO_*K%INfZE!RLiXGW~)*4~tPy$hAw@e%1DnE&m|A9si(}QwaEfO(ki6 z!UeX!p)l%d^%SaB-i)sle+a@&OJEP#8wlPLqm+d;+mAlNGrsps8E?o>G&|dr#@>P>d{SXVXsZq> z@GY)wz~6dYut~nFV>MGrG`JnpvlFZurJ=D;0eeB3*Y&Qb776Hn=$R z5@fbUm)y0|WmO3_unEiVZ7DZqXUgdMRh?7oct*l4?;JPSV{-EiMZ438#;b5p;cn@iRvi$$6n7OX7PxIrK2lhO zVGpiwtQhKqj(u&+x;|Uz<<9m~r|Mfpi8 z4-cfgZ&h>UGayPFQ97gg;Mg*hv+qJ}miY49hYZB4-ap(&XyU_Jf0#ha^Nn=g#BY+g zC(sV-!oi4j3ad`-r~(UzqT8onNfkGi9a?6;Jp1nI{_g>X<(U>zR z>QrZ0ZOr{ow`KBtd)SoI{tvX@Pxy;RvB`?et@K5b-BKLCxTeYW_|@Q+RGtdnQ$6JE z8CD9D!O@W5Jxb%NLKYs*f!hG8IwZuj_;iB^!wl+VR5<*_Uk(lU`MH*wl$w?=+T@MK z5;rcX^LeXh;NLX~Afu5Cl7Q$(jrNSLF*UG3W>QRFH`@M^n&?w~HT8 z*bS{O6k5YOgyoFkuolFLHCvz1=ogO>YdB#O^N`6FxFU*3(Z_iCm;nlL@vP;HkK{Dk zOkgk!P*8e#P&dYb0+*n#^_Q`Z>WV6)?z{gh?1z?rTHuL`i+=s$^@*hQ$oz3iE@%HG zM7iA^TvEE9o$$UMb5<8JqbN^Np``Z$`c%kpGZ5*(EdvlfPt4 zl83{_t9eK-0E9*)%d3S-0iB?RLsuJloP{LhL7{45ptd;z4(({+hWY2`WtfTfJ^;oiHU!8zni3iR zTtvyi70T_?GFk>3bW}7-fZ3YcDvR$9@SUL?Bd}=Pi{7Sh^k0JnIR`6Pyh}T-nlNbZ zx2SiH$k_RX9q2t%(Sb2bry^h76B#8D5|Uz|98oZ__tv|2JdlNFQy5kj6JP>^WMW1t zCj9gQsQ)m6+e`S}RyA7B9$#&Iz1({coyuqo*hGYV_CdSek)!}=^V<J^gXK0MI z6u{D%%EgLoZBc}j>n`ux&OdbbroClP0fxQC~oAbm2G%`Z6G&RcW^H(j$()@&7n{nLyR^A`Yxhzrt!jCt0G z#rks7DJf2evb&@Lj|cBmptVcP9|X)T4JHFVxj()p68(cld=kaJl|+FuFWN|Cu+YiT z2YP;)=5Yd(sIh)o^ZNsl9q9RTpYmz+BE|AsY|6&z0ps-D1@rwNU8HG%5aAafs`K8z0x)2VpzETe*d2oI>R8yK5qRtIN(UQPzE~hpu zcZBqDLpT&8c)phCO!-rF?jF=X0D1ih?Rf=SGcYedoXP_pPgW3>%voi)8Rj;h4100& zo~tGf_V(CzbrR{YAVYYuY1zf)80rZPWO}ml&F>D)Vj55wXok^OZ({${T-*3rQgN#L z(^gr=N*-*z&o-tLtuJdMXE+c_Q?q98O#Q)uiVH?t@P6Q48|vi8mfL9B^rp4&!W@G zGUmoTJbCE|>c78uDF|K=5>TBy>IhzFlP%@CH2d?XI8qYe-O@7a_$c?daQ3PjGGm z+fc3Zgfc$%kbDO9k_aWZEQ&?KaMW?;OGV;u9;58)Yu!w|x2S0fr~!ki(!|hp7LG<@ z`Us4HUjjHcA76F#o0}4D3Xl=#KPf$+?9l%8aU!TC% zg{dbhO~I-@S64_MYoFXoY_%|dJ#`H$oGG^`e`17Ryw}rig@Xl93&-x2JJ9m&i7IS0{tEH_^_VPK+8L@oWl zAZFMmO+1Vy>58D(3 zH^*bk-*`VW^uXTUJxMI0AkQ;W%Lt<0d7`)ZpF=&l3XH+tu9M}$lbQ7TIRW1%5`qNq z>HS1bpU5rAlqwoiu0Q6%KxusV0{# zxGxtpN2mAN*#Ghyrw7e+r49x35ZwxQeh!R?Lm%z2IYgwXB3b{V8H#_|SW@N?<{kl} z^r61MLY7s8(V(d5UREAGPV~o<3n~b++rL45y%^`%Hy*!56NLT|dysdK0UU7pmxhQQ zc5vvJA4sMuDgz2p3BP*t2{?e5dilDBwWEMHe{Q<^Q1$+rM&VEPL4dF`Co5?bR3CnM zIjkBsnWZT37CIGa#$r$2%u<$Dnuf^wx2tx_!EL18^$47TlE$Z_P@q69yKfBANlfDH zd=U}WJuoysE@Z^2asA$Vbr?^}9K0nwfSYgf#KNb*J~ax+$d~Pcd|!QQIkXrotuFQP z1OnHSvZkVou&h<`eMKI(^#y)L3)h#Ts$z9X@lM$-IIg1qk0B;E&aOuS0U(yo_&8`l z2czQ)I=k%CsaJ*}nPEeNC>F3lk2nl-_6K=c$(L5RjA&9GwnHxC>^d*!UWY7F`}?oh zakE;(Xv%6S7>tY`AP1n@4=%xvjwvhgRxw`C_x~yQhgQTYQ-8)2?oAl8-N{2;>!%jD zbOV>99-Z$q7gyhp()GA9(RY7YBcl{RwWds4^>}F9B#@eT ze7{pkoDorKBedt10!5!KGnma;dS#5^@(tu?#R^<-O)?TlG=5E~bedd`$Q$bAVASE7 z<%mR8iuV^=Z_f;fIoFNhntf_zQ6lvDL2HnaIYtbIa z!vMyn0GE{_5|1w;@9~d=!h}uZG`(5i3i$6pDt0$IFxLKuhuITFnBR}Gi$^8m_5-`C z{pEuP1%-EPFd27I%fL~>L?#Qzh&0~u4-IaR5kL81NDZZ2$LyERTt_OYC>s+Dmu^~L z7$ON(NR&s?u(+a`$Y*FCJJaZ(GgR`-d+lqZ-g&fh*GiCM{S3rua9kC%`wV+aU!Iaa zhIA#PBj-vL>=1G2HFD!?-eyPW-pDuxK?PXY87GKmXNwOGG0Et9iqJRowIZuZ+o3Uz zt|5Ncm&m~XpXU+|l`cUOVR(W(+3fF3o352X>!bQRQM}JjobLPcc9*>;n>#zlM&`DR z(12VxjNtvxnI0l!2siM-=|K!;2R}rk%XLl~xhvDd|K#cKG`RK3;uYB+7H1Dx5uL}q zSvMANa+OE{gnp4fjm;wuKoBPdbqRr+SA; zQBj9Ma%()m1qm}zWHt?M1``~NFD(1N@Lyu!-w|G0(RU>lvh^nQ8Ta$KrqsT0wC#) zAHBDBX0XV*Z-oZ~`h>ZY(S=FrDkZw-xWO#=io2x+jVlsv!KJd>^T6r%Vkg6Qre%2I z0Ymb^_>L*JwP)`OmPu{~G3OX``N5(Z)uFFM%rG$)q0$DHdjLZE8~{l@-q499_iLB?riU-+OM)@pAoYz$y!Z=-b913eVHEd`q#C$p5Yv3#EqK>bKHZKz< z53M3!?J@B`WiOZf`ib%qFPT0Bo9N6#EAQ8pii;QKrp}u_dQQKH1HD({Y>zWk_w#u> z^h9K3if{uf(&=s-Iczos27a`LpK~gE0#I(jQ`!q=5xa7;ji(LiSZ^yDz z2jADzbYa%h-?p+3CoAnhBFIE$EVTFXR5ohMZ=pRq*Wit!;C;4|85i)wBt}J?C7N(E z*fStw<7FnjLl^TKA`}~RpQZtki_XP!xl$>w6o;l)gWhFM6dOD$hV!nwS?{gTuxrS+ zOAeq&qYCTHMe;-3KS(ecToG3KXEi}sWXhG*D~Y&Kd9}uvQFp7dAZM$RfWK#S1q0Z5 zkJ9;S-Zk>`AUlW@+<(f}mjF=V*yw)96tFp5MO1deyMd2M_lW-z)#A^)7Qu#=Wn=2+ zp0j92pn$(SgV|oewMy&}Su@yM+3toeGD4F%AtoBlG{#a_j=W7yCZ>v*x1^NnlPWsr zka<<>Ej0BhFT`Pe`{%<5$+KMmw(y4{!429>3aOXh0-VwRtIcEUHe=o^m1Dn^OUOWL zLzW_(Y3-lTrWQZqySN@hxDmZ#xe>o&MUB~A2Bmfl>@s;L-^dg*XS8&arfIwoVgy!9 zYuQ%2D^=GIj0^YvZ9fo%v&?{qJHIk#u%2D5=AqEoXj_2o({Z4L{`+d^J3L3g>LZ%7 zKL{Zdqp+tSj(jLq%!9BKnHR%2mtbje905`kHo-ry#3F|?8TA*G6^o%Xf?;$>qI&$m z(%CTcCyL>Zr;RqxzKa90vjMRKHuoSsKZ4ugbsFKIXo`BpHI}k%Vcokr+c1s#lzV*! zV}g9X`3`-v6bJ@@KYpRdyHyAbi-zZ{Mc%jq(I2MGcS-=Xj4-iRtr(@8$i_ZKYdPM3 zNRlwMxLNe%S4yu?lp2BDMJTgeg40c<9Y%;hd55@~Xzzd{PH& z^X*FTuO(V7s`nY^o@ktTTY()63<;2%tl%3dDjz&P*#W-ljB_w_wW?$r2GzSZu@MBc zJun+;%oWJU!3+T!b>mgW`>N`N!*7q` zoxrW*fVvyGX!)v@o<`Hckm^vYx20~dl*Ej_Go{v((I7>1ng*9DfWCosq(n0DeNBd| zX^n-l9=4FQ0nuls@21+8r?z?V72ABp1)m9X$U&SlB@Hj7-u-rZQdbn3?$ht0jgs;o4^Il}l`z-9?3iSnp39Oa9Xusuox$Jj~2m>1De`BKvj#hy! z>>0hrFQY@uv|{DVVRqvp%d#PFakCo70VFrn?{nzXp1Y~?RM`^P&GqhAN1VGhl0|dj zof6|DyG0RiM~G(W@|a%#c>PZcp#CYZ1GEh{KXYvf)>)K4gTT4te5bVCzV;grDo3_G z-sJfq@8o%UE_KnW)wFA(zh<-zqmU-|ydZ;+g~Mr|zVP+Z{?$^Mzvi9)zk$4aidcvt zMG@(LC2*q*@4k75di6#?yz_Fc?G1X|+#5;t3#-G?qX5&r&kX%gLWybNU%oUq|ETeX z#8*}x*EbudHv!}Y>_d5A2?_5VsD&Bmy%MlNHJ{P-U2@X?#WwEUMc}qCd*TmJAFk~Pq6;9x+(Ocv+RXEad!8gbr z7AKruntmA4XPvbX}&;Jj3gQ>&W&sA-4x~9ow=(>w7V>j=V_w(X668`sy_oOyk z_xSXq$A?Z*8kTc6UfXPc{0a8m(-EqwE*NupnhCHpV$3N?%nlS!Lj@@eMJ9A}*RmJu z`YlPlKI-7-4-}U37NqjKl}s=MHN?!$J^S%@b1FU6!Aq=7c z%b!uOie<75EcAib9{Vdr@dgcS?mt9OGf%(pF|Z;UfOlM_RXg&7KWO-)FQ#CKk7kDb z1zsNFJI>3UBa<7xsRdScD<}KR9aFX{!?w{L3 zS$?UDM6Yu;Vo#8DD<9dhhQLp7>z7p-Lr#Mm6Iij~b zZi4zk%k+y{u8?2=z1Af*&LKwd@*uC<#wF?B^B3LE1fJ!xea}wl^|<76VQH)q!%Slq zr>MYt0>PPSd=d!>K4j2a_6t5A5PI5L%u%Ql79g@JZ>w+~J&}=m&F^*Xc{ad(YKTJ$ zcJ$cO8#3;FL?tchwe?06;pX6YS6NrHohx9L9FFMo5g0&32#usLzlowGNAG7$Y00H! zYi=>E-5+^J6rXL=Fqskb7!Cguqz+UDE;^M?t2M*B7rXB_@pONT#N!WRV#P~Wx4^>% z-rED;2>rKo{oSsYVfE&-)@P?Yh1DF(D>~9}s`5;n4c~S58`HgB%_({z05lev5TI0y z*P(61!If#lFY7h7WKZ}>ePJTp6d~2`p4txwNqAWG#vnJs|9&$d61n0tsqwMGc%=6$}TTiB~R`7Cu3ee zY0jQLu)E^bHLGI7-3l%ZQ~0#qsr4(5_e*}+Uc(a_ez;`tOJ6R1ymR?UfZFMH@UMe; zh;)qovU=~b=A%z$ufw`m6qIba1COD{TtIbeyhF3VcVhkbp04e+nV)E^;~3+t!MskT zA9OG#bFi=#Xm8*#_EBk-ofu>birX}?lcKP%mV*qaelBgy%u-d(RvuYixMEKNN|}DH z9u;&v4D*Y4Y3#i&w-ubQ)!57_zxSPd8n{`=8Ivhm_wA)wDOKgT6<^B=95_%ay|9E^ ze<*9ri@h4_9ei~o*qxKV5L3Bp^L19L3HY-qH`b$qW|?Yt_uPE2DD=f}8aYXPZvS+w zn_@nphG&8fK-V|jvCb2B_;8`(C_6N z|EB{*XwLOh9Y9Ls6eHrj)u?cDVEb&T$A4AKdo^Qsvvy@yTkA=N6RKr`p}s{fUfo@E z(|i0C0mb+{vimfz*_IOpHxy<(7l~+P!<&zvJe50LIhFKt1W-+91lgWGFEw& zxj~{GH*7vI23ban_ld^wRO1e$L7U5@#~Tv+{$c2>%WmccT& zwRfd9-W1R6^-yHIIP4Pl;NM7ND{)Yu9TwN-Y;t3WGSr~Ae@%9C1fSZc03`7A%Axc1 zw(4eK=O!FZR!a~X(?Toi3(OZ84Nv8k%M@lG&B0znW2==Z)|x~JGS4FGNXgJYEJpbn zN;%Y*8)engt2_i*a7(iTRvY`7PfxMCcgxwz+b;vk-uD{B@_E9vxjFj$A%Lsl6vT$m zq@@U{-!z<3?dV@8<@0`{X%!~cy7MzS@_VoLWeq9*Yu=%w*#(B+YvE?ws3Wn5Ph)GYbKfWduQLZVrYtc@sdQnNPYJG|Pmgww7pYtnWR?|1sg*EX{+ zFhxa(bU19b?fb7fjrIWjwdeDce;Dj8vT#jktZRho=_jENEFD%dQZMTbVRn02ppD+* zg_OrS^{&aLoL7_$u4j63L}BQnh!4R!w-b!8lkIr`snInu4X$!Y5f+(%!!#V>$lpc_ zZK`tHzOA+T51D4P1UJ5!!-BM`fV7sA2!7a7t7gDy(}>9}=KNFn;(5Z^ zcW(dE8A*dZLP86|t!x64$o!Uwvj6w@YDnZjMXDRDh>e2~T{GHw=)J2ZGF0Wta2PPz z((yT@^4;Hfj|f{}yzH0h8f(gaT+HmU4-^zOi-Z}*!ayv+v&J#l6CQmOAMD-kUK4_} zB%gu6_(8>J8(TwGO(G^}ryslfot5?Dwrm#KOhBiXkR8sSmNVL)pK4J7sM97U%? z2Z)5lOeyw%Lu;BI6tY9<2R?AOiBK+0v&<0*d8LjK(%dut!wz`$#p1|&U37S=6zET{UxOqCsBKh>jC-h`d8_zubHd~ zUJOCjSkqpV?eiJjexL(M_Q#^bsS(#Pklhahvtihh86FA$rLDWhERYbPLS*#^5y?a_*Tn^(L4f%T7 zoREM{KdIXB0kD39gp5dhFzn!pfEm&Nm(jrGIdSGszDK^!H}zl2s6{?}IJOaQ{KKp0x%qMr>Kx|X%AnTZ?*Z?#lTVTsKv3NY-@Yyey% zB`!|x5L~jJYuxkL*=azS^Nte->jE_K^-^gO?HOhk``rguSFe_V?^k-9Nm2$jE-?j* zYkfM#*bRbcIFtYV6|5+o$N)$TBgb!hDb^Re`_=;eEk#K$J)n0yVEYqFN?nwJ+*GuR z8MC#~XAAH}k7WC8=fSl+S~FDB!dYwuQR^88)|D^W7&uBsvY_#^U}U?^}teITkYJV3Dm#JD(iDsy+jyvg^3=(1rZRkf%4~pMMAiSCSYNOMm1K zakXIzYj{Q;drASP3!KNcoHv)ZC?3BAy8X(5!h{0_B#jwSO3!%flv-~N`{TXn0nXqB z)^H8pJp2z#u5=BNnT-Qk}(h8pB2F*Xp#X)HBI44_I zNIsG9r7F?0ioBR((Tp3xT#t*pQwW^W8EZcJ%_EpN3CPzA{VysFPe9mc`Ljtdf@t0J zA^=FRLf+%t=fb=h)RD8jhpBKqVNSf4u1*5|O%#K50fCH+EhxU)8&WORz>1sMXGw!A z9>Q8M@!nyQt1Hqj&0?%eDd=um98~gyzDF7gDx7B4|dEIElOqEe&T@ z=*YWIWwr-TV6pG*0O!M#@_V2F-y5#=1om(1U>*CoK=0n`B6h|s$rg8R<8#HWZnEmB ze)<8?cmToX^p0-G=?%w`jgj5f&g;fe5;wZ>Y_r&H2MP3gul=$!$Y-cJdx(N+%&VCp z-;4J)1HQ1b_EfL!djq6VPqX9OugRW<=z#N@|9A5Ig?9vvPHE0-_roj>gGavB(xC3W z8;OTfuXlK2t2d%fP;b|PHt>yLQpY>XfUF5G0|tFqkimZ2MuR(v#jm>O#ZYL5$_VnN zd7RJYS}E^Sl&51(haPpO(5a@KjLWv`JF$(@*_)*y@Z#>w1Cr0PT^g7bk!AnrmiZdu zIB&{XAfC;O6&)W}#vk$DcpadG5IxiID=wo&(It@Ox|&gOpC`kT-u|Lus2Q{n=JC`p z^tjK~)iY+t2Mg5$m0?n3L7P$Syr7}=z1E(_)V0TA+>6M0ZYb?fFsfre4A6uvNFK>W1~W+u>k@YvM+|maR5n&O?~X5L zDnoOZ7rer5JFE1Xjvc0a^jBlp1%7KB8e1w*X%BT1c#>gWn+7ecqDogKK(6+|5g!rw z$X9o7htOT`=CGvX%eLnP{Yx!Q6+C}_%R2xTxo_M#UA1a&fk4q;8!RH1)u;6ho_z9|=FoGWbu!L?wmM?s62wY{xoWzt~)83*_uTwNHZpqtu=0N$MMdI;Pf zMD+h43EceTwSPWH%i%_#vGC=Xhx*JA4Av6v-k#u5>y2vgFNQZ#MW!3%OQ!e_xSxKG z41uZO;*eqnEcJ&vte<(xT(Uae2UKUB^{&UGtu04< zQgmCmoAxZc;HafHW=yw3Eiv;T6q^7BG{^rtj^OedT1i=x(^O1y$6yz2JDlp}d3ZR> z+1t?eO)1uYhVy%v6WBdNs!Et4oyJOv7FGTctv05?4T&QUzt0r53@x1qNHZxK%7f4d zjrSs(=^ZrmJByFuEbXm54>DilBeJLCGxAHVYN{gpsUC-b^&H_W;2e(&*P%5Hu2v=H z(pwc+9vHKoUgzEReX1}hY7!sj|E)_iJB+ZfBLs^m@B<7z+@TE3tg_qUd=uN^_`B( zH``!;1e#8t@Ztb>w?2nI1pZew-_Ko>OrZK$J7=@__gZJh;&z!KHtBegaO!)JfNvFH zDRf@iJ#NT!!3Q7KGscr4Fa+Y$QDZP>EA4+Q94DESEJkSnbaI=peDp*gHC1sfZ9#!9 zzyfx7ODu3sbQeW6Qf+fj-mfTDZJs4LI4DE%anPtoXZ674y>g&EZnZ7r&cH~m@FYDR zp^~Wmhp4EP@9d%|^$3(?g4^jdiiLU6T6)BhV$mm6o&pyw#~=9}hYs9N72aU|o<9d&a}Zq&7j)NI6U9L}qD z{n_!L%s>k7U(^yrX4L2ERoHxn?YvCaKLB^4VwX#y!vidcW%+PR{LOT@7kB1lP~^K7 zavMGyOweSi_co;1RqMY%!_`Jqy0m$PqxI-#anjpDQinFWh##r}fjC}> zOwTH<=k{9bKa?x#B=7lSJ|8?rD%DQ{_>cATOv_I735 zE^x2?f2w5H4ziSfSC|JP&lGfk^us#z=-?nnW)UnqNg3Y`cUt~KQ>j83rg%B$;ND-q z+i5~&6w<86?Lq&jJg;AI-uT|PpWlNAikgful-LwS5O7CVB5UtQv0^kenuXIe<6XV= zA~ieN!~)flu`U|-58&7Bw0^FjMlL* zLke)%B8dkG3>5_#{PR0g{1COUV3z**wtAzv7zwh$P~9AU+9A<@mt;8o-WjsA7$;k7 zUk!Gia~{r@ip7POdRdEMr^zBN>#a}{YZ8f$T7m9GZlIm6-QPete)09euKh;KRXh7! zyX|^VZVOt9BK6??LfLul)E5G>YBk$ab7(c z@`F$LW9yNuPC3(@I+UXtI|D?q{f5av_>F;3fz&d#7+oy15WrZxB$l)pMkXqCA)*zW zhi4r2@ZzBw8trx;mH`CK$(;O?r=RkKB(^D*oWnYBweFEQsWB`!$(v%V$GYVbuAT5I zP8!bvB-pCmE*HHm22E{jxwXH3IGCcBt#33~trFCKl5=OL+P>f&Yob)@5}A|+O&dyD z5>uCXgP5MMxUuG7>}=kHLCZxDuzciG6qJJ^V)jeRMEfW8u-6Eez7NA+x4JyhKwjJe z`mn_N@curMcN3o|I=(+YCy&}fe(*Rwu1+?jfg!2roVu9A+<$ej{`VZu_*E>yYYaBx zPUN!>mB9Vik;?9Pl=ps%p1gfkepkzS>b{H(i$O{e?DwI;h8 zH2O@{hAcnztgpm;`=<%;Slm70!7G$p*#A+4MD>p8i1*sYzkI9tPQMt!|5|K=+x)AR zk7Tc2?~m;BQ+5B7x61yAqs{uY?_9&DWPqg7U{m`kMw*G3=m zd2MS%+Wh{7_WvI!h_2mGDhh`GzLPtca?~91!bhOaBjCoF-TdD_^kBG!lKrC5aXaHn zTKnDeYvj?xThEe<$Bo?K!zKQ=7SsWXs2LtPJoz;+0I1(tLZ`r%hc+(#`t~I)3RJ{+ ziu(Oy^?{FUys$CUKnpIijUoT`wTf@j0gFFt>4cx8q(Mvn>H>|t%vUyhpF;1bX#Hi$ zY+|(Ex^a~zcshUdq4R?G*X($pOWrp?U^LRSqqwHE$9E#~vpzHO<~uY%^9BpSyTBlr z=+z)cS9+w8$&lbZ|A{#)hE>C=ZILCaRdxm(XYdwY2lO`*3dYDQv?RTV_F5x?Ryw?h z|2?j|)egCWn-3DqJtYY{$SbDSFbUM;$bAsW%!J$9uN{4mjlR+Lg0J>Z6A_;zqvNLN zb#>Y5>4BOf%iM{#g?faZ(miJoxL=A}orRG~8mu1{pLndcUT!WE%G8NDyuAo96m_;< zh;|q*zs1q4$`1{88i%2TKe&$Zs6Fbm&H!(9LI$6=haR2@>sHsqqp!B@KM$u14EjdMStE~h=V!soCFLBiMxzfwky>rG+_rhdCNqfYq zT%Kl3GeJo$V~*@5YYxxZ7+n1?MYy1~_%MkD9Y~)tqVDG4s7enRE)OZENso8?jegS~ zE)=?IeD*>S9-*=OB#bH^l~c?`^^kyA*8Td-t-euzMETJaDYW(}dW385xsCkz0+z^8 zoiSH`+oFp%PH<$lGj{^D+=6NLw;?+#iuD(}V&`v~NqRxX%W|#r#H<&u^C71zXR${4 zAM2-;^Pl)7zl#@A!UiYzE=!DYHc1%H z<_bAZ&L;7=SMwNPAPsM3<;^U4bi8Fh?8;T&!_)f~(VnJA^BAj5!1TjQ6am*sm}cGJ zgh9PRW6H0AYE+*38}iGPcCrDw1OcVFE0b3T=xR1wM6$u&Maa( zeirH8ww|f@yD|NF9tp1n#pd*n(92!8uT9h9$5SFO)8GS)9rnc`wnha7w@%q(fJ;_0@Me*Nz=U8Y>B(~ zq|--YL|lqIw)UWEE+&nLHiudr_W!?*63;{vs%%PH+RotR6K}`WmDclhtI*)s9k_0B zT#(ttHZeefV=*!x1D?iUtjXmE%dk^7%_;f-X|Owdvn?~h${X+)gWuU_RXOUCN`KTL z;~sD)Bh_Qfuct6ZROAcHP|2WmSN};vSVFr3z}PF*Ii*+K?gOYFk~plGv7`jwAmI8 z2{g>O+`wE2&E29ed#e*&30!x`KrF6S_IoRaW%=y_?g}QIZ0t%*olDWhiTq}va20?5 zeF|bO`ngSG_h}vThQMPsKrlgGu$P)?ul*}Za#B>IA@@WEs%~j`3hJ*kM-RBDg*XI3TSIgEE&PFt zerL*PgWdHJG`H2f9oOEbxpv4#ez&_7w7F9Z_eVR5jSEoZ03yhqkuspZ*8cGYze5$m z3Z5p}Fm&Qj(px0H+SB5HhN%@^S`(T_cB+96w_s44brx_KJba$%{Y*n41fl)_m;Vy) z5s`=l%{%uDYpG6Tu+-EaBq=%Dz1Q9U*^w{1wJuy9Vq!WoGiQ#1ZY|y9x&M(xcrq=- zb$Vz_LvU#aSIfHMe(7ZowQ&tD2hvP$o0j#kzApBzP6K0v>Rv1D{vWJp8UMrR5{%m_ z;C6<_NaO_t?Jp8(DwN4|mHPTA-QjSy*nB80EK2X2nTjSBUk6YWs_7|57!Jq*&Eib|6deJZ&GaecFuoyg8 z!za_tSfxodrp^0O)?DHOlLW6Y{XA#E>F|KayVq zm*)4L{UwxseIT7YrM0-M^u(?lL17k%Z4ALSqm}6p0TP=3sn+2uz4tw}|Co*v9))Qp zIe!9O%3JTpEL{QAy*nmO3G$YOF54EW1_JGTvKw^TZSHE5awW}q?F&Cj>pYXMVXPIo zbHAYZ13%daVLq~6{h@d=BgSD=@2%E#s@GRf`sH|YD=S?{)(V_GiVY$BOGlXHtH*oa zR_&t8syL*;m3da@9DHVNQZ^cJk`Ec_8C}-9tIvu_phFpee7+-ekBp@PAxGMKBE0>x zH28;dQC7)9&AJ-Yyc&G(xlgIp59}S>SX!&!P47d z`9Nmm`TeF*45@fF9Bl=oh;|_XeRrBnMd)o2CEN`bB@`0m?D3w1DbF|cvW%aKmHt2) zflWb=z71U$MJKQ9rVku+1L0eV3{*zRf2b8-njJ@1?w0MHu_H{E2qAk-!sJtPp#@?< zg{y&di{jM9VwFM~|uQ`3vuN!sPL`-RGva)-5Oa(^aOwEi4W{U}NADVnI zRY;V@a8;Tc>7f#pg&hmLvK}O=#LpWtEEnw^OJ}8^C>7e$9ZGHA)8w6mn0*2SRJsN) z?Z;qO4U2rcx*PreSa-U~()oVTK^U?6NWM{61rB@ImI|T-gEQ53!BY$8(*;uU=U`cj z($oLo&}=-}FaCLByh+G#3DP0B#L#Zv7 z!H}hjl9~V+eD)0?cfZWe?C9H4%5Kf-W7Ly*Yqu;zWO>%(>43s{GYRNeS8w^j8GTRs z+fQkqJ~)>CD(MkCzsgXHaP3wTF*}B+ixgR-jMy%zWyh%U$MDh9Rx6&Y7UBp;feo%U zews^i2KB9ZXJx0}?bp1;zh7$}T$o)3m#ug0Fk+C9r34)Jw}AJtjoXG-}uq&<=$75A<73`|dw8KjMvP4TF&3>9WflVr={IKLB3fq9av?|kq>2j%>P6nX*+*wP z)hm}D6nvboX=O0u{j4<6!Z0E=CYag>bY1b0hY#opzw}@-Y1ap-m*Sy4US{xt@_Ga3 zhN~Hb9s6VRU)Su*RR)*uR*)y-zhN2GlT?WwwTM=oL4P6!6tm2pP ziQK=X6s&RaDj9=69x)=UEYNXmWO~;e=f7;{HzmJBFFKM&S>YDSatb1hKTn%`N`>>h zY72@s*{}~=R77-xul{?WGCCx1G{oTcrK%D~LvceN|62<3F(kaTto4zy`Ens_a=k`N z`8~~EonWdZ?9w4GJDyvx&7@irSjig3bRp0JeA<)<74Wo>)B%Gxzb2K}~ z(!3WN-029_F>ucsbT0tcU=;t2`M7rJt=_j9oxS`KiK^OH!)TtZD9n$mL+_*MHKt`? z_@b1(+iHT(4HJie;|Pf$EaZ5w6oQkB8T!fH74+(&3A9=9Bww0 z)*(X~Pqh;ZH^Fg_&j1!3@nZqfNWN*Q3bJWe?>GAC&{xhR?)V}{;a9wVE zGhFFmK@BTRHStvRNKw># zw9VT)l@XFA(Q6I@aqB7naZDY&r8P}KiMDlEF8V@%j2LCW3WBCC%aUHdp{#@r!_&Vu zA$U8i_U7#{)E{N#?wUUpsAx`eJIVxpNJcIQxO0Z5lxF<&q>ZS)(XB4T&c0I=W2B1( zTs~KpEf7U%SLZkufQZ9toNw&penf^xq&6s${93btt}uy%|N3dv#v%I)SueMIOmRK5 z1akK_gru|-r8I;GTmS`|LA%Q6bON^D1XO$eeV(8Df;0TI{3iOhaMw@njnUr8H*iCb z1C%CR1CjcG>81O8K*?FRquzdBQ`eeDujMY^L-Ys&m% zZwv3q%$>Hu<99KzhOYTGQGaOCvhoNna=jy+b)j<&oKB&g@RH&K2A}1vU)Z0k_+e8z*{fVyPA@YW7lkwJZDh4}?^M%xSK z`*M9XOYB)q5+rUZ+L$PHr+0RJhVN4BU9As@2YdQ#saQI<6Ug5LaPsE8s7n4?m$iJh zOb(~Dr-grlBL47$XcWF-usn8{W#G+aW#&-iGR%63R*&Qu*bUJ;}$S6xy{aQ?%xuPRRj4 z+Lw#h=U8uf2aS`mKx`J1d7kTHCes?6ldWPpu}%=&3y4pZX=7i2*QB>5MnFA!A>Fe1 z!N1}%ZdH}fU6@shk{G;A$#2ynUVkW}1_o*$<$z4%kSg*++i>39+&_O6N`Wqv>py;? z!*%p8!0Io8O7g6V=tQP9APp26bi0^P`&Y4Xy4qA z|NXOzpwvG2XX1=YYr385llFHsNml-X%PwtGhBV{DyK&s0;p_LY$jg4PRcd>HJjb!B ztKy;x9rK*Uxkk->$HI#hO3^TP4=3=-zc@lrF%z-`}Fd`lmTIrFWy)k_Q0cR*ZhuC135X#B5s-hEDiXU;CXo(X5XBVW}$sxa{BQ$a(v z9}$?!lWDa(%?!!q_1klO2vrPr6*bH$D<2oX;lXi{MQM^*j)kkoDs^!4iFvK|a*+KY z+XAU1B}2e37Ay;v!2dY&H_I&hDgG5Lt@WpouwDY*1{+E32_SFq318knMJEMAa1LEtkivo$>wT<80nMECD}B zFFNqMQKvDeg=vk3`@I;a#cHup__c0Hg7|InO#*E4_m?5dz($EQOGMR|d7HCIK#R;b zix+RKLa$MqTm5Rvd=-!2eD-ae9qMGqjJEqO<`pgGkjrCzN2EV$PpAKH}WCgpJD^_{1-#vV>; zL3&3(&ip1;_r;O^(Pd|ZdT(E5-4#Vf;8Z{3PCO@res=>iQp75OkOakvmaM6CbJsk1 zvp^)gm+z|DNfAA74k3YQ!=Z3$-|vsZXeRfG%} zFLS>Vggz+~CTu*gs-j*{BQ$zd%fkuNvG6>Pb8OXXxdVwFC5Sjg#)8 zlY&xXrHnO!;*h3g!XTVM%QOySA<+QR4seedNMCI#KB2Uvi|MZpf~AtTfo3 z=ave=Sf$aaGE+Hhvix_`$bwH-SlKY#NXjB;SyF8A{vA0P306&3GeT?n4;J@~a0D)$ zgS-5^A_>AyZw|fR)Hma5^D5yq%bXz8YBaBgjVRd!kX|P;fYHlrGpV{;jU+x`Ev-Mj zxXVO0Kkg;js#jh1J<7atm%i28--B9ox9iqSm9_gnGnU;xv79fW{mt4^iNdp8XnRW% z40#Uvyx0|Zm^pmft@KUUzlr-=_CU`b%HmtmvmX(>yxz07fE%yA_K~>1Xt2|*i+&9< zUBcb+0GsHoJw7W4HaEZtvaQIspx=rvJbAT6? zdHCA^i%hD@@;6*!DK>Si-9<{&fO07q{5uP-qJFOk*5Z6h;B%?{)m^jjK{>xqQQDMw z7dbc&3Ktu@cR3|xqM74Xl6j(8yntzILCXQw6fV{%!L70})%M;_d>JKThMspU*3Z4Q znTQIcC2ruDd>P!2g6$$O_XsUO&>h8oMvy2YCV;8mkosk{CH! zm~ITZ3Eh&!&rP(=NKWNI^t>fXrzk7g?=*;QLZ47Z?zY#ghXfl>5{i*6);HJ#xt0ye zo5TO`0Iz#Q3VrcsX&sQg?sm3XrRpard6Js)!@r!=niunTn9?>w{>zUHOMJcJj^;VV z7!MuGC5|CUB}Gk7$`bCcqe`Fb{lxsU7N4<@TOF z?5;j-JyGxb?1<*ToNR8eH?w%ws6~iL{{CtNYeGJWvs<5ukcKqZXwN1gKXC{&M@kix ztccK}&afqv5+Onlmet7@izxW-f?nvs27X66yw)TsP2gZ84Pa7&>}8ISi93`qp6qVY zKEL~k#_(7vPmCHR(B=3B7XSDLw7U9`iatwimg=3$q-UDj2&mEO$!nG7zQ-%LOtbuvn_M9&=0%%HC5(lwpa@WbBr`r@>XbDa3YKnur-(5!`%*6 z{E$@okmH`J>Pu zW%&inGQYZX?_Ujcbz+Um$yLRg!(Zr-SSu=H$j-x59Chn((a|?&dwtWQ{Wae>HCe1m zKE!X!97T(HHjM(qHACB6;`q1ZQ2pZ({{#$VXulQcB@ev9;{i-YU0oCGj86ANspV?V zvOEa%av5#1+bH5A<3@-e9sZYeHT%A)o!jrqh!tZdyNl2>UnaL}BI^}C-0nVp zpg*p?y?cLzDPO%6(}Y3=LTQ*H^(HCunbFamZ$CGwH4^gR#uBa-fpsIIiK z7t@gcw5I&+OI;8&_r9n*F;9nEwW68C`w9QA8(_ucTk-JiXTJyzn#WQ8X1`T`Vm8}S zpB7hQ(&Rz4C$C&-q-K_$TUJL~)Du$^jra#fiEkk-g z4g9quDrgx-jg^8|lA~@^nBTiFeaf`cJZEFu4z^80Eze|uCT)+EB)3r2lQi*{fOd)k zeYGm&6Ha3F1~(dZN|+U(Oy2STbgeA?rr8m>Eel&iFsMkP)4o^;D=AGHt(_@#RNKqW z5iKvbeX^u8Sg_2Hz=#PA3>>8|)M@iXXns?iYq3JxO^;KuU+&#Y5tv@+(;L6-INJ8oyR9nT4FU!!Aw4r<=8&Q3WCF zGk2WNpZwz^@>&Kva!#ojx0^=WD>~Sf9TQl{Iar!0_f?B_EUbo&We|RC=GzGH5Zqo}9pS z@5^g4>|!L@dNEFG?}04qrGqPECCl&g*Y_&tABtw!NbIqMAIvt{@yzDtfsy05lf7`i z7KFrSVO*#2l4ed)>%+4V0aCBj=Rr*EK(;%t+?*e)ARY<^%iK)C(%j|eth4E1w}zQz z+mea6s+3$HRRMRU9V&CEkX7^is%$WLiKBrHypO+ z1e@*<5V9FtZ{&A?P7WTV9>k0gQXT|ZIMX<&u9$I zYeq<_T7Av93GOi2K!n!WYWq7j)eo9!bvC^vP7o|V^*0t{*DCYV%(4DO_IgvT19Ns+ zh%l?PgZ$b&sR=MOQ&~Ca`*)%e{A&)gA881vucL?BFWrVvh}breI+6+6?S}UiSCAn> zV{UExgU@4&AA)~~Uy)*|mxi>Mse?xnLuUuc&0OKsS}RPEH#y6R$96WQx=O}H+z76+ zfdFps-*9Yw?V0xJr98qYHm#k=?bt5d%iB~EE%_htO~JVx8G~(3$HRNO(!Rk04p|#6 z?Mex%ra6mMI`!$m4H6j7cSt+|$V_U|NbHNi;2?j4y~A(pkf4;zA1@3r<06gJumLvIXyCjk`@LE0&VgGh{@@Z|2ZKFgo$)Q~jpZF{6 zt1fV~yM~@H0L2E~SSlF^oN%+jwh%CvtK93Q5wVpLeZWZt>(8(A9>F=MbMElbFBiL}Q(+cTW`JrJHv29p=u?(D|S-tng#@VdnA#?7Ap zA5)^&+jw|GGK}GN;**T{XSSVu6OfArhH3Wei>>8S@;eKoDA7<26L#=^p4)v}p8j)t zb4;UOiCh{Ao&K;NYV6v`Ry{9!VHwATQ*Xc`rCB|vCVV0U1U)Yn80W~_U)E}$qCl?S z>z&--6K#$ieBD0aTgaObQ68M%dqH-6NsYoidKS<*#XTArWza3Y+7>6-FDQ3A#66lY z;g+7u{o|h`GNK{U!_$pd@ zy}ShG1qjxyiUM%mcSe#O;Fp+^S8JEzej(Mgr0{#N2z3>#gzB^U60|~W;zP_ot<*y_ z^r?2QK=zQ-jhgIC4DMR56RrkjVk3sMalgL_Rqs47A%qoHx zXusa~Rf{ZLN)n>M&>sKxkSD{APt;9;2b#CW%6CKkeW!XY82E*rd~TB@9P|TMyj(}+ zj9M`p!qAHfJsm8-SY2&;M>j|YE+BbNZr`_(TQDucR;zXWQtW1WnNZHI<+vOi1?}~CN=@>Ad^q( zpwc_|M%ZsfnQ8emB`264ihjUl%4IKdB6H=LqUn$ zJp7+UtqqaJw#%7p5&iB-RD1{Ecp~UF}uu(o|B{p4)HR{h*fj2PAecZ2EH(D-(4 z$^IK!>{H%GSgPGCOP6Q(mLDtpfGRTYT(l)hGHlU#o@Wkar_d;h8SQw#fug>Oo;Yi` zUc z9tm-w$^kpN{SWV9D|H060V7l5R9_T*UM3-LGhLl-RQf6f$YhQYWzlKgSfbhW^>1Iix^a;u<7qZ0BJl45~8hzy64)9 z0Wp{dB(4LcO5n3{7TNg=3DoJZl`O30y6CU0@5w?C8WPdpry+A6d^N)!LEE6kQ12@% z=;68={IB@_z8wc^94L!^Yv(P|7+Oi57~0y^@J6jA{z_#Zopkj4fX5yw{K68v6U;>7 zwZSeLRO|ELGCPZH!}tV+R6mt|FKS4In#+a5eSzpc&N!x^ zM?5Mp5XbV-DeZ1~$naw4Aao2!`#=OJtfiNzhDM(<!VM-kz2EN1#qMyxgKTB;(1hLx#2Im+fC1*HO{3cXGeWl%tNKK| zjFXR=^Gq;=ev@}FEXfB}bhc-BA7#<_C@Gw32}>)xnUm7#6&Uj+ITIHbmoB$s%}@B` zm$kNOw~O&;t;j%mwmVcad-Adu^Zb(2c>Y+E1xexrw``3gWN9Qu~ z#NKlpcAWwY1ia-D7AZ&xn`89LT2JSYAA$5UptL{(yVE7$@ncGok&lMo25Qjc;(fgK ze#YA73xb<0V{Szi>(RrMM$3L*K9l2Vzrw5>C)s~X1Z3#E&@&}Zz9_9g{40}4R{f_a zRgN%_l5weQ1u=?ADy;~UVUEs{c41F-$ox*6e}bP98YyDAzyfjfOlk)O_ThSI?*e{D zj!{1G;J>vFdq&LGTncB+Xw@gL+w4IUW`gk>fBcQYD)c+A;Yt1Mt)srD;+*<^_0{LZ zt0AX_x_{rZ1r6S@Uq;{Ssj9&6Me*tqHz@R{8fz;_TEeW z%T57NNzV>k@&8m6OG08cK4mq_U#W6W7%lA?KZqN3;x3bWp&CyL7>(R?`TNC2)!vW#Pd* zBOYPF+UW^6{Ftn2lm=-biK4y;ubAw%+fDSrS+ekrsfHHbBGv7^p4yDoH?0gE-ht>Q zmPfL@DG3*Kusf-;+rLKHjWaG~rKALcsO^Ti^bN4bKdZ#D!jqN4`YaE^Wg4U~N;Hb3 z05R4)O9kqtq6IVb>WeigbcpLkmV5*yS;7OySE?Z81*jG4`iO2B2&~=@+mIz84+5`2 zd$ZHKa+`F4ql$TT0>S@_kugn;>86FOGz9g1I8u+}QV#59r*pP1DreUF`B-?%$&~mv z>4}INeRN6z|cnWV)# zSP3-iPB+94|IhPVk}ABEAkq2^=)7|y=mI~FJWibBHUVu3&2?#tswTtX<~pxWDW@&o z(3F;%6cZv~>t^SoNNY+!rxy4OZ+`%(>--8X^%`S)W!hmn?0O5ZsT@oo<6FH<;L1?- zToLIo6#wBypYqy+J#C_y6+nhWJs9CUfa|!Wz#2Gzqe$>kfh}W+q+9d}X6f#z-ShxM zbC|t#HlNJn{EVl>EFev;r3d48|J9H$(>T`1Vg0u-@_7K-&H>U95JhtXR8Z3Im-auxCzpAtP=}s=w=epe-+`3P zO6#IP0c+2NMPvBXzzF*S?@ce^3=n=BYdGK3@(MMmrrao|Dx}Ms20-HkXCnP*!Q|jTZMZzu1q&^LXwT>Z!EhevXw&t8bvwuJ7tUw|;@% zw)aieyz*M?3Bc~YvJkS}r+Q@R*mNk)B=Np~$R`y}(gb?3;wU>BxOL(&QO}$b8FmJ` zD6;6^xKS*qM1+EZLP?w^Ww5E%iEZmk8nC1Fi9y$!P#?&nXKxd2@H;6FUq0?u@Tu9R zifphJnZBmw9^vN-Vf4Z|atJH6`6S#ef7t$>p)Yd!+X-QJwqJ$q*H*q#dB%^N%VeSBKfM(>{%9X6N+Jpd>AE9iSH)VF=-`Uy$`E|SET=0HEXNm%P=aP94Ir2lOUUhERqj)GW z=9Lt9v-`KUbqg4s6N-v?78=9&!i$u~R;WJ~({xSD1|DjxPe@y-zd5XrHCDSl*wutH z=sP>#I|j$h5-52!7-?Q6N8OMAU#G>FRqY_aie3V1v}gbJO&H}9@e2(xoTC2oL+i#h zw>p$k*UY9P3>1&i77sjFPi$(|iN7@rLmfCQp44U?4GEq@!~2DYtX-H`q7NJU|JW zG^`D-aEG<|G+>|dDhC+|7xr5vU*vE?wqpx7tnoa4D~Nu#^4<%jCMPc`S>AkRz6;9} zt{h|%4BLa4c>!x&B=rQFN~hW)z!fRvp-ol-yVKk(<(UFJf#0QhOq+ud-G^18?3N(o zBWp6m>Y}rrrSf$tuU(%;_OlmhQ_3{m(ZE$Mqo~^Ci19X$9Pi_}@6M=Y=zAwD9{w?* zB`bi?L}OSW=_+^$(@C*|vVQMQEw?2s-uXKJC);A@w0ho>2$qBLc#iV=e!LRRSMI8bd(rbhvV>g z#J+O2S6Xs>eR;5USdLi{vQcTixM9|?Jg>e8=g{YAj{TPsuj+(D4r3}sCSg{d`L@{H zC%V(;RoL9s+Yq<42jc8Rc$odD4JKVCkS^gIqSDYaZIM}cBrHgpxA84}Gt!$~I?OLo z!2~v}minKP8rOQ!njTWH1SSOPpq+bY8B1$HqT$u(NmV-KfWd zD!?CAeE?c~CH{?3gSJ~rI=E1=ZdWJ22aSY<-S@ z75R?D9tX2qOFIg>nA~m|F=?|qf_3k?{WSH41UJaJ(hQw{uR{mR>kJI=4DKVbc|MA zaC&1L!rp_zhcBtEh}I^x9*pxj$@;e(lCn)n>O9p?$VyBCC!q_8I$8iAb{%+fLLQ*B82uLVCZ%*oMmO+P1aPrclX zpsRZxQX%z)-(4RjI--8YH?j*h5Ru(Tr052#{fFD{1*JxuzB>ux5BVH)@jc?_kI5Hi zAIAHBM0`=901hcx71A_RS3_mZ{P<*-x7MH<@d)8IHbFQ8$K#W03slwuYkFS3V%zITmvNk_&eMfE! zvmA0-rHZu2utzVrqyA1Kh@I^t{|+3DF}+i)3c1KqE^XojJ8B1?Ds_x7+tr^XUPD$21C5 zNdhkanb|$CAafZKsi)kT-H=s#POblyhfjqX7C~KaMc=dff|JfKU6i_<#Ofe%x78FvJRfH3T5p{geH~gZ=0n4|n~~^c6WEEFVTh zdG~v1F{ZVxP#hZk$c6*X`E*5z)#E_tLZRA+ixgiU5u~lv1Rv#Q00* zO#A3Xg4&u*onh!1o6O`Bw8-omzsM|fvAm%aqCZzXN>nPa{_<-NBv)l*Jsu@x6YryG z@E>-|drl5?!prKslQc;AWK-#;qbAT-&b_rocQwLw*L?OKm)(7LIRYW?r=`R zQL$c6J>7*JQ$7!-ul7#1XMjquV8J$SJmg$TjH}x*yz8F7&GgJ3{_EF&lcn-JHZoMM zmbh>Nwp~{tioLJ#V5-ZfT(zl)8Opw9A(sd?%5yDLqW0frC-?YV5K&idZ&N83$v>1M_H~cV5Ed-1<{B^Y&OZo z`sc33$>oSHh)=E99 z9g1qS(w#3kasFBU)1Ulf7OpJ-7NdqObFl5YTwX#6q~Z?uh*tG}>$y}*(=XR01AS2U zy)Nq=1AXJ}*o?!@iED+{U;U$v|(G54HQfi(sI;&+C#&-v$G{5!R(f z`Ou+X<(92)H>#SF%{679*8u3R%pcW{KlYD&C}xO!bDm~SrN%)O4xr~c*>`i94nxR6 z1L)}jXxbcYKw9Yi0&9xCpH2XVego)34a|%XfS&4a8-(#_nnU}3oOj;vQ*P_=GA`B! zhLYcxRBM{{4mSBVs{+^Bz0>A=N?nzPCP0LutmqQ2Egt3N^N(<1{z(>BPJl)rNp>++ zi%N`|gg8liE~xYCD%osJnIyv&?hgfs9su;=+dot=A^@jnwzg9^$&0L)-HK`la9(734OJqEwIpo1q-&8 zxhmDPKZII?3;Qc5F@ojf`Iq^`7k-Hgm(O7V;TjWjFba-w?>zZ4nd1=K_yM#7ps#g9ZCRA+P9=M5j`j$I$1hPCCB zP$j|S8dnb1(nQzRf#W$w*(wcmGTFkj%P;Zrg@<@;@lj4(d>W11!K1N}FQIhB;EqwA zzT2VmwFTd;((|DSTZY7cS+4`R#(W=jK)uWLJ1QFKjrx*|g0%`PP2ivg3)Yu)nEDJ? zL7Fz#aur=h5|qTnC|D!|)zdP>3Zk)V_NM|8p(I>Cdx(8Ahq!6`yV}FJfXg@ql^c@(#Ra zu~f1gYg7UBgi&H3m?nAUI)_f0$D4xy&{cKl@~K`m53S}Q!vUbLhkahe`Y{FdA7}-j zH^Qn@De{lp{qK0wj=PK(2|&EOfhsDh%7YI|bmWL8_ai3x;&&$^g3;!SuMvQ*Vpj!g z-2muX*F8s(^t6dXxAQ*lkP4kD zo;j+(E ztTIvB%AV;vxncI(*gtca9g{bM)PY`2a++Ylf(2_HNFtr~}}8t(!D^!k2K{RYsB7~}N<=wcJ(eP51#Iqec5s%Z5~c?lP%=W@Sb z>=)mcpaw(Qe9pEA9IF62bwE(J0D4uYc>Ojk0J>7$klOl68vsDh;dhW$kjFc)yVMT}6WE=`%^hGtqr=S`xUH2pT0|gssu_+yQW_$P zR-SF{g4qNm_P+Z3(kr~W@C}|l_vgH^^gPBGG_g(OC?eD+){YILHHp25cwKmtRkSo} zn%L?39ZbRaV4FQ|dH}t}_ZxH*scx9%8v%d{4E12adbw+_PEu?0iE34uO|rsT2e_+2 zs5otOYe6E!B-O)nzjo6-+%eaU+q)?-xn&zSPJcUl=ibV`nR}QW+n1AkF@b>|ELgBM zVg%opK@(R2LtIQ$4Jkpgf zZj@uLGyYERGXUrTK<_5LZvXM6dwjjzWB|~6381f)o?3DEvA*~7(Jw2ipa|j`CHt1| zH8&^7eP8&1bqgeED~ap{fbJNjsiaE(0Q5Y-v^o&(7Ut;;K$oc5#MyWNdPAMsAq3Dn zy(a^L2QB=Cu6duG|QW}-pf7vkFay% z;1Fh*F8QsXoH+kgp1Jf8rxu=K{@Mw*mJ;_IOzJ1|j5krothcs?nVsN%p1v;MW-P56 zGL2?q2Mlz(Mkv?g5T@sTtRsIA0Q8>t@qPusJ+DOT_5544F56?M2PS&3V4cN^t&-?e zbfUh)jpq8Q z4;C!gn$feg{Qc_a{^Z4%`1harS*~4Mq=GUrYuL6=Fp`jI7MuQRpce(8=kIYe0Q9vg z+(#Qgua^!{D?=5R_ZmP?S75e|T6|u=0Q5$LyGAzZ0J>4Q?|ok|qW6pP%O2Fkrp-h} zRlPc{M2IH8_SK)T7q6Tpl7w~`=;{MMl{_3n%bRQe6-@NY001BWNklCOP8ro}ppVav@YckhM26}#) z57VYy!$5EJEV=~+4My|xdZTNkce%bt ziRHaE({zJB#&z#~t^9jvau}@p_^Igmr7|k((xMuHNgUIi7`Q^DW9-^`m|N%WW$)bG z?3lRCv(tgBx;X8QVj9FIjbj{D5MSNqWM}G|mD<%!&VvODHp2e3wucl7DivON;~9SI zv;TmKREUV#cAa5-HUTRn#uC{+_|abEpE=NO7;^E(*nsKrQ?HX7TSxp?DEqwN2a$fi zRu!0y@U31P0=couV}1VTiFP*DJ;*1U9ip^)PdAS&!ym15HRhTfOwh1jXDfJl+xTvN z_MM-KKvRaeI0+Z0+7g49Bd%3M1(4avJP%Ra83J;V*Ohq+K zUAMiaiJ=d$GH|;E12?ZtDizemh%bjMSC)Bg;V7>!KE|t;j&kA3E0BOOF%?!2Q=-uU zZni#E0>f-*owe7#6&uE41Ma_RrP`Y8(_q1Z1?zQuBZ^h=4M-~(#z;swd+8{r=Z^xG z*vxVBj(2nY%v~JV_FhU7BL>$5TgbYSSHW)|*J$;3;9rZWR=CywKL5J`iMj;~7OW^U zbyz{V-eYdscLzW69ek!`vbEPQICKQwN!hrr1AoI9qRNgHLH|`C85{9^)#Pd1L-*&ab?|`Q>kM zZsApbLR$7fyLFN*lrbjuO+K3gbQ*%wA)k0A0O%Vwz^#>g3)VUT4;C!UucG~sxFOJV zED}@=A`uDKP#wpJ!PC)t=4fq2N0@_ z1#1}QLZ*@IquAUt32Q%Cu+jH1E)UOFn+SlGxcAN<=ft@e`Nm6sik2(PpBZEOzA_>) zHf@AFO5fEEA9~zR7W^<)UNx3B>CQxlzshX0r}s`NV6YDuxj5f5ZneB(~oHGKzL;u2!x zmgDf+!c(NWrPf{pV&B)nfKeD>9Dy_hCjRYl?>CAvMNHB<$F+@2aJuuIM1=}bwPc@!Nb(M3~C<;IJ&Y$7r z`4@TP(lM?rBwU+=u}LsQSVjay&!jIlZ}46ZD0hSZ6a`MZ0}a^(<1QoN3;Jhf0H3+q zwodKJe}Aq2w|ECBX;;zDzw+D3!^eLQ65CcHxEA1-0zuvRwfqOHAGC`)oS;P~!PQ28 zw(6YPnn$ggS{66KnrVM`0xA@3MKhi|0=IJdNvjz8h1&3f(e=Z>N}GPjHPF+0mQ2^q z)@a>ltbd#M=Pigj-=|`H(=R8(Vr_*ecFI?(2}snsr-3A(QC(v&8t8;N$t-uAVzQ0Z z^f{p07-_He%#U|G_vo$(=g3=^>w6Tz=#2!R*Ea#{YCjDYY{)@_cIW-@SSq`sb#X=Y z=hen(G;aO(Ow=OAfGWEtZ|CN1_j2=&?_&GJ&7OT;tp>G@Yn9?>S&x`hADxDp z>cN5q+ZbkZ`Hah0PF^_9um0B$W7IM>ZP;8SI@qjZhNbv%P2&HAQPLPSA!4Kz?&5 z20+-n^SalvHF@^A`?otvQ{M?Fab5 z!yn-@NB9RtgFQdG7vdHrJ1QZ+#Ipt=HL#5G>l7)%97sMyFiVNU$oj9dCg zOAL}~h&l$m`<@oTVr7hkO4giIa+`=H!4UbNH6Tr+>|e5~hoFl-TKn%Bb7!rl-u>Q3 z2fZJxdwfHR7Hp97Z{*i!O1?}d`y;BLc6M@aF2Qf@{2GH^rl-MaudX|c zPIPsW4jWCUYcn@PtZp_zGqRTfV1AxS+A%pB`oxtbYt|meKN}0W0#>t)LmihKgS3|C z$b6^;Dz4!d7AuAX#bQuRF#fYMy-|PNSfj8;3iU5CMC*N%Ka&lxi=|CO>zj$DhjwAj zwvh~@YyJrq=0}(xYx|%yuE-AAt*fjvrVJiD&-*CF$SN z@yCqOI>X+q;Y62TJO${T@JxbbeE@pmhj6tPdgBw2qEA|~Yo#YS>Cf8$;i`<;S`biG zGYzF)X`vVS+V#9Y)xcV%z1`=)p?4crUNpnx)=w(vF<4-|`DwrFwi?wxk9Ag_^tu!; zT{kIEUIo!z8FH-}x%dTIO#+=KwCi9@R-dh8%|dJg{qw5dO#fXw_QrSRwmqMh8>_MC z>Hwg2>;@<6;_hXGYos^1|F&oE2uAez`DLwb`nz^@6$jlEcaZyIw@*JLgacmw-Aeon z2!$PngU)t+g3ATc_`o69ilEjYMpuntz84D^#MmqybUlYO3(CY_FMkc!%Ly)Ad65fC zFYx%82bmh*&GoZ~xn?zGOn> zYOr7fj%_lcq)J9f-@olv-gD>Q;-P0AR-NRWYFk6lOvWkIK6m5-FK4F~F_K+3 z|K7yY2-0{%BqE{AxQ!4#7^yyx0CJyao~Oa7s45>sq6g3fVE>~s`FG~uzXa~#nl=F5 z{9x5fxR`?I`NlZ5X8yC*Bj>UffL`xR`VpW>yW?Yo+U9t*Wk-Vo(EGk|ciWV%(_w1a z#I@UR)@T#7-3JPNvS?NPR%Z^~n(BA$+g#F7F5R|$U8ftdE@IFCdPn|WH+N->OnLsWwt~!TsQr8Zr=88 z4s5@dv3RC22&14cxpy%=zR$%lV$fXdE30B*56soY$FeHX409-0uz}_yzORpB5OwlUe|q#| z{MqBb!_=f<_9hUUAR3bxOKeM6DR=j&?E>g6^3i0ZWWf3dpkq59mm>FiRqbi+GiU%E z(_DAIUGUs(fx`!&cY5wo51?z86Uj;r8}@TZjILt*n_NYIm3i)@>#SIeTJC%Am(rQy z2f7NNi=;oi--RI$J}8lb8aqxmAf62PA@(|P9c&^fn=)!Fs*I-7wb{(U27TL*+@}G^ zV1uY&gY+le--ucLCs?os^E9um9`>L1eS9~6!uW;p>(?4@wAP2+(!MZAgrVf+@GKTZ z#Sp3G)$m*at`=wDX>0eW)vfvo6VeSMAHdx=4 z_Ej*R3=*NLy!);rOvh7PU5-dr6nt}G$EYrcSU2R-t@EN@?pr!oa>ZJE`!MbM{k|9N zd(aq@AQG0dV2>&)ii#7`_Q<+m{=?CKn4Gxuq`OF)K>Dgv zhk_aC)yrzz+M$gs>M-gW76!VAv~W^dxI-99DVKt&${meaUCCK74D_ZeY9uyJQ!j@y zLJahdpMS%rRK0*1Kt?A$jsiA%h&xO!Zq=jAbwA&<)@{Yn5Fd&+)IMx-|LwdsSn&yf z+2kl)w+F;wo_6O88qME^CEo)Y=d9FUs;t3G>)a@6C1Za%wrY$KMXa)Q`Zn&`{XOiN zeFwWH4*F~P2B+EV%dNm1<2;bhg9RHF=apcIFj4_xw_~XCg~va}XO4c7t$Qt#+Y-+0Ky#TPhL|Cm=9}H;nn_0P_d{@Ff!(w zVkcf=(&dn8E-4Z@?N@6N6LDerMIL(j1;i+`<9oPi+k3ce*AK9J;-E*!>!PVz3cBAc z!yXwd*x-^LMNLH7@y;D=Srp}+cOK!PZ~XyF=jNH*<_^}dmc$Hx)s7B!RQyO43U=DuIR`EZAT-h*fJ-y(apWv1#7@mcP#Dj{ZL7s|jOUs;t?xa<-L!3wh!2 z>_t7=Ksv5k--t0*Q$6${!8qF?{8*u5yf~{?6QOH8|3xuncVO=i<;4o) z@l^WV{kO_D5q1AIAm%=Y!P_H8G!ay{&mM54HXTglW)4j{bd1B~NcuT(+dWa*%mDdN zB(rOj*96VaU^Y8JhIjZOq=Xu7YnJ0c1k0{!O!Mw8I!HB7jxv zgJJBNx`)+=AWdK%@rzFjbz;NBKoXJM`A`2BT-Ld{!F&*r9yz~SDVKWfra_yP+Ep%? z&iWpB|I+MrS(Qq`?5eC(x*jY@*I=7nx(6-UVr>e-^=3WTCbMxP0vC<_x(>w0RK2>b zStpw>;_vc%4>%(2G`{HMb$O7j)%(^0@Vi?d8#`f?O@64hnK1D6%<*>DRWmK~RS9&b zYfA@V+}Iv& z-}S@Xw(I+t9p6)(paM!c$Hq6*6^k~BYz$EGua}yNYsFn4KzzZ1wY5=JvjeGS3v)9& zdFz|r&2wkJg6>Gb8Z@#PHRxLMq-tF5*QI-_$&uAmg{dlT=cGPG+kirbPD zE^7SpIH8s7!4!h66`;saxc<3PvoY0N->w#SX&5*7Dh?MvbpQQoG{~FWTPr-Y5&OIy ze5&^IyI)7?XR4~3Nn&fRT|b^yyf0EA!d4xFC?#urAdtN~o2>Vt_C6qn`vvY(FN|(M z%y?AM&M1l7_0 zE~qft!HS6^*3x3{B<>3PU$D)vUe>!#LbXV7RxvRpQ8a=1m9u>9#DC`NCw`kf6Swo` z-QUN-?eAi`v=^b2V(t!p7eLD(r3Mc(0TK6FjWfL7)eokGrqHG|Sg`KRaL?`E!;`N) z1W620g4)P|=Z)k$2I%9%9;g97>h&1Q>iA)^&&xqpfV;!16xA>uM70pXuGs@E-yfs* zSp@Nby@J}w(xX_Dc;G!6N~bK+{~)!OEG&1br0v~94O3k%8JM)gw+{d6qM;KF7nyKgzD@1KhU#M>x3sT~%f}0Hv&{A=ZGg zK7WdQwP9`_#b6Ok_DsUQ8Z1~5cIOT6Vrp!TYfFoa&k05?h6pQ3fAU<<)(ts*aeG&9 zK0m~h!;(Mre?FM5KX*OrNL3*+kdzTAfh6uaR+x$>+qr8o>S*d2+BG5DQ7zRrsA(h6 z85LXiKo8t}P-mN;*lZeIKs5u=P!nxFAA%<1>Sr`^w>;v{P_;iS^z_e$ow_*|;ZQVj zk>Fo~^=_W8rL*2_{s%@^u#GVP+x>YvYb990@irC2Sdhr+rmKQgga}o|h;nZ66~1!( z-|@ST|4lyg{4eqBna^<5F61h5txuf{B9S91dB%AfqdF40KQy;&@aQI3uzsyy*C*kQ zH{Hv%tAc8R6H7=!4mu?HZAPpZ&~~P(o8mdGmA{D&{y=>YpWjR}rejQ^gIlka@Q$zv zL?$Ott}!aYWHi_MTA((SdVBCei2(M_9xx*1j=xa9K=dL4*LDZI{{Q&~shu&qn%QjeZw=Nw8q+pZl%W*GBXsef?e< zJEDvxUei>-2-d3^yL(W?l(8D|;`}50=_~(=-}%N5^C!>$Q;yAlnS?ma`kY8hrgiQm zB+`9U9jKSK3=6(5Sg>x*g23H3eitjt>Ignk39;*du#QIleUJ^mgO44Kv8azJo zR)|%*qYQ&=MZ3*ygKhFz+r>N$@+`rEtv|U9Yo|azW;A0ciU`;QNE~wv;}apLWv+m+ zil{5YK6dF5j$M3+l9}L^?eF8xT|daa*}JkvZlXA}+;8!&;yP_QCgC{-3syuf0^D}v zcM?A`O(e@$O|Zr`%;hrdsKEGk^Z>!)4i{R%cLUEOHpxLsQ>?B--T7V+G&jp3Mk9YC zQgQ`pkpLB38&EKn7*!%%m+`e%s<`Sk0j;Dg^c!{_9>U3O2KwN88Bk?%0n+|G1Q2Dj zIo{FQY|cd~>5H7pCQ0K-Ps)ZR*a(s?!PZPl{GV0~(q?SZW(-pi$^HZj zwjsRx8gqKBwb9m^%B{s<>xlPMoP3M87N2S&)Cfv~vEsGOl>~A7p+s0QCmTR9uO?pPzUJin5XT^)`c|?Y$LCaX6vA% zc^~rP?y^2n)IubJigo(r;`Uz^%}Sm{9O#``#TW;yi>V+OtaV^rEO2?{HIBaVX`VRs z0NW;S;jKHqpM!gTfGyDsU=cM`t3AV^N3dYs`&id8>6U$mdHwaTc%PUcMgnjcEaa0z zxCSy^{DH*y{Zw0Ud%P8c!BEizF-p|tBZ+;HT-N6t#Gq>Tyl^weFFjTBHLTZ+>qtgH zjn}TLUdhUOq!`@$)AxxLj2MzGn^7bvM%zmg^T0{B@-dCZDZ8AA>m1+KVoN8wsqJKJ z{NJ|uM3goZdYXO5b{AH!B?r<*q9Rf=$NB|w_4+eJ>jU|!b1_)ACP!d%5Ys?%0S0o!mp+OVlWZk}YWda&zDn#{BMb+K6) zkX?RnZ4t<|^8fi|*EdE{mp-u*P_cfW?Q}nE#&T~zxv&jAPXPCaJxR1}?HH}*LO1iR zRg7539FqRa?sejGTCT4F?;B}+Z#9>!Ln;Bf6Uiz9(3)+N{9v=KY8G!i!+%Jcd-4 z1Fl=1(;uGI`1aH<$f%7|u{mQiAT;~`IxwuF;#e)9A)!@bT#+$u)A_N+7+Gm=ebry9Muyv_{Ycbbs`Gd$FB@KuEu|80&3ua$i?JoF zNiZmu7_k-h&Kzj#v*WbqxIfcZe-rYhmw%T89~2C8P`>G76=^H6?WH3OHb8J_1fOBG zdcua%=3aY;K3B^G-qtYr2HP6k3F|Ldfq{3oiCwozXRy%(3)VZ=x|v7ekR_NAK~*?* z@h^Dk;#Zi6X1HVbk8tbG?_=lWO}Sjjco0NtFqtgrmk6Icd-7B8OTmIUAMJV;?B4b! zN@fOQ7d_~$bd6R1|Lnba&?VP(AND)v_WQoU>@%1d%wS)*S|Uh_iYzNpqFAzGNu|;X zh_)q1mdbIJDwbr&siab>08lBkMaNZ2#qlDG3rJgv0MW9Qk_3R-ELi|W5*rB;TmTZp z0GPqd5WsA}eBbLnCx6`A>+AQr`*rtw{oZ$v>WPDy_jS3B)!J76@#~6l0V3!!}6KnMrg|7)d}?REZyd z>1u$?V2>cCAe?tlUY4(i(~>*tQ9)OV54;Ybw?YBK>h&M66 z@v^n&!Sfv-;CCGm*9~B^8+&zw`>y=5mtn+5T_i+x^=M`i=WWgo*3SsQCJ0*~GI%|@ zh{sO-I-WT78#uh@&3N0`YxdoY*XT(AK%}Oh)uD{lS9mPF001BWNkl29drmAv8MJx7tWCvR#FGCz?_ zK7eC|J13Eqd zxsnATi+|>M^Mg(|W(PJ`%fM?$hqcYq=NQ!OtJD~4Xn7ft5QGhYLMRhR1p>m=dybH%aWF*lZ8rBZzh(BTY|VKJm)?WUoxaXy(QXv~T;?<9 z9Y1g1hCBaF{=IB{H9h6v?UR?OIs&mgwUNNMu$mMgWyhZi03n`x?LXnGul^AZZeEXD z5B@j!#%q57`!}w0JiBav7t_4N{g_siV$qA24gm_Jn@YgJ&1<0-wEG;;#j)Pr%?7&X z0nfWW(KOWR^M(2yt*@)@H6M|fIFepU+0S#_O_vi zfIv0=UmcWwCH67U0J&;A}1V%btjToKI}03-56B3^d9 zWKJCuaCq-EfO1+$HK1H_c+%W{tOj_#Zu#_#+^gmLCVjKlBxdQlsG+OtF#x6jky;4- z>HTjS)%+bX03;M{ZcE@UsBH2@lc+Cchp)+j?g zS!y34LZatG3wm-ux|i;fw^^T^qV?`XUh!mJUEp%szfA*PJrKG+@j%o0teA+J|FuqK;tWj>}Szca77?WMAty1QrZdYxH^ zecr1nJ>$IBZM@RRt~U_@5n-1(fd7^>s)wSKLjVL2A3Jz!ziVp+gMBYvX0Jx=+`zA6 z((J|0!@3F)TQ54~b5|ZbX&t-x)l*$rTm@XV1vNd8zS?>-Z^BeZpJr|3c)js<%qGC@ z_&RF>Brpsh1SJ7E?q)E7SOF1RmF_57R2AFd>v-zSefY>{?!a$8^)vY5xsPKzJiqkg z>0Z1HE5{J*8tr$Dd%KN6J$Syeb5D=0414|b1)g^i0mK`8#0n+Fk^Q$qm8gyb4*<*4 z;+l_8Z9LQ@59}zc&+4Th2G# zHIMMbwG!u2H9qd&MeQ6?k%zl1WL{S*kn*Z6tI9}eHf+{?lfS%v<{J18fuf(vbzR-F zbFm7cMNrkmF12+mnv8zCL0)FV$Gm-zUe=71XzNZ2Mzlb$HH7NNG}yEa&}Oc?4%g?( z`joO^sr&N^BTOv~R@QzjBZ*kHK2N)UZGOoar^fsVvNC(NnjG~*n#(TNtV-MXV`p!q z+VLY7&{^(sTOFIX@wsuGg z1D*98>t4!pa<0hf+r;(VJTtVm9IsS>mTk1V@Sc0DD2nKVD%F%BrRhmKTiLvW3tL@){%A%Ilt zG@31Kcv%%0GNp;v%N?wj zeh3D(82wRxFFQHGC-0}`^wmD*_OjFcrfLLGlu6tpfDEpLui@F3AH;8b_V41ipZ+;~ z;gyH%cO?ZBNVCOdF4HGL3ZBQsK(^Uwyc=(Szv4ZRyzEe6?B2Zx%nXS-T#f_w)#BZx zkDaXL_HlMHF2nF$iU?4KZ~%g(ZoBEo?SR$`ZUYcV^)9jh>p*lj@qufwVs}8CV^`w_ zLtO{cp+F+Gny!08Dl-?z&?;Mq%XebCu6!QayASEvG!y79O7~+uT@`1Y%ux}%+vydr zJ9yoAS;CyB8~C%=p_c^(+0z5hS@Y+mo8W$x67!*rm6v8Vw+s6KPZQF!!k3@W%Fdw$ zv~6B2G6rBkJVK-)gcvVgcmgk-e*%B@-2a2O9sBFJ{n!uVz^>y^kgb4ATao2}1N8eXq` zHa^%*im5=l7cV>BN9|nkfVmehlYX)+g0a3m01!b{02mliC<(v;u0-eY#OZ&BCr;gm z8}_{$x1ac7+;ZsKZA7G$0*VTNZFPE*+H2$r);OhbQTE}pmlcqdj);h$Vk~uC2T0>; z`sePmo>@bws*TM=mmsjJ`a-y5DQL4f=vlp&gA;P6_s!vLjaQ;T*lz3z7zT`+kK7Jd zPG0$t0q7o9y-SFQR8`&oMo@rXRj^S=Bv5s`-XwMxkqDyIrXU8!OSzCU_*of$aU-sE zwUNiV1^i8cjaiDhV}RSlSX1jt%3P&M&9TdUk9PQ6G&ZhYmK)gl%Hw{8CYme`+lCe& z-=7d!w0)eZP2P5T-{i+&ZP&M#zTI(qe-`J)wlQtoL|p8NXfTKjG!l>@gc2ZN!pVz| z;pD}~uy6A^y#2^`;@v0izy|NP6}2QFFksnW+;iGLXkzo@+{XJ~FGH6}J+(amq*eT= zsw=pGw0Q8``?p0-F9Fl~rZoU~{mIgKGV6KPP5>YTL!inKjA5XF+5=)$AUZ?u5?g_8 z`;Mx$N&yIT%b|BMq7a5`^H59YV$vO@Il)Onf8gP|Em$954`=MUHfr5oZ;}&{)$91m z>+r%I>0%Qy$$WN}t0xRG`d9M7n-?D=c%S6Q$cvXMzZes%*tH-qtd>esM^FjuiBtv1 zC{!Z^%uv}%nt)!qavGm}@gw-vPklfB;H&=_FJ62ck%j;)bqg!Q zfNRyi7?z zs-j0PD`)H04H)s#*ZTG+!iL}nr^YNIAj7WckfF(RMJ}rs2O6-=u!R5`xl`OgL=o80 zy*z#P_wc!M58&ADoAK`BKaRIv{auI%U?Pyj`K_^)yAXf^KS5pwEm_bk#w5rkWdwuX zIV!wV+P7i|=?iG)({q8YETdMPJXC0y%hjG{$^2QhK642fsEBPtf(V=D0PX~t`v678 zn@R>E z(1fi-_kffaFUwCLy!f&5;-yjYp3t@cN|dWskBA*8VR_31kt_0wpb8iPCaD^DfvHdsWey9K{RHLdUfOXw=C@d*eRz+nW?#5{j?Zi_A10Zx=ORH2N z%VEx1s17ZutjK=9?ookwXEXjCfg#DYrLjU3tM;djVf^=TH|gYiBv7HwDw<--99v*d zt}A=HE9Q3{+pU7)&Q)W6U(wuCEUFSLe*fN$aQuE${%+!U`@1b95iPrB zQ{$TG5gJlM>1^5>Vd>T2OUe4l&ShJDUS5GIyTni{ZEyEu+JCq)yf7wBF!E!%WDY}` zJ#v2Uv+V%4(?qQ>=)^urojE=g@tq$Z@?+EE`s~|2c&QlPVkFRTx~zZ97E|feoHDLD zSB>f0wBTPg1sMsr5?;o`FZ~-l^5O^a)??p=w;%a_T)+PvNaz~liohHfdR+1DnvQBV z*XHC2DdwN{OW8}W62ieuhG(@8HaDr_9I8W2iMdy#R@UTwvGU_%D zYlDX@)SS0q!RRwz)dWl~2k42WHy6!)&#_}0BD)DeX!WiMQ-+R7m+UN~l6{>jeC+Y##Sh{l=v-C< zxS5P%JcpzgFJ8QO@v^4=p9}&BR6>A_t4buiwtW(hy!Zip`h^eTEl0l>-+aSQ;?Tx5 zu5c_!PI4wxEiQtJ8;}y%0_*T6#Jns-p)E}U7?-Y`g_6JsSK>Q5)Xs$q6E4jw>*o#C zIch(7OaGZKxqcHfzoYHPsP$Mh0ME4pdpzs?7QLRbn+RpiJ?F-EV+^_k&ufD9#3ut( zBLE{D-F+M&(06FPy}e2Rec$NnCkf#@feqVQ)JVcptiri!(MAL7rm=`C76-I(B{*fM zRN9)Z26QReiKS`(Hm{-}Rg^M=P`poF1JN}dNg0SwBTPwnNjs)JCqt!?Isf>j>o3jeop&StI@SvQCrDE2opHTTlw1 z6ab_k5|C?JsuCe0!spNZKAt}NySQ=xyYbD}{}is@|4!KMJPcA!3#MRu4B5O}Z1YlY zzP&5#Eu(N5;G@9#o-3rYd zU(`4$tPLnp=6b(mPIRvIGw)hqMW%Uh{k1vOniSa|TH?h3K_~)Y1R%tLjcZ)f5>knorU~M_VD_x* zWkKe8hn`XcMrQUiP!9%Ws618iwQ0%VbQJ|m_hZoS`}R@Yy)a;=XRwdgE<=%}gJwIq zW@_korDysX+Js2Y`sfiDFRKka^m_we;XA1Lrd85Mo2eKAR2Yg7%%@sGzXmta^*{4!fFn43*D#6ymBx*r%vI^XVy`D^1rQ&^hPN3uC8PrcQ-4uYV z3XpTx%XIDS3qT-I=#;ASsuKVP)Y{3DIIcPBPZzl7rnJ54Z_6=hq;EY1Q_BD?m&u9V zHzXkozF!^v&)X?o*!pPiAAAD2+zF@=zqI(h2hhE&G`Le0|Fu2A<^lBO&M&Q^E>h0d z3k+Zc9L4@rp^la^ufGw9jHNjz!tPy1@eNmhKi+ZO4`4SPfRF{vNh1KpHxc!+5RsIv z7$+}2j{Bba`P#N|H34*XmABJ#|0el+lV{E5;&p!0dVFKPZg*p;Ft{{NZ^4f3cwDV- zuR8<_n63lK%txIki+zALdH3wxRm3mVlrmEDzLa^_d_&W=Yj@TOld1m(&gd6`RB-&j zt@xS$;sdOzdjI`o?zlrM55NS~Zw}r4_wPDtSjhtB3tF6cqWP$@rD=m7Bm#sITh~T#EBPoRNLzEvS~-w8F<(!q1y^IfEa>`BVv&_=Zee9N0#HEFa#kA4r{ed0&3 zZ{s+W4B#nTyevog+R-{M(iho3NbTc)b!`sMMn)xhU#E;9VLT6`h z*RQeY5Nig@{Ke$P)9}Fb(qp07ss~@8+4BT&ufMMhV|)d>mj&C!X8KRdU0+^W2Dp>H zWcXF*G^I&+`F90sd0gD<#!yK5o>XdUZJf-w5?#WRr|-j)r|-ktkA4rn<%XZafsLan z>OxhK7MK5<2jIQbiq<*7ATDg3MhF7~MF6$=b4gCuRpt5OBVz6V0zv)0G_Gli73J?M zQ?aNh9+{T@D_7W~RNMB}?R}TWr{;J*&0k9kux-6-=5eul@5lu_l&0eYI{mIqw1`Wp z(b;jZ=f)gnhP#nce59_vPw1+o1n5~DvhMmQ1J7%Z$FyxMa$8j_p6lgMWwS)c@OJG(l30{SxixIRl5Ph8z zWs0eSRxoj0$ExjVrw6<&Fdbi!aZmhu)zLu@uzT_1#fz7^zF=j*^9ARtVvhzBF(87c z&pv?9oqY&5?0*OT^7TKB>s-T8LR=MSTm`#@7bYnVGymKg`tW4w+UfhanvCepr)z%fy>FFF7my1uGgC*X3kbXEr`+e1DKR|aU8yZAJTJd7F z0QBQ~-vSr`!V$n@KpCp!8k%GcpQR8P0+5ircgNsD>x%XP#T`Z6P@=h-iVkh@)Hz19 z3SeeGAbRz&u3>u#9-OYNQsUM}fE*`Qb*l=zbno*tJ^nN3@lQ{5aW!D#Wyc5o-j;a% z@Zu%$4Mo4PLcn>k*kBPLx*Zoqq_#qlRAC@^@xl|h|LLE{P5W=hd#?LCxN-lxkn*C7 z%Q`qyWgZjRaq{OO<+5Ieylw&22q1$OFFpl=K!l(qc|KnlP3eCKufR&&o!m&NK0r)`$C;&99!=6zKKtFWVF+?FDU;)%5 zw^sGZZi%Maz+z<+Kgua?n_a`98`fn#U(-e(Jv;$c8bt3y7h1Z>y4-K;%N?Eq>5G)U z>0DOBYF!T5HZ3x!>cbiC zxS(kYm+H#un5S&7=Ve`#iC0$t^DxHZ;-nD-0)-fR!dN08Bmtl>0;m~* z5#jvyOZdI7-h&T4`8VAx(x_$oqlL)W@)d;Ez z=H;yCE^}2@PCh$1KVOBu6J2EdLw(OCZ?aQ2GqiN_2r7V4#i7mPaU)B2tnKdV^ zG6n-6aKvCya4@z}k{bK>650F$3?l}Y-Z+DgKl{u0$YXyCkG}jLuq_uXn2yl4e56cn zMaJu=mkRvBB+KCBYoCEi2xj~DDq)htEy`c4N*>*1&cEz;o)?Gd(!S5DTRXAHQkOjD zeaEvCZ%c|BuD;!thAXsg1JM}msJf*n8#b@J4hVFI{-l{*4+CLVy>$pw}%oT^@EYX`^T5r(IvO z;V4Zl^sQT5I^F;93a&<7h#wtZ&RITJthVFF9&->NRU#r#{oQV^RzDEo9zL?~CZ}-Z zjG_Qa1Fmph4uDffan`Ler*9qbAo}_S_^WA*y?9v}>*9h)zlOXF$fm=$3-jXj$jch) zq$-AU0*q|0G7?v5ZXhHOiZCVzt}8$sAOwNgfFU3uuo#TTZ{Xn;=L>Kll9sUfz^@@r=wwzNM)06B_b4OTwqTUH*;0~sD)o%RgVxc*w|cj>+2_iPK^ zi>uJ!;xorb%PO|5llwbQA`{vrwSKQP<~MTRXHEZY-uE|%pOXe)!y_-%zDqfQ+=TtaK*d~I{lHZqyVaxY6= zRclA&b;d|C&4A0SR@L1ogI^I*YNIP`W>vaZeD;a!wJ^z9n?CC~>zI9Vo1x`CNlN?& z7v52Ff8WN}lq|Jmn9A_Q>;j03s;Nq6=Wgtq&${D&8KBv^I`z4G7S27n;K;^xX@gaD z&~uHYSnbKwzS+Tz>tRUTFd6q_U;EKyQRl$(mGO@2KQBA^*?HBcSh+j!eA%zEhGJIP7cXAw1^_i}@e;uEb&a{Y z6oWCwED#Pr(HP_KB0h2QgZSv<@5Li8d=%T!>mZ12E~;v|tu+F`cG?p?aOtEU@AZlS z5d)PGfDk|kQVZI~vHw69ojp zfnC?S(=qBW^m^cD?XBQj4*y~Rv0Dm6@v#-zF^jzq#nJ(f#kwq{x}4O-u^Fn9P}RGKJ=G<)v*vs_>0R*K18{ z0+uTRK#bFY_+&8fDa8_Z-wRr1nLG z-MnY90)O}kB`f2W!}kQLptxYXE)RO8CAj~75D28WUk~iM7Q_M)1@r8mcL`j#nbe4Z22Mb`Oz?l^S(fgSDK0tKeRJ2)R(RJSt@F4oo006>p_*xei7kKgF#fukj z$LEOZknJt9&M_Nd4CvTqhH5EU7z$DaL-B@Oz+b%h0o?o8kKvIQ@5NSl*~VF{36-e= z5Hb)%AQFi9GlA=)A8BN*P}>-k;OKh*V!`7teGDWcM4X|#P;JihJM;dlFGJ0hWBQh? zt<#mLl<&qrt#Xdn7Q0xFxL^iO&A`50$Dz1iE$6w)J)oGMiRZtvlRNH=S)f%8?miBQ ztWkuT3Kz(o4`>_ZPL+vw?1JiIQLS?zzs&I&29=8ECLGEdUFUV`ZO~A4ZVhzLbE13k z;>C-%%{|0qv5(v-LQw!R426Lr0)!!LXg+j242 zf5!k|HGndRIn#T|%W6x^nar+_m{_6$08qi{%g^HFOV2<#veoAq&f`AUkx*ZNH1}d# zBGj)wUnEx0bdJcUc7yR(P1L{3>hrct7u{m4v!6*-apG;O+)nFO}ASZ1XX_ z6J(C>yUjKgP=in-RPX-{HDKa}D)Ow!%k~>xzESL>Dj;rNx7>hfo~Eb^*UAAnAMQEH z(S_^uFrRnGdbkMtyUcggYjVOxamcaTIkE3H+<#wMZGN{q8gow_e?Hs1%04sTowC_^*?Gp|Gw(WHCu zl0&YtNnB=A(SkZG0bw1+2`wHJmYJ2&56FKqiKDD=(+&?>r3ZJCllA@ zC$P)qME8e?XM3&qlNKL+_twPDZb;n4})afe3t z-%kbr?t%(XuM)jR1v&_HHvr(+zMH{ndDazCH6Yo6LiGv0Eecrod3^UHvC{yOzTY)( z_!i@^{kquU$@LCSqC9}_#fuj&Uc7i2Y8+vRz-qv-Rpw#{fI=cb8~_lgK>z|oYy(jM zDgg)qw&VgHdFft!=<&aSr_TMZ1AgrH$VWhHp@9TAM2{0~R1$ph`FkNo5wX<~XaFSv zD1>6%tLFBRyO!n1URsXn%HTM|0newd#5rh)y9CekW5J!hPoF|WICkI`5I}d_p>B7N zYCk;H8*E}g<$w7d+IQ97zj{GHU_=m@$|`i~cw4IJ~I#3#Cj9y4vO?WIYkI4!<3YUy2IfCJ__ z{FNJj0&hF=JvN?jL!FMeqzx#Ozm!xjNqm$xsHo7QLAEETBsSsV)+zkjr+?f6=9nK~ zY?>o>pRe|-Yf(jpTL&M=ekUGhq4x197uI?aGs)su#W*_4bzhph4a9Dj<-hen<$B|q zoc`|My^}_+73)PCEP}28^yDU1=?}^4;YAn_A_^-F%uWT+vx~?^_0noEL*JRG7 z1!$ek zp05rYK%Zv6x(3ion>~5}y;gpq9aC0SUTFycdO~kBFaSM%u0&6X>}IZ$-J@OjU%&l# z$^DycC2GIPa8xdxVg<+c-3CDI&f^l2>9zx$a=P+DpOtDXig^|pEp*J=u|XH#Z>ik$ z9ncRo2`2kGj#AdadlAm6cb!8!p0A;EH&cssxSrHS%{bB5u^M`{00u8!ym;}lbBzCF zGf?l_yv$B~A{b&QLI@iG0@VnwZlA`-pZ#Ur|M|a-^6!td-Lg!!1h5bj(=j`ZTWy|g_Lrs0h01c z*EHnETpKHKY~Y3zq%MNPe^E}e?AhnP7bl;%q3FC-?#161pGg-m4pZlmmdn9Nt=Hii z=I8?e(FLBPyv~~PfP)t=UcBre+rMVOx%Xww;-cpI5;$&j0w5q&m}mr6+kEuYg~#z* zPyGY@))#&jCoesjQ1U4Njw{RsAjuZJ2~=rexs;cRc-=^v&nWQ6&;4I8I%-5EG1t9I z^6Rpkvu)1WwM3|AbDG|Nk@$j_VfwPg3wM&AcOO$&{3;m}cLR%ND^vx5;@bUh#U$r> zeGYW4KRHl9< zs>K+T1)8H?WlLS@L-?f&xjsZOc>d_AkKyEnC!rE9MmGz&WLG)rB66Eq@#Q?=`Lb{j z8ibjoDY?t~fHnb+}g2s8Q^r{>^cO` zwPmVBK1VCnIp6Vq6_cs;v*!x7$CESk1zx;(@v@fU>|D|At;>d@r+#Y~zqifnnV=Ft zID%5jl+Ci~=0hPt2!((l`24y54KvRrGYl5bS{1>2NA2siO?0j zgoj`FK)lcvULe)BKx;BL?Ka+4Zz=jA?WH41oSuwz=k!qCRG0u_KkAZQZTab&MU zG6Iu9^I)oUZy3^r2{~km8|d-iPgQ3lT+# z=|B6-zxo4?3-k{I%6Gj?<{wiTT6@}ITLx~RwML>@J~Nc@=!d=~uLC)z4;R!hjd0VU zZ(snpJKm1Hk2tMWudWbpkPWKfP8HC7vUm42R%vNG*GnyZ$t`&6dVQ-Xwbj^nH7@?B zeJW88TQFVgZ!xec1{FWDvV5?ZGj@Hld71sscn739K925IFKNloGt?8~ z#phxQ?4~Ul?A>z^YCVeF9dE*+f)@U^kDUz|pHtxP9VLtw2C}FiP`Q zY$ny7qV_YjWn6b{~hhyL41F9A8%D?r= z3SxQ#oi#Hg^{Ara@74Xj7cX9Rt~9k5CEw4*3w3PS3W5xEPe4ugW9=%`+xp%9&V06y zb>f)2PW-H{h>vUuc{<(^0Er5jc4(V}!T_+nDM^7>w@>4NXWoZMh zrH#3z5O<6H2bb#$)EBz6)Dnf|wvPw>?B%cEk>@{v?I46kX{;B{SU-*Own8p7Eq`p= zSIhpE8$VxNo7BCHQ$$KROaV3X$E<%K*5p(45%b!&c>K!pvlKsSpBK&=*E&N^s-Qw1 zowdS~I=~<@rTRfSnUA*3ODFCzMJF|jHPwj+YG<=D^Apb_gPe_>&sTQ6HX>CBfItI) zZBG+J_Ot>KA);*@8C{icD_#cVR?{ZUy0JDh5d9i^+Yf>{rkd-39>lVG8yE!iPG90qe5(Kn%< znr{~4wW>LboZ6n>I*0q8`XBMJ&;J}wU;bjc`J)k_072}whZgjn;3|;V(a3neTVKRM z=58Yb45)(KCdU=IhzFi|KdywAF%koq2qIlz3tpCDGd)FNPWKm0Z#=I*^DAx2_+c9m zO$(ICHV;(^aQ)$Tw7!1jYM}`$sB-PT+h8jurJ*7WadC3yligkEw+5hUVd%gChjj}M zt9HS%74EAL7_1`K+M=I%oj~+rclVu;ye-QTjNUrnY2HN)eECZN8HADjUSJ&0eGjpY&ujZzht2vL%pWfg z&(5#7Q(SSs*KJI~g==$0X9PwAAYu@2G*Etb3>`Qv`=$n*R=~bKIv;Mimew;_9@ZwT z2h->GGam5p;>C-Xev{L62F|@NS*MtVOXfEM44{$NTtRIGp)ld;mmkE3o_H_*;-wGU zSeXDJP3c`U03m>C1R_^QEda>CHY@cyQ+rMv?L(AFO#b96{~TX>?N2}wB>ASrHQea} z4LZ-2=go(gk=xFLanyOIF6e5?ACy-rZ|uk953k5qJH!aO9jEDIRhyov=NAwFtj7JB z5m5+G1ZaS(uR68>Z6U=UgA9@LQABiX?=4IUcVMbt1)XLjx(W)r3clufmLb`xRSrPA z0?(UNq4%=bdjQdUF`dfE$IE(4kqf;GzV+^$_wc}UFJBCk@`i!dU7sCZbJ9MS|J}3#;AJs1 z=+udn99@`_fR%8~-Z!;g^ju@KTg_-fotAGaV`f@>bz-f z7UC?USpQx72*1efBvPyAUz-4zx?K~!?N+a0Le`f9Y?I10)$c`vkju7tLjzzv4sC2CQn|xc=1vNeiUe0*0t)~^D3`)Y|=DdQzaN;L{LOf7>r@Wg0x@*<^Ze# zE^nX0Z-3!`!*6}zXL0t8uOK-$FRm%1En|n}HFl%nlcJglh*&BDwL|*ZGY{aQXWj>b zK>$n5OJe_5Rlx}DCdUqqhMS%$Z<5P*KE001zVBSF%WKC?qPrS_A?Md!-@ostU$%dq z0QEWRZvbY7fN|sDcfyjZw3B)q=nB2#&a|269d|$vZ`{}d)E-je$o(3MCKV)0^pT`k zY9qd(R3Fx@LifRZcfp32dh4@klCrS1JD_TGf0TRWv>RzPIwn5rChHDxdGX@K+mr6{ zf-;|wtu*(tI&-O|G286$E`q>T4T1@gG6Vq(LQrV08IfQc1`NWIHFxU`C70e8T27tlg&Eq}ZwGhEr z5Se{3?jnk z&ip<;_{4kh_{k4hltKZJfXF}*CjafFYPQJkGbr%X*B`)x&%O_&0v!{`xSWOwq67m% zqy|t1K|0Cb`pW}(i8#jqb5kFR4?33~rjGIotQ|$93T8l6KpcV*;hO!o0MOPBAgHsj z$b#q!wSNSl*I)G}M8q&;c}(FNN2ueQN&XI+gGJR|tDEW5>4re#BS#hYVrWXy3eJQ( zQp@4T{J_%(kgGj}b3)lrtFNFiR(N)^GWqQ8$Xm^wIVE+wlT}}V5f7oiNyYQ=7`&}6OMQ-<%Xgn38)H;>Q)x44pkCEwSPNWl{C+nE-oug?G@r^ z84yy&hRyaL)z6{R*LzmaDUD$iCilB$_nTO)CF`?re+m*mI5B#CB>c*D4BWMgKCH@r zR*glLf41!TX^Q`hpG#el#;p4l?~%rzoZPeQU9>R1@pp2+_u{3o$&Ax&rkEwh$#InQG7iHfn)U6