diff --git a/CMakeLists.txt b/CMakeLists.txt index 123cd567a..0ad4e599c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,8 +28,8 @@ set(DEBIAN_PACKAGE_DESCRIPTION "Qt/KDE Client for MPD") set(DEBIAN_PACKAGE_SECTION "kde4") set(CPACK_SOURCE_GENERATOR "TBZ2") set(CPACK_PACKAGE_VERSION_MAJOR "1") -set(CPACK_PACKAGE_VERSION_MINOR "5") -set(CPACK_PACKAGE_VERSION_PATCH "51") +set(CPACK_PACKAGE_VERSION_MINOR "9") +set(CPACK_PACKAGE_VERSION_PATCH "50") set(CPACK_PACKAGE_VERSION_SPIN "") # Use ".$number" - e.g. ".1" set(CPACK_PACKAGE_CONTACT "Craig Drummond ") set(CANTATA_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}") @@ -272,13 +272,15 @@ if (ENABLE_QT5) find_package(Qt5Network REQUIRED) find_package(Qt5Concurrent REQUIRED) find_package(Qt5Svg REQUIRED) + find_package(Qt5Sql REQUIRED) + set(QTCORELIBS ${Qt5Core_LIBRARIES}) set(QTNETWORKLIBS ${Qt5Network_LIBRARIES}) set(QTGUILIBS ${Qt5Gui_LIBRARIES}) - set(QTLIBS ${QTCORELIBS} ${Qt5Widgets_LIBRARIES} ${QTNETWORKLIBS} ${QTGUILIBS} ${Qt5Xml_LIBRARIES} ${Qt5Concurrent_LIBRARIES} ${Qt5Svg_LIBRARIES}) + set(QTLIBS ${QTCORELIBS} ${Qt5Widgets_LIBRARIES} ${QTNETWORKLIBS} ${QTGUILIBS} ${Qt5Xml_LIBRARIES} ${Qt5Concurrent_LIBRARIES} ${Qt5Svg_LIBRARIES} ${Qt5Sql_LIBRARIES}) set(QTINCLUDES ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} - ${Qt5Svg_INCLUDE_DIRS}) - add_definitions(${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS} ${Qt5Xml_DEFINITIONS} ${Qt5Concurrent_DEFINITIONS} ${Qt5Svg_DEFINITIONS}) + ${Qt5Svg_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS}) + add_definitions(${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS} ${Qt5Xml_DEFINITIONS} ${Qt5Concurrent_DEFINITIONS} ${Qt5Svg_DEFINITIONS} ${Qt5Sql_DEFINITIONS}) set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") if (NOT ENABLE_UBUNTU AND NOT APPLE AND NOT WIN32) # Does Ubuntu/Touch version need DBus? @@ -300,11 +302,11 @@ if (ENABLE_QT5) exec_program(${QT_QMAKE_EXECUTABLE} ARGS -query QT_INSTALL_TRANSLATIONS OUTPUT_VARIABLE QT_TRANSLATIONS_DIR) endif (APPLE OR WIN32) else (ENABLE_QT5) - find_package(Qt4 REQUIRED QtCore QtGui QtXml QtNetwork QtSvg) + find_package(Qt4 REQUIRED QtCore QtGui QtXml QtNetwork QtSvg QtSql) set(QTCORELIBS ${QT_QTCORE_LIBRARY}) set(QTNETWORKLIBS ${QT_QTNETWORK_LIBRARY}) set(QTGUILIBS ${QT_QTGUI_LIBRARY}) - set(QTLIBS ${QT_QTXML_LIBRARY} ${QTCORELIBS} ${QTGUILIBS} ${QTNETWORKLIBS} ${QT_QTSVG_LIBRARY}) + set(QTLIBS ${QT_QTXML_LIBRARY} ${QTCORELIBS} ${QTGUILIBS} ${QTNETWORKLIBS} ${QT_QTSVG_LIBRARY} ${QT_QTSQL_LIBRARY}) if (QT_QTDBUS_FOUND) set(QTLIBS ${QTLIBS} ${QT_QTDBUS_LIBRARY}) endif (QT_QTDBUS_FOUND) @@ -404,35 +406,37 @@ include_directories(${CMAKE_SOURCE_DIR}/3rdparty ${CMAKE_SOURCE_DIR} ${CMAKE_BIN set(CANTATA_CORE_SRCS ${CANTATA_CORE_SRCS} gui/covers.cpp gui/currentcover.cpp - models/musiclibraryitemroot.cpp models/musiclibraryitemartist.cpp models/musiclibraryitemalbum.cpp models/musiclibrarymodel.cpp + models/musiclibraryitemroot.cpp models/musiclibraryitemartist.cpp models/musiclibraryitemalbum.cpp models/musiclibraryproxymodel.cpp models/playlistsmodel.cpp models/playlistsproxymodel.cpp models/playqueuemodel.cpp models/playqueueproxymodel.cpp models/dirviewmodel.cpp models/dirviewproxymodel.cpp models/dirviewitem.cpp models/dirviewitemdir.cpp - models/albumsmodel.cpp models/albumsproxymodel.cpp models/proxymodel.cpp models/actionmodel.cpp models/musiclibraryitem.cpp + models/proxymodel.cpp models/actionmodel.cpp models/musiclibraryitem.cpp models/musicmodel.cpp models/multimusicmodel.cpp models/searchmodel.cpp models/streamsmodel.cpp models/searchproxymodel.cpp - models/musiclibraryitemsong.cpp + models/musiclibraryitemsong.cpp models/sqllibrarymodel.cpp models/mpdlibrarymodel.cpp mpd-interface/mpdconnection.cpp mpd-interface/mpdparseutils.cpp mpd-interface/mpdstats.cpp mpd-interface/mpdstatus.cpp mpd-interface/song.cpp mpd-interface/cuefile.cpp + db/librarydb.cpp db/mpdlibrarydb.cpp network/networkaccessmanager.cpp network/networkproxyfactory.cpp streams/streamfetcher.cpp http/httpserver.cpp) set(CANTATA_CORE_MOC_HDRS ${CANTATA_CORE_MOC_HDRS} gui/covers.h gui/currentcover.h - models/musiclibrarymodel.h models/musiclibraryproxymodel.h models/playlistsmodel.h models/playlistsproxymodel.h models/playqueuemodel.h - models/playqueueproxymodel.h models/dirviewmodel.h models/dirviewproxymodel.h models/albumsmodel.h models/actionmodel.h - models/multimusicmodel.h models/searchmodel.h + models/musiclibraryproxymodel.h models/playlistsmodel.h models/playlistsproxymodel.h models/playqueuemodel.h + models/playqueueproxymodel.h models/dirviewmodel.h models/dirviewproxymodel.h models/actionmodel.h + models/multimusicmodel.h models/searchmodel.h models/sqllibrarymodel.h models/mpdlibrarymodel.h mpd-interface/mpdconnection.h mpd-interface/mpdstats.h mpd-interface/mpdstatus.h + db/librarydb.h db/mpdlibrarydb.h network/networkaccessmanager.h streams/streamfetcher.h widgets/notelabel.h) set(CANTATA_SRCS ${CANTATA_CORE_SRCS} ${CANTATA_SRCS} gui/settings.cpp gui/application.cpp gui/initialsettingswizard.cpp gui/mainwindow.cpp gui/preferencesdialog.cpp - gui/filesettings.cpp gui/interfacesettings.cpp gui/playbacksettings.cpp gui/serversettings.cpp gui/librarypage.cpp gui/albumspage.cpp + gui/filesettings.cpp gui/interfacesettings.cpp gui/playbacksettings.cpp gui/serversettings.cpp gui/librarypage.cpp gui/folderpage.cpp gui/playlistspage.cpp gui/trayitem.cpp gui/cachesettings.cpp gui/coverdialog.cpp gui/searchpage.cpp gui/stdactions.cpp devices/deviceoptions.cpp widgets/treeview.cpp widgets/listview.cpp widgets/itemview.cpp widgets/autohidingsplitter.cpp widgets/nowplayingwidget.cpp widgets/actionlabel.cpp widgets/playqueueview.cpp widgets/groupedview.cpp widgets/actionitemdelegate.cpp widgets/textbrowser.cpp - widgets/volumeslider.cpp widgets/genrecombo.cpp widgets/menubutton.cpp widgets/icons.cpp widgets/toolbutton.cpp widgets/wizardpage.cpp + widgets/volumeslider.cpp widgets/menubutton.cpp widgets/icons.cpp widgets/toolbutton.cpp widgets/wizardpage.cpp widgets/statuslabel.cpp widgets/searchwidget.cpp widgets/messageoverlay.cpp widgets/basicitemdelegate.cpp widgets/sizegrip.cpp widgets/sizewidget.cpp widgets/servicestatuslabel.cpp widgets/spacerwidget.cpp widgets/songdialog.cpp widgets/stretchheaderview.cpp widgets/tableview.cpp widgets/thinsplitterhandle.cpp widgets/coverwidget.cpp widgets/ratingwidget.cpp widgets/notelabel.cpp @@ -440,24 +444,23 @@ set(CANTATA_SRCS ${CANTATA_CORE_SRCS} ${CANTATA_SRCS} context/lyricsettings.cpp context/ultimatelyricsprovider.cpp context/ultimatelyrics.cpp context/lyricsdialog.cpp context/contextwidget.cpp context/view.cpp context/artistview.cpp context/albumview.cpp context/songview.cpp context/contextengine.cpp context/wikipediaengine.cpp context/wikipediasettings.cpp context/othersettings.cpp context/contextsettings.cpp context/togglelist.cpp - context/lastfmengine.cpp context/metaengine.cpp context/backdropcreator.cpp + context/lastfmengine.cpp context/metaengine.cpp scrobbling/scrobbler.cpp scrobbling/pausabletimer.cpp scrobbling/scrobblingsettings.cpp scrobbling/scrobblingstatus.cpp scrobbling/scrobblinglove.cpp) set(CANTATA_MOC_HDRS ${CANTATA_CORE_MOC_HDRS} ${CANTATA_MOC_HDRS} - gui/initialsettingswizard.h gui/mainwindow.h gui/folderpage.h gui/librarypage.h gui/albumspage.h gui/playlistspage.h + gui/initialsettingswizard.h gui/mainwindow.h gui/folderpage.h gui/librarypage.h gui/playlistspage.h gui/playbacksettings.h gui/serversettings.h gui/preferencesdialog.h gui/interfacesettings.h gui/cachesettings.h gui/trayitem.h gui/coverdialog.h gui/searchpage.h widgets/treeview.h widgets/listview.h widgets/itemview.h widgets/autohidingsplitter.h widgets/nowplayingwidget.h widgets/actionlabel.h - widgets/playqueueview.h widgets/groupedview.h widgets/actionitemdelegate.h widgets/volumeslider.h widgets/genrecombo.h + widgets/playqueueview.h widgets/groupedview.h widgets/actionitemdelegate.h widgets/volumeslider.h widgets/searchwidget.h widgets/messageoverlay.h widgets/servicestatuslabel.h widgets/stretchheaderview.h widgets/tableview.h widgets/coverwidget.h widgets/ratingwidget.h widgets/selectorlabel.h context/togglelist.h context/ultimatelyrics.h context/ultimatelyricsprovider.h context/lyricsdialog.h context/contextwidget.h context/artistview.h context/albumview.h context/songview.h context/view.h context/contextengine.h context/wikipediaengine.h context/wikipediasettings.h context/othersettings.h context/lastfmengine.h context/metaengine.h - context/backdropcreator.h scrobbling/scrobbler.h scrobbling/scrobblingsettings.h scrobbling/scrobblingstatus.h scrobbling/scrobblinglove.h) set(CANTATA_UIS ${CANTATA_UIS} - gui/initialsettingswizard.ui gui/mainwindow.ui gui/folderpage.ui gui/librarypage.ui gui/albumspage.ui gui/playlistspage.ui + gui/initialsettingswizard.ui gui/mainwindow.ui gui/folderpage.ui gui/librarypage.ui gui/playlistspage.ui gui/filesettings.ui gui/interfacesettings.ui gui/playbacksettings.ui gui/serversettings.ui gui/coverdialog.ui gui/searchpage.ui context/togglelist.ui context/othersettings.ui widgets/itemview.ui scrobbling/scrobblingsettings.ui) @@ -662,15 +665,14 @@ if (TAGLIB_FOUND) devices/device.cpp devices/fsdevice.cpp devices/umsdevice.cpp devices/splitlabelwidget.cpp models/devicesmodel.cpp devices/actiondialog.cpp devices/devicepropertieswidget.cpp devices/devicepropertiesdialog.cpp devices/encoders.cpp devices/freespaceinfo.cpp - devices/transcodingjob.cpp devices/valueslider.cpp devices/syncdialog.cpp devices/synccollectionwidget.cpp + devices/transcodingjob.cpp devices/valueslider.cpp online/onlinedevice.cpp) set(CANTATA_MOC_HDRS ${CANTATA_MOC_HDRS} devices/devicespage.h devices/filejob.h devices/fsdevice.h devices/umsdevice.h models/devicesmodel.h devices/actiondialog.h devices/devicepropertieswidget.h devices/devicepropertiesdialog.h - devices/transcodingjob.h devices/valueslider.h devices/syncdialog.h devices/synccollectionwidget.h + devices/transcodingjob.h devices/valueslider.h online/onlinedevice.h) - set(CANTATA_UIS ${CANTATA_UIS} devices/devicespage.ui devices/actiondialog.ui devices/devicepropertieswidget.ui - devices/synccollectionwidget.ui) + set(CANTATA_UIS ${CANTATA_UIS} devices/devicespage.ui devices/actiondialog.ui devices/devicepropertieswidget.ui) if (ENABLE_REMOTE_DEVICES) set(CANTATA_SRCS ${CANTATA_SRCS} devices/remotefsdevice.cpp devices/remotedevicepropertiesdialog.cpp diff --git a/ChangeLog b/ChangeLog index 556b7e09b..704cf63cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ -1.6.0 +2.0.0 ----- + 1. Use SQLite database to cache MPD libtrary. + 2. Combine Artists and Albums tabs into a single Library tab. Provide option + to group library by; Genre, Artist, or Album + +..... + 1. If connected to MPD>=0.19 using address 127.0.0.1 or localhost, then pass local files as 'file://' URLS. 2. Ignore "The" when sorting albums. @@ -32,8 +38,6 @@ album-artist, to group albums. 20. Fix updating of composer tag. 21. Fix build with proxy config and Qt5 -22. Use treble clef icon in artists list if composer tag is being used for an - artist. 23. For Linux builds, if system tray icon is null (becasue QIcon cannot find it) then add icon files manually. 24. Hide cover, HTTP stream, and top-level folder options under a 'Show All diff --git a/README b/README index d7b730535..2d02b5ffe 100644 --- a/README +++ b/README @@ -799,7 +799,7 @@ The Cantata source folder contains the following structure: dbus - DBUS related classes and XML files devices - Device related classes dynamic - Dynamic playlists - gui - General GUI classes, e.g. MainWindow, AlbumsPage, etc. + gui - General GUI classes, e.g. MainWindow, LibraryPage, etc. http - HTTP server icons - Icons models - Most models, apart from dynamic playlist and shortcut models @@ -854,7 +854,8 @@ The following debug values may be used: External tags 32768 0x00008000 Scrobbling 65536 0x00010000 Devices 131072 0x00020000 - Other 262144 0x00040000 (e.g. MediaKeys) + SQL 262144 0x00040000 + Other 524288 0x00080000 (e.g. MediaKeys) These values may be combined to enable multiple output. e.g. to enable MPD and covers logging: diff --git a/cantata.qrc b/cantata.qrc index 03cc602ad..93b08cc7e 100644 --- a/cantata.qrc +++ b/cantata.qrc @@ -12,8 +12,7 @@ icons/view-media-shuffle24.png icons/view-media-shuffle32.png -icons/sidebar-albums-dark.svg -icons/sidebar-artists-dark.svg +icons/sidebar-library-dark.svg icons/sidebar-devices-dark.svg icons/sidebar-dynamic-dark.svg icons/sidebar-folders-dark.svg @@ -23,8 +22,7 @@ icons/sidebar-playqueue-dark.svg icons/sidebar-streams-dark.svg icons/sidebar-search-dark.svg -icons/sidebar-albums-light.svg -icons/sidebar-artists-light.svg +icons/sidebar-library-light.svg icons/sidebar-devices-light.svg icons/sidebar-dynamic-light.svg icons/sidebar-folders-light.svg diff --git a/context/albumview.cpp b/context/albumview.cpp index 7efa739d3..3543a8cd5 100644 --- a/context/albumview.cpp +++ b/context/albumview.cpp @@ -28,10 +28,11 @@ #include "network/networkaccessmanager.h" #include "support/utils.h" #include "qtiocompressor/qtiocompressor.h" -#include "models/musiclibrarymodel.h" #include "contextengine.h" #include "widgets/textbrowser.h" #include "support/actioncollection.h" +#include "support/action.h" +#include "models/mpdlibrarymodel.h" #include #include #include @@ -61,7 +62,7 @@ AlbumView::AlbumView(QWidget *p) { engine=ContextEngine::create(this); refreshAction = ActionCollection::get()->createAction("refreshalbum", i18n("Refresh Album Information"), "view-refresh"); - connect(refreshAction, SIGNAL(triggered()), SLOT(refresh())); + connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh())); connect(engine, SIGNAL(searchResult(QString,QString)), this, SLOT(searchResponse(QString,QString))); connect(Covers::self(), SIGNAL(cover(Song,QImage,QString)), SLOT(coverRetrieved(Song,QImage,QString))); connect(Covers::self(), SIGNAL(coverUpdated(Song,QImage,QString)), SLOT(coverUpdated(Song,QImage,QString))); @@ -168,7 +169,7 @@ void AlbumView::playSong(const QUrl &url) void AlbumView::getTrackListing() { if (songs.isEmpty()) { - songs=MusicLibraryModel::self()->getAlbumTracks(currentSong); + songs=MpdLibraryModel::self()->getAlbumTracks(currentSong); } if (!songs.isEmpty()) { diff --git a/context/artistview.cpp b/context/artistview.cpp index 50894231c..8c09bb407 100644 --- a/context/artistview.cpp +++ b/context/artistview.cpp @@ -25,13 +25,13 @@ #include "support/localize.h" #include "gui/covers.h" #include "support/utils.h" -#include "models/musiclibrarymodel.h" #include "network/networkaccessmanager.h" #include "qtiocompressor/qtiocompressor.h" #include "widgets/textbrowser.h" #include "contextengine.h" #include "support/actioncollection.h" -#include "models/musiclibrarymodel.h" +#include "support/action.h" +#include "models/mpdlibrarymodel.h" #include #include #include @@ -58,7 +58,24 @@ static QString cacheFileName(const QString &artist, const QString &lang, bool si Covers::encodeName(artist)+(similar ? "-similar" : ("."+lang))+(similar ? ArtistView::constSimilarInfoExt : ArtistView::constInfoExt); } -static QString buildUrl(const QString &artist, const QString &album=QString()) +static QString buildUrl(const LibraryDb::Album &al) +{ + QUrl url("cantata:///"); + #if QT_VERSION < 0x050000 + QUrl &query=url; + #else + QUrlQuery query; + #endif + + query.addQueryItem("artist", al.artist); + query.addQueryItem("albumId", al.id); + #if QT_VERSION >= 0x050000 + url.setQuery(query); + #endif + return url.toString(); +} + +static QString buildUrl(const QString &artist) { QUrl url("cantata:///"); #if QT_VERSION < 0x050000 @@ -68,9 +85,6 @@ static QString buildUrl(const QString &artist, const QString &album=QString()) #endif query.addQueryItem("artist", artist); - if (!album.isEmpty()) { - query.addQueryItem("album", album); - } #if QT_VERSION >= 0x050000 url.setQuery(query); #endif @@ -98,7 +112,7 @@ ArtistView::ArtistView(QWidget *parent) { engine=ContextEngine::create(this); refreshAction = ActionCollection::get()->createAction("refreshartist", i18n("Refresh Artist Information"), "view-refresh"); - connect(refreshAction, SIGNAL(triggered()), SLOT(refresh())); + connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh())); connect(engine, SIGNAL(searchResult(QString,QString)), this, SLOT(searchResponse(QString,QString))); connect(Covers::self(), SIGNAL(artistImage(Song,QImage,QString)), SLOT(artistImage(Song,QImage,QString))); connect(Covers::self(), SIGNAL(coverUpdated(Song,QImage,QString)), SLOT(artistImageUpdated(Song,QImage,QString))); @@ -159,7 +173,7 @@ void ArtistView::update(const Song &s, bool force) bool artistChanged=song.artist!=currentSong.artist; if (artistChanged) { - artistAlbumsFirstTracks.clear(); + artistAlbums.clear(); abort(); } if (!isVisible()) { @@ -195,12 +209,12 @@ void ArtistView::update(const Song &s, bool force) } } -const QList & ArtistView::getArtistAlbumsFirstTracks() +const QList & ArtistView::getArtistAlbums() { - if (artistAlbumsFirstTracks.isEmpty() && !currentSong.isEmpty()) { - artistAlbumsFirstTracks=MusicLibraryModel::self()->getArtistAlbumsFirstTracks(currentSong); + if (artistAlbums.isEmpty() && !currentSong.isEmpty()) { + artistAlbums=MpdLibraryModel::self()->getArtistAlbums(currentSong.artist); } - return artistAlbumsFirstTracks; + return artistAlbums; } void ArtistView::artistImage(const Song &song, const QImage &i, const QString &f) @@ -307,33 +321,10 @@ void ArtistView::setBio() } if (albums.isEmpty()) { - getArtistAlbumsFirstTracks(); - QMap a; + getArtistAlbums(); - foreach (const Song &s, artistAlbumsFirstTracks) { - a[s.albumArtist()].append(s.album); - } - - QMap::ConstIterator it=a.begin(); - QMap::ConstIterator c=a.find(currentSong.artist); - QMap::ConstIterator end=a.end(); - - if (c!=end) { - QStringList artistAlbums=c.value(); - qSort(artistAlbums); - foreach (const QString &album, artistAlbums) { - albums+=QLatin1String("
  • "+album+"
  • "; - } - } - for (; it!=end; ++it) { - if (it==c) { - continue; - } - QStringList artistAlbums=it.value(); - qSort(artistAlbums); - foreach (const QString &album, artistAlbums) { - albums+=QLatin1String("
  • "+it.key()+" - "+album+"
  • "; - } + foreach (const LibraryDb::Album &al, artistAlbums) { + albums+=QLatin1String("
  • "+al.name+"
  • "; } } @@ -446,7 +437,7 @@ QStringList ArtistView::parseSimilarResponse(const QByteArray &resp) void ArtistView::buildSimilar(const QStringList &artists) { - QSet mpdArtists=MusicLibraryModel::self()->getAlbumArtists(); + QSet mpdArtists=MpdLibraryModel::self()->getArtists(); QSet mpdLowerArtists; bool first=true; foreach (QString artist, artists) { @@ -491,8 +482,8 @@ void ArtistView::show(const QUrl &url) #endif if (q.hasQueryItem("artist")) { - if (q.hasQueryItem("album")) { - emit findAlbum(q.queryItemValue("artist"), q.queryItemValue("album")); + if (q.hasQueryItem("albumId")) { + emit findAlbum(q.queryItemValue("artist"), q.queryItemValue("albumId")); } else { emit findArtist(q.queryItemValue("artist")); } diff --git a/context/artistview.h b/context/artistview.h index f6b7fc01a..bc5cf9260 100644 --- a/context/artistview.h +++ b/context/artistview.h @@ -26,6 +26,7 @@ #include "view.h" #include "mpd-interface/song.h" +#include "db/librarydb.h" #include class ComboBox; @@ -51,7 +52,7 @@ public: virtual ~ArtistView() { abort(); } void update(const Song &s, bool force=false); - const QList & getArtistAlbumsFirstTracks(); + const QList &getArtistAlbums(); Q_SIGNALS: void findArtist(const QString &artist); @@ -88,7 +89,7 @@ private: QString provider; QString webLinks; QString albums; - QList artistAlbumsFirstTracks; + QList artistAlbums; }; #endif diff --git a/context/backdropcreator.cpp b/context/backdropcreator.cpp deleted file mode 100644 index 6c17bf38a..000000000 --- a/context/backdropcreator.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "backdropcreator.h" -#include "gui/covers.h" -#include "support/thread.h" -#include "support/utils.h" -#include -#include - -#include -static bool debugEnabled=false; -#define DBUG if (debugEnabled) qWarning() << metaObject()->className() << __FUNCTION__ -void BackdropCreator::enableDebug() -{ - debugEnabled=true; -} - -BackdropCreator::BackdropCreator() - : QObject(0) -{ - connect(Covers::self(), SIGNAL(cover(const Song &, const QImage &, const QString &)), SLOT(coverRetrieved(const Song &, const QImage &, const QString &))); - imageSize=QApplication::fontMetrics().height()*12; - thread=new Thread(metaObject()->className()); - moveToThread(thread); - thread->start(); -} - -BackdropCreator::~BackdropCreator() -{ - thread->stop(); -} - -void BackdropCreator::create(const QString &artist, const QList &songs) -{ - DBUG << artist << songs.count(); - requestedArtist=artist; - images.clear(); - requested.clear(); - foreach (const Song &s, songs) { - Covers::Image img=Covers::self()->requestImage(s, true); - if (!img.img.isNull()) { - images.append(img.img.scaled(imageSize, imageSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } else { - requested.insert(s); - } - } - - if (requested.isEmpty()) { - createImage(); - } -} - -void BackdropCreator::coverRetrieved(const Song &s, const QImage &img, const QString &file) -{ - DBUG << requestedArtist << s.albumArtist(); - Q_UNUSED(file) - if (requested.contains(s)) { - requested.remove(s); - if (!img.isNull()) { - images.append(img.scaled(imageSize, imageSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } - if (requested.isEmpty()) { - createImage(); - } - } -} - -void BackdropCreator::createImage() -{ - DBUG << images.count(); - QImage backdrop; - - switch (images.count()) { - case 0: - break; - case 1: - backdrop=images.at(0); - break; - case 2: { - backdrop=QImage(2*imageSize, 2*imageSize, QImage::Format_RGB32); - QPainter p(&backdrop); - p.drawImage(0, 0, images.at(0)); - p.drawImage(0, imageSize, images.at(1)); - p.drawImage(imageSize, imageSize, images.at(0)); - p.drawImage(imageSize, 0, images.at(1)); - break; - } - default: { - static const int constHCount=10; - static const int constVCount=5; - backdrop=QImage(constHCount*imageSize, constVCount*imageSize, QImage::Format_RGB32); - QPainter p(&backdrop); - QList toUse=images; - QList currentLine; - QList lastLine; - - for (int y=0; y - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef BACKDROP_CREATOR_H -#define BACKDROP_CREATOR_H - -#include -#include -#include "mpd-interface/song.h" - -class Thread; -class QImage; - -class BackdropCreator : public QObject -{ - Q_OBJECT -public: - static void enableDebug(); - - BackdropCreator(); - virtual ~BackdropCreator(); - -Q_SIGNALS: - void created(const QString &artist, const QImage &img); - -public Q_SLOTS: - void create(const QString &artist, const QList &songs); - -private Q_SLOTS: - void coverRetrieved(const Song &s, const QImage &img, const QString &file); - -private: - void createImage(); - -private: - int imageSize; - QString requestedArtist; - QSet requested; - QList images; - QList albums; - Thread *thread; -}; - -#endif diff --git a/context/contextwidget.cpp b/context/contextwidget.cpp index 458a27113..1e99555da 100644 --- a/context/contextwidget.cpp +++ b/context/contextwidget.cpp @@ -35,7 +35,6 @@ #include "gui/settings.h" #include "wikipediaengine.h" #include "support/localize.h" -#include "backdropcreator.h" #include "support/gtkstyle.h" #include "widgets/playqueueview.h" #include "widgets/treeview.h" @@ -254,7 +253,6 @@ ContextWidget::ContextWidget(QWidget *parent) #endif , splitter(0) , viewSelector(0) - , creator(0) { QHBoxLayout *layout=new QHBoxLayout(this); mainStack=new QStackedWidget(this); @@ -438,7 +436,6 @@ void ContextWidget::readConfig() case PlayQueueView::BI_Custom: if (origType!=backdropType || backdropOpacity!=origOpacity || backdropBlur!=origBlur || origCustomBackdropFile!=customBackdropFile) { updateImage(QImage(customBackdropFile), false); - artistsCreatedBackdropsFor.clear(); } break; } @@ -565,9 +562,6 @@ void ContextWidget::updateImage(QImage img, bool created) oldIsAlbumCoverBackdrop=albumCoverBackdrop; currentBackdrop=QPixmap(); animator.stop(); - if (img.isNull()) { - backdropAlbums.clear(); - } if (img.isNull() && oldBackdrop.isNull()) { return; } @@ -693,7 +687,6 @@ void ContextWidget::updateBackdrop(bool force) return; } currentArtist=updateArtist; - backdropAlbums.clear(); if (currentArtist.isEmpty()) { updateImage(QImage()); QWidget::update(); @@ -774,9 +767,7 @@ static QString fixArtist(const QString &artist) void ContextWidget::getBackdrop() { cancel(); - if (artistsCreatedBackdropsFor.contains(currentArtist)) { - createBackdrop(); - } else if (useFanArt) { + if (useFanArt) { getFanArtBackdrop(); } else { getDiscoGsImage(); @@ -1007,9 +998,7 @@ void ContextWidget::discoGsResponse() } } - if (url.isEmpty()) { - createBackdrop(); - } else { + if (!url.isEmpty()) { job=NetworkAccessManager::self()->get(QUrl(url)); DBUG << url; connect(job, SIGNAL(finished()), this, SLOT(downloadResponse())); @@ -1033,9 +1022,7 @@ void ContextWidget::downloadResponse() img=QImage::fromData(data); } - if (img.isNull()) { - createBackdrop(); - } else { + if (!img.isNull()) { updateImage(img); bool saved=false; @@ -1077,27 +1064,6 @@ void ContextWidget::downloadResponse() } } -void ContextWidget::createBackdrop() -{ - DBUG << currentArtist; - if (!creator) { - creator = new BackdropCreator(); - connect(creator, SIGNAL(created(QString,QImage)), SLOT(backdropCreated(QString,QImage))); - connect(this, SIGNAL(createBackdrop(QString,QList)), creator, SLOT(create(QString,QList))); - } - QList artistAlbumsFirstTracks=artist->getArtistAlbumsFirstTracks(); - QSet albumNames; - - foreach (const Song &s, artistAlbumsFirstTracks) { - albumNames.insert(s.albumArtist()+" - "+s.album); - } - - if (backdropAlbums!=albumNames) { - backdropAlbums=albumNames; - emit createBackdrop(currentArtist, artistAlbumsFirstTracks); - } -} - void ContextWidget::resizeBackdrop() { #ifdef SCALE_CONTEXT_BGND @@ -1121,20 +1087,6 @@ void ContextWidget::resizeBackdrop() #endif } -void ContextWidget::backdropCreated(const QString &artist, const QImage &img) -{ - DBUG << artist << img.isNull() << currentArtist; - if (artist==currentArtist) { - artistsCreatedBackdropsFor.removeAll(artist); - artistsCreatedBackdropsFor.append(artist); - if (artistsCreatedBackdropsFor.count()>20) { - artistsCreatedBackdropsFor.removeFirst(); - } - updateImage(img, true); - QWidget::update(); - } -} - NetworkJob * ContextWidget::getReply(QObject *obj) { NetworkJob *reply = qobject_cast(obj); diff --git a/context/contextwidget.h b/context/contextwidget.h index 6c0a06297..ac99b4806 100644 --- a/context/contextwidget.h +++ b/context/contextwidget.h @@ -38,7 +38,6 @@ class ArtistView; class AlbumView; class SongView; -class BackdropCreator; class NetworkJob; class QStackedWidget; class QComboBox; @@ -120,14 +119,12 @@ Q_SIGNALS: void findArtist(const QString &artist); void findAlbum(const QString &artist, const QString &album); void playSong(const QString &file); - void createBackdrop(const QString &artist, const QList &songs); private Q_SLOTS: void musicbrainzResponse(); void fanArtResponse(); void discoGsResponse(); void downloadResponse(); - void backdropCreated(const QString &artist, const QImage &img); private: void setZoom(); @@ -179,13 +176,10 @@ private: #endif ThinSplitter *splitter; ViewSelector *viewSelector; - BackdropCreator *creator; - QSet backdropAlbums; #ifndef SCALE_CONTEXT_BGND QSize minBackdropSize; QSize maxBackdropSize; #endif - QList artistsCreatedBackdropsFor; }; #endif diff --git a/db/librarydb.cpp b/db/librarydb.cpp index f41c97dbd..7fec2997b 100644 --- a/db/librarydb.cpp +++ b/db/librarydb.cpp @@ -1,5 +1,5 @@ /* - * Cantata Web + * Cantata * * Copyright (c) 2015 Craig Drummond * @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include static const int constSchemaVersion=1; @@ -33,6 +35,7 @@ static const int constSchemaVersion=1; bool LibraryDb::dbgEnabled=false; #define DBUG if (dbgEnabled) qWarning() << metaObject()->className() << __FUNCTION__ +const QLatin1String LibraryDb::constFileExt(".sql"); const QLatin1String LibraryDb::constNullGenre("-"); const QLatin1String LibraryDb::constArtistAlbumsSortYear("year"); @@ -340,58 +343,134 @@ static QString albumSort(const Song &s) return QString(); } -LibraryDb::LibraryDb() - : currentVersion(0) +// Code taken from Clementine's LibraryQuery +class SqlQuery +{ +public: + SqlQuery(const QString &colSpec, QSqlDatabase database) + : db(database) + , fts(false) + , columSpec(colSpec) + { + } + + void addWhere(const QString &column, const QVariant &value, const QString &op="=") + { + // ignore 'literal' for IN + if (!op.compare("IN", Qt::CaseInsensitive)) { + QStringList final; + foreach(const QString &singleValue, value.toStringList()) { + final.append("?"); + boundValues << singleValue; + } + whereClauses << QString("%1 IN (" + final.join(",") + ")").arg(column); + } else { + // Do integers inline - sqlite seems to get confused when you pass integers + // to bound parameters + if (QVariant::Int==value.type()) { + whereClauses << QString("%1 %2 %3").arg(column, op, value.toString()); + } else { + whereClauses << QString("%1 %2 ?").arg(column, op); + boundValues << value; + } + } + } + + void setFilter(const QString &filter) + { + if (!filter.isEmpty()) { + whereClauses << "songs_fts match ?"; + boundValues << "\'"+filter+"\'"; + fts=true; + } + } + + bool exec() + { + QString sql=fts + ? QString("SELECT %1 FROM songs INNER JOIN songs_fts AS fts ON songs.ROWID = fts.ROWID").arg(columSpec) + : QString("SELECT %1 FROM songs").arg(columSpec); + + if (!whereClauses.isEmpty()) { + sql += " WHERE " + whereClauses.join(" AND "); + } + query=QSqlQuery(sql, db); + foreach (const QVariant &value, boundValues) { + query.addBindValue(value); + } + return query.exec(); + } + + bool next() { return query.next(); } + QString executedQuery() const { return query.executedQuery(); } + int size() const { return query.size(); } + QVariant value(int col) const { return query.value(col); } + const QSqlQuery & realQuery() const { return query; } + +private: + QSqlDatabase &db; + QSqlQuery query; + bool fts; + QString columSpec; + QStringList whereClauses; + QVariantList boundValues; +}; + +LibraryDb::LibraryDb(QObject *p, const QString &name) + : QObject(p) + , dbName(name) + , currentVersion(0) , newVersion(0) , db(0) , insertSongQuery(0) - , genresQuery(0) - , artistsQuery(0) - , artistsGenreQuery(0) - , albumsQuery(0) - , albumsGenreQuery(0) - , albumsNoArtistQuery(0) - , albumsGenreNoArtistQuery(0) - , albumsDetailsQuery(0) - , albumsDetailsGenreQuery(0) - , tracksQuery(0) - , tracksGenreQuery(0) - , tracksWildcardQuery(0) { DBUG; } LibraryDb::~LibraryDb() { -// delete insertSongsQuery; -// delete genresQuery; -// delete artistsQuery; -// delete artistsGenreQuery; -// delete albumsQuery; -// delete albumsGenreQuery; -// delete albumsNoArtistQuery; -// delete albumsGenreNoArtistQuery; -// delete albumsDetailsQuery; -// delete albumsDetailsGenreQuery; -// delete tracksQuery; -// delete tracksGenreQuery; -// delete tracksWildcardQuery; -// delete db; + reset(); } -bool LibraryDb::init(QString &dbName) +void LibraryDb::clear() { + if (db) { + QSqlQuery(*db).exec("delete from songs where version="+QString::number(currentVersion)); + currentVersion=0; + emit libraryUpdated(); + } +} + +void LibraryDb::erase() +{ + reset(); + if (!dbFileName.isEmpty() && QFile::exists(dbFileName)) { + QFile::remove(dbFileName); + } +} + +bool LibraryDb::init(const QString &dbFile) +{ + if (dbFile!=dbFileName) { + reset(); + dbFileName=dbFile; + } if (db) { return true; } + DBUG << dbFile << dbName; db=new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE")); - db->setDatabaseName(dbName); + db->setDatabaseName(dbFile); if (!db->open()) { + delete db; + db=0; return false; } - createTable("versions(collection integer, schema integer)"); + if (!createTable("versions(collection integer, schema integer)")) { + return false; + } QSqlQuery query("select collection, schema from versions", *db); int schemaVersion=0; if (query.next()) { @@ -407,25 +486,32 @@ bool LibraryDb::init(QString &dbName) } DBUG << "Current version" << currentVersion; - createTable("songs (" - "version integer," - "file string, " - "artist string, " - "artistId string, " - "albumArtist string, " - "artistSort string, " - "composer string, " - "album string, " - "albumId string, " - "albumSort string, " - "title string, " - "genre string, " - "track integer, " - "disc integer, " - "time integer, " - "year integer, " - "type integer, " - "primary key (version, file))"); + // NOTE: If this table is modified, update getSong()! + if (createTable("songs (" + "file string, " + "artist string, " + "artistId string, " + "albumArtist string, " + "artistSort string, " + "composer string, " + "album string, " + "albumId string, " + "albumSort string, " + "title string, " + "genre string, " + "track integer, " + "disc integer, " + "time integer, " + "year integer, " + "type integer, " + "primary key (file))")) { +#ifndef CANTATA_WEB + QSqlQuery(*db).exec("create virtual table songs_fts using fts4(fts_artist, fts_artistId, fts_album, fts_title, tokenize=unicode61)"); +#endif + } else { + return false; + } + emit libraryUpdated(); return true; } @@ -433,10 +519,9 @@ void LibraryDb::insertSong(const Song &s) { if (!insertSongQuery) { insertSongQuery=new QSqlQuery(*db); - insertSongQuery->prepare("insert into songs(version, file, artist, artistId, albumArtist, artistSort, composer, album, albumId, albumSort, title, genre, track, disc, time, year, type) " - "values(:version, :file, :artist, :artistId, :albumArtist, :artistSort, :composer, :album, :albumId, :albumSort, :title, :genre, :track, :disc, :time, :year, :type)"); + insertSongQuery->prepare("insert into songs(file, artist, artistId, albumArtist, artistSort, composer, album, albumId, albumSort, title, genre, track, disc, time, year, type) " + "values(:file, :artist, :artistId, :albumArtist, :artistSort, :composer, :album, :albumId, :albumSort, :title, :genre, :track, :disc, :time, :year, :type)"); } - insertSongQuery->bindValue(":version", (qulonglong)newVersion); insertSongQuery->bindValue(":file", s.file); insertSongQuery->bindValue(":artist", s.artist); insertSongQuery->bindValue(":artistId", s.artistOrComposer()); @@ -454,7 +539,7 @@ void LibraryDb::insertSong(const Song &s) insertSongQuery->bindValue(":year", s.year); insertSongQuery->bindValue(":type", s.type); if (!insertSongQuery->exec()) { - qWarning() << "insert failed" << insertSongQuery->lastError() << newVersion << s.file; + qWarning() << "insert failed" << insertSongQuery->lastError().text() << newVersion << s.file; } } @@ -463,14 +548,13 @@ QList LibraryDb::getGenres() DBUG; QMap map; if (0!=currentVersion) { - if (!genresQuery) { - genresQuery=new QSqlQuery(*db); - genresQuery->prepare("select distinct genre, artistId from songs"); - } - genresQuery->exec(); - DBUG << "genresquery" << genresQuery->executedQuery() << genresQuery->size(); - while (genresQuery->next()) { - map[genresQuery->value(0).toString()]++; + SqlQuery query("distinct genre, artistId", *db); + query.setFilter(filter); + + query.exec(); + DBUG << query.executedQuery(); + while (query.next()) { + map[query.value(0).toString()]++; } } @@ -491,27 +575,17 @@ QList LibraryDb::getArtists(const QString &genre) QMap sortMap; QMap albumMap; if (0!=currentVersion) { - QSqlQuery *query=0; - if (genre.isEmpty()) { - if (!artistsQuery) { - artistsQuery=new QSqlQuery(*db); - artistsQuery->prepare("select distinct artistId, albumId, artistSort from songs"); - } - query=artistsQuery; - } else { - if (!artistsGenreQuery) { - artistsGenreQuery=new QSqlQuery(*db); - artistsGenreQuery->prepare("select distinct artistId, albumId, artistSort from songs where genre=:genre"); - } - query=artistsGenreQuery; - artistsGenreQuery->bindValue(":genre", genre); + SqlQuery query("distinct artistId, albumId, artistSort", *db); + query.setFilter(filter); + if (!genre.isEmpty()) { + query.addWhere("genre", genre); } - query->exec(); - DBUG << "artistsquery" << query->executedQuery() << query->size(); - while (query->next()) { - QString artist=query->value(0).toString(); + query.exec(); + DBUG << query.executedQuery(); + while (query.next()) { + QString artist=query.value(0).toString(); albumMap[artist]++; - sortMap[artist]=query->value(2).toString(); + sortMap[artist]=query.value(2).toString(); } } @@ -528,76 +602,44 @@ QList LibraryDb::getArtists(const QString &genre) QList LibraryDb::getAlbums(const QString &artistId, const QString &genre, const QString &sort) { + timer.start(); DBUG << artistId << genre; QList albums; if (0!=currentVersion) { - QSqlQuery *query=0; - if (artistId.isEmpty()) { - if (genre.isEmpty()) { - if (!albumsNoArtistQuery) { - albumsNoArtistQuery=new QSqlQuery(*db); - albumsNoArtistQuery->prepare("select distinct album, albumId, albumSort, artistId, artistSort from songs"); - } - query=albumsNoArtistQuery; - } else { - if (!albumsGenreNoArtistQuery) { - albumsGenreNoArtistQuery=new QSqlQuery(*db); - albumsGenreNoArtistQuery->prepare("select distinct album, albumId, albumSort, artistId, artistSort from songs where genre=:genre"); - } - query=albumsGenreNoArtistQuery; - albumsGenreNoArtistQuery->bindValue(":genre", genre); - } - } else { - if (genre.isEmpty()) { - if (!albumsQuery) { - albumsQuery=new QSqlQuery(*db); - albumsQuery->prepare("select distinct album, albumId, albumSort from songs where artistId=:artistId"); - } - query=albumsQuery; - } else { - if (!albumsGenreQuery) { - albumsGenreQuery=new QSqlQuery(*db); - albumsGenreQuery->prepare("select distinct album, albumId, albumSort from songs where artistId=:artistId and genre=:genre"); - } - query=albumsGenreQuery; - albumsGenreQuery->bindValue(":genre", genre); - } - query->bindValue(":artistId", artistId); + SqlQuery query("distinct album, albumId, albumSort, artistId, artistSort", *db); + query.setFilter(filter); + if (!artistId.isEmpty()) { + query.addWhere("artistId", artistId); } - query->exec(); - DBUG << "albumsquery" << query->executedQuery() << query->size(); - while (query->next()) { - QSqlQuery *detailsQuery=0; - if (genre.isEmpty()) { - if (!albumsDetailsQuery) { - albumsDetailsQuery=new QSqlQuery(*db); - albumsDetailsQuery->prepare("select avg(year), count(track), sum(time) from songs where artistId=:artistId and albumId=:albumId"); - } - detailsQuery=albumsDetailsQuery; - } else { - if (!albumsDetailsGenreQuery) { - albumsDetailsGenreQuery=new QSqlQuery(*db); - albumsDetailsGenreQuery->prepare("select avg(year), count(track), sum(time) from songs where artistId=:artistId and albumId=:albumId and genre=:genre"); - } - detailsQuery=albumsDetailsGenreQuery; - albumsDetailsGenreQuery->bindValue(":genre", genre ); - } - QString album=query->value("album").toString(); - QString albumId=query->value("albumId").toString(); - QString albumSort=query->value("albumSort").toString(); - QString artist=artistId.isEmpty() ? query->value("artistId").toString() : QString(); - QString artistSort=artistId.isEmpty() ? query->value("artistSort").toString() : QString(); + if (!genre.isEmpty()) { + query.addWhere("genre", genre); + } + query.exec(); + DBUG << query.executedQuery(); + while (query.next()) { + QString album=query.value(0).toString(); + QString albumId=query.value(1).toString(); + QString albumSort=query.value(2).toString(); + QString artist=artistId.isEmpty() ? query.value(3).toString() : QString(); + QString artistSort=artistId.isEmpty() ? query.value(4).toString() : QString(); + SqlQuery detailsQuery("avg(year), count(track), sum(time)", *db); + query.setFilter(filter); - detailsQuery->bindValue(":artistId", artistId.isEmpty() ? artist : artistId); - detailsQuery->bindValue(":albumId", albumId); - detailsQuery->exec(); - DBUG << "albumdetailsquery" << detailsQuery->size(); - if (detailsQuery->next()) { - albums.append(Album(album, albumId, albumSort, artist, artistSort, detailsQuery->value(0).toInt(), detailsQuery->value(1).toInt(), detailsQuery->value(2).toInt())); + if (!genre.isEmpty()) { + detailsQuery.addWhere("genre", genre); + } + + detailsQuery.addWhere("artistId", artistId.isEmpty() ? artist : artistId); + detailsQuery.addWhere("albumId", albumId); + detailsQuery.exec(); + DBUG << detailsQuery.executedQuery(); + if (detailsQuery.next()) { + albums.append(Album(album, albumId, albumSort, artist, artistSort, detailsQuery.value(0).toInt(), detailsQuery.value(1).toInt(), detailsQuery.value(2).toInt())); } } } + DBUG << "After select" << timer.elapsed(); if (sort==constAlbumsSortAlYrAr) { qSort(albums.begin(), albums.end(), albumsSortAlYrAr); } else if (sort==constAlbumsSortArAlYr) { @@ -615,7 +657,7 @@ QList LibraryDb::getAlbums(const QString &artistId, const QStr } else { qSort(albums.begin(), albums.end(), albumsSortArYrAl); } - + DBUG << "After sort" << timer.elapsed(); return albums; } @@ -624,35 +666,21 @@ QList LibraryDb::getTracks(const QString &artistId, const QString &albumId DBUG << artistId << albumId << genre << sort; QList songs; if (0!=currentVersion) { - QSqlQuery *query=0; - if (artistId.isEmpty() || albumId.isEmpty()) { - if (!tracksWildcardQuery) { - tracksWildcardQuery=new QSqlQuery(*db); - tracksWildcardQuery->prepare("select * from songs where artistId like :artistId and albumId like :albumId and genre like :genre"); - } - query=tracksWildcardQuery; - tracksWildcardQuery->bindValue(":genre", genre.isEmpty() ? "%" : genre); - } else if (genre.isEmpty()) { - if (!tracksQuery) { - tracksQuery=new QSqlQuery(*db); - tracksQuery->prepare("select * from songs where artistId=:artistId and albumId=:albumId"); - } - query=tracksQuery; - } else { - if (!tracksGenreQuery) { - tracksGenreQuery=new QSqlQuery(*db); - tracksGenreQuery->prepare("select * from songs where artistId=:artistId and albumId=:albumId and genre=:genre"); - } - query=tracksGenreQuery; - tracksGenreQuery->bindValue(":genre", genre); + SqlQuery query("*", *db); + query.setFilter(filter); + if (!artistId.isEmpty()) { + query.addWhere("artistId", artistId); } - - query->bindValue(":artistId", artistId.isEmpty() ? "%" : artistId); - query->bindValue(":albumId", albumId.isEmpty() ? "%" : albumId); - query->exec(); - DBUG << "tracksquery" << query->executedQuery() << query->size(); - while (query->next()) { - songs.append(getSong(query)); + if (!albumId.isEmpty()) { + query.addWhere("albumId", albumId); + } + if (!genre.isEmpty()) { + query.addWhere("genre", genre); + } + query.exec(); + DBUG << query.executedQuery(); + while (query.next()) { + songs.append(getSong(&(query.realQuery()))); } } @@ -676,11 +704,55 @@ QList LibraryDb::getTracks(const QString &artistId, const QString &albumId return songs; } +QList LibraryDb::getAlbumsWithArtist(const QString &artist) +{ + QList albums; + if (0!=currentVersion) { + SqlQuery query("distinct album, albumId, albumSort", *db); + query.addWhere("artist", artist); + query.exec(); + DBUG << query.executedQuery(); + while (query.next()) { + albums.append(Album(query.value(0).toString(), query.value(1).toString(), query.value(2).toString(), artist)); + } + } + + qSort(albums.begin(), albums.end(), albumsSortArYrAl); + + return albums; +} + +#ifndef CANTATA_WEB +void LibraryDb::setFilter(const QString &f) +{ + if (f.isEmpty()) { + filter=QString(); + return; + } + QStringList strings(f.split(QRegExp("\\s+"))); + QStringList tokens; + foreach (QString str, strings) { + str.remove('('); + str.remove(')'); + str.remove('"'); + str.remove(':'); + tokens.append(str+"* "); + } + filter=tokens.join(" OR "); +} +#endif + void LibraryDb::updateStarted(time_t ver) { newVersion=ver; timer.start(); db->transaction(); + if (currentVersion>0) { + QSqlQuery(*db).exec("delete from songs"); +#ifndef CANTATA_WEB + QSqlQuery(*db).exec("delete from songs_fts"); +#endif + } DBUG; } @@ -699,51 +771,67 @@ void LibraryDb::insertSongs(QList *songs) void LibraryDb::updateFinished() { - QSqlQuery("update versions set collection ="+QString::number(newVersion)).exec(); - if (currentVersion>0) { - QSqlQuery("delete from songs where version = "+QString::number(currentVersion)).exec(); - } +#ifndef CANTATA_WEB + QSqlQuery(*db).exec("insert into songs_fts(fts_artist, fts_artistId, fts_album, fts_title) " + "select artist, artistId, album, title from songs"); +#endif + QSqlQuery(*db).exec("update versions set collection ="+QString::number(newVersion)); db->commit(); currentVersion=newVersion; DBUG << timer.elapsed(); + emit libraryUpdated(); } -void LibraryDb::createTable(const QString &q) +bool LibraryDb::createTable(const QString &q) { - QSqlQuery query("create table if not exists "+q); - - if (!query.exec()) { - qWarning() << "Failed to create table" << query.lastError(); - ::exit(-1); + QSqlQuery query(*db); + if (!query.exec("create table if not exists "+q)) { + qWarning() << "Failed to create table" << query.lastError().text(); + return false; } + return true; } -Song LibraryDb::getSong(QSqlQuery *query) +Song LibraryDb::getSong(const QSqlQuery *query) { Song s; - s.file=query->value("file").toString(); - s.artist=query->value("artist").toString(); - s.albumartist=query->value("albumArtist").toString(); - s.setComposer(query->value("composer").toString()); - s.album=query->value("album").toString(); - QString val=query->value("albumId").toString(); + s.file=query->value(0).toString(); + s.artist=query->value(1).toString(); + s.albumartist=query->value(3).toString(); + s.setComposer(query->value(5).toString()); + s.album=query->value(6).toString(); + QString val=query->value(7).toString(); if (!val.isEmpty() && val!=s.album) { s.setMbAlbumId(val); } - s.title=query->value("title").toString(); - s.genre=query->value("genre").toString(); - s.track=query->value("track").toUInt(); - s.disc=query->value("disc").toUInt(); - s.time=query->value("time").toUInt(); - s.year=query->value("year").toUInt(); - s.type=(Song::Type)query->value("type").toUInt(); - val=query->value("artistSort").toString(); + s.title=query->value(9).toString(); + s.genre=query->value(10).toString(); + s.track=query->value(11).toUInt(); + s.disc=query->value(12).toUInt(); + s.time=query->value(13).toUInt(); + s.year=query->value(14).toUInt(); + s.type=(Song::Type)query->value(15).toUInt(); + val=query->value(4).toString(); if (!val.isEmpty() && val!=s.albumArtist()) { s.setArtistSort(val); } - val=query->value("albumSort").toString(); + val=query->value(8).toString(); if (!val.isEmpty() && val!=s.album) { s.setAlbumSort(val); } + return s; } + +void LibraryDb::reset() +{ + bool removeDb=0!=db; + delete insertSongQuery; + delete db; + + insertSongQuery=0; + db=0; + if (removeDb) { + QSqlDatabase::removeDatabase(dbName); + } +} diff --git a/db/librarydb.h b/db/librarydb.h index 79eca31c8..d94f4023f 100644 --- a/db/librarydb.h +++ b/db/librarydb.h @@ -1,5 +1,5 @@ /* - * Cantata Web + * Cantata * * Copyright (c) 2015 Craig Drummond * @@ -43,6 +43,7 @@ public: static void enableDebug() { dbgEnabled=true; } static bool debugEnabled() { return dbgEnabled; } + static const QLatin1String constFileExt; static const QLatin1String constNullGenre; static const QLatin1String constArtistAlbumsSortYear; static const QLatin1String constArtistAlbumsSortName; @@ -102,15 +103,25 @@ public: int duration; }; - LibraryDb(); + LibraryDb(QObject *p, const QString &name); ~LibraryDb(); - bool init(QString &dbName); + void clear(); + void erase(); + bool init(const QString &dbFile); void insertSong(const Song &s); QList getGenres(); QList getArtists(const QString &genre=QString()); - QList getAlbums(const QString &artistId, const QString &genre=QString(), const QString &sort=QString()); + QList getAlbums(const QString &artistId=QString(), const QString &genre=QString(), const QString &sort=QString()); QList getTracks(const QString &artistId, const QString &albumId, const QString &genre=QString(), const QString &sort=QString()); + QList getAlbumsWithArtist(const QString &artist); + #ifndef CANTATA_WEB + void setFilter(const QString &f); + const QString & getFilter() const { return filter; } + #endif + +Q_SIGNALS: + void libraryUpdated(); protected Q_SLOTS: void updateStarted(time_t ver); @@ -118,29 +129,23 @@ protected Q_SLOTS: void updateFinished(); protected: - void createTable(const QString &q); - static Song getSong(QSqlQuery *query); + bool createTable(const QString &q); + static Song getSong(const QSqlQuery *query); + +protected: + virtual void reset(); protected: static bool dbgEnabled; + QString dbName; + QString dbFileName; time_t currentVersion; time_t newVersion; QSqlDatabase *db; QSqlQuery *insertSongQuery; - QSqlQuery *genresQuery; - QSqlQuery *artistsQuery; - QSqlQuery *artistsGenreQuery; - QSqlQuery *albumsQuery; - QSqlQuery *albumsGenreQuery; - QSqlQuery *albumsNoArtistQuery; - QSqlQuery *albumsGenreNoArtistQuery; - QSqlQuery *albumsDetailsQuery; - QSqlQuery *albumsDetailsGenreQuery; - QSqlQuery *tracksQuery; - QSqlQuery *tracksGenreQuery; - QSqlQuery *tracksWildcardQuery; QElapsedTimer timer; + QString filter; }; #endif diff --git a/db/mpdlibrarydb.cpp b/db/mpdlibrarydb.cpp index 5e761dfb0..ecbc3014e 100644 --- a/db/mpdlibrarydb.cpp +++ b/db/mpdlibrarydb.cpp @@ -1,5 +1,5 @@ /* - * Cantata Web + * Cantata * * Copyright (c) 2015 Craig Drummond * @@ -23,7 +23,11 @@ #include "mpdlibrarydb.h" #include "support/globalstatic.h" +#ifndef CANTATA_WEB +#include "support/utils.h" +#endif #include "mpd-interface/mpdconnection.h" +#include "gui/settings.h" #include #include #include @@ -32,43 +36,108 @@ #define DBUG if (LibraryDb::debugEnabled()) qWarning() << metaObject()->className() << __FUNCTION__ +#ifdef CANTATA_WEB GLOBAL_STATIC(MpdLibraryDb, instance) +#else +static const QLatin1String constDirName("library"); +static QString databaseName(const MPDConnectionDetails &details) +{ + QString fileName=(!details.isLocal() ? details.hostname+'_'+QString::number(details.port) : details.hostname)+LibraryDb::constFileExt; + fileName.replace('/', '_'); + fileName.replace('~', '_'); + return Utils::dataDir(constDirName, true)+fileName; +} -MpdLibraryDb::MpdLibraryDb() - : coverQuery(0) +void MpdLibraryDb::removeUnusedDbs() +{ + QSet existing; + QList connections=Settings::self()->allConnections(); + QString dirPath=Utils::dataDir(constDirName, false); + if (dirPath.isEmpty()) { + return; + } + + foreach (const MPDConnectionDetails &conn, connections) { + existing.insert(databaseName(conn).mid(dirPath.length())); + } + + QFileInfoList files=QDir(dirPath).entryInfoList(QStringList() << "*"+LibraryDb::constFileExt, QDir::Files); + foreach (const QFileInfo &file, files) { + if (!existing.contains(file.fileName())) { + QFile::remove(file.absoluteFilePath()); + } + } +} + +#endif + +MpdLibraryDb::MpdLibraryDb(QObject *p) + : LibraryDb(p, "MPD") + , coverQuery(0) + , artistImageQuery(0) { connect(MPDConnection::self(), SIGNAL(updatingLibrary(time_t)), this, SLOT(updateStarted(time_t))); connect(MPDConnection::self(), SIGNAL(librarySongs(QList*)), this, SLOT(insertSongs(QList*))); connect(MPDConnection::self(), SIGNAL(updatedLibrary()), this, SLOT(updateFinished())); connect(MPDConnection::self(), SIGNAL(statsUpdated(MPDStatsValues)), this, SLOT(statsUpdated(MPDStatsValues))); connect(this, SIGNAL(loadLibrary()), MPDConnection::self(), SLOT(loadLibrary())); + #ifndef CANTATA_WEB + connect(MPDConnection::self(), SIGNAL(connectionChanged(MPDConnectionDetails)), this, SLOT(connectionChanged(MPDConnectionDetails))); + #endif DBUG; } MpdLibraryDb::~MpdLibraryDb() { -// delete coverQuery; } Song MpdLibraryDb::getCoverSong(const QString &artistId, const QString &albumId) { DBUG << artistId << albumId; if (0!=currentVersion) { - if (!coverQuery) { - coverQuery=new QSqlQuery(*db); - coverQuery->prepare("select * from songs where artistId=:artistId and albumId=:albumId limit 1;"); + QSqlQuery *query=0; + if (albumId.isEmpty()) { + if (!artistImageQuery) { + artistImageQuery=new QSqlQuery(*db); + artistImageQuery->prepare("select * from songs where artistId=:artistId limit 1;"); + } + query=artistImageQuery; + } else { + if (!coverQuery) { + coverQuery=new QSqlQuery(*db); + coverQuery->prepare("select * from songs where artistId=:artistId and albumId=:albumId limit 1;"); + } + coverQuery->bindValue(":albumId", albumId); + query=coverQuery; } - coverQuery->bindValue(":artistId", artistId); - coverQuery->bindValue(":albumId", albumId); - coverQuery->exec(); - DBUG << "coverquery" << coverQuery->executedQuery() << coverQuery->size(); - while (coverQuery->next()) { - return getSong(coverQuery); + query->bindValue(":artistId", artistId); + query->exec(); + DBUG << "coverquery" << query->executedQuery() << query->size(); + while (query->next()) { + return getSong(query); } } return Song(); } +void MpdLibraryDb::connectionChanged(const MPDConnectionDetails &details) +{ + #ifdef CANTATA_WEB + Q_UNUSED(details) + #else + init(databaseName(details)); + #endif +} + +void MpdLibraryDb::reset() +{ + delete coverQuery; + delete artistImageQuery; + coverQuery=0; + artistImageQuery=0; + LibraryDb::reset(); +} + void MpdLibraryDb::statsUpdated(const MPDStatsValues &stats) { if (stats.dbUpdate>currentVersion) { diff --git a/db/mpdlibrarydb.h b/db/mpdlibrarydb.h index 380d5c446..68bfecd40 100644 --- a/db/mpdlibrarydb.h +++ b/db/mpdlibrarydb.h @@ -1,5 +1,5 @@ /* - * Cantata Web + * Cantata * * Copyright (c) 2015 Craig Drummond * @@ -29,9 +29,11 @@ #include #include #include "librarydb.h" +#include "config.h" #include struct MPDStatsValues; +struct MPDConnectionDetails; class QSqlDatabase; class QSqlQuery; class QSettings; @@ -41,21 +43,30 @@ class MpdLibraryDb : public LibraryDb Q_OBJECT public: + #ifndef CANTATA_WEB + static void removeUnusedDbs(); + #else static MpdLibraryDb * self(); + #endif - MpdLibraryDb(); + MpdLibraryDb(QObject *p=0); ~MpdLibraryDb(); - Song getCoverSong(const QString &artistId, const QString &albumId); + Song getCoverSong(const QString &artistId, const QString &albumId=QString()); Q_SIGNALS: void loadLibrary(); private Q_SLOTS: + void connectionChanged(const MPDConnectionDetails &details); void statsUpdated(const MPDStatsValues &stats); +private: + void reset(); + private: QSqlQuery *coverQuery; + QSqlQuery *artistImageQuery; }; #endif diff --git a/devices/actiondialog.cpp b/devices/actiondialog.cpp index a3a9cc975..02ae7121c 100644 --- a/devices/actiondialog.cpp +++ b/devices/actiondialog.cpp @@ -28,10 +28,8 @@ #include "devicepropertiesdialog.h" #include "devicepropertieswidget.h" #include "gui/settings.h" -#include "models/musiclibrarymodel.h" #include "models/musiclibraryproxymodel.h" #include "models/dirviewmodel.h" -#include "models/albumsmodel.h" #include "mpd-interface/mpdparseutils.h" #include "mpd-interface/mpdconnection.h" #include "encoders.h" @@ -75,29 +73,29 @@ enum Pages PAGE_PROGRESS }; -class SongListDialog : public Dialog -{ -public: - SongListDialog(ActionDialog *p) - : Dialog(p) { - setCaption(i18n("Songs To Be Copied")); - setButtons(Close); - MusicLibraryModel *model=new MusicLibraryModel(this, false); - MusicLibraryProxyModel *proxy=new MusicLibraryProxyModel(this); - proxy->setSourceModel(model); - model->setSupportsAlbumArtistTag(true); - TreeView *view=new TreeView(this); - view->setPageDefaults(); - view->setExpandOnClick(); - view->setModel(proxy); - view->setUseSimpleDelegate(); - model->update(p->songsToAction.toSet()); - setMainWidget(view); - int size=fontMetrics().height(); - int numArtists=model->rowCount(); - resize(20*size, qMin(qMax(10, numArtists)+4, 25)*(size*1.25)); - } -}; +//class SongListDialog : public Dialog +//{ +//public: +// SongListDialog(ActionDialog *p) +// : Dialog(p) { +// setCaption(i18n("Songs To Be Copied")); +// setButtons(Close); +// MusicLibraryModel *model=new MusicLibraryModel(this, false); +// MusicLibraryProxyModel *proxy=new MusicLibraryProxyModel(this); +// proxy->setSourceModel(model); +// model->setSupportsAlbumArtistTag(true); +// TreeView *view=new TreeView(this); +// view->setPageDefaults(); +// view->setExpandOnClick(); +// view->setModel(proxy); +// view->setUseSimpleDelegate(); +// model->update(p->songsToAction.toSet()); +// setMainWidget(view); +// int size=fontMetrics().height(); +// int numArtists=model->rowCount(); +// resize(20*size, qMin(qMax(10, numArtists)+4, 25)*(size*1.25)); +// } +//}; ActionDialog::ActionDialog(QWidget *parent) : Dialog(parent) @@ -119,7 +117,7 @@ ActionDialog::ActionDialog(QWidget *parent) connect(configureSourceButton, SIGNAL(clicked()), SLOT(configureSource())); connect(configureDestButton, SIGNAL(clicked()), SLOT(configureDest())); connect(this, SIGNAL(update()), MPDConnection::self(), SLOT(update())); - connect(songCount, SIGNAL(leftClickedUrl()), SLOT(showSongs())); +// connect(songCount, SIGNAL(leftClickedUrl()), SLOT(showSongs())); #ifdef QT_QTDBUS_FOUND unityMessage=QDBusMessage::createSignal("/Cantata", "com.canonical.Unity.LauncherEntry", "Update"); #endif @@ -184,13 +182,13 @@ void ActionDialog::deviceRenamed() } } -void ActionDialog::showSongs() -{ - if (!songDialog) { - songDialog=new SongListDialog(this); - } - songDialog->show(); -} +//void ActionDialog::showSongs() +//{ +// if (!songDialog) { +// songDialog=new SongListDialog(this); +// } +// songDialog->show(); +//} void ActionDialog::showMopidyMessage() { @@ -201,14 +199,14 @@ void ActionDialog::showMopidyMessage() QLatin1String("Mopidy")); } -void ActionDialog::hideSongs() -{ - if (songDialog) { - songDialog->setVisible(false); - songDialog->deleteLater(); - songDialog=0; - } -} +//void ActionDialog::hideSongs() +//{ +// if (songDialog) { +// songDialog->setVisible(false); +// songDialog->deleteLater(); +// songDialog=0; +// } +//} void ActionDialog::updateSongCountLabel() { @@ -424,7 +422,7 @@ void ActionDialog::slotButtonClicked(int button) } Settings::self()->saveOverwriteSongs(overwrite->isChecked()); setPage(PAGE_PROGRESS); - hideSongs(); +// hideSongs(); #ifdef ACTION_DIALOG_SHOW_TIME_REMAINING timer.start(); #endif @@ -851,8 +849,8 @@ bool ActionDialog::refreshLibrary() if (!actionedSongs.isEmpty()) { if ( (Copy==mode && !sourceUdi.isEmpty()) || (Remove==mode && sourceUdi.isEmpty()) ) { - MusicLibraryModel::self()->checkForNewSongs(); - AlbumsModel::self()->update(MusicLibraryModel::self()->root()); +// MusicLibraryModel::self()->checkForNewSongs(); +// AlbumsModel::self()->update(MusicLibraryModel::self()->root()); emit update(); } else if ( (Copy==mode && sourceUdi.isEmpty()) || (Remove==mode && !sourceUdi.isEmpty()) ) { @@ -888,7 +886,7 @@ void ActionDialog::removeSongResult(int status) if (Device::Ok!=status) { actionStatus(status); } else { - MusicLibraryModel::self()->removeSongFromList(currentSong); +// MusicLibraryModel::self()->removeSongFromList(currentSong); DirViewModel::self()->removeFileFromList(currentSong.file); actionStatus(Device::Ok); } diff --git a/devices/actiondialog.h b/devices/actiondialog.h index 4a68158cf..2dfcc4a10 100644 --- a/devices/actiondialog.h +++ b/devices/actiondialog.h @@ -79,11 +79,11 @@ private Q_SLOTS: void cacheSaved(); void controlInfoLabel(); void deviceRenamed(); - void showSongs(); +// void showSongs(); void showMopidyMessage(); private: - void hideSongs(); +// void hideSongs(); void updateSongCountLabel(); void controlInfoLabel(Device *dev); Device * getDevice(const QString &udi, bool logErrors=true); diff --git a/devices/albumdetailsdialog.cpp b/devices/albumdetailsdialog.cpp index 48329e4b2..0987ee335 100644 --- a/devices/albumdetailsdialog.cpp +++ b/devices/albumdetailsdialog.cpp @@ -24,7 +24,6 @@ #include "albumdetailsdialog.h" #include "support/localize.h" #include "audiocddevice.h" -#include "models/musiclibrarymodel.h" #include "models/musiclibraryitemsong.h" #include "support/messagebox.h" #include "support/inputdialog.h" @@ -113,7 +112,7 @@ AlbumDetailsDialog::AlbumDetailsDialog(QWidget *parent) QSet composers; QSet albums; QSet genres; - MusicLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres); +// MusicLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres); QStringList strings=albumArtists.toList(); strings.sort(); diff --git a/devices/audiocddevice.cpp b/devices/audiocddevice.cpp index 325aa67db..b4d0de483 100644 --- a/devices/audiocddevice.cpp +++ b/devices/audiocddevice.cpp @@ -31,7 +31,6 @@ #include "support/localize.h" #include "gui/plurals.h" #include "models/musiclibraryitemsong.h" -#include "models/musiclibrarymodel.h" #include "models/dirviewmodel.h" #include "support/utils.h" #include "extractjob.h" @@ -251,10 +250,10 @@ void AudioCdDevice::copySongTo(const Song &s, const QString &musicPath, bool ove if (needToFixVa) { Device::fixVariousArtists(QString(), check, false); } - if (MusicLibraryModel::self()->songExists(check)) { - emit actionStatus(SongExists); - return; - } +// if (MusicLibraryModel::self()->songExists(check)) { +// emit actionStatus(SongExists); +// return; +// } } DeviceOptions mpdOpts; @@ -329,7 +328,7 @@ void AudioCdDevice::copySongToResult(int status) currentSong.revertVariousArtists(); } Utils::setFilePerms(currentDestFile); - MusicLibraryModel::self()->addSongToList(currentSong); +// MusicLibraryModel::self()->addSongToList(currentSong); DirViewModel::self()->addFileToList(origPath.isEmpty() ? currentSong.file : origPath, origPath.isEmpty() ? QString() : currentSong.file); emit actionStatus(Ok, job && job->coverCopied()); diff --git a/devices/device.cpp b/devices/device.cpp index 59f150d2b..595b2b7df 100644 --- a/devices/device.cpp +++ b/devices/device.cpp @@ -157,12 +157,6 @@ Song Device::fixPath(const Song &orig, bool fullPath) const #include -void Device::toggleGrouping() -{ - MusicLibraryItemRoot::toggleGrouping(); - saveCache(); -} - const QLatin1String Device::constNoCover("-"); const QLatin1String Device::constEmbedCover("+"); diff --git a/devices/device.h b/devices/device.h index d0d824cb2..8d8796539 100644 --- a/devices/device.h +++ b/devices/device.h @@ -157,7 +157,6 @@ public: virtual bool isDevice() const { return true; } #ifdef ENABLE_DEVICES_SUPPORT - void toggleGrouping(); virtual void saveCache(); const QString & id() const { return deviceId; } void applyUpdate(); diff --git a/devices/devicespage.cpp b/devices/devicespage.cpp index 47fd79d36..b5831cc7f 100644 --- a/devices/devicespage.cpp +++ b/devices/devicespage.cpp @@ -37,7 +37,6 @@ #include "remotedevicepropertiesdialog.h" #include "devicepropertieswidget.h" #endif -#include "syncdialog.h" #ifdef ENABLE_REPLAYGAIN_SUPPORT #include "replaygain/rgdialog.h" #endif @@ -60,8 +59,6 @@ DevicesPage::DevicesPage(QWidget *p) setupUi(this); copyAction = new Action(Icons::self()->importIcon, i18n("Copy To Library"), this); copyToLibraryButton->setDefaultAction(copyAction); - syncAction = new Action(Icon("folder-sync"), i18n("Sync"), this); - connect(syncAction, SIGNAL(triggered()), this, SLOT(sync())); #ifdef ENABLE_REMOTE_DEVICES forgetDeviceAction=new Action(Icon("list-remove"), i18n("Forget Device"), this); connect(forgetDeviceAction, SIGNAL(triggered()), this, SLOT(forgetRemoteDevice())); @@ -69,9 +66,7 @@ DevicesPage::DevicesPage(QWidget *p) connect(DevicesModel::self()->connectAct(), SIGNAL(triggered()), this, SLOT(toggleDevice())); connect(DevicesModel::self()->disconnectAct(), SIGNAL(triggered()), this, SLOT(toggleDevice())); copyToLibraryButton->setEnabled(false); - syncAction->setEnabled(false); view->addAction(copyAction); - view->addAction(syncAction); view->addAction(StdActions::self()->organiseFilesAction); view->addAction(StdActions::self()->editTagsAction); #ifdef ENABLE_REPLAYGAIN_SUPPORT @@ -85,13 +80,10 @@ DevicesPage::DevicesPage(QWidget *p) view->addAction(StdActions::self()->deleteSongsAction); connect(this, SIGNAL(add(const QStringList &, bool, quint8)), MPDConnection::self(), SLOT(add(const QStringList &, bool, quint8))); connect(this, SIGNAL(addSongsToPlaylist(const QString &, const QStringList &)), MPDConnection::self(), SLOT(addToPlaylist(const QString &, const QStringList &))); - connect(genreCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchItems())); - connect(DevicesModel::self(), SIGNAL(updateGenres(const QSet &)), genreCombo, SLOT(update(const QSet &))); connect(DevicesModel::self(), SIGNAL(updated(QModelIndex)), this, SLOT(updated(QModelIndex))); connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &))); connect(view, SIGNAL(searchItems()), this, SLOT(searchItems())); connect(view, SIGNAL(itemsSelected(bool)), SLOT(controlActions())); - connect(view, SIGNAL(rootIndexSet(QModelIndex)), this, SLOT(updateGenres(QModelIndex))); connect(copyAction, SIGNAL(triggered()), this, SLOT(copyToLibrary())); connect(DevicesModel::self()->configureAct(), SIGNAL(triggered()), this, SLOT(configureDevice())); connect(DevicesModel::self()->refreshAct(), SIGNAL(triggered()), this, SLOT(refreshDevice())); @@ -264,7 +256,7 @@ void DevicesPage::itemDoubleClicked(const QModelIndex &) void DevicesPage::searchItems() { QString text=view->searchText().trimmed(); - proxy.update(text, genreCombo->currentIndex()<=0 ? QString() : genreCombo->currentText()); + proxy.update(text); if (proxy.enabled() && !proxy.filterText().isEmpty()) { view->expandAll(); } @@ -276,7 +268,7 @@ void DevicesPage::controlActions() bool haveTracks=false; bool onlyFs=true; bool singleUdi=true; - bool connected=false; +// bool connected=false; #ifdef ENABLE_REMOTE_DEVICES bool remoteDev=false; #endif @@ -324,15 +316,13 @@ void DevicesPage::controlActions() if (!haveTracks) { haveTracks=dev->childCount()>0; } - connected=dev->isConnected(); +// connected=dev->isConnected(); } } DevicesModel::self()->configureAct()->setEnabled(!busyDevice && 1==selected.count() && !audioCd); DevicesModel::self()->refreshAct()->setEnabled(!busyDevice && 1==selected.count()); copyAction->setEnabled(!busyDevice && haveTracks && (!deviceSelected || audioCd)); - syncAction->setEnabled(!audioCd && !busyDevice && deviceSelected && connected && 1==selected.count() && singleUdi && - MPDConnection::self()->getDetails().dirReadable); StdActions::self()->deleteSongsAction->setEnabled(!audioCd && !busyDevice && haveTracks && !deviceSelected); StdActions::self()->editTagsAction->setEnabled(!busyDevice && haveTracks && onlyFs && singleUdi && !deviceSelected); #ifdef ENABLE_REPLAYGAIN_SUPPORT @@ -546,58 +536,6 @@ void DevicesPage::toggleDevice() } } -void DevicesPage::sync() -{ - if (0!=SyncDialog::instanceCount()) { - return; - } - - if (0!=PreferencesDialog::instanceCount() || 0!=TagEditor::instanceCount() || 0!=TrackOrganiser::instanceCount() - || 0!=ActionDialog::instanceCount() || 0!=CoverDialog::instanceCount() - #ifdef ENABLE_REPLAYGAIN_SUPPORT - || 0!=RgDialog::instanceCount() - #endif - ) { - MessageBox::error(this, i18n("Please close other dialogs first.")); - return; - } - - const QModelIndexList selected = view->selectedIndexes(); - - if (1!=selected.size()) { - return; - } - - MusicLibraryItem *item=static_cast(proxy.mapToSource(selected.first()).internalPointer()); - - if (MusicLibraryItem::Type_Root==item->itemType()) { - SyncDialog *dlg=new SyncDialog(this); - dlg->sync(static_cast(item)->id()); - } -} - -void DevicesPage::updateGenres(const QModelIndex &idx) -{ - if (idx.isValid()) { - QModelIndex m=proxy.mapToSource(idx); - if (m.isValid()) { - MusicLibraryItem *item=static_cast(m.internalPointer()); - MusicLibraryItem::Type itemType=item->itemType(); - if (itemType==MusicLibraryItem::Type_Root) { - genreCombo->update(static_cast(item)->genres()); - return; - } else if (itemType==MusicLibraryItem::Type_Artist) { - genreCombo->update(static_cast(item)->genres()); - return; - } else if (itemType==MusicLibraryItem::Type_Album) { - genreCombo->update(static_cast(item)->genres()); - return; - } - } - } - genreCombo->update(DevicesModel::self()->genres()); -} - void DevicesPage::updated(const QModelIndex &idx) { view->setExpanded(proxy.mapFromSource(idx)); diff --git a/devices/devicespage.h b/devices/devicespage.h index f65642a1b..13213e98f 100644 --- a/devices/devicespage.h +++ b/devices/devicespage.h @@ -65,8 +65,6 @@ public Q_SLOTS: void addRemoteDevice(); void forgetRemoteDevice(); void toggleDevice(); - void sync(); - void updateGenres(const QModelIndex &); void updated(const QModelIndex &idx); void cdMatches(const QString &udi, const QList &albums); void editDetails(); @@ -88,8 +86,6 @@ private: #ifdef ENABLE_REMOTE_DEVICES Action *forgetDeviceAction; #endif - Action *syncAction; - QSet genres; }; #endif diff --git a/devices/devicespage.ui b/devices/devicespage.ui index c4a44a2ef..0fd9d57b6 100644 --- a/devices/devicespage.ui +++ b/devices/devicespage.ui @@ -29,7 +29,17 @@ 0 - + + + Qt::Horizontal + + + + 16 + 20 + + + @@ -50,11 +60,6 @@ QTreeView
    widgets/itemview.h
    - - GenreCombo - QComboBox -
    widgets/genrecombo.h
    -
    ToolButton QToolButton diff --git a/devices/fsdevice.cpp b/devices/fsdevice.cpp index 9ab7aeca0..5df0fd1a6 100644 --- a/devices/fsdevice.cpp +++ b/devices/fsdevice.cpp @@ -23,7 +23,6 @@ #include "umsdevice.h" #include "tags/tags.h" -#include "models/musiclibrarymodel.h" #include "models/musiclibraryitemsong.h" #include "models/musiclibraryitemalbum.h" #include "models/musiclibraryitemartist.h" @@ -80,10 +79,8 @@ void MusicScanner::scan(const QString &folder, const QString &cacheFile, bool re { if (!cacheFile.isEmpty() && readCache) { MusicLibraryItemRoot *lib=new MusicLibraryItemRoot; - MusicLibraryModel::convertCache(cacheFile); readProgress(0.0); if (lib->fromXML(cacheFile, 0, 0, folder)) { - lib->applyGrouping(); if (!stopRequested) { emit libraryUpdated(lib); } else { @@ -106,7 +103,6 @@ void MusicScanner::scan(const QString &folder, const QString &cacheFile, bool re scanFolder(library, topLevel, topLevel, existing, 0); if (!stopRequested) { - library->applyGrouping(); if (!cacheFile.isEmpty()) { writeProgress(0.0); library->toXML(cacheFile, 0, false, this); @@ -182,12 +178,7 @@ void MusicScanner::scanFolder(MusicLibraryItemRoot *library, const QString &topL if (!albumItem || albumItem->parentItem()!=artistItem || song.albumName()!=albumItem->data()) { albumItem = artistItem->album(song); } - MusicLibraryItemSong *songItem = new MusicLibraryItemSong(song, albumItem); - const QSet &songGenres=songItem->allGenres(); - albumItem->append(songItem); - albumItem->addGenres(songGenres); - artistItem->addGenres(songGenres); - library->addGenres(songGenres); + albumItem->append(new MusicLibraryItemSong(song, albumItem)); } } } @@ -446,10 +437,10 @@ void FsDevice::copySongTo(const Song &s, const QString &musicPath, bool overwrit if (needToFixVa) { Device::fixVariousArtists(QString(), check, false); } - if (MusicLibraryModel::self()->songExists(check)) { - emit actionStatus(SongExists); - return; - } +// if (MusicLibraryModel::self()->songExists(check)) { +// emit actionStatus(SongExists); +// return; +// } } QString source=audioFolder+s.file; @@ -594,7 +585,7 @@ void FsDevice::copySongToResult(int status) currentSong.revertVariousArtists(); } Utils::setFilePerms(currentDestFile); - MusicLibraryModel::self()->addSongToList(currentSong); +// MusicLibraryModel::self()->addSongToList(currentSong); DirViewModel::self()->addFileToList(origPath.isEmpty() ? currentSong.file : origPath, origPath.isEmpty() ? QString() : currentSong.file); emit actionStatus(Ok, job && job->coverCopied()); @@ -706,7 +697,7 @@ QString FsDevice::cacheFileName() const if (audioFolder.isEmpty()) { setAudioFolder(); } - return audioFolder+constCantataCacheFile+MusicLibraryModel::constLibraryCompressedExt; + return audioFolder+constCantataCacheFile+".xml.gz"; } void FsDevice::saveCache() @@ -732,13 +723,6 @@ void FsDevice::removeCache() if (QFile::exists(cacheFile)) { QFile::remove(cacheFile); } - - // Remove old (non-compressed) cache file as well... - QString oldCache=cacheFile; - oldCache.replace(MusicLibraryModel::constLibraryCompressedExt, MusicLibraryModel::constLibraryExt); - if (oldCache!=cacheFile && QFile::exists(oldCache)) { - QFile::remove(oldCache); - } } void FsDevice::readingCache(int pc) diff --git a/devices/mtpdevice.cpp b/devices/mtpdevice.cpp index 4f6973c45..4bcba09ea 100644 --- a/devices/mtpdevice.cpp +++ b/devices/mtpdevice.cpp @@ -22,7 +22,6 @@ */ #include "mtpdevice.h" -#include "models/musiclibrarymodel.h" #include "models/musiclibraryitemsong.h" #include "models/musiclibraryitemalbum.h" #include "models/musiclibraryitemartist.h" @@ -434,9 +433,6 @@ void MtpConnection::updateLibrary(const DeviceOptions &opts) MusicLibraryItemSong *songItem = new MusicLibraryItemSong(s, albumItem); const QSet &songGenres=songItem->allGenres(); albumItem->append(songItem); - albumItem->addGenres(songGenres); - artistItem->addGenres(songGenres); - library->addGenres(songGenres); #ifdef MTP_FAKE_ALBUMARTIST_SUPPORT // Store AlbumName->Artists/Songs mapping @@ -508,8 +504,6 @@ void MtpConnection::updateLibrary(const DeviceOptions &opts) albumItem=artistItem->album(song); MusicLibraryItemSong *songItem = new MusicLibraryItemSong(song, albumItem); albumItem->append(songItem); - albumItem->updateGenres(); - artistItem->updateGenres(); MusicLibraryItemAlbum *prevAlbum=(MusicLibraryItemAlbum *)s->parentItem(); prevAlbum->remove(s); if (0==prevAlbum->childCount()) { @@ -533,7 +527,6 @@ void MtpConnection::updateLibrary(const DeviceOptions &opts) if (abortRequested) { return; } - library->applyGrouping(); #ifdef TIME_MTP_OPERATIONS qWarning() << "Grouping:" << timer.elapsed(); qWarning() << "TOTAL update:" <songExists(check)) { - emit actionStatus(SongExists); - return; - } +// if (MusicLibraryModel::self()->songExists(check)) { +// emit actionStatus(SongExists); +// return; +// } } if (!songExists(s)) { @@ -1650,7 +1643,7 @@ void MtpDevice::getSongStatus(bool ok, bool copiedCover) currentSong.revertVariousArtists(); } Utils::setFilePerms(currentDestFile); - MusicLibraryModel::self()->addSongToList(currentSong); +// MusicLibraryModel::self()->addSongToList(currentSong); DirViewModel::self()->addFileToList(origPath.isEmpty() ? currentSong.file : origPath, origPath.isEmpty() ? QString() : currentSong.file); emit actionStatus(Ok, copiedCover); diff --git a/devices/synccollectionwidget.cpp b/devices/synccollectionwidget.cpp deleted file mode 100644 index 5e7c660e8..000000000 --- a/devices/synccollectionwidget.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "synccollectionwidget.h" -#include "widgets/treeview.h" -#include "models/musiclibraryproxymodel.h" -#include "models/musiclibraryitemartist.h" -#include "models/musiclibraryitemalbum.h" -#include "models/musiclibraryitemsong.h" -#include "support/icon.h" -#include "support/localize.h" -#include "support/actioncollection.h" -#include -#include - -SyncCollectionWidget::SyncCollectionWidget(QWidget *parent, const QString &title, const QString &action) - : QWidget(parent) - , performedSearch(false) - , searchTimer(0) -{ - setupUi(this); - titleLabel->setText(title); - button->setText(action); - button->setEnabled(false); - - model=new MusicLibraryModel(this, false, true); - proxy=new MusicLibraryProxyModel(this); - proxy->setSourceModel(model); - tree->setModel(proxy); - tree->setPageDefaults(); - tree->setUseSimpleDelegate(); - search->setText(QString()); - search->setPlaceholderText(i18n("Search")); - connect(proxy, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex))); - connect(model, SIGNAL(checkedSongs(QSet)), this, SLOT(songsChecked(QSet))); - connect(button, SIGNAL(clicked()), SLOT(copySongs())); - connect(search, SIGNAL(returnPressed()), this, SLOT(delaySearchItems())); - connect(search, SIGNAL(textChanged(const QString)), this, SLOT(delaySearchItems())); - - copyAction=new Action(action, this); - connect(copyAction, SIGNAL(triggered()), SLOT(copySongs())); - checkAction=new Action(i18n("Check Items"), this); - connect(checkAction, SIGNAL(triggered()), SLOT(checkItems())); - unCheckAction=new Action(i18n("Uncheck Items"), this); - connect(unCheckAction, SIGNAL(triggered()), SLOT(unCheckItems())); - tree->addAction(copyAction); - QAction *sep=new QAction(this); - sep->setSeparator(true); - tree->addAction(sep); - tree->addAction(checkAction); - tree->addAction(unCheckAction); - tree->setContextMenuPolicy(Qt::ActionsContextMenu); - - QAction *expand=ActionCollection::get()->action("expandall"); - QAction *collapse=ActionCollection::get()->action("collapseall"); - if (expand && collapse) { - sep=new QAction(this); - sep->setSeparator(true); - tree->addAction(sep); - tree->addAction(expand); - tree->addAction(collapse); - addAction(expand); - addAction(collapse); - connect(expand, SIGNAL(triggered()), this, SLOT(expandAll())); - connect(collapse, SIGNAL(triggered()), this, SLOT(collapseAll())); - } - - connect(tree, SIGNAL(itemsSelected(bool)), checkAction, SLOT(setEnabled(bool))); - connect(tree, SIGNAL(itemsSelected(bool)), unCheckAction, SLOT(setEnabled(bool))); - connect(tree, SIGNAL(itemActivated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &))); - connect(tree, SIGNAL(clicked(const QModelIndex &)), this, SLOT(itemClicked(const QModelIndex &))); - updateStats(); -} - -SyncCollectionWidget::~SyncCollectionWidget() -{ -} - -void SyncCollectionWidget::setIcon(const Icon &icon) -{ - tree->setBackgroundImage(icon); -} - -void SyncCollectionWidget::checkItems() -{ - checkItems(true); -} - -void SyncCollectionWidget::unCheckItems() -{ - checkItems(false); -} - -void SyncCollectionWidget::checkItems(bool c) -{ - const QModelIndexList selected = tree->selectedIndexes(); - - if (0==selected.size()) { - return; - } - - foreach (const QModelIndex &idx, selected) { - model->setData(proxy->mapToSource(idx), c, Qt::CheckStateRole); - } -} - -void SyncCollectionWidget::copySongs() -{ - if (checkedSongs.isEmpty()) { - return; - } - QList songs=checkedSongs.toList(); - qSort(songs); - emit copy(songs); -} - -void SyncCollectionWidget::delaySearchItems() -{ - if (search->text().trimmed().isEmpty()) { - if (searchTimer) { - searchTimer->stop(); - } - if (performedSearch) { - tree->collapseToLevel(0); - } - searchItems(); - performedSearch=false; - } else { - if (!searchTimer) { - searchTimer=new QTimer(this); - searchTimer->setSingleShot(true); - connect(searchTimer, SIGNAL(timeout()), SLOT(searchItems())); - } - searchTimer->start(500); - } -} - -void SyncCollectionWidget::searchItems() -{ - QString text=search->text().trimmed(); - proxy->update(text); - if (proxy->enabled() && !text.isEmpty()) { - tree->expandAll(); - } - performedSearch=true; -} - -void SyncCollectionWidget::expandAll() -{ - QWidget *f=QApplication::focusWidget(); - if (f && qobject_cast(f)) { - static_cast(f)->expandAll(); - } -} - -void SyncCollectionWidget::collapseAll() -{ - QWidget *f=QApplication::focusWidget(); - if (f && qobject_cast(f)) { - static_cast(f)->collapseAll(); - } -} - -void SyncCollectionWidget::itemClicked(const QModelIndex &index) -{ - if (TreeView::getForceSingleClick() && !tree->checkBoxClicked(index)) { - tree->setExpanded(index, !tree->isExpanded(index)); - } -} - -void SyncCollectionWidget::itemActivated(const QModelIndex &index) -{ - if (!TreeView::getForceSingleClick()) { - tree->setExpanded(index, !tree->isExpanded(index)); - } -} - -void SyncCollectionWidget::songsChecked(const QSet &songs) -{ - checkedSongs=songs; - button->setEnabled(!checkedSongs.isEmpty()); - updateStats(); -} - - -void SyncCollectionWidget::dataChanged(const QModelIndex &tl, const QModelIndex &br) -{ - QModelIndex firstIndex = proxy->mapToSource(tl); - QModelIndex lastIndex = proxy->mapToSource(br); - const MusicLibraryItem *item=static_cast(firstIndex.internalPointer()); - switch (item->itemType()) { - case MusicLibraryItem::Type_Artist: - for (int i=firstIndex.row(); i<=lastIndex.row(); ++i) { - QModelIndex index=model->index(i, 0, firstIndex.parent()); - const MusicLibraryItemArtist *artist=static_cast(index.internalPointer()); - foreach (const MusicLibraryItem *alItem, artist->childItems()) { - foreach (const MusicLibraryItem *sItem, static_cast(alItem)->childItems()) { - Song s=static_cast(sItem)->song(); - if (Qt::Checked==item->checkState()) { - checkedSongs.insert(s); - } else { - checkedSongs.remove(s); - } - } - } - } - break; - case MusicLibraryItem::Type_Album: - for (int i=firstIndex.row(); i<=lastIndex.row(); ++i) { - QModelIndex index=model->index(i, 0, firstIndex.parent()); - const MusicLibraryItemAlbum *album=static_cast(index.internalPointer()); - foreach (const MusicLibraryItem *sItem, album->childItems()) { - Song s=static_cast(sItem)->song(); - if (Qt::Checked==item->checkState()) { - checkedSongs.insert(s); - } else { - checkedSongs.remove(s); - } - } - } - break; - case MusicLibraryItem::Type_Song: - for (int i=firstIndex.row(); i<=lastIndex.row(); ++i) { - QModelIndex index=model->index(i, 0, firstIndex.parent()); - Song s=static_cast(index.internalPointer())->song(); - if (Qt::Checked==item->checkState()) { - checkedSongs.insert(s); - } else { - checkedSongs.remove(s); - } - } - default: - break; - } - - button->setEnabled(!checkedSongs.isEmpty()); - updateStats(); -} - -void SyncCollectionWidget::updateStats() -{ - QSet artists; - QSet albums; - - foreach (const Song &s, checkedSongs) { - artists.insert(s.albumArtist()); - albums.insert(s.albumArtist()+"--"+s.album); - } - - if (checkedSongs.isEmpty()) { - selection->setText(i18n("Nothing selected")); - } else { - selection->setText(i18n("Artists:%1, Albums:%2, Songs:%3", artists.count(), albums.count(), checkedSongs.count())); - } - copyAction->setEnabled(!checkedSongs.isEmpty()); -} diff --git a/devices/synccollectionwidget.h b/devices/synccollectionwidget.h deleted file mode 100644 index fa79be92a..000000000 --- a/devices/synccollectionwidget.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _SYNCCOLLECTIONWIDGET_H_ -#define _SYNCCOLLECTIONWIDGET_H_ - -#include "ui_synccollectionwidget.h" -#include "mpd-interface/song.h" -#include "models/musiclibrarymodel.h" -#include - -class QTimer; -class MusicLibraryProxyModel; -class Action; -class Icon; - -class SyncCollectionWidget : public QWidget, Ui::SyncCollectionWidget -{ - Q_OBJECT - -public: - SyncCollectionWidget(QWidget *parent, const QString &title, const QString &action); - virtual ~SyncCollectionWidget(); - - void update(const QSet &songs) { model->update(songs); } - void setSupportsAlbumArtistTag(bool s) { model->setSupportsAlbumArtistTag(s); } - int numArtists() { return model->rowCount(); } - void setIcon(const Icon &icon); - -Q_SIGNALS: - void copy(const QList &songs); - -private Q_SLOTS: - void songsChecked(const QSet &songs); - void dataChanged(const QModelIndex &tl, const QModelIndex &br); - void checkItems(); - void unCheckItems(); - void copySongs(); - void delaySearchItems(); - void searchItems(); - void expandAll(); - void collapseAll(); - void itemClicked(const QModelIndex &index); - void itemActivated(const QModelIndex &index); - -private: - void checkItems(bool c); - void updateStats(); - -private: - bool performedSearch; - MusicLibraryModel *model; - MusicLibraryProxyModel *proxy; - QTimer *searchTimer; - QSet checkedSongs; - Action *copyAction; - Action *checkAction; - Action *unCheckAction; -}; - -#endif diff --git a/devices/synccollectionwidget.ui b/devices/synccollectionwidget.ui deleted file mode 100644 index 590fdd399..000000000 --- a/devices/synccollectionwidget.ui +++ /dev/null @@ -1,59 +0,0 @@ - - - SyncCollectionWidget - - - - 0 - - - - - - 75 - true - - - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - SqueezedTextLabel - QLabel -
    support/squeezedtextlabel.h
    -
    - - LineEdit - QLineEdit -
    support/lineedit.h
    -
    - - TreeView - QTreeView -
    widgets/treeview.h
    -
    -
    - - -
    diff --git a/devices/syncdialog.cpp b/devices/syncdialog.cpp deleted file mode 100644 index 35f5b8cbd..000000000 --- a/devices/syncdialog.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "syncdialog.h" -#include "synccollectionwidget.h" -#include "actiondialog.h" -#include "mpd-interface/song.h" -#include "models/musiclibrarymodel.h" -#include "models/devicesmodel.h" -#include "support/localize.h" -#include "support/messagebox.h" -#include "widgets/icons.h" -#include - -struct SyncSong : public Song -{ - SyncSong(const Song &o) - : Song(o) { - } - bool operator==(const SyncSong &o) const { - return title==o.title && album==o.album && albumArtist()==o.albumArtist(); - } - bool operator<(const SyncSong &o) const { - int compare=albumArtist().compare(o.albumArtist()); - - if (0!=compare) { - return compare<0; - } - compare=album.compare(o.album); - if (0!=compare) { - return compare<0; - } - return title.compare(o.title)<0; - } -}; - -inline uint qHash(const SyncSong &key) -{ - return qHash(key.albumArtist()+key.artist+key.title); -} - -static void getDiffs(const QSet &s1, const QSet &s2, QSet &in1, QSet &in2) -{ - QSet a; - QSet b; - - foreach (const Song &s, s1) { - a.insert(s); - } - - foreach (const Song &s, s2) { - b.insert(s); - } - - QSet r=a-b; - - foreach (const Song &s, r) { - in1.insert(s); - } - - r=b-a; - - foreach (const Song &s, r) { - in2.insert(s); - } -} - -static int iCount=0; - -int SyncDialog::instanceCount() -{ - return iCount; -} - -SyncDialog::SyncDialog(QWidget *parent) - : Dialog(parent, "SyncDialog", QSize(680, 680)) - , currentDev(0) -{ - iCount++; - - QSplitter *splitter=new QSplitter(this); - libWidget=new SyncCollectionWidget(splitter, i18n("Songs Only In Library:"), i18n("Copy To Device")); - devWidget=new SyncCollectionWidget(splitter, i18n("Songs Only On Device:"), i18n("Copy To Library")); - setMainWidget(splitter); - setButtons(Close); - setAttribute(Qt::WA_DeleteOnClose); - setCaption(i18n("Synchronize")); - connect(libWidget, SIGNAL(copy(const QList &)), SLOT(copy(const QList &))); - connect(devWidget, SIGNAL(copy(const QList &)), SLOT(copy(const QList &))); - -// libWidget->setIcon(Icons::self()->libraryIcon); -} - -SyncDialog::~SyncDialog() -{ - iCount--; -} - -void SyncDialog::sync(const QString &udi) -{ - devUdi=udi; -// Device *dev=getDevice(); -// if (dev) { -// devWidget->setIcon(Icon(dev->icon())); -// } - - if (updateSongs(true)) { - show(); - } -} - -void SyncDialog::copy(const QList &songs) -{ - Device *dev=getDevice(); - - if (!dev) { - return; - } - - bool fromDev=sender()==devWidget; - ActionDialog *dlg=new ActionDialog(this); - connect(dlg, SIGNAL(completed()), SLOT(updateSongs())); - dlg->copy(fromDev ? dev->id() : QString(), fromDev ? QString() : dev->id(), songs); -} - -bool SyncDialog::updateSongs(bool firstRun) -{ - Device *dev=getDevice(); - - if (!dev) { - deleteLater(); - hide(); - return false; - } - - QSet devSongs=dev->allSongs(dev->options().fixVariousArtists); - QSet libSongs=MusicLibraryModel::self()->root()->allSongs(); - QSet inDev; - QSet inLib; - getDiffs(devSongs, libSongs, inDev, inLib); - - if (0==inDev.count() && 0==inLib.count()) { - if (firstRun) { - MessageBox::information(isVisible() ? this : parentWidget(), i18n("Device and library are in sync.")); - } - deleteLater(); - hide(); - return false; - } - if (firstRun) { - devWidget->setSupportsAlbumArtistTag(dev->supportsAlbumArtistTag()); - } - devWidget->update(inDev); - libWidget->update(inLib); - return true; -} - -Device * SyncDialog::getDevice() -{ - Device *dev=DevicesModel::self()->device(devUdi); - if (!dev) { - MessageBox::error(isVisible() ? this : parentWidget(), i18n("Device has been removed!")); - return 0; - } - - if (currentDev && dev!=currentDev) { - MessageBox::error(isVisible() ? this : parentWidget(), i18n("Device has been changed?")); - return 0; - } - - if (dev->isIdle()) { - return dev; - } - - MessageBox::error(isVisible() ? this : parentWidget(), i18n("Device is busy?")); - return 0; -} diff --git a/devices/syncdialog.h b/devices/syncdialog.h deleted file mode 100644 index 265a7c540..000000000 --- a/devices/syncdialog.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _SYNC_DIALOG_H_ -#define _SYNC_DIALOG_H_ - -#include "support/dialog.h" -#include "mpd-interface/song.h" - -class Device; -class SyncCollectionWidget; - -class SyncDialog : public Dialog -{ - Q_OBJECT - -public: - static int instanceCount(); - - SyncDialog(QWidget *parent); - virtual ~SyncDialog(); - - void sync(const QString &udi); - -private Q_SLOTS: - void copy(const QList &songs); - bool updateSongs(bool firstRun=false); - -private: - Device * getDevice(); - -private: - QString devUdi; - Device *currentDev; - SyncCollectionWidget *devWidget; - SyncCollectionWidget *libWidget; -}; - -#endif diff --git a/dynamic/dynamic.cpp b/dynamic/dynamic.cpp index ff284e879..c50212520 100644 --- a/dynamic/dynamic.cpp +++ b/dynamic/dynamic.cpp @@ -47,7 +47,7 @@ #include #include #if defined ENABLE_MODEL_TEST -#include "modeltest.h" +#include "models/modeltest.h" #endif #include diff --git a/dynamic/dynamicruledialog.cpp b/dynamic/dynamicruledialog.cpp index 460224c5a..ca1120936 100644 --- a/dynamic/dynamicruledialog.cpp +++ b/dynamic/dynamicruledialog.cpp @@ -22,7 +22,6 @@ */ #include "dynamicruledialog.h" -#include "models/musiclibrarymodel.h" #include "support/localize.h" static const int constMinDate=1800; @@ -55,7 +54,7 @@ DynamicRuleDialog::DynamicRuleDialog(QWidget *parent) QSet composers; QSet albums; QSet genres; - MusicLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres); + //MusicLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres); QStringList strings=artists.toList(); strings.sort(); diff --git a/gui/albumspage.cpp b/gui/albumspage.cpp deleted file mode 100644 index 9f0284896..000000000 --- a/gui/albumspage.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "albumspage.h" -#include "mpd-interface/mpdconnection.h" -#include "covers.h" -#include "models/musiclibrarymodel.h" -#include "models/musiclibraryitemsong.h" -#include "models/albumsmodel.h" -#include "support/localize.h" -#include "support/messagebox.h" -#include "settings.h" -#include "stdactions.h" -#include "support/utils.h" - -AlbumsPage::AlbumsPage(QWidget *p) - : QWidget(p) -{ - setupUi(this); - addToPlayQueue->setDefaultAction(StdActions::self()->addToPlayQueueAction); - replacePlayQueue->setDefaultAction(StdActions::self()->replacePlayQueueAction); - - view->addAction(StdActions::self()->addToPlayQueueAction); - view->addAction(StdActions::self()->replacePlayQueueAction); - view->addAction(StdActions::self()->addWithPriorityAction); - view->addAction(StdActions::self()->addToStoredPlaylistAction); - #ifdef TAGLIB_FOUND - #ifdef ENABLE_DEVICES_SUPPORT - view->addAction(StdActions::self()->copyToDeviceAction); - #endif - view->addAction(StdActions::self()->organiseFilesAction); - view->addAction(StdActions::self()->editTagsAction); - #ifdef ENABLE_REPLAYGAIN_SUPPORT - view->addAction(StdActions::self()->replaygainAction); - #endif - view->addAction(StdActions::self()->setCoverAction); - #ifdef ENABLE_DEVICES_SUPPORT - view->addSeparator(); - view->addAction(StdActions::self()->deleteSongsAction); - #endif - #endif // TAGLIB_FOUND - - proxy.setSourceModel(AlbumsModel::self()); - view->setModel(&proxy); - - connect(MusicLibraryModel::self(), SIGNAL(updateGenres(const QSet &)), genreCombo, SLOT(update(const QSet &))); - connect(this, SIGNAL(add(const QStringList &, bool, quint8)), MPDConnection::self(), SLOT(add(const QStringList &, bool, quint8))); - connect(this, SIGNAL(addSongsToPlaylist(const QString &, const QStringList &)), MPDConnection::self(), SLOT(addToPlaylist(const QString &, const QStringList &))); - connect(genreCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchItems())); - connect(view, SIGNAL(searchItems()), this, SLOT(searchItems())); - connect(view, SIGNAL(itemsSelected(bool)), this, SLOT(controlActions())); - connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &))); - connect(view, SIGNAL(rootIndexSet(QModelIndex)), this, SLOT(updateGenres(QModelIndex))); - connect(MPDConnection::self(), SIGNAL(updatingLibrary(time_t)), view, SLOT(updating())); - connect(MPDConnection::self(), SIGNAL(updatedLibrary()), view, SLOT(updated())); - connect(MPDConnection::self(), SIGNAL(updatingDatabase()), view, SLOT(updating())); - connect(MPDConnection::self(), SIGNAL(updatedDatabase()), view, SLOT(updated())); - view->load(metaObject()->className()); -} - -AlbumsPage::~AlbumsPage() -{ - view->save(metaObject()->className()); -} - -void AlbumsPage::showEvent(QShowEvent *e) -{ - view->focusView(); - QWidget::showEvent(e); -} - -void AlbumsPage::clear() -{ - AlbumsModel::self()->clear(); - view->update(); -} - -QStringList AlbumsPage::selectedFiles(bool allowPlaylists) const -{ - QModelIndexList selected = view->selectedIndexes(); - if (selected.isEmpty()) { - return QStringList(); - } - return AlbumsModel::self()->filenames(proxy.mapToSource(selected, proxy.enabled() && Settings::self()->filteredOnly()), allowPlaylists); -} - -QList AlbumsPage::selectedSongs(bool allowPlaylists) const -{ - QModelIndexList selected = view->selectedIndexes(); - if (selected.isEmpty()) { - return QList(); - } - return AlbumsModel::self()->songs(proxy.mapToSource(selected, proxy.enabled() && Settings::self()->filteredOnly()), allowPlaylists); -} - -Song AlbumsPage::coverRequest() const -{ - QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here... - - if (1==selected.count()) { - QList songs=AlbumsModel::self()->songs(QModelIndexList() << proxy.mapToSource(selected.at(0)), false); - if (!songs.isEmpty()) { - return songs.at(0); - } - } - return Song(); -} - -void AlbumsPage::addSelectionToPlaylist(const QString &name, bool replace, quint8 priorty) -{ - QStringList files=selectedFiles(name.isEmpty()); - - if (!files.isEmpty()) { - if (name.isEmpty()) { - emit add(files, replace, priorty); - } else { - emit addSongsToPlaylist(name, files); - } - view->clearSelection(); - } -} - -#ifdef ENABLE_DEVICES_SUPPORT -void AlbumsPage::addSelectionToDevice(const QString &udi) -{ - QList songs=selectedSongs(); - - if (!songs.isEmpty()) { - emit addToDevice(QString(), udi, songs); - view->clearSelection(); - } -} - -void AlbumsPage::deleteSongs() -{ - QList songs=selectedSongs(); - - if (!songs.isEmpty()) { - if (MessageBox::Yes==MessageBox::warningYesNo(this, i18n("Are you sure you wish to delete the selected songs?\n\nThis cannot be undone."), - i18n("Delete Songs"), StdGuiItem::del(), StdGuiItem::cancel())) { - emit deleteSongs(QString(), songs); - } - view->clearSelection(); - } -} -#endif - -void AlbumsPage::itemDoubleClicked(const QModelIndex &) -{ - if (1==view->selectedIndexes(false).size()) {//doubleclick should only have one selected item - addSelectionToPlaylist(); - } -} - -void AlbumsPage::searchItems() -{ - QString text=view->searchText().trimmed(); - proxy.update(text, genreCombo->currentIndex()<=0 ? QString() : genreCombo->currentText()); - if (proxy.enabled() && !proxy.filterText().isEmpty()) { - view->expandAll(); - } -} - -void AlbumsPage::updateGenres(const QModelIndex &idx) -{ - if (idx.isValid()) { - QModelIndex m=proxy.mapToSource(idx); - if (m.isValid() && static_cast(m.internalPointer())->isAlbum()) { - genreCombo->update(static_cast(m.internalPointer())->genres); - return; - } - } - genreCombo->update(MusicLibraryModel::self()->genres()); -} - -void AlbumsPage::controlActions() -{ - QModelIndexList selected=view->selectedIndexes(false); // Dont need sorted selection here... - bool enable=selected.count()>0; - - StdActions::self()->addToPlayQueueAction->setEnabled(enable); - StdActions::self()->addWithPriorityAction->setEnabled(enable); - StdActions::self()->replacePlayQueueAction->setEnabled(enable); - StdActions::self()->addToStoredPlaylistAction->setEnabled(enable); - #ifdef TAGLIB_FOUND - StdActions::self()->organiseFilesAction->setEnabled(enable && MPDConnection::self()->getDetails().dirReadable); - StdActions::self()->editTagsAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled()); - #ifdef ENABLE_REPLAYGAIN_SUPPORT - StdActions::self()->replaygainAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled()); - #endif - #ifdef ENABLE_DEVICES_SUPPORT - StdActions::self()->deleteSongsAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled()); - StdActions::self()->copyToDeviceAction->setEnabled(StdActions::self()->organiseFilesAction->isEnabled()); - #endif - #endif // TAGLIB_FOUND - StdActions::self()->setCoverAction->setEnabled(1==selected.count() && static_cast(proxy.mapToSource(selected.at(0)).internalPointer())->isAlbum()); -} diff --git a/gui/albumspage.h b/gui/albumspage.h deleted file mode 100644 index 6ba9563d2..000000000 --- a/gui/albumspage.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef ALBUMSPAGE_H -#define ALBUMSPAGE_H - -#include "ui_albumspage.h" -#include "models/albumsproxymodel.h" -#include "page.h" - -class Action; -class MusicLibraryItemRoot; - -class AlbumsPage : public QWidget, public Ui::AlbumsPage, public Page -{ - Q_OBJECT - -public: - AlbumsPage(QWidget *p); - virtual ~AlbumsPage(); - - void clear(); - QStringList selectedFiles(bool allowPlaylists=false) const; - QList selectedSongs(bool allowPlaylists=false) const; - Song coverRequest() const; - void addSelectionToPlaylist(const QString &name=QString(), bool replace=false, quint8 priorty=0); - #ifdef ENABLE_DEVICES_SUPPORT - void addSelectionToDevice(const QString &udi); - void deleteSongs(); - #endif - void setView(int v) { view->setMode((ItemView::Mode)v); } - ItemView::Mode viewMode() const { return view->viewMode(); } - void focusSearch() { view->focusSearch(); } - void goTop() { view->goToTop(); } - void showEvent(QShowEvent *e); - void resort() { proxy.resort(); } - -private: - void setItemSize(int v); - -Q_SIGNALS: - // These are for communicating with MPD object (which is in its own thread, so need to talk via signal/slots) - void add(const QStringList &files, bool replace, quint8 priorty); - void addSongsToPlaylist(const QString &name, const QStringList &files); - - void addToDevice(const QString &from, const QString &to, const QList &songs); - void deleteSongs(const QString &from, const QList &songs); - -public Q_SLOTS: - void itemDoubleClicked(const QModelIndex &); - void controlActions(); - void searchItems(); - void updateGenres(const QModelIndex &); - -private: - AlbumsProxyModel proxy; -}; - -#endif diff --git a/gui/albumspage.ui b/gui/albumspage.ui deleted file mode 100644 index cdd368171..000000000 --- a/gui/albumspage.ui +++ /dev/null @@ -1,71 +0,0 @@ - - - AlbumsPage - - - - 0 - 0 - 204 - 151 - - - - - 0 - - - 0 - - - - - - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - ItemView - QTreeView -
    widgets/itemview.h
    -
    - - GenreCombo - QComboBox -
    widgets/genrecombo.h
    -
    - - ToolButton - QToolButton -
    widgets/toolbutton.h
    -
    - - SizeWidget - QWidget -
    widgets/sizewidget.h
    -
    -
    - - -
    diff --git a/gui/cachesettings.cpp b/gui/cachesettings.cpp index e9e9231dd..38a01cffa 100644 --- a/gui/cachesettings.cpp +++ b/gui/cachesettings.cpp @@ -29,7 +29,6 @@ #include "context/contextwidget.h" #include "context/wikipediasettings.h" #include "covers.h" -#include "models/musiclibrarymodel.h" #include "support/utils.h" #include "support/messagebox.h" #include "config.h" @@ -254,9 +253,7 @@ CacheSettings::CacheSettings(QWidget *parent) layout->setMargin(0); int row=0; int col=0; - QLabel *label=new QLabel(i18n("Cantata caches a local copy of the music listing to speed up loading of the library. Cantata might also have cached " - "covers, lyrics, or artist images, if these have been downloaded and could not be saved into the music folder (because " - "Cantata cannot access it, or you have configured Cantata to not save these items there). Below is a summary of Cantata's " + QLabel *label=new QLabel(i18n("Cantata caches various pieces of informat (covers, lyrics, etc). Below is a summary of Cantata's " "current cache usage."), this); label->setWordWrap(true); layout->addWidget(label, row++, col, 1, 2); @@ -265,8 +262,6 @@ CacheSettings::CacheSettings(QWidget *parent) tree=new CacheTree(this); layout->addWidget(tree, row++, col, 1, 2); - new CacheItem(i18n("Music Library"), Utils::cacheDir(MusicLibraryModel::constLibraryCache, false), - QStringList() << "*"+MusicLibraryModel::constLibraryExt << "*"+MusicLibraryModel::constLibraryCompressedExt, tree); new CacheItem(i18n("Covers"), Utils::cacheDir(Covers::constCoverDir, false), QStringList() << "*.jpg" << "*.png", tree, CacheItem::Type_Covers); new CacheItem(i18n("Scaled Covers"), Utils::cacheDir(Covers::constScaledCoverDir, false), QStringList() << "*.jpg" << "*.png", tree, @@ -281,8 +276,6 @@ CacheSettings::CacheSettings(QWidget *parent) new CacheItem(i18n("Stream Listings"), Utils::cacheDir(StreamsModel::constSubDir, false), QStringList() << "*"+StreamsModel::constCacheExt, tree); #endif #ifdef ENABLE_ONLINE_SERVICES - new CacheItem(i18n("Jamendo"), Utils::cacheDir("jamendo", false), QStringList() << "*"+MusicLibraryModel::constLibraryCompressedExt << "*.jpg" << "*.png", tree); - new CacheItem(i18n("Magnatune"), Utils::cacheDir("magnatune", false), QStringList() << "*"+MusicLibraryModel::constLibraryCompressedExt << "*.jpg" << "*.png", tree); new CacheItem(i18n("Podcast Directories"), Utils::cacheDir(PodcastSearchDialog::constCacheDir, false), QStringList() << "*"+PodcastSearchDialog::constExt, tree); #endif new CacheItem(i18n("Wikipedia Languages"), Utils::cacheDir(WikipediaSettings::constSubDir, false), QStringList() << "*.xml.gz", tree); diff --git a/gui/covers.cpp b/gui/covers.cpp index fdee7f3a6..d9a0feb23 100644 --- a/gui/covers.cpp +++ b/gui/covers.cpp @@ -680,7 +680,7 @@ void CoverDownloader::downloadViaLastFm(Job &job) QUrlQuery query; #endif - query.addQueryItem("method", job.song.isArtistImageRequest() ? "artist.getInfo" : "album.getInfo"); + query.addQueryItem("method", job.song.isArtistImageRequest() || job.song.isComposerImageRequest() ? "artist.getInfo" : "album.getInfo"); query.addQueryItem("api_key", Covers::constLastFmApiKey); query.addQueryItem("autocorrect", "1"); query.addQueryItem("artist", Covers::fixArtist(job.song.albumArtist())); @@ -1680,35 +1680,36 @@ Covers::Image Covers::locateImage(const Song &song) if (song.isArtistImageRequest() || song.isComposerImageRequest()) { QString artistOrComposer=encodeName(song.isComposerImageRequest() ? song.composer() : song.albumArtist()); - - // For non-MPD tracks, see if we actually have a saved MPD cover... - if (MPDConnection::self()->getDetails().dirReadable) { - QString songDir=artistOrComposer+Utils::constDirSep; - if (!song.file.startsWith(songDir)) { - QString dirName=MPDConnection::self()->getDetails().dir+songDir; - if (QDir(dirName).exists()) { - foreach (const QString &fileName, coverFileNames) { - DBUG_CLASS("Covers") << "Checking file" << QString(dirName+fileName); - if (QFile::exists(dirName+fileName)) { - QImage img=loadImage(dirName+fileName); - if (!img.isNull()) { - DBUG_CLASS("Covers") << "Got artist/composer image" << QString(dirName+fileName); - return Image(img, dirName+fileName); + if (!artistOrComposer.isEmpty()) { + // For non-MPD tracks, see if we actually have a saved MPD cover... + if (MPDConnection::self()->getDetails().dirReadable) { + QString songDir=artistOrComposer+Utils::constDirSep; + if (!song.file.startsWith(songDir)) { + QString dirName=MPDConnection::self()->getDetails().dir+songDir; + if (QDir(dirName).exists()) { + foreach (const QString &fileName, coverFileNames) { + DBUG_CLASS("Covers") << "Checking file" << QString(dirName+fileName); + if (QFile::exists(dirName+fileName)) { + QImage img=loadImage(dirName+fileName); + if (!img.isNull()) { + DBUG_CLASS("Covers") << "Got artist/composer image" << QString(dirName+fileName); + return Image(img, dirName+fileName); + } } } } } } - } - // Check if cover is already cached - QString dir(Utils::cacheDir(constCoverDir, false)); - for (int e=0; constExtensions[e]; ++e) { - DBUG_CLASS("Covers") << "Checking cache file" << QString(dir+artistOrComposer+constExtensions[e]); - if (QFile::exists(dir+artistOrComposer+constExtensions[e])) { - QImage img=loadImage(dir+artistOrComposer+constExtensions[e]); - if (!img.isNull()) { - DBUG_CLASS("Covers") << "Got cached artist/composer image" << QString(dir+artistOrComposer+constExtensions[e]); - return Image(img, dir+artistOrComposer+constExtensions[e]); + // Check if cover is already cached + QString dir(Utils::cacheDir(constCoverDir, false)); + for (int e=0; constExtensions[e]; ++e) { + DBUG_CLASS("Covers") << "Checking cache file" << QString(dir+artistOrComposer+constExtensions[e]); + if (QFile::exists(dir+artistOrComposer+constExtensions[e])) { + QImage img=loadImage(dir+artistOrComposer+constExtensions[e]); + if (!img.isNull()) { + DBUG_CLASS("Covers") << "Got cached artist/composer image" << QString(dir+artistOrComposer+constExtensions[e]); + return Image(img, dir+artistOrComposer+constExtensions[e]); + } } } } @@ -1895,7 +1896,7 @@ void Covers::gotAlbumCover(const Song &song, const QImage &img, const QString &f if (!img.isNull()) { updatedCover=updateCache(song, img, true); } - if (updatedCover || !song.isSpecificSizeRequest()) { + if (updatedCover/* || !song.isSpecificSizeRequest()*/) { DBUG << "emit cover" << song.file << song.artist << song.albumartist << song.album << song.mbAlbumId() << img.width() << img.height() << fileName; emit cover(song, img, fileName.startsWith(constCoverInTagPrefix) ? QString() : fileName); } @@ -1915,10 +1916,10 @@ void Covers::gotArtistImage(const Song &song, const QImage &img, const QString & if (!img.isNull()) { updateCache(song, img, true); } - if (!song.isSpecificSizeRequest()) { +// if (!song.isSpecificSizeRequest()) { DBUG << "emit artistImage" << song.file << song.artist << song.albumartist << song.album << img.width() << img.height() << fileName; emit artistImage(song, img, fileName.startsWith(constCoverInTagPrefix) ? QString() : fileName); - } +// } } } @@ -1935,10 +1936,10 @@ void Covers::gotComposerImage(const Song &song, const QImage &img, const QString if (!img.isNull()) { updateCache(song, img, true); } - if (!song.isSpecificSizeRequest()) { +// if (!song.isSpecificSizeRequest()) { DBUG << "emit composerImage" << song.file << song.artist << song.albumartist << song.album << song.composer() << img.width() << img.height() << fileName; emit composerImage(song, img, fileName.startsWith(constCoverInTagPrefix) ? QString() : fileName); - } +// } } } diff --git a/gui/folderpage.cpp b/gui/folderpage.cpp index 280d4f1ec..a4dc563d6 100644 --- a/gui/folderpage.cpp +++ b/gui/folderpage.cpp @@ -23,7 +23,6 @@ #include "folderpage.h" #include "mpd-interface/mpdconnection.h" -#include "models/musiclibrarymodel.h" #include "settings.h" #include "support/localize.h" #include "support/messagebox.h" @@ -103,7 +102,7 @@ void FolderPage::refresh() if (DirViewModel::self()->isEnabled()) { if (!isVisible()) { loaded=false; // Refresh called for, but we are not currently visible... - } else if (!DirViewModel::self()->fromXML()) { + } else { emit loadFolders(); loaded=true; } @@ -121,9 +120,7 @@ void FolderPage::showEvent(QShowEvent *e) view->focusView(); QWidget::showEvent(e); if (!loaded) { - if (!DirViewModel::self()->fromXML()) { - emit loadFolders(); - } + emit loadFolders(); loaded=true; } } @@ -193,22 +190,24 @@ void FolderPage::openFileManager() QList FolderPage::selectedSongs(EmptySongMod esMod, bool allowPlaylists) const { - QList songs=MusicLibraryModel::self()->songs(selectedFiles(allowPlaylists), ES_None!=esMod); +// QList songs=MusicLibraryModel::self()->songs(selectedFiles(allowPlaylists), ES_None!=esMod); - if (ES_None!=esMod) { - QList::Iterator it(songs.begin()); - QList::Iterator end(songs.end()); - for (; it!=end; ++it) { - if ((*it).isEmpty()) { - if (ES_GuessTags==esMod) { - (*it).guessTags(); - } - (*it).fillEmptyFields(); - } - } - } +// if (ES_None!=esMod) { +// QList::Iterator it(songs.begin()); +// QList::Iterator end(songs.end()); +// for (; it!=end; ++it) { +// if ((*it).isEmpty()) { +// if (ES_GuessTags==esMod) { +// (*it).guessTags(); +// } +// (*it).fillEmptyFields(); +// } +// } +// } - return songs; +// return songs; + return QList(); + // TODO!!!! } QStringList FolderPage::selectedFiles(bool allowPlaylists) const diff --git a/gui/interfacesettings.cpp b/gui/interfacesettings.cpp index 0d52b833c..346010181 100644 --- a/gui/interfacesettings.cpp +++ b/gui/interfacesettings.cpp @@ -23,8 +23,7 @@ #include "interfacesettings.h" #include "settings.h" -#include "models/musiclibraryitemalbum.h" -#include "models/albumsmodel.h" +#include "models/sqllibrarymodel.h" #include "support/localize.h" #include "support/utils.h" #include "support/fancytabwidget.h" @@ -32,6 +31,7 @@ #include "widgets/basicitemdelegate.h" #include "widgets/playqueueview.h" #include "widgets/itemview.h" +#include "db/librarydb.h" #include #ifndef ENABLE_KDE_SUPPORT #include @@ -53,14 +53,27 @@ w->deleteLater(); \ w=0; -static void addAlbumSorts(QComboBox *box) +static void addLibraryGrouping(QComboBox *box) { - box->addItem(i18n("Album, Artist, Year"), AlbumsModel::Sort_AlbumArtist); - box->addItem(i18n("Album, Year, Artist"), AlbumsModel::Sort_AlbumYear); - box->addItem(i18n("Artist, Album, Year"), AlbumsModel::Sort_ArtistAlbum); - box->addItem(i18n("Artist, Year, Album"), AlbumsModel::Sort_ArtistYear); - box->addItem(i18n("Year, Album, Artist"), AlbumsModel::Sort_YearAlbum); - box->addItem(i18n("Year, Artist, Album"), AlbumsModel::Sort_YearArtist); + box->addItem(i18n("Genre"), SqlLibraryModel::constGroupGenre); + box->addItem(i18n("Artist"), SqlLibraryModel::constGroupArtist); + box->addItem(i18n("Album"), SqlLibraryModel::constGroupAlbum); +} + +static void addAlbumSorts(QComboBox *box, bool albumsOnly) +{ + box->clear(); + if (albumsOnly) { + box->addItem(i18n("Album, Artist, Year"), LibraryDb::constAlbumsSortAlArYr); + box->addItem(i18n("Album, Year, Artist"), LibraryDb::constAlbumsSortAlYrAr); + box->addItem(i18n("Artist, Album, Year"), LibraryDb::constAlbumsSortArAlYr); + box->addItem(i18n("Artist, Year, Album"), LibraryDb::constAlbumsSortArYrAl); + box->addItem(i18n("Year, Album, Artist"), LibraryDb::constAlbumsSortYrAlAr); + box->addItem(i18n("Year, Artist, Album"), LibraryDb::constAlbumsSortYrArAl); + } else { + box->addItem(i18n("Name"), LibraryDb::constArtistAlbumsSortName); + box->addItem(i18n("Year"), LibraryDb::constArtistAlbumsSortYear); + } } static QString viewTypeString(ItemView::Mode mode) @@ -94,11 +107,26 @@ static void selectEntry(QComboBox *box, int v) } } +static void selectEntry(QComboBox *box, const QString &v) +{ + for (int i=1; icount(); ++i) { + if (box->itemData(i).toString()==v) { + box->setCurrentIndex(i); + return; + } + } +} + static inline int getValue(QComboBox *box) { return box->itemData(box->currentIndex()).toInt(); } +static inline QString getStrValue(QComboBox *box) +{ + return box->itemData(box->currentIndex()).toString(); +} + static const char * constValueProperty="value"; InterfaceSettings::InterfaceSettings(QWidget *p) @@ -133,17 +161,15 @@ InterfaceSettings::InterfaceSettings(QWidget *p) QList standardViews=QList() << ItemView::Mode_BasicTree << ItemView::Mode_SimpleTree << ItemView::Mode_DetailedTree << ItemView::Mode_List; addViewTypes(libraryView, QList() << standardViews << ItemView::Mode_IconTop); - addViewTypes(albumsView, QList() << standardViews << ItemView::Mode_IconTop); addViewTypes(folderView, standardViews); addViewTypes(playlistsView, QList() << standardViews << ItemView::Mode_GroupedTree << ItemView::Mode_Table); addViewTypes(searchView, QList() << ItemView::Mode_List << ItemView::Mode_Table); addViewTypes(playQueueView, QList() << ItemView::Mode_GroupedTree << ItemView::Mode_Table); - addAlbumSorts(albumSort); + addLibraryGrouping(libraryGrouping); addView(i18n("Play Queue"), QLatin1String("PlayQueuePage")); - addView(i18n("Artists"), QLatin1String("LibraryPage")); - addView(i18n("Albums"), QLatin1String("AlbumsPage")); + addView(i18n("Library"), QLatin1String("LibraryPage")); addView(i18n("Folders"), QLatin1String("FolderPage")); addView(i18n("Playlists"), QLatin1String("PlaylistsPage")); #ifdef ENABLE_DYNAMIC @@ -266,15 +292,15 @@ InterfaceSettings::InterfaceSettings(QWidget *p) sbLayout->addLayout(sbOther); sbLayout->addItem(new QSpacerItem(0, 2, QSizePolicy::Expanding, QSizePolicy::Minimum)); } + connect(libraryGrouping, SIGNAL(currentIndexChanged(int)), SLOT(libraryGroupingChanged())); } void InterfaceSettings::load() { libraryArtistImage->setChecked(Settings::self()->libraryArtistImage()); selectEntry(libraryView, Settings::self()->libraryView()); - libraryYear->setChecked(Settings::self()->libraryYear()); - selectEntry(albumsView, Settings::self()->albumsView()); - selectEntry(albumSort, Settings::self()->albumSort()); + selectEntry(libraryGrouping, Settings::self()->libraryGrouping()); + libraryGroupingChanged(); selectEntry(folderView, Settings::self()->folderView()); selectEntry(playlistsView, Settings::self()->playlistsView()); playListsStartClosed->setChecked(Settings::self()->playListsStartClosed()); @@ -284,9 +310,7 @@ void InterfaceSettings::load() #ifdef ENABLE_ONLINE_SERVICES selectEntry(onlineView, Settings::self()->onlineView()); #endif - groupSingle->setChecked(Settings::self()->groupSingle()); composerGenres->setText(QStringList(Settings::self()->composerGenres().toList()).join(QString(Song::constGenreSep))); - filteredOnly->setChecked(Settings::self()->filteredOnly()); #ifdef ENABLE_DEVICES_SUPPORT showDeleteAction->setChecked(Settings::self()->showDeleteAction()); selectEntry(devicesView, Settings::self()->devicesView()); @@ -365,9 +389,12 @@ void InterfaceSettings::save() { Settings::self()->saveLibraryArtistImage(libraryArtistImage->isChecked()); Settings::self()->saveLibraryView(getValue(libraryView)); - Settings::self()->saveLibraryYear(libraryYear->isChecked()); - Settings::self()->saveAlbumsView(getValue(albumsView)); - Settings::self()->saveAlbumSort(getValue(albumSort)); + Settings::self()->saveLibraryGrouping(getStrValue(libraryGrouping)); + if (SqlLibraryModel::constGroupAlbum==Settings::self()->libraryGrouping()) { + Settings::self()->saveLibraryAlbumSort(getStrValue(librarySort)); + } else { + Settings::self()->saveLibrarySort(getStrValue(librarySort)); + } Settings::self()->saveFolderView(getValue(folderView)); Settings::self()->savePlaylistsView(getValue(playlistsView)); Settings::self()->savePlayListsStartClosed(playListsStartClosed->isChecked()); @@ -377,9 +404,7 @@ void InterfaceSettings::save() #ifdef ENABLE_ONLINE_SERVICES Settings::self()->saveOnlineView(getValue(onlineView)); #endif - Settings::self()->saveGroupSingle(groupSingle->isChecked()); Settings::self()->saveComposerGenres(composerGenres->text().trimmed().split(Song::constGenreSep).toSet()); - Settings::self()->saveFilteredOnly(filteredOnly->isChecked()); #ifdef ENABLE_DEVICES_SUPPORT Settings::self()->saveShowDeleteAction(showDeleteAction->isChecked()); Settings::self()->saveDevicesView(getValue(devicesView)); @@ -533,6 +558,14 @@ void InterfaceSettings::addView(const QString &v, const QString &prop) item->setData(Qt::UserRole, prop); } +void InterfaceSettings::libraryGroupingChanged() +{ + addAlbumSorts(librarySort, SqlLibraryModel::constGroupAlbum==getStrValue(libraryGrouping)); + selectEntry(librarySort, SqlLibraryModel::constGroupAlbum==Settings::self()->libraryGrouping() + ? Settings::self()->libraryAlbumSort() + : Settings::self()->librarySort()); +} + void InterfaceSettings::libraryViewChanged() { int vt=getValue(libraryView); diff --git a/gui/interfacesettings.h b/gui/interfacesettings.h index 6c2a018a2..16a2b44c5 100644 --- a/gui/interfacesettings.h +++ b/gui/interfacesettings.h @@ -48,6 +48,7 @@ private: void addView(const QString &v, const QString &prop); private Q_SLOTS: + void libraryGroupingChanged(); void libraryViewChanged(); void playlistsViewChanged(); void playQueueViewChanged(); diff --git a/gui/interfacesettings.ui b/gui/interfacesettings.ui index a11320cae..172928d17 100644 --- a/gui/interfacesettings.ui +++ b/gui/interfacesettings.ui @@ -136,19 +136,19 @@ - + - Artists + Library - + QFormLayout::ExpandingFieldsGrow Qt::AlignJustify|Qt::AlignTop - + - + Style: @@ -160,14 +160,37 @@ - - + + - Sort albums by year + Group by: + + + libraryGrouping - + + + + + + + Sort albums by: + + + librarySort + + + + + + + QComboBox::AdjustToContents + + + + Show artist images @@ -178,47 +201,6 @@ - - - Albums - - - - QFormLayout::ExpandingFieldsGrow - - - Qt::AlignJustify|Qt::AlignTop - - - - - Style: - - - albumsView - - - - - - - - - - Sort: - - - albumSort - - - - - - - - - - Playlists @@ -253,7 +235,7 @@ removeWidget - + Other Views @@ -584,65 +566,8 @@
    - - - Grouping - - - - - - QFormLayout::ExpandingFieldsGrow - - - - - Place albums with only one track under 'Various Artists/Single Tracks' - - - - - - - Use the 'Composer' tag, and not 'Artist' or 'Album Artist', to group albums. - - - Group albums by composer for genres: - - - composerGenres - - - - - - - - - - - - Genres should be separated via a comma (e.g. 'Classical, Symphonic') - - - - - - - Qt::Vertical - - - - 16 - 16 - - - - - - - + l Toolbar @@ -759,16 +684,22 @@ QFormLayout::ExpandingFieldsGrow - - - - <p>When adding artists, or albums to the playqueue, or editing tags, etc, then only use songs that pass the current Genre or string filter.</p><p>e.g. If you have an album with 10 songs, but only 5 have the genre set to 'Metal', then only these 5 would be added if the view's Genre filter was set to 'Metal'. - - - Only act on songs that pass filtering + + + + Use the 'Composer' tag, and not 'Artist' or 'Album Artist', to group albums. (List should be comma spearated - e.g. 'Classical, Symphonic') + + + Group albums by composer for genres: + + + composerGenres + + + diff --git a/gui/librarypage.cpp b/gui/librarypage.cpp index 67d33ee27..60c6f4397 100644 --- a/gui/librarypage.cpp +++ b/gui/librarypage.cpp @@ -25,15 +25,12 @@ #include "mpd-interface/mpdconnection.h" #include "mpd-interface/mpdstats.h" #include "covers.h" -#include "models/musiclibrarymodel.h" -#include "models/musiclibraryitemartist.h" -#include "models/musiclibraryitemalbum.h" -#include "models/musiclibraryitemsong.h" #include "support/localize.h" #include "support/messagebox.h" #include "settings.h" #include "stdactions.h" #include "support/utils.h" +#include "models/mpdlibrarymodel.h" LibraryPage::LibraryPage(QWidget *p) : QWidget(p) @@ -64,19 +61,15 @@ LibraryPage::LibraryPage(QWidget *p) connect(this, SIGNAL(add(const QStringList &, bool, quint8)), MPDConnection::self(), SLOT(add(const QStringList &, bool, quint8))); connect(this, SIGNAL(addSongsToPlaylist(const QString &, const QStringList &)), MPDConnection::self(), SLOT(addToPlaylist(const QString &, const QStringList &))); - connect(genreCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchItems())); connect(MPDConnection::self(), SIGNAL(updatingLibrary(time_t)), view, SLOT(updating())); connect(MPDConnection::self(), SIGNAL(updatedLibrary()), view, SLOT(updated())); connect(MPDConnection::self(), SIGNAL(updatingDatabase()), view, SLOT(updating())); connect(MPDConnection::self(), SIGNAL(updatedDatabase()), view, SLOT(updated())); - connect(MusicLibraryModel::self(), SIGNAL(updateGenres(const QSet &)), genreCombo, SLOT(update(const QSet &))); connect(this, SIGNAL(loadLibrary()), MPDConnection::self(), SLOT(loadLibrary())); connect(view, SIGNAL(itemsSelected(bool)), this, SLOT(controlActions())); connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &))); connect(view, SIGNAL(searchItems()), this, SLOT(searchItems())); - connect(view, SIGNAL(rootIndexSet(QModelIndex)), this, SLOT(updateGenres(QModelIndex))); - proxy.setSourceModel(MusicLibraryModel::self()); - view->setModel(&proxy); + view->setModel(MpdLibraryModel::self()); view->load(metaObject()->className()); } @@ -94,14 +87,11 @@ void LibraryPage::showEvent(QShowEvent *e) void LibraryPage::refresh() { view->goToTop(); - if (!MusicLibraryModel::self()->fromXML()) { - emit loadLibrary(); - } + // TODO: Is this used?? } void LibraryPage::clear() { - MusicLibraryModel::self()->clear(); view->goToTop(); } @@ -116,7 +106,8 @@ QStringList LibraryPage::selectedFiles(bool allowPlaylists) const if (selected.isEmpty()) { return QStringList(); } - return MusicLibraryModel::self()->filenames(proxy.mapToSource(selected, proxy.enabled() && Settings::self()->filteredOnly()), allowPlaylists); + return MpdLibraryModel::self()->filenames(selected, allowPlaylists); + return QStringList(); } QList LibraryPage::selectedSongs(bool allowPlaylists) const @@ -125,7 +116,8 @@ QList LibraryPage::selectedSongs(bool allowPlaylists) const if (selected.isEmpty()) { return QList(); } - return MusicLibraryModel::self()->songs(proxy.mapToSource(selected, proxy.enabled() && Settings::self()->filteredOnly()), allowPlaylists); + return MpdLibraryModel::self()->songs(selected, allowPlaylists); + return QList(); } Song LibraryPage::coverRequest() const @@ -133,13 +125,11 @@ Song LibraryPage::coverRequest() const QModelIndexList selected = view->selectedIndexes(false); // Dont need sorted selection here... if (1==selected.count()) { - QModelIndex idx=proxy.mapToSource(selected.at(0)); - QList songs=MusicLibraryModel::self()->songs(QModelIndexList() << idx, false); + QList songs=MpdLibraryModel::self()->songs(QModelIndexList() << selected.first(), false); if (!songs.isEmpty()) { Song s=songs.at(0); - if (MusicLibraryItem::Type_Artist==static_cast(idx.internalPointer())->itemType() && - !static_cast(idx.internalPointer())->isComposer()) { + if (SqlLibraryModel::T_Artist==static_cast(selected.first().internalPointer())->getType() && !s.useComposer()) { s.setArtistImageRequest(); } return s; @@ -201,24 +191,20 @@ void LibraryPage::showSongs(const QList &songs) return; } - genreCombo->setCurrentIndex(0); view->clearSearchText(); bool first=true; foreach (const Song &s, sngs) { - QModelIndex idx=MusicLibraryModel::self()->findSongIndex(s); + QModelIndex idx=MpdLibraryModel::self()->findSongIndex(s); if (idx.isValid()) { - QModelIndex p=proxy.mapFromSource(idx); - if (p.isValid()) { - if (ItemView::Mode_SimpleTree==view->viewMode() || ItemView::Mode_DetailedTree==view->viewMode() || first) { - view->showIndex(p, first); - } - if (first) { - first=false; - } - if (ItemView::Mode_SimpleTree!=view->viewMode() && ItemView::Mode_DetailedTree!=view->viewMode()) { - return; - } + if (ItemView::Mode_SimpleTree==view->viewMode() || ItemView::Mode_DetailedTree==view->viewMode() || first) { + view->showIndex(idx, first); + } + if (first) { + first=false; + } + if (ItemView::Mode_SimpleTree!=view->viewMode() && ItemView::Mode_DetailedTree!=view->viewMode()) { + return; } } } @@ -226,11 +212,9 @@ void LibraryPage::showSongs(const QList &songs) void LibraryPage::showArtist(const QString &artist) { - QModelIndex idx=MusicLibraryModel::self()->findArtistIndex(artist); + view->clearSearchText(); + QModelIndex idx=MpdLibraryModel::self()->findArtistIndex(artist); if (idx.isValid()) { - idx=proxy.mapFromSource(idx); - genreCombo->setCurrentIndex(0); - view->clearSearchText(); view->showIndex(idx, true); if (ItemView::Mode_SimpleTree==view->viewMode() || ItemView::Mode_DetailedTree==view->viewMode()) { view->setExpanded(idx); @@ -240,11 +224,9 @@ void LibraryPage::showArtist(const QString &artist) void LibraryPage::showAlbum(const QString &artist, const QString &album) { - QModelIndex idx=MusicLibraryModel::self()->findAlbumIndex(artist, album); + view->clearSearchText(); + QModelIndex idx=MpdLibraryModel::self()->findAlbumIndex(artist, album); if (idx.isValid()) { - idx=proxy.mapFromSource(idx); - genreCombo->setCurrentIndex(0); - view->clearSearchText(); view->showIndex(idx, true); if (ItemView::Mode_SimpleTree==view->viewMode() || ItemView::Mode_DetailedTree==view->viewMode()) { view->setExpanded(idx.parent()); @@ -259,37 +241,15 @@ void LibraryPage::itemDoubleClicked(const QModelIndex &) if (1!=selected.size()) { return; //doubleclick should only have one selected item } - MusicLibraryItem *item = static_cast(proxy.mapToSource(selected.at(0)).internalPointer()); - if (MusicLibraryItem::Type_Song==item->itemType()) { + SqlLibraryModel::Item *item = static_cast(selected.at(0).internalPointer()); + if (SqlLibraryModel::T_Track==item->getType()) { addSelectionToPlaylist(); } } void LibraryPage::searchItems() { - QString text=view->searchText().trimmed(); - proxy.update(text, genreCombo->currentIndex()<=0 ? QString() : genreCombo->currentText()); - if (proxy.enabled() && !text.isEmpty()) { - view->expandAll(); - } -} - -void LibraryPage::updateGenres(const QModelIndex &idx) -{ - if (idx.isValid()) { - QModelIndex m=proxy.mapToSource(idx); - if (m.isValid()) { - MusicLibraryItem::Type itemType=static_cast(m.internalPointer())->itemType(); - if (itemType==MusicLibraryItem::Type_Artist) { - genreCombo->update(static_cast(m.internalPointer())->genres()); - return; - } else if (itemType==MusicLibraryItem::Type_Album) { - genreCombo->update(static_cast(m.internalPointer())->genres()); - return; - } - } - } - genreCombo->update(MusicLibraryModel::self()->genres()); + MpdLibraryModel::self()->search(view->searchText().trimmed()); } void LibraryPage::controlActions() @@ -314,10 +274,10 @@ void LibraryPage::controlActions() #endif // TAGLIB_FOUND if (1==selected.count()) { - MusicLibraryItem *item=static_cast(proxy.mapToSource(selected.at(0)).internalPointer()); - MusicLibraryItem::Type type=item->itemType(); - StdActions::self()->setCoverAction->setEnabled((MusicLibraryItem::Type_Artist==type && !static_cast(item)->isComposer()) || - MusicLibraryItem::Type_Album==type); + SqlLibraryModel::Item *item=static_cast(selected.at(0).internalPointer()); + SqlLibraryModel::Type type=item->getType(); + StdActions::self()->setCoverAction->setEnabled((SqlLibraryModel::T_Artist==type/* && !static_cast(item)->isComposer()*/) || + SqlLibraryModel::T_Album==type); } else { StdActions::self()->setCoverAction->setEnabled(false); } @@ -325,7 +285,7 @@ void LibraryPage::controlActions() bool allowRandomAlbum=!selected.isEmpty(); if (allowRandomAlbum) { foreach (const QModelIndex &idx, selected) { - if (MusicLibraryItem::Type_Song==static_cast(proxy.mapToSource(idx).internalPointer())->itemType()) { + if (SqlLibraryModel::T_Track==static_cast(idx.internalPointer())->getType()) { allowRandomAlbum=false; break; } diff --git a/gui/librarypage.h b/gui/librarypage.h index 6a87af5b1..c9a06d692 100644 --- a/gui/librarypage.h +++ b/gui/librarypage.h @@ -25,7 +25,6 @@ #define LIBRARYPAGE_H #include "ui_librarypage.h" -#include "models/musiclibraryproxymodel.h" #include "page.h" class Action; @@ -55,7 +54,6 @@ public: void showAlbum(const QString &artist, const QString &album); void focusSearch() { view->focusSearch(); } void showEvent(QShowEvent *e); - void resort() { proxy.resort(); } private: void setItemSize(int v); @@ -73,10 +71,6 @@ public Q_SLOTS: void itemDoubleClicked(const QModelIndex &); void searchItems(); void controlActions(); - void updateGenres(const QModelIndex &); - -private: - MusicLibraryProxyModel proxy; }; #endif diff --git a/gui/librarypage.ui b/gui/librarypage.ui index 151a3dc9c..bdee2892c 100644 --- a/gui/librarypage.ui +++ b/gui/librarypage.ui @@ -29,7 +29,17 @@ 0 - + + + Qt::Horizontal + + + + 16 + 20 + + + @@ -50,11 +60,6 @@ QTreeView
    widgets/itemview.h
    - - GenreCombo - QComboBox -
    widgets/genrecombo.h
    -
    ToolButton QToolButton diff --git a/gui/main.cpp b/gui/main.cpp index 15cfdb326..9f2bc2b7d 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -41,6 +41,7 @@ #include "mainwindow.h" #include "mpd-interface/song.h" #include "support/thread.h" +#include "db/librarydb.h" // To enable debug... #include "mpd-interface/mpdconnection.h" @@ -49,7 +50,6 @@ #include "context/wikipediaengine.h" #include "context/lastfmengine.h" #include "context/metaengine.h" -#include "context/backdropcreator.h" #ifdef ENABLE_DYNAMIC #include "dynamic/dynamic.h" #endif @@ -153,7 +153,7 @@ enum Debug { Dbg_Context_LastFm = 0x00000010, Dbg_Context_Meta = 0x00000020, Dbg_Context_Widget = 0x00000040, - Dbg_Context_Backdrop = 0x00000080, +// Dbg_Context_Backdrop = 0x00000080, Dbg_Dynamic = 0x00000100, Dbg_StreamFetching = 0x00000200, Dbg_HttpServer = 0x00000400, @@ -164,10 +164,11 @@ enum Debug { Dbg_Tags = 0x00008000, Dbg_Scrobbling = 0x00010000, Dbg_Devices = 0x00020000, - DBG_Other = 0x00040000, + Dbg_Sql = 0x00040000, + DBG_Other = 0x00080000, // NOTE: MUST UPDATE Dbg_All IF ADD NEW ITEMS!!! - Dbg_All = 0x0007FFFF + Dbg_All = 0x000FFFFF }; static void installDebugMessageHandler() @@ -205,9 +206,6 @@ static void installDebugMessageHandler() if (dbg&Dbg_Context_Widget) { ContextWidget::enableDebug(); } - if (dbg&Dbg_Context_Backdrop) { - BackdropCreator::enableDebug(); - } #ifdef ENABLE_DYNAMIC if (dbg&Dbg_Dynamic) { Dynamic::enableDebug(); @@ -244,6 +242,9 @@ static void installDebugMessageHandler() DevicesModel::enableDebug(); } #endif + if (dbg&Dbg_Sql) { + LibraryDb::enableDebug(); + } #ifndef ENABLE_KDE_SUPPORT if (dbg&DBG_Other) { MediaKeys::enableDebug(); diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 51e79c77a..03b4be0e6 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -43,11 +43,10 @@ #include "settings.h" #include "support/utils.h" #include "support/touchproxystyle.h" -#include "models/musiclibrarymodel.h" #include "models/musiclibraryitemartist.h" #include "models/musiclibraryitemalbum.h" +#include "models/mpdlibrarymodel.h" #include "librarypage.h" -#include "albumspage.h" #include "folderpage.h" #ifdef ENABLE_STREAMS #include "streams/streamspage.h" @@ -60,7 +59,6 @@ #include "devices/devicespage.h" #include "models/devicesmodel.h" #include "devices/actiondialog.h" -#include "devices/syncdialog.h" #if defined CDDB_FOUND || defined MUSICBRAINZ5_FOUND #include "devices/audiocddevice.h" #endif @@ -195,7 +193,6 @@ MainWindow::MainWindow(QWidget *parent) messageWidget->hide(); // Need to set these values here, as used in library/device loading... - MPDParseUtils::setGroupSingle(Settings::self()->groupSingle()); Song::setComposerGenres(Settings::self()->composerGenres()); int hSpace=Utils::layoutSpacing(this); @@ -327,12 +324,6 @@ MainWindow::MainWindow(QWidget *parent) connect(cancelAction, SIGNAL(triggered()), messageWidget, SLOT(animatedHide())); connect(clearPlayQueueAction, SIGNAL(triggered()), messageWidget, SLOT(animatedHide())); connect(clearPlayQueueAction, SIGNAL(triggered()), this, SLOT(clearPlayQueue())); - clearNewStateAction = ActionCollection::get()->createAction("clearnewstate", i18n("Clear 'New' State Of Artists and Albums")); - connect(clearNewStateAction, SIGNAL(triggered()), MusicLibraryModel::self(), SLOT(clearNewState())); - connect(clearNewStateAction, SIGNAL(triggered()), AlbumsModel::self(), SLOT(clearNewState())); - connect(MusicLibraryModel::self(), SIGNAL(haveNewItems(bool)), clearNewStateAction, SLOT(setEnabled(bool))); - connect(MusicLibraryModel::self(), SIGNAL(error(QString)), this, SLOT(showError(QString))); - clearNewStateAction->setEnabled(false); StdActions::self()->playPauseTrackAction->setEnabled(false); StdActions::self()->nextTrackAction->setEnabled(false); @@ -388,16 +379,10 @@ MainWindow::MainWindow(QWidget *parent) tabWidget->addTab(playQueuePage, TAB_ACTION(showPlayQueueAction), playQueueInSidebar); connect(showPlayQueueAction, SIGNAL(triggered()), this, SLOT(showPlayQueue())); libraryPage = new LibraryPage(this); - addAction(libraryTabAction = ActionCollection::get()->createAction("showlibrarytab", i18n("Artists"), Icons::self()->artistsIcon)); + addAction(libraryTabAction = ActionCollection::get()->createAction("showlibrarytab", i18n("Library"), Icons::self()->libraryIcon)); libraryTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey)); tabWidget->addTab(libraryPage, TAB_ACTION(libraryTabAction), !hiddenPages.contains(libraryPage->metaObject()->className())); connect(libraryTabAction, SIGNAL(triggered()), this, SLOT(showLibraryTab())); - albumsPage = new AlbumsPage(this); - addAction(albumsTabAction = ActionCollection::get()->createAction("showalbumstab", i18n("Albums"), Icons::self()->albumsIcon)); - albumsTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey)); - tabWidget->addTab(albumsPage, TAB_ACTION(albumsTabAction), !hiddenPages.contains(albumsPage->metaObject()->className())); - connect(albumsTabAction, SIGNAL(triggered()), this, SLOT(showAlbumsTab())); - AlbumsModel::self()->setEnabled(!hiddenPages.contains(albumsPage->metaObject()->className())); folderPage = new FolderPage(this); addAction(foldersTabAction = ActionCollection::get()->createAction("showfolderstab", i18n("Folders"), Icons::self()->foldersIcon)); foldersTabAction->setShortcut(Qt::ControlModifier+Qt::ShiftModifier+nextKey(sidebarPageShortcutKey)); @@ -562,7 +547,6 @@ MainWindow::MainWindow(QWidget *parent) clearPlayQueueButton->setIcon(Icons::self()->clearListIcon); } #endif - MusicLibraryItemAlbum::setSortByDate(Settings::self()->libraryYear()); expandedSize=Settings::self()->mainWindowSize(); collapsedSize=Settings::self()->mainWindowCollapsedSize(); @@ -643,7 +627,6 @@ MainWindow::MainWindow(QWidget *parent) #else mainMenu->addAction(prefAction); mainMenu->addAction(refreshDbAction); - mainMenu->addAction(clearNewStateAction); mainMenu->addSeparator(); mainMenu->addAction(StdActions::self()->searchAction); mainMenu->addAction(searchPlayQueueAction); @@ -676,8 +659,6 @@ MainWindow::MainWindow(QWidget *parent) addMenuAction(menu, playQueueModel.undoAct()); addMenuAction(menu, playQueueModel.redoAct()); menu->addSeparator(); - addMenuAction(menu, clearNewStateAction); - menu->addSeparator(); addMenuAction(menu, StdActions::self()->searchAction); addMenuAction(menu, searchPlayQueueAction); #ifndef ENABLE_KDE_SUPPORT @@ -804,7 +785,6 @@ MainWindow::MainWindow(QWidget *parent) connect(DevicesModel::self(), SIGNAL(addToDevice(const QString &)), this, SLOT(addToDevice(const QString &))); connect(DevicesModel::self(), SIGNAL(error(const QString &)), this, SLOT(showError(const QString &))); connect(libraryPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &))); - connect(albumsPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &))); connect(folderPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &))); connect(playlistsPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &))); connect(devicesPage, SIGNAL(addToDevice(const QString &, const QString &, const QList &)), SLOT(copyToDevice(const QString &, const QString &, const QList &))); @@ -812,7 +792,6 @@ MainWindow::MainWindow(QWidget *parent) connect(StdActions::self()->deleteSongsAction, SIGNAL(triggered()), SLOT(deleteSongs())); connect(devicesPage, SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &))); connect(libraryPage, SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &))); - connect(albumsPage, SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &))); connect(folderPage, SIGNAL(deleteSongs(const QString &, const QList &)), SLOT(deleteSongs(const QString &, const QList &))); #endif connect(StdActions::self()->addPrioHighestAction, SIGNAL(triggered()), this, SLOT(addWithPriority())); @@ -1082,7 +1061,6 @@ void MainWindow::mpdConnectionStateChanged(bool connected) updateWindowTitle(); } else { libraryPage->clear(); - albumsPage->clear(); folderPage->clear(); playlistsPage->clear(); playQueueModel.clear(); @@ -1153,7 +1131,6 @@ void MainWindow::connectToMpd(const MPDConnectionDetails &details) { if (!MPDConnection::self()->isConnected() || details!=MPDConnection::self()->getDetails()) { libraryPage->clear(); - albumsPage->clear(); folderPage->clear(); playlistsPage->clear(); playQueueModel.clear(); @@ -1203,8 +1180,7 @@ void MainWindow::refreshDbPromp() void MainWindow::fullDbRefresh() { - MusicLibraryModel::self()->removeCache(); - DirViewModel::self()->removeCache(); + MpdLibraryModel::self()->clear(); } #ifdef ENABLE_KDE_SUPPORT @@ -1262,7 +1238,7 @@ bool MainWindow::canShowDialog() || TagEditor::instanceCount() || TrackOrganiser::instanceCount() #endif #ifdef ENABLE_DEVICES_SUPPORT - || ActionDialog::instanceCount() || SyncDialog::instanceCount() + || ActionDialog::instanceCount() #endif #ifdef ENABLE_REPLAYGAIN_SUPPORT || RgDialog::instanceCount() @@ -1309,7 +1285,7 @@ void MainWindow::quit() } #endif #ifdef ENABLE_DEVICES_SUPPORT - if (ActionDialog::instanceCount() || 0!=SyncDialog::instanceCount()) { + if (ActionDialog::instanceCount()) { return; } #endif @@ -1482,17 +1458,13 @@ void MainWindow::readSettings() CurrentCover::self()->setEnabled(Settings::self()->showPopups() || 0!=Settings::self()->playQueueBackground() || Settings::self()->showCoverWidget()); #endif checkMpdDir(); + MpdLibraryModel::self()->readSettings(); Covers::self()->readConfig(); HttpServer::self()->readConfig(); #ifdef ENABLE_DEVICES_SUPPORT StdActions::self()->deleteSongsAction->setVisible(Settings::self()->showDeleteAction()); #endif - MPDParseUtils::setGroupSingle(Settings::self()->groupSingle()); Song::setComposerGenres(Settings::self()->composerGenres()); - albumsPage->setView(Settings::self()->albumsView()); - AlbumsModel::self()->setAlbumSort(Settings::self()->albumSort()); - MusicLibraryItemAlbum::setSortByDate(Settings::self()->libraryYear()); - MusicLibraryModel::self()->readConfig(); libraryPage->setView(Settings::self()->libraryView()); playlistsPage->setView(Settings::self()->playlistsView()); #ifdef ENABLE_STREAMS @@ -1544,36 +1516,8 @@ void MainWindow::updateSettings() { connectToMpd(); Settings::self()->save(); - bool diffAlbumSort=AlbumsModel::self()->albumSort()!=Settings::self()->albumSort(); - bool diffLibYear=MusicLibraryItemAlbum::sortByDate()!=Settings::self()->libraryYear(); - bool diffGrouping=MPDParseUtils::groupSingle()!=Settings::self()->groupSingle() || - Song::composerGenres()!=Settings::self()->composerGenres(); - readSettings(); - if (diffGrouping) { - MusicLibraryModel::self()->toggleGrouping(); - #ifdef ENABLE_DEVICES_SUPPORT - DevicesModel::self()->toggleGrouping(); - #endif - #ifdef ENABLE_ONLINE_SERVICES - OnlineServicesModel::self()->toggleGrouping(); - #endif - } - - if (diffAlbumSort) { - albumsPage->resort(); - } - if (diffLibYear) { - libraryPage->resort(); - #ifdef ENABLE_ONLINE_SERVICES - onlinePage->resort(); - #endif - #ifdef ENABLE_DEVICES_SUPPORT - devicesPage->resort(); - #endif - } - bool wasAutoExpand=playQueue->isAutoExpand(); bool wasStartClosed=playQueue->isStartClosed(); bool wasGrouped=playQueue->isGrouped(); @@ -1843,17 +1787,16 @@ void MainWindow::scrollPlayQueue(bool wasEmpty) void MainWindow::updateStats() { // Check if remote db is more recent than local one - if (0==MusicLibraryModel::self()->lastUpdate() || MPDStats::self()->dbUpdate() > MusicLibraryModel::self()->lastUpdate()) { - if (0==MusicLibraryModel::self()->lastUpdate()) { - libraryPage->clear(); - //albumsPage->clear(); - folderPage->clear(); - playlistsPage->clear(); - } - albumsPage->goTop(); - libraryPage->refresh(); - folderPage->refresh(); - } +// if (0==MusicLibraryModel::self()->lastUpdate() || MPDStats::self()->dbUpdate() > MusicLibraryModel::self()->lastUpdate()) { +// if (0==MusicLibraryModel::self()->lastUpdate()) { +// libraryPage->clear(); +// folderPage->clear(); +// playlistsPage->clear(); +// } +// libraryPage->refresh(); +// folderPage->refresh(); +// } + // TODO???? } void MainWindow::updateStatus() @@ -2205,7 +2148,6 @@ void MainWindow::currentTabChanged(int index) controlDynamicButton(); switch(index) { case PAGE_LIBRARY: currentPage=libraryPage; break; - case PAGE_ALBUMS: currentPage=albumsPage; break; case PAGE_FOLDERS: folderPage->load(); currentPage=folderPage; break; case PAGE_PLAYLISTS: currentPage=playlistsPage; break; #ifdef ENABLE_DYNAMIC @@ -2262,9 +2204,6 @@ void MainWindow::tabToggled(int index) case PAGE_LIBRARY: locateTrackAction->setVisible(tabWidget->isEnabled(index)); break; - case PAGE_ALBUMS: - AlbumsModel::self()->setEnabled(!AlbumsModel::self()->isEnabled()); - break; case PAGE_FOLDERS: folderPage->setEnabled(!folderPage->isEnabled()); break; @@ -2307,10 +2246,8 @@ void MainWindow::toggleMonoIcons() Icons::self()->initSidebarIcons(); showPlayQueueAction->setIcon(Icons::self()->playqueueIcon); tabWidget->setIcon(PAGE_PLAYQUEUE, showPlayQueueAction->icon()); - libraryTabAction->setIcon(Icons::self()->artistsIcon); + libraryTabAction->setIcon(Icons::self()->libraryIcon); tabWidget->setIcon(PAGE_LIBRARY, libraryTabAction->icon()); - albumsTabAction->setIcon(Icons::self()->albumsIcon); - tabWidget->setIcon(PAGE_ALBUMS, albumsTabAction->icon()); foldersTabAction->setIcon(Icons::self()->foldersIcon); tabWidget->setIcon(PAGE_FOLDERS, foldersTabAction->icon()); playlistsTabAction->setIcon(Icons::self()->playlistsIcon); @@ -2453,7 +2390,7 @@ void MainWindow::editTags() } } #endif - MusicLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres); + //MusicLibraryModel::self()->getDetails(artists, albumArtists, composers, albums, genres); TagEditor *dlg=new TagEditor(this, songs, artists, albumArtists, composers, albums, genres, udi); dlg->show(); #endif @@ -2614,7 +2551,6 @@ void MainWindow::updateActionToolTips() ActionCollection::get()->updateToolTips(); tabWidget->setToolTip(PAGE_PLAYQUEUE, showPlayQueueAction->toolTip()); tabWidget->setToolTip(PAGE_LIBRARY, libraryTabAction->toolTip()); - tabWidget->setToolTip(PAGE_ALBUMS, albumsTabAction->toolTip()); tabWidget->setToolTip(PAGE_FOLDERS, foldersTabAction->toolTip()); tabWidget->setToolTip(PAGE_PLAYLISTS, playlistsTabAction->toolTip()); #ifdef ENABLE_DYNAMIC diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 72bc84c89..a2c465b9e 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -49,7 +49,6 @@ class ActionCollection; class MainWindow; class Page; class LibraryPage; -class AlbumsPage; class FolderPage; class PlaylistsPage; #ifdef ENABLE_DYNAMIC @@ -109,7 +108,6 @@ public: { PAGE_PLAYQUEUE, PAGE_LIBRARY, - PAGE_ALBUMS, PAGE_FOLDERS, PAGE_PLAYLISTS, #ifdef ENABLE_DYNAMIC @@ -227,7 +225,6 @@ public Q_SLOTS: void tabToggled(int index); void showPlayQueue() { showTab(PAGE_PLAYQUEUE); } void showLibraryTab() { showTab(PAGE_LIBRARY); } - void showAlbumsTab() { showTab(PAGE_ALBUMS); } void showFoldersTab() { showTab(PAGE_FOLDERS); } void showPlaylistsTab() { showTab(PAGE_PLAYLISTS); } void showDynamicTab() { @@ -353,7 +350,6 @@ private: Action *serverInfoAction; Action *clearPlayQueueAction; Action *cancelAction; - Action *clearNewStateAction; Action *ratingAction; Action *fwdAction; Action *revAction; @@ -367,8 +363,6 @@ private: QWidget *playQueuePage; Action *libraryTabAction; LibraryPage *libraryPage; - Action *albumsTabAction; - AlbumsPage *albumsPage; Action *foldersTabAction; FolderPage *folderPage; Action *playlistsTabAction; diff --git a/gui/playlistspage.cpp b/gui/playlistspage.cpp index 86e4bc6c1..b66534071 100644 --- a/gui/playlistspage.cpp +++ b/gui/playlistspage.cpp @@ -60,8 +60,6 @@ PlaylistsPage::PlaylistsPage(QWidget *p) removeDuplicatesAction=new Action(i18n("Remove Duplicates"), this); removeDuplicatesAction->setEnabled(false); replacePlayQueue->setDefaultAction(StdActions::self()->replacePlayQueueAction); - connect(genreCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchItems())); - connect(PlaylistsModel::self(), SIGNAL(updateGenres(const QSet &)), genreCombo, SLOT(update(const QSet &))); view->allowGroupedView(); view->allowTableView(new PlaylistTableView(view)); @@ -86,7 +84,6 @@ PlaylistsPage::PlaylistsPage(QWidget *p) connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &))); connect(view, SIGNAL(itemsSelected(bool)), SLOT(controlActions())); connect(view, SIGNAL(searchItems()), this, SLOT(searchItems())); - connect(view, SIGNAL(rootIndexSet(QModelIndex)), this, SLOT(updateGenres(QModelIndex))); //connect(this, SIGNAL(add(const QStringList &)), MPDConnection::self(), SLOT(add(const QStringList &))); connect(this, SIGNAL(addSongsToPlaylist(const QString &, const QStringList &)), MPDConnection::self(), SLOT(addToPlaylist(const QString &, const QStringList &))); connect(this, SIGNAL(loadPlaylist(const QString &, bool)), MPDConnection::self(), SLOT(loadPlaylist(const QString &, bool))); @@ -443,7 +440,7 @@ void PlaylistsPage::controlActions() void PlaylistsPage::searchItems() { QString text=view->searchText().trimmed(); - bool updated=proxy.update(text, genreCombo->currentIndex()<=0 ? QString() : genreCombo->currentText()); + bool updated=proxy.update(text); if (proxy.enabled() && !proxy.filterText().isEmpty()) { view->expandAll(); } @@ -456,15 +453,3 @@ void PlaylistsPage::updated(const QModelIndex &index) { view->updateRows(proxy.mapFromSource(index)); } - -void PlaylistsPage::updateGenres(const QModelIndex &idx) -{ - if (idx.isValid()) { - QModelIndex m=proxy.mapToSource(idx); - if (m.isValid() && static_cast(m.internalPointer())->isPlaylist()) { - genreCombo->update(static_cast(m.internalPointer())->genres); - return; - } - } - genreCombo->update(PlaylistsModel::self()->genres()); -} diff --git a/gui/playlistspage.h b/gui/playlistspage.h index 1a551fdcb..bd1fe037d 100644 --- a/gui/playlistspage.h +++ b/gui/playlistspage.h @@ -79,7 +79,6 @@ private Q_SLOTS: void itemDoubleClicked(const QModelIndex &index); void searchItems(); void updated(const QModelIndex &index); - void updateGenres(const QModelIndex &); private: Action *renamePlaylistAction; diff --git a/gui/playlistspage.ui b/gui/playlistspage.ui index 626900782..7b29ec148 100644 --- a/gui/playlistspage.ui +++ b/gui/playlistspage.ui @@ -29,7 +29,17 @@ 0
    - + + + Qt::Horizontal + + + + 16 + 20 + + + @@ -49,12 +59,7 @@ ItemView QTreeView
    widgets/itemview.h
    - - - GenreCombo - QComboBox -
    widgets/genrecombo.h
    -
    + s ToolButton QToolButton diff --git a/gui/serversettings.cpp b/gui/serversettings.cpp index ca84ce859..7914b3cfd 100644 --- a/gui/serversettings.cpp +++ b/gui/serversettings.cpp @@ -27,7 +27,7 @@ #include "support/inputdialog.h" #include "support/messagebox.h" #include "widgets/icons.h" -#include "models/musiclibrarymodel.h" +#include "db/mpdlibrarydb.h" #ifdef ENABLE_SIMPLE_MPD_SUPPORT #include "mpd-interface/mpduser.h" #endif @@ -218,7 +218,7 @@ void ServerSettings::save() } #endif Settings::self()->saveCurrentConnection(current.details.name); - MusicLibraryModel::cleanCache(); + MpdLibraryDb::removeUnusedDbs(); } void ServerSettings::cancel() diff --git a/gui/settings.cpp b/gui/settings.cpp index 0ba0b65f4..dc71f5f64 100644 --- a/gui/settings.cpp +++ b/gui/settings.cpp @@ -22,13 +22,13 @@ */ #include "settings.h" -#include "models/musiclibraryitemalbum.h" +#include "models/sqllibrarymodel.h" #include "support/fancytabwidget.h" -#include "models/albumsmodel.h" #include "widgets/itemview.h" #include "mpd-interface/mpdparseutils.h" #include "support/utils.h" #include "support/globalstatic.h" +#include "db/librarydb.h" #if defined ENABLE_KDE_SUPPORT && defined ENABLE_KWALLET #include #endif @@ -342,11 +342,6 @@ int Settings::libraryView() return ItemView::toMode(cfg.get("libraryView", ItemView::modeStr(ItemView::Mode_DetailedTree))); } -int Settings::albumsView() -{ - return ItemView::toMode(cfg.get("albumsView", ItemView::modeStr(ItemView::Mode_IconTop))); -} - int Settings::folderView() { return ItemView::toMode(cfg.get("folderView", ItemView::modeStr(ItemView::Mode_DetailedTree))); @@ -373,16 +368,9 @@ bool Settings::libraryArtistImage() return cfg.get("libraryArtistImage", false); } -int Settings::albumSort() +QString Settings::libraryAlbumSort() { - if (version() Settings::composerGenres() @@ -982,11 +970,6 @@ void Settings::saveLibraryView(int v) cfg.set("libraryView", ItemView::modeStr((ItemView::Mode)v)); } -void Settings::saveAlbumsView(int v) -{ - cfg.set("albumsView", ItemView::modeStr((ItemView::Mode)v)); -} - void Settings::saveFolderView(int v) { cfg.set("folderView", ItemView::modeStr((ItemView::Mode)v)); @@ -1013,9 +996,9 @@ void Settings::saveLibraryArtistImage(bool v) cfg.set("libraryArtistImage", v); } -void Settings::saveAlbumSort(int v) +void Settings::saveLibraryAlbumSort(const QString &v) { - cfg.set("albumSort", AlbumsModel::sortStr((AlbumsModel::Sort)v)); + cfg.set("libraryAlbumSort", v); } void Settings::saveSidebar(int v) @@ -1023,14 +1006,14 @@ void Settings::saveSidebar(int v) cfg.set("sidebar", v); } -void Settings::saveLibraryYear(bool v) +void Settings::saveLibrarySort(const QString &v) { - cfg.set("libraryYear", v); + cfg.set("librarySort", v); } -void Settings::saveGroupSingle(bool v) +void Settings::saveLibraryGrouping(const QString &v) { - cfg.set("groupSingle", v); + cfg.set("libraryGrouping", v); } void Settings::saveComposerGenres(const QSet &v) diff --git a/gui/settings.h b/gui/settings.h index 5d9245fd1..b1898f0d1 100644 --- a/gui/settings.h +++ b/gui/settings.h @@ -74,17 +74,16 @@ public: bool storeBackdropsInMpdDir(); #ifndef ENABLE_UBUNTU int libraryView(); - int albumsView(); int folderView(); int playlistsView(); int streamsView(); int onlineView(); #endif bool libraryArtistImage(); - int albumSort(); + QString libraryAlbumSort(); int sidebar(); - bool libraryYear(); - bool groupSingle(); + QString librarySort(); + QString libraryGrouping(); QSet composerGenres(); QStringList lyricProviders(); QStringList wikipediaLangs(); @@ -199,17 +198,16 @@ public: void saveStoreBackdropsInMpdDir(bool v); #ifndef ENABLE_UBUNTU void saveLibraryView(int v); - void saveAlbumsView(int v); void saveFolderView(int v); void savePlaylistsView(int v); void saveStreamsView(int v); void saveOnlineView(int v); #endif void saveLibraryArtistImage(bool v); - void saveAlbumSort(int v); + void saveLibraryAlbumSort(const QString &v); void saveSidebar(int v); - void saveLibraryYear(bool v); - void saveGroupSingle(bool v); + void saveLibrarySort(const QString &v); + void saveLibraryGrouping(const QString &v); void saveComposerGenres(const QSet &v); void saveLyricProviders(const QStringList &v); void saveWikipediaLangs(const QStringList &v); diff --git a/icons/sidebar-albums-dark.svg b/icons/sidebar-albums-dark.svg deleted file mode 100755 index 182b9194d..000000000 --- a/icons/sidebar-albums-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/icons/sidebar-albums-light.svg b/icons/sidebar-albums-light.svg deleted file mode 100644 index 93a4c03e4..000000000 --- a/icons/sidebar-albums-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/icons/sidebar-artists-dark.svg b/icons/sidebar-artists-dark.svg deleted file mode 100755 index 428622865..000000000 --- a/icons/sidebar-artists-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/sidebar-artists-light.svg b/icons/sidebar-artists-light.svg deleted file mode 100644 index 194beba17..000000000 --- a/icons/sidebar-artists-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/sidebar-library-dark.svg b/icons/sidebar-library-dark.svg new file mode 100644 index 000000000..7eeaf77b1 --- /dev/null +++ b/icons/sidebar-library-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/icons/sidebar-library-light.svg b/icons/sidebar-library-light.svg new file mode 100644 index 000000000..20830a84c --- /dev/null +++ b/icons/sidebar-library-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/models/albumsmodel.cpp b/models/albumsmodel.cpp deleted file mode 100644 index e77eaa09c..000000000 --- a/models/albumsmodel.cpp +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * Copyright (c) 2014 Niklas Wenzel - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include "support/localize.h" -#include "gui/plurals.h" -#include "support/globalstatic.h" -#include "albumsmodel.h" -#include "gui/settings.h" -#include "musiclibraryitemsong.h" -#include "musiclibraryitemartist.h" -#include "musiclibraryitemroot.h" -#include "musiclibrarymodel.h" -#include "playqueuemodel.h" -#include "proxymodel.h" -#include "mpd-interface/song.h" -#include "gui/covers.h" -#include "roles.h" -#include "mpd-interface/mpdparseutils.h" -#include "widgets/icons.h" -#include "support/utils.h" -#include "config.h" -#if defined ENABLE_MODEL_TEST -#include "modeltest.h" -#endif - -static int sortAlbums=AlbumsModel::Sort_AlbumArtist; - -GLOBAL_STATIC(AlbumsModel, instance) - -AlbumsModel::Sort AlbumsModel::toSort(const QString &str) -{ - for (int i=0; i<=Sort_YearArtist; ++i) { - if (sortStr((Sort)i)==str) { - return (Sort)i; - } - } - return AlbumsModel::Sort_AlbumArtist; -} - -QString AlbumsModel::sortStr(Sort m) -{ - switch (m) { - default: - case Sort_AlbumArtist: return QLatin1String("album-artist"); - case Sort_AlbumYear: return QLatin1String("album-year"); - case Sort_ArtistAlbum: return QLatin1String("artist-album"); - case Sort_ArtistYear: return QLatin1String("artist-year"); - case Sort_YearAlbum: return QLatin1String("year-album"); - case Sort_YearArtist: return QLatin1String("year-artist"); - } -} - - -AlbumsModel::AlbumsModel(QObject *parent) - : ActionModel(parent) - , enabled(false) -// , coversRequested(false) -{ - #if defined ENABLE_MODEL_TEST - new ModelTest(this, this); - #endif -} - -AlbumsModel::~AlbumsModel() -{ -} - -QVariant AlbumsModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const -{ - return QVariant(); -} - -int AlbumsModel::rowCount(const QModelIndex &index) const -{ - if (!index.isValid()) { - return items.size(); - } - - Item *item=static_cast(index.internalPointer()); - if (item->isAlbum()) { - AlbumItem *al=static_cast(index.internalPointer()); - return al->songs.count(); - } - return 0; -} - -bool AlbumsModel::hasChildren(const QModelIndex &parent) const -{ - return !parent.isValid() || static_cast(parent.internalPointer())->isAlbum(); -} - -QModelIndex AlbumsModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) { - return QModelIndex(); - } - - Item *item=static_cast(index.internalPointer()); - - if (item->isAlbum()) { - return QModelIndex(); - } else { - SongItem *song=static_cast(item); - - if (song->parent) { - return createIndex(items.indexOf(song->parent), 0, song->parent); - } - } - - return QModelIndex(); -} - -QModelIndex AlbumsModel::index(int row, int col, const QModelIndex &parent) const -{ - if (!hasIndex(row, col, parent)) { - return QModelIndex(); - } - - if (parent.isValid()) { - Item *p=static_cast(parent.internalPointer()); - - if (p->isAlbum()) { - AlbumItem *pl=static_cast(p); - return rowsongs.count() ? createIndex(row, col, pl->songs.at(row)) : QModelIndex(); - } - } - - return row(index.internalPointer()); - - if (item->isAlbum()) { - AlbumItem *al=static_cast(item); - - switch (role) { - default: - return ActionModel::data(index, role); - #ifdef ENABLE_UBUNTU - case Cantata::Role_Image: { - QString cover=al->cover(); - return cover.isEmpty() ? constDefaultCover : cover; - } - #endif - case Cantata::Role_ListImage: - return true; - #ifndef ENABLE_UBUNTU - case Qt::DecorationRole: - return Icons::self()->albumIcon; - #endif - case Qt::ToolTipRole: - if (!Settings::self()->infoTooltips()) { - return QVariant(); - } - return 0==al->songs.count() - ? QString() - : (al->artist+QLatin1Char('\n')+al->albumDisplay()+QLatin1Char('\n')+ - Plurals::tracksWithDuration(al->trackCount(), Utils::formatTime(al->totalTime(), true))); - case Qt::DisplayRole: - return al->album; - case Qt::FontRole: - if (al->isNew) { - QFont f=ActionModel::data(index, role).value(); - f.setBold(true); - return f; - } - break; - case Cantata::Role_MainText: - return al->albumDisplay(); - case Cantata::Role_BriefMainText: - return al->album; - case Cantata::Role_SubText: - return al->artist; - case Cantata::Role_TitleText: - return i18nc("Album by Artist", "%1 by %2", al->album, al->artist); - case Cantata::Role_CoverSong: { - QVariant v; - v.setValue(al->coverSong()); - return v; - } - } - } else { - SongItem *si=static_cast(item); - - switch (role) { - default: - return ActionModel::data(index, role); - #ifdef ENABLE_UBUNTU - case Cantata::Role_Image: - return QString(); - #endif - case Cantata::Role_ListImage: - return false; - #ifndef ENABLE_UBUNTU - case Qt::DecorationRole: - return Song::Playlist==si->type ? Icons::self()->playlistIcon : Icons::self()->audioFileIcon; - #endif - case Qt::ToolTipRole: - if (!Settings::self()->infoTooltips()) { - return QVariant(); - } - return si->toolTip(); - case Cantata::Role_MainText: - case Qt::DisplayRole: - if (Song::Playlist==si->type) { - return si->isCueFile() ? i18n("Cue Sheet") : i18n("Playlist"); - } - if (Song::SingleTracks==si->parent->type) { - return si->artistSong(); - } - return si->trackAndTitleStr(); - case Cantata::Role_SubText: - return Utils::formatTime(si->time, true); - } - } - - return QVariant(); -} - -Qt::ItemFlags AlbumsModel::flags(const QModelIndex &index) const -{ - if (index.isValid()) { - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; - } - return Qt::NoItemFlags; -} - -QStringList AlbumsModel::filenames(const QModelIndexList &indexes, bool allowPlaylists) const -{ - QList songList=songs(indexes, allowPlaylists); - QStringList fnames; - foreach (const Song &s, songList) { - fnames.append(s.file); - } - return fnames; -} - -QList AlbumsModel::songs(const QModelIndexList &indexes, bool allowPlaylists) const -{ - QList songs; - QSet files; - - foreach(QModelIndex index, indexes) { - Item *item=static_cast(index.internalPointer()); - - if (item->isAlbum()) { - QList albumSongs; - const SongItem *cue=allowPlaylists ? static_cast(item)->getCueFile() : 0; - if (cue) { - if (!files.contains(cue->file)) { - albumSongs << *cue; - files << cue->file; - } - } else { - foreach (const SongItem *s, static_cast(item)->songs) { - if ((/*allowPlaylists || */Song::Playlist!=s->type) && !files.contains(s->file)) { - albumSongs << *s; - files << s->file; - } - } - } - qSort(albumSongs); - songs << albumSongs; - } else if ((allowPlaylists || Song::Playlist!=static_cast(item)->type) && !files.contains(static_cast(item)->file)) { - songs << *static_cast(item); - files << static_cast(item)->file; - } - } - return songs; -} - -#ifndef ENABLE_UBUNTU -QMimeData * AlbumsModel::mimeData(const QModelIndexList &indexes) const -{ - QMimeData *mimeData = new QMimeData(); - QStringList files=filenames(indexes, true); - PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files); -// if (!MPDConnection::self()->getDetails().dir.isEmpty()) { -// QStringList paths; -// foreach (const QString &f, files) { -// paths << MPDConnection::self()->getDetails().dir+f; -// } -// PlayQueueModel::encode(*mimeData, PlayQueueModel::constUriMimeType, paths); -// } - return mimeData; -} -#endif - -void AlbumsModel::update(const MusicLibraryItemRoot *root, bool incremental) -{ - if (!enabled) { - return; - } - - bool changesMade=false; - bool resettingModel=!incremental || items.isEmpty() || 0==root->childCount(); - - if (!incremental) { - clear(); - } - - if (resettingModel) { - beginResetModel(); - } - QList::Iterator it=items.begin(); - QList::Iterator end=items.end(); - - for (; it!=end; ++it) { - (*it)->updated=false; - } - - for (int i = 0; i < root->childCount(); i++) { - MusicLibraryItemArtist *artistItem = static_cast(root->childItem(i)); - const QString &artist=artistItem->data(); - for (int j = 0; j < artistItem->childCount(); j++) { - MusicLibraryItemAlbum *albumItem = static_cast(artistItem->childItem(j)); - const QString &albumId=albumItem->albumId(); - bool found=false; - it=items.begin(); - end=items.end(); - for (; it!=end; ++it) { - if ((*it)->year==albumItem->year() && (*it)->artist==artist && (*it)->albumId()==albumId) { - if (!resettingModel) { - QModelIndex albumIndex=index(items.indexOf(*it), 0, QModelIndex()); - bool hadSongs=!(*it)->songs.isEmpty(); - if (hadSongs) { - beginRemoveRows(albumIndex, 0, (*it)->songs.count()-1); - } - (*it)->clearSongs(); - if (hadSongs) { - endRemoveRows(); - } - if (albumItem->childCount()) { - beginInsertRows(albumIndex, 0, albumItem->childCount()-1); - } - } - (*it)->setSongs(albumItem); - if (!resettingModel && albumItem->childCount()) { - endInsertRows(); - } - (*it)->genres=albumItem->genres(); - (*it)->updated=true; - found=true; - if ((*it)->isNew!=albumItem->isNew()) { - (*it)->isNew=albumItem->isNew(); - if (!resettingModel) { - QModelIndex albumIndex=index(items.indexOf(*it), 0, QModelIndex()); - emit dataChanged(albumIndex, albumIndex); - } - } - break; - } - } - - if (!found) { - changesMade=true; - AlbumItem *a=new AlbumItem(artist, albumItem->data(), albumId, - artistItem->hasSort() ? artistItem->sortString() : QString(), - albumItem->hasSort() ? albumItem->sortString() : QString(), - albumItem->year()); - a->setSongs(albumItem); - a->genres=albumItem->genres(); - a->updated=true; - a->type=albumItem->songType(); - a->isNew=albumItem->isNew(); - if (!resettingModel) { - beginInsertRows(QModelIndex(), items.count(), items.count()); - } - items.append(a); - if (!resettingModel) { - endInsertRows(); - } - } - } - } - - if (resettingModel) { - endResetModel(); - } else { - for (int i=0; iupdated) { - beginRemoveRows(QModelIndex(), i, i); - delete items.takeAt(i); - endRemoveRows(); - } else { - ++i; - } - } - } - - if (changesMade) { - emit updated(); - } -} - -void AlbumsModel::setCover(const Song &song, const QImage &img, const QString &file) -{ - #ifdef ENABLE_UBUNTU - if (img.isNull()) { - if (!file.isEmpty()) return; //If empty, we need to execute the stuff below to set m_coverRequested to false - } - - QList::Iterator it=items.begin(); - QList::Iterator end=items.end(); - QString artist=MusicLibraryItemRoot::artistName(song); - const QString &albumId=song.albumId(); - - for (int row=0; it!=end; ++it, ++row) { - if ((*it)->artist==artist && (*it)->albumId()==albumId) { - if ((*it)->coverRequested) { - (*it)->coverRequested=false; - if (!file.isEmpty()) { - (*it)->coverFile="file://"+file; - QModelIndex idx=index(row, 0, QModelIndex()); - emit dataChanged(idx, idx); - } - } - return; - } - } - #else - Q_UNUSED(song) - Q_UNUSED(img) - Q_UNUSED(file) - #endif -} - -void AlbumsModel::coverLoaded(const Song &song, int s) -{ - Q_UNUSED(s) - #ifdef ENABLE_UBUNTU - Q_UNUSED(song) - #else - if (!song.isArtistImageRequest() && !song.isComposerImageRequest()) { - QList::Iterator it=items.begin(); - QList::Iterator end=items.end(); - const QString &albumArtist=song.albumArtist(); - const QString &albumId=song.albumId(); - - for (int row=0; it!=end; ++it, ++row) { - if ((*it)->artist==albumArtist && (*it)->albumId()==albumId) { - QModelIndex idx=index(row, 0, QModelIndex()); - emit dataChanged(idx, idx); - } - } - } - #endif -} - -void AlbumsModel::clearNewState() -{ - for (int i=0; iisNew) { - al->isNew=false; - QModelIndex idx=index(i, 0, QModelIndex()); - emit dataChanged(idx, idx); - } - } -} - -void AlbumsModel::clear() -{ - beginResetModel(); - qDeleteAll(items); - items.clear(); - endResetModel(); - - #ifdef ENABLE_UBUNTU //For displaying a message when there is no album - emit updated(); - #endif -} - -void AlbumsModel::setEnabled(bool e) -{ - if (e==enabled) { - return; - } - enabled=e; - - if (enabled) { - #ifdef ENABLE_UBUNTU - connect(Covers::self(), SIGNAL(cover(const Song &, const QImage &, const QString &)), - this, SLOT(setCover(const Song &, const QImage &, const QString &))); - #else - connect(Covers::self(), SIGNAL(loaded(Song,int)), this, SLOT(coverLoaded(Song,int))); - #endif - update(MusicLibraryModel::self()->root(), false); - } else { - clear(); - #ifdef ENABLE_UBUNTU - disconnect(Covers::self(), SIGNAL(cover(const Song &, const QImage &, const QString &)), - this, SLOT(setCover(const Song &, const QImage &, const QString &))); - #else - disconnect(Covers::self(), SIGNAL(loaded(Song,int)), this, SLOT(coverLoaded(Song,int))); - #endif - } -} - -int AlbumsModel::albumSort() const -{ - return sortAlbums; -} - -void AlbumsModel::setAlbumSort(int s) -{ - if (s!=sortAlbums) { - beginResetModel(); - sortAlbums=s; - endResetModel(); - } -} - -static const QLatin1String constThe("The "); - -AlbumsModel::AlbumItem::AlbumItem(const QString &ar, const QString &al, const QString &i, const QString &arSort, const QString &alSort, quint16 y) - : artist(ar) - , album(al) - , id(i) - , year(y) - , updated(false) - , numTracks(0) - , time(0) - , isNew(false) - #ifdef ENABLE_UBUNTU - , coverRequested(false) - #endif -{ - if (!arSort.isEmpty()) { - artistSortString=arSort; - } else if (artist.startsWith(constThe)) { - artistSortString=artist.mid(4); - } - - if (!alSort.isEmpty()) { - albumSortString=alSort; - } else if (artist.startsWith(constThe)) { - albumSortString=artist.mid(4); - } -} - -AlbumsModel::AlbumItem::~AlbumItem() -{ - clearSongs(); -} - -bool AlbumsModel::AlbumItem::operator<(const AlbumItem &o) const -{ - switch (sortAlbums) { - default: - case Sort_AlbumArtist: { - int compare=sortAlbum().localeAwareCompare(o.sortAlbum()); - if (0!=compare) { - return compare<0; - } - compare=sortArtist().localeAwareCompare(o.sortArtist()); - if (0!=compare) { - return compare<0; - } - return year==o.year ? id.compare(o.id)<0 : yearchildItems()) { - songs.append(new SongItem(static_cast(item)->song(), this)); - } -} - -quint32 AlbumsModel::AlbumItem::trackCount() -{ - updateStats(); - return numTracks; -} - -quint32 AlbumsModel::AlbumItem::totalTime() -{ - updateStats(); - return time; -} - -void AlbumsModel::AlbumItem::updateStats() -{ - if (0==time) { - numTracks=0; - foreach (SongItem *s, songs) { - if (Song::Playlist!=s->type) { - time+=s->time; - numTracks++; - } - } - } -} - -#ifdef ENABLE_UBUNTU -QString AlbumsModel::AlbumItem::cover() -{ - if (Song::SingleTracks!=type && songs.count() && coverFile.isEmpty() && !coverRequested) { - coverRequested=true; - coverFile=Covers::self()->requestImage(coverSong()).fileName; - if (!coverFile.isEmpty()) { - coverRequested=false; - coverFile="file://"+coverFile; - } - } - - return coverFile; -} -#endif - -const Song & AlbumsModel::AlbumItem::coverSong() -{ - if (cSong.isEmpty() && songs.count()) { - SongItem *firstSong=songs.first(); - cSong.artist=firstSong->artist; - cSong.albumartist=/*Song::useComposer() && !firstSong->composer().isEmpty() - ? */firstSong->albumArtist()/* : artist*/; - cSong.album=album; - cSong.year=year; - cSong.file=firstSong->file; - cSong.type=type; - cSong.setComposer(firstSong->composer()); - cSong.setMbAlbumId(firstSong->mbAlbumId()); - } - return cSong; -} - -const AlbumsModel::SongItem *AlbumsModel::AlbumItem::getCueFile() const -{ - foreach (SongItem *s, songs) { - if (s->isCueFile()) { - return s; - } - } - - return 0; -} diff --git a/models/albumsmodel.h b/models/albumsmodel.h deleted file mode 100644 index 68319ad3c..000000000 --- a/models/albumsmodel.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * Copyright (c) 2014 Niklas Wenzel - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -#ifndef ALBUMSMODEL_H -#define ALBUMSMODEL_H - -#include -#include -#include -#include "mpd-interface/song.h" -#include "musiclibraryitemalbum.h" -#include "actionmodel.h" - -class MusicLibraryItemRoot; -class QSize; -class QPixmap; - -class AlbumsModel : public ActionModel -{ - Q_OBJECT - -public: - enum Sort - { - Sort_AlbumArtist, - Sort_AlbumYear, - Sort_ArtistAlbum, - Sort_ArtistYear, - Sort_YearAlbum, - Sort_YearArtist - }; - - static Sort toSort(const QString &str); - static QString sortStr(Sort m); - - enum Columnms - { - COL_NAME, - COL_FILES, - COL_GENRES - }; - - struct Item - { - virtual bool isAlbum() { return false; } - virtual ~Item() { } - }; - - struct AlbumItem; - struct SongItem : public Item, public Song - { - SongItem(const Song &s, AlbumItem *p=0) : Song(s), parent(p) { } - virtual ~SongItem() { } - AlbumItem *parent; - }; - - struct AlbumItem : public Item - { - AlbumItem(const QString &ar, const QString &al, const QString &i, const QString &arSort, const QString &alSort, quint16 y); - virtual ~AlbumItem(); - bool operator<(const AlbumItem &o) const; - bool isAlbum() { return true; } - void clearSongs(); - void setSongs(MusicLibraryItemAlbum *ai); - quint32 trackCount(); - quint32 totalTime(); - void updateStats(); - #ifdef ENABLE_UBUNTU - QString cover(); - #endif - bool isSingleTracks() const { return Song::SingleTracks==type; } - const SongItem *getCueFile() const; - QString albumDisplay() const { return Song::displayAlbum(album, year); } - const QString & sortArtist() const { return artistSortString.isEmpty() ? artist : artistSortString; } - const QString & sortAlbum() const { return albumSortString.isEmpty() ? album : albumSortString; } - const QString & albumId() const { return id.isEmpty() ? album : id; } - const Song & coverSong(); - QString artist; - QString artistSortString; - QString album; - QString albumSortString; - QString id; - quint16 year; - QList songs; - QSet genres; - bool updated; - Song::Type type; - quint32 numTracks; - quint32 time; - Song cSong; - bool isNew; - #ifdef ENABLE_UBUNTU - QString coverFile; - bool coverRequested; - #endif - }; - - static AlbumsModel * self(); - - AlbumsModel(QObject *parent=0); - ~AlbumsModel(); - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - bool hasChildren(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex&) const { return 1; } - QModelIndex parent(const QModelIndex &index) const; - QModelIndex index(int row, int column, const QModelIndex &parent) const; - QVariant data(const QModelIndex &, int) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - QStringList filenames(const QModelIndexList &indexes, bool allowPlaylists=false) const; - QList songs(const QModelIndexList &indexes, bool allowPlaylists=false) const; - #ifndef ENABLE_UBUNTU - QMimeData * mimeData(const QModelIndexList &indexes) const; - #endif - void clear(); - bool isEnabled() const { return enabled; } - void setEnabled(bool e); - int albumSort() const; - void setAlbumSort(int s); - -Q_SIGNALS: - void updated(); - -public Q_SLOTS: - void clearNewState(); - void coverLoaded(const Song &song, int s); - // Touch version... - void setCover(const Song &song, const QImage &img, const QString &file); - void update(const MusicLibraryItemRoot *root, bool incremental=true); - -private: - bool enabled; -// bool coversRequested; - mutable QList items; -}; - -#endif diff --git a/models/devicesmodel.cpp b/models/devicesmodel.cpp index b1d87ecae..b5f0ed802 100644 --- a/models/devicesmodel.cpp +++ b/models/devicesmodel.cpp @@ -25,7 +25,6 @@ #include "musiclibraryitemartist.h" #include "musiclibraryitemsong.h" #include "musiclibraryitemroot.h" -#include "musiclibrarymodel.h" #include "devicesmodel.h" #include "playqueuemodel.h" #include "gui/settings.h" @@ -341,7 +340,6 @@ void DevicesModel::deviceUpdating(const QString &udi, bool state) } QModelIndex modelIndex=createIndex(idx, 0, dev); emit dataChanged(modelIndex, modelIndex); - MultiMusicModel::updateGenres(); emit updated(modelIndex); } } @@ -461,7 +459,6 @@ void DevicesModel::deviceRemoved(const QString &udi) static_cast(collections.takeAt(idx))->deleteLater(); endRemoveRows(); updateItemMenu(); - MultiMusicModel::updateGenres(); } } @@ -493,8 +490,6 @@ void DevicesModel::addRemoteDevice(const DeviceOptions &opts, RemoteFsDevice::De connect(dev, SIGNAL(cover(const Song &, const QImage &)), SLOT(setCover(const Song &, const QImage &))); if (Device::RemoteFs==dev->devType()) { connect(static_cast(dev), SIGNAL(udiChanged()), SLOT(remoteDeviceUdiChanged())); - connect(static_cast(dev), SIGNAL(connectionStateHasChanged(QString, bool)), - SLOT(removeDeviceConnectionStateChanged(QString, bool))); } updateItemMenu(); } @@ -520,7 +515,6 @@ void DevicesModel::removeRemoteDevice(const QString &udi, bool removeFromConfig) collections.takeAt(idx); endRemoveRows(); updateItemMenu(); - MultiMusicModel::updateGenres(); RemoteFsDevice *rfs=qobject_cast(dev); if (rfs) { // Destroy will stop device, and delete it (via deleteLater()) @@ -540,22 +534,6 @@ void DevicesModel::remoteDeviceUdiChanged() #endif } -void DevicesModel::removeDeviceConnectionStateChanged(const QString &udi, bool state) -{ - #ifdef ENABLE_REMOTE_DEVICES - if (!state) { - int idx=indexOf(udi); - if (idx<0) { - return; - } - MultiMusicModel::updateGenres(); - } - #else - Q_UNUSED(udi) - Q_UNUSED(state) - #endif -} - void DevicesModel::mountsChanged() { #ifdef ENABLE_REMOTE_DEVICES @@ -662,8 +640,6 @@ void DevicesModel::loadRemote() connect(dev, SIGNAL(cover(const Song &, const QImage &)), SLOT(setCover(const Song &, const QImage &))); if (Device::RemoteFs==dev->devType()) { connect(static_cast(dev), SIGNAL(udiChanged()), SLOT(remoteDeviceUdiChanged())); - connect(static_cast(dev), SIGNAL(connectionStateHasChanged(QString, bool)), - SLOT(removeDeviceConnectionStateChanged(QString, bool))); } } endInsertRows(); diff --git a/models/devicesmodel.h b/models/devicesmodel.h index b4cb65ece..bdf775a9b 100644 --- a/models/devicesmodel.h +++ b/models/devicesmodel.h @@ -81,7 +81,6 @@ public Q_SLOTS: void addRemoteDevice(const DeviceOptions &opts, RemoteFsDevice::Details details); void removeRemoteDevice(const QString &udi, bool removeFromConfig=true); void remoteDeviceUdiChanged(); - void removeDeviceConnectionStateChanged(const QString &udi, bool state); void mountsChanged(); private: @@ -91,7 +90,6 @@ private: #endif Q_SIGNALS: - void updateGenres(const QSet &genres); void addToDevice(const QString &udi); void error(const QString &text); void updated(const QModelIndex &idx); diff --git a/models/dirviewmodel.cpp b/models/dirviewmodel.cpp index a54ca7105..a9e67f333 100644 --- a/models/dirviewmodel.cpp +++ b/models/dirviewmodel.cpp @@ -38,7 +38,6 @@ #include "dirviewitem.h" #include "dirviewitemfile.h" #include "playqueuemodel.h" -#include "musiclibrarymodel.h" #include "roles.h" #include "gui/settings.h" #include "mpd-interface/mpdconnection.h" @@ -52,18 +51,6 @@ #include "modeltest.h" #endif -const QLatin1String DirViewModel::constCacheName("-folder-listing"); - -static QString cacheFileName() -{ - MPDConnectionDetails details=MPDConnection::self()->getDetails(); - QString fileName=(!details.isLocal() ? details.hostname+'_'+QString::number(details.port) : details.hostname) - +DirViewModel::constCacheName+MusicLibraryModel::constLibraryCompressedExt; - fileName.replace('/', '_'); - fileName.replace('~', '_'); - return Utils::cacheDir(MusicLibraryModel::constLibraryCache)+fileName; -} - GLOBAL_STATIC(DirViewModel, instance) DirViewModel::DirViewModel(QObject *parent) @@ -90,12 +77,9 @@ void DirViewModel::setEnabled(bool e) enabled=e; if (enabled) { - connect(MPDConnection::self(), SIGNAL(updatingDatabase()), this, SLOT(updatingMpd())); connect(MPDConnection::self(), SIGNAL(dirViewUpdated(DirViewItemRoot *, time_t)), this, SLOT(updateDirView(DirViewItemRoot *, time_t))); } else { clear(); - removeCache(); - disconnect(MPDConnection::self(), SIGNAL(updatingDatabase()), this, SLOT(updatingMpd())); disconnect(MPDConnection::self(), SIGNAL(dirViewUpdated(DirViewItemRoot *, time_t)), this, SLOT(updateDirView(DirViewItemRoot *, time_t))); } } @@ -243,150 +227,7 @@ void DirViewModel::clear() endResetModel(); } -static QLatin1String constTopTag("CantataFolders"); -static const QString constVersionAttribute=QLatin1String("version"); -static const QString constDateAttribute=QLatin1String("date"); -static const QString constDateUnreliableAttribute=QLatin1String("dateUnreliable"); -static const QString constNameAttribute=QLatin1String("name"); -static const QString constPathAttribute=QLatin1String("path"); -static const QString constDirTag=QLatin1String("dir"); -static const QString constFileTag=QLatin1String("file"); -static const QString constTrueValue=QLatin1String("true"); - -static quint32 constVersion=2; - -void DirViewModel::toXML() -{ - QString filename=cacheFileName(); - if ((!rootItem || 0==rootItem->childCount()) && !MusicLibraryModel::validCacheDate(databaseTime)) { - if (QFile::exists(filename)) { - QFile::remove(filename); - } - return; - } - - QFile file(filename); - QtIOCompressor compressor(&file); - compressor.setStreamFormat(QtIOCompressor::GzipFormat); - if (!compressor.open(QIODevice::WriteOnly)) { - return; - } - - QXmlStreamWriter writer(&compressor); - - writer.writeStartDocument(); - writer.writeStartElement(constTopTag); - writer.writeAttribute(constVersionAttribute, QString::number(constVersion)); - writer.writeAttribute(constDateAttribute, QString::number(databaseTime)); - if (databaseTimeUnreliable) { - writer.writeAttribute(constDateUnreliableAttribute, constTrueValue); - } - - if (rootItem) { - foreach (const DirViewItem *i, rootItem->childItems()) { - toXML(i, writer); - } - } - writer.writeEndElement(); - writer.writeEndDocument(); - compressor.close(); -} - -void DirViewModel::removeCache() -{ - QString cacheFile(cacheFileName()); - if (QFile::exists(cacheFile)) { - QFile::remove(cacheFile); - } - - databaseTime = 0; -} - -void DirViewModel::toXML(const DirViewItem *item, QXmlStreamWriter &writer) -{ - writer.writeStartElement(DirViewItem::Type_File==item->type() ? constFileTag : constDirTag); - writer.writeAttribute(constNameAttribute, item->name()); - if (DirViewItem::Type_Dir==item->type()) { - foreach (const DirViewItem *i, static_cast(item)->childItems()) { - toXML(i, writer); - } - } else { - const DirViewItemFile *f=static_cast(item); - if (!f->filePath().isEmpty()) { - writer.writeAttribute(constPathAttribute, f->filePath()); - } - } - writer.writeEndElement(); -} - -bool DirViewModel::fromXML() -{ - clear(); - QFile file(cacheFileName()); - QtIOCompressor compressor(&file); - compressor.setStreamFormat(QtIOCompressor::GzipFormat); - if (!compressor.open(QIODevice::ReadOnly)) { - return false; - } - - DirViewItemRoot *root=new DirViewItemRoot; - time_t date=fromXML(&compressor, MPDStats::self()->dbUpdate(), root); - compressor.close(); - if (!date) { - delete root; - return false; - } - - updateDirView(root, date, true); - return true; -} - -time_t DirViewModel::fromXML(QIODevice *dev, time_t dt, DirViewItemRoot *root) -{ - QXmlStreamReader reader(dev); - time_t xmlDate=0; - DirViewItemDir *currentDir=root; - QList dirStack; - - while (!reader.atEnd()) { - reader.readNext(); - if (reader.error()) { - delete root; - return 0; - } - if (reader.isStartElement()) { - QString element = reader.name().toString(); - QXmlStreamAttributes attributes=reader.attributes(); - - if (constTopTag == element) { - quint32 version = attributes.value(constVersionAttribute).toString().toUInt(); - xmlDate = attributes.value(constDateAttribute).toString().toUInt(); - if ( version < constVersion || (dt>0 && xmlDate < dt)) { - return 0; - } - databaseTimeUnreliable=constTrueValue==attributes.value(constDateUnreliableAttribute).toString(); - } else if (constDirTag==element) { - DirViewItemDir *dir=new DirViewItemDir(attributes.value(constNameAttribute).toString(), currentDir); - currentDir->add(dir); - dirStack.append(currentDir); - currentDir=dir; - } else if (constFileTag==element) { - currentDir->add(new DirViewItemFile(attributes.value(constNameAttribute).toString(), - attributes.value(constPathAttribute).toString(), currentDir)); - } else { - return 0; - } - } else if (reader.isEndElement()) { - if (constDirTag==reader.name().toString()) { - currentDir=dirStack.takeLast(); - } - } - } - - return xmlDate; -} - -void DirViewModel::updateDirView(DirViewItemRoot *newroot, time_t dbUpdate, bool fromFile) +void DirViewModel::updateDirView(DirViewItemRoot *newroot, time_t dbUpdate) { if (databaseTime>0 && databaseTime >= dbUpdate) { delete newroot; @@ -394,12 +235,6 @@ void DirViewModel::updateDirView(DirViewItemRoot *newroot, time_t dbUpdate, bool } bool incremental=enabled && rootItem->childCount() && newroot->childCount(); - bool updatedListing=false; - bool needToSave=databaseTime==0 || (MusicLibraryModel::validCacheDate(dbUpdate) && dbUpdate>databaseTime); - - if (incremental && !QFile::exists(cacheFileName())) { - incremental=false; - } databaseTime=dbUpdate; if (incremental) { @@ -418,31 +253,12 @@ void DirViewModel::updateDirView(DirViewItemRoot *newroot, time_t dbUpdate, bool foreach (const QString &s, removed) { removeFileFromList(Song::decodePath(s)); } - updatedListing=!added.isEmpty() || !removed.isEmpty(); } else { const DirViewItemRoot *oldRoot = rootItem; beginResetModel(); rootItem = newroot; delete oldRoot; endResetModel(); - updatedListing=true; - } - - // MPD proxy DB plugin (MPD < 0.18.5) does not provide a datetime for the DB. Also, Mopidy - // returns 0 for the database time (which equates to 1am Jan 1st 1970!). Therefore, in these - // cases we just use current datetime so that we dont keep requesting DB listing each time - // Cantata starts... - // - // Mopidy users, and users of the proxy DB plugin, will have to force Cantata to refresh :-( - if (!fromFile) { - databaseTimeUnreliable=!MusicLibraryModel::validCacheDate(dbUpdate); // See note in updatingMpd() - } - if (!MusicLibraryModel::validCacheDate(databaseTime) && !MusicLibraryModel::validCacheDate(dbUpdate)) { - databaseTime=QDateTime::currentDateTime().toTime_t(); - } - - if (!fromFile && (needToSave || updatedListing)) { - toXML(); } #ifdef ENABLE_UBUNTU @@ -450,18 +266,6 @@ void DirViewModel::updateDirView(DirViewItemRoot *newroot, time_t dbUpdate, bool #endif } -void DirViewModel::updatingMpd() -{ - // MPD/Mopidy is being updated. If MPD's database-time is not reliable (as is the case for older proxy DBs, and Mopidy) - // then we set the databaseTime to NOW when updated. This means we will miss any updates. So, for these scenarios, when - // a user presses 'Refresh Database' in Cantata's main window, we need to reset our view of the databaseTime to null, so - // that we update. This does mean that we will ALWAYS fetch the whole listing - but we have no way of knowing if it changed - // or not :-( - if (databaseTimeUnreliable) { - removeCache(); - } -} - void DirViewModel::addFileToList(const QString &file, const QString &mopidyPath) { if (!enabled) { diff --git a/models/dirviewmodel.h b/models/dirviewmodel.h index 906b25ef5..2da400abc 100644 --- a/models/dirviewmodel.h +++ b/models/dirviewmodel.h @@ -38,8 +38,6 @@ class DirViewModel : public ActionModel Q_OBJECT public: - static const QLatin1String constCacheName; - static DirViewModel * self(); DirViewModel(QObject *parent = 0); @@ -60,13 +58,9 @@ public: void removeFileFromList(const QString &file); bool isEnabled() const { return enabled; } void setEnabled(bool e); - void removeCache(); - void toXML(); - bool fromXML(); public Q_SLOTS: - void updateDirView(DirViewItemRoot *newroot, time_t dbUpdate=0, bool fromFile=false); - void updatingMpd(); + void updateDirView(DirViewItemRoot *newroot, time_t dbUpdate=0); Q_SIGNALS: void updated(); diff --git a/models/modeltest.cpp b/models/modeltest.cpp index 9cbc95771..4d5d554cf 100644 --- a/models/modeltest.cpp +++ b/models/modeltest.cpp @@ -472,7 +472,7 @@ void ModelTest::data() */ void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int end ) { -// Q_UNUSED(end) + Q_UNUSED(end) // qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() // << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); // qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); diff --git a/models/mpdlibrarymodel.cpp b/models/mpdlibrarymodel.cpp new file mode 100644 index 000000000..78d32cc99 --- /dev/null +++ b/models/mpdlibrarymodel.cpp @@ -0,0 +1,179 @@ +/* + * Cantata + * + * Copyright (c) 2015 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "mpdlibrarymodel.h" +#include "support/globalstatic.h" +#include "db/mpdlibrarydb.h" +#include "gui/settings.h" +#include "gui/covers.h" +#include "roles.h" +#include + +GLOBAL_STATIC(MpdLibraryModel, instance) + +MpdLibraryModel::MpdLibraryModel() + : SqlLibraryModel(new MpdLibraryDb(0), 0) +{ + readSettings(); + connect(Covers::self(), SIGNAL(cover(Song,QImage,QString)), this, SLOT(cover(Song,QImage,QString))); + connect(Covers::self(), SIGNAL(coverUpdated(Song,QImage,QString)), this, SLOT(coverUpdated(Song,QImage,QString))); + connect(Covers::self(), SIGNAL(artistImage(Song,QImage,QString)), this, SLOT(artistImage(Song,QImage,QString))); + connect(Covers::self(), SIGNAL(composerImage(Song,QImage,QString)), this, SLOT(artistImage(Song,QImage,QString))); +} + +QVariant MpdLibraryModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + switch (role) { + case Cantata::Role_CoverSong: { + QVariant v; + Item *item = static_cast(index.internalPointer()); + switch (item->getType()) { + case T_Album: + if (item->getSong().isEmpty()) { + Song song=static_cast(db)->getCoverSong(T_Album==topLevel() ? static_cast(item)->getArtistId() : item->getParent()->getId(), item->getId()); + item->setSong(song); + if (T_Genre==topLevel()) { + song.genre=item->getParent()->getParent()->getId(); + } + } + v.setValue(item->getSong()); + break; + case T_Artist: + if (item->getSong().isEmpty()) { + Song song=static_cast(db)->getCoverSong(item->getId()); + if (song.useComposer()) { + song.setComposerImageRequest(); + } else { + song.setArtistImageRequest(); + } + if (T_Genre==topLevel()) { + song.genre=item->getParent()->getId(); + } + item->setSong(song); + qWarning() << item->getText() << "SET COVER SONG" << song.file << song.isArtistImageRequest() << song.isComposerImageRequest(); + } + v.setValue(item->getSong()); + break; + default: + break; + } + return v; + } + } + return SqlLibraryModel::data(index, role); +} + +void MpdLibraryModel::readSettings() +{ + settings(Settings::self()->libraryGrouping(), Settings::self()->librarySort(), Settings::self()->libraryAlbumSort()); +} + +void MpdLibraryModel::cover(const Song &song, const QImage &img, const QString &file) +{ + if (file.isEmpty() || img.isNull()) { + return; + } + switch(topLevel()) { + case T_Genre: { + const Item *genre=root ? root->getChild(song.genre) : 0; + if (genre) { + const Item *artist=static_cast(genre)->getChild(song.artistOrComposer()); + if (artist) { + const Item *album=static_cast(artist)->getChild(song.albumId()); + if (album) { + QModelIndex idx=index(album->getRow(), 0, index(artist->getRow(), 0, index(genre->getRow(), 0, QModelIndex()))); + emit dataChanged(idx, idx); + } + } + } + break; + } + case T_Artist: { + const Item *artist=root ? root->getChild(song.artistOrComposer()) : 0; + if (artist) { + const Item *album=static_cast(artist)->getChild(song.albumId()); + if (album) { + QModelIndex idx=index(album->getRow(), 0, index(artist->getRow(), 0, QModelIndex())); + emit dataChanged(idx, idx); + } + } + break; + } + case T_Album: { + const Item *album=root ? root->getChild(song.artistOrComposer()+song.albumId()) : 0; + if (album) { + QModelIndex idx=index(album->getRow(), 0, QModelIndex()); + emit dataChanged(idx, idx); + } + break; + } + default: + break; + } +} + +void MpdLibraryModel::coverUpdated(const Song &song, const QImage &img, const QString &file) +{ + if (file.isEmpty() || img.isNull() || (T_Album==topLevel() && (song.isArtistImageRequest() || song.isComposerImageRequest()))) { + return; + } + cover(song, img, file); +} + +void MpdLibraryModel::artistImage(const Song &song, const QImage &img, const QString &file) +{ + qWarning() << song.artistOrComposer() << file; + + if (file.isEmpty() || img.isNull() || T_Album==topLevel()) { + return; + } + switch(topLevel()) { + case T_Genre: { + const Item *genre=root ? root->getChild(song.genre) : 0; + if (genre) { + const Item *artist=static_cast(genre)->getChild(song.artistOrComposer()); + if (artist) { + QModelIndex idx=index(artist->getRow(), 0, index(genre->getRow(), 0, QModelIndex())); + emit dataChanged(idx, idx); + } + } + break; + } + case T_Artist: { + const Item *artist=root ? root->getChild(song.artistOrComposer()) : 0; + qWarning() << "A" << (void *)artist; + if (artist) { + QModelIndex idx=index(artist->getRow(), 0, QModelIndex()); + qWarning() << idx.isValid() << artist->getRow(); + emit dataChanged(idx, idx); + } + break; + } + default: + break; + } +} diff --git a/widgets/genrecombo.h b/models/mpdlibrarymodel.h similarity index 59% rename from widgets/genrecombo.h rename to models/mpdlibrarymodel.h index 2d24597c4..b2320005c 100644 --- a/widgets/genrecombo.h +++ b/models/mpdlibrarymodel.h @@ -1,7 +1,7 @@ /* * Cantata * - * Copyright (c) 2011-2014 Craig Drummond + * Copyright (c) 2015 Craig Drummond * * ---- * @@ -21,32 +21,24 @@ * Boston, MA 02110-1301, USA. */ -#ifndef GENRECOMBO_H -#define GENRECOMBO_H +#ifndef MPD_LIBRARY_MODEL_H +#define MPD_LIBRARY_MODEL_H -#include "support/combobox.h" -#include +#include "sqllibrarymodel.h" -class GenreCombo : public ComboBox +class MpdLibraryModel : public SqlLibraryModel { Q_OBJECT public: - GenreCombo(QWidget *p); - virtual ~GenreCombo() { } - - const QSet & entries() const { return genres; } - - void paintEvent(QPaintEvent *e); - bool event(QEvent *event); - -public Q_SLOTS: - void update(const QSet &g); + static MpdLibraryModel * self(); + MpdLibraryModel(); + QVariant data(const QModelIndex &index, int role) const; + void readSettings(); private Q_SLOTS: - void showEntries(); - -private: - QSet genres; + void cover(const Song &song, const QImage &img, const QString &file); + void coverUpdated(const Song &song, const QImage &img, const QString &file); + void artistImage(const Song &song, const QImage &img, const QString &file); }; #endif diff --git a/models/multimusicmodel.cpp b/models/multimusicmodel.cpp index 7704f4807..bf86e036e 100644 --- a/models/multimusicmodel.cpp +++ b/models/multimusicmodel.cpp @@ -83,27 +83,6 @@ int MultiMusicModel::rowCount(const QModelIndex &parent) const return parent.isValid() ? static_cast(parent.internalPointer())->childCount() : collections.count(); } -void MultiMusicModel::updateGenres() -{ - QSet newGenres; - foreach (MusicLibraryItemRoot *col, collections) { - newGenres+=col->genres(); - } - if (newGenres!=colGenres) { - colGenres=newGenres; - emit updateGenres(colGenres); - } -} - -void MultiMusicModel::toggleGrouping() -{ - beginResetModel(); - foreach (MusicLibraryItemRoot *col, collections) { - col->toggleGrouping(); - } - endResetModel(); -} - void MultiMusicModel::getDetails(QSet &artists, QSet &albumArtists, QSet &composers, QSet &albums, QSet &genres) { foreach (MusicLibraryItemRoot *col, collections) { diff --git a/models/multimusicmodel.h b/models/multimusicmodel.h index 269eeb641..aad365864 100644 --- a/models/multimusicmodel.h +++ b/models/multimusicmodel.h @@ -42,22 +42,15 @@ public: QModelIndex parent(const QModelIndex &index) const; int rowCount(const QModelIndex &parent=QModelIndex()) const; void getDetails(QSet &artists, QSet &albumArtists, QSet &composers, QSet &albums, QSet &genres); - void toggleGrouping(); QList songs(const QModelIndexList &indexes, bool playableOnly=false, bool fullPath=false) const; QStringList filenames(const QModelIndexList &indexes, bool playableOnly=false, bool fullPath=false) const; - const QSet & genres() { return colGenres; } int row(void *i) const { return collections.indexOf(static_cast(i)); } -Q_SIGNALS: - void updateGenres(const QSet &genres); - protected: int indexOf(const QString &id); - void updateGenres(); protected: QList collections; - QSet colGenres; }; #endif diff --git a/models/musiclibraryitem.cpp b/models/musiclibraryitem.cpp index adf8951a1..7f15b519a 100644 --- a/models/musiclibraryitem.cpp +++ b/models/musiclibraryitem.cpp @@ -55,7 +55,6 @@ void MusicLibraryItem::setParent(MusicLibraryItemContainer *p) } m_parentItem=p; m_parentItem->m_childItems.append(this); - m_parentItem->m_genres+=allGenres(); } MusicLibraryItem * MusicLibraryItemContainer::childItem(const QString &name) const @@ -69,17 +68,6 @@ MusicLibraryItem * MusicLibraryItemContainer::childItem(const QString &name) con return 0; } -void MusicLibraryItemContainer::updateGenres() -{ - m_genres.clear(); - foreach (MusicLibraryItem *i, m_childItems) { - if (MusicLibraryItem::Type_Song!=i->itemType()) { - static_cast(i)->updateGenres(); - } - m_genres+=i->allGenres(); - } -} - void MusicLibraryItemContainer::resetRows() { if (m_rowsSet) { @@ -94,6 +82,5 @@ void MusicLibraryItemContainer::clear() { qDeleteAll(m_childItems); m_childItems.clear(); - m_genres.clear(); m_rowsSet=false; } diff --git a/models/musiclibraryitem.h b/models/musiclibraryitem.h index db7e68588..ebb51c918 100644 --- a/models/musiclibraryitem.h +++ b/models/musiclibraryitem.h @@ -59,8 +59,6 @@ public: Qt::CheckState checkState() const { return m_checkState; } void setCheckState(Qt::CheckState s) { m_checkState=s; } - virtual bool hasGenre(const QString &genre) const=0; - virtual QSet allGenres() const=0; virtual Type itemType() const=0; protected: @@ -84,11 +82,7 @@ public: void setData(const QString &d) { m_itemData=d; } int childCount() const { return m_childItems.count(); } const QList & childItems() const { return m_childItems; } - void addGenres(const QSet &genres) { m_genres+=genres; } - bool hasGenre(const QString &genre) const { return m_genres.contains(genre); } - const QSet & genres() const { return m_genres; } - QSet allGenres() const { return genres(); } - void updateGenres(); + void resetRows(); void clear(); int indexOf(MusicLibraryItem *c) const { return m_childItems.indexOf(c); } @@ -99,7 +93,6 @@ protected: friend class MusicLibraryItem; QString m_itemData; QList m_childItems; - QSet m_genres; bool m_isNew:1; bool m_rowsSet:1; }; diff --git a/models/musiclibraryitemartist.cpp b/models/musiclibraryitemartist.cpp index b46fe6cd4..51777546e 100644 --- a/models/musiclibraryitemartist.cpp +++ b/models/musiclibraryitemartist.cpp @@ -28,7 +28,6 @@ #include "musiclibraryitemartist.h" #include "musiclibraryitemalbum.h" #include "musiclibraryitemsong.h" -#include "musiclibrarymodel.h" #include "mpd-interface/mpdparseutils.h" #include "support/localize.h" #include "gui/covers.h" diff --git a/models/musiclibraryitemroot.cpp b/models/musiclibraryitemroot.cpp index 7ba60dd45..7a6614ee2 100644 --- a/models/musiclibraryitemroot.cpp +++ b/models/musiclibraryitemroot.cpp @@ -58,50 +58,6 @@ MusicLibraryItemArtist * MusicLibraryItemRoot::createArtist(const Song &s, bool return item; } -void MusicLibraryItemRoot::groupSingleTracks() -{ - if (!supportsAlbumArtist || isFlat) { - return; - } - - QList::iterator it=m_childItems.begin(); - MusicLibraryItemArtist *various=0; - bool created=false; - - for (; it!=m_childItems.end(); ) { - if (various!=(*it) && static_cast(*it)->allSingleTrack()) { - if (!various) { - various=getArtist(Song::variousArtists()); - if (!various) { - various=new MusicLibraryItemArtist(Song::variousArtists(), QString(), QString(), this); - created=true; - } - } - various->addToSingleTracks(static_cast(*it)); - delete (*it); - it=m_childItems.erase(it); - } else { - ++it; - } - } - - if (various) { - if (created) { - m_childItems.append(various); - } - refreshIndexes(); - } -} - -bool MusicLibraryItemRoot::isFromSingleTracks(const Song &s) const -{ - if (!isFlat && (supportsAlbumArtist && !s.file.isEmpty())) { - MusicLibraryItemArtist *various=getArtist(Song::variousArtists()); - return various && various->isFromSingleTracks(s); - } - return false; -} - void MusicLibraryItemRoot::refreshIndexes() { if (isFlat) { @@ -178,9 +134,6 @@ void MusicLibraryItemRoot::getDetails(QSet &artists, QSet &alb albumArtists.insert(s.albumArtist()); composers.insert(s.composer()); albums.insert(s.album); - if (!s.genre.isEmpty()) { - genres.insert(s.genre); - } } else if (MusicLibraryItem::Type_Artist==child->itemType()) { foreach (const MusicLibraryItem *album, static_cast(child)->childItems()) { foreach (const MusicLibraryItem *song, static_cast(album)->childItems()) { @@ -189,9 +142,6 @@ void MusicLibraryItemRoot::getDetails(QSet &artists, QSet &alb albumArtists.insert(s.albumArtist()); composers.insert(s.composer()); albums.insert(s.album); - if (!s.genre.isEmpty()) { - genres.insert(s.genre); - } } } } @@ -269,8 +219,6 @@ static const QString constGuessedAttribute=QLatin1String("guessed"); static const QString constDateAttribute=QLatin1String("date"); static const QString constDateUnreliableAttribute=QLatin1String("dateUnreliable"); static const QString constVersionAttribute=QLatin1String("version"); -static const QString constGroupSingleAttribute=QLatin1String("groupSingle"); -static const QString constSingleTracksAttribute=QLatin1String("singleTracks"); static const QString constMultipleArtistsAttribute=QLatin1String("multipleArtists"); static const QString constImageAttribute=QLatin1String("img"); static const QString constnumTracksAttribute=QLatin1String("numTracks"); @@ -299,9 +247,6 @@ void MusicLibraryItemRoot::toXML(QXmlStreamWriter &writer, time_t date, bool dat if (dateUnreliable) { writer.writeAttribute(constDateUnreliableAttribute, constTrueValue); } - if (MPDParseUtils::groupSingle()) { - writer.writeAttribute(constGroupSingleAttribute, constTrueValue); - } foreach (const MusicLibraryItem *a, childItems()) { foreach (const MusicLibraryItem *al, static_cast(a)->childItems()) { total+=al->childCount(); @@ -329,16 +274,9 @@ void MusicLibraryItemRoot::toXML(QXmlStreamWriter &writer, time_t date, bool dat return; } const MusicLibraryItemAlbum *album = static_cast(al); - QString albumGenre=Song::combineGenres(album->genres()); writer.writeStartElement(constAlbumElement); writer.writeAttribute(constNameAttribute, album->originalName().isEmpty() ? album->data() : album->originalName()); writer.writeAttribute(constYearAttribute, QString::number(album->year())); - if (!albumGenre.isEmpty() && albumGenre!=Song::unknown()) { - writer.writeAttribute(constGenreAttribute, albumGenre); - } - if (album->isSingleTracks()) { - writer.writeAttribute(constSingleTracksAttribute, constTrueValue); - } if (!album->imageUrl().isEmpty()) { writer.writeAttribute(constImageAttribute, album->imageUrl()); } @@ -377,7 +315,7 @@ void MusicLibraryItemRoot::toXML(QXmlStreamWriter &writer, time_t date, bool dat writer.writeAttribute(constComposerAttribute, track->song().composer()); } QString trackGenre=track->multipleGenres() ? Song::combineGenres(track->allGenres()) : track->genre(); - if (!trackGenre.isEmpty() && trackGenre!=albumGenre && trackGenre!=Song::unknown()) { + if (!trackGenre.isEmpty() && trackGenre!=Song::unknown()) { writer.writeAttribute(constGenreAttribute, track->song().genre); } if (album->isSingleTracks()) { @@ -449,8 +387,6 @@ time_t MusicLibraryItemRoot::fromXML(QXmlStreamReader &reader, time_t date, bool quint32 xmlDate=0; quint64 total=0; quint64 count=0; - bool gs=MPDParseUtils::groupSingle(); - bool gm=false; int percent=0; bool online=isOnlineService(); QElapsedTimer timer; @@ -474,8 +410,6 @@ time_t MusicLibraryItemRoot::fromXML(QXmlStreamReader &reader, time_t date, bool if (constTopTag == element) { quint32 version = attributes.value(constVersionAttribute).toString().toUInt(); xmlDate = attributes.value(constDateAttribute).toString().toUInt(); - gs = constTrueValue==attributes.value(constGroupSingleAttribute).toString(); - gm = constTrueValue==attributes.value(constGroupSingleAttribute).toString(); if ( version < constVersion || (date>0 && xmlDate < date)) { return 0; } @@ -510,12 +444,7 @@ time_t MusicLibraryItemRoot::fromXML(QXmlStreamReader &reader, time_t date, bool if (!img.isEmpty()) { albumItem->setImageUrl(img); } - if (constTrueValue==attributes.value(constSingleTracksAttribute).toString()) { - albumItem->setIsSingleTracks(); - song.type=Song::SingleTracks; - } else { - song.type=Song::Standard; - } + song.type=Song::Standard; } else if (constTrackElement==element) { song.title=attributes.value(constNameAttribute).toString(); song.file=attributes.value(constFileAttribute).toString(); @@ -584,11 +513,6 @@ time_t MusicLibraryItemRoot::fromXML(QXmlStreamReader &reader, time_t date, bool } MusicLibraryItemSong *songItem=new MusicLibraryItemSong(song, albumItem); - QSet songGenres=songItem->allGenres(); - albumItem->append(songItem); - albumItem->addGenres(songGenres); - artistItem->addGenres(songGenres); - addGenres(songGenres); if (prog && !prog->wasStopped() && total>0) { count++; @@ -606,13 +530,6 @@ time_t MusicLibraryItemRoot::fromXML(QXmlStreamReader &reader, time_t date, bool } } - // Grouping has changed! - // As of 1.4.0, Cantata no-longer groups multiple-artist albums under 'Various Artists' - so if this setting - // has been used, we need to undo it! - if (gs!=MPDParseUtils::groupSingle() || gm) { - toggleGrouping(); - } - return xmlDate; } @@ -636,57 +553,7 @@ void MusicLibraryItemRoot::add(const QSet &songs) if (!albumItem || albumItem->parentItem()!=artistItem || s.album!=albumItem->data()) { albumItem = artistItem->album(s); } - - MusicLibraryItemSong *songItem=new MusicLibraryItemSong(s, albumItem); - QSet songGenres=songItem->allGenres(); - albumItem->append(songItem); - albumItem->addGenres(songGenres); - artistItem->addGenres(songGenres); - addGenres(songGenres); - } -} - -void MusicLibraryItemRoot::toggleGrouping() -{ - if (isFlat) { - return; - } - - // Grouping has changed, so we need to recreate whole structure from list of songs. - QSet songs=allSongs(); - clearItems(); - MusicLibraryItemArtist *artistItem = 0; - MusicLibraryItemAlbum *albumItem = 0; - - foreach (Song currentSong, songs) { - if (Song::Standard!=currentSong.type && Song::Playlist!=currentSong.type) { - currentSong.type=MPDConnection::isPlaylist(currentSong.file) ? Song::Playlist : Song::Standard; - } - - if (!artistItem || currentSong.artistOrComposer()!=artistItem->data()) { - artistItem = artist(currentSong); - } - if (!albumItem || currentSong.year!=albumItem->year() || albumItem->parentItem()!=artistItem || currentSong.albumName()!=albumItem->data()) { - albumItem = artistItem->album(currentSong); - } - - MusicLibraryItemSong *song=new MusicLibraryItemSong(currentSong, albumItem); - QSet songGenres=song->allGenres(); - albumItem->append(song); - albumItem->addGenres(songGenres); - artistItem->addGenres(songGenres); - addGenres(songGenres); - } - - // Library rebuilt, now apply any grouping... - applyGrouping(); -} - -void MusicLibraryItemRoot::applyGrouping() -{ - if (MPDParseUtils::groupSingle()) { - groupSingleTracks(); - updateGenres(); + albumItem->append(new MusicLibraryItemSong(s, albumItem)); } } @@ -695,7 +562,6 @@ void MusicLibraryItemRoot::clearItems() qDeleteAll(m_childItems); m_childItems.clear(); m_indexes.clear(); - m_genres.clear(); } bool MusicLibraryItemRoot::update(const QSet &songs) @@ -713,9 +579,6 @@ bool MusicLibraryItemRoot::update(const QSet &songs) foreach (const Song &s, added) { addSongToList(s); } - if (updatedSongs) { - updateGenres(); - } return updatedSongs; } @@ -754,16 +617,6 @@ bool MusicLibraryItemRoot::songExists(const Song &s) const if (!s.isVariousArtists()) { Song mod(s); mod.albumartist=Song::variousArtists(); - if (MPDParseUtils::groupSingle()) { - mod.album=i18n("Single Tracks"); - song=findSong(mod); - if (song) { - Song sng=static_cast(song)->song(); - if (sng.albumArtist()==s.albumArtist() && sng.album==s.album) { - return true; - } - } - } } return false; @@ -780,9 +633,6 @@ bool MusicLibraryItemRoot::updateSong(const Song &orig, const Song &edit) foreach (MusicLibraryItem *song, childItems()) { if (static_cast(song)->song().file==orig.file) { static_cast(song)->setSong(edit); - if (orig.genre!=edit.genre) { - updateGenres(); - } QModelIndex idx=m_model->createIndex(songRow, 0, song); emit m_model->dataChanged(idx, idx); return true; @@ -803,11 +653,6 @@ bool MusicLibraryItemRoot::updateSong(const Song &orig, const Song &edit) if (static_cast(song)->song().file==orig.file) { static_cast(song)->setSong(edit); bool yearUpdated=orig.year!=edit.year && albumItem->updateYear(); - if (orig.genre!=edit.genre) { - albumItem->updateGenres(); - artistItem->updateGenres(); - updateGenres(); - } QModelIndex idx=m_model->createIndex(songRow, 0, song); emit m_model->dataChanged(idx, idx); if (yearUpdated) { diff --git a/models/musiclibraryitemroot.h b/models/musiclibraryitemroot.h index 183de83de..9fbb98708 100644 --- a/models/musiclibraryitemroot.h +++ b/models/musiclibraryitemroot.h @@ -77,9 +77,6 @@ public: virtual bool isDevice() const { return false; } MusicLibraryItemArtist * artist(const Song &s, bool create=true); MusicLibraryItemArtist * createArtist(const Song &s, bool forceComposer=false); - void groupSingleTracks(); - void groupMultipleArtists(); - bool isFromSingleTracks(const Song &s) const; void refreshIndexes(); void remove(MusicLibraryItemArtist *artist); QSet allSongs(bool revertVa=false) const; @@ -93,8 +90,6 @@ public: void add(const QSet &songs); bool supportsAlbumArtistTag() const { return supportsAlbumArtist; } void setSupportsAlbumArtistTag(bool s) { supportsAlbumArtist=s; } - virtual void toggleGrouping(); - void applyGrouping(); void clearItems(); void setModel(MusicModel *m) { m_model=m; } bool flat() const { return isFlat; } diff --git a/models/musiclibrarymodel.cpp b/models/musiclibrarymodel.cpp deleted file mode 100644 index c5965d05b..000000000 --- a/models/musiclibrarymodel.cpp +++ /dev/null @@ -1,957 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - */ -/* - * Copyright (c) 2008 Sander Knopper (sander AT knopper DOT tk) and - * Roeland Douma (roeland AT rullzer DOT com) - * - * This file is part of QtMPC. - * - * QtMPC is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * QtMPC 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 QtMPC. If not, see . - */ - -#include "musiclibraryitemalbum.h" -#include "musiclibraryitemartist.h" -#include "musiclibraryitemsong.h" -#include "musiclibraryitemroot.h" -#include "musiclibrarymodel.h" -#include "albumsmodel.h" -#include "playqueuemodel.h" -#include "dirviewmodel.h" -#include "config.h" -#include "roles.h" -#include "gui/covers.h" -#include "mpd-interface/mpdparseutils.h" -#include "mpd-interface/mpdconnection.h" -#include "support/localize.h" -#include "support/utils.h" -#include "widgets/icons.h" -#include "gui/stdactions.h" -#include "qtiocompressor/qtiocompressor.h" -#include "support/globalstatic.h" -#ifndef ENABLE_UBUNTU -#include "gui/settings.h" -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#if defined ENABLE_MODEL_TEST -#include "modeltest.h" -#endif - -GLOBAL_STATIC(MusicLibraryModel, instance) - -const QLatin1String MusicLibraryModel::constLibraryCache("library/"); -const QLatin1String MusicLibraryModel::constLibraryExt(".xml"); -const QLatin1String MusicLibraryModel::constLibraryCompressedExt(".xml.gz"); - -static QString cacheFileName(const MPDConnectionDetails &details, bool withPort=true) -{ - QString fileName=(withPort && !details.isLocal() ? details.hostname+'_'+QString::number(details.port) : details.hostname) - +MusicLibraryModel::constLibraryCompressedExt; - fileName.replace('/', '_'); - fileName.replace('~', '_'); - return Utils::cacheDir(MusicLibraryModel::constLibraryCache)+fileName; -} - -static QString cacheFileName(bool withPort=true) -{ - return cacheFileName(MPDConnection::self()->getDetails(), withPort); -} - -void MusicLibraryModel::convertCache(const QString &compressedName) -{ - QString prev=compressedName; - prev.replace(constLibraryCompressedExt, constLibraryExt); - - if (QFile::exists(prev) && !QFile::exists(compressedName)) { - QFile old(prev); - if (old.open(QIODevice::ReadOnly)) { - QByteArray a=old.readAll(); - old.close(); - - QFile newCache(compressedName); - QtIOCompressor compressor(&newCache); - compressor.setStreamFormat(QtIOCompressor::GzipFormat); - if (compressor.open(QIODevice::WriteOnly)) { - compressor.write(a); - compressor.close(); - QFile::remove(prev); - } - } - } -} - -void MusicLibraryModel::cleanCache() -{ - #ifdef ENABLE_UBUNTU - // TODO??? - #else - QSet existing; - QList connections=Settings::self()->allConnections(); - QString dirPath=Utils::cacheDir(MusicLibraryModel::constLibraryCache, false); - if (dirPath.isEmpty()) { - return; - } - - foreach (const MPDConnectionDetails &conn, connections) { - QString fileName=cacheFileName(conn).mid(dirPath.length()); - existing.insert(fileName); - // Dir view cache file... - fileName=fileName.left(fileName.length()-QString(constLibraryCompressedExt).length()); - fileName+=DirViewModel::constCacheName+(constLibraryCompressedExt); - existing.insert(fileName); - } - QFileInfoList files=QDir(dirPath).entryInfoList(QStringList() << "*"+constLibraryExt << "*"+constLibraryCompressedExt, QDir::Files); - foreach (const QFileInfo &file, files) { - if (!existing.contains(file.fileName())) { - QFile::remove(file.absoluteFilePath()); - } - } - #endif -} - -MusicLibraryModel::MusicLibraryModel(QObject *parent, bool isMpdModel, bool isCheckable) - : MusicModel(parent) - , mpdModel(isMpdModel) - , checkable(isCheckable) - , artistImages(false) - , rootItem(new MusicLibraryItemRoot) - , databaseTimeUnreliable(false) -{ - if (mpdModel) - { - connect(Covers::self(), SIGNAL(loaded(Song,int)), this, SLOT(coverLoaded(Song,int))); - connect(Covers::self(), SIGNAL(cover(const Song &, const QImage &, const QString &)), - this, SLOT(setCover(const Song &, const QImage &, const QString &))); - #ifdef ENABLE_UBUNTU - connect(Covers::self(), SIGNAL(artistImage(const Song &, const QImage &, const QString &)), - this, SLOT(setArtistImage(const Song &, const QImage &, const QString &))); - #endif - connect(MPDConnection::self(), SIGNAL(updatingDatabase()), this, SLOT(updatingMpd())); - connect(MPDConnection::self(), SIGNAL(musicLibraryUpdated(MusicLibraryItemRoot *, time_t)), - this, SLOT(updateMusicLibrary(MusicLibraryItemRoot *, time_t))); - } - rootItem->setModel(this); - #if defined ENABLE_MODEL_TEST - new ModelTest(this, this); - #endif -} - -MusicLibraryModel::~MusicLibraryModel() -{ - delete rootItem; -} - -void MusicLibraryModel::readConfig() -{ - #ifndef ENABLE_UBUNTU - bool aa=Settings::self()->libraryArtistImage(); - if (aa!=artistImages) { - artistImages=aa; - for (int r=0; rchildItem(r)); - emit dataChanged(index, index); - } - } - #endif -} - -QModelIndex MusicLibraryModel::index(int row, int column, const QModelIndex &parent) const -{ - if (!hasIndex(row, column, parent)) { - return QModelIndex(); - } - - const MusicLibraryItem * parentItem; - - if (!parent.isValid()) { - parentItem = rootItem; - } else { - parentItem = static_cast(parent.internalPointer()); - } - - MusicLibraryItem * const childItem = parentItem->childItem(row); - if (childItem) { - return createIndex(row, column, childItem); - } - - return QModelIndex(); -} - -QModelIndex MusicLibraryModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) { - return QModelIndex(); - } - - const MusicLibraryItem * const childItem = static_cast(index.internalPointer()); - MusicLibraryItem * const parentItem = childItem->parentItem(); - - if (parentItem == rootItem) { - return QModelIndex(); - } - - return createIndex(parentItem->row(), 0, parentItem); -} - -int MusicLibraryModel::rowCount(const QModelIndex &parent) const -{ - if (parent.column() > 0) { - return 0; - } - - const MusicLibraryItem *parentItem; - - if (!parent.isValid()) { - parentItem = rootItem; - } else { - parentItem = static_cast(parent.internalPointer()); - } - - return parentItem->childCount(); -} - -QVariant MusicLibraryModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - return QVariant(); - } - - if (checkable && Qt::CheckStateRole==role) { - return static_cast(index.internalPointer())->checkState(); - } - - if (!checkable && Qt::FontRole==role) { - MusicLibraryItem *item = static_cast(index.internalPointer()); - if ((MusicLibraryItem::Type_Album==item->itemType() || MusicLibraryItem::Type_Artist==item->itemType()) && - static_cast(item)->isNew()) { - QFont f=MusicModel::data(index, role).value(); - f.setBold(true); - return f; - } - } - - if (Cantata::Role_ListImage==role && artistImages) { - MusicLibraryItem *item = static_cast(index.internalPointer()); - return MusicLibraryItem::Type_Album==item->itemType() || MusicLibraryItem::Type_Artist==item->itemType(); - } - - return MusicModel::data(index, role); -} - -bool MusicLibraryModel::setData(const QModelIndex &idx, const QVariant &value, int role) -{ - if (checkable && Qt::CheckStateRole==role) { - if (!idx.isValid()) { - return false; - } - - MusicLibraryItem *item = static_cast(idx.internalPointer()); - Qt::CheckState check=value.toBool() ? Qt::Checked : Qt::Unchecked; - - if (item->checkState()==check) { - return false; - } - - switch (item->itemType()) { - case MusicLibraryItem::Type_Podcast: - break; - case MusicLibraryItem::Type_Artist: { - MusicLibraryItemArtist *artistItem=static_cast(item); - QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex()); - item->setCheckState(check); - foreach (MusicLibraryItem *album, artistItem->childItems()) { - if (check!=album->checkState()) { - MusicLibraryItemAlbum *albumItem=static_cast(album); - QModelIndex albumIndex=index(albumItem->row(), 0, artistIndex); - album->setCheckState(check); - foreach (MusicLibraryItem *song, albumItem->childItems()) { - song->setCheckState(check); - } - emit dataChanged(index(0, 0, albumIndex), index(0, albumItem->childCount(), albumIndex)); - } - emit dataChanged(index(0, 0, artistIndex), index(0, artistItem->childCount(), artistIndex)); - } - emit dataChanged(idx, idx); - break; - } - case MusicLibraryItem::Type_Album: { - MusicLibraryItemArtist *artistItem=static_cast(item->parentItem()); - MusicLibraryItemAlbum *albumItem=static_cast(item); - QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex()); - item->setCheckState(check); - foreach (MusicLibraryItem *song, albumItem->childItems()) { - song->setCheckState(check); - } - setParentState(artistIndex); - emit dataChanged(idx, idx); - break; - } - case MusicLibraryItem::Type_Song: { - item->setCheckState(check); - MusicLibraryItemAlbum *albumItem=static_cast(item->parentItem()); - MusicLibraryItemArtist *artistItem=static_cast(albumItem->parentItem()); - QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex()); - QModelIndex albumIndex=index(albumItem->row(), 0, artistIndex); - setParentState(albumIndex); - setParentState(artistIndex); - emit dataChanged(idx, idx); - break; - } - case MusicLibraryItem::Type_Root: - return false; - } - - return true; - } - return ActionModel::setData(idx, value, role); -} - -void MusicLibraryModel::setParentState(const QModelIndex &parent) -{ - MusicLibraryItemContainer *parentItem=static_cast(parent.internalPointer()); - Qt::CheckState parentCheck=parentItem->checkState(); - bool haveCheckedChildren=false; - bool haveUncheckedChildren=false; - bool stop=false; - - foreach (MusicLibraryItem *child, parentItem->childItems()) { - switch (child->checkState()) { - case Qt::PartiallyChecked: - parentCheck=Qt::PartiallyChecked; - stop=true; - break; - case Qt::Unchecked: - haveUncheckedChildren=true; - parentCheck=haveCheckedChildren ? Qt::PartiallyChecked : Qt::Unchecked; - stop=haveCheckedChildren && haveUncheckedChildren; - break; - case Qt::Checked: - haveCheckedChildren=true; - parentCheck=haveUncheckedChildren ? Qt::PartiallyChecked : Qt::Checked; - stop=haveCheckedChildren && haveUncheckedChildren; - break; - } - if (stop) { - break; - } - } - - if (parentItem->checkState()!=parentCheck) { - parentItem->setCheckState(parentCheck); - emit dataChanged(parent, parent); - } -} - -void MusicLibraryModel::clear() -{ - const MusicLibraryItemRoot *oldRoot = rootItem; - beginResetModel(); - databaseTime = 0; - rootItem = new MusicLibraryItemRoot; - rootItem->setModel(this); - delete oldRoot; - endResetModel(); - - if (mpdModel) { - AlbumsModel::self()->update(rootItem, false); - } -} - -QModelIndex MusicLibraryModel::findSongIndex(const Song &s) const -{ - MusicLibraryItemArtist *artistItem = rootItem->artist(s, false); - if (artistItem) { - MusicLibraryItemAlbum *albumItem = artistItem->album(s, false); - if (albumItem) { - foreach (MusicLibraryItem *songItem, albumItem->childItems()) { - if (songItem->data()==s.displayTitle()) { - return createIndex(songItem->row(), 0, songItem); - } - } - } - } - - return QModelIndex(); -} - -QModelIndex MusicLibraryModel::findArtistIndex(const QString &artist) const -{ - Song s; - s.artist=artist; - MusicLibraryItemArtist *artistItem = rootItem->artist(s, false); - return artistItem ? index(artistItem->row(), 0, QModelIndex()) : QModelIndex(); -} - -QModelIndex MusicLibraryModel::findAlbumIndex(const QString &artist, const QString &album) const -{ - Song s; - s.artist=artist; - s.album=album; - MusicLibraryItemArtist *artistItem = rootItem->artist(s, false); - MusicLibraryItemAlbum *albumItem = artistItem ? artistItem->album(s, false) : 0; - return albumItem ? index(albumItem->row(), 0, index(artistItem->row(), 0, QModelIndex())) : QModelIndex(); -} - -void MusicLibraryModel::removeCache() -{ - QString cacheFile(cacheFileName()); - if (QFile::exists(cacheFile)) { - QFile::remove(cacheFile); - } - - // Remove old (non-compressed) cache file as well... - QString cacheFileWithoutPort(cacheFileName(false)); - if (cacheFileWithoutPort!=cacheFile && QFile::exists(cacheFileWithoutPort)) { - QFile::remove(cacheFileWithoutPort); - } - - // Remove old (non-compressed) cache file as well... - QString oldCache=cacheFile; - oldCache.replace(constLibraryCompressedExt, constLibraryExt); - if (oldCache!=cacheFile && QFile::exists(oldCache)) { - QFile::remove(oldCache); - } - - databaseTime = 0; -} - -QSet MusicLibraryModel::getAlbumArtists() -{ - QSet a; - foreach (MusicLibraryItem *i, rootItem->childItems()) { - a.insert(i->data()); - } - return a; -} - -void MusicLibraryModel::checkForNewSongs() -{ - foreach (MusicLibraryItem *artist, rootItem->childItems()) { - MusicLibraryItemArtist *artistItem=static_cast(artist); - if (artistItem->isNew()) { - emit haveNewItems(true); - return; - } - } -} - -void MusicLibraryModel::clearNewState() -{ - foreach (MusicLibraryItem *artist, rootItem->childItems()) { - MusicLibraryItemArtist *artistItem=static_cast(artist); - if (artistItem->isNew()) { - QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex()); - foreach (MusicLibraryItem *album, artistItem->childItems()) { - MusicLibraryItemAlbum *albumItem=static_cast(album); - if (albumItem->isNew()) { - albumItem->setIsNew(false); - QModelIndex albumIndex=index(albumItem->row(), 0, artistIndex); - emit dataChanged(albumIndex, albumIndex); - } - } - artistItem->setIsNew(false); - emit dataChanged(artistIndex, artistIndex); - } - } - emit haveNewItems(false); -} - -void MusicLibraryModel::updateMusicLibrary(MusicLibraryItemRoot *newroot, time_t dbUpdate, bool fromFile) -{ - if (!mpdModel || (databaseTime>0 && databaseTime >= dbUpdate)) { - delete newroot; - return; - } - - bool updatedSongs=false; - bool needToSave=0==databaseTime || (validCacheDate(dbUpdate) && dbUpdate>databaseTime); - bool incremental=rootItem->childCount() && newroot->childCount(); - - if (incremental && !QFile::exists(cacheFileName())) { - incremental=false; - } - - databaseTime = dbUpdate; - if (incremental) { - updatedSongs=update(newroot->allSongs()); - if (updatedSongs) { - checkForNewSongs(); - } - delete newroot; - } else { - const MusicLibraryItemRoot *oldRoot = rootItem; - beginResetModel(); - rootItem = newroot; - rootItem->setModel(this); - delete oldRoot; - endResetModel(); - updatedSongs=true; - } - - // MPD proxy DB plugin (MPD < 0.18.5) does not provide a datetime for the DB. Also, Mopidy - // returns 0 for the database time (which equates to 1am Jan 1st 1970!). Therefore, in these - // cases we just use current datetime so that we dont keep requesting DB listing each time - // Cantata starts... - // - // Mopidy users, and users of the proxy DB plugin, will have to force Cantata to refresh :-( - if (!fromFile) { - databaseTimeUnreliable=!validCacheDate(dbUpdate); // See note in updatingMpd() - } - if (!validCacheDate(databaseTime) && !validCacheDate(dbUpdate)) { - databaseTime=QDateTime::currentDateTime().toTime_t(); - } - - if (!fromFile && (needToSave || updatedSongs)) { - rootItem->toXML(cacheFileName(), databaseTime, databaseTimeUnreliable); - } - - AlbumsModel::self()->update(rootItem, incremental); - emit updateGenres(rootItem->genres()); - #ifdef ENABLE_UBUNTU - if (updatedSongs) { - emit updated(); - } - #endif -} - -void MusicLibraryModel::updatingMpd() -{ - // MPD/Mopidy is being updated. If MPD's database-time is not reliable (as is the case for older proxy DBs, and Mopidy) - // then we set the databaseTime to NOW when updated. This means we will miss any updates. So, for these scenarios, when - // a user presses 'Refresh Database' in Cantata's main window, we need to reset our view of the databaseTime to null, so - // that we update. This does mean that we will ALWAYS fetch the whole listing - but we have no way of knowing if it changed - // or not :-( - if (databaseTimeUnreliable) { - removeCache(); - } -} - -void MusicLibraryModel::setArtistImage(const Song &song, const QImage &img, const QString &file) -{ - #ifdef ENABLE_UBUNTU - - if (img.isNull() || song.file.startsWith("http://") || song.name().startsWith("http://")) { - if (!file.isEmpty()) return; //If empty, we need to execute the stuff below to set m_coverRequested to false - } - - MusicLibraryItemArtist *artistItem = rootItem->artist(song, false); - - if (artistItem && artistItem->coverName().isEmpty()) { - artistItem->setCover(file); //Always execute to set m_coverRequested to false - if (!file.isEmpty()) { - QModelIndex idx=index(artistItem->row(), 0, QModelIndex()); - emit dataChanged(idx, idx); - } - } - #else - Q_UNUSED(song) - Q_UNUSED(img) - Q_UNUSED(file) - #endif -} - -bool MusicLibraryModel::update(const QSet &songs) -{ - bool updatedSongs=rootItem->update(songs); - - if (updatedSongs && checkable) { - QSet chkdSongs; - foreach (MusicLibraryItem *artist, rootItem->childItems()) { - MusicLibraryItemArtist *artistItem=static_cast(artist); - QModelIndex artistIndex; - int numCheckedAlbums=0; - int numUnCheckedAlbums=0; - int numPartialAlbums=0; - - foreach (MusicLibraryItem *album, artistItem->childItems()) { - MusicLibraryItemAlbum *albumItem=static_cast(album); - int numCheckedSongs=0; - int numUnCheckedSongs=0; - foreach (MusicLibraryItem *song, albumItem->childItems()) { - if (Qt::Unchecked==song->checkState()) { - numUnCheckedSongs++; - } else { - chkdSongs.insert(static_cast(song)->song()); - numCheckedSongs++; - } - } - Qt::CheckState albumState=numCheckedAlbums && numUnCheckedAlbums ? Qt::PartiallyChecked : numCheckedAlbums ? Qt::Checked : Qt::Unchecked; - if (albumState!=albumItem->checkState()) { - albumItem->setCheckState(albumState); - if (!artistIndex.isValid()) { - artistIndex=index(artistItem->row(), 0, QModelIndex()); - } - QModelIndex albumIndex=index(albumItem->row(), 0, artistIndex); - emit dataChanged(albumIndex, albumIndex); - } - - switch (albumState) { - case Qt::PartiallyChecked: numPartialAlbums++; break; - case Qt::Checked: numCheckedAlbums++; break; - case Qt::Unchecked: numUnCheckedAlbums++; break; - } - } - - Qt::CheckState artistState=numPartialAlbums ? Qt::PartiallyChecked : numCheckedAlbums ? Qt::Checked : Qt::Unchecked; - if (artistState!=artistItem->checkState()) { - artistItem->setCheckState(artistState); - if (!artistIndex.isValid()) { - artistIndex=index(artistItem->row(), 0, QModelIndex()); - } - emit dataChanged(artistIndex, artistIndex); - } - } - emit checkedSongs(chkdSongs); - } - - return updatedSongs; -} - -//void MusicLibraryModel::uncheckAll() -//{ -// if (!checkable) { -// return; -// } - -// foreach (MusicLibraryItem *artist, rootItem->childItems()) { -// MusicLibraryItemArtist *artistItem=static_cast(artist); -// QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex()); - -// foreach (MusicLibraryItem *album, artistItem->childItems()) { -// MusicLibraryItemAlbum *albumItem=static_cast(album); -// QModelIndex albumIndex=index(albumItem->row(), 0, artistIndex); - -// foreach (MusicLibraryItem *song, albumItem->childItems()) { -// if (Qt::Unchecked!=song->checkState()) { -// song->setCheckState(Qt::Unchecked); -// QModelIndex songIndex=index(song->row(), 0, albumIndex); -// emit dataChanged(songIndex, songIndex); -// } -// } -// if (Qt::Unchecked!=albumItem->checkState()) { -// albumItem->setCheckState(Qt::Unchecked); -// emit dataChanged(albumIndex, albumIndex); -// } -// } -// if (Qt::Unchecked!=artistItem->checkState()) { -// artistItem->setCheckState(Qt::Unchecked); -// emit dataChanged(artistIndex, artistIndex); -// } -// } -// emit checkedSongs(QSet()); -//} - -void MusicLibraryModel::toggleGrouping() -{ - beginResetModel(); - rootItem->toggleGrouping(); - rootItem->toXML(cacheFileName(), databaseTime, databaseTimeUnreliable); - endResetModel(); - if (mpdModel) { - AlbumsModel::self()->update(rootItem, false); - } -} - -QList MusicLibraryModel::getAlbumTracks(const Song &s) const -{ - QList songs; - MusicLibraryItemArtist *artistItem = rootItem->artist(s, false); - if (artistItem) { - MusicLibraryItemAlbum *albumItem = artistItem->album(s, false); - if (albumItem) { - foreach (MusicLibraryItem *songItem, albumItem->childItems()) { - songs.append(static_cast(songItem)->song()); - } - qSort(songs); - } - } - return songs; -} - -QList MusicLibraryModel::getArtistAlbumsFirstTracks(const Song &song) const -{ - //QString artist=song.isVariousArtists() ? song.artist : song.albumArtist(); - QString basicArtist=song.basicArtist(); - QList tracks; - bool foundCurrent=false; - bool checkAll=!song.isVariousArtists() && song.albumArtist()!=basicArtist; - - foreach (MusicLibraryItem *ar, rootItem->childItems()) { - if (static_cast(ar)->isVarious() || checkAll) { - foreach (MusicLibraryItem *al, static_cast(ar)->childItems()) { - MusicLibraryItemAlbum *a=static_cast(al); - if (a->containsArtist(basicArtist)) { - Song first=static_cast(a->childItems().first())->song(); - tracks.append(first); - if (!foundCurrent && first.album==song.album && first.albumArtist()==song.albumArtist()) { - foundCurrent=true; - } - } - } - } else if (ar->data()==basicArtist) { // i.e. album-artist == basic artist (most cases!) - foreach (MusicLibraryItem *al, static_cast(ar)->childItems()) { - MusicLibraryItemContainer *a=static_cast(al); - if (!a->childItems().isEmpty()) { - Song first=static_cast(a->childItems().first())->song(); - tracks.append(first); - if (!foundCurrent && first.album==song.album && first.albumArtist()==song.albumArtist()) { - foundCurrent=true; - } - } - } - } - } - if (!foundCurrent) { - tracks.append(song); - } - return tracks; -} - -// Currently ONLY artist images are always loaded from non UI thread. -void MusicLibraryModel::coverLoaded(const Song &song, int size) -{ - Q_UNUSED(size) - #ifdef ENABLE_ONLINE_SERVICES - if (song.isFromOnlineService()) { - return; - } - #endif - if (song.isCdda() || song.file.startsWith("http://") || song.name().startsWith("http://")) { - return; - } - if (song.isArtistImageRequest() || song.isComposerImageRequest()) { - MusicLibraryItemArtist *artistItem = rootItem->artist(song, false); - if (artistItem) { - QModelIndex idx=index(artistItem->row(), 0, QModelIndex()); - emit dataChanged(idx, idx); - } - } else { - MusicLibraryItemArtist *artistItem = rootItem->artist(song, false); - if (artistItem) { - MusicLibraryItemAlbum *albumItem = artistItem->album(song, false); - if (albumItem) { - QModelIndex idx=index(albumItem->row(), 0, index(artistItem->row(), 0, QModelIndex())); - emit dataChanged(idx, idx); - } - } - } -} - -void MusicLibraryModel::setCover(const Song &song, const QImage &img, const QString &file) -{ - if (img.isNull() || file.isEmpty() || - song.isCdda() || song.file.startsWith("http:/") || song.name().startsWith("http:/")) { - return; - } - #ifdef ENABLE_ONLINE_SERVICES - if (song.isFromOnlineService()) { - return; - } - #endif - - MusicLibraryItemArtist *artistItem = rootItem->artist(song, false); - if (artistItem) { - MusicLibraryItemAlbum *albumItem = artistItem->album(song, false); - #ifdef ENABLE_UBUNTU - if (albumItem && albumItem->coverName().isEmpty()) { - albumItem->setCover(file); - QModelIndex idx=index(albumItem->row(), 0, index(artistItem->row(), 0, QModelIndex())); - emit dataChanged(idx, idx); - } - #else - if (albumItem) { - QModelIndex idx=index(albumItem->row(), 0, index(artistItem->row(), 0, QModelIndex())); - emit dataChanged(idx, idx); - } - #endif - } -} - -/** - * Read an xml file from disk. - * - * @param filename The name of the xmlfile to read the db from - * - * @return true on succesfull parsing, false otherwise - * TODO: check for hostname - * TODO: check for database version - */ -bool MusicLibraryModel::fromXML() -{ - // If socket connection used, then check if cache file has port number... - QString withPort=cacheFileName(); - QString withoutPort=cacheFileName(false); - if (withPort!=withoutPort && QFile::exists(withoutPort) && !QFile::exists(withPort)) { - QFile::rename(withoutPort, withPort); - } - - convertCache(cacheFileName()); - MusicLibraryItemRoot *root=new MusicLibraryItemRoot; - time_t date=root->fromXML(cacheFileName(), MPDStats::self()->dbUpdate(), &databaseTimeUnreliable, QString(), 0, this); - if (!date) { - delete root; - return false; - } - updateMusicLibrary(root, date, true); - return true; -} - -Qt::ItemFlags MusicLibraryModel::flags(const QModelIndex &index) const -{ - if (index.isValid()) { - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | (checkable ? Qt::ItemIsUserCheckable : Qt::NoItemFlags); - } - return Qt::ItemIsDropEnabled; -} - -QStringList MusicLibraryModel::filenames(const QModelIndexList &indexes, bool allowPlaylists) const -{ - QList songList=songs(indexes, allowPlaylists); - QStringList fnames; - foreach (const Song &s, songList) { - fnames.append(s.file); - } - return fnames; -} - -static inline void addSong(const MusicLibraryItem *song, QList &insertInto, QSet &checkAgainst, bool allowPlaylists) -{ - if (MusicLibraryItem::Type_Song==song->itemType() && - (allowPlaylists || Song::Playlist!=static_cast(song)->song().type) && - !checkAgainst.contains(static_cast(song)->file())) { - insertInto << static_cast(song)->song(); - checkAgainst << static_cast(song)->file(); - } -} - -QList MusicLibraryModel::songs(const QModelIndexList &indexes, bool allowPlaylists) const -{ - QList songs; - QSet files; - - foreach(QModelIndex index, indexes) { - const MusicLibraryItem *item = static_cast(index.internalPointer()); - - switch (item->itemType()) { - case MusicLibraryItem::Type_Artist: { - // First, sort all albums as they would appear in UI... - QList artistAlbums=static_cast(item)->childItems(); - qSort(artistAlbums.begin(), artistAlbums.end(), MusicLibraryItemAlbum::lessThan); - - foreach (MusicLibraryItem *i, artistAlbums) { - QList albumSongs; - const MusicLibraryItemSong *cue=allowPlaylists ? static_cast(i)->getCueFile() : 0; - if (cue) { - addSong(cue, albumSongs, files, true); - } else { - foreach (const MusicLibraryItem *song, static_cast(i)->childItems()) { - addSong(song, albumSongs, files, false); - } - } - qSort(albumSongs); - songs << albumSongs; - } - break; - } - case MusicLibraryItem::Type_Album: { - QList albumSongs; - const MusicLibraryItemSong *cue=allowPlaylists ? static_cast(item)->getCueFile() : 0; - if (cue) { - addSong(cue, albumSongs, files, true); - } else { - foreach (const MusicLibraryItem *song, static_cast(item)->childItems()) { - addSong(song, albumSongs, files, false); - } - } - qSort(albumSongs); - songs << albumSongs; - break; - } - case MusicLibraryItem::Type_Song: - addSong(item, songs, files, allowPlaylists); - break; - default: - break; - } - } - - return songs; -} - -QList MusicLibraryModel::songs(const QStringList &filenames, bool insertNotFound) const -{ - QList songs; - - if (filenames.isEmpty()) { - return songs; - } - - QSet files=filenames.toSet(); - - foreach (const MusicLibraryItem *artist, rootItem->childItems()) { - foreach (const MusicLibraryItem *album, static_cast(artist)->childItems()) { - foreach (const MusicLibraryItem *song, static_cast(album)->childItems()) { - QSet::Iterator it=files.find(static_cast(song)->file()); - - if (it!=files.end()) { - files.erase(it); - songs.append(static_cast(song)->song()); - } - } - } - } - - if (insertNotFound && files.size()) { - foreach (const QString &file, files) { - Song s; - s.file=file; - songs.append(s); - } - } - return songs; -} - -#ifndef ENABLE_UBUNTU -/** -* Convert the data at indexes into mimedata ready for transport -* -* @param indexes The indexes to pack into mimedata -* @return The mimedata -*/ -QMimeData *MusicLibraryModel::mimeData(const QModelIndexList &indexes) const -{ - QMimeData *mimeData = new QMimeData(); - QStringList files=filenames(indexes, true); - PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files); -// if (!MPDConnection::self()->getDetails().dir.isEmpty()) { -// QStringList paths; -// foreach (const QString &f, files) { -// paths << MPDConnection::self()->getDetails().dir+f; -// } -// PlayQueueModel::encode(*mimeData, PlayQueueModel::constUriMimeType, paths); -// } - return mimeData; -} -#endif - diff --git a/models/musiclibrarymodel.h b/models/musiclibrarymodel.h deleted file mode 100644 index 3299ae37b..000000000 --- a/models/musiclibrarymodel.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - */ -/* - * Copyright (c) 2008 Sander Knopper (sander AT knopper DOT tk) and - * Roeland Douma (roeland AT rullzer DOT com) - * - * This file is part of QtMPC. - * - * QtMPC is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * QtMPC 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 QtMPC. If not, see . - */ - -#ifndef MUSIC_LIBRARY_MODEL_H -#define MUSIC_LIBRARY_MODEL_H - -#include -#include -#include "musiclibraryitemroot.h" -#include "musiclibraryitemalbum.h" -#include "mpd-interface/song.h" -#include "musicmodel.h" - -class QMimeData; -class MusicLibraryItemArtist; - -class MusicLibraryModel : public MusicModel, public MusicLibraryErrorMonitor -{ - Q_OBJECT - -public: - static const QLatin1String constLibraryCache; - static const QLatin1String constLibraryExt; - static const QLatin1String constLibraryCompressedExt; - - static MusicLibraryModel * self(); - - static void convertCache(const QString &compressedName); - static void cleanCache(); - static bool validCacheDate(time_t dt) { return dt>0; } - - MusicLibraryModel(QObject *parent=0, bool isMpdModel=true, bool isCheckable=false); - ~MusicLibraryModel(); - void readConfig(); - QModelIndex index(int, int, const QModelIndex & = QModelIndex()) const; - QModelIndex parent(const QModelIndex &) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &, int) const; - bool setData(const QModelIndex &idx, const QVariant &value, int role = Qt::EditRole); - Qt::ItemFlags flags(const QModelIndex &index) const; - QStringList filenames(const QModelIndexList &indexes, bool allowPlaylists=false) const; - QList songs(const QModelIndexList &indexes, bool allowPlaylists=false) const; - QList songs(const QStringList &filenames, bool insertNotFound=false) const; - #ifndef ENABLE_UBUNTU - QMimeData *mimeData(const QModelIndexList &indexes) const; - #endif - const MusicLibraryItemRoot * root() const { return rootItem; } - const MusicLibraryItemRoot * root(const MusicLibraryItem *) const { return root(); } - bool isFromSingleTracks(const Song &s) const { return rootItem->isFromSingleTracks(s); } - bool fromXML(); - void clear(); - QModelIndex findSongIndex(const Song &s) const; - QModelIndex findArtistIndex(const QString &artist) const; - QModelIndex findAlbumIndex(const QString &artist, const QString &album) const; - const MusicLibraryItem * findSong(const Song &s) const; - bool songExists(const Song &s) const { return rootItem->songExists(s); } - bool updateSong(const Song &orig, const Song &edit) { return rootItem->updateSong(orig, edit); } - void addSongToList(const Song &s) { rootItem->addSongToList(s); } - void removeSongFromList(const Song &s) { rootItem->removeSongFromList(s); } - void updateSongFile(const Song &from, const Song &to) { rootItem->updateSongFile(from, to); } - void removeCache(); - void getDetails(QSet &artists, QSet &albumArtists, QSet &composers, QSet &albums, QSet &genres) - { rootItem->getDetails(artists, albumArtists, composers, albums, genres); } - QSet getAlbumArtists(); - bool update(const QSet &songs); -// void uncheckAll(); - time_t lastUpdate() { return databaseTime; } - void setSupportsAlbumArtistTag(bool s) { rootItem->setSupportsAlbumArtistTag(s); } - void toggleGrouping(); - const QSet & genres() const { return rootItem->genres(); } - // Get tracks of an album - QList getAlbumTracks(const Song &s) const; - // Get 1 track from each album by artist - used to create context view backdrop! - QList getArtistAlbumsFirstTracks(const Song &song) const; - void checkForNewSongs(); - -public Q_SLOTS: - void clearNewState(); - void updateMusicLibrary(MusicLibraryItemRoot * root, time_t dbUpdate = 0, bool fromFile = false); - void coverLoaded(const Song &song, int size); - void updatingMpd(); - // Touch version... - void setArtistImage(const Song &song, const QImage &img, const QString &file); - void setCover(const Song &song, const QImage &img, const QString &file); - -Q_SIGNALS: - void haveNewItems(bool); -// void updated(const MusicLibraryItemRoot *root); - void updateGenres(const QSet &genres); - void checkedSongs(const QSet &songs); - void error(const QString &msg); - - // Used in Touch variant only... - void updated(); - -private: - void setParentState(const QModelIndex &parent); - void loadError(const QString &msg) { emit error(msg); } - -private: - bool mpdModel; - bool checkable; - bool artistImages; - MusicLibraryItemRoot *rootItem; - time_t databaseTime; - bool databaseTimeUnreliable; -}; - -#endif diff --git a/models/musiclibraryproxymodel.cpp b/models/musiclibraryproxymodel.cpp index ad4e81ff0..ee12acd85 100644 --- a/models/musiclibraryproxymodel.cpp +++ b/models/musiclibraryproxymodel.cpp @@ -94,10 +94,6 @@ bool MusicLibraryProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex & const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const MusicLibraryItem *item = static_cast(index.internalPointer()); - if (!filterGenre.isEmpty() && !item->hasGenre(filterGenre)) { - return false; - } - if (filter) { if (item==filter) { // Accept top-level item! return true; diff --git a/models/onlineservicesmodel.cpp b/models/onlineservicesmodel.cpp index eb40f5007..026d5b6b6 100644 --- a/models/onlineservicesmodel.cpp +++ b/models/onlineservicesmodel.cpp @@ -27,7 +27,6 @@ #include "musiclibraryitemsong.h" #include "musiclibraryitemroot.h" #include "musiclibraryitempodcast.h" -#include "musiclibrarymodel.h" #include "dirviewmodel.h" #include "onlineservicesmodel.h" #include "online/onlineservice.h" @@ -368,23 +367,11 @@ void OnlineServicesModel::removeService(const QString &name) beginRemoveRows(QModelIndex(), idx, idx); collections.takeAt(idx); endRemoveRows(); - MultiMusicModel::updateGenres(); // Destroy will stop service, and delete it (via deleteLater()) srv->destroy(); } } -void OnlineServicesModel::stateChanged(const QString &name, bool state) -{ - if (!state) { - int idx=indexOf(name); - if (idx<0) { - return; - } - MultiMusicModel::updateGenres(); - } -} - void OnlineServicesModel::load() { QSet hidden=Settings::self()->hiddenOnlineProviders().toSet(); @@ -554,7 +541,6 @@ void OnlineServicesModel::setHiddenProviders(const QSet &prov) // if (added && collections.count()>1) { // emit needToSort(); // } - updateGenres(); if (changed) { emit providersChanged(); } diff --git a/models/onlineservicesmodel.h b/models/onlineservicesmodel.h index 6e68008ba..b905a3366 100644 --- a/models/onlineservicesmodel.h +++ b/models/onlineservicesmodel.h @@ -93,9 +93,6 @@ public: QList getProviders() const; void setHiddenProviders(const QSet &prov); -public Q_SLOTS: - void stateChanged(const QString &name, bool state); - private: OnlineService * addService(const QString &name, const QSet &hidden); void removeService(const QString &name); diff --git a/models/playlistsmodel.cpp b/models/playlistsmodel.cpp index 7169f940c..dac0a6839 100644 --- a/models/playlistsmodel.cpp +++ b/models/playlistsmodel.cpp @@ -845,13 +845,11 @@ void PlaylistsModel::playlistInfoRetrieved(const QString &name, const QListupdateGenres(); emit updated(idx); emit dataChanged(idx, idx); } else { emit listPlaylists(); } - updateGenreList(); } void PlaylistsModel::removedFromPlaylist(const QString &name, const QList &positions) @@ -886,10 +884,8 @@ void PlaylistsModel::removedFromPlaylist(const QString &name, const QListupdateGenres(); } emit updated(parent); - updateGenreList(); } void PlaylistsModel::movedInPlaylist(const QString &name, const QList &idx, quint32 pos) @@ -1021,20 +1017,6 @@ void PlaylistsModel::clearPlaylists() qDeleteAll(items); items.clear(); - updateGenreList(); -} - -void PlaylistsModel::updateGenreList() -{ - QSet newGenres; - foreach (PlaylistItem *p, items) { - newGenres+=p->genres; - } - - if (newGenres!=plGenres) { - plGenres=newGenres; - emit updateGenres(plGenres); - } } quint32 PlaylistsModel::allocateKey() @@ -1049,18 +1031,6 @@ quint32 PlaylistsModel::allocateKey() return 0xFFFFFFFF; } -PlaylistsModel::SongItem::SongItem(const Song &s, PlaylistItem *p) - : Song(s) - , parent(p) - , genreSet(0) -{ - QStringList g=genres(); - if (g.count()>1) { - genreSet=new QSet(); - *genreSet=g.toSet(); - } -} - PlaylistsModel::PlaylistItem::PlaylistItem(const Playlist &pl, quint32 k) : name(pl.name) , time(0) @@ -1078,21 +1048,10 @@ PlaylistsModel::PlaylistItem::~PlaylistItem() clearSongs(); } -void PlaylistsModel::PlaylistItem::updateGenres() -{ - genres.clear(); - foreach (const SongItem *s, songs) { - if (!s->genre.isEmpty()) { - genres+=s->allGenres(); - } - } -} - void PlaylistsModel::PlaylistItem::clearSongs() { qDeleteAll(songs); songs.clear(); - updateGenres(); } PlaylistsModel::SongItem * PlaylistsModel::PlaylistItem::getSong(const Song &song, int offset) diff --git a/models/playlistsmodel.h b/models/playlistsmodel.h index c7853b1ca..1fbd5c8dd 100644 --- a/models/playlistsmodel.h +++ b/models/playlistsmodel.h @@ -66,14 +66,10 @@ public: struct PlaylistItem; struct SongItem : public Item, public Song { - SongItem() : parent(0), genreSet(0) { } - SongItem(const Song &s, PlaylistItem *p=0); - virtual ~SongItem() { delete genreSet; } + SongItem() : parent(0) { } + SongItem(const Song &s, PlaylistItem *p=0) : Song(s), parent(p) { } bool isPlaylist() { return false; } - bool hasGenre(const QString &g) const { return genreSet ? genreSet->contains(g) : genre==g; } - QSet allGenres() const { return genreSet ? *genreSet : genres().toSet(); } PlaylistItem *parent; - QSet *genreSet; // Only store genres in a set if more than 1 }; struct PlaylistItem : public Item @@ -82,7 +78,6 @@ public: PlaylistItem(const Playlist &pl, quint32 k); virtual ~PlaylistItem(); bool isPlaylist() { return true; } - void updateGenres(); SongItem * getSong(const Song &song, int offset); void clearSongs(); quint32 totalTime(); @@ -92,7 +87,6 @@ public: bool loaded; bool isSmartPlaylist; QList songs; - QSet genres; quint32 time; quint32 key; QDateTime lastModified; @@ -133,7 +127,6 @@ public: #ifndef ENABLE_UBUNTU QMenu * menu(); #endif - const QSet & genres() { return plGenres; } static QString strippedText(QString s); void setMultiColumn(bool m) { multiCol=m; } @@ -147,7 +140,6 @@ Q_SIGNALS: void addToNew(); void addToExisting(const QString &name); - void updateGenres(const QSet &genres); void updated(const QModelIndex &idx); void playlistRemoved(quint32 key); @@ -165,7 +157,6 @@ private Q_SLOTS: void coverLoaded(const Song &song, int s); private: - void updateGenreList(); void updateItemMenu(bool craete=false); PlaylistItem * getPlaylist(const QString &name); void clearPlaylists(); @@ -176,7 +167,6 @@ private: bool multiCol; QList items; QSet usedKeys; - QSet plGenres; #ifndef ENABLE_UBUNTU QMenu *itemMenu; quint32 dropAdjust; diff --git a/models/playlistsproxymodel.cpp b/models/playlistsproxymodel.cpp index 3f8ae1bef..abc429542 100644 --- a/models/playlistsproxymodel.cpp +++ b/models/playlistsproxymodel.cpp @@ -51,10 +51,6 @@ bool PlaylistsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sou if (item->isPlaylist()) { PlaylistsModel::PlaylistItem *pl = static_cast(item); - if (!filterGenre.isEmpty() && !pl->genres.contains(filterGenre)) { - return false; - } - if (matchesFilter(QStringList() << pl->name)) { return true; } @@ -66,10 +62,6 @@ bool PlaylistsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sou } } else { PlaylistsModel::SongItem *s = static_cast(item); - - if (!filterGenre.isEmpty() && !s->hasGenre(filterGenre)) { - return false; - } return matchesFilter(*s); } diff --git a/models/proxymodel.cpp b/models/proxymodel.cpp index bc851e700..cf4230d01 100644 --- a/models/proxymodel.cpp +++ b/models/proxymodel.cpp @@ -92,11 +92,11 @@ bool ProxyModel::matchesFilter(const QStringList &strings) const return false; } //#include -bool ProxyModel::update(const QString &txt, const QString &genre) +bool ProxyModel::update(const QString &txt) { QString text=txt.length()<2 ? QString() : txt; -// qWarning() << "UPDATE" << txt << genre << (void *)f; - if (text==origFilterText && genre==filterGenre) { +// qWarning() << "UPDATE" << txt << (void *)f; + if (text==origFilterText) { // qWarning() <<"NO CHANGE"; return false; } @@ -110,9 +110,8 @@ bool ProxyModel::update(const QString &txt, const QString &genre) } origFilterText=text; - filterGenre=genre; - if (text.isEmpty() && genre.isEmpty()) { + if (text.isEmpty()) { if (filterEnabled) { filterEnabled=false; if (!wasEmpty) { diff --git a/models/proxymodel.h b/models/proxymodel.h index 52cef2bda..1d953a843 100644 --- a/models/proxymodel.h +++ b/models/proxymodel.h @@ -37,12 +37,12 @@ public: ProxyModel(QObject *parent) : QSortFilterProxyModel(parent), isSorted(false), filterEnabled(false), filter(0) { } virtual ~ProxyModel() { } - bool update(const QString &text, const QString &genre=QString()); + bool update(const QString &text); const void * filterItem() const { return filter; } void setFilterItem(void *f) { filter=f; } void setRootIndex(const QModelIndex &idx) { rootIndex=idx.isValid() ? mapToSource(idx) : idx; } bool isChildOfRoot(const QModelIndex &idx) const; - bool isEmpty() const { return filterGenre.isEmpty() && filterStrings.isEmpty() && 0==filter; } + bool isEmpty() const { return filterStrings.isEmpty() && 0==filter; } bool enabled() const { return filterEnabled; } const QString & filterText() const { return origFilterText; } void resort(); @@ -66,7 +66,6 @@ private: protected: bool isSorted; bool filterEnabled; - QString filterGenre; QModelIndex rootIndex; QString origFilterText; QStringList filterStrings; diff --git a/models/sqllibrarymodel.cpp b/models/sqllibrarymodel.cpp new file mode 100644 index 000000000..f3a5ffc0e --- /dev/null +++ b/models/sqllibrarymodel.cpp @@ -0,0 +1,431 @@ +/* + * Cantata + * + * Copyright (c) 2015 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "sqllibrarymodel.h" +#include "playqueuemodel.h" +#include "gui/plurals.h" +#include "gui/settings.h" +#include "widgets/icons.h" +#include "support/localize.h" +#include "roles.h" +#include +#if defined ENABLE_MODEL_TEST +#include "modeltest.h" +#endif + +static QString parentData(const SqlLibraryModel::Item *i) +{ + QString data; + const SqlLibraryModel::Item *itm=i; + + while (itm->getParent()) { + if (!itm->getParent()->getText().isEmpty()) { + if (SqlLibraryModel::T_Root==itm->getParent()->getType()) { + data=""+itm->getParent()->getText()+"
    "+data; + } else { + data=itm->getParent()->getText()+"
    "+data; + } + } + itm=itm->getParent(); + } + return data; +} + +const QLatin1String SqlLibraryModel::constGroupGenre("genre"); +const QLatin1String SqlLibraryModel::constGroupArtist("artist"); +const QLatin1String SqlLibraryModel::constGroupAlbum("album"); + +SqlLibraryModel::SqlLibraryModel(LibraryDb *d, QObject *p) + : ActionModel(p) + , tl(T_Artist) + , root(0) + , db(d) + , librarySort(LibraryDb::constArtistAlbumsSortYear) + , albumSort(LibraryDb::constAlbumsSortAlArYr) +{ + connect(db, SIGNAL(libraryUpdated()), SLOT(libraryUpdated())); + #if defined ENABLE_MODEL_TEST + new ModelTest(this, this); + #endif +} + +void SqlLibraryModel::clear() +{ + db->clear(); +} + +void SqlLibraryModel::settings(const QString &top, const QString &lib, const QString &al) +{ + Type t=T_Artist; + if (top==constGroupGenre) { + t=T_Genre; + } else if (top==constGroupAlbum) { + t=T_Album; + } + + bool changed= t!=tl || (T_Album!=t && lib!=librarySort) || (T_Album==t && al!=albumSort); + tl=t; + librarySort=lib; + albumSort=al; + if (changed) { + libraryUpdated(); + } +} + +void SqlLibraryModel::libraryUpdated() +{ + beginResetModel(); + delete root; + root=new CollectionItem(T_Root, QString()); + switch (tl) { + case T_Genre: { + QList genres=db->getGenres(); + if (!genres.isEmpty()) { + foreach (const LibraryDb::Genre &genre, genres) { + root->add(new CollectionItem(T_Genre, genre.name, genre.name, Plurals::artists(genre.artistCount), root)); + } + } + break; + } + case T_Artist: { + QList artists=db->getArtists(); + if (!artists.isEmpty()) { + foreach (const LibraryDb::Artist &artist, artists) { + root->add(new CollectionItem(T_Artist, artist.name, artist.name, Plurals::albums(artist.albumCount), root)); + } + } + break; + } + case T_Album: { + QList albums=db->getAlbums(); + if (!albums.isEmpty()) { + foreach (const LibraryDb::Album &album, albums) { + root->add(new AlbumItem(album.artist, album.id, Song::displayAlbum(album.name, album.year), + T_Album==tl + ? album.artist + : Plurals::tracksWithDuration(album.trackCount, Utils::formatTime(album.duration, true)), root)); + } + } + break; + } + default: + break; + } + + endResetModel(); +} + +void SqlLibraryModel::search(const QString &str) +{ + if (str!=db->getFilter()) { + db->setFilter(str); + libraryUpdated(); + } +} + +Qt::ItemFlags SqlLibraryModel::flags(const QModelIndex &index) const +{ + if (index.isValid()) { + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; + } + return Qt::ItemIsDropEnabled; +} + +QModelIndex SqlLibraryModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + const CollectionItem * p = parent.isValid() ? static_cast(parent.internalPointer()) : root; + const Item * c = rowgetChildCount() ? p->getChildren().at(row) : 0; + return c ? createIndex(row, column, (void *)c) : QModelIndex(); +} + +QModelIndex SqlLibraryModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) { + return QModelIndex(); + } + + const Item * const item = static_cast(child.internalPointer()); + Item * const parentItem = item->getParent(); + if (parentItem == root || 0==parentItem) { + return QModelIndex(); + } + + return createIndex(parentItem->getRow(), 0, parentItem); +} + +int SqlLibraryModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) { + return 0; + } + + const CollectionItem *parentItem=parent.isValid() ? static_cast(parent.internalPointer()) : root; + return parentItem ? parentItem->getChildCount() : 0; +} + +int SqlLibraryModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +bool SqlLibraryModel::hasChildren(const QModelIndex &index) const +{ + Item *item=toItem(index); + return item && T_Track!=item->getType(); +} + +bool SqlLibraryModel::canFetchMore(const QModelIndex &index) const +{ + if (index.isValid()) { + Item *item = toItem(index); + return item && T_Track!=item->getType() && 0==item->getChildCount(); + } else { + return false; + } +} + +void SqlLibraryModel::fetchMore(const QModelIndex &index) +{ + if (!index.isValid()) { + return; + } + + CollectionItem *item = static_cast(toItem(index)); + switch (item->getType()) { + case T_Root: + break; + case T_Genre: { + QList artists=db->getArtists(item->getId()); + if (!artists.isEmpty()) { + beginInsertRows(index, 0, artists.count()-1); + foreach (const LibraryDb::Artist &artist, artists) { + item->add(new CollectionItem(T_Artist, artist.name, artist.name, Plurals::albums(artist.albumCount), item)); + } + endInsertRows(); + } + break; + } + case T_Artist: { + QList albums=db->getAlbums(item->getId(), T_Genre==tl ? item->getParent()->getId() : QString(), T_Album==topLevel() ? albumSort : librarySort); + if (!albums.isEmpty()) { + beginInsertRows(index, 0, albums.count()-1); + foreach (const LibraryDb::Album &album, albums) { + item->add(new CollectionItem(T_Album, album.id, Song::displayAlbum(album.name, album.year), + Plurals::tracksWithDuration(album.trackCount, Utils::formatTime(album.duration, true)), item)); + } + endInsertRows(); + } + break; + } + case T_Album: { + QList songs=T_Album==tl + ? db->getTracks(static_cast(item)->getArtistId(), item->getId()) + : db->getTracks(item->getParent()->getId(), item->getId(), + T_Genre==tl ? item->getParent()->getParent()->getId() : QString()); + + if (!songs.isEmpty()) { + beginInsertRows(index, 0, songs.count()-1); + foreach (const Song &song, songs) { + item->add(new TrackItem(song, item)); + } + endInsertRows(); + } + break; + } + default: + break; + } +} + +QVariant SqlLibraryModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + Item *item = static_cast(index.internalPointer()); + + switch (role) { + case Qt::DecorationRole: + switch (item->getType()) { + case T_Artist: + return /*item-> composer? ? Icons::self()->composerIcon :*/ Icons::self()->artistIcon; + case T_Album: + return Icons::self()->albumIcon; + case T_Track: + return Song::Playlist==item->getSong().type ? Icons::self()->playlistIcon : Icons::self()->audioFileIcon; + default: + return QVariant(); + } + case Cantata::Role_BriefMainText: + if (T_Album==item->getType()) { + return item->getText(); + } + case Cantata::Role_MainText: + case Qt::DisplayRole: + if (T_Track==item->getType()) { + TrackItem *track = static_cast(item); + if (Song::Playlist==track->getSong().type) { + return track->getSong().isCueFile() ? i18n("Cue Sheet") : i18n("Playlist"); + } + } + return item->getText(); + case Qt::ToolTipRole: + if (!Settings::self()->infoTooltips()) { + return QVariant(); + } + if (T_Track==item->getType()) { + return static_cast(item)->getSong().toolTip(); + } + return parentData(item)+ + (0==item->getChildCount() + ? item->getText() + : (item->getText()+"
    "+data(index, Cantata::Role_SubText).toString())); + case Cantata::Role_SubText: + return item->getSubText(); + case Cantata::Role_ListImage: + return T_Album==item->getType(); + case Cantata::Role_TitleText: + return item->getText(); + default: + break; + } + return ActionModel::data(index, role); +} + +QMimeData * SqlLibraryModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData(); + QStringList files=filenames(indexes, true); + PlayQueueModel::encode(*mimeData, PlayQueueModel::constFileNameMimeType, files); + return mimeData; +} + +QList SqlLibraryModel::songs(const QModelIndexList &list, bool allowPlaylists) const +{ + QList songList; + populate(list); + foreach (const QModelIndex &idx, list) { + songList+=songs(idx, allowPlaylists); + } + + return songList; +} + +QStringList SqlLibraryModel::filenames(const QModelIndexList &list, bool allowPlaylists) const +{ + QStringList files; + QList songList=songs(list, allowPlaylists); + foreach (const Song &s, songList) { + files.append(s.file); + } + return files; +} + +QModelIndex SqlLibraryModel::findSongIndex(const Song &song) +{ + // TODO!!! + return QModelIndex(); +} + +QModelIndex SqlLibraryModel::findAlbumIndex(const QString &artist, const QString &album) +{ + // TODO!!! + return QModelIndex(); +} + +QModelIndex SqlLibraryModel::findArtistIndex(const QString &artist) +{ + // TODO!!! + return QModelIndex(); +} + +QSet SqlLibraryModel::getArtists() const +{ + // TODO!!! + return QSet(); +} + +QList SqlLibraryModel::getAlbumTracks(const Song &song) const +{ + // TODO!!! + return QList(); +} + +QList SqlLibraryModel::getArtistAlbums(const QString &artist) const +{ + return db->getAlbumsWithArtist(artist); +} + +void SqlLibraryModel::populate(const QModelIndexList &list) const +{ + foreach (const QModelIndex &idx, list) { + if (canFetchMore(idx)) { + const_cast(this)->fetchMore(idx); + populate(children(idx)); + } + } +} + +QModelIndexList SqlLibraryModel::children(const QModelIndex &parent) const +{ + QModelIndexList list; + + for(int r=0; r SqlLibraryModel::songs(const QModelIndex &idx, bool allowPlaylists) const +{ + QList list; + if (hasChildren(idx)) { + foreach (const QModelIndex &c, children(idx)) { + list+=songs(c, allowPlaylists); + } + } else { + const Item *i=static_cast(idx.internalPointer()); + if (i && T_Track==i->getType() && (allowPlaylists || Song::Playlist!=i->getSong().type)) { + list.append(i->getSong()); + } + } + return list; +} + +void SqlLibraryModel::CollectionItem::add(Item *i) +{ + children.append(i); + i->setRow(children.count()-1); + childMap.insert(i->getUniqueId(), i); +} + +const SqlLibraryModel::Item *SqlLibraryModel::CollectionItem::getChild(const QString &id) const +{ + QMap::ConstIterator it=childMap.find(id); + return childMap.constEnd()==it ? 0 : it.value(); +} diff --git a/models/sqllibrarymodel.h b/models/sqllibrarymodel.h new file mode 100644 index 000000000..b102a0fa2 --- /dev/null +++ b/models/sqllibrarymodel.h @@ -0,0 +1,177 @@ +/* + * Cantata + * + * Copyright (c) 2015 Craig Drummond + * + * ---- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SQL_LIBRARY_MODEL_H +#define SQL_LIBRARY_MODEL_H + +#include "actionmodel.h" +#include "mpd-interface/song.h" +#include "support/utils.h" +#include "db/librarydb.h" +#include + +class SqlLibraryModel : public ActionModel +{ + Q_OBJECT + +public: + + static const QLatin1String constGroupGenre; + static const QLatin1String constGroupArtist; + static const QLatin1String constGroupAlbum; + + enum Type { + T_Root, + T_Genre, + T_Artist, + T_Album, + T_Track + }; + + class CollectionItem; + class Item + { + public: + Item(Type t, CollectionItem *p=0) + : type(t), parent(p) { } + virtual ~Item() { } + + Type getType() const { return type; } + virtual const QString & getId() const =0; + virtual const QString getUniqueId() const { return getId(); } + virtual QString getText() const =0; + virtual QString getSubText() const =0; + CollectionItem * getParent() const { return parent; } + int getRow() const { return row; } + void setRow(int r) { row=r; } + virtual int getChildCount() const { return 0;} + const Song & getSong() const { return song; } + void setSong(const Song &s) { song=s; } + + private: + Type type; + CollectionItem *parent; + int row; + Song song; + }; + + class TrackItem : public Item + { + public: + TrackItem(const Song &s, CollectionItem *p=0) + : Item(T_Track, p) { setSong(s); } + virtual ~TrackItem() { } + + virtual const QString & getId() const { return getSong().file; } + virtual QString getText() const { return getSong().trackAndTitleStr(); } + virtual QString getSubText() const { return Utils::formatTime(getSong().time, true); } + }; + + class CollectionItem : public Item + { + public: + CollectionItem(Type t, const QString &i, const QString &txt=QString(), const QString &sub=QString(), CollectionItem *p=0) + : Item(t, p), id(i), text(txt), subText(sub) { } + virtual ~CollectionItem() { qDeleteAll(children); } + + const QList getChildren() const { return children; } + virtual int getChildCount() const { return children.count();} + void add(Item *i); + const Item * getChild(const QString &id) const; + virtual const QString & getId() const { return id; } + virtual QString getText() const { return text; } + virtual QString getSubText() const { return subText; } + + private: + QString id; + QString text; + QString subText; + QList children; + QMap childMap; + }; + + class AlbumItem : public CollectionItem { + public: + AlbumItem(const QString &ar, const QString &i, const QString &txt=QString(), const QString &sub=QString(), CollectionItem *p=0) + : CollectionItem(T_Album, i, txt, sub, p), artistId(ar) { } + virtual ~AlbumItem() { } + + const QString & getArtistId() const { return artistId; } + const QString getUniqueId() const { return artistId+getId(); } + + private: + QString artistId; + }; + + struct AlbumInfo { + QString albumName; + QString albumId; + QString artistId; + QString genreId; + }; + + SqlLibraryModel(LibraryDb *d, QObject *p); + + void clear(); + void settings(const QString &top, const QString &lib, const QString &al); + Type topLevel() const { return tl; } + void search(const QString &str); + + Qt::ItemFlags flags(const QModelIndex &index) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + bool hasChildren(const QModelIndex &index) const; + bool canFetchMore(const QModelIndex &index) const; + void fetchMore(const QModelIndex &index); + QVariant data(const QModelIndex &index, int role) const; + QMimeData * mimeData(const QModelIndexList &indexes) const; + + QList songs(const QModelIndexList &list, bool allowPlaylists) const; + QStringList filenames(const QModelIndexList &list, bool allowPlaylists) const; + QModelIndex findSongIndex(const Song &song); + QModelIndex findAlbumIndex(const QString &artist, const QString &album); + QModelIndex findArtistIndex(const QString &artist); + QSet getArtists() const; + QList getAlbumTracks(const Song &song) const; + QList getArtistAlbums(const QString &artist) const; + +private Q_SLOTS: + void libraryUpdated(); + +private: + void populate(const QModelIndexList &list) const; + QModelIndexList children(const QModelIndex &parent) const; + QList songs(const QModelIndex &idx, bool allowPlaylists) const; + Item * toItem(const QModelIndex &index) const { return index.isValid() ? static_cast(index.internalPointer()) : root; } + +protected: + Type tl; + CollectionItem *root; + LibraryDb *db; + QString librarySort; + QString albumSort; +}; + +#endif diff --git a/mpd-interface/mpdconnection.cpp b/mpd-interface/mpdconnection.cpp index 951c36d4c..474c15b8e 100644 --- a/mpd-interface/mpdconnection.cpp +++ b/mpd-interface/mpdconnection.cpp @@ -27,7 +27,6 @@ #include "mpdconnection.h" #include "mpdparseutils.h" #ifndef CANTATA_WEB -#include "models/musiclibraryitemroot.h" #include "models/streamsmodel.h" #endif #ifdef ENABLE_SIMPLE_MPD_SUPPORT @@ -539,6 +538,7 @@ void MPDConnection::setDetails(const MPDConnectionDetails &d) details=det; if (diffDetails || State_Connected!=state) { + emit connectionChanged(details); DBUG << "setDetails" << details.hostname << details.port << (details.password.isEmpty() ? false : true); if (State_Connected==state && fadingVolume()) { sendCommand("stop"); @@ -1501,27 +1501,7 @@ void MPDConnection::update() void MPDConnection::loadLibrary() { emit updatingLibrary(dbUpdate); - #ifdef CANTATA_WEB listDirInfo(details.topLevel.isEmpty() ? "/" : details.topLevel); - #else - Response response=alwaysUseLsInfo || !details.topLevel.isEmpty() ? Response(false) : sendCommand("listallinfo", false); - MusicLibraryItemRoot *root=0; - if (response.ok) { - root = new MusicLibraryItemRoot; - MPDParseUtils::parseLibraryItems(response.data, details.dir, ver, mopidy, root); - } else { // MPD >=0.18 can fail listallinfo for large DBs, so get info dir by dir... - root = new MusicLibraryItemRoot; - if (!listDirInfo(details.topLevel.isEmpty() ? "/" : details.topLevel, root)) { - delete root; - root=0; - } - } - - if (root) { - root->applyGrouping(); - emit musicLibraryUpdated(root, dbUpdate); - } - #endif emit updatedLibrary(); } @@ -1888,7 +1868,6 @@ void MPDConnection::toggleStopAfterCurrent(bool afterCurrent) } } -#ifdef CANTATA_WEB bool MPDConnection::listDirInfo(const QString &dir) { bool topLevel="/"==dir; @@ -1908,25 +1887,6 @@ bool MPDConnection::listDirInfo(const QString &dir) return false; } } -#else -bool MPDConnection::listDirInfo(const QString &dir, MusicLibraryItemRoot *root) -{ - bool topLevel="/"==dir; - Response response=sendCommand(topLevel ? "lsinfo" : ("lsinfo "+encodeName(dir))); - if (response.ok) { - QSet childDirs; - MPDParseUtils::parseLibraryItems(response.data, details.dir, ver, mopidy, root, !topLevel, &childDirs); - foreach (const QString &child, childDirs) { - if (!listDirInfo(child, root)) { - return false; - } - } - return true; - } else { - return false; - } -} -#endif #ifdef ENABLE_DYNAMIC bool MPDConnection::checkRemoteDynamicSupport() diff --git a/mpd-interface/mpdconnection.h b/mpd-interface/mpdconnection.h index 5d4419bfd..58bee979b 100644 --- a/mpd-interface/mpdconnection.h +++ b/mpd-interface/mpdconnection.h @@ -43,9 +43,7 @@ #include #ifndef CANTATA_WEB -class MusicLibraryItemArtist; class DirViewItemRoot; -class MusicLibraryItemRoot; #endif class QTimer; class Thread; @@ -328,6 +326,7 @@ public Q_SLOTS: void reverse() { seek(false); } Q_SIGNALS: + void connectionChanged(const MPDConnectionDetails &details); void stateChanged(bool connected); void passwordError(); void currentSongUpdated(const Song &song); @@ -335,10 +334,8 @@ Q_SIGNALS: void statsUpdated(const MPDStatsValues &stats); void statusUpdated(const MPDStatusValues &status); void outputsUpdated(const QList &outputs); - #ifdef CANTATA_WEB void librarySongs(QList *songs); - #else - void musicLibraryUpdated(MusicLibraryItemRoot *root, time_t dbUpdate); + #ifndef CANTATA_WEB void dirViewUpdated(DirViewItemRoot *root, time_t dbUpdate); #endif void playlistsRetrieved(const QList &data); @@ -405,11 +402,7 @@ private: void parseIdleReturn(const QByteArray &data); bool doMoveInPlaylist(const QString &name, const QList &items, quint32 pos, quint32 size); void toggleStopAfterCurrent(bool afterCurrent); - #ifdef CANTATA_WEB bool listDirInfo(const QString &dir); - #else - bool listDirInfo(const QString &dir, MusicLibraryItemRoot *root); - #endif #ifdef ENABLE_DYNAMIC bool checkRemoteDynamicSupport(); bool subscribe(const QByteArray &channel); diff --git a/mpd-interface/mpdparseutils.cpp b/mpd-interface/mpdparseutils.cpp index e589f99c6..6c0923eb3 100644 --- a/mpd-interface/mpdparseutils.cpp +++ b/mpd-interface/mpdparseutils.cpp @@ -33,10 +33,6 @@ #include "models/dirviewitemroot.h" #include "models/dirviewitemdir.h" #include "models/dirviewitemfile.h" -#include "models/musiclibraryitemartist.h" -#include "models/musiclibraryitemalbum.h" -#include "models/musiclibraryitemsong.h" -#include "models/musiclibraryitemroot.h" #endif #include "mpdparseutils.h" #include "mpdstatus.h" @@ -528,25 +524,12 @@ MPDParseUtils::MessageMap MPDParseUtils::parseMessages(const QByteArray &data) } #endif -static bool groupSingleTracks=false; - -bool MPDParseUtils::groupSingle() -{ - return groupSingleTracks; -} - -void MPDParseUtils::setGroupSingle(bool g) -{ - groupSingleTracks=g; -} - -#ifdef CANTATA_WEB void MPDParseUtils::parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion, QList &songs, bool parsePlaylists, QSet *childDirs) { QList currentItem; QList lines = data.split('\n'); int amountOfLines = lines.size(); - + for (int i = 0; i < amountOfLines; i++) { const QByteArray &line=lines.at(i); if (childDirs && line.startsWith(constDirectoryKey)) { @@ -559,14 +542,13 @@ void MPDParseUtils::parseLibraryItems(const QByteArray &data, const QString &mpd if (currentSong.file.isEmpty()) { continue; } - + + // lsinfo / will return all stored playlists - but this is deprecated. + if (Song::Playlist==currentSong.type && !parsePlaylists) { + continue; + } + if (Song::Playlist==currentSong.type && !songs.isEmpty()) { - - // lsinfo / will return all stored playlists - but this is deprecated. - if (!parsePlaylists) { - continue; - } - Song firstSong=songs.at(0); QList cueSongs; // List of songs from cue file QSet cueFiles; // List of source (flac, mp3, etc) files referenced in cue file @@ -689,221 +671,8 @@ void MPDParseUtils::parseLibraryItems(const QByteArray &data, const QString &mpd } } } -#else -void MPDParseUtils::parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion, - bool isMopidy, MusicLibraryItemRoot *rootItem, bool parsePlaylists, - QSet *childDirs) -{ - bool canSplitCue=mpdVersion>=CANTATA_MAKE_VERSION(0,17,0); - QList currentItem; - QList lines = data.split('\n'); - int amountOfLines = lines.size(); - MusicLibraryItemArtist *artistItem = 0; - MusicLibraryItemAlbum *albumItem = 0; - MusicLibraryItemSong *songItem = 0; - - for (int i = 0; i < amountOfLines; i++) { - const QByteArray &line=lines.at(i); - if (childDirs && line.startsWith(constDirectoryKey)) { - childDirs->insert(QString::fromUtf8(line.mid(constDirectoryKey.length()))); - } - currentItem.append(line); - if (i == amountOfLines - 1 || lines.at(i + 1).startsWith(constFileKey) || lines.at(i + 1).startsWith(constPlaylistKey)) { - Song currentSong = parseSong(currentItem, Loc_Library); - currentItem.clear(); - if (currentSong.file.isEmpty() || (isMopidy && !currentSong.file.startsWith(Song::constMopidyLocal))) { - continue; - } - - if (Song::Playlist==currentSong.type) { - // lsinfo / will return all stored playlists - but this is deprecated. - if (!parsePlaylists) { - continue; - } - - MusicLibraryItemAlbum *prevAlbum=albumItem; - QString prevSongFile=songItem ? songItem->file() : QString(); - QList cueSongs; // List of songs from cue file - QSet cueFiles; // List of source (flac, mp3, etc) files referenced in cue file - - DBUG << "Got playlist item" << currentSong.file << "prevFile:" << prevSongFile; - - bool parseCue=canSplitCue && currentSong.isCueFile() && !mpdDir.startsWith(constHttpProtocol) && QFile::exists(mpdDir+currentSong.file); - bool cueParseStatus=false; - if (parseCue) { - DBUG << "Parsing cue file:" << currentSong.file << "mpdDir:" << mpdDir; - cueParseStatus=CueFile::parse(currentSong.file, mpdDir, cueSongs, cueFiles); - if (!cueParseStatus) { - DBUG << "Failed to parse cue file!"; - continue; - } else DBUG << "Parsed cue file, songs:" << cueSongs.count() << "files:" << cueFiles; - } - if (cueParseStatus && - (cueFiles.count()data()==Song::unknown() && albumItem->parentItem()->data()==Song::unknown()))) { - - bool canUseThisCueFile=true; - foreach (const Song &s, cueSongs) { - if (!QFile::exists(mpdDir+s.name())) { - DBUG << QString(mpdDir+s.name()) << "is referenced in cue file, but does not exist in MPD folder"; - canUseThisCueFile=false; - break; - } - } - - if (!canUseThisCueFile) { - continue; - } - - bool canUseCueFileTracks=false; - QList fixedCueSongs; // Songs taken from cueSongs that have been updated... - - if (albumItem) { - QMap origFiles=albumItem->getSongs(cueFiles); - DBUG << "Original files:" << origFiles.keys(); - if (origFiles.size()==cueFiles.size()) { - // We have a previous album, if any of the details of the songs from the cue are empty, - // use those from the album... - bool setTimeFromSource=origFiles.size()==cueSongs.size(); - quint32 albumTime=1==cueFiles.size() ? albumItem->totalTime() : 0; - quint32 usedAlbumTime=0; - foreach (const Song &orig, cueSongs) { - Song s=orig; - Song albumSong=origFiles[s.name()]; - s.setName(QString()); // CueFile has placed source file name here! - if (s.artist.isEmpty() && !albumSong.artist.isEmpty()) { - s.artist=albumSong.artist; - DBUG << "Get artist from album" << albumSong.artist; - } - if (s.composer().isEmpty() && !albumSong.composer().isEmpty()) { - s.setComposer(albumSong.composer()); - DBUG << "Get composer from album" << albumSong.composer(); - } - if (s.album.isEmpty() && !albumSong.album.isEmpty()) { - s.album=albumSong.album; - DBUG << "Get album from album" << albumSong.album; - } - if (s.albumartist.isEmpty() && !albumSong.albumartist.isEmpty()) { - s.albumartist=albumSong.albumartist; - DBUG << "Get albumartist from album" << albumSong.albumartist; - } - if (0==s.year && 0!=albumSong.year) { - s.year=albumSong.year; - DBUG << "Get year from album" << albumSong.year; - } - if (0==s.time && setTimeFromSource) { - s.time=albumSong.time; - } else if (0!=albumTime) { - // Try to set duration of last track by subtracting previous track durations from album duration... - if (0==s.time) { - s.time=albumTime-usedAlbumTime; - } else { - usedAlbumTime+=s.time; - } - } - fixedCueSongs.append(s); - } - canUseCueFileTracks=true; - } else DBUG << "ERROR: file count mismatch" << origFiles.size() << cueFiles.size(); - } else DBUG << "ERROR: No album???"; - - if (!canUseCueFileTracks) { - // No revious album, or album had a different number of source files to the CUE file. If so, then we need to ensure - // all tracks have meta data - otherwise just fallback to listing file + cue - foreach (const Song &orig, cueSongs) { - Song s=orig; - s.setName(QString()); // CueFile has placed source file name here! - if (s.artist.isEmpty() || s.album.isEmpty()) { - break; - } - fixedCueSongs.append(s); - } - - if (fixedCueSongs.count()==cueSongs.count()) { - canUseCueFileTracks=true; - } else DBUG << "ERROR: Not all cue tracks had meta data"; - } - - if (canUseCueFileTracks) { - QSet updatedAlbums; - updatedAlbums.insert(albumItem); - foreach (Song s, fixedCueSongs) { - s.fillEmptyFields(); - if (!artistItem || s.albumArtist()!=artistItem->data()) { - artistItem = rootItem->artist(s); - } - if (!albumItem || s.year!=albumItem->year() || albumItem->parentItem()!=artistItem || s.album!=albumItem->data()) { - albumItem = artistItem->album(s); - } - DBUG << "Create new track from cue" << s.file << s.title << s.artist << s.albumartist << s.album; - songItem=new MusicLibraryItemSong(s, albumItem); - QSet songGenres=songItem->allGenres(); - albumItem->append(songItem); - albumItem->addGenres(songGenres); - artistItem->addGenres(songGenres); - rootItem->addGenres(songGenres); - updatedAlbums.insert(albumItem); - } - - // For each album that was updated/created, remove any source files referenced in cue file... - foreach (MusicLibraryItemAlbum *al, updatedAlbums) { - if (al) { - al->removeAll(cueFiles); - } - } - if (prevAlbum && !updatedAlbums.contains(prevAlbum)) { - DBUG << "Removing" << cueFiles.count() << " files from " << prevAlbum->data(); - prevAlbum->removeAll(cueFiles); - } - - // Remove any artist/album that was created and is now empty. - // This will happen if the source file (e.g. the flac file) does not have any metadata... - if (prevAlbum && 0==prevAlbum->childCount()) { - DBUG << "Removing empty previous album" << prevAlbum->data(); - MusicLibraryItemArtist *ar=static_cast(prevAlbum->parentItem()); - ar->remove(prevAlbum); - if (0==ar->childCount()) { - rootItem->remove(ar); - } - } - } - } - - // Add playlist file (or cue file) to current album, if it has the same path! - // This assumes that MPD always send playlists as the last file... - if (!prevSongFile.isEmpty() && Utils::getDir(prevSongFile)==Utils::getDir(currentSong.file)) { - currentSong.albumartist=currentSong.artist=artistItem->data(); - currentSong.album=albumItem->data(); - currentSong.time=albumItem->totalTime(); - DBUG << "Adding playlist file to" << albumItem->parentItem()->data() << albumItem->data() << (void *)albumItem; - songItem = new MusicLibraryItemSong(currentSong, albumItem); - albumItem->append(songItem); - } - continue; - } -// if (currentSong.isEmpty()) { -// continue; -// } - - currentSong.fillEmptyFields(); - if (!artistItem || currentSong.artistOrComposer()!=artistItem->data()) { - artistItem = rootItem->artist(currentSong); - DBUG << "New artist item for " << currentSong.file << artistItem->data() << (void *)artistItem; - } - if (!albumItem || currentSong.year!=albumItem->year() || albumItem->parentItem()!=artistItem || currentSong.albumId()!=albumItem->albumId()) { - albumItem = artistItem->album(currentSong); - DBUG << "New album item for " << currentSong.file << artistItem->data() << albumItem->data() << (void *)albumItem; - } - songItem=new MusicLibraryItemSong(currentSong, albumItem); - QSet songGenres=songItem->allGenres(); - albumItem->append(songItem); - albumItem->addGenres(songGenres); - artistItem->addGenres(songGenres); - rootItem->addGenres(songGenres); - }/* else if (childDirs) { - }*/ - } -} +#ifndef CANTATA_WEB DirViewItemRoot * MPDParseUtils::parseDirViewItems(const QByteArray &data, bool isMopidy) { QList lines = data.split('\n'); diff --git a/mpd-interface/mpdparseutils.h b/mpd-interface/mpdparseutils.h index 3666a0096..1e1f0362f 100644 --- a/mpd-interface/mpdparseutils.h +++ b/mpd-interface/mpdparseutils.h @@ -30,12 +30,9 @@ #include #include #include "config.h" -#ifdef CANTATA_WEB #include "song.h" -#else -struct Song; +#ifndef CANTATA_WEB class DirViewItemRoot; -class MusicLibraryItemRoot; #endif struct Playlist; struct Output; @@ -75,14 +72,8 @@ namespace MPDParseUtils typedef QMap MessageMap; extern MessageMap parseMessages(const QByteArray &data); #endif - extern bool groupSingle(); - extern void setGroupSingle(bool g); - #ifdef CANTATA_WEB extern void parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion, QList &songs, bool parsePlaylists=true, QSet *childDirs=0); - #else - extern void parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion, - bool isMopidy, MusicLibraryItemRoot *rootItem, bool parsePlaylists=true, - QSet *childDirs=0); + #ifndef CANTATA_WEB extern DirViewItemRoot * parseDirViewItems(const QByteArray &data, bool isMopidy); #endif extern QList parseOuputs(const QByteArray &data); diff --git a/mpd-interface/song.cpp b/mpd-interface/song.cpp index 3d41ec54f..99f74fc7e 100644 --- a/mpd-interface/song.cpp +++ b/mpd-interface/song.cpp @@ -691,6 +691,16 @@ QString Song::describe(bool withMarkup) const return descr; } +bool Song::useComposer() const +{ + foreach (const QString &g, genres()) { + if (isComposerGenre(g)) { + return true; + } + } + return false; +} + //QString Song::basicDescription() const //{ // return isStandardStream() diff --git a/mpd-interface/song.h b/mpd-interface/song.h index e82f97b31..dfba037d0 100644 --- a/mpd-interface/song.h +++ b/mpd-interface/song.h @@ -203,6 +203,7 @@ struct Song QString filePath() const { return decodePath(file); } QString displayAlbum() const { return displayAlbum(album, year); } QString describe(bool withMarkup=false) const; + bool useComposer() const; // QString basicDescription() const; // diff --git a/online/jamendoservice.cpp b/online/jamendoservice.cpp index 8b5e9da5b..bd4940695 100644 --- a/online/jamendoservice.cpp +++ b/online/jamendoservice.cpp @@ -315,12 +315,7 @@ void JamendoMusicLoader::parseSong(MusicLibraryItemArtist *artist, MusicLibraryI if (!s.title.isEmpty()) { s.fillEmptyFields(); s.track=album->childItems().count()+1; - MusicLibraryItemSong *song=new MusicLibraryItemSong(s, album); - QSet songGenres=song->allGenres(); - album->append(song); - album->addGenres(songGenres); - artist->addGenres(songGenres); - library->addGenres(songGenres); + album->append(new MusicLibraryItemSong(s, album)); } } diff --git a/online/magnatuneservice.cpp b/online/magnatuneservice.cpp index 06c668613..934bab8b7 100644 --- a/online/magnatuneservice.cpp +++ b/online/magnatuneservice.cpp @@ -103,12 +103,7 @@ void MagnatuneMusicLoader::parseSong(QXmlStreamReader &xml) s.fillEmptyFields(); MusicLibraryItemArtist *artist = library->artist(s); MusicLibraryItemAlbum *album = artist->album(s); - MusicLibraryItemSong *song=new MusicLibraryItemSong(s, album); - QSet songGenres=song->allGenres(); - album->append(song); - album->addGenres(songGenres); - artist->addGenres(songGenres); - library->addGenres(songGenres); + album->append(new MusicLibraryItemSong(s, album)); // if (!artistImg.isEmpty() && artist->imageUrl().isEmpty()) { // artist->setImageUrl(artistImg); // } diff --git a/online/onlinedevice.cpp b/online/onlinedevice.cpp index b2b20a38d..81ba33158 100644 --- a/online/onlinedevice.cpp +++ b/online/onlinedevice.cpp @@ -22,7 +22,6 @@ */ #include "onlinedevice.h" -#include "models/musiclibrarymodel.h" #include "models/dirviewmodel.h" #include "support/utils.h" #include "network/networkaccessmanager.h" @@ -36,7 +35,7 @@ void OnlineDevice::copySongTo(const Song &s, const QString &musicPath, bool over jobAbortRequested=false; QString baseDir=MPDConnection::self()->getDetails().dir; QString dest(baseDir+musicPath); - if (!overwrite && (MusicLibraryModel::self()->songExists(s) || QFile::exists(dest))) { + if (!overwrite && (/*MusicLibraryModel::self()->songExists(s) ||*/ QFile::exists(dest))) { emit actionStatus(SongExists); return; } @@ -86,7 +85,7 @@ void OnlineDevice::downloadFinished() currentSong.file=Song::encodePath(currentSong.file); } Utils::setFilePerms(currentDestFile); - MusicLibraryModel::self()->addSongToList(currentSong); +// MusicLibraryModel::self()->addSongToList(currentSong); DirViewModel::self()->addFileToList(origPath.isEmpty() ? currentSong.file : origPath, origPath.isEmpty() ? QString() : currentSong.file); emit actionStatus(Ok); diff --git a/online/onlineservice.cpp b/online/onlineservice.cpp index d5f6d2237..53c79af13 100644 --- a/online/onlineservice.cpp +++ b/online/onlineservice.cpp @@ -23,7 +23,6 @@ #include "onlineservice.h" #include "models/onlineservicesmodel.h" -#include "models/musiclibrarymodel.h" #include "support/utils.h" #include "network/networkaccessmanager.h" #include "mpd-interface/mpdparseutils.h" @@ -38,7 +37,7 @@ static QString cacheFileName(OnlineService *srv, bool create=false) { - return Utils::cacheDir(srv->id().toLower(), create)+QLatin1String("library")+MusicLibraryModel::constLibraryCompressedExt; + return Utils::cacheDir(srv->id().toLower(), create)+QLatin1String("library")+".xml.gz"; } OnlineMusicLoader::OnlineMusicLoader(const QUrl &src) @@ -97,7 +96,6 @@ bool OnlineMusicLoader::readFromCache() emit status(i18n("Reading cache"), 0); if (library->fromXML(cache, 0, 0, QString(), this)) { if (!stopRequested) { - fixLibrary(); emit status(i18n("Updating display"), -100); emit loaded(); } @@ -107,14 +105,6 @@ bool OnlineMusicLoader::readFromCache() return false; } -void OnlineMusicLoader::fixLibrary() -{ - emit status(i18n("Grouping tracks"), -100); - if (MPDParseUtils::groupSingle()) { - library->groupSingleTracks(); - } -} - void OnlineMusicLoader::downloadFinished() { NetworkJob *reply=qobject_cast(sender()); @@ -136,7 +126,6 @@ void OnlineMusicLoader::downloadFinished() QXmlStreamReader reader; reader.setDevice(&comp); if (parse(reader)) { - fixLibrary(); emit status(i18n("Saving cache"), 0); library->toXML(cache, 0, false, this); emit loaded(); @@ -306,7 +295,6 @@ void OnlineService::clear() } lProgress=0.0; setStatusMessage(QString()); - static_cast(m_model)->updateGenres(); } void OnlineService::removeCache() @@ -352,7 +340,6 @@ void OnlineService::applyUpdate() } delete update; update=0; - static_cast(m_model)->updateGenres(); emitUpdated(); } setBusy(false); diff --git a/online/onlineservice.h b/online/onlineservice.h index e39d838f7..146bbb2ed 100644 --- a/online/onlineservice.h +++ b/online/onlineservice.h @@ -75,7 +75,6 @@ private Q_SLOTS: private: bool readFromCache(); - void fixLibrary(); void readProgress(double pc); void writeProgress(double pc); void progressReport(const QString &str, int pc); diff --git a/online/onlineservicespage.cpp b/online/onlineservicespage.cpp index 03283e1a0..99750a336 100644 --- a/online/onlineservicespage.cpp +++ b/online/onlineservicespage.cpp @@ -63,8 +63,6 @@ OnlineServicesPage::OnlineServicesPage(QWidget *p) connect(this, SIGNAL(add(const QStringList &, bool, quint8)), MPDConnection::self(), SLOT(add(const QStringList &, bool, quint8))); connect(this, SIGNAL(addSongsToPlaylist(const QString &, const QStringList &)), MPDConnection::self(), SLOT(addToPlaylist(const QString &, const QStringList &))); - connect(genreCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchItems())); - connect(OnlineServicesModel::self(), SIGNAL(updateGenres(const QSet &)), genreCombo, SLOT(update(const QSet &))); connect(OnlineServicesModel::self(), SIGNAL(updated(QModelIndex)), this, SLOT(updated(QModelIndex))); // connect(OnlineServicesModel::self(), SIGNAL(needToSort()), this, SLOT(sortList())); connect(OnlineServicesModel::self(), SIGNAL(busy(bool)), view, SLOT(showSpinner(bool))); @@ -73,7 +71,6 @@ OnlineServicesPage::OnlineServicesPage(QWidget *p) connect(view, SIGNAL(searchItems()), this, SLOT(searchItems())); connect(view, SIGNAL(searchIsActive(bool)), this, SLOT(controlSearch(bool))); connect(view, SIGNAL(itemsSelected(bool)), SLOT(controlActions())); - connect(view, SIGNAL(rootIndexSet(QModelIndex)), this, SLOT(updateGenres(QModelIndex))); connect(view, SIGNAL(rootIndexSet(QModelIndex)), this, SLOT(setSearchable(QModelIndex))); connect(OnlineServicesModel::self()->configureAct(), SIGNAL(triggered()), this, SLOT(configureService())); connect(OnlineServicesModel::self()->refreshAct(), SIGNAL(triggered()), this, SLOT(refreshService())); @@ -308,22 +305,17 @@ void OnlineServicesPage::controlSearch(bool on) // } if (on) { - genreCombo->setEnabled(true); OnlineService *srv=OnlineServicesModel::self()->service(searchService); if (searchService.isEmpty()) { view->setSearchVisible(false); view->setBackgroundImage(QIcon()); } else { - if (onlineSearchRequest) { - genreCombo->setCurrentIndex(0); - genreCombo->setEnabled(false); - } view->setSearchLabelText(i18nc("Search ServiceName:", "Search %1:", searchService)); view->setBackgroundImage(srv->icon()); } QModelIndex filterIndex=srv ? OnlineServicesModel::self()->serviceIndex(srv) : QModelIndex(); proxy.setFilterItem(srv); - proxy.update(QString(), QString()); + proxy.update(QString()); view->setSearchIndex(filterIndex.isValid() ? proxy.mapFromSource(filterIndex) : QModelIndex()); view->setSearchResetLevel(filterIndex.isValid() ? 0 : 1); if (filterIndex.isValid()) { @@ -334,10 +326,9 @@ void OnlineServicesPage::controlSearch(bool on) if (srv) { srv->cancelSearch(); } - genreCombo->setEnabled(true); searchService=QString(); proxy.setFilterItem(0); - proxy.update(QString(), QString()); + proxy.update(QString()); view->setBackgroundImage(QIcon()); view->setSearchIndex(QModelIndex()); view->setSearchResetLevel(1); @@ -356,7 +347,7 @@ void OnlineServicesPage::searchItems() if (!view->isSearchActive()) { proxy.setFilterItem(0); } - proxy.update(view->isSearchActive() ? text : QString(), genreCombo->currentIndex()<=0 ? QString() : genreCombo->currentText()); + proxy.update(view->isSearchActive() ? text : QString()); if (proxy.enabled() && !proxy.filterText().isEmpty()) { view->expandAll(proxy.filterItem() && view->isSearchActive() ? proxy.mapFromSource(OnlineServicesModel::self()->serviceIndex(static_cast(proxy.filterItem()))) @@ -472,28 +463,6 @@ void OnlineServicesPage::refreshService() } } -void OnlineServicesPage::updateGenres(const QModelIndex &idx) -{ - if (idx.isValid()) { - QModelIndex m=proxy.mapToSource(idx); - if (m.isValid()) { - MusicLibraryItem *item=static_cast(m.internalPointer()); - MusicLibraryItem::Type itemType=item->itemType(); - if (itemType==MusicLibraryItem::Type_Root) { - genreCombo->update(static_cast(item)->genres()); - return; - } else if (itemType==MusicLibraryItem::Type_Artist) { - genreCombo->update(static_cast(item)->genres()); - return; - } else if (itemType==MusicLibraryItem::Type_Album) { - genreCombo->update(static_cast(item)->genres()); - return; - } - } - } - genreCombo->update(OnlineServicesModel::self()->genres()); -} - void OnlineServicesPage::setSearchable(const QModelIndex &idx) { searchable=!idx.isValid(); diff --git a/online/onlineservicespage.h b/online/onlineservicespage.h index 5c19f5f64..65d6ed8fd 100644 --- a/online/onlineservicespage.h +++ b/online/onlineservicespage.h @@ -58,7 +58,6 @@ public Q_SLOTS: void controlActions(); void configureService(); void refreshService(); - void updateGenres(const QModelIndex &idx); void setSearchable(const QModelIndex &idx); void updated(const QModelIndex &idx); // void sortList(); @@ -94,7 +93,6 @@ private: Action *deleteDownloadedPodcastAction; Action *markPodcastAsNewAction; Action *markPodcastAsListenedAction; - QSet genres; bool onlineSearchRequest; QString searchService; bool searchable; diff --git a/online/onlineservicespage.ui b/online/onlineservicespage.ui index 622656573..fd5f8aa71 100644 --- a/online/onlineservicespage.ui +++ b/online/onlineservicespage.ui @@ -29,7 +29,17 @@ 0 - + + + Qt::Horizontal + + + + 16 + 20 + + + @@ -53,11 +63,6 @@ QTreeView
    widgets/itemview.h
    - - GenreCombo - QComboBox -
    widgets/genrecombo.h
    -
    ToolButton QToolButton diff --git a/streams/streamspage.cpp b/streams/streamspage.cpp index 18db52459..f00f8ddff 100644 --- a/streams/streamspage.cpp +++ b/streams/streamspage.cpp @@ -497,7 +497,7 @@ void StreamsPage::searchItems() if (!view->isSearchActive()) { proxy->setFilterItem(0); } - proxy->update(view->isSearchActive() ? text : QString(), QString()); + proxy->update(view->isSearchActive() ? text : QString()); if (proxy->enabled() && !text.isEmpty()) { view->expandAll(proxy->filterItem() ? proxy->mapFromSource(StreamsModel::self()->categoryIndex(static_cast(proxy->filterItem()))) @@ -552,7 +552,7 @@ void StreamsPage::controlSearch(bool on) view->setSearchLabelText(i18n("Search %1:", cat->name)); view->setBackgroundImage(cat->icon); proxy->setFilterItem(cat); - proxy->update(QString(), QString()); + proxy->update(QString()); QModelIndex filterIndex=cat ? StreamsModel::self()->categoryIndex(cat) : QModelIndex(); if (filterIndex.isValid()) { view->expand(proxy->mapFromSource(filterIndex), true); @@ -571,7 +571,7 @@ void StreamsPage::controlSearch(bool on) view->clearSelection(); } else { proxy->setFilterItem(0); - proxy->update(QString(), QString()); + proxy->update(QString()); proxy=&streamsProxy; searchModel.clear(); view->setSearchVisible(false); diff --git a/tags/tageditor.cpp b/tags/tageditor.cpp index cfff3f5d2..1f47cd5ee 100644 --- a/tags/tageditor.cpp +++ b/tags/tageditor.cpp @@ -24,7 +24,6 @@ #include "tageditor.h" #include "tags.h" #include "widgets/tagspinbox.h" -#include "models/musiclibrarymodel.h" #include "mpd-interface/mpdconnection.h" #include "gui/settings.h" #include "support/messagebox.h" @@ -1172,10 +1171,10 @@ bool TagEditor::applyUpdates() } else #endif { - if (!MusicLibraryModel::self()->updateSong(orig, edit)) { - MusicLibraryModel::self()->removeSongFromList(orig); - MusicLibraryModel::self()->addSongToList(edit); - } +// if (!MusicLibraryModel::self()->updateSong(orig, edit)) { +// MusicLibraryModel::self()->removeSongFromList(orig); +// MusicLibraryModel::self()->addSongToList(edit); +// } } updatedSongs.append(edit); if (!renameFiles && file!=opts.createFilename(edit)) { diff --git a/tags/trackorganiser.cpp b/tags/trackorganiser.cpp index 6223d35db..c233641bc 100644 --- a/tags/trackorganiser.cpp +++ b/tags/trackorganiser.cpp @@ -27,7 +27,6 @@ #include "models/devicesmodel.h" #endif #include "devices/device.h" -#include "models/musiclibrarymodel.h" #include "models/dirviewmodel.h" #include "gui/settings.h" #include "mpd-interface/mpdconnection.h" @@ -430,7 +429,7 @@ void TrackOrganiser::renameFile() updated=true; if (deviceUdi.isEmpty()) { - MusicLibraryModel::self()->updateSongFile(s, to); +// MusicLibraryModel::self()->updateSongFile(s, to); DirViewModel::self()->removeFileFromList(s.file); DirViewModel::self()->addFileToList(origPath.isEmpty() ? to.file : origPath, origPath.isEmpty() ? QString() : to.file); diff --git a/web/CMakeLists.txt b/web/CMakeLists.txt index 346ea5282..9c4742b68 100644 --- a/web/CMakeLists.txt +++ b/web/CMakeLists.txt @@ -39,10 +39,10 @@ set(ENABLE_QT5 ON) set(ENABLE_WEB ON) #find_package(Sqlite REQUIRED) -add_definitions(${Qt5Network_DEFINITIONS} ${Qt5Concurrent_DEFINITIONS} ${Qt5Sql_DEFINITIONS} ${Qt5WebSockets_DEFINITIONS} ${SQLITE_DEFINITIONS} -DCANTATA_WEB) +add_definitions(${Qt5Network_DEFINITIONS} ${Qt5Concurrent_DEFINITIONS} ${Qt5Sql_DEFINITIONS} ${Qt5WebSockets_DEFINITIONS} -DCANTATA_WEB) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} - ${QTINCLUDES} ${SQLITE_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/..) + ${QTINCLUDES} ${CMAKE_SOURCE_DIR}/..) set(CANTATA_WEB_SRCS ${CANTATA_WEB_SRCS} app/main.cpp app/application.cpp app/httpserver.cpp app/apihandler.cpp app/libraryapi.cpp app/playlistsapi.cpp diff --git a/widgets/genrecombo.cpp b/widgets/genrecombo.cpp deleted file mode 100644 index fd6b10b42..000000000 --- a/widgets/genrecombo.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Cantata - * - * Copyright (c) 2011-2014 Craig Drummond - * - * ---- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "genrecombo.h" -#include "toolbutton.h" -#include "support/localize.h" -#include "support/actioncollection.h" -#include "support/action.h" -#include - -static Action *action=0; - -GenreCombo::GenreCombo(QWidget *p) - : ComboBox(p) -{ - setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - update(QSet()); - setEditable(false); - setFocusPolicy(Qt::NoFocus); - if (!action) { - action=ActionCollection::get()->createAction("genrefilter", i18n("Filter On Genre"), 0); - action->setShortcut(Qt::ControlModifier+Qt::Key_G); - } - addAction(action); - connect(action, SIGNAL(triggered()), SLOT(showEntries())); -} - -void GenreCombo::update(const QSet &g) -{ - if (count() && g==genres) { - return; - } - - QSet mg=g; - mg.remove(QString()); - if (mg.count()!=g.count() && count() && mg==genres) { - return; - } - - genres=mg; - QStringList entries=g.toList(); - qSort(entries); - entries.prepend(i18n("All Genres")); - - if (count()==entries.count()) { - bool noChange=true; - for (int i=0; i1); - // If we are 'hidden' then we need to ingore mouse events - so that these get passed to parent widget. - // The Oxygen's window drag still functions... - setAttribute(Qt::WA_TransparentForMouseEvents, count()<2); -} - -void GenreCombo::showEntries() -{ - if (isVisible()) { - showPopup(); - } -} - -void GenreCombo::paintEvent(QPaintEvent *e) -{ - if (count()>1) { - ComboBox::paintEvent(e); - } else { - QWidget::paintEvent(e); - } -} - -bool GenreCombo::event(QEvent *event) -{ - if (QEvent::ToolTip==event->type() && toolTip()!=action->toolTip()) { - setToolTip(action->toolTip()); - } - return ComboBox::event(event); -} diff --git a/widgets/icons.cpp b/widgets/icons.cpp index cb4046a6a..e155780b5 100644 --- a/widgets/icons.cpp +++ b/widgets/icons.cpp @@ -493,8 +493,7 @@ void Icons::initSidebarIcons() QColor highlightedTexCol=QApplication::palette().color(QPalette::Active, QPalette::HighlightedText); #endif playqueueIcon=loadSidebarIcon(QLatin1String("playqueue"), textCol, highlightedTexCol); - artistsIcon=loadSidebarIcon(QLatin1String("artists"), textCol, highlightedTexCol); - albumsIcon=loadSidebarIcon(QLatin1String("albums"), textCol, highlightedTexCol); + libraryIcon=loadSidebarIcon(QLatin1String("library"), textCol, highlightedTexCol); foldersIcon=loadSidebarIcon(QLatin1String("folders"), textCol, highlightedTexCol); playlistsIcon=loadSidebarIcon(QLatin1String("playlists"), textCol, highlightedTexCol); #ifdef ENABLE_DYNAMIC @@ -514,8 +513,7 @@ void Icons::initSidebarIcons() } else { monoSb=false; playqueueIcon=Icon("media-playback-start"); - artistsIcon=artistIcon; - albumsIcon=albumIcon; + libraryIcon=audioFileIcon; foldersIcon=Icon("inode-directory"); playlistsIcon=playlistIcon; #ifdef ENABLE_DYNAMIC diff --git a/widgets/icons.h b/widgets/icons.h index fdc30645c..1268933f4 100644 --- a/widgets/icons.h +++ b/widgets/icons.h @@ -74,8 +74,7 @@ public: Icon importIcon; Icon playqueueIcon; - Icon artistsIcon; - Icon albumsIcon; + Icon libraryIcon; Icon foldersIcon; Icon playlistsIcon; #ifdef ENABLE_DYNAMIC diff --git a/widgets/itemview.cpp b/widgets/itemview.cpp index 758a6380b..7ff424955 100644 --- a/widgets/itemview.cpp +++ b/widgets/itemview.cpp @@ -779,7 +779,9 @@ void ItemView::setMode(Mode m) treeView->setHidden(false); static_cast(treeView->itemDelegate())->setSimple(Mode_SimpleTree==mode || Mode_BasicTree==mode); static_cast(treeView->itemDelegate())->setNoIcons(Mode_BasicTree==mode); - itemModel->setRootIndex(QModelIndex()); + if (dynamic_cast(itemModel)) { + static_cast(itemModel)->setRootIndex(QModelIndex()); + } treeView->reset(); } else if (Mode_GroupedTree==mode) { treeView->setModel(0); @@ -791,7 +793,9 @@ void ItemView::setMode(Mode m) groupedView->setHidden(false); treeView->setHidden(true); groupedView->setModel(itemModel); - itemModel->setRootIndex(QModelIndex()); + if (dynamic_cast(itemModel)) { + static_cast(itemModel)->setRootIndex(QModelIndex()); + } stackIndex=groupedView->property(constPageProp).toInt(); } else if (Mode_Table==mode) { int w=view()->width(); @@ -804,7 +808,9 @@ void ItemView::setMode(Mode m) treeView->setHidden(true); tableView->setModel(itemModel); tableView->initHeader(); - itemModel->setRootIndex(QModelIndex()); + if (dynamic_cast(itemModel)) { + static_cast(itemModel)->setRootIndex(QModelIndex()); + } tableView->resize(w, tableView->height()); stackIndex=tableView->property(constPageProp).toInt(); } else { @@ -859,7 +865,9 @@ QModelIndexList ItemView::selectedIndexes(bool sorted) const void ItemView::goToTop() { setLevel(0); - itemModel->setRootIndex(QModelIndex()); + if (dynamic_cast(itemModel)) { + static_cast(itemModel)->setRootIndex(QModelIndex()); + } listView->setRootIndex(QModelIndex()); emit rootIndexSet(QModelIndex()); } @@ -923,7 +931,7 @@ QAbstractItemView * ItemView::view() const } } -void ItemView::setModel(ProxyModel *m) +void ItemView::setModel(QAbstractItemModel *m) { bool needtToInit=!itemModel; itemModel=m; @@ -1060,7 +1068,9 @@ void ItemView::showIndex(const QModelIndex &idx, bool scrollTo) } setLevel(0); listView->setRootIndex(QModelIndex()); - itemModel->setRootIndex(QModelIndex()); + if (dynamic_cast(itemModel)) { + static_cast(itemModel)->setRootIndex(QModelIndex()); + } foreach (const QModelIndex &i, indexes) { activateItem(i, false); } @@ -1279,7 +1289,9 @@ void ItemView::backActivated() return; } setLevel(currentLevel-1); - itemModel->setRootIndex(listView->rootIndex().parent()); + if (dynamic_cast(itemModel)) { + static_cast(itemModel)->setRootIndex(listView->rootIndex().parent()); + } listView->setRootIndex(listView->rootIndex().parent()); emit rootIndexSet(listView->rootIndex().parent()); @@ -1343,12 +1355,20 @@ void ItemView::activateItem(const QModelIndex &index, bool emitRootSet) if (!index.parent().isValid()) { tableView->setExpanded(index, !tableView->TreeView::isExpanded(index)); } - } else if (index.isValid() && index.child(0, 0).isValid() && index!=listView->rootIndex()) { + } else if (index.isValid() && (index.child(0, 0).isValid() || itemModel->canFetchMore(index)) && index!=listView->rootIndex()) { + if (itemModel->canFetchMore(index)) { + itemModel->fetchMore(index); + } + QModelIndex fistChild=index.child(0, 0); + if (!fistChild.isValid()) { + return; + } + prevTopIndex=listView->indexAt(QPoint(8, 8)); if (qobject_cast(listView->model())) { prevTopIndex=static_cast(listView->model())->mapToSource(prevTopIndex); } - setLevel(currentLevel+1, index.child(0, 0).child(0, 0).isValid()); + setLevel(currentLevel+1, itemModel->canFetchMore(fistChild) || fistChild.child(0, 0).isValid()); QString text=index.data(Cantata::Role_TitleText).toString(); if (text.isEmpty()) { text=index.data(Qt::DisplayRole).toString(); @@ -1356,7 +1376,9 @@ void ItemView::activateItem(const QModelIndex &index, bool emitRootSet) currentText=text; backButton->setText(text); listView->setRootIndex(index); - itemModel->setRootIndex(index); + if (dynamic_cast(itemModel)) { + static_cast(itemModel)->setRootIndex(index); + } if (emitRootSet) { emit rootIndexSet(index); } diff --git a/widgets/itemview.h b/widgets/itemview.h index 3b180d0de..652138375 100644 --- a/widgets/itemview.h +++ b/widgets/itemview.h @@ -31,7 +31,6 @@ #include #include -class ProxyModel; class Spinner; class QTimer; class GroupedView; @@ -104,7 +103,7 @@ public: void setMode(Mode m); Mode viewMode() const { return mode; } QAbstractItemView * view() const; - void setModel(ProxyModel *m); + void setModel(QAbstractItemModel *m); void clearSelection() { view()->selectionModel()->clearSelection(); } void setCurrentIndex(const QModelIndex &idx) { view()->setCurrentIndex(idx); } void select(const QModelIndex &idx); @@ -182,7 +181,7 @@ private: private: QTimer *searchTimer; - ProxyModel *itemModel; + QAbstractItemModel *itemModel; int currentLevel; Mode mode; QString currentText;