Commit e67658a3 authored by Scott Wheeler's avatar Scott Wheeler

Reworked the playlist restoring and saving code to use a binary cache

instead of m3u files.  Some API cleanup thown in as a special bonus.

svn path=/trunk/kdemultimedia/juk/; revision=206815
parent de8c37a5
......@@ -63,9 +63,10 @@ void GStreamerPlayer::play(float volume)
// 1.0 is full volume
positionNs = 0;
durationNs = 0;
if (player->getState() != Element::STATE_PLAYING)
if (player->getState() != Element::STATE_PLAYING) {
player->setState(Element::STATE_PLAYING);
player->setVolume(volume);
}
}
void GStreamerPlayer::pause()
......
......@@ -47,7 +47,6 @@
////////////////////////////////////////////////////////////////////////////////
Playlist::Playlist(PlaylistSplitter *s, QWidget *parent, const QString &name) : KListView(parent, name.latin1()),
internalFile(true),
playlistName(name),
splitter(s),
boxItem(0)
......@@ -56,7 +55,6 @@ Playlist::Playlist(PlaylistSplitter *s, QWidget *parent, const QString &name) :
}
Playlist::Playlist(PlaylistSplitter *s, const QFileInfo &playlistFile, QWidget *parent, const char *name) : KListView(parent, name),
internalFile(false),
playlistFileName(playlistFile.absFilePath()),
splitter(s)
......@@ -77,26 +75,16 @@ Playlist::Playlist(PlaylistSplitter *s, const QFileInfo &playlistFile, QWidget *
while(!stream.atEnd()) {
QString itemName = (stream.readLine()).stripWhiteSpace();
// Here we're checking to see if JuK has recorded a name for the playlist
// in the m3u file. i.e. if one of the lines is
// "#NAME=Really Good Music" this will pick that out.
QFileInfo item(itemName);
if(itemName.startsWith("#")) {
if(itemName.startsWith("#NAME="))
setName(itemName.section('=', 1, -1));
}
else {
QFileInfo item(itemName);
if(item.isRelative())
item.setFile(QDir::cleanDirPath(playlistFile.dirPath(true) + "/" + itemName));
if(item.exists() && item.isFile() && item.isReadable()) {
if(after)
after = createItem(item, after);
else
after = createItem(item);
}
if(item.isRelative())
item.setFile(QDir::cleanDirPath(playlistFile.dirPath(true) + "/" + itemName));
if(item.exists() && item.isFile() && item.isReadable()) {
if(after)
after = createItem(item, after);
else
after = createItem(item);
}
}
......@@ -108,14 +96,9 @@ Playlist::~Playlist()
}
void Playlist::save(bool autoGenerateFileName)
void Playlist::save()
{
if(autoGenerateFileName && playlistFileName == QString::null) {
QString dataDir = KGlobal::dirs()->saveLocation("appdata");
// kdDebug() << "Playlist::save() - name() == " << name() << endl;
playlistFileName = dataDir + name() + "." + splitter->playlistExtensions().first();
}
else if(!autoGenerateFileName && (internalFile || playlistFileName == QString::null))
if(playlistFileName.isEmpty())
return saveAs();
QFile file(playlistFileName);
......@@ -125,9 +108,6 @@ void Playlist::save(bool autoGenerateFileName)
QTextStream stream(&file);
if(playlistName != QString::null)
stream << "#NAME=" << playlistName << endl;
QStringList fileList = files();
for(QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it)
......@@ -138,31 +118,18 @@ void Playlist::save(bool autoGenerateFileName)
void Playlist::saveAs()
{
// If this is one of our internal files, remove it when we save it to an
// "external" file.
if(internalFile && playlistFileName != QString::null)
QFile::remove(playlistFileName);
// This needs to be replace with something that sets the default to the
// playlist name.
QStringList extensions = splitter->playlistExtensions();
playlistFileName = KFileDialog::getSaveFileName(QString::null,
splitter->extensionsString(splitter->playlistExtensions(),
i18n("Playlists")));
playlistFileName = KFileDialog::getSaveFileName(QString::null, splitter->extensionsString(extensions, i18n("Playlists")));
playlistFileName = playlistFileName.stripWhiteSpace();
internalFile = false;
if(playlistFileName != QString::null) {
if(splitter->playlistExtensions().find(playlistFileName.section('.', -1)) == splitter->playlistExtensions().end())
playlistFileName.append('.' + splitter->playlistExtensions().first());
if(extensions.find(playlistFileName.section('.', -1)) == extensions.end())
playlistFileName.append('.' + extensions.first());
if(playlistName == QString::null)
if(playlistName.isEmpty())
emit(nameChanged(name()));
save();
}
}
......@@ -187,7 +154,7 @@ void Playlist::clearItems(const PlaylistItemList &items)
QPtrListIterator<PlaylistItem> it(items);
while(it.current()) {
emit(aboutToRemove(it.current()));
// members.remove(it.current()->absFilePath()); // TODO: fix this for the new sorted class
members.remove(it.current()->absFilePath());
delete it.current();
++it;
}
......@@ -533,4 +500,39 @@ void Playlist::applyTags(QListViewItem *item, const QString &text, int column)
}
QDataStream &operator<<(QDataStream &s, const Playlist &p)
{
s << p.name();
s << p.fileName();
s << p.files();
return s;
}
QDataStream &operator>>(QDataStream &s, Playlist &p)
{
QString buffer;
s >> buffer;
p.setName(buffer);
s >> buffer;
p.setFileName(buffer);
QStringList files;
s >> files;
PlaylistItem *after = 0;
p.setSorting(p.columns() + 1);
for(QStringList::Iterator it = files.begin(); it != files.end(); ++it ) {
QFileInfo info(*it);
after = p.createItem(info, after);
}
return s;
}
#include "playlist.moc"
......@@ -52,10 +52,9 @@ public:
/**
* 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. However, by setting autoGenerateFileName, you can tell save to
* pick a file name.
* name.
*/
virtual void save(bool autoGenerateFileName = false);
virtual void save();
virtual void saveAs();
virtual void refresh();
virtual void clearItems(const PlaylistItemList &items);
......@@ -63,8 +62,8 @@ public:
/**
* All of the (media) files in the list.
*/
QStringList files() const;
/**
* Returns a list of all of the items in the playlist.
*/
......@@ -94,12 +93,6 @@ public:
*/
virtual PlaylistItem *createItem(const QFileInfo &file, QListViewItem *after = 0);
/**
* Internal files are files which have not been saved by the user, but rather
* are stored in JuK's data directory and are restored by session management.
*/
bool isInternalFile() const { return internalFile; }
void setInternal(bool internal) { internalFile = internal; }
QString fileName() const { return playlistFileName; }
void setFileName(const QString &n) { playlistFileName = n; }
......@@ -193,13 +186,6 @@ private:
int processed;
bool allowDuplicates;
/**
* If a file is "internal" it is not one that the user has yet chosen to
* save. However for the purposes of being able to restore a user's
* loaded playlists it will be saved "internally" in:
* $KDEHOME/share/apps/juk/playlists.
*/
bool internalFile;
QString playlistFileName;
/**
......@@ -213,4 +199,7 @@ private:
int rmbEditID;
};
QDataStream &operator<<(QDataStream &s, const Playlist &p);
QDataStream &operator>>(QDataStream &s, Playlist &p);
#endif
......@@ -167,32 +167,23 @@ void PlaylistBox::deleteItem()
void PlaylistBox::deleteItem(PlaylistBoxItem *item)
{
if(item && item->playlist()) {
// If the file is "internal" (not loaded from a file and not yet saved),
// or the file name is null, or the user specifically chooses to delete
// the file then delete it. Otherwise, just remove the file from the
// PlaylistBox.
if(item->playlist()->fileName() != QString::null) {
if(item->playlist()->isInternalFile())
QFile::remove(item->playlist()->fileName());
else {
int remove = KMessageBox::warningYesNoCancel(this, i18n("Do you want to delete this file from the disk as well?"));
if(remove == KMessageBox::Yes) {
if(!QFile::remove(item->playlist()->fileName()))
KMessageBox::sorry(this, i18n("Could not delete the specified file."));
}
else if(remove == KMessageBox::Cancel)
return;
}
if(!item || !item->playlist())
return;
if(!item->playlist()->fileName().isEmpty()) {
int remove = KMessageBox::warningYesNoCancel(this, i18n("Do you want to delete this file from the disk as well?"));
if(remove == KMessageBox::Yes) {
if(!QFile::remove(item->playlist()->fileName()))
KMessageBox::sorry(this, i18n("Could not delete the specified file."));
}
nameList.remove(item->text());
delete item->playlist();
delete item;
else if(remove == KMessageBox::Cancel)
return;
}
nameList.remove(item->text());
delete item->playlist();
delete item;
}
////////////////////////////////////////////////////////////////////////////////
......@@ -201,10 +192,6 @@ void PlaylistBox::deleteItem(PlaylistBoxItem *item)
void PlaylistBox::resizeEvent(QResizeEvent *e)
{
// hack-ish, but functional
// for(int i = 0; i <= count(); i++)
// updateItem(i);
triggerUpdate(true);
KListBox::resizeEvent(e);
......
......@@ -18,13 +18,17 @@
#include <klocale.h>
#include <kiconloader.h>
#include <kapplication.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <qinputdialog.h>
#include "playlistitem.h"
#include "playlistsplitter.h"
#include "collectionlist.h"
#include "directorylist.h"
#include "playlist.h"
////////////////////////////////////////////////////////////////////////////////
// helper functions
......@@ -395,15 +399,24 @@ void PlaylistSplitter::readConfig()
directoryList = config->readListEntry("DirectoryList");
if(restore) {
QStringList external = config->readListEntry("ExternalPlaylists");
for(QStringList::Iterator it = external.begin(); it != external.end(); ++it)
openPlaylist(*it);
QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";
QFile f(playlistsFile);
QStringList internal = config->readListEntry("InternalPlaylists");
for(QStringList::Iterator it = internal.begin(); it != internal.end(); ++it) {
Playlist *p = openPlaylist(*it);
if(p)
p->setInternal(true);
if(f.open(IO_ReadOnly)) {
QDataStream s(&f);
while(!s.atEnd()) {
Playlist *p = new Playlist(this, playlistStack);
s >> *p;
// check to see if we've alredy loaded this item before continuing
if(!playlistFiles.insert(p->fileName()))
setupPlaylist(p);
else
delete p;
}
}
open(directoryList);
......@@ -419,27 +432,29 @@ void PlaylistSplitter::saveConfig()
// Save the list of open playlists.
if(restore && playlistBox) {
QStringList internalPlaylists;
QStringList externalPlaylists;
// Start at item 1. We want to skip the collection list.
for(uint i = 1; i < playlistBox->count(); i++) {
PlaylistBoxItem *item = static_cast<PlaylistBoxItem *>(playlistBox->item(i));
if(item && item->playlist()) {
Playlist *p = item->playlist();
if(p->isInternalFile()) {
p->save(true);
internalPlaylists.append(p->fileName());
QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";
QFile f(playlistsFile);
if(f.open(IO_WriteOnly)) {
QDataStream s(&f);
for(uint i = 1; i < playlistBox->count(); i++) {
PlaylistBoxItem *item = static_cast<PlaylistBoxItem *>(playlistBox->item(i));
if(item && item->playlist()) {
Playlist *p = item->playlist();
s << *p;
}
else
externalPlaylists.append(p->fileName());
}
}
f.close();
}
{ // block for Playlists group
KConfigGroupSaver saver(config, "Playlists");
config->writeEntry("InternalPlaylists", internalPlaylists);
config->writeEntry("ExternalPlaylists", externalPlaylists);
config->writeEntry("DirectoryList", directoryList);
}
}
......
......@@ -24,14 +24,13 @@
#include <qsplitter.h>
#include <qwidgetstack.h>
#include "playlistitem.h"
#include "playlistbox.h"
#include "collectionlist.h"
#include "tageditor.h"
#include "playlist.h"
#include "stringhash.h"
#include "tageditor.h"
class PlaylistBoxItem;
class PlaylistItem;
/**
* This is the main layout class of JuK. It should contain a PlaylistBox and
......
......@@ -25,7 +25,7 @@ public:
enum Color { Red, Black };
Node(const QString &value) : key(value), parent(0), left(0), right(0), color(Black) {}
~Node();
~Node() {}
QString key;
Node *parent;
......@@ -51,18 +51,69 @@ bool SortedStringList::insert(const QString &value)
bool SortedStringList::contains(const QString &value) const
{
Node *n = root;
while(n && value != n->key) {
if(value < n->key)
n = n->left;
else
n = n->right;
return find(value);
}
SortedStringList::Node *SortedStringList::treeMinimum(Node *n) const
{
while(n->left)
n = n->left;
return n;
}
SortedStringList::Node *SortedStringList::treeSuccessor(Node *n) const
{
if(n->right)
return treeMinimum(n->right);
Node *p = n->parent;
while(p && n == p->right) {
n = p;
p = p->parent;
}
return p;
}
if(n)
return true;
else
bool SortedStringList::remove(const QString &value)
{
Node *n = find(value);
if(!n)
return false;
Node *y;
Node *x;
if(!n->left || !n->right)
y = n;
else
y = treeSuccessor(n);
if(y->left)
x = y->left;
else
x = y->right;
if(x)
x->parent = y->parent;
if(!y->parent)
root = x;
else {
if(y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
}
if(y != x)
n->key = y->key;
delete y;
return true;
}
QStringList SortedStringList::values() const
......@@ -76,6 +127,19 @@ QStringList SortedStringList::values() const
// private methods
////////////////////////////////////////////////////////////////////////////////
SortedStringList::Node *SortedStringList::find(const QString &value) const
{
Node *n = root;
while(n && value != n->key) {
if(value < n->key)
n = n->left;
else
n = n->right;
}
return n;
}
bool SortedStringList::BSTInsert(const QString &value)
{
Node *previousNode = 0;
......@@ -99,14 +163,10 @@ bool SortedStringList::BSTInsert(const QString &value)
if(!root)
root = n;
else {
if(value < previousNode->key) {
if(value < previousNode->key)
previousNode->left = n;
// kdDebug() << "LEFT - " << value << endl;
}
else {
else
previousNode->right = n;
// kdDebug() << "RIGHT - " << value << endl;
}
}
return false;
......
......@@ -29,6 +29,7 @@ public:
bool insert(const QString &value);
bool contains(const QString &value) const;
bool remove(const QString &value);
/**
* Returns a sorted list of the values.
......@@ -40,6 +41,7 @@ public:
private:
class Node;
Node *find(const QString &value) const;
/**
* The insertion implementation. Returns true if the item was already
* present in the list.
......@@ -47,6 +49,10 @@ private:
bool BSTInsert(const QString &value);
void traverse(const Node *n, QStringList &list) const;
Node *treeMinimum(Node *n) const;
Node *treeSuccessor(Node *n) const;
Node *root;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment