diff --git a/advancedsearchdialog.h b/advancedsearchdialog.h index 3ff4ff0e..86cf40bc 100644 --- a/advancedsearchdialog.h +++ b/advancedsearchdialog.h @@ -1,73 +1,73 @@ /** * Copyright (C) 2003-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ADVANCEDSEARCHDIALOG_H #define ADVANCEDSEARCHDIALOG_H #include #include #include "playlistsearch.h" class QLineEdit; class QPushButton; class QRadioButton; class SearchLine; class QBoxLayout; class AdvancedSearchDialog : public QDialog { Q_OBJECT public: - AdvancedSearchDialog( + explicit AdvancedSearchDialog( const QString& defaultName, const PlaylistSearch& defaultSearch = PlaylistSearch(), QWidget* parent = nullptr); PlaylistSearch resultSearch() const { return m_search; } QString resultPlaylistName() const { return m_playlistName; } protected slots: void accept() Q_DECL_OVERRIDE; void clearSearches(); void more(); void fewer(); private: void updateButtons(); QBoxLayout *m_criteriaLayout; PlaylistSearch m_search; QString m_playlistName; QList m_searchLines; QLineEdit *m_playlistNameLineEdit; QRadioButton *m_matchAnyButton; QRadioButton *m_matchAllButton; QPushButton *m_moreButton; QPushButton *m_fewerButton; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/cache.h b/cache.h index d3e35c21..afc26acc 100644 --- a/cache.h +++ b/cache.h @@ -1,96 +1,96 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * Copyright (C) 2008, 2013 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_CACHE_H #define JUK_CACHE_H -#include -#include -#include +#include +#include +#include class Playlist; class PlaylistCollection; class FileHandle; template class QList; typedef QList PlaylistList; /** * A simple QDataStream subclass that has an extra field to indicate the cache * version. */ class CacheDataStream : public QDataStream { public: CacheDataStream(QIODevice *d) : QDataStream(d), m_cacheVersion(0) {} CacheDataStream() : m_cacheVersion(0) { } int cacheVersion() const { return m_cacheVersion; } void setCacheVersion(int v) { m_cacheVersion = v; } private: int m_cacheVersion; }; class Cache { public: static Cache *instance(); static void loadPlaylists(PlaylistCollection *collection); static void savePlaylists(const PlaylistList &playlists); static void ensureAppDataStorageExists(); static bool cacheFileExists(); static QString fileHandleCacheFileName(); static QString playlistsCacheFileName(); bool prepareToLoadCachedItems(); FileHandle loadNextCachedItem(); /** * QDataStream version for serialized list of playlists * 1, 2: Who knows? * 3: Current. */ static const int playlistListCacheVersion; /** * QDataStream version for serialized list of playlist items in a playlist * 1: Original cache version * 2: KDE 4.0.1+, explicitly sets QDataStream encoding. */ static const int playlistItemsCacheVersion; private: // private to force access through instance() Cache(); private: QFile m_loadFile; QBuffer m_loadFileBuffer; CacheDataStream m_loadDataStream; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/collectionlist.h b/collectionlist.h index d1bccf2b..3c3e9531 100644 --- a/collectionlist.h +++ b/collectionlist.h @@ -1,212 +1,212 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_COLLECTIONLIST_H #define JUK_COLLECTIONLIST_H #include #include #include "playlist.h" #include "playlistitem.h" class ViewMode; class KFileItem; class KFileItemList; class KDirWatch; /** * This type is for mapping QString track attributes like the album, artist * and track to an integer count representing the number of outstanding items * that hold the string. */ typedef QHash TagCountDict; typedef QHashIterator TagCountDictIterator; /** * We then have an array of dicts, one for each column in the list view. * The array is sparse (not every vector will have a TagCountDict so we use * pointers. */ typedef QVector TagCountDicts; /** * This is the "collection", or all of the music files that have been opened * in any playlist and not explicitly removed from the collection. * * It is being implemented as a "semi-singleton" because I need universal access * to just one instance. However, because the collection needs initialization * parameters (that will not always be available when an instance is needed). - * Hence there will be the familiar singleton "instance()" method allong with an + * Hence there will be the familiar singleton "instance()" method along with an * "initialize()" method. */ class CollectionListItem : public PlaylistItem { friend class Playlist; friend class CollectionList; friend class PlaylistItem; public: virtual void refresh(); PlaylistItem *itemForPlaylist(const Playlist *playlist); void updateCollectionDict(const QString &oldPath, const QString &newPath); void repaint() const; PlaylistItemList children() const { return m_children; } protected: CollectionListItem(CollectionList *parent, const FileHandle &file); virtual ~CollectionListItem(); void addChildItem(PlaylistItem *child); void removeChildItem(PlaylistItem *child); /** * Returns true if the item is now up to date (even if this required a refresh) or * false if the item is invalid. */ bool checkCurrent(); virtual CollectionListItem *collectionItem() { return this; } private: bool m_shuttingDown; PlaylistItemList m_children; }; class CollectionList : public Playlist { friend class CollectionListItem; Q_OBJECT public: /** * A variety of unique value lists will be kept in the collection. This * enum can be used as an index into those structures. */ enum UniqueSetType { Artists = 0, Albums = 1, Genres = 2 }; static CollectionList *instance(); static void initialize(PlaylistCollection *collection); /** * Returns a unique set of values associated with the type specified. */ QStringList uniqueSet(UniqueSetType t) const; CollectionListItem *lookup(const QString &file) const; virtual CollectionListItem *createItem(const FileHandle &file, QTreeWidgetItem * = nullptr); virtual void clearItems(const PlaylistItemList &items); void setupTreeViewEntries(ViewMode *viewMode) const; virtual bool canReload() const { return true; } void saveItemsToCache() const; public slots: virtual void paste(); virtual void clear(); void slotCheckCache(); void slotRemoveItem(const QString &file); void slotRefreshItem(const QString &file); void slotNewItems(const KFileItemList &items); void slotRefreshItems(const QList > &items); void slotDeleteItems(const KFileItemList &items); protected: CollectionList(PlaylistCollection *collection); virtual ~CollectionList(); virtual void dropEvent(QDropEvent *e); virtual void dragMoveEvent(QDragMoveEvent *e); // These methods are used by CollectionListItem, which is a friend class. void addToDict(const QString &file, CollectionListItem *item) { m_itemsDict.insert(file, item); } void removeFromDict(const QString &file) { m_itemsDict.remove(file); } // These methods are also used by CollectionListItem, to manage the // strings used in generating the unique sets and tree view mode playlists. QString addStringToDict(const QString &value, int column); void removeStringFromDict(const QString &value, int column); void addWatched(const QString &file); void removeWatched(const QString &file); virtual bool hasItem(const QString &file) const { return m_itemsDict.contains(file); } signals: void signalCollectionChanged(); /** * This is emitted when the set of columns that is visible is changed. * * \see Playlist::hideColumn() * \see Playlist::showColumn() * \see Playlist::isColumnVisible() */ void signalNewTag(const QString &, unsigned); void signalRemovedTag(const QString &, unsigned); // Emitted once cached items are loaded, which allows for folder scanning // and invalid track detection to proceed. void cachedItemsLoaded(); public slots: /** * Loads the CollectionListItems from the Cache. Should be called after program * initialization. */ void startLoadingCachedItems(); /** * Loads a few items at a time. Intended to be single-shotted into the event * loop so that loading the music doesn't freeze the GUI. */ void loadNextBatchCachedItems(); /** * Teardown from cache loading (e.g. a sort operation). Should * always be called if startLoadingCachedItems is called. */ void completedLoadingCachedItems(); private: /** * Just the size of the above enum to keep from hard coding it in several * locations. */ static const int m_uniqueSetCount = 3; static CollectionList *m_list; QHash m_itemsDict; KDirWatch *m_dirWatch; TagCountDicts m_columnTags; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/coverdialog.h b/coverdialog.h index 1d493665..2daccbb2 100644 --- a/coverdialog.h +++ b/coverdialog.h @@ -1,48 +1,48 @@ /** * Copyright (C) 2005 Michael Pyne * Copyright (C) 2014 Arnold Dumas * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_COVERDIALOG_H #define JUK_COVERDIALOG_H #include "ui_coverdialogbase.h" #include class QListWidgetItem; class CoverDialog : public QWidget, public Ui::CoverDialogBase { Q_OBJECT public: - CoverDialog(QWidget *parent); + explicit CoverDialog(QWidget *parent); ~CoverDialog(); virtual void show(); public slots: void slotArtistClicked(QListWidgetItem *item); void slotContextRequested(const QPoint &pt); void slotSearchPatternChanged(const QString& pattern); private slots: void loadCovers(); void removeSelectedCover(); }; #endif /* JUK_COVERDIALOG_H */ // vim: set et sw=4 tw=0 sta: diff --git a/coverinfo.h b/coverinfo.h index 91fce54e..bf3b2e8e 100644 --- a/coverinfo.h +++ b/coverinfo.h @@ -1,99 +1,99 @@ /** * Copyright (C) 2004 Nathan Toone * Copyright (C) 2008 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef COVERINFO_H #define COVERINFO_H #include "filehandle.h" #include "covermanager.h" #include class QPixmap; class CoverInfo { friend class FileHandle; public: enum CoverSize { FullSize, Thumbnail }; - CoverInfo(const FileHandle &file); + explicit CoverInfo(const FileHandle &file); bool hasCover() const; void clearCover(); void setCover(const QImage &image = QImage()); // Use this to assign to a specific cover id. void setCoverId(coverKey id); /** * This function sets the cover identifier for all tracks that have the * same Artist and Album as this track, to the cover identifier of this * track. * * @param overwriteExistingCovers If set to true, this function will always * apply the new cover to a track even if the track already had * a different cover set. */ void applyCoverToWholeAlbum(bool overwriteExistingCovers = false) const; coverKey coverId() const; QPixmap pixmap(CoverSize size) const; /** * Returns the path to the cover data. For embedded covers the art will be * extracted to a temporary file, and the returned path will be that of the * temporary file. The temporary file will be owned by the caller. * * Note that it is possible to have a valid filename even for covers that * do not have "coverKey" since JuK supports using cover.{jpg,png} in a * directory. * * @param fallbackFileName The filename (including full absolute path) * of the file to write to if embedded album art is present and to be * extracted. * * If no cover is present, an empty string is returned. */ QString localPathToCover(const QString &fallbackFileName) const; void popup() const; private: QImage scaleCoverToThumbnail(const QImage &image) const; // Not supported for all file types as we must build on top of TagLib // support. QImage embeddedAlbumArt() const; bool hasEmbeddedAlbumArt() const; FileHandle m_file; // Mutable to allow this info to be cached. mutable bool m_hasCover; mutable bool m_hasAttachedCover; mutable bool m_haveCheckedForCover; mutable coverKey m_coverKey; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/covermanager.h b/covermanager.h index 281ece86..cedecff9 100644 --- a/covermanager.h +++ b/covermanager.h @@ -1,270 +1,270 @@ /** * Copyright (C) 2005, 2008 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_COVERMANAGER_H #define JUK_COVERMANAGER_H #include #include #include #include class CoverManagerPrivate; class CoverProxy; class QPixmap; class QTimer; class QUrl; class KJob; template class QList; /** * This class saves the covers when its saveCovers() slot is called to avoid * making CoverManager a QObject and avoid moving the actual implementation * class (CoverManagerPrivate) to this .h file. Used with a QTimer to save * the covers after changes are made. */ class CoverSaveHelper : public QObject { Q_OBJECT public: - CoverSaveHelper(QObject *parent); + explicit CoverSaveHelper(QObject *parent); void saveCovers(); private slots: void commitChanges(); private: QTimer *m_timer; }; /** * This class holds the data on a cover. * Don't assume that the artist or album information is filled out, it is * there to allow the CoverManager to try to automatically assign covers to * new tracks. */ class CoverData { public: QPixmap pixmap() const; QPixmap thumbnail() const; QString artist; QString album; QString path; unsigned refCount; // Refers to number of tracks using this. }; typedef unsigned long coverKey; ///< Type of the id for a cover. using CoverDataMap = std::map; using CoverDataMapIterator = typename CoverDataMap::const_iterator; typedef QList CoverList; /** * This class is used to drag covers in JuK. It adds a special mimetype that * contains the cover ID used for this cover, and also supports an image/png * mimetype for dragging to other applications. * * The mimetype is "application/x-juk-coverid" * * @author Michael Pyne */ class CoverDrag : public QMimeData { Q_OBJECT public: - CoverDrag(coverKey id); + explicit CoverDrag(coverKey id); static const char* mimetype(); static bool isCover(const QMimeData *data); // CoverDrag stores QByteArray data for the cover id, this can convert it // back. static coverKey idFromData(const QMimeData *data); }; /** * This class holds all of the cover art, and manages looking it up by artist * and/or album. This class is similar to a singleton class, but instead all * of the methods are static. This way you can invoke methods like this: * \code * CoverManager::method() * \endcode * instead of using: * \code * CoverManager::instance()->method() * \endcode * * @author Michael Pyne */ class CoverManager { public: /// The set of different sizes you can request a pixmap as. typedef enum { Thumbnail, FullSize } Size; /** * Tries to match @p artist and @p album to a cover in the database. * * @param artist The artist to look for matching covers on. * @param album The album to look for matching covers on. * @return NoMatch if no match could be found, otherwise the id of the * cover art that matches the given metadata. */ static coverKey idFromMetadata(const QString &artist, const QString &album); /** * Returns the cover art for @p id. * * @param id The id of the cover. * @param size The size to return it as. Note that FullSize doesn't * necessarily mean the pixmap is large, so you may need to * scale it up. * @return QPixmap::null if there is no cover art for @p id, otherwise the * cover art. */ static QPixmap coverFromId(coverKey id, Size size = Thumbnail); /** * Returns the cover art for @p ptr. This function is intended for use * by CoverData. * * @param ptr The CoverData to get the cover of. * @param size The size to return it as. * @see CoverData */ static QPixmap coverFromData(const CoverData &coverData, Size size = Thumbnail); /** * Returns the full suite of information known about the cover given by * @p id. * * @param id the id of the cover to retrieve info on. * @return 0 if there is no info on @p id, otherwise its information. */ static CoverData coverInfo(coverKey id); /** * Adds @p large to the cover database, associating with it @p artist and * @p album. * * @param large The full size cover (the thumbnail is automatically * generated). * @param artist The artist of the new cover. * @param album The album of the new cover. */ static coverKey addCover(const QPixmap &large, const QString &artist = "", const QString &album = ""); /** * Adds the file pointed to by the local path @p path to the database, * associating it with @p artist and @p album. * * @param path The absolute path to the fullsize cover art. * @param artist The artist of the new cover. * @param album The album of the new cover. */ static coverKey addCover(const QUrl &path, const QString &artist = "", const QString &album = ""); /** * Function to determine if @p id matches any covers in the database. * * @param id The id of the cover to search for. * @return true if the database has a cover identified by @p id, false * otherwise. */ static bool hasCover(coverKey id); /** * Removes the cover identified by @p id. * * @param id the id of the cover to remove. * @return true if the removal was successful, false if unsuccessful or if * the cover didn't exist. */ static bool removeCover(coverKey id); /** * Replaces the cover art for the cover identified by @p id with @p large. * Any other metadata such as artist and album is unchanged. * * @param id The id of the cover to replace. * @param large The full size cover art for the new cover. */ static bool replaceCover(coverKey id, const QPixmap &large); /** * Saves the current CoverManager information to disk. Changes are not * automatically written to disk due to speed issues, so you can * periodically call this function while running to reduce the chance of * lost data in the event of a crash. */ static void saveCovers(); /** * @return Iterator pointing to the first element in the cover database. */ static CoverDataMapIterator begin(); /** * @return Iterator pointing after the last element in the cover database. */ static CoverDataMapIterator end(); /** * Associates @p path with the cover identified by @id. No comparison of * metadata is performed to enforce this matching. * * @param path The absolute file path to the track. * @param id The identifier of the cover to use with @p path. */ static void setIdForTrack(const QString &path, coverKey id); /** * Returns the identifier of the cover for the track at @p path. * * @param path The absolute file path to the track. * @return NoMatch if @p path doesn't have a cover, otherwise the id of * its cover. */ static coverKey idForTrack(const QString &path); /** * This identifier is used to indicate that no cover was found in the * database. */ static const coverKey NoMatch; private: friend class CoverProxy; // Our QObject-wielding friend. /// Called by CoverProxy to notify of a completed job. static void jobComplete(KJob *job, bool completedSatisfactory); static CoverManagerPrivate *data(); static QPixmap createThumbnail(const QPixmap &base); }; #endif /* JUK_COVERMANAGER_H */ // vim: set et sw=4 tw=0 sta: diff --git a/coverproxy.h b/coverproxy.h index 6f63dd37..0bcda5b3 100644 --- a/coverproxy.h +++ b/coverproxy.h @@ -1,39 +1,39 @@ /** * Copyright (C) 2009 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_COVERPROXY_H #define JUK_COVERPROXY_H -#include +#include class KJob; /** * This class is responsible for tracking status of KIO::Jobs that are * downloading covers for the CoverManager. */ class CoverProxy : public QObject { Q_OBJECT public: - CoverProxy(QObject *parent = 0); + explicit CoverProxy(QObject *parent = 0); private slots: void handleResult(KJob *); }; #endif diff --git a/deletedialog.h b/deletedialog.h index e5030093..dfccacb9 100644 --- a/deletedialog.h +++ b/deletedialog.h @@ -1,79 +1,79 @@ /** * Copyright (C) 2004, 2008 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_DELETEDIALOG_H #define JUK_DELETEDIALOG_H class QStringList; #include #include #include namespace Ui { class DeleteDialogBase; } class DeleteDialog; class DeleteWidget : public QWidget { Q_OBJECT public: - DeleteWidget(QWidget *parent); + explicit DeleteWidget(QWidget *parent); void setFiles(const QStringList &files); bool shouldDelete() const; signals: void signalShouldDelete(bool); void accepted(); void rejected(); protected slots: virtual void slotShouldDelete(bool shouldDelete); private: friend DeleteDialog; // TODO: Move KGuiItem stuff into here too Ui::DeleteDialogBase *m_ui; }; class DeleteDialog : public QDialog { Q_OBJECT public: - DeleteDialog(QWidget* parent); + explicit DeleteDialog(QWidget* parent); bool confirmDeleteList(const QStringList &condemnedFiles); void setFiles(const QStringList &files); bool shouldDelete() const { return m_widget->shouldDelete(); } protected slots: virtual void accept(); void slotShouldDelete(bool shouldDelete); private: DeleteWidget *m_widget; KGuiItem m_trashGuiItem; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/directorylist.h b/directorylist.h index 5af745e4..a0c4b8ba 100644 --- a/directorylist.h +++ b/directorylist.h @@ -1,83 +1,83 @@ /** * Copyright (C) 2003-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_DIRECTORYLIST_H #define JUK_DIRECTORYLIST_H #include #include "ui_directorylistbase.h" class QStringListModel; class DirectoryListBase : public QWidget, public Ui::DirectoryListBase { Q_OBJECT public: DirectoryListBase(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class DirectoryList : public QDialog { Q_OBJECT public: struct Result { QStringList addedDirs; QStringList removedDirs; QStringList excludedDirs; DialogCode status; bool addPlaylists; }; - DirectoryList( + explicit DirectoryList( const QStringList &directories, const QStringList &excludeDirectories, bool importPlaylists, QWidget *parent = nullptr); Result dialogResult() const { return m_result; } public slots: int exec(); signals: void signalDirectoryAdded(const QString &directory); void signalDirectoryRemoved(const QString &directory); void signalExcludeDirectoryAdded(const QString &directory); void signalExcludeDirectoryRemoved(const QString &directory); private slots: void slotAddDirectory(); void slotRemoveDirectory(); void slotAddExcludeDirectory(); void slotRemoveExcludeDirectory(); private: QStringListModel *m_dirListModel; QStringListModel *m_excludedDirListModel; DirectoryListBase *m_base; Result m_result; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/exampleoptions.h b/exampleoptions.h index d2113069..bed8df2a 100644 --- a/exampleoptions.h +++ b/exampleoptions.h @@ -1,71 +1,71 @@ /** * Copyright (C) 2004 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_EXAMPLEOPTIONS_H #define JUK_EXAMPLEOPTIONS_H class QUrl; #include #include #include "ui_exampleoptionsbase.h" class ExampleOptions : public QWidget, public Ui::ExampleOptionsBase { Q_OBJECT public: - ExampleOptions(QWidget *parent); + explicit ExampleOptions(QWidget *parent); protected slots: void exampleSelectionChanged(); void exampleDataChanged(); void exampleFileChanged(); signals: void dataChanged(); void fileChanged(); }; class ExampleOptionsDialog : public QDialog { Q_OBJECT public: - ExampleOptionsDialog(QWidget *parent); + explicit ExampleOptionsDialog(QWidget *parent); const ExampleOptions *widget() const { return m_options; } protected: virtual void hideEvent(QHideEvent *); virtual void showEvent(QShowEvent *); protected slots: void fileModeSelected(); void urlChanged(const QUrl &); signals: void fileChanged(const QString &); void dataChanged(); void signalHidden(); void signalShown(); private: ExampleOptions *m_options; }; #endif /* JUK_EXAMPLEOPTIONS_H */ // vim: set et sw=4 tw=0 sta: diff --git a/filerenamer.h b/filerenamer.h index 36d2342c..1de538fb 100644 --- a/filerenamer.h +++ b/filerenamer.h @@ -1,552 +1,552 @@ /** * Copyright (C) 2004, 2007 Michael Pyne * Copyright (C) 2003 Frerich Raabe * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_FILERENAMER_H #define JUK_FILERENAMER_H #include #include #include #include "ui_filerenamerbase.h" #include "categoryreaderinterface.h" #include "tagrenameroptions.h" class QCheckBox; class QPushButton; class QSignalMapper; class QUrl; class ExampleOptionsDialog; class PlaylistItem; template class QList; typedef QList PlaylistItemList; // Used to decide what direction the FileRenamerWidget will move rows in. enum MovementDirection { MoveUp, MoveDown }; /** * This is used by FileRenamerWidget to store information about a particular * tag type, including its position, the QFrame holding the information, * the up, down, and enable buttons, and the user-selected renaming options. */ struct Row { Row() : widget(0), upButton(0), downButton(0), enableButton(0) {} QWidget *widget; QPushButton *upButton, *downButton, *optionsButton, *enableButton; TagRenamerOptions options; CategoryID category; // Includes category and a disambiguation id. int position; ///< Position in the GUI (0 == top) QString name; }; /** * A list of rows, each of which may have its own category options and other * associated data. There is no relation between the order of rows in the vector and their * GUI layout. Instead, each Row has a position member which indicates what GUI position it * takes up. The index into the vector is known as the row identifier (which is unique but * not necessarily constant). */ typedef QVector Rows; /** * Holds a list directory separator checkboxes which separate a row. There * should always be 1 less than the number of rows in the GUI. * * Used for ConfigCategoryReader. */ typedef QVector DirSeparatorCheckBoxes; /** * Associates a CategoryID combination with a set of options. * * Used for ConfigCategoryReader */ typedef QMap CategoryOptionsMap; /** * An implementation of CategoryReaderInterface that reads the user's settings * from the global KConfig configuration object, and reads track information * from whatever the given PlaylistItem is. You can assign different * PlaylistItems in order to change the returned tag category information. * * @author Michael Pyne */ class ConfigCategoryReader : public CategoryReaderInterface { public: // ConfigCategoryReader specific members ConfigCategoryReader(); const PlaylistItem *playlistItem() const { return m_currentItem; } void setPlaylistItem(const PlaylistItem *item) { m_currentItem = item; } // CategoryReaderInterface reimplementations virtual QString categoryValue(TagType type) const; virtual QString prefix(const CategoryID &category) const; virtual QString suffix(const CategoryID &category) const; virtual TagRenamerOptions::EmptyActions emptyAction(const CategoryID &category) const; virtual QString emptyText(const CategoryID &category) const; virtual QList categoryOrder() const; virtual QString separator() const; virtual QString musicFolder() const; virtual int trackWidth(int categoryNum) const; virtual bool hasFolderSeparator(int index) const; virtual bool isDisabled(const CategoryID &category) const; private: const PlaylistItem *m_currentItem; CategoryOptionsMap m_options; QList m_categoryOrder; QString m_separator; QString m_musicFolder; QVector m_folderSeparators; }; /** * This class implements a dialog that allows the user to alter the behavior * of the file renamer. It supports 6 different genre types at this point, * and it shouldn't be too difficult to extend that in the future if needed. * It allows the user to open an external dialog, which will let the user see * an example of what their current options will look like, by either allowing * the user to type in some sample information, or by loading a file and * reading tags from there. * * It also implements the CategoryReaderInterface in order to implement the * example filename functionality. * * @author Michael Pyne */ class FileRenamerWidget : public QWidget, public CategoryReaderInterface { Q_OBJECT public: - FileRenamerWidget(QWidget *parent); + explicit FileRenamerWidget(QWidget *parent); ~FileRenamerWidget(); /// Maximum number of total categories the widget will allow. static int const MAX_CATEGORIES = 16; /** * This function saves all of the category options to the global KConfig * object. You must call this manually, FileRenamerWidget doesn't call it * automatically so that situations where the user hits "Cancel" work * correctly. */ void saveConfig(); signals: void accepted(); // for the QDialogButtonBox void rejected(); protected slots: /** * This function should be called whenever the example text may need to be * changed. For example, when the user selects a different separator or * changes the example text, this slot should be called. */ virtual void exampleTextChanged(); /** * This function shows the example dialog if it is hidden, and hides the * example dialog if it is shown. */ virtual void toggleExampleDialog(); /** * This function inserts the currently selected category, so that the * user can use duplicate tags in the file renamer. */ virtual void insertCategory(); private: /** * This function initializes the category options by loading the data from * the global KConfig object. This is called automatically in the constructor. */ void loadConfig(); /** * This function adds a "Insert Folder separator" checkbox to the end of * the current layout. The setting defaults to being unchecked. */ void addFolderSeparatorCheckbox(); /** * This function creates a row in the main view for category, appending it * to the end. It handles connecting signals to the mapper and such as * well. * * @param category Type of row to append. * @return identifier of newly added row. */ int addRowCategory(TagType category); /** * Removes the given row, updating the other rows to have the correct * number of categoryNumber. * * @param id The identifier of the row to remove. * @return true if the delete succeeded, false otherwise. */ bool removeRow(int id); /** * Updates the mappings currently set for the row identified by oldId so * that they emit newId instead. Does not actually delete the row given * by oldId. * * @param oldId The identifier of the row to change mappings for. * @param newId The identifier to use instead. */ void moveSignalMappings(int oldId, int newId); /** * This function sets up the internal view by creating the checkboxes and * the rows for each category. */ void createTagRows(); /** * Returns the value for \p category by retrieving the tag from m_exampleFile. * If \p category is Track, then an appropriate fixup will be applied if needed * to match the user's desired minimum width. * * @param category the category to retrieve the value for. * @return the string representation of the value for \p category. */ QString fileCategoryValue(TagType category) const; /** * Returns the value for \p category by reading the user entry for that * category. If \p category is Track, then an appropriate fixup will be applied * if needed to match the user's desired minimum width. * * @param category the category to retrieve the value for. * @return the string representation of the value for \p category. */ virtual QString categoryValue(TagType category) const; /** * Returns the user-specified prefix string for \p category. * * @param category the category to retrieve the value for. * @return user-specified prefix string for \p category. */ virtual QString prefix(const CategoryID &category) const { return m_rows[findIdentifier(category)].options.prefix(); } /** * Returns the user-specified suffix string for \p category. * * @param category the category to retrieve the value for. * @return user-specified suffix string for \p category. */ virtual QString suffix(const CategoryID &category) const { return m_rows[findIdentifier(category)].options.suffix(); } /** * Returns the user-specified empty action for \p category. * * @param category the category to retrieve the value for. * @return user-specified empty action for \p category. */ virtual TagRenamerOptions::EmptyActions emptyAction(const CategoryID &category) const { return m_rows[findIdentifier(category)].options.emptyAction(); } /** * Returns the user-specified empty text for \p category. This text might * be used to replace an empty value. * * @param category the category to retrieve the value for. * @return the user-specified empty text for \p category. */ virtual QString emptyText(const CategoryID &category) const { return m_rows[findIdentifier(category)].options.emptyText(); } /** * @return list of CategoryIDs corresponding to the user-specified category order. */ virtual QList categoryOrder() const; /** * @return string that separates the tag values in the file name. */ virtual QString separator() const; /** * @return local path to the music folder used to store renamed files. */ virtual QString musicFolder() const; /** * @param categoryNum Zero-based number of category to get results for (if more than one). * @return the minimum width of the track category. */ virtual int trackWidth(int categoryNum) const { CategoryID id(Track, categoryNum); return m_rows[findIdentifier(id)].options.trackWidth(); } /** * @param index, the 0-based index for the folder boundary. * @return true if there should be a folder separator between category * index and index + 1, and false otherwise. Note that for purposes * of this function, only categories that are required or non-empty * should count. */ virtual bool hasFolderSeparator(int index) const; /** * @param category The category to get the status of. * @return true if \p category is disabled by the user, and false otherwise. */ virtual bool isDisabled(const CategoryID &category) const { return m_rows[findIdentifier(category)].options.disabled(); } /** * This moves the widget \p l in the direction given by \p direction, taking * care to make sure that the checkboxes are not moved, and that they are * enabled or disabled as appropriate for the new layout, and that the up and * down buttons are also adjusted as necessary. * * @param id the identifier of the row to move * @param direction the direction to move */ void moveItem(int id, MovementDirection direction); /** * This function actually performs the work of showing the options dialog for * \p category. * * @param category the category to show the options dialog for. */ void showCategoryOptions(TagType category); /** * This function enables or disables the widget in the row identified by \p id, * controlled by \p enable. This function also makes sure that checkboxes are * enabled or disabled as appropriate if they no longer make sense due to the * adjacent category being enabled or disabled. * * @param id the identifier of the row to change. This is *not* the category to * change. * @param enable enables the category if true, disables if false. */ void setCategoryEnabled(int id, bool enable); /** * This function enables all of the up buttons. */ void enableAllUpButtons(); /** * This function enables all of the down buttons. */ void enableAllDownButtons(); /** * This function returns the identifier of the row at \p position. * * @param position The position to find the identifier of. * @return The unique id of the row at \p position. */ int idOfPosition(int position) const; /** * This function returns the identifier of the row in the m_rows index that * contains \p category and matches \p categoryNum. * * @param category the category to find. * @return the identifier of the category, or MAX_CATEGORIES if it couldn't * be found. */ int findIdentifier(const CategoryID &category) const; private slots: /** * This function reads the tags from \p file and ensures that the dialog will * use those tags until a different file is selected or dataSelected() is * called. * * @param file the path to the local file to read. */ virtual void fileSelected(const QString &file); /** * This function reads the tags from the user-supplied examples and ensures * that the dialog will use those tags until a file is selected using * fileSelected(). */ virtual void dataSelected(); /** * This function brings up a dialog that allows the user to edit the options * for \p id. * * @param id the unique id to bring up the options for. */ virtual void showCategoryOption(int id); /** * This function removes the row identified by id and updates the internal data to be * consistent again, by forwarding the call to removeRow(). * This roundabout way is done due to QSignalMapper. * * @param id The unique id to update */ virtual void slotRemoveRow(int id); /** * This function moves \p category up in the layout. * * @param id the unique id of the widget to move up. */ virtual void moveItemUp(int id); /** * This function moves \p category down in the layout. * * @param id the unique id of the widget to move down. */ virtual void moveItemDown(int id); /** * This slot should be called whenever the example input dialog is shown. */ virtual void exampleDialogShown(); /** - * This slot should be called whever the example input dialog is hidden. + * This slot should be called whenever the example input dialog is hidden. */ virtual void exampleDialogHidden(); private: /// This is the frame that holds all of the category widgets and checkboxes. QFrame *m_mainFrame; Ui::FileRenamerBase *m_ui; /** * This is the meat of the widget, it holds the rows for the user configuration. It is * initially created such that m_rows[0] is the top and row + 1 is the row just below. * However, this is NOT NECESSARILY true, so don't rely on this. As soon as the user * clicks an arrow to move a row then the order will be messed up. Use row.position to * determine where the row is in the GUI. * * @see idOfPosition * @see findIdentifier */ Rows m_rows; /** * This holds an array of checkboxes that allow the user to insert folder * separators in between categories. */ DirSeparatorCheckBoxes m_folderSwitches; ExampleOptionsDialog *m_exampleDialog; /// This is true if we're reading example tags from m_exampleFile. bool m_exampleFromFile; QString m_exampleFile; // Used to map signals from rows to the correct widget. QSignalMapper *mapper; QSignalMapper *toggleMapper; QSignalMapper *upMapper; QSignalMapper *downMapper; }; /** * This class contains the backend code to actually implement the file renaming. It performs * the function of moving the files from one location to another, constructing the file name * based off of the user's options (see ConfigCategoryReader) and of setting folder icons * if appropriate. * * @author Michael Pyne */ class FileRenamer { public: FileRenamer(); /** * Renames the filename on disk of the file represented by item according * to the user configuration stored in KConfig. * * @param item The item to rename. */ void rename(PlaylistItem *item); /** * Renames the filenames on disk of the files given in items according to * the user configuration stored in KConfig. * * @param items The items to rename. */ void rename(const PlaylistItemList &items); /** * Returns the file name that would be generated based on the options read from * interface, which must implement CategoryReaderInterface. (A whole interface is used * so that we can re-use the code to generate filenames from a in-memory GUI and from * KConfig). * * @param interface object to read options/data from. */ static QString fileName(const CategoryReaderInterface &interface); private: /** * Sets the folder icon for elements of the destination path for item (if * there is not already a folder icon set, and if the folder's name has * the album name. */ void setFolderIcon(const QUrl &dst, const PlaylistItem *item); /** * Attempts to rename the file from \a src to \a dest. Returns true if the * operation succeeded. */ bool moveFile(const QString &src, const QString &dest); }; #endif /* JUK_FILERENAMER_H */ // vim: set et sw=4 tw=0 sta: diff --git a/filerenamerconfigdlg.h b/filerenamerconfigdlg.h index 85bd0781..def31995 100644 --- a/filerenamerconfigdlg.h +++ b/filerenamerconfigdlg.h @@ -1,40 +1,40 @@ /** * Copyright (C) 2004 Michael Pyne * Copyright (C) 2003 Frerich Raabe * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_FILERENAMERCONFIGDLG_H #define JUK_FILERENAMERCONFIGDLG_H #include class FileRenamerWidget; class FileRenamerConfigDlg : public QDialog { Q_OBJECT public: - FileRenamerConfigDlg(QWidget *parent); + explicit FileRenamerConfigDlg(QWidget *parent); protected slots: virtual void accept(); private: FileRenamerWidget *m_renamerWidget; }; #endif // JUK_FILERENAMERCONFIGDLG_H // vim: set et sw=4 tw=0 sta: diff --git a/historyplaylist.h b/historyplaylist.h index 63d75974..bfed5b24 100644 --- a/historyplaylist.h +++ b/historyplaylist.h @@ -1,72 +1,72 @@ /** * Copyright (C) 2003-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_HISTORYPLAYLIST_H #define JUK_HISTORYPLAYLIST_H #include #include "playlist.h" #include "playlistitem.h" class HistoryPlaylistItem : public PlaylistItem { public: HistoryPlaylistItem(CollectionListItem *item, Playlist *parent, QTreeWidgetItem *after = nullptr); QDateTime dateTime() const { return m_dateTime; } void setDateTime(const QDateTime &dt); private: QDateTime m_dateTime; }; class HistoryPlaylist : public Playlist { Q_OBJECT public: - HistoryPlaylist(PlaylistCollection *collection); + explicit HistoryPlaylist(PlaylistCollection *collection); virtual ~HistoryPlaylist(); virtual HistoryPlaylistItem *createItem(const FileHandle &file, QTreeWidgetItem *after = nullptr); virtual void createItems(const PlaylistItemList &siblings); virtual int columnOffset() const { return 1; } virtual bool readOnly() const { return true; } static int delay() { return 5000; } public slots: void cut() {} void clear() {} void appendProposedItem(const FileHandle &file); private slots: void slotCreateNewItem(); private: using Playlist::createItems; FileHandle m_file; QTimer *m_timer; }; QDataStream &operator<<(QDataStream &s, const HistoryPlaylist &p); QDataStream &operator>>(QDataStream &s, HistoryPlaylist &p); #endif // vim: set et sw=4 tw=0 sta: diff --git a/juk.h b/juk.h index 9de30a1e..2b188994 100644 --- a/juk.h +++ b/juk.h @@ -1,106 +1,106 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_H #define JUK_H #include #include "playermanager.h" #include class KToggleAction; class KGlobalAccel; class SliderAction; class StatusLabel; class SystemTray; class PlayerManager; class PlaylistSplitter; class Scrobbler; class JuK : public KXmlGuiWindow { Q_OBJECT public: - JuK(const QStringList &filesToOpen, QWidget* parent = 0); + explicit JuK(const QStringList &filesToOpen, QWidget* parent = 0); virtual ~JuK(); static JuK* JuKInstance(); PlayerManager *playerManager() const; // Use a null cover for failure void coverDownloaded(const QPixmap &cover); private: void setupLayout(); void setupActions(); void setupGlobalAccels(); void keyPressEvent(QKeyEvent *); void activateScrobblerIfEnabled(); /** * readSettings() is separate from readConfig() in that it contains settings * that need to be read before the GUI is setup. */ void readSettings(); void readConfig(); void saveConfig(); virtual bool queryClose(); private slots: void slotSetupSystemTray(); void slotShowHide(); void slotQuit(); void slotToggleSystemTray(bool enabled); void slotEditKeys(); void slotConfigureTagGuesser(); void slotConfigureFileRenamer(); void slotConfigureScrobbling(); void slotUndo(); void slotCheckAlbumNextAction(bool albumRandomEnabled); void slotProcessArgs(); void slotClearOldCovers(); private: PlaylistSplitter *m_splitter; StatusLabel *m_statusLabel; SystemTray *m_systemTray; KToggleAction *m_randomPlayAction; KToggleAction *m_toggleSystemTrayAction; KToggleAction *m_toggleDockOnCloseAction; KToggleAction *m_togglePopupsAction; PlayerManager *m_player; Scrobbler *m_scrobbler; QStringList m_filesToOpen; bool m_startDocked; bool m_shuttingDown; uint m_pmToken; static JuK* m_instance; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/ktrm.h b/ktrm.h index 9e0b4419..53afc3f6 100644 --- a/ktrm.h +++ b/ktrm.h @@ -1,224 +1,224 @@ /** * Copyright (C) 2004 Scott Wheeler */ /** * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version * 2.1 as published by the Free Software Foundation. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ /* * At some point this will likely be a library class, as such it's been written * as such and is LGPL'ed. */ #ifndef KTRM_H #define KTRM_H #include // FIXME ktrm #if 0 template class QList; class QString; class QStringList; class KJob; /** * This represents a potential match for a TRM lookup. KTRMResultList is * returned from KTRMLookup and will be sorted by relevance (better matches * at the beginning of the list). */ class KTRMResult { friend class KTRMLookup; public: KTRMResult(); KTRMResult(const KTRMResult &result); ~KTRMResult(); /** * Returns the title of the track for the potential match. */ QString title() const; /** * Returns the artist name of the track for the potential match. */ QString artist() const; /** * Returns the album name of the track for the potential match. */ QString album() const; /** * Returns the track number of the track for the potential match. */ int track() const; /** * Returns the original release year of the track for the potential match. */ int year() const; /** * Returns true if all of the values for the result are empty. */ bool isEmpty() const; /** * Compares to \a r based on the relevance of the match. Better matches * will be greater than less accurate matches. */ bool operator<(const KTRMResult &r) const; /** * Compares to \a r based on the relevance of the match. Better matches * will be greater than less accurate matches. */ bool operator>(const KTRMResult &r) const; private: class KTRMResultPrivate; KTRMResultPrivate *d; }; typedef QList KTRMResultList; /** * An abstraction for libtunepimp's TRM based lookup and file recognition. * * A lookup is started when the object is created. One of the virtual methods * -- recognized(), unrecognized(), collision() or error(). Those methods * should be reimplemented in subclasses to specify what behavior should happen * for each result. * * The lookups themselves happen in a background thread, but the return calls * are guaranteed to run in the GUI thread. */ class KTRMLookup : public QObject { Q_OBJECT public: /** * Creates and starts a lookup for \a file. If \a autoDelete is set to * true the lookup will delete itself when it has finished. */ explicit KTRMLookup(const QString &file, bool autoDelete = false); virtual ~KTRMLookup(); /** * Returns the file name that was specified in the constructor. */ QString file() const; /** * Returns the TunePimp file ID for the file. This is of no use to the * public API. * * @internal */ int fileId() const; /** * This method is called when the puid was already generated. It will then do * the lookup with MusicBrainz's server. This may be reimplemented to provide - * specific behavion for the lookup. + * specific behavior for the lookup. */ virtual void puidGenerated(); /** * This method is called if the track was recognized by the TRM server. * results() will return just one value. This may be reimplemented to - * provide specific behavion in the case of the track being recognized. + * provide specific behavior in the case of the track being recognized. * * \note The base class call should happen at the beginning of the subclass * reimplementation; it populates the values of results(). */ virtual void recognized(); /** * This method is called if the track was not recognized by the TRM server. * results() will return an empty set. This may be reimplemented to provide - * specific behavion in the case of the track not being recognized. + * specific behavior in the case of the track not being recognized. */ virtual void unrecognized(); /** * This method is called if there are multiple potential matches for the TRM * value. results() will return a list of the potential matches, sorted by * liklihood. This may be reimplemented to provide - * specific behavion in the case of the track not being recognized. + * specific behavior in the case of the track not being recognized. * * \note The base class call should happen at the beginning of the subclass * reimplementation; it populates the values of results(). */ virtual void collision(); /** * This method is called if the track was not recognized by the TRM server. * results() will return an empty set. This may be reimplemented to provide - * specific behavion in the case of the track not being recognized. + * specific behavior in the case of the track not being recognized. */ virtual void error(); /** * Returns the list of matches found by the lookup. In the case that there * was a TRM collision this list will contain multiple entries. In the case * that it was recognized this will only contain one entry. Otherwise it * will remain empty. */ KTRMResultList results() const; protected: /** * This method is called when any of terminal states (recognized, * unrecognized, collision or error) has been reached after the specific * method for the result has been called. * * This should be reimplemented in the case that there is some general * processing to be done for all terminal states. */ virtual void finished(); protected slots: virtual void lookupResult( KJob* ); signals: void sigResult( KTRMResultList, QString ); private: class KTRMLookupPrivate; KTRMLookupPrivate *d; }; /* * Helper Functions used for sorting MusicBrainz results */ double stringSimilarity(QString s1, QString s2); double stringSimilarity(const QStringList &l, const QString &s); #endif #endif // vim: set et sw=4 tw=0 sta: diff --git a/lyricswidget.cpp b/lyricswidget.cpp index dbda32ce..1b77816c 100644 --- a/lyricswidget.cpp +++ b/lyricswidget.cpp @@ -1,162 +1,163 @@ /** * Copyright (C) 2012 Martin Sandsmark * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ +#include "lyricswidget.h" + #include #include #include #include #include #include #include #include #include #include #include -#include "lyricswidget.h" #include "tag.h" #include "actioncollection.h" #include "juk_debug.h" LyricsWidget::LyricsWidget(QWidget* parent): QTextBrowser(parent), m_networkAccessManager(new QNetworkAccessManager), m_lyricsCurrent(false) { setMinimumWidth(200); setReadOnly(true); setWordWrapMode(QTextOption::WordWrap); setOpenExternalLinks(true); KToggleAction *show = new KToggleAction(QIcon::fromTheme(QLatin1String("view-media-lyrics")), i18n("Show &Lyrics"), this); ActionCollection::actions()->addAction("showLyrics", show); connect(show, SIGNAL(toggled(bool)), this, SLOT(setVisible(bool))); KConfigGroup config(KSharedConfig::openConfig(), "LyricsWidget"); bool shown = config.readEntry("Show", true); show->setChecked(shown); setVisible(shown); } LyricsWidget::~LyricsWidget() { delete m_networkAccessManager; saveConfig(); } void LyricsWidget::saveConfig() { KConfigGroup config(KSharedConfig::openConfig(), "LyricsWidget"); config.writeEntry("Show", ActionCollection::action("showLyrics")->isChecked()); } void LyricsWidget::makeLyricsRequest() { m_lyricsCurrent = true; if(m_playingFile.isNull()) { setHtml(i18n("No file playing.")); return; } setHtml(i18n("Loading...")); // TODO time for https (as long as it doesn't break this) QUrl listUrl("http://lyrics.wikia.com/api.php"); QUrlQuery listUrlQuery; listUrlQuery.addQueryItem("action", "lyrics"); listUrlQuery.addQueryItem("func", "getSong"); listUrlQuery.addQueryItem("fmt", "xml"); listUrlQuery.addQueryItem("artist", m_playingFile.tag()->artist()); listUrlQuery.addQueryItem("song", m_playingFile.tag()->title()); listUrl.setQuery(listUrlQuery); m_title = m_playingFile.tag()->artist() + " – " + m_playingFile.tag()->title(); connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveListReply(QNetworkReply*))); m_networkAccessManager->get(QNetworkRequest(listUrl)); } void LyricsWidget::playing(const FileHandle &file) { m_playingFile = file; m_lyricsCurrent = false; if(isVisible()) { makeLyricsRequest(); } } void LyricsWidget::showEvent(QShowEvent *) { if(!m_lyricsCurrent) { makeLyricsRequest(); } } void LyricsWidget::receiveListReply(QNetworkReply* reply) { disconnect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveListReply(QNetworkReply*))); if (reply->error() != QNetworkReply::NoError) { qCWarning(JUK_LOG) << "Error while fetching lyrics: " << reply->errorString(); setHtml(i18n("Error while retrieving lyrics!")); return; } QDomDocument document; document.setContent(reply); QString artist = document.elementsByTagName("artist").at(0).toElement().text(); QString title = document.elementsByTagName("song").at(0).toElement().text(); // TODO time for https (as long as it doesn't break this) QUrl url("http://lyrics.wikia.com/api.php"); QUrlQuery urlQuery; urlQuery.addQueryItem("action", "query"); urlQuery.addQueryItem("prop", "revisions"); urlQuery.addQueryItem("rvprop", "content"); urlQuery.addQueryItem("format", "xml"); urlQuery.addQueryItem("titles", artist + ':' + title); url.setQuery(urlQuery); connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveLyricsReply(QNetworkReply*))); m_networkAccessManager->get(QNetworkRequest(url)); } void LyricsWidget::receiveLyricsReply(QNetworkReply* reply) { disconnect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveLyricsReply(QNetworkReply*))); if (reply->error() != QNetworkReply::NoError) { qCWarning(JUK_LOG) << "Error while fetching lyrics: " << reply->errorString(); setHtml(i18n("Error while retrieving lyrics!")); return; } QString content = QString::fromUtf8(reply->readAll()); int lIndex = content.indexOf("<lyrics>"); int rIndex = content.indexOf("</lyrics>"); if (lIndex == -1 || rIndex == -1) { qCWarning(JUK_LOG) << Q_FUNC_INFO << "Unable to find lyrics in text"; setText(i18n("No lyrics available.")); return; } lIndex += 15; // We skip the tag content = content.mid(lIndex, rIndex - lIndex).trimmed(); content.replace('\n', "
"); //setText(content); setHtml("

" + m_title + "

" + content + i18n("

Lyrics provided by LyricWiki")); } diff --git a/main.cpp b/main.cpp index 18e26691..5902e8f8 100644 --- a/main.cpp +++ b/main.cpp @@ -1,117 +1,117 @@ /** * Copyright (C) 2002-2007 Scott Wheeler * Copyright (C) 2004-2017 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "juk.h" -#include "config-juk.h" +#include static const char description[] = I18N_NOOP("Jukebox and music manager by the KDE community"); static const char scott[] = I18N_NOOP("Author, chief dork and keeper of the funk"); static const char michael[] = I18N_NOOP("Assistant super-hero, fixer of many things"); static const char georg[] = I18N_NOOP("More KDE Platform 4 porting efforts"); static const char daniel[] = I18N_NOOP("System tray docking, \"inline\" tag editing,\nbug fixes, evangelism, moral support"); static const char tim[] = I18N_NOOP("GStreamer port"); static const char stefan[] = I18N_NOOP("Global keybindings support"); static const char stephen[] = I18N_NOOP("Track announcement popups"); static const char frerich[] = I18N_NOOP("Automagic track data guessing, bugfixes"); static const char zack[] = I18N_NOOP("More automagical things, now using MusicBrainz"); static const char adam[] = I18N_NOOP("Co-conspirator in MusicBrainz wizardry"); static const char matthias[] = I18N_NOOP("Friendly, neighborhood aRts guru"); static const char maks[] = I18N_NOOP("Making JuK friendlier to people with terabytes of music"); static const char antonio[] = I18N_NOOP("DCOP interface"); static const char allan[] = I18N_NOOP("FLAC and MPC support"); static const char nathan[] = I18N_NOOP("Album cover manager"); static const char pascal[] = I18N_NOOP("Gimper of splash screen"); static const char laurent[] = I18N_NOOP("Porting to KDE 4 when no one else was around"); static const char giorgos[] = I18N_NOOP("Badly-needed tag editor bugfixes."); static const char sandsmark[] = I18N_NOOP("Last.fm scrobbling support, lyrics, prepping for KDE Frameworks."); static const char sho[] = I18N_NOOP("MPRIS2 Interface implementation."); static const char kacper[] = I18N_NOOP("Porting to KF5 when no one else was around"); int main(int argc, char *argv[]) { QApplication a(argc, argv); KLocalizedString::setApplicationDomain("juk"); KAboutData aboutData(QStringLiteral("juk"), i18n("JuK"), QStringLiteral(JUK_VERSION), i18n(description), KAboutLicense::GPL, i18n("© 2002–2017, Scott Wheeler, Michael Pyne, and others"), QStringLiteral(""), QStringLiteral("https://www.kde.org/applications/multimedia/juk/")); aboutData.addAuthor(i18n("Scott Wheeler"), i18n(scott), "wheeler@kde.org"); aboutData.addAuthor(i18n("Michael Pyne"), i18n(michael), "mpyne@kde.org"); aboutData.addCredit(i18n("Kacper Kasper"), i18n(kacper), "kacperkasper@gmail.com", "http://kacperkasper.pl/"); aboutData.addCredit(i18n("Eike Hein"), i18n(sho), "hein@kde.org"); aboutData.addCredit(i18n("Martin Sandsmark"), i18n(sandsmark), "martin.sandsmark@kde.org"); aboutData.addCredit(i18n("Γιώργος Κυλάφας (Giorgos Kylafas)"), i18n(giorgos), "gekylafas@gmail.com"); aboutData.addCredit(i18n("Georg Grabler"), i18n(georg), "georg@grabler.net"); aboutData.addCredit(i18n("Laurent Montel"), i18n(laurent), "montel@kde.org"); aboutData.addCredit(i18n("Nathan Toone"), i18n(nathan), "nathan@toonetown.com"); aboutData.addCredit(i18n("Matthias Kretz"), i18n(matthias), "kretz@kde.org"); aboutData.addCredit(i18n("Daniel Molkentin"), i18n(daniel), "molkentin@kde.org"); aboutData.addCredit(i18n("Tim Jansen"), i18n(tim), "tim@tjansen.de"); aboutData.addCredit(i18n("Stefan Asserhäll"), i18n(stefan), "stefan.asserhall@telia.com"); aboutData.addCredit(i18n("Stephen Douglas"), i18n(stephen), "stephen_douglas@yahoo.com"); aboutData.addCredit(i18n("Frerich Raabe"), i18n(frerich), "raabe@kde.org"); aboutData.addCredit(i18n("Zack Rusin"), i18n(zack), "zack@kde.org"); aboutData.addCredit(i18n("Adam Treat"), i18n(adam), "manyoso@yahoo.com"); aboutData.addCredit(i18n("Maks Orlovich"), i18n(maks), "maksim@kde.org"); aboutData.addCredit(i18n("Antonio Larrosa Jimenez"), i18n(antonio), "larrosa@kde.org"); aboutData.addCredit(i18n("Allan Sandfeld Jensen"), i18n(allan), "kde@carewolf.com"); aboutData.addCredit(i18n("Pascal Klein"), i18n(pascal), "4pascal@tpg.com.au"); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.addPositionalArgument(QLatin1String("[file(s)]"), i18n("File(s) to open")); parser.process(a); aboutData.processCommandLine(&parser); KCrash::initialize(); // Create the main window and such JuK *juk = new JuK(parser.positionalArguments()); if(a.isSessionRestored() && KMainWindow::canBeRestored(1)) juk->restore(1, false /* don't show */); KConfigGroup config(KSharedConfig::openConfig(), "Settings"); if(!config.readEntry("StartDocked", false) || !config.readEntry("DockInSystemTray", false)) { juk->show(); } else if(!a.isSessionRestored()) { QString message = i18n("JuK running in docked mode\nUse context menu in system tray to restore."); KNotification::event("dock_mode",i18n("JuK Docked"), message); } return a.exec(); } // vim: set et sw=4 tw=0 sta fileencoding=utf8: diff --git a/mediafiles.cpp b/mediafiles.cpp index cf059ff4..861896e7 100644 --- a/mediafiles.cpp +++ b/mediafiles.cpp @@ -1,288 +1,288 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "mediafiles.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "config-juk.h" +#include #if TAGLIB_HAS_OPUSFILE # include #endif #ifdef TAGLIB_WITH_ASF #include #endif #ifdef TAGLIB_WITH_MP4 #include #endif #include "juk_debug.h" namespace MediaFiles { static QStringList savedMimeTypes; static const char mp3Type[] = "audio/mpeg"; static const char oggType[] = "audio/ogg"; static const char flacType[] = "audio/x-flac"; static const char mpcType[] = "audio/x-musepack"; static const char m3uType[] = "audio/x-mpegurl"; static const char vorbisType[] = "audio/x-vorbis+ogg"; static const char oggflacType[] = "audio/x-flac+ogg"; #ifdef TAGLIB_WITH_ASF static const char asfType[] = "video/x-ms-asf"; #endif #ifdef TAGLIB_WITH_MP4 static const char mp4Type[] = "audio/mp4"; static const char mp4AudiobookType[] = "audio/x-m4b"; #endif #if TAGLIB_HAS_OPUSFILE static const char oggopusType[] = "audio/x-opus+ogg"; #endif static const char *const mediaTypes[] = { mp3Type, oggType, flacType, mpcType, vorbisType, oggflacType #ifdef TAGLIB_WITH_ASF ,asfType #endif #ifdef TAGLIB_WITH_MP4 ,mp4Type ,mp4AudiobookType #endif #if TAGLIB_HAS_OPUSFILE ,oggopusType #endif }; static const char playlistExtension[] = ".m3u"; } #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) static QString getMusicDir() { const auto musicLocation = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); QDir musicDir(musicLocation); if (Q_UNLIKELY( !musicDir.exists() && musicDir.isAbsolute() && // safety precaution here !musicDir.mkpath(musicLocation))) { qCWarning(JUK_LOG) << "Failed to create music dir:" << musicLocation; } return musicLocation; } QStringList MediaFiles::openDialog(QWidget *parent) { QFileDialog dialog(parent); dialog.setFileMode(QFileDialog::ExistingFiles); dialog.setMimeTypeFilters(mimeTypes()); // limit to only file:// for now dialog.setSupportedSchemes(QStringList() << QStringLiteral("file")); dialog.setDirectory(getMusicDir()); dialog.setWindowTitle(i18nc("open audio file", "Open")); if(dialog.exec()) { return dialog.selectedFiles(); } return QStringList(); } QString MediaFiles::savePlaylistDialog(const QString &playlistName, QWidget *parent) { QString fileName = QFileDialog::getSaveFileName( parent, i18n("Save Playlist") + QStringLiteral(" ") + playlistName, getMusicDir(), QStringLiteral("Playlists (*") + playlistExtension + QStringLiteral(")") ); return fileName; } TagLib::File *MediaFiles::fileFactoryByType(const QString &fileName) { QMimeDatabase db; QMimeType result = db.mimeTypeForFile(fileName); if(!result.isValid()) return nullptr; TagLib::File *file(nullptr); QByteArray encodedFileName(QFile::encodeName(fileName)); if(result.inherits(QLatin1String(mp3Type))) file = new TagLib::MPEG::File(encodedFileName.constData()); else if(result.inherits(QLatin1String(flacType))) file = new TagLib::FLAC::File(encodedFileName.constData()); else if(result.inherits(QLatin1String(vorbisType))) file = new TagLib::Vorbis::File(encodedFileName.constData()); #ifdef TAGLIB_WITH_ASF else if(result.inherits(QLatin1String(asfType))) file = new TagLib::ASF::File(encodedFileName.constData()); #endif #ifdef TAGLIB_WITH_MP4 else if(result.inherits(QLatin1String(mp4Type)) || result.inherits(QLatin1String(mp4AudiobookType))) file = new TagLib::MP4::File(encodedFileName.constData()); #endif else if(result.inherits(QLatin1String(mpcType))) file = new TagLib::MPC::File(encodedFileName.constData()); else if(result.inherits(QLatin1String(oggflacType))) file = new TagLib::Ogg::FLAC::File(encodedFileName.constData()); #if TAGLIB_HAS_OPUSFILE else if(result.inherits(QLatin1String(oggopusType)) || (result.inherits(QLatin1String(oggType)) && fileName.endsWith(QLatin1String(".opus"))) ) { file = new TagLib::Ogg::Opus::File(encodedFileName.constData()); } #endif return file; } bool MediaFiles::isMediaFile(const QString &fileName) { QMimeDatabase db; QMimeType result = db.mimeTypeForFile(fileName); if(!result.isValid()) return false; // Search through our table of media types for a match for(const auto &mimeType : mimeTypes()) { if(result.inherits(mimeType)) return true; } return false; } static bool isFileOfMimeType(const QString &fileName, const QString &mimeType) { QMimeDatabase db; QMimeType result = db.mimeTypeForFile(fileName); return result.isValid() && result.inherits(mimeType); } bool MediaFiles::isPlaylistFile(const QString &fileName) { return isFileOfMimeType(fileName, m3uType); } bool MediaFiles::isMP3(const QString &fileName) { return isFileOfMimeType(fileName, mp3Type); } bool MediaFiles::isOgg(const QString &fileName) { return isFileOfMimeType(fileName, oggType); } bool MediaFiles::isFLAC(const QString &fileName) { return isFileOfMimeType(fileName, flacType); } bool MediaFiles::isMPC(const QString &fileName) { return isFileOfMimeType(fileName, mpcType); } bool MediaFiles::isVorbis(const QString &fileName) { return isFileOfMimeType(fileName, vorbisType); } #ifdef TAGLIB_WITH_ASF bool MediaFiles::isASF(const QString &fileName) { return isFileOfMimeType(fileName, asfType); } #endif #ifdef TAGLIB_WITH_MP4 bool MediaFiles::isMP4(const QString &fileName) { return isFileOfMimeType(fileName, mp4Type) || isFileOfMimeType(fileName, mp4AudiobookType); } #endif bool MediaFiles::isOggFLAC(const QString &fileName) { return isFileOfMimeType(fileName, oggflacType); } QStringList MediaFiles::mimeTypes() { if(!savedMimeTypes.isEmpty()) return savedMimeTypes; for(unsigned i = 0; i < ARRAY_SIZE(mediaTypes); ++i) { savedMimeTypes << QLatin1String(mediaTypes[i]); } return savedMimeTypes; } QStringList MediaFiles::convertURLsToLocal(const QList &urlList, QWidget *w) { QStringList result; QUrl localUrl; for(const auto &url : urlList) { auto localizerJob = KIO::mostLocalUrl(url); KJobWidgets::setWindow(localizerJob, w); if(localizerJob->exec() && (localUrl = localizerJob->mostLocalUrl()).isLocalFile()) result.append(localUrl.path()); else qCDebug(JUK_LOG) << url << " is not a local file, skipping."; } return result; } // vim: set et sw=4 tw=0 sta: diff --git a/mpris2/mediaplayer2player.h b/mpris2/mediaplayer2player.h index 3b6fb29d..2f9cc6df 100644 --- a/mpris2/mediaplayer2player.h +++ b/mpris2/mediaplayer2player.h @@ -1,106 +1,106 @@ /*********************************************************************** * Copyright 2012 Eike Hein * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ***********************************************************************/ -#ifndef JUK_MEDIAPLAYRER2PLAYER_H -#define JUK_MEDIAPLAYRER2PLAYER_H +#ifndef JUK_MEDIAPLAYER2PLAYER_H +#define JUK_MEDIAPLAYER2PLAYER_H #include #include #include #include #include "playermanager.h" class MediaPlayer2Player : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player") // Docs: http://www.mpris.org/2.1/spec/Player_Node.html Q_PROPERTY(QString PlaybackStatus READ PlaybackStatus) Q_PROPERTY(QString LoopStatus READ LoopStatus WRITE setLoopStatus) Q_PROPERTY(double Rate READ Rate WRITE setRate) Q_PROPERTY(bool Shuffle READ Shuffle WRITE setShuffle) Q_PROPERTY(QVariantMap Metadata READ Metadata) Q_PROPERTY(double Volume READ Volume WRITE setVolume) Q_PROPERTY(qlonglong Position READ Position) Q_PROPERTY(double MinimumRate READ MinimumRate) Q_PROPERTY(double MaximumRate READ MaximumRate) Q_PROPERTY(bool CanGoNext READ CanGoNext) Q_PROPERTY(bool CanGoPrevious READ CanGoPrevious) Q_PROPERTY(bool CanPlay READ CanPlay) Q_PROPERTY(bool CanPause READ CanPause) Q_PROPERTY(bool CanSeek READ CanSeek) Q_PROPERTY(bool CanControl READ CanControl) public: explicit MediaPlayer2Player(QObject* parent); ~MediaPlayer2Player(); QString PlaybackStatus() const; QString LoopStatus() const; Q_NOREPLY void setLoopStatus(const QString& loopStatus) const; double Rate() const; Q_NOREPLY void setRate(double rate) const; bool Shuffle() const; void setShuffle(bool shuffle) const; QVariantMap Metadata() const; double Volume() const; Q_NOREPLY void setVolume(double volume) const; qlonglong Position() const; double MinimumRate() const; double MaximumRate() const; bool CanGoNext() const; bool CanGoPrevious() const; bool CanPlay() const; bool CanPause() const; bool CanSeek() const; bool CanControl() const; signals: void Seeked(qlonglong Position) const; public slots: Q_NOREPLY void Next() const; Q_NOREPLY void Previous() const; Q_NOREPLY void Pause() const; Q_NOREPLY void PlayPause() const; Q_NOREPLY void Stop() const; Q_NOREPLY void Play() const; Q_NOREPLY void Seek(qlonglong Offset) const; Q_NOREPLY void SetPosition(const QDBusObjectPath& TrackId, qlonglong Position) const; Q_NOREPLY void OpenUri(QString Uri) const; private slots: void currentSourceChanged() const; void stateUpdated() const; void totalTimeChanged() const; void seekableChanged(bool seekable) const; void volumeChanged(float newVol) const; void seeked(int newPos) const; private: void signalPropertiesChange(const QVariantMap& properties) const; int oldPos; QPointer m_player; }; #endif diff --git a/musicbrainzquery.h b/musicbrainzquery.h index 9377732c..589489b4 100644 --- a/musicbrainzquery.h +++ b/musicbrainzquery.h @@ -1,46 +1,46 @@ /** * Copyright (C) 2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef MUSICBRAINZQUERY_H #define MUSICBRAINZQUERY_H #include #if HAVE_TUNEPIMP #include "ktrm.h" #include "filehandle.h" class MusicBrainzLookup : public KTRMLookup { public: - MusicBrainzLookup(const FileHandle &file); + explicit MusicBrainzLookup(const FileHandle &file); virtual void recognized(); virtual void unrecognized(); virtual void collision(); virtual void error(); private: void message(const QString &s) const; void confirmation(); FileHandle m_file; }; #endif #endif // vim: set et sw=4 tw=0 sta: diff --git a/nowplaying.h b/nowplaying.h index 4cd4eea6..5480edbe 100644 --- a/nowplaying.h +++ b/nowplaying.h @@ -1,143 +1,143 @@ /** * Copyright (C) 2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef NOWPLAYING_H #define NOWPLAYING_H #include #include #include #include #include "filehandle.h" #include "playlistinterface.h" class QTimer; class QPoint; class NowPlayingItem; class PlaylistCollection; class Playlist; /** * This is the widget that holds all of the other items and handles updating them * when the playing item changes. */ class NowPlaying : public QWidget { Q_OBJECT public: NowPlaying(QWidget *parent, PlaylistCollection *collection); void addItem(NowPlayingItem *item); PlaylistCollection *collection() const; public slots: void slotUpdate(const FileHandle &file); void slotReloadCurrentItem(); signals: void nowPlayingHidden(); private: struct Observer : public PlaylistObserver { Observer(NowPlaying *parent, PlaylistInterface *playlist) : PlaylistObserver(playlist), m_parent(parent) {} virtual void playlistItemDataHasChanged() Q_DECL_FINAL { m_parent->slotReloadCurrentItem(); } NowPlaying *m_parent; }; friend struct Observer; Observer m_observer; Observer m_collectionListObserver; PlaylistCollection *m_collection; QList m_items; FileHandle m_file; }; /** * Abstract base for the other NowPlaying items. */ class NowPlayingItem { public: virtual ~NowPlayingItem() {} virtual void update(const FileHandle &file) = 0; NowPlaying *parentManager() const { return m_parent; } protected: NowPlayingItem(NowPlaying *parent) : m_parent(parent) { parent->addItem(this); } private: NowPlaying *m_parent; }; /** * Displays the cover of the currently playing file if available, or hides * itself if not. */ class CoverItem : public QLabel, public NowPlayingItem { public: - CoverItem(NowPlaying *parent); + explicit CoverItem(NowPlaying *parent); virtual void update(const FileHandle &file); virtual void mouseReleaseEvent(QMouseEvent *event); protected: virtual void dragEnterEvent(QDragEnterEvent *e); virtual void dropEvent(QDropEvent *e); virtual void mousePressEvent(QMouseEvent *e); virtual void mouseMoveEvent(QMouseEvent *e); private: FileHandle m_file; bool m_dragging; QPoint m_dragStart; }; /** * Show the text information on the current track and provides links to the * album and artist of the currently playing item. */ class TrackItem : public QWidget, public NowPlayingItem { Q_OBJECT public: - TrackItem(NowPlaying *parent); + explicit TrackItem(NowPlaying *parent); virtual void update(const FileHandle &file); private slots: void slotOpenLink(const QString &link); void slotUpdate(); void slotClearShowMore(); private: FileHandle m_file; QLabel *m_label; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/playlist.cpp b/playlist.cpp index 0943e8dd..7cc34182 100644 --- a/playlist.cpp +++ b/playlist.cpp @@ -1,2125 +1,2125 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * Copyright (C) 2008-2018 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "playlist.h" #include "juk-exception.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "playlistitem.h" #include "playlistcollection.h" #include "playlistsearch.h" #include "playlistsharedsettings.h" #include "mediafiles.h" #include "collectionlist.h" #include "filerenamer.h" #include "actioncollection.h" #include "tracksequencemanager.h" #include "tag.h" #include "upcomingplaylist.h" #include "deletedialog.h" #include "webimagefetcher.h" #include "coverinfo.h" #include "coverdialog.h" #include "tagtransactionmanager.h" #include "cache.h" #include "juk_debug.h" using namespace ActionCollection; /** * Used to give every track added in the program a unique identifier. See * PlaylistItem */ quint32 g_trackID = 0; /** * Just a shortcut of sorts. */ static bool manualResize() { return action("resizeColumnsManually")->isChecked(); } //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool Playlist::m_visibleChanged = false; bool Playlist::m_shuttingDown = false; PlaylistItemList Playlist::m_history; QVector Playlist::m_backMenuItems; int Playlist::m_leftColumn = 0; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Playlist::Playlist(PlaylistCollection *collection, const QString &name, const QString &iconName) : QTreeWidget(collection->playlistStack()), m_collection(collection), m_fetcher(new WebImageFetcher(this)), m_allowDuplicates(true), m_applySharedSettings(true), m_columnWidthModeChanged(false), m_disableColumnWidthUpdates(true), m_time(0), m_widthsDirty(true), m_searchEnabled(true), m_playlistName(name), m_rmbMenu(0), m_toolTip(0), m_blockDataChanged(false) { setup(); collection->setupPlaylist(this, iconName); } Playlist::Playlist(PlaylistCollection *collection, const PlaylistItemList &items, const QString &name, const QString &iconName) : QTreeWidget(collection->playlistStack()), m_collection(collection), m_fetcher(new WebImageFetcher(this)), m_allowDuplicates(true), m_applySharedSettings(true), m_columnWidthModeChanged(false), m_disableColumnWidthUpdates(true), m_time(0), m_widthsDirty(true), m_searchEnabled(true), m_playlistName(name), m_rmbMenu(0), m_toolTip(0), m_blockDataChanged(false) { setup(); collection->setupPlaylist(this, iconName); createItems(items); } Playlist::Playlist(PlaylistCollection *collection, const QFileInfo &playlistFile, const QString &iconName) : QTreeWidget(collection->playlistStack()), m_collection(collection), m_fetcher(new WebImageFetcher(this)), m_allowDuplicates(true), m_applySharedSettings(true), m_columnWidthModeChanged(false), m_disableColumnWidthUpdates(true), m_time(0), m_widthsDirty(true), m_searchEnabled(true), m_fileName(playlistFile.canonicalFilePath()), m_rmbMenu(0), m_toolTip(0), m_blockDataChanged(false) { setup(); loadFile(m_fileName, playlistFile); collection->setupPlaylist(this, iconName); } Playlist::Playlist(PlaylistCollection *collection, bool delaySetup, int extraColumns) : QTreeWidget(collection->playlistStack()), m_collection(collection), m_fetcher(new WebImageFetcher(this)), m_allowDuplicates(true), m_applySharedSettings(true), m_columnWidthModeChanged(false), m_disableColumnWidthUpdates(true), m_time(0), m_widthsDirty(true), m_searchEnabled(true), m_rmbMenu(0), m_toolTip(0), m_blockDataChanged(false) { for(int i = 0; i < extraColumns; ++i) { addColumn(i18n("JuK")); // Placeholder text! } setup(); if(!delaySetup) collection->setupPlaylist(this, "audio-midi"); } Playlist::~Playlist() { // In some situations the dataChanged signal from clearItems will cause observers to // subsequently try to access a deleted item. Since we're going away just remove all // observers. clearObservers(); // clearItem() will take care of removing the items from the history, // so call clearItems() to make sure it happens. clearItems(items()); /* delete m_toolTip; */ if(!m_shuttingDown) m_collection->removePlaylist(this); } QString Playlist::name() const { if(m_playlistName.isEmpty()) return m_fileName.section(QDir::separator(), -1).section('.', 0, -2); else return m_playlistName; } FileHandle Playlist::currentFile() const { return playingItem() ? playingItem()->file() : FileHandle(); } int Playlist::time() const { // Since this method gets a lot of traffic, let's optimize for such. if(!m_addTime.isEmpty()) { foreach(const PlaylistItem *item, m_addTime) { if(item) m_time += item->file().tag()->seconds(); } m_addTime.clear(); } if(!m_subtractTime.isEmpty()) { foreach(const PlaylistItem *item, m_subtractTime) { if(item) m_time -= item->file().tag()->seconds(); } m_subtractTime.clear(); } return m_time; } void Playlist::playFirst() { TrackSequenceManager::instance()->setNextItem(static_cast( *QTreeWidgetItemIterator(const_cast(this), QTreeWidgetItemIterator::NotHidden))); action("forward")->trigger(); } void Playlist::playNextAlbum() { PlaylistItem *current = TrackSequenceManager::instance()->currentItem(); if(!current) return; // No next album if we're not already playing. QString currentAlbum = current->file().tag()->album(); current = TrackSequenceManager::instance()->nextItem(); while(current && current->file().tag()->album() == currentAlbum) current = TrackSequenceManager::instance()->nextItem(); TrackSequenceManager::instance()->setNextItem(current); action("forward")->trigger(); } void Playlist::playNext() { TrackSequenceManager::instance()->setCurrentPlaylist(this); setPlaying(TrackSequenceManager::instance()->nextItem()); } void Playlist::stop() { m_history.clear(); setPlaying(nullptr); } void Playlist::playPrevious() { if(!playingItem()) return; bool random = action("randomPlay") && action("randomPlay")->isChecked(); PlaylistItem *previous = 0; if(random && !m_history.isEmpty()) { PlaylistItemList::Iterator last = --m_history.end(); previous = *last; m_history.erase(last); } else { m_history.clear(); previous = TrackSequenceManager::instance()->previousItem(); } if(!previous) previous = static_cast(playingItem()->itemAbove()); setPlaying(previous, false); } void Playlist::setName(const QString &n) { m_collection->addNameToDict(n); m_collection->removeNameFromDict(m_playlistName); m_playlistName = n; emit signalNameChanged(m_playlistName); } void Playlist::save() { if(m_fileName.isEmpty()) return saveAs(); QFile file(m_fileName); if(!file.open(QIODevice::WriteOnly)) return KMessageBox::error(this, i18n("Could not save to file %1.", m_fileName)); QTextStream stream(&file); QStringList fileList = files(); foreach(const QString &file, fileList) stream << file << endl; file.close(); } void Playlist::saveAs() { m_collection->removeFileFromDict(m_fileName); m_fileName = MediaFiles::savePlaylistDialog(name(), this); if(!m_fileName.isEmpty()) { m_collection->addFileToDict(m_fileName); // If there's no playlist name set, use the file name. if(m_playlistName.isEmpty()) emit signalNameChanged(name()); save(); } } void Playlist::updateDeletedItem(PlaylistItem *item) { m_members.remove(item->file().absFilePath()); m_search.clearItem(item); m_history.removeAll(item); m_addTime.removeAll(item); m_subtractTime.removeAll(item); } void Playlist::clearItem(PlaylistItem *item) { // Automatically updates internal structs via updateDeletedItem delete item; playlistItemsChanged(); } void Playlist::clearItems(const PlaylistItemList &items) { foreach(PlaylistItem *item, items) delete item; playlistItemsChanged(); } PlaylistItem *Playlist::playingItem() // static { return PlaylistItem::playingItems().isEmpty() ? 0 : PlaylistItem::playingItems().front(); } QStringList Playlist::files() const { QStringList list; for(QTreeWidgetItemIterator it(const_cast(this)); *it; ++it) list.append(static_cast(*it)->file().absFilePath()); return list; } PlaylistItemList Playlist::items() { return items(QTreeWidgetItemIterator::IteratorFlag(0)); } PlaylistItemList Playlist::visibleItems() { return items(QTreeWidgetItemIterator::NotHidden); } PlaylistItemList Playlist::selectedItems() { return items(QTreeWidgetItemIterator::Selected | QTreeWidgetItemIterator::NotHidden); } PlaylistItem *Playlist::firstChild() const { return static_cast(topLevelItem(0)); } void Playlist::updateLeftColumn() { int newLeftColumn = leftMostVisibleColumn(); if(m_leftColumn != newLeftColumn) { updatePlaying(); m_leftColumn = newLeftColumn; } } void Playlist::setItemsVisible(const PlaylistItemList &items, bool visible) // static { m_visibleChanged = true; foreach(PlaylistItem *playlistItem, items) playlistItem->setHidden(!visible); } void Playlist::setSearch(const PlaylistSearch &s) { m_search = s; if(!m_searchEnabled) return; setItemsVisible(s.matchedItems(), true); setItemsVisible(s.unmatchedItems(), false); TrackSequenceManager::instance()->iterator()->playlistChanged(); } void Playlist::setSearchEnabled(bool enabled) { if(m_searchEnabled == enabled) return; m_searchEnabled = enabled; if(enabled) { setItemsVisible(m_search.matchedItems(), true); setItemsVisible(m_search.unmatchedItems(), false); } else setItemsVisible(items(), true); } // Mostly seems to be for DynamicPlaylist // TODO: See if this can't all be eliminated by making 'is-playing' a predicate // of the playlist item itself void Playlist::synchronizePlayingItems(const PlaylistList &sources, bool setMaster) { foreach(const Playlist *p, sources) { if(p->playing()) { CollectionListItem *base = playingItem()->collectionItem(); for(QTreeWidgetItemIterator itemIt(this); *itemIt; ++itemIt) { PlaylistItem *item = static_cast(*itemIt); if(base == item->collectionItem()) { item->setPlaying(true, setMaster); PlaylistItemList playing = PlaylistItem::playingItems(); TrackSequenceManager::instance()->setCurrent(item); return; } } return; } } } //////////////////////////////////////////////////////////////////////////////// // public slots //////////////////////////////////////////////////////////////////////////////// void Playlist::copy() { PlaylistItemList items = selectedItems(); QList urls; foreach(PlaylistItem *item, items) { urls << QUrl::fromLocalFile(item->file().absFilePath()); } QMimeData *mimeData = new QMimeData; mimeData->setUrls(urls); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); } void Playlist::paste() { decode(QApplication::clipboard()->mimeData(), static_cast(currentItem())); } void Playlist::clear() { PlaylistItemList l = selectedItems(); if(l.isEmpty()) l = items(); clearItems(l); } void Playlist::slotRefresh() { PlaylistItemList l = selectedItems(); if(l.isEmpty()) l = visibleItems(); QApplication::setOverrideCursor(Qt::WaitCursor); foreach(PlaylistItem *item, l) { item->refreshFromDisk(); if(!item->file().tag() || !item->file().fileInfo().exists()) { qCDebug(JUK_LOG) << "Error while trying to refresh the tag. " << "This file has probably been removed."; delete item->collectionItem(); } processEvents(); } QApplication::restoreOverrideCursor(); } void Playlist::slotRenameFile() { FileRenamer renamer; PlaylistItemList items = selectedItems(); if(items.isEmpty()) return; emit signalEnableDirWatch(false); m_blockDataChanged = true; renamer.rename(items); m_blockDataChanged = false; playlistItemsChanged(); emit signalEnableDirWatch(true); } void Playlist::slotViewCover() { const PlaylistItemList items = selectedItems(); if (items.isEmpty()) return; foreach(const PlaylistItem *item, items) item->file().coverInfo()->popup(); } void Playlist::slotRemoveCover() { PlaylistItemList items = selectedItems(); if(items.isEmpty()) return; int button = KMessageBox::warningContinueCancel(this, i18n("Are you sure you want to delete these covers?"), QString(), KGuiItem(i18n("&Delete Covers"))); if(button == KMessageBox::Continue) refreshAlbums(items); } void Playlist::slotShowCoverManager() { static CoverDialog *managerDialog = 0; if(!managerDialog) managerDialog = new CoverDialog(this); managerDialog->show(); } void Playlist::slotAddCover(bool retrieveLocal) { PlaylistItemList items = selectedItems(); if(items.isEmpty()) return; if(!retrieveLocal) { m_fetcher->setFile((*items.begin())->file()); m_fetcher->searchCover(); return; } QUrl file = QFileDialog::getOpenFileUrl( this, i18n("Select Cover Image File"), QUrl::fromLocalFile(QDir::home().path()), i18n("Images (*.png *.jpg)"), nullptr, 0, QStringList() << QStringLiteral("file") ); if(file.isEmpty()) return; QString artist = items.front()->file().tag()->artist(); QString album = items.front()->file().tag()->album(); coverKey newId = CoverManager::addCover(file, artist, album); if(newId != CoverManager::NoMatch) refreshAlbums(items, newId); } // Called when image fetcher has added a new cover. void Playlist::slotCoverChanged(int coverId) { qCDebug(JUK_LOG) << "Refreshing information for newly changed covers.\n"; refreshAlbums(selectedItems(), coverId); } void Playlist::slotGuessTagInfo(TagGuesser::Type type) { QApplication::setOverrideCursor(Qt::WaitCursor); const PlaylistItemList items = selectedItems(); setDynamicListsFrozen(true); m_blockDataChanged = true; foreach(PlaylistItem *item, items) { item->guessTagInfo(type); processEvents(); } // MusicBrainz queries automatically commit at this point. What would // be nice is having a signal emitted when the last query is completed. if(type == TagGuesser::FileName) TagTransactionManager::instance()->commit(); m_blockDataChanged = false; playlistItemsChanged(); setDynamicListsFrozen(false); QApplication::restoreOverrideCursor(); } void Playlist::slotReload() { QFileInfo fileInfo(m_fileName); if(!fileInfo.exists() || !fileInfo.isFile() || !fileInfo.isReadable()) return; clearItems(items()); loadFile(m_fileName, fileInfo); } void Playlist::slotWeightDirty(int column) { if(column < 0) { m_weightDirty.clear(); for(int i = 0; i < columnCount(); i++) { if(!isColumnHidden(i)) m_weightDirty.append(i); } return; } if(!m_weightDirty.contains(column)) m_weightDirty.append(column); } void Playlist::slotShowPlaying() { if(!playingItem()) return; Playlist *l = playingItem()->playlist(); l->clearSelection(); // Raise the playlist before selecting the items otherwise the tag editor // will not update when it gets the selectionChanged() notification // because it will think the user is choosing a different playlist but not // selecting a different item. m_collection->raise(l); l->setCurrentItem(playingItem()); l->scrollToItem(playingItem()); } void Playlist::slotColumnResizeModeChanged() { if(manualResize()) { header()->setSectionResizeMode(QHeaderView::Interactive); setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } else { header()->setSectionResizeMode(QHeaderView::Fixed); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } if(!manualResize()) slotUpdateColumnWidths(); SharedSettings::instance()->sync(); } void Playlist::playlistItemsChanged() { if(m_blockDataChanged) return; PlaylistInterface::playlistItemsChanged(); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void Playlist::removeFromDisk(const PlaylistItemList &items) { if(isVisible() && !items.isEmpty()) { QStringList files; foreach(const PlaylistItem *item, items) files.append(item->file().absFilePath()); DeleteDialog dialog(this); m_blockDataChanged = true; if(dialog.confirmDeleteList(files)) { bool shouldDelete = dialog.shouldDelete(); QStringList errorFiles; foreach(PlaylistItem *item, items) { if(playingItem() == item) action("forward")->trigger(); QString removePath = item->file().absFilePath(); QUrl removeUrl = QUrl::fromLocalFile(removePath); if((!shouldDelete && KIO::trash(removeUrl)->exec()) || (shouldDelete && QFile::remove(removePath))) { delete item->collectionItem(); } else errorFiles.append(item->file().absFilePath()); } if(!errorFiles.isEmpty()) { QString errorMsg = shouldDelete ? i18n("Could not delete these files") : i18n("Could not move these files to the Trash"); KMessageBox::errorList(this, errorMsg, errorFiles); } } m_blockDataChanged = false; playlistItemsChanged(); } } void Playlist::synchronizeItemsTo(const PlaylistItemList &itemList) { const auto &existingItems = items(); if(qAbs(itemList.count() - existingItems.count()) > qMax(itemList.count(), existingItems.count()) / 2) { // Large imbalance in list sizes, just clear all and add without // figuring out the diff also clearItems(existingItems); createItems(itemList); return; } // Determine differences between existing playlist items and patch up QHash oldItems; oldItems.reserve(qMax(existingItems.count(), itemList.count())); for(const auto &item : existingItems) { oldItems.insert(item->collectionItem(), item); } PlaylistItemList newItems; for(const auto &item : itemList) { if(oldItems.remove(item->collectionItem()) == 0) { newItems.append(item->collectionItem()); } } clearItems(PlaylistItemList(oldItems.values())); createItems(newItems); } void Playlist::dragEnterEvent(QDragEnterEvent *e) { if(CoverDrag::isCover(e->mimeData())) { //setDropHighlighter(true); setDropIndicatorShown(false); e->accept(); return; } setDropIndicatorShown(true); if(e->mimeData()->hasUrls() && !e->mimeData()->urls().isEmpty()) e->acceptProposedAction(); else e->ignore(); } bool Playlist::acceptDrag(QDropEvent *e) const { return CoverDrag::isCover(e->mimeData()) || e->mimeData()->hasUrls(); } void Playlist::decode(const QMimeData *s, PlaylistItem *item) { Q_UNUSED(s); Q_UNUSED(item); // TODO Re-add drag-drop } bool Playlist::eventFilter(QObject *watched, QEvent *e) { if(watched == header()) { switch(e->type()) { case QEvent::MouseMove: { if((static_cast(e)->modifiers() & Qt::LeftButton) == Qt::LeftButton && !action("resizeColumnsManually")->isChecked()) { m_columnWidthModeChanged = true; action("resizeColumnsManually")->setChecked(true); slotColumnResizeModeChanged(); } break; } case QEvent::MouseButtonPress: { if(static_cast(e)->button() == Qt::RightButton) m_headerMenu->popup(QCursor::pos()); break; } case QEvent::MouseButtonRelease: { if(m_columnWidthModeChanged) { m_columnWidthModeChanged = false; notifyUserColumnWidthModeChanged(); } if(!manualResize() && m_widthsDirty) QTimer::singleShot(0, this, SLOT(slotUpdateColumnWidths())); break; } default: break; } } return QTreeWidget::eventFilter(watched, e); } void Playlist::keyPressEvent(QKeyEvent *event) { if(event->key() == Qt::Key_Up) { using ::operator|; QTreeWidgetItemIterator selected(this, QTreeWidgetItemIterator::Selected | QTreeWidgetItemIterator::NotHidden); if(*selected) { QTreeWidgetItemIterator visible(this, QTreeWidgetItemIterator::NotHidden); if(*selected == *visible) QApplication::postEvent(parent(), new FocusUpEvent); } } else if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { slotPlayCurrent(); } QTreeWidget::keyPressEvent(event); } QStringList Playlist::mimeTypes() const { return QStringList("text/uri-list"); } QMimeData* Playlist::mimeData(const QList items) const { QList urls; foreach(QTreeWidgetItem *item, items) { urls << QUrl::fromLocalFile(static_cast(item)->file().absFilePath()); } QMimeData *urlDrag = new QMimeData(); urlDrag->setUrls(urls); return urlDrag; } bool Playlist::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) { // TODO: Re-add DND Q_UNUSED(parent); Q_UNUSED(index); Q_UNUSED(data); Q_UNUSED(action); return false; } void Playlist::dropEvent(QDropEvent *e) { QPoint vp = e->pos(); PlaylistItem *item = static_cast(itemAt(vp)); // First see if we're dropping a cover, if so we can get it out of the // way early. if(item && CoverDrag::isCover(e->mimeData())) { coverKey id = CoverDrag::idFromData(e->mimeData()); // If the item we dropped on is selected, apply cover to all selected // items, otherwise just apply to the dropped item. if(item->isSelected()) { const PlaylistItemList selItems = selectedItems(); foreach(PlaylistItem *playlistItem, selItems) { playlistItem->file().coverInfo()->setCoverId(id); playlistItem->refresh(); } } else { item->file().coverInfo()->setCoverId(id); item->refresh(); } return; } // When dropping on the toUpper half of an item, insert before this item. // This is what the user expects, and also allows the insertion at // top of the list QRect rect = visualItemRect(item); if(!item) item = static_cast(topLevelItem(topLevelItemCount() - 1)); else if(vp.y() < rect.y() + rect.height() / 2) item = static_cast(item->itemAbove()); m_blockDataChanged = true; if(e->source() == this) { // Since we're trying to arrange things manually, turn off sorting. sortItems(columnCount() + 1, Qt::AscendingOrder); const QList items = QTreeWidget::selectedItems(); foreach(QTreeWidgetItem *listViewItem, items) { if(!item) { // Insert the item at the top of the list. This is a bit ugly, // but I don't see another way. takeItem(listViewItem); insertItem(listViewItem); } //else // listViewItem->moveItem(item); item = static_cast(listViewItem); } } else decode(e->mimeData(), item); m_blockDataChanged = false; playlistItemsChanged(); emit signalPlaylistItemsDropped(this); QTreeWidget::dropEvent(e); } void Playlist::showEvent(QShowEvent *e) { if(m_applySharedSettings) { SharedSettings::instance()->apply(this); m_applySharedSettings = false; } QTreeWidget::showEvent(e); } void Playlist::applySharedSettings() { m_applySharedSettings = true; } void Playlist::read(QDataStream &s) { s >> m_playlistName >> m_fileName; // m_fileName is probably empty. if(m_playlistName.isEmpty()) throw BICStreamException(); // Do not sort. Add the files in the order they were saved. setSortingEnabled(false); QStringList files; s >> files; QTreeWidgetItem *after = 0; m_blockDataChanged = true; foreach(const QString &file, files) { if(file.isEmpty()) throw BICStreamException(); after = createItem(FileHandle(file), after); } m_blockDataChanged = false; playlistItemsChanged(); m_collection->setupPlaylist(this, "audio-midi"); } void Playlist::paintEvent(QPaintEvent *pe) { // If there are columns that need to be updated, well, update them. if(!m_weightDirty.isEmpty() && !manualResize()) { calculateColumnWeights(); slotUpdateColumnWidths(); } QTreeWidget::paintEvent(pe); } void Playlist::resizeEvent(QResizeEvent *re) { // If the width of the view has changed, manually update the column // widths. if(re->size().width() != re->oldSize().width() && !manualResize()) slotUpdateColumnWidths(); QTreeWidget::resizeEvent(re); } // Reimplemented to show a visual indication of which of the view's playlist // items is actually playing. void Playlist::drawRow(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { PlaylistItem *item = static_cast(itemFromIndex(index)); if(Q_LIKELY(!PlaylistItem::playingItems().contains(item))) { return QTreeWidget::drawRow(p, option, index); } // Seems that the view draws the background now so we have to do this // manually p->fillRect(option.rect, QPalette{}.midlight()); QStyleOptionViewItem newOption {option}; newOption.font.setBold(true); QTreeWidget::drawRow(p, newOption, index); } void Playlist::insertItem(QTreeWidgetItem *item) { // Because we're called from the PlaylistItem ctor, item may not be a // PlaylistItem yet (it would be QListViewItem when being inserted. But, // it will be a PlaylistItem by the time it matters, but be careful if // you need to use the PlaylistItem from here. m_addTime.append(static_cast(item)); QTreeWidget::insertTopLevelItem(0, item); } void Playlist::takeItem(QTreeWidgetItem *item) { // See the warning in Playlist::insertItem. m_subtractTime.append(static_cast(item)); int index = indexOfTopLevelItem(item); QTreeWidget::takeTopLevelItem(index); } void Playlist::addColumn(const QString &label, int) { m_columns.append(label); setHeaderLabels(m_columns); } PlaylistItem *Playlist::createItem(const FileHandle &file, QTreeWidgetItem *after) { return createItem(file, after); } void Playlist::createItems(const PlaylistItemList &siblings, PlaylistItem *after) { createItems(siblings, after); } void Playlist::addFiles(const QStringList &files, PlaylistItem *after) { if(!after) after = static_cast(topLevelItem(topLevelItemCount() - 1)); QApplication::setOverrideCursor(Qt::WaitCursor); m_blockDataChanged = true; FileHandleList queue; foreach(const QString &file, files) addFile(file, queue, true, &after); addFileHelper(queue, &after, true); m_blockDataChanged = false; slotWeightDirty(); playlistItemsChanged(); QApplication::restoreOverrideCursor(); } void Playlist::refreshAlbums(const PlaylistItemList &items, coverKey id) { QList< QPair > albums; bool setAlbumCovers = items.count() == 1; foreach(const PlaylistItem *item, items) { QString artist = item->file().tag()->artist(); QString album = item->file().tag()->album(); if(!albums.contains(qMakePair(artist, album))) albums.append(qMakePair(artist, album)); item->file().coverInfo()->setCoverId(id); if(setAlbumCovers) item->file().coverInfo()->applyCoverToWholeAlbum(true); } for(QList< QPair >::ConstIterator it = albums.constBegin(); it != albums.constEnd(); ++it) { refreshAlbum((*it).first, (*it).second); } } void Playlist::updatePlaying() const { foreach(const PlaylistItem *item, PlaylistItem::playingItems()) item->treeWidget()->viewport()->update(); } void Playlist::refreshAlbum(const QString &artist, const QString &album) { ColumnList columns; columns.append(PlaylistItem::ArtistColumn); PlaylistSearch::Component artistComponent(artist, false, columns, PlaylistSearch::Component::Exact); columns.clear(); columns.append(PlaylistItem::AlbumColumn); PlaylistSearch::Component albumComponent(album, false, columns, PlaylistSearch::Component::Exact); PlaylistSearch::ComponentList components; components.append(artist); components.append(album); PlaylistList playlists; playlists.append(CollectionList::instance()); PlaylistSearch search(playlists, components); const PlaylistItemList matches = search.matchedItems(); foreach(PlaylistItem *item, matches) item->refresh(); } void Playlist::hideColumn(int c, bool updateSearch) { foreach (QAction *action, m_headerMenu->actions()) { if(!action) continue; if (action->data().toInt() == c) { action->setChecked(false); break; } } if(isColumnHidden(c)) return; QTreeWidget::hideColumn(c); if(c == m_leftColumn) { updatePlaying(); m_leftColumn = leftMostVisibleColumn(); } if(!manualResize()) { slotUpdateColumnWidths(); viewport()->update(); } if(this != CollectionList::instance()) CollectionList::instance()->hideColumn(c, false); if(updateSearch) redisplaySearch(); } void Playlist::showColumn(int c, bool updateSearch) { foreach (QAction *action, m_headerMenu->actions()) { if(!action) continue; if (action->data().toInt() == c) { action->setChecked(true); break; } } if(!isColumnHidden(c)) return; QTreeWidget::showColumn(c); if(c == leftMostVisibleColumn()) { updatePlaying(); m_leftColumn = leftMostVisibleColumn(); } if(!manualResize()) { slotUpdateColumnWidths(); viewport()->update(); } if(this != CollectionList::instance()) CollectionList::instance()->showColumn(c, false); if(updateSearch) redisplaySearch(); } void Playlist::sortByColumn(int column, Qt::SortOrder order) { setSortingEnabled(true); QTreeWidget::sortByColumn(column, order); } void Playlist::slotInitialize() { addColumn(i18n("Track Name")); addColumn(i18n("Artist")); addColumn(i18n("Album")); addColumn(i18n("Cover")); addColumn(i18nc("cd track number", "Track")); addColumn(i18n("Genre")); addColumn(i18n("Year")); addColumn(i18n("Length")); addColumn(i18n("Bitrate")); addColumn(i18n("Comment")); addColumn(i18n("File Name")); addColumn(i18n("File Name (full path)")); setAllColumnsShowFocus(true); setSelectionMode(QTreeWidget::ExtendedSelection); header()->setSortIndicatorShown(true); m_columnFixedWidths.resize(columnCount()); ////////////////////////////////////////////////// // setup header RMB menu ////////////////////////////////////////////////// QAction *showAction; for(int i = 0; i < header()->count(); ++i) { if(i - columnOffset() == PlaylistItem::FileNameColumn) m_headerMenu->addSeparator(); showAction = new QAction(headerItem()->text(i), m_headerMenu); showAction->setData(i); showAction->setCheckable(true); showAction->setChecked(!isColumnHidden(i)); m_headerMenu->addAction(showAction); resizeColumnToContents(i); } connect(m_headerMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotToggleColumnVisible(QAction*))); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotShowRMBMenu(QPoint))); connect(this, &QTreeWidget::itemChanged, this, &Playlist::slotInlineEditDone); connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotPlayCurrent())); connect(action("resizeColumnsManually"), SIGNAL(triggered()), this, SLOT(slotColumnResizeModeChanged())); if(action("resizeColumnsManually")->isChecked()) { header()->setSectionResizeMode(QHeaderView::Interactive); setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } else { header()->setSectionResizeMode(QHeaderView::Fixed); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } viewport()->setAcceptDrops(true); setDropIndicatorShown(true); setDragEnabled(true); m_disableColumnWidthUpdates = false; } void Playlist::setupItem(PlaylistItem *item) { item->setTrackId(g_trackID); g_trackID++; if(!m_search.isEmpty()) item->setHidden(!m_search.checkItem(item)); if(topLevelItemCount() <= 2 && !manualResize()) { slotWeightDirty(); slotUpdateColumnWidths(); viewport()->update(); } } void Playlist::setDynamicListsFrozen(bool frozen) { m_collection->setDynamicListsFrozen(frozen); } CollectionListItem *Playlist::collectionListItem(const FileHandle &file) { CollectionListItem *item = CollectionList::instance()->lookup(file.absFilePath()); if(!item) { if(!QFile::exists(file.absFilePath())) { qCCritical(JUK_LOG) << "File" << file.absFilePath() << "does not exist."; return nullptr; } item = CollectionList::instance()->createItem(file); } return item; } //////////////////////////////////////////////////////////////////////////////// // protected slots //////////////////////////////////////////////////////////////////////////////// void Playlist::slotPopulateBackMenu() const { if(!playingItem()) return; QMenu *menu = action("back")->menu(); menu->clear(); m_backMenuItems.clear(); m_backMenuItems.reserve(10); int count = 0; PlaylistItemList::ConstIterator it = m_history.constEnd(); QAction *action; while(it != m_history.constBegin() && count < 10) { ++count; --it; action = new QAction((*it)->file().tag()->title(), menu); action->setData(count - 1); menu->addAction(action); m_backMenuItems << *it; } } void Playlist::slotPlayFromBackMenu(QAction *backAction) const { int number = backAction->data().toInt(); if(number >= m_backMenuItems.size()) return; TrackSequenceManager::instance()->setNextItem(m_backMenuItems[number]); action("forward")->trigger(); } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Playlist::setup() { setRootIsDecorated(false); setContextMenuPolicy(Qt::CustomContextMenu); setUniformRowHeights(true); setEditTriggers(QAbstractItemView::EditKeyPressed); // Don't edit on double-click connect(header(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(slotColumnOrderChanged(int,int,int))); connect(m_fetcher, SIGNAL(signalCoverChanged(int)), this, SLOT(slotCoverChanged(int))); // Prevent list of selected items from changing while internet search is in // progress. connect(this, SIGNAL(itemSelectionChanged()), m_fetcher, SLOT(abortSearch())); sortByColumn(1, Qt::AscendingOrder); // This apparently must be created very early in initialization for other // Playlist code requiring m_headerMenu. m_columnVisibleAction = new KActionMenu(i18n("&Show Columns"), this); ActionCollection::actions()->addAction("showColumns", m_columnVisibleAction); m_headerMenu = m_columnVisibleAction->menu(); header()->installEventFilter(this); // TODO: Determine if other stuff in setup must happen before slotInitialize(). // Explicitly call slotInitialize() so that the columns are added before // SharedSettings::apply() sets the visible and hidden ones. slotInitialize(); } void Playlist::loadFile(const QString &fileName, const QFileInfo &fileInfo) { QFile file(fileName); if(!file.open(QIODevice::ReadOnly)) return; QTextStream stream(&file); // Turn off non-explicit sorting. setSortingEnabled(false); PlaylistItem *after = nullptr; m_disableColumnWidthUpdates = true; m_blockDataChanged = true; while(!stream.atEnd()) { QString itemName = stream.readLine().trimmed(); QFileInfo item(itemName); if(item.isRelative()) item.setFile(QDir::cleanPath(fileInfo.absolutePath() + '/' + itemName)); if(item.exists() && item.isFile() && item.isReadable() && MediaFiles::isMediaFile(item.fileName())) { after = createItem(FileHandle(item), after); } } m_blockDataChanged = false; m_disableColumnWidthUpdates = false; file.close(); playlistItemsChanged(); } void Playlist::setPlaying(PlaylistItem *item, bool addToHistory) { if(playingItem() == item) return; if(playingItem()) { if(addToHistory) { if(playingItem()->playlist() == playingItem()->playlist()->m_collection->upcomingPlaylist()) m_history.append(playingItem()->collectionItem()); else m_history.append(playingItem()); } playingItem()->setPlaying(false); } TrackSequenceManager::instance()->setCurrent(item); // TODO is this replaced by MPRIS2? //kapp->dcopClient()->emitDCOPSignal("Player", "trackChanged()", data); if(!item) return; item->setPlaying(true); bool enableBack = !m_history.isEmpty(); action("back")->menu()->setEnabled(enableBack); } bool Playlist::playing() const { return playingItem() && this == playingItem()->playlist(); } int Playlist::leftMostVisibleColumn() const { int i = 0; while(i < PlaylistItem::lastColumn() && isColumnHidden(i)) i++; return i < PlaylistItem::lastColumn() ? i : 0; } PlaylistItemList Playlist::items(QTreeWidgetItemIterator::IteratorFlags flags) { PlaylistItemList list; for(QTreeWidgetItemIterator it(this, flags); *it; ++it) list.append(static_cast(*it)); return list; } void Playlist::calculateColumnWeights() { if(m_disableColumnWidthUpdates) return; PlaylistItemList l = items(); QList::Iterator columnIt; QVector averageWidth(columnCount()); double itemCount = l.size(); QVector cachedWidth; // Here we're not using a real average, but averaging the squares of the // column widths and then using the square root of that value. This gives // a nice weighting to the longer columns without doing something arbitrary // like adding a fixed amount of padding. foreach(PlaylistItem *item, l) { cachedWidth = item->cachedWidths(); // Extra columns start at 0, but those weights aren't shared with all // items. for(int i = 0; i < columnOffset(); ++i) { averageWidth[i] += std::pow(double(columnWidth(i)), 2.0) / itemCount; } for(int column = columnOffset(); column < columnCount(); ++column) { averageWidth[column] += std::pow(double(cachedWidth[column - columnOffset()]), 2.0) / itemCount; } } if(m_columnWeights.isEmpty()) m_columnWeights.fill(-1, columnCount()); foreach(int column, m_weightDirty) { m_columnWeights[column] = int(std::sqrt(averageWidth[column]) + 0.5); } m_weightDirty.clear(); } void Playlist::addFile(const QString &file, FileHandleList &files, bool importPlaylists, PlaylistItem **after) { if(hasItem(file) && !m_allowDuplicates) return; addFileHelper(files, after); // Our biggest thing that we're fighting during startup is too many stats // of files. Make sure that we don't do one here if it's not needed. const CollectionListItem *item = CollectionList::instance()->lookup(file); if(item && !item->file().isNull()) { FileHandle cached(item->file()); cached.tag(); files.append(cached); return; } const QFileInfo fileInfo(QDir::cleanPath(file)); if(!fileInfo.exists()) return; const QString canonicalPath = fileInfo.canonicalFilePath(); if(fileInfo.isFile() && fileInfo.isReadable()) { if(MediaFiles::isMediaFile(file)) { FileHandle f(fileInfo); f.tag(); files.append(f); } } if(importPlaylists && MediaFiles::isPlaylistFile(file) && !m_collection->containsPlaylistFile(canonicalPath)) { new Playlist(m_collection, fileInfo); return; } if(fileInfo.isDir()) { foreach(const QString &directory, m_collection->excludedFolders()) { if(canonicalPath.startsWith(directory)) return; // Exclude it } QDirIterator dirIterator(canonicalPath, QDir::AllEntries | QDir::NoDotAndDotDot); while(dirIterator.hasNext()) { // We set importPlaylists to the value from the add directories // dialog as we want to load all of the ones that the user has // explicitly asked for, but not those that we find in lower // directories. addFile(dirIterator.next(), files, m_collection->importPlaylists(), after); } } } void Playlist::addFileHelper(FileHandleList &files, PlaylistItem **after, bool ignoreTimer) { static QTime time = QTime::currentTime(); // Process new items every 10 seconds, when we've loaded 1000 items, or when // it's been requested in the API. if(ignoreTimer || time.elapsed() > 10000 || (files.count() >= 1000 && time.elapsed() > 1000)) { time.restart(); const bool focus = hasFocus(); const bool visible = isVisible() && files.count() > 20; if(visible) m_collection->raiseDistraction(); PlaylistItem *newAfter = *after; foreach(const FileHandle &fileHandle, files) newAfter = createItem(fileHandle, newAfter); *after = newAfter; files.clear(); if(visible) m_collection->lowerDistraction(); if(focus) setFocus(); } } //////////////////////////////////////////////////////////////////////////////// // private slots //////////////////////////////////////////////////////////////////////////////// void Playlist::slotUpdateColumnWidths() { if(m_disableColumnWidthUpdates || manualResize()) return; // Make sure that the column weights have been initialized before trying to // update the columns. QList visibleColumns; for(int i = 0; i < columnCount(); i++) { if(!isColumnHidden(i)) visibleColumns.append(i); } if(count() == 0) { foreach(int column, visibleColumns) setColumnWidth(column, header()->fontMetrics().width(headerItem()->text(column)) + 10); return; } if(m_columnWeights.isEmpty()) return; // First build a list of minimum widths based on the strings in the listview // header. We won't let the width of the column go below this width. QVector minimumWidth(columnCount(), 0); int minimumWidthTotal = 0; // Also build a list of either the minimum *or* the fixed width -- whichever is // greater. QVector minimumFixedWidth(columnCount(), 0); int minimumFixedWidthTotal = 0; foreach(int column, visibleColumns) { minimumWidth[column] = header()->fontMetrics().width(headerItem()->text(column)) + 10; minimumWidthTotal += minimumWidth[column]; minimumFixedWidth[column] = qMax(minimumWidth[column], m_columnFixedWidths[column]); minimumFixedWidthTotal += minimumFixedWidth[column]; } // Make sure that the width won't get any smaller than this. We have to // account for the scrollbar as well. Since this method is called from the // resize event this will set a pretty hard toLower bound on the size. setMinimumWidth(minimumWidthTotal + verticalScrollBar()->width()); // If we've got enough room for the fixed widths (larger than the minimum // widths) then instead use those for our "minimum widths". if(minimumFixedWidthTotal < viewport()->width()) { minimumWidth = minimumFixedWidth; minimumWidthTotal = minimumFixedWidthTotal; } // We've got a list of columns "weights" based on some statistics gathered // about the widths of the items in that column. We need to find the total // useful weight to use as a divisor for each column's weight. double totalWeight = 0; foreach(int column, visibleColumns) totalWeight += m_columnWeights[column]; // Computed a "weighted width" for each visible column. This would be the // width if we didn't have to handle the cases of minimum and maximum widths. QVector weightedWidth(columnCount(), 0); foreach(int column, visibleColumns) weightedWidth[column] = int(double(m_columnWeights[column]) / totalWeight * viewport()->width() + 0.5); // The "extra" width for each column. This is the weighted width less the // minimum width or zero if the minimum width is greater than the weighted // width. QVector extraWidth(columnCount(), 0); // This is used as an indicator if we have any columns where the weighted // width is less than the minimum width. If this is false then we can // just use the weighted width with no problems, otherwise we have to // "readjust" the widths. bool readjust = false; // If we have columns where the weighted width is less than the minimum width // we need to steal that space from somewhere. The amount that we need to // steal is the "neededWidth". int neededWidth = 0; // While we're on the topic of stealing -- we have to have somewhere to steal // from. availableWidth is the sum of the amount of space beyond the minimum // width that each column has been allocated -- the sum of the values of // extraWidth[]. int availableWidth = 0; // Fill in the values discussed above. foreach(int column, visibleColumns) { if(weightedWidth[column] < minimumWidth[column]) { readjust = true; extraWidth[column] = 0; neededWidth += minimumWidth[column] - weightedWidth[column]; } else { extraWidth[column] = weightedWidth[column] - minimumWidth[column]; availableWidth += extraWidth[column]; } } // The adjustmentRatio is the amount of the "extraWidth[]" that columns will // actually be given. double adjustmentRatio = (double(availableWidth) - double(neededWidth)) / double(availableWidth); // This will be the sum of the total space that we actually use. Because of // rounding error this won't be the exact available width. int usedWidth = 0; // Now set the actual column widths. If the weighted widths are all greater - // than the minimum widths, just use those, otherwise use the "reajusted + // than the minimum widths, just use those, otherwise use the "readjusted // weighted width". foreach(int column, visibleColumns) { int width; if(readjust) { int adjustedExtraWidth = int(double(extraWidth[column]) * adjustmentRatio + 0.5); width = minimumWidth[column] + adjustedExtraWidth; } else width = weightedWidth[column]; setColumnWidth(column, width); usedWidth += width; } // Fill the remaining gap for a clean fit into the available space. int remainingWidth = viewport()->width() - usedWidth; setColumnWidth(visibleColumns.back(), columnWidth(visibleColumns.back()) + remainingWidth); m_widthsDirty = false; } void Playlist::slotAddToUpcoming() { m_collection->setUpcomingPlaylistEnabled(true); m_collection->upcomingPlaylist()->appendItems(selectedItems()); } void Playlist::slotShowRMBMenu(const QPoint &point) { QTreeWidgetItem *item = itemAt(point); int column = columnAt(point.x()); if(!item) return; // Create the RMB menu on demand. if(!m_rmbMenu) { // Probably more of these actions should be ported over to using KActions. m_rmbMenu = new QMenu(this); m_rmbMenu->addAction(SmallIcon("go-jump-today"), i18n("Add to Play Queue"), this, SLOT(slotAddToUpcoming())); m_rmbMenu->addSeparator(); if(!readOnly()) { m_rmbMenu->addAction( action("edit_cut") ); m_rmbMenu->addAction( action("edit_copy") ); m_rmbMenu->addAction( action("edit_paste") ); m_rmbMenu->addSeparator(); m_rmbMenu->addAction( action("removeFromPlaylist") ); } else m_rmbMenu->addAction( action("edit_copy") ); m_rmbEdit = m_rmbMenu->addAction(i18n("Edit")); m_rmbMenu->addAction( action("refresh") ); m_rmbMenu->addAction( action("removeItem") ); m_rmbMenu->addSeparator(); m_rmbMenu->addAction( action("guessTag") ); m_rmbMenu->addAction( action("renameFile") ); m_rmbMenu->addAction( action("coverManager") ); m_rmbMenu->addSeparator(); m_rmbMenu->addAction( SmallIcon("folder-new"), i18n("Create Playlist From Selected Items..."), this, SLOT(slotCreateGroup())); } // Ignore any columns added by subclasses. const int adjColumn = column - columnOffset(); bool showEdit = (adjColumn == PlaylistItem::TrackColumn) || (adjColumn == PlaylistItem::ArtistColumn) || (adjColumn == PlaylistItem::AlbumColumn) || (adjColumn == PlaylistItem::TrackNumberColumn) || (adjColumn == PlaylistItem::GenreColumn) || (adjColumn == PlaylistItem::YearColumn); if(showEdit) { m_rmbEdit->setText(i18n("Edit '%1'", item->text(column))); m_rmbEdit->disconnect(this); connect(m_rmbEdit, &QAction::triggered, this, [this, item, column]() { this->editItem(item, column); }); } m_rmbEdit->setVisible(showEdit); // Disable edit menu if only one file is selected, and it's read-only FileHandle file = static_cast(item)->file(); m_rmbEdit->setEnabled(file.fileInfo().isWritable() || selectedItems().count() > 1); // View cover is based on if there is a cover to see. We should only have // the remove cover option if the cover is in our database (and not directly // embedded in the file, for instance). action("viewCover")->setEnabled(file.coverInfo()->hasCover()); action("removeCover")->setEnabled(file.coverInfo()->coverId() != CoverManager::NoMatch); m_rmbMenu->popup(mapToGlobal(point)); } bool Playlist::editTag(PlaylistItem *item, const QString &text, int column) { Tag *newTag = TagTransactionManager::duplicateTag(item->file().tag()); switch(column - columnOffset()) { case PlaylistItem::TrackColumn: newTag->setTitle(text); break; case PlaylistItem::ArtistColumn: newTag->setArtist(text); break; case PlaylistItem::AlbumColumn: newTag->setAlbum(text); break; case PlaylistItem::TrackNumberColumn: { bool ok; int value = text.toInt(&ok); if(ok) newTag->setTrack(value); break; } case PlaylistItem::GenreColumn: newTag->setGenre(text); break; case PlaylistItem::YearColumn: { bool ok; int value = text.toInt(&ok); if(ok) newTag->setYear(value); break; } } TagTransactionManager::instance()->changeTagOnItem(item, newTag); return true; } void Playlist::slotInlineEditDone(QTreeWidgetItem *item, int column) { // The column we get is as passed from QTreeWidget so it does not need // adjustment to get the right text from the QTreeWidgetItem QString text = item->text(column); const PlaylistItemList l = selectedItems(); // See if any of the files have a tag different from the input. const int adjColumn = column - columnOffset(); bool changed = std::any_of(l.cbegin(), l.cend(), [text, adjColumn] (const PlaylistItem *item) { return item->text(adjColumn) != text; } ); if(!changed || (l.count() > 1 && KMessageBox::warningContinueCancel( 0, i18n("This will edit multiple files. Are you sure?"), QString(), KGuiItem(i18n("Edit")), KStandardGuiItem::cancel(), "DontWarnMultipleTags") == KMessageBox::Cancel)) { return; } for(auto &item : l) { editTag(item, text, column); } TagTransactionManager::instance()->commit(); CollectionList::instance()->playlistItemsChanged(); playlistItemsChanged(); } void Playlist::slotColumnOrderChanged(int, int from, int to) { if(from == 0 || to == 0) { updatePlaying(); m_leftColumn = header()->sectionPosition(0); } SharedSettings::instance()->setColumnOrder(this); } void Playlist::slotToggleColumnVisible(QAction *action) { int column = action->data().toInt(); if(isColumnHidden(column)) { int fileNameColumn = PlaylistItem::FileNameColumn + columnOffset(); int fullPathColumn = PlaylistItem::FullPathColumn + columnOffset(); if(column == fileNameColumn && !isColumnHidden(fullPathColumn)) { hideColumn(fullPathColumn, false); SharedSettings::instance()->toggleColumnVisible(fullPathColumn); } if(column == fullPathColumn && !isColumnHidden(fileNameColumn)) { hideColumn(fileNameColumn, false); SharedSettings::instance()->toggleColumnVisible(fileNameColumn); } } if(!isColumnHidden(column)) hideColumn(column); else showColumn(column); if(column >= columnOffset()) { SharedSettings::instance()->toggleColumnVisible(column - columnOffset()); } } void Playlist::slotCreateGroup() { QString name = m_collection->playlistNameDialog(i18n("Create New Playlist")); if(!name.isEmpty()) new Playlist(m_collection, selectedItems(), name); } void Playlist::notifyUserColumnWidthModeChanged() { KMessageBox::information(this, i18n("Manual column widths have been enabled. You can " "switch back to automatic column sizes in the view " "menu."), i18n("Manual Column Widths Enabled"), "ShowManualColumnWidthInformation"); } void Playlist::columnResized(int column, int, int newSize) { m_widthsDirty = true; m_columnFixedWidths[column] = newSize; } void Playlist::slotInlineCompletionModeChanged(KCompletion::CompletionMode mode) { SharedSettings::instance()->setInlineCompletionMode(mode); } void Playlist::slotPlayCurrent() { QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected); PlaylistItem *next = static_cast(*it); TrackSequenceManager::instance()->setNextItem(next); action("forward")->trigger(); } //////////////////////////////////////////////////////////////////////////////// // helper functions //////////////////////////////////////////////////////////////////////////////// QDataStream &operator<<(QDataStream &s, const Playlist &p) { s << p.name(); s << p.fileName(); s << p.files(); return s; } QDataStream &operator>>(QDataStream &s, Playlist &p) { p.read(s); return s; } bool processEvents() { static QTime time = QTime::currentTime(); if(time.elapsed() > 100) { time.restart(); qApp->processEvents(); return true; } return false; } // vim: set et sw=4 tw=0 sta: diff --git a/playlist.h b/playlist.h index 60e6c4ff..88a7d4cf 100644 --- a/playlist.h +++ b/playlist.h @@ -1,774 +1,774 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * Copyright (C) 2007 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_PLAYLIST_H #define JUK_PLAYLIST_H #include #include #include #include #include #include "covermanager.h" #include "stringhash.h" #include "playlistsearch.h" #include "tagguesser.h" #include "playlistinterface.h" #include "filehandle.h" #include "juk_debug.h" class KActionMenu; class QFileInfo; class QMimeData; class QAction; class WebImageFetcher; class PlaylistItem; class PlaylistCollection; class PlaylistToolTip; class CollectionListItem; typedef QList PlaylistItemList; class Playlist : public QTreeWidget, public PlaylistInterface { Q_OBJECT public: explicit Playlist(PlaylistCollection *collection, const QString &name = QString(), const QString &iconName = "audio-midi"); Playlist(PlaylistCollection *collection, const PlaylistItemList &items, const QString &name = QString(), const QString &iconName = "audio-midi"); Playlist(PlaylistCollection *collection, const QFileInfo &playlistFile, const QString &iconName = "audio-midi"); /** * This constructor should generally only be used either by the cache * restoration methods or by subclasses that want to handle calls to * PlaylistCollection::setupPlaylist() differently. * * @param extraColumns is used to preallocate columns for subclasses that * need them (since extra columns are assumed to start from 0). extraColumns * should be equal to columnOffset() (we can't use columnOffset until the * ctor has run). */ Playlist(PlaylistCollection *collection, bool delaySetup, int extraColumns = 0); virtual ~Playlist(); // The following group of functions implement the PlaylistInterface API. virtual QString name() const; virtual FileHandle currentFile() const; virtual int count() const { return model()->rowCount(); } virtual int time() const; virtual void playNext(); virtual void playPrevious(); virtual void stop(); /** * Plays the top item of the playlist. */ void playFirst(); /** * Plays the next album in the playlist. Only useful when in album random * play mode. */ void playNextAlbum(); /** * Saves the file to the currently set file name. If there is no filename * currently set, the default behavior is to prompt the user for a file * name. */ virtual void save(); /** * Standard "save as". Prompts the user for a location where to save the * playlist to. */ virtual void saveAs(); /** * Removes \a item from the Playlist, but not from the disk. * * Since the GUI updates after an item is cleared, you should use clearItems() if you have * a list of items to remove, as that will remove the whole batch before updating * other components/GUI to the change. */ virtual void clearItem(PlaylistItem *item); /** * Remove \a items from the playlist and emit a signal indicating * that the number of items in the list has changed. */ virtual void clearItems(const PlaylistItemList &items); /** * Accessor function to return a pointer to the currently playing file. * * @return 0 if no file is playing, otherwise a pointer to the PlaylistItem * of the track that is currently playing. */ static PlaylistItem *playingItem(); /** * All of the (media) files in the list. */ QStringList files() const; /** * Returns a list of all of the items in the playlist. */ virtual PlaylistItemList items(); /** * Returns a list of all of the \e visible items in the playlist. */ PlaylistItemList visibleItems(); /** * Returns a list of the currently selected items. */ PlaylistItemList selectedItems(); /** * Returns properly casted first child item in list. */ PlaylistItem *firstChild() const; /** * Allow duplicate files in the playlist. */ void setAllowDuplicates(bool allow) { m_allowDuplicates = allow; } /** * This is being used as a mini-factory of sorts to make the construction * of PlaylistItems virtual. In this case it allows for the creation of * both PlaylistItems and CollectionListItems. */ virtual PlaylistItem *createItem(const FileHandle &file, QTreeWidgetItem *after = nullptr); /** * This is implemented as a template method to allow subclasses to * instantiate their PlaylistItem subclasses using the same method. */ template ItemType *createItem(const FileHandle &file, QTreeWidgetItem *after = nullptr); virtual void createItems(const PlaylistItemList &siblings, PlaylistItem *after = nullptr); /** * This handles adding files of various types -- music, playlist or directory * files. Music files that are found will be added to this playlist. New * playlist files that are found will result in new playlists being created. * * Note that this should not be used in the case of adding *only* playlist * items since it has the overhead of checking to see if the file is a playlist * or directory first. */ virtual void addFiles(const QStringList &files, PlaylistItem *after = 0); /** * Returns the file name associated with this playlist (an m3u file) or * an empty QString if no such file exists. */ QString fileName() const { return m_fileName; } /** * Sets the file name to be associated with this playlist; this file should * have the "m3u" extension. */ void setFileName(const QString &n) { m_fileName = n; } /** * Hides column \a c. If \a updateSearch is true then a signal that the * visible columns have changed will be emitted and things like the search - * will be udated. + * will be updated. */ void hideColumn(int c, bool updateSearch = true); /** * Shows column \a c. If \a updateSearch is true then a signal that the * visible columns have changed will be emitted and things like the search * will be updated. */ void showColumn(int c, bool updateSearch = true); void sortByColumn(int column, Qt::SortOrder order = Qt::AscendingOrder); /** * This sets a name for the playlist that is \e different from the file name. */ void setName(const QString &n); /** * Returns the KActionMenu that allows this to be embedded in menus outside * of the playlist. */ KActionMenu *columnVisibleAction() const { return m_columnVisibleAction; } /** * Set item to be the playing item. If \a item is null then this will clear * the playing indicator. */ static void setPlaying(PlaylistItem *item, bool addToHistory = true); /** * Returns true if this playlist is currently playing. */ bool playing() const; /** * This forces an update of the left most visible column, but does not save * the settings for this. */ void updateLeftColumn(); /** * Returns the leftmost visible column of the listview. */ int leftColumn() const { return m_leftColumn; } /** * Sets the items in the list to be either visible based on the value of * visible. This is useful for search operations and such. */ static void setItemsVisible(const PlaylistItemList &items, bool visible = true); /** * Returns the search associated with this list, or an empty search if one * has not yet been set. */ PlaylistSearch search() const { return m_search; } /** - * Set the search associtated with this playlist. + * Set the search associated with this playlist. */ void setSearch(const PlaylistSearch &s); /** * If the search is disabled then all items will be shown, not just those that * match the current search. */ void setSearchEnabled(bool searchEnabled); /** * Subclasses of Playlist which add new columns will set this value to * specify how many of those columns exist. This allows the Playlist * class to do some internal calculations on the number and positions * of columns. */ virtual int columnOffset() const { return 0; } /** * Some subclasses of Playlist will be "read only" lists (i.e. the history * playlist). This is a way for those subclasses to indicate that to the * Playlist internals. */ virtual bool readOnly() const { return false; } /** * Returns true if it's possible to reload this playlist. */ virtual bool canReload() const { return !m_fileName.isEmpty(); } /** * Returns true if the playlist is a search playlist and the search should be * editable. */ virtual bool searchIsEditable() const { return false; } /** * Synchronizes the playing item in this playlist with the playing item * in \a sources. If \a setMaster is true, this list will become the source * for determining the next item. */ void synchronizePlayingItems(const PlaylistList &sources, bool setMaster); /** * Playlists have a common set of shared settings such as visible columns * that should be applied just before the playlist is shown. Calling this * method applies those. */ void applySharedSettings(); void read(QDataStream &s); static void setShuttingDown() { m_shuttingDown = true; } public slots: /** * Remove the currently selected items from the playlist and disk. */ void slotRemoveSelectedItems() { removeFromDisk(selectedItems()); } /* * The edit slots are required to use the canonical names so that they are * detected by the application wide framework. */ virtual void cut() { copy(); clear(); } /** * Puts a list of URLs pointing to the files in the current selection on the * clipboard. */ virtual void copy(); /** * Checks the clipboard for local URLs to be inserted into this playlist. */ virtual void paste(); /** * Removes the selected items from the list, but not the disk. * * @see clearItem() * @see clearItems() */ virtual void clear(); virtual void selectAll() { QTreeView::selectAll(); } /** * Refreshes the tags of the selection from disk, or all of the files in the * list if there is no selection. */ virtual void slotRefresh(); void slotGuessTagInfo(TagGuesser::Type type); /** * Renames the selected items' files based on their tags contents. * * @see PlaylistItem::renameFile() */ void slotRenameFile(); /** * Sets the cover of the selected items, pass in true if you want to load from the local system, * false if you want to load from the internet. */ void slotAddCover(bool fromLocal); /** * Shows a large image of the cover */ void slotViewCover(); /** * Removes covers from the selected items */ void slotRemoveCover(); /** * Shows the cover manager GUI dialog */ void slotShowCoverManager(); /** * Reload the playlist contents from the m3u file. */ virtual void slotReload(); /** * Tells the listview that the next time that it paints that the weighted * column widths must be recalculated. If this is called without a column * all visible columns are marked as dirty. */ void slotWeightDirty(int column = -1); void slotShowPlaying(); void slotColumnResizeModeChanged(); virtual void playlistItemsChanged(); protected: /** * Remove \a items from the playlist and disk. This will ignore items that * are not actually in the list. */ void removeFromDisk(const PlaylistItemList &items); /** * Adds and removes items from this Playlist as necessary to ensure that * the same items are present in this Playlist as in @p itemList. * * No ordering guarantees are imposed, just that the playlist will have the * same items as in the given list afterwards. */ void synchronizeItemsTo(const PlaylistItemList &itemList); // the following are all reimplemented from base classes virtual bool eventFilter(QObject *watched, QEvent *e); virtual void keyPressEvent(QKeyEvent *e); virtual void decode(const QMimeData *s, PlaylistItem *item = 0); QStringList mimeTypes() const; QMimeData* mimeData(const QList items) const; virtual bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action); virtual void dropEvent(QDropEvent *e); virtual void dragEnterEvent(QDragEnterEvent *e); virtual void showEvent(QShowEvent *e); virtual bool acceptDrag(QDropEvent *e) const; virtual void paintEvent(QPaintEvent *pe); virtual void resizeEvent(QResizeEvent *re); virtual void drawRow(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual void insertItem(QTreeWidgetItem *item); virtual void takeItem(QTreeWidgetItem *item); virtual bool hasItem(const QString &file) const { return m_members.contains(file); } virtual void addColumn(const QString &label, int width = -1); /** * Do some final initialization of created items. Notably ensure that they * are shown or hidden based on the contents of the current PlaylistSearch. * * This is called by the PlaylistItem constructor. */ void setupItem(PlaylistItem *item); /** * Forwards the call to the parent to enable or disable automatic deletion * of tree view playlists. Used by CollectionListItem. */ void setDynamicListsFrozen(bool frozen); template ItemType *createItem(SiblingType *sibling, ItemType *after = nullptr); /** * As a template this allows us to use the same code to initialize the items * in subclasses. ItemType should be a PlaylistItem subclass. */ template void createItems(const QList &siblings, ItemType *after = nullptr); protected slots: void slotPopulateBackMenu() const; void slotPlayFromBackMenu(QAction *) const; signals: /** * This is connected to the PlaylistBox::Item to let it know when the * playlist's name has changed. */ void signalNameChanged(const QString &name); /** * This signal is emitted just before a playlist item is removed from the * list allowing for any cleanup that needs to happen. Typically this * is used to remove the item from the history and safeguard against * dangling pointers. */ void signalAboutToRemove(PlaylistItem *item); void signalEnableDirWatch(bool enable); void signalPlaylistItemsDropped(Playlist *p); private: void setup(); /** * This function is called to let the user know that JuK has automatically enabled * manual column width adjust mode. */ void notifyUserColumnWidthModeChanged(); /** * Load the playlist from a file. \a fileName should be the absolute path. * \a fileInfo should point to the same file as \a fileName. This is a * little awkward API-wise, but keeps us from throwing away useful * information. */ void loadFile(const QString &fileName, const QFileInfo &fileInfo); /** * Writes \a text to \a item in \a column. This is used by the inline tag * editor. Returns false if the tag update failed. */ bool editTag(PlaylistItem *item, const QString &text, int column); /** * Returns the index of the left most visible column in the playlist. * * \see isColumnHidden() */ int leftMostVisibleColumn() const; /** * This method is used internally to provide the backend to the other item * lists. * * \see items() * \see visibleItems() * \see selectedItems() */ PlaylistItemList items(QTreeWidgetItemIterator::IteratorFlags flags); /** * Build the column "weights" for the weighted width mode. */ void calculateColumnWeights(); void addFile(const QString &file, FileHandleList &files, bool importPlaylists, PlaylistItem **after); void addFileHelper(FileHandleList &files, PlaylistItem **after, bool ignoreTimer = false); void redisplaySearch() { setSearch(m_search); } /** * Sets the cover for items to the cover identified by id. */ void refreshAlbums(const PlaylistItemList &items, coverKey id = CoverManager::NoMatch); void refreshAlbum(const QString &artist, const QString &album); void updatePlaying() const; /** * This function should be called when item is deleted to ensure that any * internal bookkeeping is performed. It is automatically called by * PlaylistItem::~PlaylistItem and by clearItem() and clearItems(). */ void updateDeletedItem(PlaylistItem *item); /** * Used as a helper to implement template<> createItem(). This grabs the * CollectionListItem for file if it exists, otherwise it creates a new one and * returns that. If nullptr is returned then some kind of error occurred, * and you should probably do nothing with the FileHandle you have. */ CollectionListItem *collectionListItem(const FileHandle &file); /** * This class is used internally to store settings that are shared by all * of the playlists, such as column order. It is implemented as a singleton. */ class SharedSettings; private slots: /** * Handle the necessary tasks needed to create and setup the playlist that * don't need to happen in the ctor, such as setting up the columns, * initializing the RMB menu, and setting up signal/slot connections. * * Used to be a subclass of K3ListView::polish() but the timing of the * call is not consistent and therefore lead to crashes. */ void slotInitialize(); void slotUpdateColumnWidths(); void slotAddToUpcoming(); /** * Show the RMB menu. Matches the signature for the signal * QListView::contextMenuRequested(). */ void slotShowRMBMenu(const QPoint &point); /** * This slot is called when the inline tag editor has completed its editing * and starts the process of renaming the values. */ void slotInlineEditDone(QTreeWidgetItem *, int column); /** * The image fetcher will update the cover asynchronously, this internal * slot is called when it happens. */ void slotCoverChanged(int coverId); /** * Moves the column \a from to the position \a to. This matches the signature * for the signal QHeader::indexChange(). */ void slotColumnOrderChanged(int, int from, int to); /** * Toggles a columns visible status. Useful for KActions. * * \see hideColumn() * \see showColumn() */ void slotToggleColumnVisible(QAction *action); /** * Prompts the user to create a new playlist with from the selected items. */ void slotCreateGroup(); /** * This slot is called when the user drags the slider in the listview header * to manually set the size of the column. */ void columnResized(int column, int oldSize, int newSize); /** * The slot is called when the completion mode for the line edit in the * inline tag editor is changed. It saves the settings and through the * magic of the SharedSettings class will apply it to the other playlists as * well. */ void slotInlineCompletionModeChanged(KCompletion::CompletionMode mode); void slotPlayCurrent(); private: friend class PlaylistItem; PlaylistCollection *m_collection; StringHash m_members; WebImageFetcher *m_fetcher; QAction *m_rmbEdit; bool m_allowDuplicates; bool m_applySharedSettings; bool m_columnWidthModeChanged; QList m_weightDirty; bool m_disableColumnWidthUpdates; mutable int m_time; mutable PlaylistItemList m_addTime; mutable PlaylistItemList m_subtractTime; /** * The average minimum widths of columns to be used in balancing calculations. */ QVector m_columnWeights; QVector m_columnFixedWidths; bool m_widthsDirty; static PlaylistItemList m_history; PlaylistSearch m_search; bool m_searchEnabled; /** * Used to store the text for inline editing before it is changed so that * we can know if something actually changed and as such if we need to save * the tag. */ QString m_editText; /** * This is only defined if the playlist name is something other than the * file name. */ QString m_playlistName; QString m_fileName; QStringList m_columns; QMenu *m_rmbMenu; QMenu *m_headerMenu; KActionMenu *m_columnVisibleAction; PlaylistToolTip *m_toolTip; /** * This is used to indicate if the list of visible items has changed (via a * call to setVisibleItems()) while random play is playing. */ static bool m_visibleChanged; static bool m_shuttingDown; static int m_leftColumn; static QVector m_backMenuItems; bool m_blockDataChanged; }; typedef QList PlaylistList; bool processEvents(); class FocusUpEvent : public QEvent { public: FocusUpEvent() : QEvent(id) {} Type type() const { return id; } static const Type id = static_cast(QEvent::User + 1); }; QDataStream &operator<<(QDataStream &s, const Playlist &p); QDataStream &operator>>(QDataStream &s, Playlist &p); // template method implementations template ItemType *Playlist::createItem(const FileHandle &file, QTreeWidgetItem *after) { CollectionListItem *item = collectionListItem(file); if(item && (!m_members.insert(file.absFilePath()) || m_allowDuplicates)) { auto i = new ItemType(item, this, after); setupItem(i); return i; } else return nullptr; } template ItemType *Playlist::createItem(SiblingType *sibling, ItemType *after) { m_disableColumnWidthUpdates = true; if(!m_members.insert(sibling->file().absFilePath()) || m_allowDuplicates) { after = new ItemType(sibling->collectionItem(), this, after); setupItem(after); } m_disableColumnWidthUpdates = false; return after; } template void Playlist::createItems(const QList &siblings, ItemType *after) { if(siblings.isEmpty()) return; foreach(SiblingType *sibling, siblings) after = createItem(sibling, after); playlistItemsChanged(); slotWeightDirty(); } #endif // vim: set et sw=4 tw=0 sta: diff --git a/playlistitem.h b/playlistitem.h index ca6d8ff2..6076420a 100644 --- a/playlistitem.h +++ b/playlistitem.h @@ -1,221 +1,221 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_PLAYLISTITEM_H #define JUK_PLAYLISTITEM_H #include #include #include #include #include #include "tagguesser.h" #include "filehandle.h" #include "juk_debug.h" class Playlist; class PlaylistItem; class CollectionListItem; class CollectionList; typedef QList PlaylistItemList; /** * Items for the Playlist and the baseclass for CollectionListItem. * The constructors and destructor are protected and new items should be * created via Playlist::createItem(). Items should be removed by * Playlist::clear(), Playlist::deleteFromDisk(), Playlist::clearItem() or * Playlist::clearItem(). */ class PlaylistItem : public QTreeWidgetItem { friend class Playlist; friend class SearchPlaylist; friend class UpcomingPlaylist; friend class CollectionList; friend class CollectionListItem; friend class Pointer; public: enum ColumnType { TrackColumn = 0, ArtistColumn = 1, AlbumColumn = 2, CoverColumn = 3, TrackNumberColumn = 4, GenreColumn = 5, YearColumn = 6, LengthColumn = 7, BitrateColumn = 8, CommentColumn = 9, FileNameColumn = 10, FullPathColumn = 11 }; /** * A helper class to implement guarded pointer semantics. */ class Pointer { public: Pointer() : m_item(0) {} Pointer(PlaylistItem *item); Pointer(const Pointer &p); ~Pointer(); Pointer &operator=(PlaylistItem *item); bool operator==(const Pointer &p) const { return m_item == p.m_item; } bool operator!=(const Pointer &p) const { return m_item != p.m_item; } PlaylistItem *operator->() const { return m_item; } PlaylistItem &operator*() const { return *m_item; } operator PlaylistItem*() const { return m_item; } static void clear(PlaylistItem *item); private: PlaylistItem *m_item; static QMap > m_map; }; friend class Pointer; static int lastColumn() { return FullPathColumn; } void setFile(const FileHandle &file); void setFile(const QString &file); FileHandle file() const; virtual const QPixmap *pixmap(int column) const; virtual QString text(int column) const; virtual void setText(int column, const QString &text); void setPlaying(bool playing = true, bool master = true); void guessTagInfo(TagGuesser::Type type); Playlist *playlist() const; virtual CollectionListItem *collectionItem() { return m_collectionItem; } /** * This is an identifier for the playlist item which will remain unique * throughout the process lifetime. It stays constant once the PlaylistItem * is created. */ quint32 trackId() const { return m_trackId; } /** * The widths of items are cached when they're updated for us in computations * in the "weighted" listview column width mode. */ QVector cachedWidths() const; /** * This just refreshes from the in memory data. This may seem pointless at * first, but this data is shared between all of the list view items that are * based on the same file, so if another one of those items changes its data * it is important to refresh the others. */ virtual void refresh(); /** * This rereads the tag from disk. This affects all PlaylistItems based on * the same file. */ virtual void refreshFromDisk(); /** * Asks the item's playlist to remove the item (which uses deleteLater()). */ virtual void clear(); /** * Returns properly casted item below this one. */ PlaylistItem *itemBelow() { return static_cast(treeWidget()->itemBelow(this)); } /** * Returns properly casted item above this one. */ PlaylistItem *itemAbove() { return static_cast(treeWidget()->itemAbove(this)); } /** - * Returns a reference to the list of the currnetly playing items, with the + * Returns a reference to the list of the currently playing items, with the * first being the "master" item (i.e. the item from which the next track is * chosen). */ static const PlaylistItemList &playingItems() { return m_playingItems; } protected: /** * Items should always be created using Playlist::createItem() or through a * subclass or friend class. */ PlaylistItem(CollectionListItem *item, Playlist *parent); PlaylistItem(CollectionListItem *item, Playlist *parent, QTreeWidgetItem *after); /** - * This is the constructor that shold be used by subclasses. + * This is the constructor that should be used by subclasses. */ PlaylistItem(CollectionList *parent); /** * See the class documentation for an explanation of construction and deletion * of PlaylistItems. */ virtual ~PlaylistItem(); virtual int compare(const QTreeWidgetItem *item, int column, bool ascending) const; int compare(const PlaylistItem *firstItem, const PlaylistItem *secondItem, int column, bool ascending) const; bool operator<(const QTreeWidgetItem &other) const; bool isValid() const; void setTrackId(quint32 id); /** * Shared data between all PlaylistItems from the same track (incl. the CollectionItem * representing said track. */ struct Data : public QSharedData { FileHandle fileHandle; // Set within CollectionList QVector metadata; ///< Artist, album, or genre tags. Other columns unfilled QVector cachedWidths; }; using DataPtr = QExplicitlySharedDataPointer; DataPtr sharedData() const { return d; } private: DataPtr d; void setup(CollectionListItem *item); CollectionListItem *m_collectionItem; quint32 m_trackId; bool m_watched; static PlaylistItemList m_playingItems; }; inline QDebug operator<<(QDebug s, const PlaylistItem &item) { s << item.text(PlaylistItem::TrackColumn); return s; } #endif // vim: set et sw=4 tw=0 sta: diff --git a/playlistsearch.h b/playlistsearch.h index 67330275..9f52dca8 100644 --- a/playlistsearch.h +++ b/playlistsearch.h @@ -1,153 +1,153 @@ /** * Copyright (C) 2003-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef PLAYLISTSEARCH_H #define PLAYLISTSEARCH_H #include #include class Playlist; class PlaylistItem; typedef QList ColumnList; typedef QList PlaylistItemList; typedef QList PlaylistList; class PlaylistSearch { public: class Component; typedef QList ComponentList; enum SearchMode { MatchAny = 0, MatchAll = 1 }; PlaylistSearch(); PlaylistSearch(const PlaylistList &playlists, const ComponentList &components, SearchMode mode = MatchAny, bool searchNow = true); void search(); bool checkItem(PlaylistItem *item); PlaylistItemList searchedItems() const { return m_items; } PlaylistItemList matchedItems() const { return m_matchedItems; } PlaylistItemList unmatchedItems() const { return m_unmatchedItems; } void addPlaylist(Playlist *p) { m_playlists.append(p); } void clearPlaylists() { m_playlists.clear(); } PlaylistList playlists() const { return m_playlists; } void addComponent(const Component &c); void clearComponents(); ComponentList components() const; void setSearchMode(SearchMode m) { m_mode = m; } SearchMode searchMode() const { return m_mode; } bool isNull() const; bool isEmpty() const; /** * This is used to clear an item from the matched and unmatched lists. This * is useful because it can prevent keeping a dangling pointer around without * requiring invalidating the search. */ void clearItem(PlaylistItem *item); private: PlaylistList m_playlists; ComponentList m_components; SearchMode m_mode; PlaylistItemList m_items; PlaylistItemList m_matchedItems; PlaylistItemList m_unmatchedItems; }; /** - * A search is built from several search components. These corespond to to lines + * A search is built from several search components. These correspond to to lines * in the search bar. */ class PlaylistSearch::Component { public: enum MatchMode { Contains = 0, Exact = 1, ContainsWord = 2 }; /** * Create an empty search component. This is only provided for use by * QValueList and should not be used in any other context. */ Component(); /** - * Create a query component. This defaults to searching all visible coulumns. + * Create a query component. This defaults to searching all visible columns. */ Component(const QString &query, bool caseSensitive = false, const ColumnList &columns = ColumnList(), MatchMode mode = Contains); /** * Create a query component. This defaults to searching all visible coulumns. */ Component(const QRegExp &query, const ColumnList &columns = ColumnList()); QString query() const { return m_query; } QRegExp pattern() const { return m_queryRe; } ColumnList columns() const { return m_columns; } bool matches(PlaylistItem *item) const; bool isPatternSearch() const { return m_re; } bool isCaseSensitive() const { return m_caseSensitive; } MatchMode matchMode() const { return m_mode; } bool operator==(const Component &v) const; private: QString m_query; QRegExp m_queryRe; mutable ColumnList m_columns; MatchMode m_mode; bool m_searchAllVisible; bool m_caseSensitive; bool m_re; }; /** * Streams \a search to the stream \a s. * \note This does not save the playlist list, but instead will assume that the * search is just relevant to the collection list. This is all that is presently * needed by JuK. */ QDataStream &operator<<(QDataStream &s, const PlaylistSearch &search); /** * Streams \a search from the stream \a s. * \note This does not save the playlist list, but instead will assume that the * search is just relevant to the collection list. This is all that is presently * needed by JuK. */ QDataStream &operator>>(QDataStream &s, PlaylistSearch &search); QDataStream &operator<<(QDataStream &s, const PlaylistSearch::Component &c); QDataStream &operator>>(QDataStream &s, PlaylistSearch::Component &c); #endif // vim: set et sw=4 tw=0 sta: diff --git a/scrobbleconfigdlg.h b/scrobbleconfigdlg.h index 2e2d9906..51d3faed 100644 --- a/scrobbleconfigdlg.h +++ b/scrobbleconfigdlg.h @@ -1,57 +1,57 @@ /** * Copyright (C) 2012 Martin Sandsmark * Copyright (C) 2014 Arnold Dumas * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -#ifndef JUK_SCROBBLESETTINGS_H -#define JUK_SCROBBLESETTINGS_H +#ifndef JUK_SCROBBLECONFIGDLG_H +#define JUK_SCROBBLECONFIGDLG_H #include #include #include using namespace KWallet; class KLineEdit; class QAbstractButton; class QPushButton; class QLabel; class ScrobbleConfigDlg : public QDialog { Q_OBJECT public: explicit ScrobbleConfigDlg(QWidget* parent = nullptr); private slots: void testLogin(); void validLogin(); void invalidLogin(); void save(); void valuesChanged(); private: KLineEdit *m_usernameEdit; KLineEdit *m_passwordEdit; QPushButton *m_testButton; QAbstractButton *m_saveButton; QLabel *m_testFeedbackLabel; std::unique_ptr m_wallet; }; -#endif //JUK_SCROBBLESETTINGS_H +#endif //JUK_SCROBBLECONFIGDLG_H diff --git a/slider.h b/slider.h index 7c4c6f89..4f2decce 100644 --- a/slider.h +++ b/slider.h @@ -1,124 +1,124 @@ /** * Copyright (c) 2003-2009 Mark Kretschmann * Copyright (c) 2005 Gabor Lehel * Copyright (c) 2008 Dan Meltzer * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef SLIDERWIDGET_H #define SLIDERWIDGET_H #include #include #include #include class QPalette; class QTimer; class Slider : public QSlider { Q_OBJECT public: explicit Slider( Qt::Orientation, uint max = 0, QWidget* parent = 0 ); virtual void setValue( int ); signals: //we emit this when the user has specifically changed the slider //so connect to it if valueChanged() is too generic //Qt also emits valueChanged( int ) void sliderReleased( int ); protected: virtual void wheelEvent( QWheelEvent* ); virtual void mouseMoveEvent( QMouseEvent* ); virtual void mouseReleaseEvent( QMouseEvent* ); virtual void mousePressEvent( QMouseEvent* ); virtual void slideEvent( QMouseEvent* ); QRect sliderHandleRect( const QRect &slider, qreal percent ) const; void paintCustomSlider( QPainter *p ); bool m_sliding; bool m_usingCustomStyle; static const int s_borderWidth = 6; static const int s_borderHeight = 6; static const int s_sliderInsertX = 5; static const int s_sliderInsertY = 5; private: bool m_outside; int m_prevValue; bool m_needsResize; QPixmap m_topLeft; QPixmap m_topRight; QPixmap m_top; QPixmap m_bottomRight; QPixmap m_right; QPixmap m_bottomLeft; QPixmap m_bottom; QPixmap m_left; Q_DISABLE_COPY( Slider ) }; class VolumeSlider : public Slider { Q_OBJECT public: explicit VolumeSlider( uint max, QWidget *parent, bool customStyle = true ); // VolumePopupButton needs to access this virtual void wheelEvent( QWheelEvent *e ); protected: virtual void paintEvent( QPaintEvent* ); virtual void mousePressEvent( QMouseEvent* ); virtual void contextMenuEvent( QContextMenuEvent* ); signals: void volumeChanged( float ); private slots: void emitVolumeChanged( int ); private: Q_DISABLE_COPY( VolumeSlider ) }; class TimeSlider : public Slider { Q_OBJECT public: - TimeSlider( QWidget *parent ); + explicit TimeSlider( QWidget *parent ); void setSliderValue( int value ); protected: virtual void paintEvent( QPaintEvent* ); virtual void mousePressEvent( QMouseEvent* ); virtual void sliderChange( SliderChange change ); private: Q_DISABLE_COPY( TimeSlider ) int m_knobX; // The position of the current indicator. }; #endif diff --git a/stringhash.h b/stringhash.h index f64617f8..32c45534 100644 --- a/stringhash.h +++ b/stringhash.h @@ -1,46 +1,46 @@ /** * Copyright (C) 2003-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef STRINGHASH_H #define STRINGHASH_H #include /** * A simple hash representing an (un-mapped) set of data. */ template class Hash : public QSet { public: /** * To combine two operations into one (that takes the same amount as each - * independantly) this inserts an item and returns true if the item was + * independently) this inserts an item and returns true if the item was * already in the set or false if it did not. */ inline bool insert(const T &value) { if(this->contains(value)) return true; QSet::insert(value); return false; } }; typedef Hash StringHash; #endif // vim: set et sw=4 tw=0 sta: diff --git a/tag.cpp b/tag.cpp index 4723da90..79090eb2 100644 --- a/tag.cpp +++ b/tag.cpp @@ -1,247 +1,247 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * Copyright (C) 2009 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "tag.h" #include -#include +#include #include #include #include #include #include "cache.h" #include "mediafiles.h" #include "stringshare.h" #include "juk_debug.h" //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Tag::Tag(const QString &fileName) : m_fileName(fileName), m_track(0), m_year(0), m_seconds(0), m_bitrate(0), m_isValid(false) { if(fileName.isEmpty()) { qCCritical(JUK_LOG) << "Trying to add empty file"; return; } TagLib::File *file = MediaFiles::fileFactoryByType(fileName); if(file && file->isValid()) { setup(file); delete file; } else { qCCritical(JUK_LOG) << "Couldn't resolve the mime type of \"" << fileName << "\" -- this shouldn't happen."; } } bool Tag::save() const { bool result; TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8); TagLib::File *file = MediaFiles::fileFactoryByType(m_fileName); if(file && !file->readOnly() && file->isValid() && file->tag()) { file->tag()->setTitle(TagLib::String(m_title.toUtf8().constData(), TagLib::String::UTF8)); file->tag()->setArtist(TagLib::String(m_artist.toUtf8().constData(), TagLib::String::UTF8)); file->tag()->setAlbum(TagLib::String(m_album.toUtf8().constData(), TagLib::String::UTF8)); file->tag()->setGenre(TagLib::String(m_genre.toUtf8().constData(), TagLib::String::UTF8)); file->tag()->setComment(TagLib::String(m_comment.toUtf8().constData(), TagLib::String::UTF8)); file->tag()->setTrack(m_track); file->tag()->setYear(m_year); result = file->save(); } else { qCCritical(JUK_LOG) << "Couldn't save file."; result = false; } delete file; return result; } QString Tag::playingString() const { QString str; if(artist().isEmpty()) str = title(); else { str = i18nc("a playing track, %1 is artist, %2 is song title", "%1 - %2", artist(), title()); } return str; } CacheDataStream &Tag::read(CacheDataStream &s) { switch(s.cacheVersion()) { case 1: { qint32 track; qint32 year; qint32 bitrate; qint32 seconds; s >> m_title >> m_artist >> m_album >> m_genre >> track >> year >> m_comment >> bitrate >> m_lengthString >> seconds; m_track = track; m_year = year; m_bitrate = bitrate; m_seconds = seconds; break; } default: { static QString dummyString; static int dummyInt; QString bitrateString; s >> dummyInt >> m_title >> m_artist >> m_album >> m_genre >> dummyInt >> m_track >> dummyString >> m_year >> dummyString >> m_comment >> bitrateString >> m_lengthString >> m_seconds >> dummyString; bool ok; m_bitrate = bitrateString.toInt(&ok); if(!ok) m_bitrate = 0; break; } } minimizeMemoryUsage(); return s; } //////////////////////////////////////////////////////////////////////////////// // private methods //////////////////////////////////////////////////////////////////////////////// Tag::Tag(const QString &fileName, bool) : m_fileName(fileName), m_track(0), m_year(0), m_seconds(0), m_bitrate(0), m_isValid(true) { } void Tag::setup(TagLib::File *file) { if(!file || !file->tag()) { qCWarning(JUK_LOG) << "Can't setup invalid file" << m_fileName; return; } m_title = TStringToQString(file->tag()->title()).trimmed(); m_artist = TStringToQString(file->tag()->artist()).trimmed(); m_album = TStringToQString(file->tag()->album()).trimmed(); m_genre = TStringToQString(file->tag()->genre()).trimmed(); m_comment = TStringToQString(file->tag()->comment()).trimmed(); m_track = file->tag()->track(); m_year = file->tag()->year(); m_seconds = file->audioProperties()->length(); m_bitrate = file->audioProperties()->bitrate(); const int seconds = m_seconds % 60; const int minutes = (m_seconds - seconds) / 60; m_lengthString = QString::number(minutes) + (seconds >= 10 ? ":" : ":0") + QString::number(seconds); if(m_title.isEmpty()) { int i = m_fileName.lastIndexOf('/'); int j = m_fileName.lastIndexOf('.'); m_title = i > 0 ? m_fileName.mid(i + 1, j - i - 1) : m_fileName; } minimizeMemoryUsage(); m_isValid = true; } void Tag::minimizeMemoryUsage() { // Try to reduce memory usage: share tags that frequently repeat, squeeze others m_title.squeeze(); m_lengthString.squeeze(); m_comment = StringShare::tryShare(m_comment); m_artist = StringShare::tryShare(m_artist); m_album = StringShare::tryShare(m_album); m_genre = StringShare::tryShare(m_genre); } //////////////////////////////////////////////////////////////////////////////// // related functions //////////////////////////////////////////////////////////////////////////////// QDataStream &operator<<(QDataStream &s, const Tag &t) { s << t.title() << t.artist() << t.album() << t.genre() << qint32(t.track()) << qint32(t.year()) << t.comment() << qint32(t.bitrate()) << t.lengthString() << qint32(t.seconds()); return s; } CacheDataStream &operator>>(CacheDataStream &s, Tag &t) { return t.read(s); } // vim: set et sw=4 tw=0 sta: diff --git a/tageditor.h b/tageditor.h index ff1928c3..7285f8ed 100644 --- a/tageditor.h +++ b/tageditor.h @@ -1,99 +1,99 @@ /** * Copyright (C) 2002-2004 Scott Wheeler * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_TAGEDITOR_H #define JUK_TAGEDITOR_H #include #include #include #include "ui_tageditor.h" class KComboBox; class KLineEdit; class KIntSpinBox; class KTextEdit; class KConfigGroup; class QCheckBox; class QBoxLayout; class CollectionObserver; class Playlist; class PlaylistItem; typedef QList PlaylistItemList; class TagEditor : public QWidget, public Ui::TagEditor { Q_OBJECT public: - TagEditor(QWidget *parent = 0); + explicit TagEditor(QWidget *parent = 0); virtual ~TagEditor(); PlaylistItemList items() const { return m_items; } void setupObservers(); public slots: void slotSave() { save(m_items); } void slotSetItems(const PlaylistItemList &list); void slotRefresh(); void slotClear(); void slotPlaylistDestroyed(Playlist *p); /** * Update collection if we're visible, or defer otherwise */ void slotUpdateCollection(); private: void updateCollection(); void setupActions(); void setupLayout(); void readConfig(); void readCompletionMode(const KConfigGroup &config, KComboBox *box, const QString &key); void saveConfig(); void save(const PlaylistItemList &list); void saveChangesPrompt(); virtual void showEvent(QShowEvent *e); private slots: void slotDataChanged(); void slotItemRemoved(PlaylistItem *item); void slotPlaylistRemoved() { m_currentPlaylist = 0; } private: typedef QMap BoxMap; BoxMap m_enableBoxes; QStringList m_genreList; PlaylistItemList m_items; Playlist *m_currentPlaylist; CollectionObserver *m_observer; bool m_dataChanged; bool m_collectionChanged; bool m_performingSave; friend class CollectionObserver; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/tagguesser.h b/tagguesser.h index 0673ef4e..a0411b25 100644 --- a/tagguesser.h +++ b/tagguesser.h @@ -1,85 +1,85 @@ /** * Copyright (C) 2003 Frerich Raabe * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef TAGGUESSER_H #define TAGGUESSER_H #include #include #include class FileNameScheme { public: typedef QList List; FileNameScheme() { } - FileNameScheme(const QString &s); + explicit FileNameScheme(const QString &s); bool matches(const QString &s) const; QString title() const; QString artist() const; QString album() const; QString track() const; QString comment() const; private: QString composeRegExp(const QString &s) const; mutable QRegExp m_regExp; int m_titleField; int m_artistField; int m_albumField; int m_trackField; int m_commentField; }; class TagGuesser { public: enum Type { FileName = 0, MusicBrainz = 1 }; static QStringList schemeStrings(); static void setSchemeStrings(const QStringList &schemes); TagGuesser(); - TagGuesser(const QString &absFileName); + explicit TagGuesser(const QString &absFileName); void guess(const QString &absFileName); QString title() const { return m_title; } QString artist() const { return m_artist; } QString album() const { return m_album; } QString track() const { return m_track; } QString comment() const { return m_comment; } private: void loadSchemes(); QString capitalizeWords(const QString &s); FileNameScheme::List m_schemes; QString m_title; QString m_artist; QString m_album; QString m_track; QString m_comment; }; #endif // TAGGUESSER_H // vim: set et sw=4 tw=0 sta: diff --git a/tagrenameroptions.h b/tagrenameroptions.h index d459de3c..40c5d0fa 100644 --- a/tagrenameroptions.h +++ b/tagrenameroptions.h @@ -1,176 +1,176 @@ /** * Copyright (C) 2004 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef TAGRENAMEROPTIONS_H #define TAGRENAMEROPTIONS_H -#include +#include // Insert all new tag types before NumTypes, that way NumTypes will always be // the count of valid tag types. enum TagType { StartTag, Title = StartTag, Artist, Album, Track, Genre, Year, NumTypes, TagUnknown }; /** * Class that uniquely identifies a user's category (since the user may have * the same category more than once in their file renaming structure). */ struct CategoryID { CategoryID() : category(TagUnknown), categoryNumber(0) { } CategoryID(const CategoryID &other) : category(other.category), categoryNumber(other.categoryNumber) { } CategoryID(TagType cat, unsigned num) : category(cat), categoryNumber(num) { } CategoryID &operator=(const CategoryID &other) { if(this == &other) return *this; category = other.category; categoryNumber = other.categoryNumber; return *this; } bool operator==(const CategoryID &other) const { return category == other.category && categoryNumber == other.categoryNumber; } bool operator!=(const CategoryID &other) const { return !(*this == other); } bool operator<(const CategoryID &other) const { if(category == other.category) return categoryNumber < other.categoryNumber; return category < other.category; } TagType category; unsigned categoryNumber; }; /** * Defines options for a tag type. Used by FileRenamerTagOptions as its * data type. * * @author Michael Pyne */ class TagRenamerOptions { public: enum EmptyActions { ForceEmptyInclude, IgnoreEmptyTag, UseReplacementValue }; TagRenamerOptions(); /** * Construct the options by loading from KConfig. * * @param category The category to load the options for. */ TagRenamerOptions(const CategoryID &category); TagRenamerOptions(const TagRenamerOptions &other); QString prefix() const { return m_prefix; } QString suffix() const { return m_suffix; } QString emptyText() const { return m_emptyText; } EmptyActions emptyAction() const { return m_emptyAction; } unsigned trackWidth() const { return m_trackWidth; } bool disabled() const { return m_disabled; } TagType category() const { return m_category; } void setPrefix(const QString &prefix) { m_prefix = prefix; } void setSuffix(const QString &suffix) { m_suffix = suffix; } void setEmptyText(const QString &emptyText) { m_emptyText = emptyText; } void setEmptyAction(EmptyActions action) { m_emptyAction = action; } void setTrackWidth(unsigned width) { m_trackWidth = width; } void setDisabled(bool disabled) { m_disabled = disabled; } void setCategory(TagType category) { m_category = category; } /** * Maps \p type to a textual representation of its name. E.g. Track => "Track" * * @param type the category to retrieve a text representation of. * @param translate if true, the string is translated (if possible). * @return text representation of category. */ static QString tagTypeText(TagType category, bool translate = true); QString tagTypeText(bool translate = true) const { return tagTypeText(category(), translate); } /** * Function that tries to match a string back to its category. Uses only * the untranslated and case-sensitive form of the string. If it fails it * will return TagUnknown. */ static TagType tagFromCategoryText(const QString &text); /** * This saves the options to the global KConfig object. * * @param categoryNum The zero-based count of the number of this type of * category. For example, this would be 1 for the * second category of this type. The stored category * number is not used in order to allow you to save with * a different one (for compaction purposes perhaps). */ void saveConfig(unsigned categoryNum) const; private: // Member variables QString m_prefix; QString m_suffix; /// Defines the action to take when the tag is empty. EmptyActions m_emptyAction; /// If m_emptyAction is UseReplacementValue, this holds the text of the value /// to use. QString m_emptyText; /// Used only for the Track type. Defines the minimum track width when /// expanding the track token. unsigned m_trackWidth; /// This is true if this tag is always disabled when expanding file names. bool m_disabled; TagType m_category; }; #endif /* TAGRENAMEROPTIONS_H */ // vim: set et sw=4 tw=0 sta: diff --git a/tracksequencemanager.cpp b/tracksequencemanager.cpp index c49646fc..4a5c38d5 100644 --- a/tracksequencemanager.cpp +++ b/tracksequencemanager.cpp @@ -1,182 +1,182 @@ /** * Copyright (C) 2002-2004 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ +#include "tracksequencemanager.h" #include #include #include #include "actioncollection.h" -#include "tracksequencemanager.h" #include "playlist.h" #include "playlistitem.h" #include "tracksequenceiterator.h" #include "tag.h" #include "filehandle.h" #include "collectionlist.h" ///////////////////////////////////////////////////////////////////////////// // public functions ///////////////////////////////////////////////////////////////////////////// TrackSequenceManager::~TrackSequenceManager() { // m_playlist doesn't belong to us, don't try to delete if(m_iterator == m_defaultIterator) m_iterator = 0; delete m_iterator; delete m_defaultIterator; } bool TrackSequenceManager::installIterator(TrackSequenceIterator *iterator) { PlaylistItem *oldItem = m_iterator ? m_iterator->current() : 0; if(m_iterator != m_defaultIterator) delete m_iterator; m_iterator = m_defaultIterator; if(iterator) m_iterator = iterator; m_iterator->setCurrent(oldItem); return true; } PlaylistItem *TrackSequenceManager::currentItem() const { return m_iterator->current(); } TrackSequenceIterator *TrackSequenceManager::takeIterator() { TrackSequenceIterator *temp = m_iterator; m_iterator = nullptr; return temp; } TrackSequenceManager *TrackSequenceManager::instance() { static TrackSequenceManager manager; if(!manager.m_initialized) manager.initialize(); return &manager; } PlaylistItem *TrackSequenceManager::nextItem() { if(m_playNextItem) { // Force the iterator to reset state (such as random item lists) m_iterator->reset(); m_iterator->prepareToPlay(m_playNextItem->playlist()); m_iterator->setCurrent(m_playNextItem); m_playNextItem = nullptr; } else if(m_iterator->current()) m_iterator->advance(); else if(currentPlaylist()) m_iterator->prepareToPlay(currentPlaylist()); else m_iterator->prepareToPlay(CollectionList::instance()); return m_iterator->current(); } PlaylistItem *TrackSequenceManager::previousItem() { m_iterator->backup(); return m_iterator->current(); } ///////////////////////////////////////////////////////////////////////////// // public slots ///////////////////////////////////////////////////////////////////////////// void TrackSequenceManager::setNextItem(PlaylistItem *item) { m_playNextItem = item; } void TrackSequenceManager::setCurrentPlaylist(Playlist *list) { if(m_playlist) m_playlist->disconnect(this); m_playlist = list; connect(m_playlist, SIGNAL(signalAboutToRemove(PlaylistItem*)), this, SLOT(slotItemAboutToDie(PlaylistItem*))); } void TrackSequenceManager::setCurrent(PlaylistItem *item) { if(item != m_iterator->current()) { m_iterator->setCurrent(item); if(item) setCurrentPlaylist(item->playlist()); else m_iterator->reset(); } } ///////////////////////////////////////////////////////////////////////////// // private functions ///////////////////////////////////////////////////////////////////////////// void TrackSequenceManager::initialize() { CollectionList *collection = CollectionList::instance(); if(!collection) return; // Make sure we don't use m_playNextItem if it's invalid. connect(collection, SIGNAL(signalAboutToRemove(PlaylistItem*)), this, SLOT(slotItemAboutToDie(PlaylistItem*))); m_initialized = true; } TrackSequenceManager::TrackSequenceManager() : QObject(), m_playlist(0), m_playNextItem(0), m_iterator(0), m_initialized(false) { m_defaultIterator = new DefaultSequenceIterator(); m_iterator = m_defaultIterator; } ///////////////////////////////////////////////////////////////////////////// // protected slots ///////////////////////////////////////////////////////////////////////////// void TrackSequenceManager::slotItemAboutToDie(PlaylistItem *item) { if(item == m_playNextItem) m_playNextItem = 0; m_iterator->itemAboutToDie(item); } // vim: set et sw=4 tw=0 sta: diff --git a/viewmode.h b/viewmode.h index 7b9d0c96..622d1e07 100644 --- a/viewmode.h +++ b/viewmode.h @@ -1,152 +1,152 @@ /** * Copyright (C) 2003-2004 Scott Wheeler * Copyright (C) 2007 Michael Pyne * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef VIEWMODE_H #define VIEWMODE_H #include #include #include #include "playlistbox.h" class QPainter; class QColorGroup; class ViewMode : public QObject { Q_OBJECT public: - ViewMode(PlaylistBox *b); + explicit ViewMode(PlaylistBox *b); virtual ~ViewMode(); virtual QString name() const; virtual void setShown(bool shown); /*virtual void paintCell(PlaylistBox::Item *item, QPainter *painter, const QColorGroup &colorGroup, int column, int width, int align);*/ virtual bool eventFilter(QObject *watched, QEvent *e); void queueRefresh() { m_needsRefresh = true; } virtual void setupItem(PlaylistBox::Item *item) const; virtual void setupDynamicPlaylists() {} /** * If the view mode has dynamic lists, this function is used to temporarily * freeze them to prevent them from deleting dynamic elements. */ virtual void setDynamicListsFrozen(bool /* frozen */) {} /** * Used for dynamic view modes. This function will be called when \p items * are added to \p column (even if the view mode hasn't been shown yet). */ virtual void addItems(const QStringList &items, unsigned column) { Q_UNUSED(items); Q_UNUSED(column); } /** * Used for dynamic view modes. This function will be called when \p item * is removed from \p column (even if the view mode hasn't been shown yet). */ virtual void removeItem(const QString &item, unsigned column) { Q_UNUSED(item); Q_UNUSED(column); } protected: PlaylistBox *playlistBox() const { return m_playlistBox; } bool visible() const { return m_visible; } void setVisible(bool v) { m_visible = v; } void updateIcons(int size); virtual void updateHeights(); static void paintDropIndicator(QPainter *painter, int width, int height); private: static QStringList lines(const PlaylistBox::Item *item, const QFontMetrics &fm, int width); PlaylistBox *m_playlistBox; bool m_visible; bool m_needsRefresh; QMap m_lines; static const int border = 4; }; //////////////////////////////////////////////////////////////////////////////// class CompactViewMode : public ViewMode { public: - CompactViewMode(PlaylistBox *b); + explicit CompactViewMode(PlaylistBox *b); virtual ~CompactViewMode(); virtual QString name() const; virtual void setShown(bool shown); /*virtual void paintCell(PlaylistBox::Item *item, QPainter *painter, const QColorGroup &colorGroup, int column, int width, int align);*/ virtual void setupItem(PlaylistBox::Item *) const { /*item->QTreeWidgetItem::setup();*/ } protected: virtual void updateHeights(); }; //////////////////////////////////////////////////////////////////////////////// class TreeViewItemPlaylist; class TreeViewMode : public CompactViewMode { Q_OBJECT public: - TreeViewMode(PlaylistBox *l); + explicit TreeViewMode(PlaylistBox *l); virtual ~TreeViewMode(); virtual QString name() const; virtual void setShown(bool shown); virtual void setupDynamicPlaylists(); virtual void setDynamicListsFrozen(bool frozen); virtual void removeItem(const QString &item, unsigned column); virtual void addItems(const QStringList &items, unsigned column); signals: void signalPlaylistDestroyed(Playlist*); private: QMap m_searchCategories; QMap m_treeViewItems; QStringList m_pendingItemsToRemove; bool m_dynamicListsFrozen; bool m_setup; }; #endif // vim: set et sw=4 tw=0 sta: diff --git a/volumepopupbutton.h b/volumepopupbutton.h index a7853a74..0162dc65 100644 --- a/volumepopupbutton.h +++ b/volumepopupbutton.h @@ -1,56 +1,56 @@ /** * Copyright (c) 2009 Nikolaj Hald Nielsen * Copyright (c) 2009 Mark Kretschmann * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef VOLUMEPOPUPBUTTON_H #define VOLUMEPOPUPBUTTON_H #include class QAction; class QEvent; class QLabel; class QMenu; class QMouseEvent; class QWheelEvent; class VolumeSlider; class VolumePopupButton : public QToolButton { Q_OBJECT public: - VolumePopupButton( QWidget * parent ); + explicit VolumePopupButton( QWidget * parent ); void refresh(); protected: virtual void mouseReleaseEvent( QMouseEvent * event ); virtual void wheelEvent( QWheelEvent * event ); private slots: void volumeChanged( float newVolume ); void muteStateChanged( bool muted ); private: QLabel * m_volumeLabel; QMenu * m_volumeMenu; VolumeSlider * m_volumeSlider; QAction * m_muteAction; float m_volumeBeforeMute; }; #endif // VOLUMEPOPUPBUTTON_H diff --git a/webimagefetcher.h b/webimagefetcher.h index 701f6f06..de121028 100644 --- a/webimagefetcher.h +++ b/webimagefetcher.h @@ -1,56 +1,56 @@ /** * Copyright (C) 2004 Nathan Toone * Copyright (C) 2007 Michael Pyne * Copyright (C) 2012 Martin Sandsmark * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JUK_WEBIMAGEFETCHER_H #define JUK_WEBIMAGEFETCHER_H #include class KJob; class FileHandle; class WebImageFetcher : public QObject { Q_OBJECT public: - WebImageFetcher(QObject *parent); + explicit WebImageFetcher(QObject *parent); ~WebImageFetcher(); void setFile(const FileHandle &file); public slots: void abortSearch(); void searchCover(); signals: void signalCoverChanged(int coverId); private slots: void slotWebRequestFinished(KJob *job); void slotImageFetched(KJob *job); void slotCoverChosen(); private: class Private; Private *d; }; #endif // vim: set et sw=4 tw=0 sta: