diff --git a/autotests/localfilelistingtest.cpp b/autotests/localfilelistingtest.cpp index 650e7c32..eb8fc4d4 100644 --- a/autotests/localfilelistingtest.cpp +++ b/autotests/localfilelistingtest.cpp @@ -1,500 +1,489 @@ /* * Copyright 2015-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "databasetestdata.h" #include "file/localfilelisting.h" #include "musicaudiotrack.h" #include "config-upnp-qt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class LocalFileListingTests: public QObject, public DatabaseTestData { Q_OBJECT public: LocalFileListingTests(QObject *parent = nullptr) : QObject(parent) { } private Q_SLOTS: void initTestCase() { qRegisterMetaType>("QHash"); qRegisterMetaType>("QHash"); qRegisterMetaType>("QList"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType>("QList"); - qRegisterMetaType("NotificationItem"); } void initialTestWithNoTrack() { LocalFileListing myListing; QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.init(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.setAllRootPaths({QStringLiteral("/directoryNotExist")}); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(myListing.allRootPaths(), QStringList{QStringLiteral("/directoryNotExist")}); myListing.refreshContent(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); } void initialTestWithTracks() { LocalFileListing myListing; QString musicPath = QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music"); QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.init(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.setAllRootPaths({musicPath}); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); //QCOMPARE(myListing.rootPath(), musicPath); myListing.refreshContent(); QCOMPARE(tracksListSpy.count(), 2); QCOMPARE(removedTracksListSpy.count(), 0); const auto &firstNewTracksSignal = tracksListSpy.at(0); auto firstNewTracks = firstNewTracksSignal.at(0).value>(); const auto &secondNewTracksSignal = tracksListSpy.at(1); auto secondNewTracks = secondNewTracksSignal.at(0).value>(); auto newCovers = secondNewTracksSignal.at(1).value>(); QCOMPARE(firstNewTracks.count() + secondNewTracks.count(), 5); QCOMPARE(newCovers.count(), 5); } void addAndRemoveTracks() { LocalFileListing myListing; QString musicOriginPath = QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music"); QString musicPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2/data/innerData"); QString musicParentPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2"); QDir musicParentDirectory(musicParentPath); QDir rootDirectory(QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH)); musicParentDirectory.removeRecursively(); rootDirectory.mkpath(QStringLiteral("music2/data/innerData")); QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); QSignalSpy errorWatchingFileSystemChangesSpy(&myListing, &LocalFileListing::errorWatchingFileSystemChanges); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.init(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.setAllRootPaths({musicParentPath}); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(myListing.allRootPaths(), QStringList{musicParentPath}); myListing.refreshContent(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QFile myTrack(musicOriginPath + QStringLiteral("/test.ogg")); myTrack.copy(musicPath + QStringLiteral("/test.ogg")); QFile myCover(musicOriginPath + QStringLiteral("/cover.jpg")); myCover.copy(musicPath + QStringLiteral("/cover.jpg")); QCOMPARE(tracksListSpy.wait(), true); QCOMPARE(tracksListSpy.count(), 1); QCOMPARE(removedTracksListSpy.count(), 0); auto newTracksSignal = tracksListSpy.at(0); auto newTracks = newTracksSignal.at(0).value>(); auto newCovers = newTracksSignal.at(1).value>(); QCOMPARE(newTracks.count(), 1); QCOMPARE(newCovers.count(), 1); QDir musicDirectory(musicPath); QCOMPARE(musicDirectory.removeRecursively(), true); auto removedFilesWorking = removedTracksListSpy.wait(); if (!removedFilesWorking && errorWatchingFileSystemChangesSpy.count()) { QEXPECT_FAIL("", "Impossible watching file system for changes", Abort); } QCOMPARE(removedFilesWorking, true); QCOMPARE(tracksListSpy.count(), 1); QCOMPARE(removedTracksListSpy.count(), 1); auto removeSignal = removedTracksListSpy.at(0); auto removedTracks = removeSignal.at(0).value>(); QCOMPARE(removedTracks.isEmpty(), false); QCOMPARE(rootDirectory.mkpath(QStringLiteral("music2/data/innerData")), true); QCOMPARE(myTrack.copy(musicPath + QStringLiteral("/test.ogg")), true); QCOMPARE(myCover.copy(musicPath + QStringLiteral("/cover.jpg")), true); if (tracksListSpy.count() == 1) { QCOMPARE(tracksListSpy.wait(), true); } QCOMPARE(tracksListSpy.count(), 2); QCOMPARE(removedTracksListSpy.count(), 1); auto newTracksSignalLast = tracksListSpy.at(1); auto newTracksLast = newTracksSignalLast.at(0).value>(); auto newCoversLast = newTracksSignalLast.at(1).value>(); QCOMPARE(newTracksLast.count(), 1); QCOMPARE(newCoversLast.count(), 1); } void addTracksAndRemoveDirectory() { LocalFileListing myListing; QString musicOriginPath = QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music"); QString musicPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2/data/innerData"); QString innerMusicPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2/data"); QString musicParentPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2"); QDir musicParentDirectory(musicParentPath); QDir rootDirectory(QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH)); musicParentDirectory.removeRecursively(); rootDirectory.mkpath(QStringLiteral("music2/data/innerData")); QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); QSignalSpy errorWatchingFileSystemChangesSpy(&myListing, &LocalFileListing::errorWatchingFileSystemChanges); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.init(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); myListing.setAllRootPaths({musicParentPath}); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(myListing.allRootPaths(), QStringList{musicParentPath}); myListing.refreshContent(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QFile myTrack(musicOriginPath + QStringLiteral("/test.ogg")); myTrack.copy(musicPath + QStringLiteral("/test.ogg")); QFile myCover(musicOriginPath + QStringLiteral("/cover.jpg")); myCover.copy(musicPath + QStringLiteral("/cover.jpg")); QCOMPARE(tracksListSpy.wait(), true); QCOMPARE(tracksListSpy.count(), 1); QCOMPARE(removedTracksListSpy.count(), 0); auto newTracksSignal = tracksListSpy.at(0); auto newTracks = newTracksSignal.at(0).value>(); auto newCovers = newTracksSignal.at(1).value>(); QCOMPARE(newTracks.count(), 1); QCOMPARE(newCovers.count(), 1); QDir musicDirectory(innerMusicPath); musicDirectory.removeRecursively(); auto removedFilesWorking = removedTracksListSpy.wait(); if (!removedFilesWorking && errorWatchingFileSystemChangesSpy.count()) { QEXPECT_FAIL("", "Impossible watching file system for changes", Abort); } QCOMPARE(removedFilesWorking, true); QCOMPARE(tracksListSpy.count(), 1); QCOMPARE(removedTracksListSpy.count(), 1); auto removeSignal = removedTracksListSpy.at(0); auto removedTracks = removeSignal.at(0).value>(); QCOMPARE(removedTracks.isEmpty(), false); QCOMPARE(rootDirectory.mkpath(QStringLiteral("music2/data/innerData")), true); QCOMPARE(myTrack.copy(musicPath + QStringLiteral("/test.ogg")), true); QCOMPARE(myCover.copy(musicPath + QStringLiteral("/cover.jpg")), true); if (tracksListSpy.count() == 1) { QCOMPARE(tracksListSpy.wait(), true); } QCOMPARE(tracksListSpy.count(), 2); QCOMPARE(removedTracksListSpy.count(), 1); auto newTracksSignalLast = tracksListSpy.at(1); auto newTracksLast = newTracksSignalLast.at(0).value>(); auto newCoversLast = newTracksSignalLast.at(1).value>(); QCOMPARE(newTracksLast.count(), 1); QCOMPARE(newCoversLast.count(), 1); } void addAndMoveTracks() { LocalFileListing myListing; QString musicOriginPath = QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music"); QString musicPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2/data/innerData"); QDir musicDirectory(musicPath); QString musicParentPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2"); QDir musicParentDirectory(musicParentPath); QString musicFriendPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music3"); QDir musicFriendDirectory(musicFriendPath); QCOMPARE(musicFriendDirectory.removeRecursively(), true); QCOMPARE(musicParentDirectory.removeRecursively(), true); musicFriendDirectory.mkpath(musicFriendPath); musicDirectory.mkpath(musicPath); QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); QSignalSpy modifiedTracksListSpy(&myListing, &LocalFileListing::modifyTracksList); QSignalSpy errorWatchingFileSystemChangesSpy(&myListing, &LocalFileListing::errorWatchingFileSystemChanges); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); myListing.init(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); myListing.setAllRootPaths({musicParentPath}); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); QCOMPARE(myListing.allRootPaths(), QStringList{musicParentPath}); myListing.refreshContent(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); QFile myTrack(musicOriginPath + QStringLiteral("/test.ogg")); myTrack.copy(musicPath + QStringLiteral("/test.ogg")); QFile myCover(musicOriginPath + QStringLiteral("/cover.jpg")); myCover.copy(musicPath + QStringLiteral("/cover.jpg")); QCOMPARE(tracksListSpy.wait(), true); QCOMPARE(tracksListSpy.count(), 1); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); auto newTracksSignal = tracksListSpy.at(0); auto newTracks = newTracksSignal.at(0).value>(); auto newCovers = newTracksSignal.at(1).value>(); QCOMPARE(newTracks.count(), 1); QCOMPARE(newCovers.count(), 1); QDir().rename(musicPath, musicFriendPath); auto removedFilesWorking = removedTracksListSpy.wait(); if (!removedFilesWorking && errorWatchingFileSystemChangesSpy.count()) { QEXPECT_FAIL("", "Impossible watching file system for changes", Abort); } QCOMPARE(removedFilesWorking, true); QCOMPARE(tracksListSpy.count(), 1); QCOMPARE(removedTracksListSpy.count(), 1); QCOMPARE(modifiedTracksListSpy.count(), 0); auto removeSignal = removedTracksListSpy.at(0); auto removedTracks = removeSignal.at(0).value>(); QCOMPARE(removedTracks.isEmpty(), false); QCOMPARE(musicFriendDirectory.mkpath(musicFriendPath), true); QCOMPARE(musicDirectory.mkpath(musicPath), true); QCOMPARE(myTrack.copy(musicPath + QStringLiteral("/test.ogg")), true); QCOMPARE(myCover.copy(musicPath + QStringLiteral("/cover.jpg")), true); if (tracksListSpy.count() == 1) { QCOMPARE(tracksListSpy.wait(), true); } QCOMPARE(tracksListSpy.count(), 2); QCOMPARE(removedTracksListSpy.count(), 1); QCOMPARE(modifiedTracksListSpy.count(), 0); auto newTracksSignalLast = tracksListSpy.at(1); auto newTracksLast = newTracksSignalLast.at(0).value>(); auto newCoversLast = newTracksSignalLast.at(1).value>(); QCOMPARE(newTracksLast.count(), 1); QCOMPARE(newCoversLast.count(), 1); } void restoreRemovedTracks() { LocalFileListing myListing; QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); QSignalSpy modifiedTracksListSpy(&myListing, &LocalFileListing::modifyTracksList); QSignalSpy indexingStartedSpy(&myListing, &LocalFileListing::indexingStarted); QSignalSpy indexingFinishedSpy(&myListing, &LocalFileListing::indexingFinished); - QSignalSpy newNotificationSpy(&myListing, &LocalFileListing::newNotification); - QSignalSpy closeNotificationSpy(&myListing, &LocalFileListing::closeNotification); QSignalSpy askRestoredTracksSpy(&myListing, &LocalFileListing::askRestoredTracks); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); QCOMPARE(indexingStartedSpy.count(), 0); QCOMPARE(indexingFinishedSpy.count(), 0); - QCOMPARE(newNotificationSpy.count(), 0); - QCOMPARE(closeNotificationSpy.count(), 0); QCOMPARE(askRestoredTracksSpy.count(), 0); myListing.setAllRootPaths({QStringLiteral("/does/not/exists")}); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); QCOMPARE(indexingStartedSpy.count(), 0); QCOMPARE(indexingFinishedSpy.count(), 0); - QCOMPARE(newNotificationSpy.count(), 0); - QCOMPARE(closeNotificationSpy.count(), 0); QCOMPARE(askRestoredTracksSpy.count(), 0); myListing.init(); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 0); QCOMPARE(modifiedTracksListSpy.count(), 0); QCOMPARE(indexingStartedSpy.count(), 0); QCOMPARE(indexingFinishedSpy.count(), 0); - QCOMPARE(newNotificationSpy.count(), 0); - QCOMPARE(closeNotificationSpy.count(), 0); QCOMPARE(askRestoredTracksSpy.count(), 1); myListing.restoredTracks({{QUrl::fromLocalFile(QStringLiteral("/removed/files1")), QDateTime::fromMSecsSinceEpoch(1)}, {QUrl::fromLocalFile(QStringLiteral("/removed/files2")), QDateTime::fromMSecsSinceEpoch(2)}}); QCOMPARE(tracksListSpy.count(), 0); QCOMPARE(removedTracksListSpy.count(), 1); QCOMPARE(modifiedTracksListSpy.count(), 0); QCOMPARE(indexingStartedSpy.count(), 1); QCOMPARE(indexingFinishedSpy.count(), 0); - QCOMPARE(newNotificationSpy.count(), 0); - QCOMPARE(closeNotificationSpy.count(), 0); QCOMPARE(askRestoredTracksSpy.count(), 1); auto removedTracksSignal = removedTracksListSpy.at(0); QCOMPARE(removedTracksSignal.count(), 1); auto removedTracks = removedTracksSignal.at(0).value>(); QCOMPARE(removedTracks.count(), 2); std::sort(removedTracks.begin(), removedTracks.end()); QCOMPARE(removedTracks[0], QUrl::fromLocalFile(QStringLiteral("/removed/files1"))); QCOMPARE(removedTracks[1], QUrl::fromLocalFile(QStringLiteral("/removed/files2"))); } }; QTEST_GUILESS_MAIN(LocalFileListingTests) #include "localfilelistingtest.moc" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9a9f6b8f..c45128ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,485 +1,481 @@ include_directories(${elisa_BINARY_DIR}) set(elisaLib_SOURCES mediaplaylist.cpp musicaudiotrack.cpp progressindicator.cpp databaseinterface.cpp musiclistenersmanager.cpp managemediaplayercontrol.cpp manageheaderbar.cpp manageaudioplayer.cpp trackslistener.cpp elisaapplication.cpp modeldataloader.cpp - notificationitem.cpp - topnotificationmanager.cpp elisautils.cpp abstractfile/abstractfilelistener.cpp abstractfile/abstractfilelisting.cpp filescanner.cpp viewmanager.cpp powermanagementinterface.cpp file/filelistener.cpp file/localfilelisting.cpp models/datamodel.cpp models/abstractmediaproxymodel.cpp models/gridviewproxymodel.cpp models/alltracksproxymodel.cpp models/singlealbumproxymodel.cpp models/trackmetadatamodel.cpp models/trackcontextmetadatamodel.cpp models/viewsmodel.cpp ) ecm_qt_declare_logging_category(elisaLib_SOURCES HEADER "indexersManager.h" IDENTIFIER "orgKdeElisaIndexersManager" CATEGORY_NAME "org.kde.elisa.indexers.manager" DEFAULT_SEVERITY Info ) ecm_qt_declare_logging_category(elisaLib_SOURCES HEADER "databaseLogging.h" IDENTIFIER "orgKdeElisaDatabase" CATEGORY_NAME "org.kde.elisa.database" DEFAULT_SEVERITY Info ) ecm_qt_declare_logging_category(elisaLib_SOURCES HEADER "abstractfile/indexercommon.h" IDENTIFIER "orgKdeElisaIndexer" CATEGORY_NAME "org.kde.elisa.indexer" DEFAULT_SEVERITY Info ) if (LIBVLC_FOUND) ecm_qt_declare_logging_category(elisaLib_SOURCES HEADER "vlcLogging.h" IDENTIFIER "orgKdeElisaPlayerVlc" CATEGORY_NAME "org.kde.elisa.player.vlc" DEFAULT_SEVERITY Info ) set(elisaLib_SOURCES ${elisaLib_SOURCES} audiowrapper_libvlc.cpp ) else() ecm_qt_declare_logging_category(elisaLib_SOURCES HEADER "qtMultimediaLogging.h" IDENTIFIER "orgKdeElisaPlayerQtMultimedia" CATEGORY_NAME "org.kde.elisa.player.qtMultimedia" DEFAULT_SEVERITY Info ) set(elisaLib_SOURCES ${elisaLib_SOURCES} audiowrapper_qtmultimedia.cpp ) endif() if (ANDROID) set(elisaLib_SOURCES ${elisaLib_SOURCES} android/androidmusiclistener.cpp ) endif() if (KF5KIO_FOUND) set(elisaLib_SOURCES ${elisaLib_SOURCES} models/filebrowsermodel.cpp models/filebrowserproxymodel.cpp ) endif() if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) ecm_qt_declare_logging_category(elisaLib_SOURCES HEADER "baloo/baloocommon.h" IDENTIFIER "orgKdeElisaBaloo" CATEGORY_NAME "org.kde.elisa.baloo" DEFAULT_SEVERITY Info ) set(elisaLib_SOURCES ${elisaLib_SOURCES} baloo/localbaloofilelisting.cpp baloo/baloolistener.cpp baloo/baloodetector.cpp ) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.main.xml baloo/main) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.fileindexer.xml baloo/fileindexer) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.scheduler.xml baloo/scheduler) qt5_add_dbus_adaptor(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.BalooWatcherApplication.xml baloo/localbaloofilelisting.h LocalBalooFileListing) endif() endif() if (Qt5DBus_FOUND) set(elisaLib_SOURCES ${elisaLib_SOURCES} mpris2/mpris2.cpp mpris2/mediaplayer2.cpp mpris2/mediaplayer2player.cpp ) endif() if (UPNPQT_FOUND) set(elisaLib_SOURCES ${elisaLib_SOURCES} upnp/upnpcontrolcontentdirectory.cpp upnp/upnpcontentdirectorymodel.cpp upnp/upnpcontrolconnectionmanager.cpp upnp/upnpcontrolmediaserver.cpp upnp/didlparser.cpp upnp/upnplistener.cpp upnp/upnpdiscoverallmusic.cpp ) endif() if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.fileindexer.xml baloo/fileindexer) qt5_add_dbus_interface(elisaLib_SOURCES ${BALOO_DBUS_INTERFACES_DIR}/org.kde.baloo.scheduler.xml baloo/scheduler) set(elisaLib_SOURCES ${elisaLib_SOURCES} ../src/baloo/baloolistener.cpp ../src/baloo/localbaloofilelisting.cpp ) endif() endif() kconfig_add_kcfg_files(elisaLib_SOURCES ../src/elisa_settings.kcfgc ) set(elisaLib_SOURCES ${elisaLib_SOURCES} ../src/elisa_core.kcfg ) add_library(elisaLib ${elisaLib_SOURCES}) target_link_libraries(elisaLib LINK_PUBLIC Qt5::Multimedia LINK_PRIVATE Qt5::Core Qt5::Sql Qt5::Widgets Qt5::Concurrent Qt5::Qml KF5::I18n KF5::CoreAddons KF5::ConfigCore KF5::ConfigGui) if (KF5FileMetaData_FOUND) target_link_libraries(elisaLib LINK_PRIVATE KF5::FileMetaData ) endif() if (KF5KIO_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets ) endif() if (KF5XmlGui_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::XmlGui ) endif() if (KF5ConfigWidgets_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::ConfigWidgets ) endif() if (KF5KCMUtils_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::KCMUtils ) endif() if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::Baloo ) endif() endif() if (Qt5DBus_FOUND) target_link_libraries(elisaLib LINK_PUBLIC Qt5::DBus ) if (KF5DBusAddons_FOUND) target_link_libraries(elisaLib LINK_PUBLIC KF5::DBusAddons ) endif() endif() if (LIBVLC_FOUND) target_include_directories(elisaLib PRIVATE ${LIBVLC_INCLUDE_DIR} ) target_link_libraries(elisaLib LINK_PRIVATE ${LIBVLC_LIBRARY} ) endif() if (ANDROID) target_link_libraries(elisaLib LINK_PUBLIC Qt5::AndroidExtras ) endif() generate_export_header(elisaLib BASE_NAME ElisaLib EXPORT_FILE_NAME elisaLib_export.h) set_target_properties(elisaLib PROPERTIES VERSION 0.1 SOVERSION 0 EXPORT_NAME ElisaLib ) if (NOT APPLE AND NOT WIN32) install(TARGETS elisaLib LIBRARY DESTINATION ${KDE_INSTALL_FULL_LIBDIR}/elisa NAMELINK_SKIP RUNTIME DESTINATION ${KDE_INSTALL_FULL_LIBDIR}/elisa BUNDLE DESTINATION ${KDE_INSTALL_FULL_LIBDIR}/elisa ) else() install(TARGETS elisaLib ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() set(elisaqmlplugin_SOURCES elisaqmlplugin.cpp elisautils.cpp ) if (KF5FileMetaData_FOUND) set(elisaqmlplugin_SOURCES ${elisaqmlplugin_SOURCES} embeddedcoverageimageprovider.cpp ) endif() add_library(elisaqmlplugin SHARED ${elisaqmlplugin_SOURCES}) target_link_libraries(elisaqmlplugin LINK_PRIVATE Qt5::Quick Qt5::Widgets KF5::ConfigCore KF5::ConfigGui elisaLib ) if (KF5FileMetaData_FOUND) target_link_libraries(elisaqmlplugin LINK_PRIVATE KF5::FileMetaData ) endif() set_target_properties(elisaqmlplugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/org/kde/elisa ) if (NOT APPLE AND NOT WIN32) set_target_properties(elisaqmlplugin PROPERTIES INSTALL_RPATH "${KDE_INSTALL_FULL_LIBDIR}/elisa;${CMAKE_INSTALL_RPATH}" ) endif() install(TARGETS elisaqmlplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/elisa/) install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/elisa) add_custom_target(copy) add_custom_target(copy2) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/org/kde/elisa) add_custom_command(TARGET copy PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${CMAKE_BINARY_DIR}/bin/org/kde/elisa/) add_custom_command(TARGET copy2 PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/plugins.qmltypes ${CMAKE_BINARY_DIR}/bin/org/kde/elisa/) add_dependencies(elisaqmlplugin copy copy2) if (Qt5Quick_FOUND AND Qt5Widgets_FOUND) set(elisa_SOURCES main.cpp windows/WindowsTheme.qml windows/PlatformIntegration.qml android/ElisaMainWindow.qml android/AndroidTheme.qml android/PlatformIntegration.qml android/AlbumsView.qml android/ArtistsView.qml android/TracksView.qml android/GenresView.qml qml/ElisaMainWindow.qml qml/ApplicationMenu.qml qml/BaseTheme.qml qml/Theme.qml qml/PlatformIntegration.qml qml/LabelWithToolTip.qml qml/RatingStar.qml qml/DraggableItem.qml - qml/TopNotification.qml - qml/TopNotificationItem.qml qml/TrackImportNotification.qml qml/HeaderBar.qml qml/NavigationActionBar.qml qml/MediaPlayerControl.qml qml/ContextView.qml qml/ContentView.qml qml/ViewSelector.qml qml/ViewSelectorDelegate.qml qml/DataGridView.qml qml/DataListView.qml qml/MediaPlayListView.qml qml/PlayListBasicView.qml qml/PlayListEntry.qml qml/SimplePlayListView.qml qml/PlayListAlbumHeader.qml qml/BasicPlayListAlbumHeader.qml qml/MetaDataDelegate.qml qml/TracksDiscHeader.qml qml/MediaTrackMetadataView.qml qml/GridBrowserView.qml qml/GridBrowserDelegate.qml qml/ListBrowserView.qml qml/ListBrowserDelegate.qml qml/FileBrowserView.qml qml/ScrollHelper.qml qml/FlatButtonWithToolTip.qml qml/HeaderFooterToolbar.qml ) qt5_add_resources(elisa_SOURCES resources.qrc) set_property(SOURCE qrc_resources.cpp PROPERTY SKIP_AUTOMOC ON) set(elisa_ICONS_PNG ../icons/128-apps-elisa.png ../icons/64-apps-elisa.png ../icons/48-apps-elisa.png ../icons/32-apps-elisa.png ../icons/22-apps-elisa.png ../icons/16-apps-elisa.png ) # add icons to application sources, to have them bundled ecm_add_app_icon(elisa_SOURCES ICONS ${elisa_ICONS_PNG}) add_executable(elisa ${elisa_SOURCES}) target_include_directories(elisa PRIVATE ${KDSoap_INCLUDE_DIRS}) target_link_libraries(elisa LINK_PRIVATE elisaLib Qt5::Widgets Qt5::QuickControls2 KF5::I18n KF5::CoreAddons KF5::ConfigCore KF5::ConfigGui ) if (ANDROID) target_link_libraries(elisa LINK_PRIVATE Qt5::AndroidExtras Qt5::Svg Qt5::Sql Qt5::Concurrent KF5::Kirigami2 ) endif() if (KF5Crash_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::Crash ) endif() if (KF5Declarative_FOUND) target_link_libraries(elisa LINK_PRIVATE KF5::Declarative ) endif() if (NOT APPLE AND NOT WIN32) set_target_properties(elisa PROPERTIES INSTALL_RPATH "${KDE_INSTALL_FULL_LIBDIR}/elisa;${CMAKE_INSTALL_RPATH}" ) endif() install(TARGETS elisa ${INSTALL_TARGETS_DEFAULT_ARGS}) endif() if (KF5ConfigWidgets_FOUND AND KF5Declarative_FOUND) add_subdirectory(localFileConfiguration) endif() set(elisaImport_SOURCES elisaimport.cpp elisaimportapplication.cpp ) kconfig_add_kcfg_files(elisaImport_SOURCES ../src/elisa_settings.kcfgc ) set(elisaImport_SOURCES ${elisaImport_SOURCES} ../src/elisa_core.kcfg ) add_executable(elisaImport ${elisaImport_SOURCES}) target_link_libraries(elisaImport LINK_PRIVATE KF5::ConfigCore KF5::ConfigGui elisaLib ) if (KF5FileMetaData_FOUND) target_link_libraries(elisaImport LINK_PRIVATE KF5::FileMetaData ) endif() set(QML_IMPORT_PATH ${CMAKE_BINARY_DIR}/bin CACHE INTERNAL "qml import path" FORCE) if (ANDROID) kirigami_package_breeze_icons(ICONS elisa) endif() diff --git a/src/abstractfile/abstractfilelistener.cpp b/src/abstractfile/abstractfilelistener.cpp index 62f8953a..c5a7bb67 100644 --- a/src/abstractfile/abstractfilelistener.cpp +++ b/src/abstractfile/abstractfilelistener.cpp @@ -1,115 +1,110 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "abstractfilelistener.h" #include "abstractfilelisting.h" #include "databaseinterface.h" -#include "notificationitem.h" #include class AbstractFileListenerPrivate { public: QThread mFileQueryThread; AbstractFileListing *mFileListing = nullptr; }; AbstractFileListener::AbstractFileListener(QObject *parent) : QObject(parent), d(std::make_unique()) { } AbstractFileListener::~AbstractFileListener() { d->mFileQueryThread.quit(); d->mFileQueryThread.wait(); } DatabaseInterface *AbstractFileListener::databaseInterface() const { return nullptr; } void AbstractFileListener::setDatabaseInterface(DatabaseInterface *model) { if (model) { connect(this, &AbstractFileListener::newTrackFile, d->mFileListing, &AbstractFileListing::newTrackFile); connect(d->mFileListing, &AbstractFileListing::tracksList, model, &DatabaseInterface::insertTracksList); connect(d->mFileListing, &AbstractFileListing::removedTracksList, model, &DatabaseInterface::removeTracksList); connect(d->mFileListing, &AbstractFileListing::modifyTracksList, model, &DatabaseInterface::insertTracksList); connect(d->mFileListing, &AbstractFileListing::askRestoredTracks, model, &DatabaseInterface::askRestoredTracks); connect(model, &DatabaseInterface::restoredTracks, d->mFileListing, &AbstractFileListing::restoredTracks); connect(model, &DatabaseInterface::cleanedDatabase, d->mFileListing, &AbstractFileListing::refreshContent); connect(model, &DatabaseInterface::finishRemovingTracksList, d->mFileListing, &AbstractFileListing::databaseFinishedRemovingTracksList); connect(model, &DatabaseInterface::finishInsertingTracksList, d->mFileListing, &AbstractFileListing::databaseFinishedInsertingTracksList); } Q_EMIT databaseInterfaceChanged(); } void AbstractFileListener::applicationAboutToQuit() { d->mFileListing->applicationAboutToQuit(); d->mFileQueryThread.exit(); d->mFileQueryThread.wait(); } void AbstractFileListener::quitListener() { applicationAboutToQuit(); Q_EMIT clearDatabase(); } void AbstractFileListener::setAllRootPaths(const QStringList &allRootPaths) { d->mFileListing->setAllRootPaths(allRootPaths); } void AbstractFileListener::setFileListing(AbstractFileListing *fileIndexer) { d->mFileListing = fileIndexer; d->mFileQueryThread.start(); d->mFileListing->moveToThread(&d->mFileQueryThread); connect(fileIndexer, &AbstractFileListing::indexingStarted, this, &AbstractFileListener::indexingStarted); connect(fileIndexer, &AbstractFileListing::indexingFinished, this, &AbstractFileListener::indexingFinished); - connect(fileIndexer, &AbstractFileListing::newNotification, - this, &AbstractFileListener::newNotification); - connect(fileIndexer, &AbstractFileListing::closeNotification, - this, &AbstractFileListener::closeNotification); } AbstractFileListing *AbstractFileListener::fileListing() const { return d->mFileListing; } #include "moc_abstractfilelistener.cpp" diff --git a/src/abstractfile/abstractfilelistener.h b/src/abstractfile/abstractfilelistener.h index dbc0c3d7..c1c2d81c 100644 --- a/src/abstractfile/abstractfilelistener.h +++ b/src/abstractfile/abstractfilelistener.h @@ -1,92 +1,84 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef ABSTRACTFILELISTENER_H #define ABSTRACTFILELISTENER_H -#include "notificationitem.h" - #include #include #include #include class AbstractFileListenerPrivate; class DatabaseInterface; class MusicAudioTrack; class AbstractFileListing; class AbstractFileListener : public QObject { Q_OBJECT Q_PROPERTY(DatabaseInterface* databaseInterface READ databaseInterface WRITE setDatabaseInterface NOTIFY databaseInterfaceChanged) public: explicit AbstractFileListener(QObject *parent = nullptr); ~AbstractFileListener() override; DatabaseInterface* databaseInterface() const; AbstractFileListing* fileListing() const; Q_SIGNALS: void databaseInterfaceChanged(); void newTrackFile(const MusicAudioTrack &newTrack); void indexingStarted(); void indexingFinished(); void configurationChanged(); void clearDatabase(); - void newNotification(NotificationItem notification); - - void closeNotification(QString notificationId); - public Q_SLOTS: void setDatabaseInterface(DatabaseInterface* databaseInterface); void applicationAboutToQuit(); void quitListener(); void setAllRootPaths(const QStringList &allRootPaths); protected: void setFileListing(AbstractFileListing *fileIndexer); - NotificationItem& currentNotification(); - private: std::unique_ptr d; }; #endif // ABSTRACTFILELISTENER_H diff --git a/src/abstractfile/abstractfilelisting.cpp b/src/abstractfile/abstractfilelisting.cpp index 9619ef49..474dfe4d 100644 --- a/src/abstractfile/abstractfilelisting.cpp +++ b/src/abstractfile/abstractfilelisting.cpp @@ -1,475 +1,469 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "abstractfilelisting.h" #include "config-upnp-qt.h" #include "abstractfile/indexercommon.h" #include "musicaudiotrack.h" -#include "notificationitem.h" #include "filescanner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include class AbstractFileListingPrivate { public: QStringList mAllRootPaths; QFileSystemWatcher mFileSystemWatcher; QHash mAllAlbumCover; QHash>> mDiscoveredFiles; FileScanner mFileScanner; QMimeDatabase mMimeDb; QHash mAllFiles; QAtomicInt mStopRequest = 0; int mImportedTracksCount = 0; - int mNotificationUpdateInterval = 1; - int mNewFilesEmitInterval = 1; bool mHandleNewFiles = true; bool mWaitEndTrackRemoval = false; bool mErrorWatchingFileSystemChanges = false; }; AbstractFileListing::AbstractFileListing(QObject *parent) : QObject(parent), d(std::make_unique()) { connect(&d->mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &AbstractFileListing::directoryChanged); connect(&d->mFileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &AbstractFileListing::fileChanged); } AbstractFileListing::~AbstractFileListing() = default; void AbstractFileListing::init() { Q_EMIT askRestoredTracks(); } void AbstractFileListing::newTrackFile(const MusicAudioTrack &partialTrack) { auto scanFileInfo = QFileInfo(partialTrack.resourceURI().toLocalFile()); const auto &newTrack = scanOneFile(partialTrack.resourceURI(), scanFileInfo); if (newTrack.isValid() && newTrack != partialTrack) { Q_EMIT modifyTracksList({newTrack}, d->mAllAlbumCover); } } void AbstractFileListing::restoredTracks(QHash allFiles) { executeInit(std::move(allFiles)); refreshContent(); } void AbstractFileListing::setAllRootPaths(const QStringList &allRootPaths) { if (d->mAllRootPaths == allRootPaths) { return; } d->mAllRootPaths = allRootPaths; } void AbstractFileListing::databaseFinishedInsertingTracksList() { } void AbstractFileListing::databaseFinishedRemovingTracksList() { if (waitEndTrackRemoval()) { Q_EMIT indexingFinished(); setWaitEndTrackRemoval(false); } } void AbstractFileListing::applicationAboutToQuit() { d->mStopRequest = 1; } const QStringList &AbstractFileListing::allRootPaths() const { return d->mAllRootPaths; } void AbstractFileListing::scanDirectory(QList &newFiles, const QUrl &path) { if (d->mStopRequest == 1) { return; } QDir rootDirectory(path.toLocalFile()); rootDirectory.refresh(); if (rootDirectory.exists()) { watchPath(path.toLocalFile()); } auto ¤tDirectoryListingFiles = d->mDiscoveredFiles[path]; auto currentFilesList = QSet(); rootDirectory.refresh(); const auto entryList = rootDirectory.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (const auto &oneEntry : entryList) { auto newFilePath = QUrl::fromLocalFile(oneEntry.canonicalFilePath()); if (oneEntry.isDir() || oneEntry.isFile()) { currentFilesList.insert(newFilePath); } } auto removedTracks = QVector>(); for (const auto &removedFilePath : currentDirectoryListingFiles) { auto itFilePath = std::find(currentFilesList.begin(), currentFilesList.end(), removedFilePath.first); if (itFilePath != currentFilesList.end()) { continue; } removedTracks.push_back(removedFilePath); } auto allRemovedTracks = QList(); for (const auto &oneRemovedTrack : removedTracks) { if (oneRemovedTrack.second) { allRemovedTracks.push_back(oneRemovedTrack.first); } else { removeFile(oneRemovedTrack.first, allRemovedTracks); } } for (const auto &oneRemovedTrack : removedTracks) { currentDirectoryListingFiles.remove(oneRemovedTrack); currentDirectoryListingFiles.remove(oneRemovedTrack); } if (!allRemovedTracks.isEmpty()) { Q_EMIT removedTracksList(allRemovedTracks); } if (!d->mHandleNewFiles) { return; } for (const auto &newFilePath : currentFilesList) { QFileInfo oneEntry(newFilePath.toLocalFile()); auto itFilePath = std::find(currentDirectoryListingFiles.begin(), currentDirectoryListingFiles.end(), QPair{newFilePath, oneEntry.isFile()}); if (itFilePath != currentDirectoryListingFiles.end()) { continue; } if (oneEntry.isDir()) { addFileInDirectory(newFilePath, path); scanDirectory(newFiles, newFilePath); if (d->mStopRequest == 1) { break; } continue; } if (!oneEntry.isFile()) { continue; } auto newTrack = scanOneFile(newFilePath, oneEntry); if (newTrack.isValid() && d->mStopRequest == 0) { addCover(newTrack); addFileInDirectory(newTrack.resourceURI(), path); newFiles.push_back(newTrack); ++d->mImportedTracksCount; - if (d->mImportedTracksCount % d->mNotificationUpdateInterval == 0) { - d->mNotificationUpdateInterval = std::min(50, 1 + d->mNotificationUpdateInterval * 2); - } if (newFiles.size() > d->mNewFilesEmitInterval && d->mStopRequest == 0) { d->mNewFilesEmitInterval = std::min(50, 1 + d->mNewFilesEmitInterval * d->mNewFilesEmitInterval); emitNewFiles(newFiles); newFiles.clear(); } } if (d->mStopRequest == 1) { break; } } } void AbstractFileListing::directoryChanged(const QString &path) { const auto directoryEntry = d->mDiscoveredFiles.find(QUrl::fromLocalFile(path)); if (directoryEntry == d->mDiscoveredFiles.end()) { return; } Q_EMIT indexingStarted(); scanDirectoryTree(path); Q_EMIT indexingFinished(); } void AbstractFileListing::fileChanged(const QString &modifiedFileName) { QFileInfo modifiedFileInfo(modifiedFileName); auto modifiedFile = QUrl::fromLocalFile(modifiedFileName); auto modifiedTrack = scanOneFile(modifiedFile, modifiedFileInfo); if (modifiedTrack.isValid()) { Q_EMIT modifyTracksList({modifiedTrack}, d->mAllAlbumCover); } } void AbstractFileListing::executeInit(QHash allFiles) { d->mAllFiles = std::move(allFiles); } void AbstractFileListing::triggerRefreshOfContent() { d->mImportedTracksCount = 0; } void AbstractFileListing::refreshContent() { triggerRefreshOfContent(); } MusicAudioTrack AbstractFileListing::scanOneFile(const QUrl &scanFile, const QFileInfo &scanFileInfo) { MusicAudioTrack newTrack; qCDebug(orgKdeElisaIndexer) << "AbstractFileListing::scanOneFile" << scanFile; auto localFileName = scanFile.toLocalFile(); const auto &fileMimeType = d->mMimeDb.mimeTypeForFile(localFileName); if (!fileMimeType.name().startsWith(QStringLiteral("audio/"))) { return newTrack; } if (scanFileInfo.exists()) { auto itExistingFile = d->mAllFiles.find(scanFile); if (itExistingFile != d->mAllFiles.end()) { if (*itExistingFile >= scanFileInfo.fileTime(QFile::FileModificationTime)) { d->mAllFiles.erase(itExistingFile); return newTrack; } } } newTrack = d->mFileScanner.scanOneFile(scanFile, d->mMimeDb); if (newTrack.isValid()) { newTrack.setHasEmbeddedCover(checkEmbeddedCoverImage(localFileName)); newTrack.setFileModificationTime(scanFileInfo.fileTime(QFile::FileModificationTime)); if (scanFileInfo.exists()) { watchPath(scanFile.toLocalFile()); } } return newTrack; } void AbstractFileListing::watchPath(const QString &pathName) { if (!d->mFileSystemWatcher.addPath(pathName)) { qCDebug(orgKdeElisaIndexer) << "AbstractFileListing::watchPath" << "fail for" << pathName; if (!d->mErrorWatchingFileSystemChanges) { d->mErrorWatchingFileSystemChanges = true; Q_EMIT errorWatchingFileSystemChanges(); } } } void AbstractFileListing::addFileInDirectory(const QUrl &newFile, const QUrl &directoryName) { const auto directoryEntry = d->mDiscoveredFiles.find(directoryName); if (directoryEntry == d->mDiscoveredFiles.end()) { watchPath(directoryName.toLocalFile()); QDir currentDirectory(directoryName.toLocalFile()); if (currentDirectory.cdUp()) { const auto parentDirectoryName = currentDirectory.absolutePath(); const auto parentDirectory = QUrl::fromLocalFile(parentDirectoryName); const auto parentDirectoryEntry = d->mDiscoveredFiles.find(parentDirectory); if (parentDirectoryEntry == d->mDiscoveredFiles.end()) { watchPath(parentDirectoryName); } auto &parentCurrentDirectoryListingFiles = d->mDiscoveredFiles[parentDirectory]; parentCurrentDirectoryListingFiles.insert({directoryName, false}); } } auto ¤tDirectoryListingFiles = d->mDiscoveredFiles[directoryName]; QFileInfo isAFile(newFile.toLocalFile()); currentDirectoryListingFiles.insert({newFile, isAFile.isFile()}); } void AbstractFileListing::scanDirectoryTree(const QString &path) { auto newFiles = QList(); scanDirectory(newFiles, QUrl::fromLocalFile(path)); if (!newFiles.isEmpty() && d->mStopRequest == 0) { emitNewFiles(newFiles); } } void AbstractFileListing::setHandleNewFiles(bool handleThem) { d->mHandleNewFiles = handleThem; } void AbstractFileListing::emitNewFiles(const QList &tracks) { Q_EMIT tracksList(tracks, d->mAllAlbumCover); } void AbstractFileListing::addCover(const MusicAudioTrack &newTrack) { auto itCover = d->mAllAlbumCover.find(newTrack.albumName()); if (itCover != d->mAllAlbumCover.end()) { return; } auto coverUrl = d->mFileScanner.searchForCoverFile(newTrack.resourceURI().toLocalFile()); if (!coverUrl.isEmpty()) { d->mAllAlbumCover[newTrack.resourceURI().toString()] = coverUrl; } } void AbstractFileListing::removeDirectory(const QUrl &removedDirectory, QList &allRemovedFiles) { const auto itRemovedDirectory = d->mDiscoveredFiles.find(removedDirectory); if (itRemovedDirectory == d->mDiscoveredFiles.end()) { return; } const auto ¤tRemovedDirectory = *itRemovedDirectory; for (const auto &itFile : currentRemovedDirectory) { if (itFile.first.isValid() && !itFile.first.isEmpty()) { removeFile(itFile.first, allRemovedFiles); if (itFile.second) { allRemovedFiles.push_back(itFile.first); } } } d->mDiscoveredFiles.erase(itRemovedDirectory); } void AbstractFileListing::removeFile(const QUrl &oneRemovedTrack, QList &allRemovedFiles) { auto itRemovedDirectory = d->mDiscoveredFiles.find(oneRemovedTrack); if (itRemovedDirectory != d->mDiscoveredFiles.end()) { removeDirectory(oneRemovedTrack, allRemovedFiles); } } QHash &AbstractFileListing::allFiles() { return d->mAllFiles; } void AbstractFileListing::checkFilesToRemove() { QList allRemovedFiles; for (auto itFile = d->mAllFiles.begin(); itFile != d->mAllFiles.end(); ++itFile) { allRemovedFiles.push_back(itFile.key()); } if (!allRemovedFiles.isEmpty()) { setWaitEndTrackRemoval(true); Q_EMIT removedTracksList(allRemovedFiles); } } FileScanner &AbstractFileListing::fileScanner() { return d->mFileScanner; } bool AbstractFileListing::checkEmbeddedCoverImage(const QString &localFileName) { return d->mFileScanner.checkEmbeddedCoverImage(localFileName); } bool AbstractFileListing::waitEndTrackRemoval() const { return d->mWaitEndTrackRemoval; } void AbstractFileListing::setWaitEndTrackRemoval(bool wait) { d->mWaitEndTrackRemoval = wait; } const QMimeDatabase &AbstractFileListing::mimeDatabase() const { return d->mMimeDb; } #include "moc_abstractfilelisting.cpp" diff --git a/src/abstractfile/abstractfilelisting.h b/src/abstractfile/abstractfilelisting.h index ca564d2a..54cb0ec7 100644 --- a/src/abstractfile/abstractfilelisting.h +++ b/src/abstractfile/abstractfilelisting.h @@ -1,146 +1,139 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef ABSTRACTFILELISTING_H #define ABSTRACTFILELISTING_H #include "elisaLib_export.h" -#include "notificationitem.h" - #include #include #include #include #include #include #include class AbstractFileListingPrivate; class MusicAudioTrack; -class NotificationItem; class FileScanner; class QFileInfo; class QMimeDatabase; class ELISALIB_EXPORT AbstractFileListing : public QObject { Q_OBJECT public: explicit AbstractFileListing(QObject *parent = nullptr); ~AbstractFileListing() override; virtual void applicationAboutToQuit(); const QStringList& allRootPaths() const; Q_SIGNALS: void tracksList(const QList &tracks, const QHash &covers); void removedTracksList(const QList &removedTracks); void modifyTracksList(const QList &modifiedTracks, const QHash &covers); void indexingStarted(); void indexingFinished(); - void newNotification(NotificationItem notification); - - void closeNotification(QString notificationId); - void askRestoredTracks(); void errorWatchingFileSystemChanges(); public Q_SLOTS: void refreshContent(); void init(); void newTrackFile(const MusicAudioTrack &partialTrack); void restoredTracks(QHash allFiles); void setAllRootPaths(const QStringList &allRootPaths); void databaseFinishedInsertingTracksList(); void databaseFinishedRemovingTracksList(); protected Q_SLOTS: void directoryChanged(const QString &path); void fileChanged(const QString &modifiedFileName); protected: virtual void executeInit(QHash allFiles); virtual void triggerRefreshOfContent(); void scanDirectory(QList &newFiles, const QUrl &path); virtual MusicAudioTrack scanOneFile(const QUrl &scanFile, const QFileInfo &scanFileInfo); void watchPath(const QString &pathName); void addFileInDirectory(const QUrl &newFile, const QUrl &directoryName); void scanDirectoryTree(const QString &path); void setHandleNewFiles(bool handleThem); void emitNewFiles(const QList &tracks); void addCover(const MusicAudioTrack &newTrack); void removeDirectory(const QUrl &removedDirectory, QList &allRemovedFiles); void removeFile(const QUrl &oneRemovedTrack, QList &allRemovedFiles); QHash& allFiles(); void checkFilesToRemove(); FileScanner& fileScanner(); bool checkEmbeddedCoverImage(const QString &localFileName); bool waitEndTrackRemoval() const; void setWaitEndTrackRemoval(bool wait); const QMimeDatabase& mimeDatabase() const; private: std::unique_ptr d; }; #endif // ABSTRACTFILELISTING_H diff --git a/src/android/androidmusiclistener.h b/src/android/androidmusiclistener.h index 11c406b5..1659ebbb 100644 --- a/src/android/androidmusiclistener.h +++ b/src/android/androidmusiclistener.h @@ -1,113 +1,107 @@ /* * Copyright 2018 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef ANDROIDMUSICLISTENER_H #define ANDROIDMUSICLISTENER_H -#include "notificationitem.h" - #include #include #include #include #include #include class DatabaseInterface; class MusicAudioTrack; class AndroidMusicListenerPrivate; class AndroidMusicListener : public QObject { Q_OBJECT Q_PROPERTY(DatabaseInterface* databaseInterface READ databaseInterface WRITE setDatabaseInterface NOTIFY databaseInterfaceChanged) public: explicit AndroidMusicListener(QObject *parent = nullptr); ~AndroidMusicListener() override; DatabaseInterface* databaseInterface() const; static void registerNativeMethods(); static AndroidMusicListener* currentInstance(); void androidMusicTracksScanStarted(); void newMusicTrack(const QString &trackDescription); void androidMusicTracksScanFinished(); void androidMusicAlbumsScanStarted(); void newMusicAlbum(const QString &albumDescription); void androidMusicAlbumsScanFinished(); Q_SIGNALS: void databaseInterfaceChanged(); void indexingStarted(); void indexingFinished(); void clearDatabase(const QString &listenerName); - void newNotification(NotificationItem notification); - - void closeNotification(QString notificationId); - void tracksList(const QList &tracks, const QHash &covers, const QString &musicSource); void removedTracksList(const QList &removedTracks); void modifyTracksList(const QList &modifiedTracks, const QHash &covers, const QString &musicSource); void askRestoredTracks(const QString &musicSource); public Q_SLOTS: void setDatabaseInterface(DatabaseInterface* databaseInterface); void applicationAboutToQuit(); void quitListener(); void restoredTracks(const QString &musicSource, QHash allFiles); void init(); private: static AndroidMusicListener* mCurrentInstance; std::unique_ptr d; }; #endif // ANDROIDMUSICLISTENER_H diff --git a/src/baloo/localbaloofilelisting.cpp b/src/baloo/localbaloofilelisting.cpp index 0aa44a90..75f140e6 100644 --- a/src/baloo/localbaloofilelisting.cpp +++ b/src/baloo/localbaloofilelisting.cpp @@ -1,429 +1,427 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "localbaloofilelisting.h" #include "baloo/baloocommon.h" #include "musicaudiotrack.h" -#include "notificationitem.h" #include "elisa_settings.h" #include "elisautils.h" #include "baloo/scheduler.h" #include "baloo/fileindexer.h" #include "baloo/main.h" #include "baloowatcherapplicationadaptor.h" #include "filescanner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class LocalBalooFileListingPrivate { public: Baloo::Query mQuery; QHash> mAllAlbums; QDBusServiceWatcher mServiceWatcher; QScopedPointer mBalooMainInterface; QScopedPointer mBalooIndexer; QScopedPointer mBalooScheduler; BalooWatcherApplicationAdaptor *mDbusAdaptor = nullptr; QAtomicInt mStopRequest = 0; bool mIsRegisteredToBaloo = false; bool mIsRegisteringToBaloo = false; bool mIsRegisteredToBalooWatcher = false; bool mIsRegisteringToBalooWatcher = false; }; LocalBalooFileListing::LocalBalooFileListing(QObject *parent) : AbstractFileListing(parent), d(std::make_unique()) { d->mQuery.addType(QStringLiteral("Audio")); setHandleNewFiles(false); auto sessionBus = QDBusConnection::sessionBus(); d->mDbusAdaptor = new BalooWatcherApplicationAdaptor(this); sessionBus.registerObject(QStringLiteral("/org/kde/BalooWatcherApplication"), d->mDbusAdaptor, QDBusConnection::ExportAllContents); connect(&d->mServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &LocalBalooFileListing::serviceRegistered); connect(&d->mServiceWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &LocalBalooFileListing::serviceOwnerChanged); connect(&d->mServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &LocalBalooFileListing::serviceUnregistered); d->mServiceWatcher.setConnection(sessionBus); d->mServiceWatcher.addWatchedService(QStringLiteral("org.kde.baloo")); if (sessionBus.interface()->isServiceRegistered(QStringLiteral("org.kde.baloo"))) { registerToBaloo(); } } LocalBalooFileListing::~LocalBalooFileListing() { - Q_EMIT closeNotification(QStringLiteral("balooInvalidConfiguration")); } void LocalBalooFileListing::applicationAboutToQuit() { AbstractFileListing::applicationAboutToQuit(); d->mStopRequest = 1; } void LocalBalooFileListing::newBalooFile(const QString &fileName) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::newBalooFile" << fileName; auto scanFileInfo = QFileInfo(fileName); if (!scanFileInfo.exists()) { return; } const auto &fileMimeType = mimeDatabase().mimeTypeForFile(fileName); if (!fileMimeType.name().startsWith(QStringLiteral("audio/"))) { return; } Q_EMIT indexingStarted(); auto newFile = QUrl::fromLocalFile(fileName); auto newTrack = scanOneFile(newFile, scanFileInfo); if (newTrack.isValid()) { QFileInfo newFileInfo(fileName); addFileInDirectory(newFile, QUrl::fromLocalFile(newFileInfo.absoluteDir().absolutePath())); emitNewFiles({newTrack}); } Q_EMIT indexingFinished(); } void LocalBalooFileListing::registeredToBaloo(QDBusPendingCallWatcher *watcher) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBaloo"; if (!watcher) { return; } QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBaloo" << reply.error().name() << reply.error().message(); d->mIsRegisteredToBaloo = false; } else { d->mIsRegisteredToBaloo = true; } d->mIsRegisteringToBaloo = false; watcher->deleteLater(); } void LocalBalooFileListing::registeredToBalooWatcher(QDBusPendingCallWatcher *watcher) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBalooWatcher"; if (!watcher) { return; } QDBusPendingReply<> reply = *watcher; if (reply.isError()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registeredToBalooWatcher" << reply.error().name() << reply.error().message(); d->mIsRegisteredToBalooWatcher = false; } else { d->mIsRegisteredToBalooWatcher = true; } d->mIsRegisteringToBalooWatcher = false; watcher->deleteLater(); } void LocalBalooFileListing::registerToBaloo() { if (d->mIsRegisteringToBaloo || d->mIsRegisteringToBalooWatcher) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "already registering"; return; } qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo"; d->mIsRegisteringToBaloo = true; d->mIsRegisteringToBalooWatcher = true; auto sessionBus = QDBusConnection::sessionBus(); d->mBalooMainInterface.reset(new org::kde::baloo::main(QStringLiteral("org.kde.baloo"), QStringLiteral("/"), sessionBus, this)); if (!d->mBalooMainInterface->isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "invalid org.kde.baloo/main interface"; return; } d->mBalooIndexer.reset(new org::kde::baloo::fileindexer(QStringLiteral("org.kde.baloo"), QStringLiteral("/fileindexer"), sessionBus, this)); if (!d->mBalooIndexer->isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "invalid org.kde.baloo/fileindexer interface"; return; } connect(d->mBalooIndexer.data(), &org::kde::baloo::fileindexer::finishedIndexingFile, this, &LocalBalooFileListing::newBalooFile); d->mBalooScheduler.reset(new org::kde::baloo::scheduler(QStringLiteral("org.kde.baloo"), QStringLiteral("/scheduler"), sessionBus, this)); if (!d->mBalooScheduler->isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "invalid org.kde.baloo/scheduler interface"; return; } qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "call registerMonitor"; auto answer = d->mBalooIndexer->registerMonitor(); if (answer.isError()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::executeInit" << answer.error().name() << answer.error().message(); } auto pendingCallWatcher = new QDBusPendingCallWatcher(answer); connect(pendingCallWatcher, &QDBusPendingCallWatcher::finished, this, &LocalBalooFileListing::registeredToBaloo); if (pendingCallWatcher->isFinished()) { registeredToBaloo(pendingCallWatcher); } auto pendingCall = d->mBalooMainInterface->registerBalooWatcher(QStringLiteral("org.mpris.MediaPlayer2.elisa/org/kde/BalooWatcherApplication")); qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::registerToBaloo" << "call registerBalooWatcher"; auto pendingCallWatcher2 = new QDBusPendingCallWatcher(pendingCall); connect(pendingCallWatcher2, &QDBusPendingCallWatcher::finished, this, &LocalBalooFileListing::registeredToBalooWatcher); if (pendingCallWatcher2->isFinished()) { registeredToBalooWatcher(pendingCallWatcher2); } } void LocalBalooFileListing::renamedFiles(const QString &from, const QString &to, const QStringList &listFiles) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::renamedFiles" << from << to << listFiles; } void LocalBalooFileListing::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(oldOwner); Q_UNUSED(newOwner); qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::serviceOwnerChanged" << serviceName << oldOwner << newOwner; if (serviceName == QStringLiteral("org.kde.baloo") && !newOwner.isEmpty()) { d->mIsRegisteredToBaloo = false; d->mIsRegisteredToBalooWatcher = false; registerToBaloo(); } } void LocalBalooFileListing::serviceRegistered(const QString &serviceName) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::serviceRegistered" << serviceName; if (serviceName == QStringLiteral("org.kde.baloo")) { registerToBaloo(); } } void LocalBalooFileListing::serviceUnregistered(const QString &serviceName) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::serviceUnregistered" << serviceName; if (serviceName == QStringLiteral("org.kde.baloo")) { d->mIsRegisteredToBaloo = false; d->mIsRegisteredToBalooWatcher = false; } } void LocalBalooFileListing::executeInit(QHash allFiles) { AbstractFileListing::executeInit(std::move(allFiles)); } void LocalBalooFileListing::triggerRefreshOfContent() { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent"; Q_EMIT indexingStarted(); AbstractFileListing::triggerRefreshOfContent(); const auto &rootPaths = allRootPaths(); bool hasSingleRootPath = (rootPaths.size() == 1); auto singleRootPath = rootPaths.at(0); auto resultIterator = d->mQuery.exec(); auto newFiles = QList(); while(resultIterator.next() && d->mStopRequest == 0) { const auto &fileName = resultIterator.filePath(); if (hasSingleRootPath) { if (!fileName.startsWith(singleRootPath)) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "does not match root paths"; continue; } } else { bool isIncluded = false; for (const auto &oneRootPath : rootPaths) { if (fileName.startsWith(oneRootPath)) { isIncluded = true; break; } } if (!isIncluded) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "does not match root paths"; continue; } } const auto &newFileUrl = QUrl::fromLocalFile(resultIterator.filePath()); auto scanFileInfo = QFileInfo(fileName); if (!scanFileInfo.exists()) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "file does not exists"; return; } auto itExistingFile = allFiles().find(newFileUrl); if (itExistingFile != allFiles().end()) { if (*itExistingFile >= scanFileInfo.fileTime(QFile::FileModificationTime)) { allFiles().erase(itExistingFile); qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "file not modified since last scan"; continue; } } const auto currentDirectory = QUrl::fromLocalFile(scanFileInfo.absoluteDir().absolutePath()); addFileInDirectory(newFileUrl, currentDirectory); const auto &newTrack = scanOneFile(newFileUrl, scanFileInfo); if (newTrack.isValid()) { newFiles.push_back(newTrack); if (newFiles.size() > 500 && d->mStopRequest == 0) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << "insert new tracks in database" << newFiles.count(); emitNewFiles(newFiles); newFiles.clear(); } } else { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << fileName << "invalid track" << newTrack; } } if (!newFiles.isEmpty() && d->mStopRequest == 0) { qCDebug(orgKdeElisaBaloo()) << "LocalBalooFileListing::triggerRefreshOfContent" << "insert new tracks in database" << newFiles.count(); emitNewFiles(newFiles); } setWaitEndTrackRemoval(false); checkFilesToRemove(); if (!waitEndTrackRemoval()) { Q_EMIT indexingFinished(); } } MusicAudioTrack LocalBalooFileListing::scanOneFile(const QUrl &scanFile, const QFileInfo &scanFileInfo) { auto newTrack = MusicAudioTrack(); auto localFileName = scanFile.toLocalFile(); Baloo::File match(localFileName); match.load(); newTrack.setFileModificationTime(scanFileInfo.fileTime(QFile::FileModificationTime)); newTrack.setResourceURI(scanFile); fileScanner().scanProperties(match, newTrack); if (!newTrack.isValid()) { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::scanOneFile" << scanFile << "falling back to plain file metadata analysis"; newTrack = AbstractFileListing::scanOneFile(scanFile, scanFileInfo); } if (newTrack.isValid()) { newTrack.setHasEmbeddedCover(checkEmbeddedCoverImage(localFileName)); addCover(newTrack); watchPath(localFileName); } else { qCDebug(orgKdeElisaBaloo) << "LocalBalooFileListing::scanOneFile" << scanFile << "invalid track"; } return newTrack; } #include "moc_localbaloofilelisting.cpp" diff --git a/src/elisaimport.cpp b/src/elisaimport.cpp index 3dbe6d6a..0cea5379 100644 --- a/src/elisaimport.cpp +++ b/src/elisaimport.cpp @@ -1,62 +1,61 @@ /* * Copyright 2015-2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "config-upnp-qt.h" #include "musiclistenersmanager.h" #include "elisaimportapplication.h" #include "elisa_settings.h" #include "musicaudiotrack.h" #include #include #include #include int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); qRegisterMetaType>("QHash"); qRegisterMetaType>("QList"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType>(); - qRegisterMetaType("NotificationItem"); qRegisterMetaType>("QMap"); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); parser.process(app); auto configurationFileName = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); configurationFileName += QStringLiteral("/elisarc"); Elisa::ElisaConfiguration::instance(configurationFileName); Elisa::ElisaConfiguration::self()->load(); Elisa::ElisaConfiguration::self()->save(); MusicListenersManager myMusicManager; ElisaImportApplication myApplication; QObject::connect(&myMusicManager, &MusicListenersManager::indexerBusyChanged, &myApplication, &ElisaImportApplication::indexingChanged); return app.exec(); } diff --git a/src/elisaqmlplugin.cpp b/src/elisaqmlplugin.cpp index 2b65bf88..05907452 100644 --- a/src/elisaqmlplugin.cpp +++ b/src/elisaqmlplugin.cpp @@ -1,184 +1,180 @@ /* * Copyright 2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "elisaqmlplugin.h" #if defined UPNPQT_FOUND && UPNPQT_FOUND #include "upnp/upnpcontrolconnectionmanager.h" #include "upnp/upnpcontrolmediaserver.h" #include "upnp/upnpcontrolcontentdirectory.h" #include "upnp/upnpcontentdirectorymodel.h" #include "upnpdevicedescription.h" #include "upnp/didlparser.h" #include "upnp/upnpdiscoverallmusic.h" #include "upnpssdpengine.h" #include "upnpabstractservice.h" #include "upnpcontrolabstractdevice.h" #include "upnpcontrolabstractservice.h" #include "upnpbasictypes.h" #endif #include "elisautils.h" #include "elisaapplication.h" #include "progressindicator.h" #include "mediaplaylist.h" #include "managemediaplayercontrol.h" #include "manageheaderbar.h" #include "manageaudioplayer.h" #include "musicaudiotrack.h" #include "musiclistenersmanager.h" #include "trackslistener.h" #include "viewmanager.h" #include "databaseinterface.h" #include "models/datamodel.h" #include "models/trackmetadatamodel.h" #include "models/trackcontextmetadatamodel.h" #include "models/viewsmodel.h" #include "models/gridviewproxymodel.h" #include "models/alltracksproxymodel.h" #include "models/singlealbumproxymodel.h" #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND #include "embeddedcoverageimageprovider.h" #endif #if defined KF5KIO_FOUND && KF5KIO_FOUND #include "models/filebrowsermodel.h" #include "models/filebrowserproxymodel.h" #endif #include "audiowrapper.h" -#include "notificationitem.h" -#include "topnotificationmanager.h" #if defined Qt5DBus_FOUND && Qt5DBus_FOUND #include "mpris2/mpris2.h" #include "mpris2/mediaplayer2player.h" #endif #include #include #include #include #include #include void ElisaQmlTestPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { QQmlExtensionPlugin::initializeEngine(engine, uri); #if defined KF5FileMetaData_FOUND && KF5FileMetaData_FOUND engine->addImageProvider(QStringLiteral("cover"), new EmbeddedCoverageImageProvider); #endif } void ElisaQmlTestPlugin::registerTypes(const char *uri) { #if defined UPNPQT_FOUND && UPNPQT_FOUND qmlRegisterType(uri, 1, 0, "UpnpSsdpEngine"); qmlRegisterType(uri, 1, 0, "UpnpDiscoverAllMusic"); qmlRegisterType(uri, 1, 0, "UpnpAbstractDevice"); qmlRegisterType(uri, 1, 0, "UpnpAbstractService"); qmlRegisterType(uri, 1, 0, "UpnpControlAbstractDevice"); qmlRegisterType(uri, 1, 0, "UpnpControlAbstractService"); qmlRegisterType(uri, 1, 0, "UpnpControlConnectionManager"); qmlRegisterType(uri, 1, 0, "UpnpControlMediaServer"); qmlRegisterType(uri, 1, 0, "UpnpContentDirectoryModel"); qmlRegisterType(uri, 1, 0, "DidlParser"); qmlRegisterType(uri, 1, 0, "UpnpControlContentDirectory"); qmlRegisterType(uri, 1, 0, "UpnpDeviceDescription"); qRegisterMetaType(); qRegisterMetaType >(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); #endif qmlRegisterType(uri, 1, 0, "MediaPlayList"); qmlRegisterType(uri, 1, 0, "ManageMediaPlayerControl"); qmlRegisterType(uri, 1, 0, "ManageHeaderBar"); qmlRegisterType(uri, 1, 0, "ManageAudioPlayer"); qmlRegisterType(uri, 1, 0, "ProgressIndicator"); qmlRegisterType(uri, 1, 0, "MusicListenersManager"); qmlRegisterType(uri, 1, 0, "ViewManager"); qmlRegisterType(uri, 1, 0, "DataModel"); qmlRegisterType(uri, 1, 0, "TrackMetadataModel"); qmlRegisterType(uri, 1, 0, "TrackContextMetaDataModel"); qmlRegisterType(uri, 1, 0, "ViewsModel"); qmlRegisterType(uri, 1, 0, "GridViewProxyModel"); qmlRegisterType(uri, 1, 0, "AllTracksProxyModel"); qmlRegisterType(uri, 1, 0, "SingleAlbumProxyModel"); #if defined KF5KIO_FOUND && KF5KIO_FOUND qmlRegisterType(uri, 1, 0, "FileBrowserModel"); qmlRegisterType(uri, 1, 0, "FileBrowserProxyModel"); #endif qmlRegisterType(uri, 1, 0, "AudioWrapper"); - qmlRegisterType(uri, 1, 0, "TopNotificationManager"); qmlRegisterUncreatableType(uri, 1, 0, "DatabaseInterface", QStringLiteral("Only created in c++")); #if defined Qt5DBus_FOUND && Qt5DBus_FOUND qmlRegisterType(uri, 1, 0, "Mpris2"); qRegisterMetaType(); #endif qRegisterMetaType(); qRegisterMetaType>("QHash"); qRegisterMetaType>("QHash"); qRegisterMetaType>("QList"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QVector"); qRegisterMetaType>("QHash"); qRegisterMetaType("DatabaseInterface::ListTrackDataType"); qRegisterMetaType("DatabaseInterface::ListAlbumDataType"); qRegisterMetaType("DatabaseInterface::ListArtistDataType"); qRegisterMetaType("DatabaseInterface::ListGenreDataType"); qRegisterMetaType("ModelDataLoader::ListTrackDataType"); qRegisterMetaType("ModelDataLoader::ListAlbumDataType"); qRegisterMetaType("ModelDataLoader::ListArtistDataType"); qRegisterMetaType("ModelDataLoader::ListGenreDataType"); qRegisterMetaType("ModelDataLoader::AlbumDataType"); qRegisterMetaType("TracksListener::ListTrackDataType"); qRegisterMetaType>(); qRegisterMetaType(); - qRegisterMetaType("NotificationItem"); qRegisterMetaType>("QMap"); qRegisterMetaType("ElisaUtils::PlayListEnqueueMode"); qRegisterMetaType("ElisaUtils::PlayListEnqueueTriggerPlay"); qRegisterMetaType("ElisaUtils::PlayListEntryType"); qRegisterMetaType("ElisaUtils::EntryData"); qRegisterMetaType("ElisaUtils::EntryDataList"); qRegisterMetaType("ElisaUtils::FilterType"); qRegisterMetaType("DatabaseInterface::TrackDataType"); qRegisterMetaType("DatabaseInterface::AlbumDataType"); qRegisterMetaType("DatabaseInterface::ArtistDataType"); qRegisterMetaType("DatabaseInterface::GenreDataType"); qRegisterMetaType("ModelDataLoader::TrackDataType"); qRegisterMetaType("TracksListener::TrackDataType"); qRegisterMetaType("ViewManager::ViewsType"); qRegisterMetaType("ViewManager::SortOrder"); qRegisterMetaTypeStreamOperators("PlayListControler::PlayerState"); qmlRegisterUncreatableType(uri, 1, 0, "ElisaApplication", QStringLiteral("only one and done in c++")); qmlRegisterUncreatableMetaObject(ElisaUtils::staticMetaObject, uri, 1, 0, "ElisaUtils", QStringLiteral("Namespace ElisaUtils")); } diff --git a/src/musiclistenersmanager.cpp b/src/musiclistenersmanager.cpp index 76ce78f2..c445b542 100644 --- a/src/musiclistenersmanager.cpp +++ b/src/musiclistenersmanager.cpp @@ -1,508 +1,473 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "musiclistenersmanager.h" #include "config-upnp-qt.h" #include "indexersManager.h" #if defined UPNPQT_FOUND && UPNPQT_FOUND #include "upnp/upnplistener.h" #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND #include "baloo/baloolistener.h" #include "baloo/baloodetector.h" #endif #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND #include "android/androidmusiclistener.h" #endif #include "databaseinterface.h" #include "mediaplaylist.h" #include "file/filelistener.h" #include "file/localfilelisting.h" #include "trackslistener.h" -#include "notificationitem.h" #include "elisaapplication.h" #include "elisa_settings.h" #include "modeldataloader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class MusicListenersManagerPrivate { public: QThread mDatabaseThread; QThread mListenerThread; #if defined UPNPQT_FOUND && UPNPQT_FOUND UpnpListener mUpnpListener; #endif #if defined KF5Baloo_FOUND && KF5Baloo_FOUND BalooDetector mBalooDetector; BalooListener mBalooListener; #endif FileListener mFileListener; #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND std::unique_ptr mAndroidMusicListener; #endif DatabaseInterface mDatabaseInterface; std::unique_ptr mTracksListener; QFileSystemWatcher mConfigFileWatcher; ElisaApplication *mElisaApplication = nullptr; int mImportedTracksCount = 0; bool mIndexerBusy = false; bool mFileSystemIndexerActive = false; bool mBalooIndexerActive = false; bool mBalooIndexerAvailable = false; bool mAndroidIndexerActive = false; bool mAndroidIndexerAvailable = false; }; MusicListenersManager::MusicListenersManager(QObject *parent) : QObject(parent), d(std::make_unique()) { d->mListenerThread.start(); d->mDatabaseThread.start(); d->mDatabaseInterface.moveToThread(&d->mDatabaseThread); connect(&d->mDatabaseInterface, &DatabaseInterface::requestsInitDone, this, &MusicListenersManager::databaseReady); connect(this, &MusicListenersManager::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::clearData); connect(&d->mDatabaseInterface, &DatabaseInterface::cleanedDatabase, this, &MusicListenersManager::cleanedDatabase); const auto &localDataPaths = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); auto databaseFileName = QString(); if (!localDataPaths.isEmpty()) { QDir myDataDirectory; myDataDirectory.mkpath(localDataPaths.first()); databaseFileName = localDataPaths.first() + QStringLiteral("/elisaDatabase.db"); } QMetaObject::invokeMethod(&d->mDatabaseInterface, "init", Qt::QueuedConnection, Q_ARG(QString, QStringLiteral("listeners")), Q_ARG(QString, databaseFileName)); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &MusicListenersManager::applicationAboutToQuit); connect(Elisa::ElisaConfiguration::self(), &Elisa::ElisaConfiguration::configChanged, this, &MusicListenersManager::configChanged); connect(&d->mConfigFileWatcher, &QFileSystemWatcher::fileChanged, this, &MusicListenersManager::configChanged); auto initialRootPath = Elisa::ElisaConfiguration::rootPath(); if (initialRootPath.isEmpty()) { auto systemMusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation); for (const auto &musicPath : qAsConst(systemMusicPaths)) { initialRootPath.push_back(musicPath); } Elisa::ElisaConfiguration::setRootPath(initialRootPath); Elisa::ElisaConfiguration::self()->save(); } d->mConfigFileWatcher.addPath(Elisa::ElisaConfiguration::self()->config()->name()); connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, this, &MusicListenersManager::increaseImportedTracksCount); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND connect(&d->mBalooDetector, &BalooDetector::balooAvailabilityChanged, this, &MusicListenersManager::balooAvailabilityChanged); #endif qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is inactive"; qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable"; qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive"; } MusicListenersManager::~MusicListenersManager() = default; DatabaseInterface *MusicListenersManager::viewDatabase() const { return &d->mDatabaseInterface; } void MusicListenersManager::subscribeForTracks(MediaPlayList *client) { createTracksListener(); connect(d->mTracksListener.get(), &TracksListener::trackHasChanged, client, &MediaPlayList::trackChanged); connect(d->mTracksListener.get(), &TracksListener::trackHasBeenRemoved, client, &MediaPlayList::trackRemoved); connect(d->mTracksListener.get(), &TracksListener::tracksListAdded, client, &MediaPlayList::tracksListAdded); connect(client, &MediaPlayList::newEntryInList, d->mTracksListener.get(), &TracksListener::newEntryInList); connect(client, &MediaPlayList::newTrackByNameInList, d->mTracksListener.get(), &TracksListener::trackByNameInList); } int MusicListenersManager::importedTracksCount() const { return d->mImportedTracksCount; } ElisaApplication *MusicListenersManager::elisaApplication() const { return d->mElisaApplication; } bool MusicListenersManager::indexerBusy() const { return d->mIndexerBusy; } bool MusicListenersManager::fileSystemIndexerActive() const { return d->mFileSystemIndexerActive; } bool MusicListenersManager::balooIndexerActive() const { return d->mBalooIndexerActive; } bool MusicListenersManager::balooIndexerAvailable() const { return d->mBalooIndexerAvailable; } bool MusicListenersManager::androidIndexerActive() const { return d->mAndroidIndexerActive; } bool MusicListenersManager::androidIndexerAvailable() const { return d->mAndroidIndexerAvailable; } void MusicListenersManager::databaseReady() { configChanged(); } void MusicListenersManager::applicationAboutToQuit() { d->mDatabaseInterface.applicationAboutToQuit(); Q_EMIT applicationIsTerminating(); d->mDatabaseThread.exit(); d->mDatabaseThread.wait(); d->mListenerThread.exit(); d->mListenerThread.wait(); } void MusicListenersManager::showConfiguration() { auto configureAction = d->mElisaApplication->action(QStringLiteral("options_configure")); configureAction->trigger(); } void MusicListenersManager::setElisaApplication(ElisaApplication *elisaApplication) { if (d->mElisaApplication == elisaApplication) { return; } d->mElisaApplication = elisaApplication; emit elisaApplicationChanged(); } void MusicListenersManager::playBackError(const QUrl &sourceInError, QMediaPlayer::Error playerError) { qCDebug(orgKdeElisaIndexersManager) << "MusicListenersManager::playBackError" << sourceInError; if (playerError == QMediaPlayer::ResourceError) { Q_EMIT removeTracksInError({sourceInError}); if (sourceInError.isLocalFile()) { Q_EMIT displayTrackError(sourceInError.toLocalFile()); } else { Q_EMIT displayTrackError(sourceInError.toString()); } } } void MusicListenersManager::connectModel(ModelDataLoader *dataLoader) { dataLoader->moveToThread(&d->mDatabaseThread); } void MusicListenersManager::resetMusicData() { Q_EMIT clearDatabase(); } void MusicListenersManager::configChanged() { auto currentConfiguration = Elisa::ElisaConfiguration::self(); d->mConfigFileWatcher.addPath(currentConfiguration->config()->name()); currentConfiguration->load(); currentConfiguration->read(); const auto &allRootPaths = currentConfiguration->rootPath(); d->mFileListener.setAllRootPaths(allRootPaths); #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.setAllRootPaths(allRootPaths); #endif if (!d->mBalooIndexerActive && !d->mFileSystemIndexerActive) { testBalooIndexerAvailability(); } else { if (d->mBalooIndexerActive) { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection); #endif } else if (d->mFileSystemIndexerActive) { QMetaObject::invokeMethod(d->mFileListener.fileListing(), "init", Qt::QueuedConnection); } } #if defined UPNPQT_FOUND && UPNPQT_FOUND d->mUpnpListener.setDatabaseInterface(&d->mDatabaseInterface); d->mUpnpListener.moveToThread(&d->mDatabaseThread); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mUpnpListener, &UpnpListener::applicationAboutToQuit, Qt::DirectConnection); #endif #if defined Qt5AndroidExtras_FOUND && Qt5AndroidExtras_FOUND if (!d->mAndroidMusicListener) { d->mAndroidMusicListener = std::make_unique(); d->mAndroidMusicListener->moveToThread(&d->mListenerThread); d->mAndroidMusicListener->setDatabaseInterface(&d->mDatabaseInterface); connect(this, &MusicListenersManager::applicationIsTerminating, d->mAndroidMusicListener.get(), &AndroidMusicListener::applicationAboutToQuit, Qt::DirectConnection); connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::removeAllTracksFromSource); - connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::newNotification, - this, &MusicListenersManager::newNotification); - connect(d->mAndroidMusicListener.get(), &AndroidMusicListener::closeNotification, - this, &MusicListenersManager::closeNotification); } #endif } void MusicListenersManager::increaseImportedTracksCount(const DatabaseInterface::ListTrackDataType &allTracks) { d->mImportedTracksCount += allTracks.size(); - //if (d->mImportedTracksCount >= 4) { - Q_EMIT closeNotification(QStringLiteral("notEnoughTracks")); - //} - Q_EMIT importedTracksCountChanged(); } void MusicListenersManager::decreaseImportedTracksCount() { --d->mImportedTracksCount; Q_EMIT importedTracksCountChanged(); } void MusicListenersManager::monitorStartingListeners() { d->mIndexerBusy = true; Q_EMIT indexerBusyChanged(); } void MusicListenersManager::monitorEndingListeners() { - /*if (d->mImportedTracksCount < 4 && d->mElisaApplication) { - NotificationItem notEnoughTracks; - - notEnoughTracks.setNotificationId(QStringLiteral("notEnoughTracks")); - - notEnoughTracks.setTargetObject(this); - - notEnoughTracks.setMessage(i18nc("No track found message", "No track have been found")); - - auto configureAction = d->mElisaApplication->action(QStringLiteral("options_configure")); - - notEnoughTracks.setMainButtonText(configureAction->text()); - notEnoughTracks.setMainButtonIconName(configureAction->icon().name()); - notEnoughTracks.setMainButtonMethodName(QStringLiteral("showConfiguration")); - - Q_EMIT newNotification(notEnoughTracks); - }*/ - d->mIndexerBusy = false; Q_EMIT indexerBusyChanged(); } void MusicListenersManager::cleanedDatabase() { d->mImportedTracksCount = 0; Q_EMIT importedTracksCountChanged(); } void MusicListenersManager::balooAvailabilityChanged() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND if (!d->mBalooDetector.balooAvailability()) { #else if (true) { #endif if (!d->mFileSystemIndexerActive) { startLocalFileSystemIndexing(); } return; } qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is available"; d->mBalooIndexerAvailable = true; Q_EMIT balooIndexerAvailableChanged(); startBalooIndexing(); } void MusicListenersManager::testBalooIndexerAvailability() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooDetector.checkBalooAvailability(); #else qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is unavailable"; d->mBalooIndexerAvailable = false; Q_EMIT balooIndexerAvailableChanged(); qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is inactive"; d->mBalooIndexerActive = false; Q_EMIT balooIndexerActiveChanged(); startLocalFileSystemIndexing(); #endif } void MusicListenersManager::startLocalFileSystemIndexing() { if (d->mFileSystemIndexerActive) { return; } d->mFileListener.setDatabaseInterface(&d->mDatabaseInterface); d->mFileListener.moveToThread(&d->mListenerThread); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mFileListener, &FileListener::applicationAboutToQuit, Qt::DirectConnection); connect(&d->mFileListener, &FileListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(&d->mFileListener, &FileListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); - connect(&d->mFileListener, &FileListener::newNotification, - this, &MusicListenersManager::newNotification); - connect(&d->mFileListener, &FileListener::closeNotification, - this, &MusicListenersManager::closeNotification); QMetaObject::invokeMethod(d->mFileListener.fileListing(), "init", Qt::QueuedConnection); qCInfo(orgKdeElisaIndexersManager) << "Local file system indexer is active"; d->mFileSystemIndexerActive = true; Q_EMIT fileSystemIndexerActiveChanged(); } void MusicListenersManager::startBalooIndexing() { #if defined KF5Baloo_FOUND && KF5Baloo_FOUND d->mBalooListener.moveToThread(&d->mListenerThread); d->mBalooListener.setDatabaseInterface(&d->mDatabaseInterface); connect(this, &MusicListenersManager::applicationIsTerminating, &d->mBalooListener, &BalooListener::applicationAboutToQuit, Qt::DirectConnection); connect(&d->mBalooListener, &BalooListener::indexingStarted, this, &MusicListenersManager::monitorStartingListeners); connect(&d->mBalooListener, &BalooListener::indexingFinished, this, &MusicListenersManager::monitorEndingListeners); connect(&d->mBalooListener, &BalooListener::clearDatabase, &d->mDatabaseInterface, &DatabaseInterface::clearData); - connect(&d->mBalooListener, &BalooListener::newNotification, - this, &MusicListenersManager::newNotification); - connect(&d->mBalooListener, &BalooListener::closeNotification, - this, &MusicListenersManager::closeNotification); QMetaObject::invokeMethod(d->mBalooListener.fileListing(), "init", Qt::QueuedConnection); qCInfo(orgKdeElisaIndexersManager) << "Baloo indexer is active"; d->mBalooIndexerActive = true; Q_EMIT balooIndexerActiveChanged(); #endif } void MusicListenersManager::createTracksListener() { if (!d->mTracksListener) { d->mTracksListener = std::make_unique(&d->mDatabaseInterface); d->mTracksListener->moveToThread(&d->mDatabaseThread); connect(this, &MusicListenersManager::removeTracksInError, &d->mDatabaseInterface, &DatabaseInterface::removeTracksList); connect(&d->mDatabaseInterface, &DatabaseInterface::trackRemoved, d->mTracksListener.get(), &TracksListener::trackRemoved); connect(&d->mDatabaseInterface, &DatabaseInterface::tracksAdded, d->mTracksListener.get(), &TracksListener::tracksAdded); connect(&d->mDatabaseInterface, &DatabaseInterface::trackModified, d->mTracksListener.get(), &TracksListener::trackModified); } } #include "moc_musiclistenersmanager.cpp" diff --git a/src/musiclistenersmanager.h b/src/musiclistenersmanager.h index 7a8a23f4..15e350fa 100644 --- a/src/musiclistenersmanager.h +++ b/src/musiclistenersmanager.h @@ -1,183 +1,177 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef MUSICLISTENERSMANAGER_H #define MUSICLISTENERSMANAGER_H #include "elisaLib_export.h" -#include "notificationitem.h" #include "databaseinterface.h" #include #include #include class MusicListenersManagerPrivate; class MediaPlayList; -class NotificationItem; class ElisaApplication; class ModelDataLoader; class ELISALIB_EXPORT MusicListenersManager : public QObject { Q_OBJECT Q_PROPERTY(DatabaseInterface* viewDatabase READ viewDatabase NOTIFY viewDatabaseChanged) Q_PROPERTY(int importedTracksCount READ importedTracksCount NOTIFY importedTracksCountChanged) Q_PROPERTY(ElisaApplication* elisaApplication READ elisaApplication WRITE setElisaApplication NOTIFY elisaApplicationChanged) Q_PROPERTY(bool indexerBusy READ indexerBusy NOTIFY indexerBusyChanged) Q_PROPERTY(bool fileSystemIndexerActive READ fileSystemIndexerActive NOTIFY fileSystemIndexerActiveChanged) Q_PROPERTY(bool balooIndexerActive READ balooIndexerActive NOTIFY balooIndexerActiveChanged) Q_PROPERTY(bool balooIndexerAvailable READ balooIndexerAvailable NOTIFY balooIndexerAvailableChanged) Q_PROPERTY(bool androidIndexerActive READ androidIndexerActive NOTIFY androidIndexerActiveChanged) Q_PROPERTY(bool androidIndexerAvailable READ androidIndexerAvailable NOTIFY androidIndexerAvailableChanged) public: explicit MusicListenersManager(QObject *parent = nullptr); ~MusicListenersManager() override; DatabaseInterface* viewDatabase() const; void subscribeForTracks(MediaPlayList *client); int importedTracksCount() const; ElisaApplication* elisaApplication() const; bool indexerBusy() const; bool fileSystemIndexerActive() const; bool balooIndexerActive() const; bool balooIndexerAvailable() const; bool androidIndexerActive() const; bool androidIndexerAvailable() const; Q_SIGNALS: void viewDatabaseChanged(); void applicationIsTerminating(); void importedTracksCountChanged(); - void newNotification(NotificationItem notification); - - void closeNotification(QString notificationId); - void elisaApplicationChanged(); void removeTracksInError(QList tracks); void displayTrackError(const QString &fileName); void indexerBusyChanged(); void clearDatabase(); void fileSystemIndexerActiveChanged(); void balooIndexerActiveChanged(); void balooIndexerAvailableChanged(); void androidIndexerActiveChanged(); void androidIndexerAvailableChanged(); public Q_SLOTS: void databaseReady(); void applicationAboutToQuit(); void showConfiguration(); void setElisaApplication(ElisaApplication* elisaApplication); void playBackError(const QUrl &sourceInError, QMediaPlayer::Error playerError); void connectModel(ModelDataLoader *dataLoader); void resetMusicData(); private Q_SLOTS: void configChanged(); void increaseImportedTracksCount(const DatabaseInterface::ListTrackDataType &allTracks); void decreaseImportedTracksCount(); void monitorStartingListeners(); void monitorEndingListeners(); void cleanedDatabase(); void balooAvailabilityChanged(); private: void testBalooIndexerAvailability(); void startLocalFileSystemIndexing(); void startBalooIndexing(); std::unique_ptr d; void createTracksListener(); }; #endif // MUSICLISTENERSMANAGER_H diff --git a/src/notificationitem.cpp b/src/notificationitem.cpp deleted file mode 100644 index 0557c34c..00000000 --- a/src/notificationitem.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2017 Matthieu Gallien - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "notificationitem.h" - -#include - -class NotificationItemPrivate { -public: - - QString mNotificationId; - - QString mMessage; - - QString mMainButtonText; - - QString mMainButtonIconName; - - QString mSecondaryButtonText; - - QString mSecondaryButtonIconName; - - QObject *mTarget = nullptr; - - QString mMainButtonMethodName; - - QString mSecondaryButtonMethodName; - -}; - -NotificationItem::NotificationItem() : d(std::make_unique()) -{ -} - -NotificationItem::NotificationItem(const NotificationItem &other) : d(std::make_unique(*other.d)) -{ -} - -NotificationItem::NotificationItem(NotificationItem &&other) -{ - other.d.swap(d); -} - -NotificationItem& NotificationItem::operator=(const NotificationItem &other) -{ - if (&other != this) { - *d = *(other.d); - } - - return *this; -} - -NotificationItem::~NotificationItem() -= default; - -const QString& NotificationItem::notificationId() const -{ - return d->mNotificationId; -} - -const QString& NotificationItem::message() const -{ - return d->mMessage; -} - -const QString& NotificationItem::mainButtonText() const -{ - return d->mMainButtonText; -} - -const QString& NotificationItem::secondaryButtonText() const -{ - return d->mSecondaryButtonText; -} - -const QString& NotificationItem::secondaryButtonIconName() const -{ - return d->mSecondaryButtonIconName; -} - -void NotificationItem::setNotificationId(QString notificationId) -{ - if (d->mNotificationId == notificationId) { - return; - } - - d->mNotificationId = std::move(notificationId); -} - -const QString& NotificationItem::mainButtonIconName() const -{ - return d->mMainButtonIconName; -} - -void NotificationItem::setMessage(QString message) -{ - if (d->mMessage == message) { - return; - } - - d->mMessage = std::move(message); -} - -void NotificationItem::setMainButtonText(QString mainButtonText) -{ - if (d->mMainButtonText == mainButtonText) { - return; - } - - d->mMainButtonText = std::move(mainButtonText); -} - -void NotificationItem::setSecondaryButtonText(QString secondaryButtonText) -{ - if (d->mSecondaryButtonText == secondaryButtonText) { - return; - } - - d->mSecondaryButtonText = std::move(secondaryButtonText); -} - -void NotificationItem::setSecondaryButtonIconName(QString secondaryButtonIconName) -{ - if (d->mSecondaryButtonIconName == secondaryButtonIconName) { - return; - } - - d->mSecondaryButtonIconName = std::move(secondaryButtonIconName); -} - -void NotificationItem::setTargetObject(QObject *target) -{ - d->mTarget = target; -} - -void NotificationItem::setMainButtonMethodName(QString methodName) -{ - d->mMainButtonMethodName = std::move(methodName); -} - -void NotificationItem::setSecondaryButtonMethodName(QString methodName) -{ - d->mSecondaryButtonMethodName = std::move(methodName); -} - -void NotificationItem::triggerMainButton() const -{ - if (!d->mTarget) { - return; - } - - QMetaObject::invokeMethod(d->mTarget, d->mMainButtonMethodName.toLatin1().data(), Qt::QueuedConnection); -} - -void NotificationItem::triggerSecondaryButton() const -{ - if (!d->mTarget) { - return; - } - - QMetaObject::invokeMethod(d->mTarget, d->mSecondaryButtonMethodName.toLatin1().data(), Qt::QueuedConnection); -} - -void NotificationItem::setMainButtonIconName(QString mainButtonIconName) -{ - if (d->mMainButtonIconName == mainButtonIconName) { - return; - } - - d->mMainButtonIconName = std::move(mainButtonIconName); -} diff --git a/src/notificationitem.h b/src/notificationitem.h deleted file mode 100644 index 12164f87..00000000 --- a/src/notificationitem.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017 Matthieu Gallien - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#ifndef NOTIFICATIONITEM_H -#define NOTIFICATIONITEM_H - -#include "elisaLib_export.h" - -#include - -#include - -class NotificationItemPrivate; -class QObject; - -class ELISALIB_EXPORT NotificationItem -{ - -public: - - NotificationItem(); - - NotificationItem(const NotificationItem &other); - - NotificationItem(NotificationItem &&other); - - NotificationItem& operator=(const NotificationItem &other); - - NotificationItem& operator=(NotificationItem &&other); - - ~NotificationItem(); - - const QString& notificationId() const; - - const QString& message() const; - - const QString& mainButtonText() const; - - const QString& mainButtonIconName() const; - - const QString& secondaryButtonText() const; - - const QString& secondaryButtonIconName() const; - - void setNotificationId(QString notificationId); - - void setMessage(QString message); - - void setMainButtonText(QString mainButtonText); - - void setMainButtonIconName(QString mainButtonIconName); - - void setSecondaryButtonText(QString secondaryButtonText); - - void setSecondaryButtonIconName(QString secondaryButtonIconName); - - void setTargetObject(QObject *target); - - void setMainButtonMethodName(QString methodName); - - void setSecondaryButtonMethodName(QString methodName); - - void triggerMainButton() const; - - void triggerSecondaryButton() const; - -private: - - std::unique_ptr d; - -}; - -#endif // NOTIFICATIONITEM_H diff --git a/src/qml/ContentView.qml b/src/qml/ContentView.qml index b6d6effd..73f27c16 100644 --- a/src/qml/ContentView.qml +++ b/src/qml/ContentView.qml @@ -1,389 +1,379 @@ /* * Copyright 2016-2018 Matthieu Gallien * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import org.kde.elisa 1.0 import org.kde.kirigami 2.8 as Kirigami RowLayout { id: contentViewContainer spacing: 0 property bool showPlaylist property bool showExpandedFilterView property alias currentViewIndex: listViews.currentIndex function goBack() { viewManager.goBack() } function openArtist(name) { viewManager.openChildView(name, '', elisaTheme.artistIcon, 0, ElisaUtils.Artist, ViewManager.NoDiscHeaders) } function openAlbum(album, artist, image, albumID, showDiscHeader) { image = !image ? elisaTheme.defaultAlbumImage : image; viewManager.openChildView(album, artist, image, albumID, ElisaUtils.Album, showDiscHeader); } function openNowPlaying() { viewManager.closeAllViews(); } ViewManager { id: viewManager onSwitchOffAllViews: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > 1) { browseStackView.pop() } } onOpenGridView: { if (expectedDepth === 1) { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) } while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(dataGridView, { viewType: viewType, filterType: filterType, mainTitle: pageModel.viewMainTitle(viewType, mainTitle), secondaryTitle: secondaryTitle, image: pageModel.viewImageUrl(viewType, imageUrl), modelType: dataType, defaultIcon: viewDefaultIcon, showRating: viewShowRating, delegateDisplaySecondaryText: viewDelegateDisplaySecondaryText, genreFilterText: genreNameFilter, artistFilter: artistNameFilter, isSubPage: (browseStackView.depth >= 2), stackView: browseStackView, opacity: 0, }) } onOpenListView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(dataListView, { viewType: viewType, filterType: filterType, isSubPage: expectedDepth > 1, mainTitle: mainTitle, secondaryTitle: secondaryTitle, databaseId: databaseId, image: imageUrl, modelType: dataType, sortRole: sortRole, sortAscending: sortOrder, stackView: browseStackView, displaySingleAlbum: displaySingleAlbum, showSection: showDiscHeaders, opacity: 0, }) } onSwitchFilesBrowserView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(filesBrowserView, { viewType: viewType, mainTitle: mainTitle, image: imageUrl, opacity: 0, }) } onSwitchContextView: { listViews.setCurrentIndex(pageModel.indexFromViewType(viewType)) while(browseStackView.depth > expectedDepth) { browseStackView.pop() } browseStackView.push(albumContext, { viewType: viewType, mainTitle: mainTitle, image: imageUrl, opacity: 0, }) } onPopOneView: { if (browseStackView.depth > 2) { browseStackView.pop() } } } ViewsModel { id: pageModel } ViewSelector { id: listViews model: pageModel Layout.fillHeight: true Behavior on Layout.maximumWidth { NumberAnimation { duration: 150 } } onSwitchView: viewManager.openParentView(viewType, pageModel.viewMainTitle(viewType, ""), pageModel.viewImageUrl(viewType, "")) } Kirigami.Separator { id: viewSelectorSeparatorItem Layout.fillHeight: true } ColumnLayout { Layout.fillHeight: true Layout.fillWidth: true spacing: 0 - TopNotification { - id: invalidBalooConfiguration - - Layout.fillWidth: true - - musicManager: elisa.musicManager - - focus: true - } - Item { Layout.fillHeight: true Layout.fillWidth: true RowLayout { anchors.fill: parent spacing: 0 id: contentZone FocusScope { id: mainContentView focus: true Layout.fillHeight: true visible: Layout.minimumWidth != 0 MouseArea { anchors.fill: parent acceptedButtons: Qt.BackButton onClicked: goBack() } Rectangle { radius: 3 color: myPalette.base anchors.fill: parent StackView { id: browseStackView anchors.fill: parent clip: true initialItem: Item { } popEnter: Transition { OpacityAnimator { from: 0.0 to: 1.0 duration: 300 } } popExit: Transition { OpacityAnimator { from: 1.0 to: 0.0 duration: 300 } } pushEnter: Transition { OpacityAnimator { from: 0.0 to: 1.0 duration: 300 } } pushExit: Transition { OpacityAnimator { from: 1.0 to: 0.0 duration: 300 } } replaceEnter: Transition { OpacityAnimator { from: 0.0 to: 1.0 duration: 300 } } replaceExit: Transition { OpacityAnimator { from: 1.0 to: 0.0 duration: 300 } } } } } Kirigami.Separator { id: playListSeparatorItem Layout.fillHeight: true } MediaPlayListView { id: playList Layout.fillHeight: true Layout.fillWidth: true onStartPlayback: elisa.audioControl.ensurePlay() onPausePlayback: elisa.audioControl.playPause() } } } states: [ State { name: "browsingViewsNoPlaylist" when: contentViewContainer.showPlaylist === false extend: "browsingViews" PropertyChanges { target: mainContentView Layout.minimumWidth: contentZone.width Layout.maximumWidth: contentZone.width Layout.preferredWidth: contentZone.width } PropertyChanges { target: playList Layout.minimumWidth: 0 Layout.maximumWidth: 0 Layout.preferredWidth: 0 } PropertyChanges { target: playListSeparatorItem visible: false } }, State { name: 'browsingViews' when: contentViewContainer.showPlaylist === true PropertyChanges { target: mainContentView Layout.minimumWidth: contentZone.width * 0.66 Layout.maximumWidth: contentZone.width * 0.68 Layout.preferredWidth: contentZone.width * 0.68 } PropertyChanges { target: playListSeparatorItem visible: true } } ] transitions: Transition { NumberAnimation { properties: "Layout.minimumWidth, Layout.maximumWidth, Layout.preferredWidth, opacity" easing.type: Easing.InOutQuad duration: 300 } } } Component { id: dataGridView DataGridView { StackView.onActivated: viewManager.viewIsLoaded(viewType) expandedFilterView: showExpandedFilterView } } Component { id: dataListView DataListView { StackView.onActivated: viewManager.viewIsLoaded(viewType) expandedFilterView: showExpandedFilterView } } Component { id: filesBrowserView FileBrowserView { StackView.onActivated: viewManager.viewIsLoaded(viewType) expandedFilterView: showExpandedFilterView } } Component { id: albumContext ContextView { StackView.onActivated: viewManager.viewIsLoaded(viewType) databaseId: elisa.manageHeaderBar.databaseId title: elisa.manageHeaderBar.title artistName: elisa.manageHeaderBar.artist albumName: elisa.manageHeaderBar.album albumArtUrl: elisa.manageHeaderBar.image fileUrl: elisa.manageHeaderBar.fileName } } } diff --git a/src/qml/TopNotification.qml b/src/qml/TopNotification.qml deleted file mode 100644 index 403291aa..00000000 --- a/src/qml/TopNotification.qml +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright 2017 Matthieu Gallien - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -import QtQuick 2.7 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.2 -import QtQml.Models 2.2 -import org.kde.elisa 1.0 - -FocusScope { - id: topItem - - property var musicManager - property bool isViewExpanded: false - - property int rowHeight: elisaTheme.delegateHeight * 2 - - visible: Layout.preferredHeight > 0 - - Rectangle { - anchors.fill: parent - - color: myPalette.mid - } - - Component { - id: highlightBar - - Rectangle { - width: notificationColumn.width - height: rowHeight - color: (topItem.state === 'expanded' ? myPalette.highlight : myPalette.mid) - - Behavior on color { - ColorAnimation { - duration: 300 - } - } - } - } - - Rectangle { - id: notificationCounter - - anchors { - top: parent.top - left: parent.left - margins: elisaTheme.layoutVerticalMargin - } - - width: elisaTheme.delegateToolButtonSize - height: elisaTheme.delegateToolButtonSize - - radius: width / 2 - - color: myPalette.window - - opacity: 0.0 - visible: opacity > 0 - - z: 3 - - ToolButton { - anchors.centerIn: parent - text: manager.countNotifications - - onClicked: topItem.isViewExpanded = !topItem.isViewExpanded - Accessible.onPressAction: onClicked - } - } - - ScrollView { - id: expandedView - - ScrollBar.vertical.policy: ScrollBar.AlwaysOff - - anchors { - top: parent.top - right: parent.right - left: notificationCounter.right - bottom: parent.bottom - } - - opacity: 0 - visible: opacity > 0 - - focus: true - - z: 2 - - ListView { - id: notificationColumn - - focus: true - - populate: Transition { - NumberAnimation { properties: "opacity"; from: 0; to: 1.0; duration: 300 } - } - - add: Transition { - NumberAnimation { properties: "opacity"; from: 0; to: 1.0; duration: 300 } - } - - displaced: Transition { - NumberAnimation { properties: "x,y"; duration: 300 } - } - - highlight: highlightBar - - model: DelegateModel { - model: manager - - delegate: TopNotificationItem { - id: currentDelegate - - focus: true - - width: ListView.view.width - height: rowHeight - - onEntered: notificationColumn.currentIndex = index - - onClose: manager.closeNotification(index) - - onMainButtonClicked: manager.triggerMainButton(index) - - onSecondaryButtonClicked: manager.triggerSecondaryButton(index) - - itemMessage: message - itemMainButtonText: mainButtonText - itemMainButtonIconName: mainButtonIconName - itemSecondaryButtonText: secondaryButtonText - itemSecondaryButtonIconName: secondaryButtonIconName - - ListView.onRemove: SequentialAnimation { - PropertyAction { target: currentDelegate; property: "ListView.delayRemove"; value: true } - ParallelAnimation { - NumberAnimation { target: currentDelegate; properties: "height"; from: elisaTheme.delegateHeight * 2; to: 0; duration: 300 } - NumberAnimation { target: currentDelegate; properties: "opacity"; from: 1.0; to: 0; duration: 300 } - } - PropertyAction { target: currentDelegate; property: "ListView.delayRemove"; value: false } - } - } - } - - function gotoBeginning() { - anim.from = notificationColumn.contentY; - anim.to = 0; - anim.running = true; - } - - NumberAnimation { id: anim; target: notificationColumn; property: "contentY"; duration: 300 } - } - } - - TopNotificationManager { - id: manager - } - - Connections { - target: elisa.musicManager - - onNewNotification: - manager.addNotification(notification) - - onCloseNotification: - manager.closeNotificationById(notificationId) - } - - states: [ - State { - name: "empty" - when: manager.countNotifications === 0 - - PropertyChanges { - target: topItem - Layout.preferredHeight: 0 - } - PropertyChanges { - target: notificationCounter - opacity: 0.0 - } - PropertyChanges { - target: notificationCounter - width: 0 - } - PropertyChanges { - target: notificationColumn - interactive: false - } - PropertyChanges { - target: expandedView - opacity: 0.0 - ScrollBar.vertical.policy: ScrollBar.AlwaysOff - } - StateChangeScript { - script: notificationColumn.gotoBeginning() - } - }, - State { - name: "oneNotification" - when: manager.countNotifications === 1 - - PropertyChanges { - target: topItem - Layout.preferredHeight: elisaTheme.delegateHeight * 2 - } - PropertyChanges { - target: notificationCounter - opacity: 0.0 - } - PropertyChanges { - target: notificationCounter - width: 0 - } - PropertyChanges { - target: notificationColumn - interactive: false - } - PropertyChanges { - target: expandedView - opacity: 1.0 - ScrollBar.vertical.policy: ScrollBar.AlwaysOff - } - StateChangeScript { - script: notificationColumn.gotoBeginning() - } - }, - State { - name: "multipleNotifications" - when: manager.countNotifications > 1 && !isViewExpanded - - PropertyChanges { - target: topItem - Layout.preferredHeight: elisaTheme.delegateHeight * 2 - } - PropertyChanges { - target: notificationCounter - opacity: 1.0 - } - PropertyChanges { - target: notificationCounter - width: elisaTheme.delegateToolButtonSize - } - PropertyChanges { - target: notificationColumn - interactive: false - } - PropertyChanges { - target: expandedView - opacity: 1.0 - ScrollBar.vertical.policy: ScrollBar.AsNeeded - } - StateChangeScript { - script: notificationColumn.gotoBeginning() - } - }, - State { - name: "expanded" - when: manager.countNotifications > 1 && isViewExpanded - - PropertyChanges { - target: topItem - Layout.preferredHeight: elisaTheme.delegateHeight * 4 - } - PropertyChanges { - target: notificationCounter - opacity: 1.0 - } - PropertyChanges { - target: notificationCounter - width: elisaTheme.delegateToolButtonSize - } - PropertyChanges { - target: notificationColumn - interactive: true - } - PropertyChanges { - target: expandedView - opacity: 1.0 - ScrollBar.vertical.policy: ScrollBar.AsNeeded - } - } - ] - - transitions: [ - Transition { - SequentialAnimation { - PropertyAction { - target: expandedView - property: "ScrollBar.vertical.policy" - value: ScrollBar.AlwaysOff - } - ParallelAnimation { - NumberAnimation { - target: topItem - - duration: 300 - - property: "Layout.preferredHeight" - } - NumberAnimation { - target: notificationCounter - - duration: 300 - - property: "opacity" - } - NumberAnimation { - target: notificationCounter - - duration: 300 - - property: "width" - } - NumberAnimation { - target: expandedView - - duration: 300 - - property: "opacity" - } - } - } - } - ] -} diff --git a/src/qml/TopNotificationItem.qml b/src/qml/TopNotificationItem.qml deleted file mode 100644 index c2a1ddd5..00000000 --- a/src/qml/TopNotificationItem.qml +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2017 Matthieu Gallien - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -import QtQuick 2.7 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 2.3 -import org.kde.elisa 1.0 - -FocusScope { - id: topItem - - signal close() - signal mainButtonClicked() - signal secondaryButtonClicked() - signal entered() - - property alias itemMessage: notificationText.text - property alias itemMainButtonText: mainButton.text - property alias itemMainButtonIconName: mainButton.icon.name - property alias itemSecondaryButtonText: secondaryButton.text - property alias itemSecondaryButtonIconName: secondaryButton.icon.name - property var parentList - - MouseArea { - anchors.fill: parent - - hoverEnabled: true - acceptedButtons: Qt.NoButton - - onEntered: topItem.entered() - } - - RowLayout { - id: content - anchors.fill: parent - - Label { - id: notificationText - font.pointSize: Math.round(elisaTheme.defaultFontPoinPoint * 1.5) - - Layout.leftMargin: elisaTheme.layoutHorizontalMargin - Layout.alignment: Qt.AlignHCenter - - visible: topItem.height > height - opacity: (topItem.height - height) / height - } - - Button { - id: mainButton - - Layout.leftMargin: elisaTheme.layoutHorizontalMargin - Layout.alignment: Qt.AlignHCenter - Layout.maximumHeight: elisaTheme.delegateHeight - - visible: text !== "" && topItem.height > height - opacity: (topItem.height - height) / height - - onClicked: { - mainButton.enabled = false - enableAgainMainButtonTimer.start() - mainButtonClicked() - } - Accessible.onPressAction: onClicked - - Timer { - id: enableAgainMainButtonTimer - interval: 500 - onTriggered: mainButton.enabled = true - } - } - - Button { - id: secondaryButton - - Layout.leftMargin: elisaTheme.layoutHorizontalMargin - Layout.alignment: Qt.AlignHCenter - Layout.maximumHeight: elisaTheme.delegateHeight - - visible: text !== "" && topItem.height > height - opacity: (topItem.height - height) / height - - onClicked: { - secondaryButton.enabled = false - enableAgainSecondaryButtonTimer.start() - secondaryButtonClicked() - } - Accessible.onPressAction: onClicked - - Timer { - id: enableAgainSecondaryButtonTimer - interval: 500 - onTriggered: secondaryButton.enabled = true - } - } - - Item { - Layout.fillWidth: true - } - } - - FlatButtonWithToolTip { - anchors.top: parent.top - anchors.right: parent.right - icon.name: 'dialog-close' - - visible: topItem.height > height - opacity: (topItem.height - height) / height - - onClicked: close() - Accessible.onPressAction: onClicked - } -} diff --git a/src/resources.qrc b/src/resources.qrc index d8df04cd..73cbea68 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,57 +1,55 @@ qml/MediaPlayerControl.qml qml/RatingStar.qml qml/MediaPlayListView.qml qml/ElisaMainWindow.qml qml/ApplicationMenu.qml qml/HeaderBar.qml qml/ContextView.qml qml/ContentView.qml qml/DraggableItem.qml qml/NavigationActionBar.qml qml/PlayListEntry.qml qml/Theme.qml qml/PlatformIntegration.qml qml/LabelWithToolTip.qml - qml/TopNotification.qml qml/TrackImportNotification.qml - qml/TopNotificationItem.qml qml/MediaTrackMetadataView.qml qml/GridBrowserView.qml qml/GridBrowserDelegate.qml qml/ListBrowserView.qml qml/ListBrowserDelegate.qml qml/FileBrowserView.qml qtquickcontrols2.conf background.png qml/BaseTheme.qml qml/ScrollHelper.qml qml/FlatButtonWithToolTip.qml qml/DataGridView.qml qml/DataListView.qml qml/PlayListBasicView.qml qml/SimplePlayListView.qml qml/ViewSelector.qml qml/PlayListAlbumHeader.qml qml/BasicPlayListAlbumHeader.qml qml/MetaDataDelegate.qml qml/ViewSelectorDelegate.qml qml/HeaderFooterToolbar.qml qml/TracksDiscHeader.qml windows/WindowsTheme.qml windows/PlatformIntegration.qml windows/LabelWithToolTip.qml android/ElisaMainWindow.qml android/AndroidTheme.qml android/PlatformIntegration.qml android/AlbumsView.qml android/ArtistsView.qml android/TracksView.qml android/GenresView.qml diff --git a/src/topnotificationmanager.cpp b/src/topnotificationmanager.cpp deleted file mode 100644 index 20c4e5f2..00000000 --- a/src/topnotificationmanager.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2017 Matthieu Gallien - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "topnotificationmanager.h" -#include "notificationitem.h" - -class TopNotificationManagerPrivate -{ -public: - - QList mNotifications; - -}; - -TopNotificationManager::TopNotificationManager(QObject *parent) : QAbstractListModel(parent), d(std::make_unique()) -{ -} - -TopNotificationManager::~TopNotificationManager() -=default; - -int TopNotificationManager::countNotifications() const -{ - return d->mNotifications.count(); -} - -QModelIndex TopNotificationManager::parent(const QModelIndex &index) const -{ - Q_UNUSED(index); - - return {}; -} - -void TopNotificationManager::addNotification(NotificationItem notification) -{ - beginInsertRows({}, d->mNotifications.count(), d->mNotifications.count()); - d->mNotifications.push_back(notification); - endInsertRows(); - - Q_EMIT countNotificationsChanged(); - Q_EMIT notificationMessageChanged(); - Q_EMIT notificationMainButtonTextChanged(); - Q_EMIT notificationMainButtonIconNameChanged(); - Q_EMIT notificationSecondaryButtonTextChanged(); - Q_EMIT notificationSecondaryButtonIconNameChanged(); -} - -void TopNotificationManager::closeNotification(int index) -{ - if (index < 0 || index >= d->mNotifications.count()) { - return; - } - - beginRemoveRows({}, index, index); - d->mNotifications.removeAt(index); - endRemoveRows(); - - Q_EMIT countNotificationsChanged(); - Q_EMIT notificationMessageChanged(); - Q_EMIT notificationMainButtonTextChanged(); - Q_EMIT notificationMainButtonIconNameChanged(); - Q_EMIT notificationSecondaryButtonTextChanged(); - Q_EMIT notificationSecondaryButtonIconNameChanged(); -} - -void TopNotificationManager::closeNotificationById(const QString ¬ificationId) -{ - for (int i = 0; i < d->mNotifications.size(); ) { - if (d->mNotifications.at(i).notificationId() == notificationId) { - closeNotification(i); - } else { - ++i; - } - } -} - -void TopNotificationManager::triggerMainButton(int index) const -{ - if (index < 0 || index >= d->mNotifications.count()) { - return; - } - - d->mNotifications.at(index).triggerMainButton(); -} - -void TopNotificationManager::triggerSecondaryButton(int index) const -{ - if (index < 0 || index >= d->mNotifications.count()) { - return; - } - - d->mNotifications.at(index).triggerSecondaryButton(); -} - -int TopNotificationManager::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - return 0; - } - - return d->mNotifications.count(); -} - -QVariant TopNotificationManager::data(const QModelIndex &index, int role) const -{ - auto result = QVariant(); - - if (!index.isValid()) { - return result; - } - - if (index.column() != 0) { - return result; - } - - if (index.row() < 0 || index.row() >= d->mNotifications.count()) { - return result; - } - - if (role < ColumnsRoles::MessageRole || role > ColumnsRoles::SecondaryButtonIconNameRole) { - return result; - } - - auto realRole = static_cast(role); - - switch(realRole) - { - case ColumnsRoles::MessageRole: - result = d->mNotifications.at(index.row()).message(); - break; - case ColumnsRoles::MainButtonTextRole: - result = d->mNotifications.at(index.row()).mainButtonText(); - break; - case ColumnsRoles::MainButtonIconNameRole: - result = d->mNotifications.at(index.row()).mainButtonIconName(); - break; - case ColumnsRoles::SecondaryButtonTextRole: - result = d->mNotifications.at(index.row()).secondaryButtonText(); - break; - case ColumnsRoles::SecondaryButtonIconNameRole: - result = d->mNotifications.at(index.row()).secondaryButtonIconName(); - break; - } - - return result; -} - -QHash TopNotificationManager::roleNames() const -{ - QHash roles; - - roles[static_cast(ColumnsRoles::MessageRole)] = "message"; - roles[static_cast(ColumnsRoles::MainButtonTextRole)] = "mainButtonText"; - roles[static_cast(ColumnsRoles::MainButtonIconNameRole)] = "mainButtonIconName"; - roles[static_cast(ColumnsRoles::SecondaryButtonTextRole)] = "secondaryButtonText"; - roles[static_cast(ColumnsRoles::SecondaryButtonIconNameRole)] = "secondaryButtonIconName"; - - return roles; -} - -QString TopNotificationManager::notificationMessage() const -{ - return data(index(0, 0, {}), TopNotificationManager::MessageRole).toString(); -} - -QString TopNotificationManager::notificationMainButtonText() const -{ - return data(index(0, 0, {}), TopNotificationManager::MainButtonTextRole).toString(); -} - -QString TopNotificationManager::notificationMainButtonIconName() const -{ - return data(index(0, 0, {}), TopNotificationManager::MainButtonIconNameRole).toString(); -} - -QString TopNotificationManager::notificationSecondaryButtonText() const -{ - return data(index(0, 0, {}), TopNotificationManager::SecondaryButtonTextRole).toString(); -} - -QString TopNotificationManager::notificationSecondaryButtonIconName() const -{ - return data(index(0, 0, {}), TopNotificationManager::SecondaryButtonIconNameRole).toString(); -} - - -//#include "moc_topnotificationmanager.cpp" diff --git a/src/topnotificationmanager.h b/src/topnotificationmanager.h deleted file mode 100644 index 325efb94..00000000 --- a/src/topnotificationmanager.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2017 Matthieu Gallien - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#ifndef TOPNOTIFICATIONMANAGER_H -#define TOPNOTIFICATIONMANAGER_H - -#include "elisaLib_export.h" - -#include "notificationitem.h" - -#include -#include - -#include - -class TopNotificationManagerPrivate; - -class ELISALIB_EXPORT TopNotificationManager : public QAbstractListModel -{ - - Q_OBJECT - - Q_PROPERTY(int countNotifications - READ countNotifications - NOTIFY countNotificationsChanged) - - Q_PROPERTY(QString notificationMessage - READ notificationMessage - NOTIFY notificationMessageChanged) - - Q_PROPERTY(QString notificationMainButtonText - READ notificationMainButtonText - NOTIFY notificationMainButtonTextChanged) - - Q_PROPERTY(QString notificationMainButtonIconName - READ notificationMainButtonIconName - NOTIFY notificationMainButtonIconNameChanged) - - Q_PROPERTY(QString notificationSecondaryButtonText - READ notificationSecondaryButtonText - NOTIFY notificationSecondaryButtonTextChanged) - - Q_PROPERTY(QString notificationSecondaryButtonIconName - READ notificationSecondaryButtonIconName - NOTIFY notificationSecondaryButtonIconNameChanged) - -public: - - enum ColumnsRoles { - MessageRole = Qt::UserRole + 1, - MainButtonTextRole, - MainButtonIconNameRole, - SecondaryButtonTextRole, - SecondaryButtonIconNameRole, - }; - - Q_ENUM(ColumnsRoles) - - explicit TopNotificationManager(QObject *parent = nullptr); - - ~TopNotificationManager(); - - int countNotifications() const; - - QModelIndex parent(const QModelIndex &index) const override; - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - QHash roleNames() const override; - - QString notificationMessage() const; - - QString notificationMainButtonText() const; - - QString notificationMainButtonIconName() const; - - QString notificationSecondaryButtonText() const; - - QString notificationSecondaryButtonIconName() const; - -Q_SIGNALS: - - void countNotificationsChanged(); - - void notificationMessageChanged(); - - void notificationMainButtonTextChanged(); - - void notificationMainButtonIconNameChanged(); - - void notificationSecondaryButtonTextChanged(); - - void notificationSecondaryButtonIconNameChanged(); - -public Q_SLOTS: - - void addNotification(NotificationItem notification); - - void closeNotification(int index); - - void closeNotificationById(const QString ¬ificationId); - - void triggerMainButton(int index) const; - - void triggerSecondaryButton(int index) const; - -private: - - std::unique_ptr d; - -}; - - - -#endif // TOPNOTIFICATIONMANAGER_H