From aca4ba6073edcf5d86af11094b5edab849a5dc80 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sun, 10 Oct 2010 18:57:23 +0000 Subject: [PATCH] Add a tag widget, and get song tags from last.fm --- debian/copyright | 35 +++- src/CMakeLists.txt | 3 + src/songinfo/artistinfoview.cpp | 3 + src/songinfo/lastfmtrackinfoprovider.cpp | 28 +++ src/songinfo/lastfmtrackinfoprovider.h | 1 + src/songinfo/songinfoview.cpp | 8 + src/songinfo/songinfoview.h | 3 + src/songinfo/tagwidget.cpp | 107 ++++++++++++ src/songinfo/tagwidget.h | 72 ++++++++ src/translations/ar.po | 3 + src/translations/bg.po | 3 + src/translations/ca.po | 3 + src/translations/cs.po | 3 + src/translations/da.po | 3 + src/translations/de.po | 3 + src/translations/el.po | 3 + src/translations/en_CA.po | 3 + src/translations/en_GB.po | 3 + src/translations/es.po | 3 + src/translations/fi.po | 3 + src/translations/fr.po | 3 + src/translations/gl.po | 3 + src/translations/hu.po | 3 + src/translations/it.po | 3 + src/translations/kk.po | 3 + src/translations/lt.po | 3 + src/translations/nb.po | 3 + src/translations/nl.po | 3 + src/translations/oc.po | 3 + src/translations/pl.po | 3 + src/translations/pt.po | 3 + src/translations/pt_BR.po | 3 + src/translations/ro.po | 3 + src/translations/ru.po | 3 + src/translations/sk.po | 3 + src/translations/sl.po | 3 + src/translations/sr.po | 3 + src/translations/sv.po | 3 + src/translations/tr.po | 3 + src/translations/translations.pot | 3 + src/translations/uk.po | 3 + src/translations/zh_CN.po | 3 + src/translations/zh_TW.po | 3 + src/ui/flowlayout.cpp | 213 +++++++++++++++++++++++ src/ui/flowlayout.h | 79 +++++++++ 45 files changed, 652 insertions(+), 2 deletions(-) create mode 100644 src/songinfo/tagwidget.cpp create mode 100644 src/songinfo/tagwidget.h create mode 100644 src/ui/flowlayout.cpp create mode 100644 src/ui/flowlayout.h diff --git a/debian/copyright b/debian/copyright index 2549af468..451ccc44a 100644 --- a/debian/copyright +++ b/debian/copyright @@ -39,6 +39,10 @@ Files: src/widgets/fancytabwidget.* Copyright: 2010, Nokia Corporation License: Qt Commercial or LGPL-2.1 +Files: src/ui/flowlayout.* +Copyright: 2010, Nokia Corporation +License: BSD-Nokia + Files: src/analyzers/analyzerbase.* src/analyzers/blockanalyzer.* src/analyzers/baranalyzer.* @@ -55,7 +59,7 @@ License: GPL-2+ Files: 3rdparty/gmock/* Copyright: 2008, Google Inc. -License: BSD +License: BSD-Google Files: 3rdparty/qsqlite/* Copyright: 2009, Nokia Corporation @@ -127,7 +131,7 @@ License: GPL-3 Version 3 can be found in the file `/usr/share/common-licenses/GPL-3`. -License: BSD +License: BSD-Google Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -154,6 +158,33 @@ License: BSD (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +License: BSD-Nokia + 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 Nokia Corporation and its Subsidiary(-ies) 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 THE COPYRIGHT + OWNER OR CONTRIBUTORS 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. + License: Qt Commercial Licensees holding valid Qt Commercial licenses may use this file in accordance with the Qt Commercial License Agreement provided with the diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 83e9c4f8f..fdd88160a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -139,6 +139,7 @@ set(SOURCES songinfo/songinfoprovider.cpp songinfo/songinfoview.cpp songinfo/songplaystats.cpp + songinfo/tagwidget.cpp songinfo/ultimatelyricsprovider.cpp songinfo/ultimatelyricsreader.cpp @@ -149,6 +150,7 @@ set(SOURCES ui/albumcoversearcher.cpp ui/edittagdialog.cpp ui/equalizer.cpp + ui/flowlayout.cpp ui/globalshortcutgrabber.cpp ui/globalshortcutsconfig.cpp ui/iconloader.cpp @@ -281,6 +283,7 @@ set(HEADERS songinfo/songinfoprovider.h songinfo/songinfoview.h songinfo/songplaystats.h + songinfo/tagwidget.h songinfo/ultimatelyricsprovider.h songinfo/ultimatelyricsreader.h diff --git a/src/songinfo/artistinfoview.cpp b/src/songinfo/artistinfoview.cpp index 792719f77..4d706b97b 100644 --- a/src/songinfo/artistinfoview.cpp +++ b/src/songinfo/artistinfoview.cpp @@ -29,6 +29,9 @@ ArtistInfoView::~ArtistInfoView() { } bool ArtistInfoView::NeedsUpdate(const Song& old_metadata, const Song& new_metadata) const { + if (new_metadata.artist().isEmpty()) + return false; + return old_metadata.artist() != new_metadata.artist(); } diff --git a/src/songinfo/lastfmtrackinfoprovider.cpp b/src/songinfo/lastfmtrackinfoprovider.cpp index 213d7a4da..d61c85348 100644 --- a/src/songinfo/lastfmtrackinfoprovider.cpp +++ b/src/songinfo/lastfmtrackinfoprovider.cpp @@ -16,6 +16,7 @@ #include "lastfmtrackinfoprovider.h" #include "songplaystats.h" +#include "tagwidget.h" #include "ui/iconloader.h" #include "widgets/autosizedtextedit.h" @@ -57,6 +58,7 @@ void LastfmTrackInfoProvider::RequestFinished() { GetPlayCounts(id, query); GetWiki(id, query); + GetTags(id, query); } catch (std::runtime_error&) { } @@ -102,6 +104,9 @@ void LastfmTrackInfoProvider::GetPlayCounts(int id, const lastfm::XmlQuery& q) { void LastfmTrackInfoProvider::GetWiki(int id, const lastfm::XmlQuery& q) { // Parse the response + if (q["track"].children("wiki").isEmpty()) + return; // No wiki element + const QString content = q["track"]["wiki"]["content"].text(); if (content.isEmpty()) @@ -119,3 +124,26 @@ void LastfmTrackInfoProvider::GetWiki(int id, const lastfm::XmlQuery& q) { emit InfoReady(id, data); } + +void LastfmTrackInfoProvider::GetTags(int id, const lastfm::XmlQuery& q) { + // Parse the response + if (q["track"].children("toptags").isEmpty()) + return; // No tag elements + + CollapsibleInfoPane::Data data; + data.title_ = tr("Last.fm tags"); + data.type_ = CollapsibleInfoPane::Data::Type_Biography; + data.icon_ = QIcon(":/last.fm/icon_tag.png"); + + TagWidget* widget = new TagWidget; + data.contents_ = widget; + + widget->SetIcon(data.icon_); + widget->SetUrlPattern("lastfm://globaltags/%1"); + + foreach (const lastfm::XmlQuery& e, q["track"]["toptags"].children("tag")) { + widget->AddTag(e["name"].text()); + } + + emit InfoReady(id, data); +} diff --git a/src/songinfo/lastfmtrackinfoprovider.h b/src/songinfo/lastfmtrackinfoprovider.h index 0221b7493..77095cdd3 100644 --- a/src/songinfo/lastfmtrackinfoprovider.h +++ b/src/songinfo/lastfmtrackinfoprovider.h @@ -37,6 +37,7 @@ private slots: private: void GetPlayCounts(int id, const lastfm::XmlQuery& q); void GetWiki(int id, const lastfm::XmlQuery& q); + void GetTags(int id, const lastfm::XmlQuery& q); private: QMap requests_; diff --git a/src/songinfo/songinfoview.cpp b/src/songinfo/songinfoview.cpp index edfe06a50..2c01b3a22 100644 --- a/src/songinfo/songinfoview.cpp +++ b/src/songinfo/songinfoview.cpp @@ -61,6 +61,14 @@ void SongInfoView::UltimateLyricsParsed() { ReloadSettings(); } +bool SongInfoView::NeedsUpdate(const Song& old_metadata, const Song& new_metadata) const { + if (new_metadata.title().isEmpty() || new_metadata.artist().isEmpty()) + return false; + + return old_metadata.title() != new_metadata.title() || + old_metadata.artist() != new_metadata.artist(); +} + void SongInfoView::ResultReady(int id, const SongInfoFetcher::Result& result) { if (id != current_request_id_) return; diff --git a/src/songinfo/songinfoview.h b/src/songinfo/songinfoview.h index 3dc970e1a..1b65ea649 100644 --- a/src/songinfo/songinfoview.h +++ b/src/songinfo/songinfoview.h @@ -38,6 +38,9 @@ public: public slots: void ReloadSettings(); +protected: + bool NeedsUpdate(const Song& old_metadata, const Song& new_metadata) const; + protected slots: void ResultReady(int id, const SongInfoFetcher::Result& result); diff --git a/src/songinfo/tagwidget.cpp b/src/songinfo/tagwidget.cpp new file mode 100644 index 000000000..1ec0ef1ee --- /dev/null +++ b/src/songinfo/tagwidget.cpp @@ -0,0 +1,107 @@ +/* This file is part of Clementine. + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#include "tagwidget.h" +#include "ui/flowlayout.h" + +#include +#include + +const int TagWidgetTag::kIconSize = 16; +const int TagWidgetTag::kIconTextSpacing = 6; +const int TagWidgetTag::kPadding = 2; + +TagWidgetTag::TagWidgetTag(const QIcon& icon, const QString& text, QWidget* parent) + : QWidget(parent), + text_(text), + icon_(icon), + opacity_(0.0), + animation_(new QPropertyAnimation(this, "background_opacity", this)) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +QSize TagWidgetTag::sizeHint() const { + QSize text = fontMetrics().size(0, text_); + QSize icon(kIconSize + kIconTextSpacing, kIconSize); + + return QSize(kPadding + icon.width() + text.width() + kPadding, + kPadding + qMax(text.height(), icon.height()) + kPadding); +} + +void TagWidgetTag::set_background_opacity(float opacity) { + opacity_ = opacity; + update(); +} + +void TagWidgetTag::enterEvent(QEvent*) { + animation_->stop(); + animation_->setEndValue(1.0); + animation_->setDuration(80); + animation_->start(); +} + +void TagWidgetTag::leaveEvent(QEvent*) { + animation_->stop(); + animation_->setEndValue(0.0); + animation_->setDuration(160); + animation_->start(); +} + +void TagWidgetTag::paintEvent(QPaintEvent*) { + QPainter p(this); + + const QRect tag_rect(rect()); + const QRect icon_rect(tag_rect.topLeft() + QPoint(kPadding, kPadding), + QSize(kIconSize, kIconSize)); + const QRect text_rect(icon_rect.topRight() + QPoint(kIconTextSpacing, 0), + QSize(tag_rect.width() - icon_rect.right() - kIconTextSpacing - kPadding, + icon_rect.height())); + + // Use the tag's opacity + p.setOpacity(0.3 + opacity_ * 0.7); + + // Background + QColor background_color = palette().color(QPalette::Highlight); + background_color.setAlpha(128); + + p.setRenderHint(QPainter::Antialiasing); + p.setPen(QPen(palette().dark(), 1.0)); + p.setBrush(background_color); + p.drawRoundedRect(tag_rect, 5, 5); + + // Icon + p.drawPixmap(icon_rect, icon_.pixmap(kIconSize)); + + // Text + p.setOpacity(1.0); + p.setPen(palette().color(QPalette::Text)); + p.drawText(text_rect, text_); +} + + +TagWidget::TagWidget(QWidget* parent) + : QWidget(parent) +{ + setLayout(new FlowLayout); +} + +void TagWidget::AddTag(const QString& tag) { + if (tag.isEmpty()) + return; + + layout()->addWidget(new TagWidgetTag(icon_, tag, this)); +} diff --git a/src/songinfo/tagwidget.h b/src/songinfo/tagwidget.h new file mode 100644 index 000000000..1e35ad635 --- /dev/null +++ b/src/songinfo/tagwidget.h @@ -0,0 +1,72 @@ +/* This file is part of Clementine. + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef TAGWIDGET_H +#define TAGWIDGET_H + +#include +#include + +class QPropertyAnimation; + +class TagWidgetTag : public QWidget { + Q_OBJECT + Q_PROPERTY(float background_opacity + READ background_opacity + WRITE set_background_opacity); + +public: + TagWidgetTag(const QIcon& icon, const QString& text, QWidget* parent); + + static const int kIconSize; + static const int kIconTextSpacing; + static const int kPadding; + + float background_opacity() const { return opacity_; } + void set_background_opacity(float opacity); + + QSize sizeHint() const; + +protected: + void enterEvent(QEvent*); + void leaveEvent(QEvent*); + void paintEvent(QPaintEvent*); + +private: + QString text_; + QIcon icon_; + float opacity_; + + QPropertyAnimation* animation_; +}; + +class TagWidget : public QWidget { + Q_OBJECT + +public: + TagWidget(QWidget* parent = 0); + + void SetUrlPattern(const QString& pattern) { url_pattern_ = pattern; } + void SetIcon(const QIcon& icon) { icon_ = icon; } + void AddTag(const QString& tag); + +private: + QString url_pattern_; + QIcon icon_; + QStringList tags_; +}; + +#endif // TAGWIDGET_H diff --git a/src/translations/ar.po b/src/translations/ar.po index c199753bb..bd6b3d046 100644 --- a/src/translations/ar.po +++ b/src/translations/ar.po @@ -1056,6 +1056,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/bg.po b/src/translations/bg.po index 9f46f8be8..d990ff55c 100644 --- a/src/translations/bg.po +++ b/src/translations/bg.po @@ -1057,6 +1057,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/ca.po b/src/translations/ca.po index 8eae5cc5d..d0293da33 100644 --- a/src/translations/ca.po +++ b/src/translations/ca.po @@ -1082,6 +1082,9 @@ msgstr "Contrasenya de Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Usuari de Last.fm" diff --git a/src/translations/cs.po b/src/translations/cs.po index 85f1d6175..0cc629275 100644 --- a/src/translations/cs.po +++ b/src/translations/cs.po @@ -1061,6 +1061,9 @@ msgstr "Heslo k Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Uživatelské jméno k Last.fm" diff --git a/src/translations/da.po b/src/translations/da.po index 0edc15bc6..7888d3b18 100644 --- a/src/translations/da.po +++ b/src/translations/da.po @@ -1062,6 +1062,9 @@ msgstr "Last.fm-adgangskode" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm-brugernavn" diff --git a/src/translations/de.po b/src/translations/de.po index 1ba10530c..93f704b85 100644 --- a/src/translations/de.po +++ b/src/translations/de.po @@ -1083,6 +1083,9 @@ msgstr "Last.fm Passwort" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm Benutzername" diff --git a/src/translations/el.po b/src/translations/el.po index 44a190a0d..49b800eb5 100644 --- a/src/translations/el.po +++ b/src/translations/el.po @@ -1088,6 +1088,9 @@ msgstr "Last.fm συνθηματικό" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm όνομα χρήστη" diff --git a/src/translations/en_CA.po b/src/translations/en_CA.po index f041321d5..514ad04d1 100644 --- a/src/translations/en_CA.po +++ b/src/translations/en_CA.po @@ -1060,6 +1060,9 @@ msgstr "Last.fm password" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm username" diff --git a/src/translations/en_GB.po b/src/translations/en_GB.po index 457634cec..b9a06331d 100644 --- a/src/translations/en_GB.po +++ b/src/translations/en_GB.po @@ -1058,6 +1058,9 @@ msgstr "Last.fm password" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm username" diff --git a/src/translations/es.po b/src/translations/es.po index c693a3788..63b8d5f01 100644 --- a/src/translations/es.po +++ b/src/translations/es.po @@ -1087,6 +1087,9 @@ msgstr "Contraseña" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Usuario" diff --git a/src/translations/fi.po b/src/translations/fi.po index e396f7eab..1f221d938 100644 --- a/src/translations/fi.po +++ b/src/translations/fi.po @@ -1058,6 +1058,9 @@ msgstr "Last.fm-salasana" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm-tunnus" diff --git a/src/translations/fr.po b/src/translations/fr.po index 2f5c3ef68..85044c0e3 100644 --- a/src/translations/fr.po +++ b/src/translations/fr.po @@ -1091,6 +1091,9 @@ msgstr "Mot de passe" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Nom d'utilisateur" diff --git a/src/translations/gl.po b/src/translations/gl.po index ce044640f..01d3ca0c1 100644 --- a/src/translations/gl.po +++ b/src/translations/gl.po @@ -1063,6 +1063,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/hu.po b/src/translations/hu.po index 922fff15c..9d7ec9d2e 100644 --- a/src/translations/hu.po +++ b/src/translations/hu.po @@ -1081,6 +1081,9 @@ msgstr "Last.fm jelszó" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm felhasználói név" diff --git a/src/translations/it.po b/src/translations/it.po index b785c54f6..30fbe531d 100644 --- a/src/translations/it.po +++ b/src/translations/it.po @@ -1090,6 +1090,9 @@ msgstr "Password Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Nome utente Last.fm" diff --git a/src/translations/kk.po b/src/translations/kk.po index 671b7ffce..99e409ce8 100644 --- a/src/translations/kk.po +++ b/src/translations/kk.po @@ -1058,6 +1058,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/lt.po b/src/translations/lt.po index a128bf1e0..8b84b7be4 100644 --- a/src/translations/lt.po +++ b/src/translations/lt.po @@ -1057,6 +1057,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/nb.po b/src/translations/nb.po index edce14edc..c9322c57d 100644 --- a/src/translations/nb.po +++ b/src/translations/nb.po @@ -1060,6 +1060,9 @@ msgstr "Last.fm passord" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm brukernavn" diff --git a/src/translations/nl.po b/src/translations/nl.po index b993f029d..c5ff2e47f 100644 --- a/src/translations/nl.po +++ b/src/translations/nl.po @@ -1085,6 +1085,9 @@ msgstr "Last.fm wachtwoord" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm gebruikersnaam" diff --git a/src/translations/oc.po b/src/translations/oc.po index fc08641b9..e671c649a 100644 --- a/src/translations/oc.po +++ b/src/translations/oc.po @@ -1056,6 +1056,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/pl.po b/src/translations/pl.po index 00492db1f..e6a374d3e 100644 --- a/src/translations/pl.po +++ b/src/translations/pl.po @@ -1082,6 +1082,9 @@ msgstr "Hasło Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Użytkownik Last.fm" diff --git a/src/translations/pt.po b/src/translations/pt.po index 47e8ea7aa..c56a22864 100644 --- a/src/translations/pt.po +++ b/src/translations/pt.po @@ -1082,6 +1082,9 @@ msgstr "Senha Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Nome de utilizador Last.fm" diff --git a/src/translations/pt_BR.po b/src/translations/pt_BR.po index b59e205d6..03a3959a7 100644 --- a/src/translations/pt_BR.po +++ b/src/translations/pt_BR.po @@ -1073,6 +1073,9 @@ msgstr "Senha do Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Nome de usuário Last.fm" diff --git a/src/translations/ro.po b/src/translations/ro.po index 307804cc0..8a0ad65f1 100644 --- a/src/translations/ro.po +++ b/src/translations/ro.po @@ -1057,6 +1057,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/ru.po b/src/translations/ru.po index 038f8c4de..a5cedf75c 100644 --- a/src/translations/ru.po +++ b/src/translations/ru.po @@ -1076,6 +1076,9 @@ msgstr "Пароль Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Логин Last.fm" diff --git a/src/translations/sk.po b/src/translations/sk.po index 45479e608..282661214 100644 --- a/src/translations/sk.po +++ b/src/translations/sk.po @@ -1079,6 +1079,9 @@ msgstr "Last.fm heslo" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm použ. meno" diff --git a/src/translations/sl.po b/src/translations/sl.po index 537dc5fd1..337ec09aa 100644 --- a/src/translations/sl.po +++ b/src/translations/sl.po @@ -1078,6 +1078,9 @@ msgstr "Geslo Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Uporabniško ime Last.fm" diff --git a/src/translations/sr.po b/src/translations/sr.po index 6f500729c..f7542b16e 100644 --- a/src/translations/sr.po +++ b/src/translations/sr.po @@ -1062,6 +1062,9 @@ msgstr "ЛастФМ лозинка" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "ЛастФМ корисничко име" diff --git a/src/translations/sv.po b/src/translations/sv.po index 54bd9c320..afe09cc9b 100644 --- a/src/translations/sv.po +++ b/src/translations/sv.po @@ -1066,6 +1066,9 @@ msgstr "Last.fm lösenord" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm användarnamn" diff --git a/src/translations/tr.po b/src/translations/tr.po index f71f869aa..789854acf 100644 --- a/src/translations/tr.po +++ b/src/translations/tr.po @@ -1079,6 +1079,9 @@ msgstr "Last.fm parolası" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Last.fm kullanıcı adı" diff --git a/src/translations/translations.pot b/src/translations/translations.pot index d96993319..be027ff03 100644 --- a/src/translations/translations.pot +++ b/src/translations/translations.pot @@ -1047,6 +1047,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/uk.po b/src/translations/uk.po index f2e180ab0..6e15ba36e 100644 --- a/src/translations/uk.po +++ b/src/translations/uk.po @@ -1078,6 +1078,9 @@ msgstr "Пароль Last.fm" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "Користувач Last.fm" diff --git a/src/translations/zh_CN.po b/src/translations/zh_CN.po index a327ba1bd..06b5403dd 100644 --- a/src/translations/zh_CN.po +++ b/src/translations/zh_CN.po @@ -1056,6 +1056,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/translations/zh_TW.po b/src/translations/zh_TW.po index 8f419da8a..f0aa771da 100644 --- a/src/translations/zh_TW.po +++ b/src/translations/zh_TW.po @@ -1062,6 +1062,9 @@ msgstr "" msgid "Last.fm play counts" msgstr "" +msgid "Last.fm tags" +msgstr "" + msgid "Last.fm username" msgstr "" diff --git a/src/ui/flowlayout.cpp b/src/ui/flowlayout.cpp new file mode 100644 index 000000000..dc4bb0cc9 --- /dev/null +++ b/src/ui/flowlayout.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "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 Nokia Corporation and its Subsidiary(-ies) 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 THE COPYRIGHT +** OWNER OR CONTRIBUTORS 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." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "flowlayout.h" +//! [1] +FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} +//! [1] + +//! [2] +FlowLayout::~FlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + delete item; +} +//! [2] + +//! [3] +void FlowLayout::addItem(QLayoutItem *item) +{ + itemList.append(item); +} +//! [3] + +//! [4] +int FlowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { + return m_hSpace; + } else { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int FlowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { + return m_vSpace; + } else { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} +//! [4] + +//! [5] +int FlowLayout::count() const +{ + return itemList.size(); +} + +QLayoutItem *FlowLayout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem *FlowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + else + return 0; +} +//! [5] + +//! [6] +Qt::Orientations FlowLayout::expandingDirections() const +{ + return 0; +} +//! [6] + +//! [7] +bool FlowLayout::hasHeightForWidth() const +{ + return true; +} + +int FlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} +//! [7] + +//! [8] +void FlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize FlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize FlowLayout::minimumSize() const +{ + QSize size; + QLayoutItem *item; + foreach (item, itemList) + size = size.expandedTo(item->minimumSize()); + + size += QSize(2*margin(), 2*margin()); + return size; +} +//! [8] + +//! [9] +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; +//! [9] + +//! [10] + QLayoutItem *item; + foreach (item, itemList) { + QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); +//! [10] +//! [11] + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} +//! [11] +//! [12] +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, 0, pw); + } else { + return static_cast(parent)->spacing(); + } +} +//! [12] diff --git a/src/ui/flowlayout.h b/src/ui/flowlayout.h new file mode 100644 index 000000000..b7a5848b2 --- /dev/null +++ b/src/ui/flowlayout.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "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 Nokia Corporation and its Subsidiary(-ies) 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 THE COPYRIGHT +** OWNER OR CONTRIBUTORS 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." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include +#include +#include +//! [0] +class FlowLayout : public QLayout +{ +public: + FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayout(); + + void addItem(QLayoutItem *item); + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const; + bool hasHeightForWidth() const; + int heightForWidth(int) const; + int count() const; + QLayoutItem *itemAt(int index) const; + QSize minimumSize() const; + void setGeometry(const QRect &rect); + QSize sizeHint() const; + QLayoutItem *takeAt(int index); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; +//! [0] + +#endif