Commit 3a9ea139 authored by Scott Wheeler's avatar Scott Wheeler

Updates from the weekend. Lots of cleanups. Now also, all items in the

playlists are "children" of the CollectionListItems, and as such always
share their PlaylistItem::Data sections.  I also fixed things so that
an update in one item will automatically trigger updates in the others.

svn path=/trunk/kdemultimedia/juk/; revision=178225
parent 28dc5e48
......@@ -3,12 +3,12 @@ bin_PROGRAMS = juk
## INCLUDES were found outside kdevelop specific part
juk_SOURCES = collectionlist.cpp playlistitem.cpp playlist.cpp playlistsplitter.cpp listboxpixmap.cpp playlistbox.cpp tageditor.cpp cache.cpp audiodata.cpp genrelistreader.cpp genrelistlist.cpp genrelist.cpp genre.cpp player.cpp tag.cpp customaction.cpp playlistwidget.cpp slideraction.cpp taggerwidget.cpp juk.cpp main.cpp
juk_SOURCES = collectionlist.cpp playlistitem.cpp playlist.cpp playlistsplitter.cpp listboxpixmap.cpp playlistbox.cpp tageditor.cpp cache.cpp audiodata.cpp genrelistreader.cpp genrelistlist.cpp genrelist.cpp genre.cpp player.cpp tag.cpp customaction.cpp slideraction.cpp juk.cpp main.cpp
juk_LDADD = -lid3 -lsoundserver_idl $(LIB_KFILE) $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) $(LIBSOCKET)
SUBDIRS = pics data
EXTRA_DIST = main.cpp juk.cpp juk.h juk.desktop jukui.rc taggerwidget.cpp taggerwidget.h slideraction.cpp slideraction.h playlistwidget.cpp playlistwidget.h customaction.h customaction.cpp tag.cpp tag.h player.cpp player.h genre.h genre.cpp hi16-app-juk.png hi32-app-juk.png hi48-app-juk.png genrelist.cpp genrelist.h genrelistlist.cpp genrelistlist.h genrelistreader.cpp genrelistreader.h audiodata.cpp audiodata.h cache.cpp cache.h tageditor.cpp tageditor.h playlistbox.cpp playlistbox.h listboxpixmap.cpp listboxpixmap.h playlistsplitter.cpp playlistsplitter.h playlist.cpp playlist.h playlistitem.cpp playlistitem.h collectionlist.cpp collectionlist.h
EXTRA_DIST = main.cpp juk.cpp juk.h juk.desktop jukui.rc slideraction.cpp slideraction.h customaction.h customaction.cpp tag.cpp tag.h player.cpp player.h genre.h genre.cpp hi16-app-juk.png hi32-app-juk.png hi48-app-juk.png genrelist.cpp genrelist.h genrelistlist.cpp genrelistlist.h genrelistreader.cpp genrelistreader.h audiodata.cpp audiodata.h cache.cpp cache.h tageditor.cpp tageditor.h playlistbox.cpp playlistbox.h listboxpixmap.cpp listboxpixmap.h playlistsplitter.cpp playlistsplitter.h playlist.cpp playlist.h playlistitem.cpp playlistitem.h collectionlist.cpp collectionlist.h
install-data-local:
$(mkinstalldirs) $(kde_appsdir)/Multimedia/
......
This diff is collapsed.
......@@ -15,9 +15,69 @@
* *
***************************************************************************/
#include <kdebug.h>
#include "collectionlist.h"
CollectionList::CollectionList(QWidget *parent, const char *name) : Playlist(parent, name)
////////////////////////////////////////////////////////////////////////////////
// static methods
////////////////////////////////////////////////////////////////////////////////
CollectionList *CollectionList::list = 0;
CollectionList *CollectionList::instance()
{
return(list);
}
void CollectionList::initialize(QWidget *parent)
{
list = new CollectionList(parent);
}
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
void CollectionList::append(const QString &item)
{
Playlist::append(item);
emit(collectionChanged());
}
void CollectionList::append(const QStringList &items)
{
Playlist::append(items);
emit(collectionChanged());
}
CollectionListItem *CollectionList::lookup(const QString &file)
{
return(itemsDict.find(file));
}
PlaylistItem *CollectionList::createItem(const QFileInfo &file)
{
PlaylistItem *item = new CollectionListItem(file);
}
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////
void CollectionListItem::refresh()
{
refreshImpl();
// This is connected to refreshImpl() for all of the items children.
emit(refreshed());
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
CollectionList::CollectionList(QWidget *parent) : Playlist(parent, "collectionList")
{
}
......@@ -26,3 +86,52 @@ CollectionList::~CollectionList()
{
}
void CollectionList::appendImpl(const QString &item)
{
if(!lookup(item))
Playlist::appendImpl(item);
}
void CollectionList::addToDict(const QString &file, CollectionListItem *item)
{
itemsDict.replace(file, item);
}
void CollectionList::removeFromDict(const QString &file)
{
itemsDict.remove(file);
}
////////////////////////////////////////////////////////////////////////////////
// CollectionListItem public methods
////////////////////////////////////////////////////////////////////////////////
CollectionListItem::CollectionListItem(const QFileInfo &file) : PlaylistItem(CollectionList::instance())
{
CollectionList *l = CollectionList::instance();
if(l) {
l->addToDict(file.absFilePath(), this);
setData(Data::newUser(file));
refresh();
connect(this, SIGNAL(refreshed()), l, SIGNAL(dataChanged()));
}
else
kdError() << "CollectionListItems should not be created before"
<< "CollectionList::initialize() has been called." << endl;
}
CollectionListItem::~CollectionListItem()
{
CollectionList *l = CollectionList::instance();
if(l)
l->removeFromDict(getData()->absFilePath());
}
void CollectionListItem::addChildItem(PlaylistItem *child)
{
connect(child, SIGNAL(refreshed()), this, SLOT(refresh()));
connect(this, SIGNAL(refreshed()), child, SLOT(refreshImpl()));
}
#include "collectionlist.moc"
......@@ -18,14 +18,63 @@
#ifndef COLLECTIONLIST_H
#define COLLECTIONLIST_H
#include <qdict.h>
#include "playlist.h"
#include "playlistitem.h"
/** 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
"initialize()" method.
*/
class CollectionListItem;
class CollectionList : public Playlist
{
friend class CollectionListItem;
Q_OBJECT
public:
CollectionList(QWidget *parent = 0, const char *name = 0);
static CollectionList *instance();
static void initialize(QWidget *parent);
virtual void append(const QString &item);
virtual void append(const QStringList &items);
CollectionListItem *lookup(const QString &file);
virtual PlaylistItem *createItem(const QFileInfo &file);
protected:
CollectionList(QWidget *parent = 0);
virtual ~CollectionList();
virtual void appendImpl(const QString &item);
void addToDict(const QString &file, CollectionListItem *item);
void removeFromDict(const QString &file);
private:
static CollectionList *list;
QDict<CollectionListItem> itemsDict;
};
class CollectionListItem : public PlaylistItem
{
Q_OBJECT
public:
CollectionListItem(const QFileInfo &file);
virtual ~CollectionListItem();
void addChildItem(PlaylistItem *child);
public slots:
virtual void refresh();
};
#endif
......@@ -16,14 +16,11 @@
***************************************************************************/
#include <klocale.h>
#include <keditcl.h>
#include <kfiledialog.h>
#include <kiconloader.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <qsplitter.h>
#include "juk.h"
////////////////////////////////////////////////////////////////////////////////
......@@ -32,6 +29,8 @@
JuK::JuK(QWidget *parent, const char *name) : KMainWindow(parent, name)
{
// Expect segfaults if you change this order.
setupActions();
setupLayout();
setupPlayer();
......@@ -64,6 +63,9 @@ void JuK::setupActions()
KStdAction::paste(this, SLOT(paste()), actionCollection());
KStdAction::selectAll(this, SLOT(selectAll()), actionCollection());
// view menu
showEditorAction = new KToggleAction(i18n("Show Tag Editor"), 0, actionCollection(), "showEditor");
// play menu
playAction = new KAction(i18n("&Play"), "1rightarrow", 0, this, SLOT(playFile()), actionCollection(), "playFile");
pauseAction = new KAction(i18n("P&ause"), "player_pause", 0, this, SLOT(pauseFile()), actionCollection(), "pauseFile");
......@@ -82,6 +84,7 @@ void JuK::setupLayout()
splitter = new PlaylistSplitter(this, "playlistSplitter");
setCentralWidget(splitter);
connect(showEditorAction, SIGNAL(toggled(bool)), splitter, SLOT(setEditorVisible(bool)));
// set the slider to the proper orientation and make it stay that way
sliderAction->updateOrientation();
......@@ -89,6 +92,8 @@ void JuK::setupLayout()
// playlist item activation connection
connect(splitter, SIGNAL(playlistDoubleClicked(QListViewItem *)), this, SLOT(playItem(QListViewItem *)));
splitter->setFocus();
}
void JuK::setupPlayer()
......@@ -132,6 +137,12 @@ void JuK::readConfig()
sliderAction->getVolumeSlider()->setValue(volume);
}
}
{ // view Settings
KConfigGroupSaver saver(config, "View");
bool showEditor = config->readBoolEntry("ShowEditor", true);
showEditorAction->setChecked(showEditor);
splitter->setEditorVisible(showEditor);
}
}
void JuK::saveConfig()
......@@ -142,6 +153,10 @@ void JuK::saveConfig()
if(sliderAction && sliderAction->getVolumeSlider())
config->writeEntry("Volume", sliderAction->getVolumeSlider()->value());
}
{ // view settings
KConfigGroupSaver saver(config, "View");
config->writeEntry("ShowEditor", showEditorAction->isChecked());
}
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -28,13 +28,9 @@
#include <kmainwindow.h>
#include <qtimer.h>
#include <qwidgetstack.h>
#include "taggerwidget.h"
#include "playlistwidget.h"
#include "slideraction.h"
#include "player.h"
#include "playlistbox.h"
#include "playlistsplitter.h"
class JuK : public KMainWindow
......@@ -57,6 +53,7 @@ private:
PlaylistSplitter *splitter;
// actions
KToggleAction *showEditorAction;
SliderAction *sliderAction;
KAction *playAction;
KAction *pauseAction;
......
......@@ -11,7 +11,10 @@
<Separator/>
<Action name="file_quit"/>
</Menu>
<Menu name="player"><text>&amp;Player</text>
<Menu name="view"><text>&amp;View</text>
<Action name="showEditor"/>
</Menu>
<Menu name="player"><text>&amp;Player</text>
<Action name="playFile"/>
<Action name="pauseFile"/>
<Action name="stopFile"/>
......@@ -27,7 +30,6 @@
<Action name="edit_cut"/>
<Action name="edit_copy"/>
<Action name="edit_paste"/>
<Separator lineSeparator="true"/>
</ToolBar>
<ToolBar name="playToolBar" noMerge="1"><text>Play Toolbar</text>
......
......@@ -26,6 +26,7 @@
#include <qptrlist.h>
#include "playlist.h"
#include "collectionlist.h"
////////////////////////////////////////////////////////////////////////////////
// public members
......@@ -34,6 +35,7 @@
Playlist::Playlist(QWidget *parent, const char *name) : KListView(parent, name)
{
setup();
allowDuplicates = false;
}
Playlist::~Playlist()
......@@ -43,38 +45,27 @@ Playlist::~Playlist()
void Playlist::append(const QString &item)
{
collectionListChanged = false;
QApplication::setOverrideCursor(Qt::waitCursor);
appendImpl(item);
QApplication::restoreOverrideCursor();
emit(collectionChanged(this));
if(collectionListChanged)
emit(collectionChanged());
}
void Playlist::append(const QStringList &items)
{
collectionListChanged = false;
QApplication::setOverrideCursor(Qt::waitCursor);
for(QStringList::ConstIterator it = items.begin(); it != items.end(); ++it)
appendImpl(*it);
QApplication::restoreOverrideCursor();
emit(collectionChanged(this));
}
void Playlist::append(PlaylistItem *item)
{
if(item && members.contains(item->absFilePath()) == 0) {
members.append(item->absFilePath());
(void) new PlaylistItem(*item, this);
}
emit(collectionChanged(this));
}
void Playlist::append(QPtrList<PlaylistItem> &items)
{
QPtrListIterator<PlaylistItem> it(items);
while(it.current()) {
append(it.current());
++it;
}
// the emit(collectionChanged()) is handled in the above function
if(collectionListChanged)
emit(collectionChanged());
}
void Playlist::clearItems(const QPtrList<PlaylistItem> &items)
......@@ -132,6 +123,47 @@ QStringList &Playlist::getAlbumList()
return(albumList);
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
PlaylistItem *Playlist::createItem(const QFileInfo &file)
{
CollectionListItem *item = CollectionList::instance()->lookup(file.absFilePath());
if(item)
return(new PlaylistItem(item, this));
else if(CollectionList::instance()) {
item = new CollectionListItem(file);
collectionListChanged = true;
return(new PlaylistItem(item, this));
}
else
return(0);
}
void Playlist::appendImpl(const QString &item)
{
processEvents();
QFileInfo file(QDir::cleanDirPath(item));
if(file.exists()) {
if(file.isDir()) {
QDir dir(file.filePath());
QStringList dirContents=dir.entryList();
for(QStringList::Iterator it = dirContents.begin(); it != dirContents.end(); ++it)
if(*it != "." && *it != "..")
appendImpl(file.filePath() + QDir::separator() + *it);
}
else {
QString extension = file.extension(false);
if(extensions.contains(extension) > 0 && (members.contains(file.absFilePath()) == 0 || allowDuplicates)) {
members.append(file.absFilePath());
(void) createItem(file);
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
......@@ -161,31 +193,6 @@ void Playlist::setup()
connect(this, SIGNAL(selectionChanged()), this, SLOT(emitSelected()));
}
void Playlist::appendImpl(const QString &item)
{
processEvents();
QFileInfo file(QDir::cleanDirPath(item));
if(file.exists()) {
if(file.isDir()) {
QDir dir(file.filePath());
QStringList dirContents=dir.entryList();
for(QStringList::Iterator it = dirContents.begin(); it != dirContents.end(); ++it) {
if(*it != "." && *it != "..") {
appendImpl(file.filePath() + QDir::separator() + *it);
}
}
}
else {
QString extension = file.extension(false);
if(extensions.contains(extension) > 0 && members.contains(file.absFilePath()) == 0) {
members.append(file.absFilePath());
(void) new PlaylistItem(file, this);
}
}
}
}
void Playlist::processEvents()
{
if(processed == 0)
......
......@@ -33,8 +33,6 @@ public:
virtual void append(const QString &item);
virtual void append(const QStringList &items);
virtual void append(PlaylistItem *item);
virtual void append(QPtrList<PlaylistItem> &items);
virtual void clearItems(const QPtrList<PlaylistItem> &items);
......@@ -43,6 +41,9 @@ public:
void remove();
void remove(const QPtrList<PlaylistItem> &items);
/** Allow duplicate files in the playlist. */
void setAllowDuplicates(bool allow);
// These are used in a hard-core, encapsulation breaking way and should be
// replaced soon (see PlaylistItem). Unfortunately they're more efficient
// than elegant solutions. These also should be removed by doing the
......@@ -52,27 +53,39 @@ public:
QStringList &getArtistList();
QStringList &getAlbumList();
protected:
/** This is being used as a mini-factory of sorts to make the construction
of PlaylistItems virtual. */
virtual PlaylistItem *createItem(const QFileInfo &file);
virtual void appendImpl(const QString &item);
private:
void setup();
void appendImpl(const QString &item);
void processEvents();
QStringList extensions;
QStringList members;
QStringList artistList;
QStringList albumList;
void processEvents();
int processed;
bool collectionListChanged;
bool allowDuplicates;
private slots:
void emitSelected();
signals:
/** This signal is connected to PlaylistItem::refreshed() in the
PlaylistItem class. */
void dataChanged();
// This signal should later be moved to the "CollectionList" subclass of
// Playlist.
/** This signal is emitted when items are added to the collection list.
This happens in the createItem() method when items are added to the
collection. */
void collectionChanged();
void collectionChanged(Playlist *list);
/** This is emitted when the playlist selection is changed. This is used
primarily to notify the TagEditor of the new data. */
void selectionChanged(const QPtrList<PlaylistItem> &selection);
};
......
......@@ -19,141 +19,72 @@
#include "playlistitem.h"
#include "playlist.h"
#include "cache.h"
#include "collectionlist.h"
class PlaylistItem::Data : public QFileInfo
{
public:
static Data *newUser(const QFileInfo &file);
Data *newUser();
void deleteUser();
Tag *getTag();
AudioData *getAudioData();
void setFile(const QString &file);
protected:
// Because we're trying to use this as a shared item, we want all access
// to be through pointers (so that it's safe to use delete this). Thus
// creation of the object should be done by the newUser methods above
// and deletion should be handled by deleteUser. Making the constructor
// and destructor private ensures this.
Data(const QFileInfo &file);
virtual ~Data();
private:
int referenceCount;
CacheItem *cache;
Tag *tag;
AudioData *audioData;
};
////////////////////////////////////////////////////////////////////////////////
// PlaylistItem::Data public methods
// PlaylistItem public methods
////////////////////////////////////////////////////////////////////////////////
PlaylistItem::Data *PlaylistItem::Data::newUser(const QFileInfo &file)
PlaylistItem::PlaylistItem(CollectionListItem *item, Playlist *parent) : QObject(parent), KListViewItem(parent)
{
return(new Data(file));
if(item) {
data = item->getData()->newUser();
item->addChildItem(this);
refreshImpl();
connect(this, SIGNAL(refreshed()), parent, SIGNAL(dataChanged()));
}
setDragEnabled(true);
}
PlaylistItem::Data *PlaylistItem::Data::newUser()
PlaylistItem::~PlaylistItem()
{
referenceCount++;
return(this);
data->deleteUser();
}
void PlaylistItem::Data::deleteUser()
void PlaylistItem::setFile(const QString &file)
{
// The delete(this) is safe because we control object creation through a
// protected constructor and the newUser() methods.
if(--referenceCount == 0)
delete(this);
data->setFile(file);
refresh();
}
Tag *PlaylistItem::Data::getTag()
Tag *PlaylistItem::getTag()
{
if(!tag)
tag = new Tag(filePath());
return(tag);
return(data->getTag());
}
AudioData *PlaylistItem::Data::getAudioData()
AudioData *PlaylistItem::getAudioData()
{
if(!audioData)
audioData = new AudioData(filePath());
return(audioData);
return(data->getAudioData());
}
void PlaylistItem::Data::setFile(const QString &file)
{
delete(tag);
tag = 0;
// QFileInfo-ish methods
QFileInfo::setFile(file);
}
QString PlaylistItem::fileName() const { return(data->fileName()); }
QString PlaylistItem::filePath() const { return(data->filePath()); }
QString PlaylistItem::absFilePath() const { return(data->absFilePath()); }
QString PlaylistItem::dirPath(bool absPath) const { return(data->dirPath(absPath)); }
bool PlaylistItem::isWritable() const { return(data->isWritable()); }
////////////////////////////////////////////////////////////////////////////////
// PlaylistItem::Data protected methods
// PlaylistItem public slots
////////////////////////////////////////////////////////////////////////////////
PlaylistItem::Data::Data(const QFileInfo &file) : QFileInfo(file)
{
referenceCount = 1;
// initialize pointers to null
cache = 0;
tag = 0;
audioData = 0;
}
PlaylistItem::Data::~Data()
void PlaylistItem::refresh()
{
// Create our cache "on the way out" to avoid having lots of duplicate copies
// of the same information in memory. This checks to see if the item is in
// the cache and
if(tag && !cache && !Cache::instance()->isEmpty() && !Cache::instance()->find(absFilePath()) )
Cache::instance()->replace(absFilePath(), new CacheItem(*tag));
delete(tag);
delete(audioData);
// This signal will be received by the "parent" CollectionListItem which will
// in turn call refreshImpl() for all of its children, including this item.
emit(refreshed());
}
////////////////////////////////////////////////////////////////////////////////
// PlaylistItem public methods
// PlaylistItem protected methods
////////////////////////////////////////////////////////////////////////////////
PlaylistItem::PlaylistItem(const QFileInfo &file, Playlist *parent) : QObject(parent), KListViewItem(parent)
{
data = Data::newUser(file);
refresh();
connect(this, SIGNAL(refreshed()), parent, SIGNAL(dataChanged()));
}
PlaylistItem::PlaylistItem(PlaylistItem &item, Playlist *parent) : QObject(parent), KListViewItem(parent)
{
data = item.getData()->newUser();
// connect(&item, SIGNAL(destroyed(PlaylistItem *)), this, SLOT(parentDestroyed(PlaylistItem *)));
addSibling(&item);
refresh();
connect(this, SIGNAL(refreshed()), parent, SIGNAL(dataChanged()));
}
PlaylistItem::~PlaylistItem()
{
data->deleteUser();
}
void PlaylistItem::setFile(const QString &file)
PlaylistItem::PlaylistItem(Playlist *parent) : QObject(parent), KListViewItem(parent)
{
data->setFile(file);
refresh();
setDragEnabled(true);
}
PlaylistItem::Data *PlaylistItem::getData()
......@@ -161,31 +92,18 @@ PlaylistItem::Data *PlaylistItem::getData()