Commit 7c48ba7a authored by Scott Wheeler's avatar Scott Wheeler

Make the status bar work again by adding a PlaylistObserver class / pattern

to the playlistinterface.h file that can be subclassed by things that want
to track playlist changes using the generic API.

This also allowed removing a fair bit of cruft that was being thrown around
by signals in the Playlist class hierarchy.

This should be split up into two types of updates -- one for track changes
and another for data changes, but it was convenient in the first go to
roll them into one.  Splitting them up should be pretty trivial.

svn path=/trunk/kdemultimedia/juk/; revision=317251
parent 51d56c27
......@@ -30,6 +30,7 @@ juk_SOURCES = \
playlist.cpp \
playlistbox.cpp \
playlistcollection.cpp \
playlistinterface.cpp \
playlistitem.cpp \
playlistsearch.cpp \
playlistsplitter.cpp \
......
......@@ -99,7 +99,7 @@ void CollectionList::clearItems(const PlaylistItemList &items)
clearItem(*it, false);
}
emit signalCountChanged(this);
PlaylistInterface::update();
}
////////////////////////////////////////////////////////////////////////////////
......@@ -244,7 +244,7 @@ void CollectionListItem::refresh()
}
repaint();
playlist()->emitDataChanged();
playlist()->PlaylistInterface::update();
}
////////////////////////////////////////////////////////////////////////////////
......@@ -263,7 +263,7 @@ CollectionListItem::CollectionListItem(const FileHandle &file) :
if(file.tag()) {
refresh();
l->emitCountChanged();
l->PlaylistInterface::update();
// l->addWatched(m_path);
}
else
......
......@@ -20,6 +20,25 @@
#include "dynamicplaylist.h"
#include "collectionlist.h"
// TODO: this current updates even when things are just played in the watched
// playlists. There should be different update types and this should only
// watch the data changes.
class PlaylistDirtyObserver : public PlaylistObserver
{
public:
PlaylistDirtyObserver(DynamicPlaylist *parent, Playlist *playlist) :
PlaylistObserver(playlist),
m_parent(parent)
{
}
virtual void update() { m_parent->slotSetDirty(); }
private:
DynamicPlaylist *m_parent;
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
......@@ -34,13 +53,22 @@ DynamicPlaylist::DynamicPlaylist(const PlaylistList &playlists,
{
setSorting(columns() + 1);
for(PlaylistList::ConstIterator it = m_playlists.begin(); it != m_playlists.end(); ++it) {
connect(*it, SIGNAL(signalDataChanged()), this, SLOT(slotSetDirty()));
connect(*it, SIGNAL(signalCountChanged(Playlist *)), this, SLOT(slotSetDirty()));
}
for(PlaylistList::ConstIterator it = playlists.begin(); it != playlists.end(); ++it)
m_observers.append(new PlaylistDirtyObserver(this, *it));
connect(CollectionList::instance(), SIGNAL(signalCollectionChanged()), this, SLOT(slotSetDirty()));
}
DynamicPlaylist::~DynamicPlaylist()
{
for(QValueList<PlaylistObserver *>::ConstIterator it = m_observers.begin();
it != m_observers.end();
++it)
{
delete *it;
}
}
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////
......
......@@ -36,12 +36,15 @@ public:
const QString &name = QString::null,
const QString &iconName = "midi");
virtual ~DynamicPlaylist();
public slots:
/**
* Reimplemented so that it will reload all of the playlists that are
* associated with the dynamic list.
*/
virtual void slotReload();
void slotSetDirty() { m_dirty = true; }
protected:
/**
......@@ -70,9 +73,6 @@ protected:
*/
virtual void updateItems();
protected slots:
void slotSetDirty() { m_dirty = true; }
private:
/**
* Checks to see if the current list of items is "dirty" and if so updates
......@@ -85,6 +85,7 @@ private slots:
void slotUpdateItems();
private:
QValueList<PlaylistObserver *> m_observers;
PlaylistItemList m_siblings;
PlaylistList m_playlists;
bool m_dirty;
......
......@@ -128,7 +128,7 @@ QDataStream &operator>>(QDataStream &s, HistoryPlaylist &p)
after->setDateTime(dateTime);
}
p.emitCountChanged();
p.PlaylistInterface::update();
return s;
}
......
......@@ -89,12 +89,11 @@ KActionCollection *JuK::actionCollection() const
void JuK::setupLayout()
{
m_statusLabel = new StatusLabel(statusBar());
statusBar()->addWidget(m_statusLabel, 1);
m_splitter = new PlaylistSplitter(this, "playlistSplitter");
setCentralWidget(m_splitter);
m_statusLabel = new StatusLabel(m_splitter->playlist(), statusBar());
statusBar()->addWidget(m_statusLabel, 1);
PlayerManager::instance()->setStatusLabel(m_statusLabel);
m_splitter->setFocus();
......
......@@ -217,16 +217,13 @@ void PlayerManager::play(const FileHandle &file)
m_playlistInterface->playNext();
m_file = m_playlistInterface->currentFile();
if(!m_file.isNull()) {
if(!m_file.isNull())
player()->play(m_file);
m_statusLabel->setPlayingItemInfo(m_file, m_playlistInterface);
}
}
}
else {
m_file = file;
player()->play(file);
m_statusLabel->setPlayingItemInfo(file, m_playlistInterface);
}
// Make sure that the player() actually starts before doing anything.
......@@ -286,8 +283,6 @@ void PlayerManager::stop()
m_sliderAction->trackPositionSlider()->setValue(0);
m_sliderAction->trackPositionSlider()->setEnabled(false);
m_statusLabel->clear();
player()->stop();
m_playlistInterface->stop();
......
......@@ -515,8 +515,7 @@ void Playlist::clearItem(PlaylistItem *item, bool emitChanged)
if(!m_randomList.isEmpty() && !m_visibleChanged)
m_randomList.remove(item);
delete item;
if(emitChanged)
emit signalCountChanged(this);
PlaylistInterface::update();
}
void Playlist::clearItems(const PlaylistItemList &items)
......@@ -524,7 +523,7 @@ void Playlist::clearItems(const PlaylistItemList &items)
for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it)
clearItem(*it, false);
emit signalCountChanged(this);
PlaylistInterface::update();
}
QStringList Playlist::files()
......@@ -814,7 +813,7 @@ void Playlist::removeFromDisk(const PlaylistItemList &items)
}
}
emit signalCountChanged(this);
PlaylistInterface::update();
}
}
......@@ -1022,7 +1021,7 @@ void Playlist::addFiles(const QStringList &files, bool importPlaylists,
after = addFile(*it, importPlaylists, after);
slotWeightDirty();
emit signalCountChanged(this);
PlaylistInterface::update();
KApplication::restoreOverrideCursor();
}
......@@ -1183,8 +1182,6 @@ void Playlist::setup()
setItemMargin(3);
connect(header(), SIGNAL(indexChange(int, int, int)), this, SLOT(slotColumnOrderChanged(int, int, int)));
connect(this, SIGNAL(signalDataChanged()), this, SIGNAL(signalChanged()));
connect(this, SIGNAL(signalCountChanged(Playlist *)), this, SIGNAL(signalChanged()));
setSorting(1);
}
......@@ -1224,7 +1221,7 @@ void Playlist::loadFile(const QString &fileName, const QFileInfo &fileInfo)
file.close();
emit signalCountChanged(this);
PlaylistInterface::update();
m_disableColumnWidthUpdates = false;
}
......
......@@ -100,9 +100,6 @@ public:
* preferable to delay this notification until after other operations have
* completed. In those cases set \a emitChanged to false and call the
* signal directly.
*
* @see signalCountChanged()
* @see emitCountChanged()
*/
virtual void clearItem(PlaylistItem *item, bool emitChanged = true);
......@@ -251,15 +248,6 @@ public:
*/
void setSearchEnabled(bool searchEnabled);
/**
* Emits a signal indicating that the number of items have changed. This
* is useful in conjunction with createItem() where emitChanged is false.
*
* In many situations it is not practical for speed reasons to trigger the
* actions associated with signalCountChanged() after each insertion.
*/
void emitCountChanged() { emit signalCountChanged(this); }
/**
* Marks \a item as either selected or deselected based.
*/
......@@ -289,8 +277,6 @@ public:
*/
void applySharedSettings();
void emitDataChanged() { emit signalDataChanged(); }
/**
* Returns true if full path sort is currently enabled for the file column.
*/
......@@ -426,30 +412,6 @@ signals:
*/
void signalNameChanged(const QString &name);
/**
* This signal is emitted when items are added to or removed from the list.
*
* \see signalDataChanged()
* \see signalChanged()
*/
void signalCountChanged(Playlist *);
/**
* This signal is connected to PlaylistItem::refreshed() in the PlaylistItem
* class. It is emitted when a playlist item's data has been changed.
*
* \see signalCountChanged()
* \see signalChanged()
*/
void signalDataChanged();
/**
* This is the union of signalDataChanged() and signalCountChanged().
* It is emitted with either quantity or value of the PlaylistItems are
* changed.
*/
void signalChanged();
/**
* This signal is emitted just before a playlist item is removed from the
* list allowing for any cleanup that needs to happen. Typically this
......@@ -661,10 +623,8 @@ ItemType *Playlist::createItem(const FileHandle &file, QListViewItem *after,
if(!m_randomList.isEmpty() && !m_visibleChanged)
m_randomList.append(i);
emit signalCountChanged(this);
if(emitChanged)
emit signalCountChanged(this);
PlaylistInterface::update();
return i;
}
......@@ -691,7 +651,7 @@ void Playlist::createItems(const QValueList<SiblingType *> &siblings)
}
}
emit signalCountChanged(this);
PlaylistInterface::update();
m_disableColumnWidthUpdates = false;
slotWeightDirty();
}
......
......@@ -44,7 +44,8 @@ using namespace ActionCollection;
PlaylistCollection::PlaylistCollection(QWidgetStack *playlistStack) :
m_playlistStack(playlistStack),
m_importPlaylists(true),
m_searchEnabled(true)
m_searchEnabled(true),
m_playing(false)
{
m_actionHandler = new ActionHandler(this);
PlayerManager::instance()->setPlaylistInterface(this);
......@@ -77,22 +78,35 @@ int PlaylistCollection::time() const
void PlaylistCollection::playFirst()
{
m_playing = true;
currentPlaylist()->playFirst();
update();
}
void PlaylistCollection::playPrevious()
{
m_playing = true;
currentPlaylist()->playPrevious();
update();
}
void PlaylistCollection::playNext()
{
m_playing = true;
currentPlaylist()->playNext();
update();
}
void PlaylistCollection::stop()
{
m_playing = false;
currentPlaylist()->stop();
update();
}
bool PlaylistCollection::playing() const
{
return m_playing;
}
void PlaylistCollection::open(const QStringList &l)
......
......@@ -50,6 +50,7 @@ public:
virtual void playNext();
virtual void playPrevious();
virtual void stop();
virtual bool playing() const;
// Play the top item of the current playlist
void playFirst();
......@@ -109,6 +110,7 @@ private:
bool m_importPlaylists;
bool m_restore;
bool m_searchEnabled;
bool m_playing;
};
/**
......@@ -158,6 +160,7 @@ private slots:
signals:
void signalSelectedItemsChanged();
void signalCountChanged();
private:
PlaylistCollection *m_collection;
......
/***************************************************************************
copyright : (C) 2004 by Scott Wheeler
email : wheeler@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. *
* *
***************************************************************************/
#include "playlistinterface.h"
////////////////////////////////////////////////////////////////////////////////
// Watched implementation
////////////////////////////////////////////////////////////////////////////////
void Watched::update()
{
for(QValueList<PlaylistObserver *>::ConstIterator it = m_observers.begin();
it != m_observers.end();
++it)
{
(*it)->update();
}
}
void Watched::addObserver(PlaylistObserver *observer)
{
m_observers.append(observer);
}
void Watched::removeObserver(PlaylistObserver *observer)
{
m_observers.remove(observer);
}
Watched::~Watched()
{
QValueList<PlaylistObserver *> l = m_observers;
for(QValueList<PlaylistObserver *>::Iterator it = l.begin(); it != l.end(); ++it)
(*it)->clearWatched();
}
////////////////////////////////////////////////////////////////////////////////
// PlaylistObserver implementation
////////////////////////////////////////////////////////////////////////////////
PlaylistObserver::~PlaylistObserver()
{
if(m_playlist)
m_playlist->removeObserver(this);
}
PlaylistObserver::PlaylistObserver(PlaylistInterface *playlist) :
m_playlist(playlist)
{
playlist->addObserver(this);
}
const PlaylistInterface *PlaylistObserver::playlist() const
{
return m_playlist;
}
/***************************************************************************
playlistinterface.h
-------------------
begin : Fri Feb 27 2004
copyright : (C) 2004 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
......@@ -21,13 +18,30 @@
#include "filehandle.h"
#include <qstring.h>
#include <qvaluelist.h>
class PlaylistObserver;
class Watched
{
public:
void addObserver(PlaylistObserver *observer);
void removeObserver(PlaylistObserver *observer);
virtual void update();
protected:
virtual ~Watched();
private:
QValueList<PlaylistObserver *> m_observers;
};
/**
* This is a simple interface that should be used by things that implement a
* playlist-like API.
*/
class PlaylistInterface
class PlaylistInterface : public Watched
{
public:
virtual QString name() const = 0;
......@@ -38,6 +52,24 @@ public:
virtual void playNext() = 0;
virtual void playPrevious() = 0;
virtual void stop() = 0;
virtual bool playing() const = 0;
};
class PlaylistObserver
{
public:
virtual ~PlaylistObserver();
virtual void update() = 0;
void clearWatched() { m_playlist = 0; }
protected:
PlaylistObserver(PlaylistInterface *playlist);
const PlaylistInterface *playlist() const;
private:
PlaylistInterface *m_playlist;
};
#endif
......@@ -75,7 +75,7 @@ void SearchPlaylist::updateItems()
clearItem(it.current(), false);
if(!oldItems.isEmpty() && newItems.isEmpty())
emit signalCountChanged(this);
PlaylistInterface::update();
createItems(newItems);
}
......
......@@ -37,9 +37,9 @@ using namespace ActionCollection;
// public methods
////////////////////////////////////////////////////////////////////////////////
StatusLabel::StatusLabel(QWidget *parent, const char *name) :
StatusLabel::StatusLabel(PlaylistInterface *playlist, QWidget *parent, const char *name) :
QHBox(parent, name),
mode(PlaylistInfo),
PlaylistObserver(playlist),
m_showTimeRemaining(false)
{
QFrame *trackAndPlaylist = new QFrame(this);
......@@ -92,6 +92,8 @@ StatusLabel::StatusLabel(QWidget *parent, const char *name) :
connect(jumpButton, SIGNAL(clicked()), action("showPlaying"), SLOT(activate()));
installEventFilter(this);
update();
}
StatusLabel::~StatusLabel()
......@@ -99,55 +101,41 @@ StatusLabel::~StatusLabel()
}
void StatusLabel::setPlaylist(PlaylistInterface *playlist)
void StatusLabel::update()
{
int days = playlist->time() / (60 * 60 * 24);
int hours = playlist->time() / (60 * 60) % 24;
int minutes = playlist->time() / 60 % 60;
int seconds = playlist->time() % 60;
QString time;
kdDebug(65432) << k_funcinfo << playlist()->playing() << endl;
if(days > 0) {
time = i18n("1 day", "%n days", days);
time.append(" ");
if(playlist()->playing()) {
FileHandle file = playlist()->currentFile();
QString text = file.tag()->artist() + " - " + file.tag()->title();
m_trackLabel->setText(text);
m_playlistLabel->setText(playlist()->name().simplifyWhiteSpace());
}
if(days > 0 || hours > 0)
time.append(QString().sprintf("%1d:%02d:%02d", hours, minutes, seconds));
else
time.append(QString().sprintf("%1d:%02d", minutes, seconds));
m_playlistLength = i18n("1 item", "%n items", playlist->count()) + " - " + time;
m_playlistName = playlist->name();
if(mode == PlaylistInfo) {
m_playlistLabel->setText(m_playlistName);
m_trackLabel->setText(m_playlistLength);
else {
setItemTotalTime(0);
setItemCurrentTime(0);
int days = playlist()->time() / (60 * 60 * 24);
int hours = playlist()->time() / (60 * 60) % 24;
int minutes = playlist()->time() / 60 % 60;
int seconds = playlist()->time() % 60;
QString time;
if(days > 0) {
time = i18n("1 day", "%n days", days);
time.append(" ");
}
if(days > 0 || hours > 0)
time.append(QString().sprintf("%1d:%02d:%02d", hours, minutes, seconds));
else
time.append(QString().sprintf("%1d:%02d", minutes, seconds));
m_playlistLabel->setText(playlist()->name());
m_trackLabel->setText(i18n("1 item", "%n items", playlist()->count()) + " - " + time);
}
}
void StatusLabel::setPlayingItemInfo(const FileHandle &file, const PlaylistInterface *playlist)
{
mode = PlayingItemInfo;
QString text = file.tag()->artist() + " - " + file.tag()->title();
m_trackLabel->setText(text);
m_playlistLabel->setText(playlist->name().simplifyWhiteSpace());
}
void StatusLabel::clear()
{
setItemTotalTime(0);
setItemCurrentTime(0);
mode = PlaylistInfo;
m_playlistLabel->setText(m_playlistName);
m_trackLabel->setText(m_playlistLength);
}
////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////
......@@ -158,26 +146,26 @@ void StatusLabel::updateTime()
int seconds;
if(m_showTimeRemaining) {
minutes = int((m_itemTotalTime - m_itemCurrentTime) / 60);
seconds = (m_itemTotalTime - m_itemCurrentTime) % 60;
minutes = int((m_itemTotalTime - m_itemCurrentTime) / 60);
seconds = (m_itemTotalTime - m_itemCurrentTime) % 60;
}
else {
minutes = int(m_itemCurrentTime / 60);
seconds = m_itemCurrentTime % 60;
minutes = int(m_itemCurrentTime / 60);
seconds = m_itemCurrentTime % 60;
}
int totalMinutes = int(m_itemTotalTime / 60);
int totalSeconds = m_itemTotalTime % 60;
QString timeString = formatTime(minutes, seconds) + " / " +
formatTime(totalMinutes, totalSeconds);
formatTime(totalMinutes, totalSeconds);
m_itemTimeLabel->setText(timeString);
}
bool StatusLabel::eventFilter(QObject *o, QEvent *e)
{
if(!o || !e)
return false;
return false;
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(e);
if(mouseEvent &&
......@@ -185,14 +173,14 @@ bool StatusLabel::eventFilter(QObject *o, QEvent *e)