Commit b46844f6 authored by Michael Pyne's avatar Michael Pyne
Browse files

Improve track sequencing by removing the track sequencing classes.

This removes one of my first contributions to JuK :(

But it's worth it because the extra code is not worth the complexity,
seeing as how the job is really pretty simple in the first place, even
with album random play and randomized playback.

I believe this also fixes some bugs, including some longstanding ones.
Bug 417551 (being unable to drag and drop into Play Queue) had some
related work in a recent commit but was still broken until now.

BUG:100356
BUG:166711
BUG:302250
BUG:303901
BUG:336637
BUG:353259
BUG:404157
BUG:417551
FIXED-IN:21.04

(cherry picked from commit 6aef3be8)
parent b718c542
......@@ -126,8 +126,6 @@ set(juk_SRCS
tagguesserconfigdlg.cpp
tagrenameroptions.cpp
tagtransactionmanager.cpp
tracksequenceiterator.cpp
tracksequencemanager.cpp
treeviewitemplaylist.cpp
upcomingplaylist.cpp
viewmode.cpp )
......
......@@ -17,7 +17,6 @@
#include "dynamicplaylist.h"
#include "collectionlist.h"
#include "playlistcollection.h"
#include "tracksequencemanager.h"
#include <QTimer>
#include <QObject>
......@@ -94,29 +93,17 @@ void DynamicPlaylist::slotReload()
void DynamicPlaylist::lower(QWidget *top)
{
if(top == this)
if(top == this || !playing())
return;
if(playing()) {
PlaylistList l;
l.append(this);
PlaylistList l;
l.append(this);
for(const auto &playlist : qAsConst(m_playlists)) {
if(!playlist)
continue;
playlist->synchronizePlayingItems(l, true);
}
for(const auto &playlist : qAsConst(m_playlists)) {
if(!playlist)
continue;
playlist->synchronizePlayingItems(l, true);
}
const auto playlistItems = PlaylistItem::playingItems();
const auto itemIt = std::find_if(
playlistItems.begin(), playlistItems.end(),
[this](const PlaylistItem *item) {
return item->playlist() != this;
});
if(itemIt != playlistItems.end())
TrackSequenceManager::instance()->setCurrentPlaylist((*itemIt)->playlist());
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -28,6 +28,8 @@ using GuardedPlaylist = QPointer<Playlist>;
/**
* A Playlist that is a union of other playlists that is created dynamically.
* Normally seen when you select multiple playlists at once (which is, or was,
* something you can do).
*/
class DynamicPlaylist : public Playlist
......
......@@ -60,6 +60,7 @@
#include "collectionlist.h"
#include "covermanager.h"
#include "tagtransactionmanager.h"
#include "juk_debug.h"
using namespace ActionCollection;
......
......@@ -208,8 +208,7 @@ void PlayerManager::play(const QString &file)
{
CollectionListItem *item = CollectionList::instance()->lookup(file);
if(item) {
Playlist::setPlaying(item);
play(item->file());
Playlist::setPlaying(item); // Will reentrantly call play(FileHandle)
}
}
......@@ -222,10 +221,8 @@ void PlayerManager::play()
emit seeked(0);
}
else {
// Will reentrantly call play(FileHandle)
m_playlistInterface->playNext();
const auto file = m_playlistInterface->currentFile();
play(file);
}
}
......@@ -445,16 +442,23 @@ void PlayerManager::setVolume(qreal volume)
void PlayerManager::setupAudio()
{
using namespace Phonon;
connect(m_output, &AudioOutput::mutedChanged, this, &PlayerManager::slotMutedChanged);
connect(m_output, &AudioOutput::volumeChanged, this, &PlayerManager::setVolume);
connect(m_media, &MediaObject::stateChanged, this, &PlayerManager::slotStateChanged);
connect(m_media, &MediaObject::currentSourceChanged, this, &PlayerManager::trackHasChanged);
connect(m_media, &MediaObject::totalTimeChanged, this, &PlayerManager::slotLength);
connect(m_media, &MediaObject::tick, this, &PlayerManager::slotTick);
connect(m_media, &MediaObject::aboutToFinish, this, &PlayerManager::trackAboutToFinish);
connect(m_media, &MediaObject::finished, this, &PlayerManager::slotFinished);
connect(m_media, &MediaObject::seekableChanged, this, &PlayerManager::slotSeekableChanged);
connect(m_output, &AudioOutput::mutedChanged,
this, &PlayerManager::slotMutedChanged);
connect(m_output, &AudioOutput::volumeChanged,
this, &PlayerManager::setVolume);
connect(m_media, &MediaObject::stateChanged,
this, &PlayerManager::slotStateChanged);
connect(m_media, &MediaObject::currentSourceChanged,
this, &PlayerManager::trackHasChanged);
connect(m_media, &MediaObject::totalTimeChanged,
this, &PlayerManager::slotLength);
connect(m_media, &MediaObject::tick,
this, &PlayerManager::slotTick);
connect(m_media, &MediaObject::finished,
this, &PlayerManager::slotFinished);
connect(m_media, &MediaObject::seekableChanged,
this, &PlayerManager::slotSeekableChanged);
m_media->setTickInterval(100);
}
......@@ -497,18 +501,4 @@ void PlayerManager::trackHasChanged(const Phonon::MediaSource &newSource)
}
}
void PlayerManager::trackAboutToFinish()
{
// Called when playback is in progress and a track is about to finish, gives us a
// chance to keep audio playback going without Phonon entering StoppedState
if(!m_playlistInterface)
return;
m_playlistInterface->playNext();
const auto file = m_playlistInterface->currentFile();
if(!file.isNull())
m_media->enqueue(QUrl::fromLocalFile(file.absFilePath()));
}
// vim: set et sw=4 tw=0 sta:
......@@ -97,7 +97,6 @@ public slots:
bool mute();
void trackHasChanged(const Phonon::MediaSource &newSource);
void trackAboutToFinish();
void setRandomPlayMode(const QString &randomMode);
......
......@@ -74,7 +74,6 @@
#include "collectionlist.h"
#include "filerenamer.h"
#include "actioncollection.h"
#include "tracksequencemanager.h"
#include "juktag.h"
#include "upcomingplaylist.h"
#include "deletedialog.h"
......@@ -196,31 +195,29 @@ FileHandle Playlist::currentFile() const
void Playlist::playFirst()
{
TrackSequenceManager::instance()->setNextItem(static_cast<PlaylistItem *>(
*QTreeWidgetItemIterator(const_cast<Playlist *>(this), QTreeWidgetItemIterator::NotHidden)));
action("forward")->trigger();
QTreeWidgetItemIterator listIt(const_cast<Playlist *>(this), QTreeWidgetItemIterator::NotHidden);
beginPlayingItem(static_cast<PlaylistItem *>(*listIt));
}
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();
#if 0
#else
playNext();
#endif
}
void Playlist::playNext()
{
TrackSequenceManager::instance()->setCurrentPlaylist(this);
setPlaying(TrackSequenceManager::instance()->nextItem());
auto nowPlaying = playingItem();
QTreeWidgetItemIterator listIt(nowPlaying, QTreeWidgetItemIterator::NotHidden);
PlaylistItem *next = nullptr;
if(*listIt) {
++listIt;
next = static_cast<PlaylistItem *>(*listIt);
}
beginPlayingItem(next);
}
void Playlist::stop()
......@@ -245,13 +242,11 @@ void Playlist::playPrevious()
}
else {
m_history.clear();
previous = TrackSequenceManager::instance()->previousItem();
QTreeWidgetItemIterator listIt(playingItem(), QTreeWidgetItemIterator::NotHidden);
previous = static_cast<PlaylistItem *>(*--listIt);
}
if(!previous)
previous = static_cast<PlaylistItem *>(playingItem()->itemAbove());
setPlaying(previous, false);
beginPlayingItem(previous);
}
void Playlist::setName(const QString &n)
......@@ -385,8 +380,6 @@ void Playlist::setSearch(PlaylistSearch* s)
for(int row = 0; row < topLevelItemCount(); ++row)
topLevelItem(row)->setHidden(true);
setItemsVisible(s->matchedItems(), true);
TrackSequenceManager::instance()->iterator()->playlistChanged();
}
void Playlist::setSearchEnabled(bool enabled)
......@@ -421,7 +414,6 @@ void Playlist::synchronizePlayingItems(Playlist *playlist, bool setMaster)
PlaylistItem *item = static_cast<PlaylistItem *>(*itemIt);
if(base == item->collectionItem()) {
item->setPlaying(true, setMaster);
TrackSequenceManager::instance()->setCurrent(item);
return;
}
}
......@@ -508,6 +500,19 @@ void Playlist::slotRenameFile()
emit signalEnableDirWatch(true);
}
void Playlist::slotBeginPlayback()
{
QTreeWidgetItemIterator visible(this, QTreeWidgetItemIterator::NotHidden);
PlaylistItem *item = static_cast<PlaylistItem *>(*visible);
if(item) {
beginPlayingItem(item);
}
else {
action("stop")->trigger();
}
}
void Playlist::slotViewCover()
{
const PlaylistItemList items = selectedItems();
......@@ -728,6 +733,18 @@ void Playlist::synchronizeItemsTo(const PlaylistItemList &itemList)
createItems(itemList);
}
void Playlist::beginPlayingItem(PlaylistItem *itemToPlay)
{
if(itemToPlay) {
setPlaying(itemToPlay, true);
m_collection->requestPlaybackFor(itemToPlay->file());
}
else {
setPlaying(nullptr);
action("stop")->trigger();
}
}
void Playlist::dragEnterEvent(QDragEnterEvent *e)
{
if(CoverDrag::isCover(e->mimeData())) {
......@@ -1359,15 +1376,15 @@ void Playlist::slotPopulateBackMenu() const
}
}
void Playlist::slotPlayFromBackMenu(QAction *backAction) const
void Playlist::slotPlayFromBackMenu(QAction *backAction)
{
int number = backAction->data().toInt();
if(number >= m_backMenuItems.size())
return;
TrackSequenceManager::instance()->setNextItem(m_backMenuItems[number]);
action("forward")->trigger();
auto &nextItem = m_backMenuItems[number];
beginPlayingItem(nextItem);
}
////////////////////////////////////////////////////////////////////////////////
......@@ -1459,33 +1476,28 @@ void Playlist::loadFile(const QString &fileName, const QFileInfo &fileInfo)
playlistItemsChanged();
}
// static
void Playlist::setPlaying(PlaylistItem *item, bool addToHistory)
{
if(playingItem() == item)
auto wasPlayingItem = playingItem();
if(wasPlayingItem == 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(wasPlayingItem && addToHistory) {
m_history.append(wasPlayingItem->collectionItem());
if(!item)
return;
const bool enableBack = !m_history.isEmpty();
action<KToolBarPopupAction>("back")->menu()->setEnabled(enableBack);
}
item->setPlaying(true);
if(wasPlayingItem) {
// NB: will clear the whole list of playing items recursively
wasPlayingItem->setPlaying(false);
}
bool enableBack = !m_history.isEmpty();
action<KToolBarPopupAction>("back")->menu()->setEnabled(enableBack);
if(item) {
item->setPlaying(true);
}
}
bool Playlist::playing() const
......@@ -2053,8 +2065,7 @@ void Playlist::slotPlayCurrent()
{
QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected);
PlaylistItem *next = static_cast<PlaylistItem *>(*it);
TrackSequenceManager::instance()->setNextItem(next);
action("forward")->trigger();
beginPlayingItem(next);
}
void Playlist::slotUpdateTime()
......
......@@ -372,6 +372,13 @@ public slots:
*/
void slotRenameFile();
/**
* Select a track to play after being stopped.
*
* @see playNext()
*/
void slotBeginPlayback();
/**
* 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.
......@@ -425,6 +432,12 @@ protected:
*/
void synchronizeItemsTo(const PlaylistItemList &itemList);
/**
* Completes the actions with the parent PlaylistCollection needed to
* actually start playing back the given PlaylistItem.
*/
virtual void beginPlayingItem(PlaylistItem *itemToPlay);
// the following are all reimplemented from base classes
virtual bool eventFilter(QObject *watched, QEvent *e) override;
......@@ -471,7 +484,7 @@ protected:
protected slots:
void slotPopulateBackMenu() const;
void slotPlayFromBackMenu(QAction *) const;
void slotPlayFromBackMenu(QAction *);
signals:
......
/**
* Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org>
* Copyright (C) 2021 Michael Pyne <mpyne@kde.org>
*
* 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
......@@ -51,7 +52,6 @@
#include "treeviewitemplaylist.h"
#include "actioncollection.h"
#include "cache.h"
#include "tracksequencemanager.h"
#include "tagtransactionmanager.h"
#include "playermanager.h"
#include "dbuscollectionproxy.h"
......@@ -130,11 +130,11 @@ PlaylistBox::PlaylistBox(PlayerManager *player, QWidget *parent, QStackedWidget
viewModeAction->setCurrentItem(m_viewModeIndex);
m_viewModes[m_viewModeIndex]->setShown(true);
TrackSequenceManager::instance()->setCurrentPlaylist(CollectionList::instance());
raise(CollectionList::instance());
m_contextMenu->addAction( viewModeAction );
connect(viewModeAction, SIGNAL(triggered(int)), this, SLOT(slotSetViewMode(int)));
m_contextMenu->addAction(viewModeAction);
connect(viewModeAction, &QAction::triggered,
this, &PlaylistBox::slotSetViewMode);
connect(this, SIGNAL(itemSelectionChanged()),
this, SLOT(slotPlaylistChanged()));
......@@ -145,9 +145,14 @@ PlaylistBox::PlaylistBox(PlayerManager *player, QWidget *parent, QStackedWidget
connect(this, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(slotShowContextMenu(QPoint)));
TagTransactionManager *tagManager = TagTransactionManager::instance();
connect(tagManager, SIGNAL(signalAboutToModifyTags()), SLOT(slotFreezePlaylists()));
connect(tagManager, SIGNAL(signalDoneModifyingTags()), SLOT(slotUnfreezePlaylists()));
connect(this, &PlaylistBox::signalPlayFile,
player, QOverload<const FileHandle &>::of(&PlayerManager::play));
const auto *tagManager = TagTransactionManager::instance();
connect(tagManager, &TagTransactionManager::signalAboutToModifyTags,
this, &PlaylistBox::slotFreezePlaylists);
connect(tagManager, &TagTransactionManager::signalDoneModifyingTags,
this, &PlaylistBox::slotUnfreezePlaylists);
setupUpcomingPlaylist();
......@@ -236,6 +241,12 @@ void PlaylistBox::scanFolders()
emit startupComplete();
}
bool PlaylistBox::requestPlaybackFor(const FileHandle &file)
{
emit signalPlayFile(file);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox public slots
////////////////////////////////////////////////////////////////////////////////
......@@ -465,7 +476,7 @@ void PlaylistBox::setDynamicListsFrozen(bool frozen)
void PlaylistBox::slotSavePlaylists()
{
qCDebug(JUK_LOG) << "Auto-saving playlists.\n";
qCDebug(JUK_LOG) << "Auto-saving playlists.";
PlaylistList l;
CollectionList *collection = CollectionList::instance();
......@@ -630,21 +641,9 @@ void PlaylistBox::slotDoubleClicked(QTreeWidgetItem *item)
{
if(!item)
return;
auto *playlist = static_cast<Item *>(item)->playlist();
TrackSequenceManager *manager = TrackSequenceManager::instance();
Item *playlistItem = static_cast<Item *>(item);
manager->setCurrentPlaylist(playlistItem->playlist());
manager->setCurrent(0); // Reset playback
PlaylistItem *next = manager->nextItem(); // Allow manager to choose
if(next) {
emit startFilePlayback(next->file());
playlistItem->playlist()->setPlaying(next);
}
else
action("stop")->trigger();
playlist->slotBeginPlayback();
}
void PlaylistBox::slotShowContextMenu(const QPoint &point)
......
......@@ -59,6 +59,8 @@ public:
// in managed directories.
virtual void scanFolders() override;
virtual bool requestPlaybackFor(const FileHandle &file) override;
/**
* For view modes that have dynamic playlists, this freezes them from
* removing playlists.
......@@ -89,7 +91,7 @@ signals:
void signalPlaylistDestroyed(Playlist *);
void signalMoveFocusAway(); // Handles keyboard scrolling up out of playlist
void startupComplete(); ///< Emitted after playlists are loaded.
void startFilePlayback(const FileHandle &file);
void signalPlayFile(const FileHandle &file);
private:
void readConfig();
......
/**
* Copyright (C) 2004 Scott Wheeler <wheeler@kde.org>
* Copyright (C) 2009 Michael Pyne <mpyne@kde.org>
* Copyright (C) 2009, 2021 Michael Pyne <mpyne@kde.org>
*
* 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
......@@ -52,7 +52,6 @@
#include "directorylist.h"
#include "mediafiles.h"
#include "playermanager.h"
#include "tracksequencemanager.h"
#include "juk.h"
//Laurent: readd it
......@@ -645,7 +644,7 @@ Playlist *PlaylistCollection::currentPlaylist() const
if(m_belowDistraction)
return m_belowDistraction;
if(m_upcomingPlaylist && m_upcomingPlaylist->active())
if(m_upcomingPlaylist && m_upcomingPlaylist->topLevelItemCount() > 0)
return m_upcomingPlaylist;
if(Playlist::playingItem())
......@@ -666,7 +665,6 @@ void PlaylistCollection::raise(Playlist *playlist)
if(m_dynamicPlaylist && currentPlaylist() == m_dynamicPlaylist)
m_dynamicPlaylist->lower(playlist);
TrackSequenceManager::instance()->setCurrentPlaylist(playlist);
playlist->applySharedSettings();
playlist->setSearchEnabled(m_searchEnabled);
m_playlistStack->setCurrentWidget(playlist);
......
......@@ -113,6 +113,10 @@ public:
// virtual to allow our QWidget subclass to emit a signal after we're done
virtual void scanFolders();
// Must be implemented by subclasses and is used to request a file
// start playback
virtual bool requestPlaybackFor(const FileHandle &file) = 0;
void createPlaylist();
void createSearchPlaylist();
void createFolderPlaylist();
......
......@@ -16,6 +16,8 @@
#include "playlistitem.h"
#include <algorithm>
#include <kiconloader.h>
#include <QCollator>
......@@ -29,6 +31,7 @@
#include "coverinfo.h"
#include "covermanager.h"
#include "tagtransactionmanager.h"
#include "juk_debug.h"
PlaylistItemList PlaylistItem::m_playingItems; // static
......@@ -136,6 +139,14 @@ void PlaylistItem::setText(int column, const QString &text)
playlist()->slotWeightDirty(column);
}
bool PlaylistItem::isPlaying() const
{
return std::any_of(m_playingItems.begin(), m_playingItems.end(),
[this](const PlaylistItem *playingItem) {
return this == playingItem;
});
}
void PlaylistItem::setPlaying(bool playing, bool master)
{
m_playingItems.removeAll(this);
......
......@@ -98,6 +98,7 @@ public:
virtual QString text(int column) const;
virtual void setText(int column, const QString &text);
bool isPlaying() const;
void setPlaying(bool playing = true, bool master = true);
void guessTagInfo(TagGuesser::Type type);
......
......@@ -210,8 +210,6 @@ void PlaylistSplitter::setupLayout()
connect(m_playlistBox, SIGNAL(startupComplete()), SLOT(slotEnable()));
connect(m_playlistBox, &QTreeWidget::currentItemChanged,
this, &PlaylistSplitter::slotCurrentPlaylistChanged);
connect(m_playlistBox, SIGNAL(startFilePlayback(FileHandle)),
m_player, SLOT(play(FileHandle)));
m_player->setPlaylistInterface(m_playlistBox);
......
/**
* Copyright (C) 2002-2004 Michael Pyne <mpyne@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "tracksequenceiterator.h"
#include <QAction>
#include <QRandomGenerator>
#include <KToggleAction>
#include "playlist.h"
#include "actioncollection.h"
#include "juktag.h"
#include "filehandle.h"
#include "juk_debug.h"