Commit 4b04d9a4 authored by Scott Wheeler's avatar Scott Wheeler

Several days of changes -- again.

First: Partial restoring of Playlists.  This is a little broken at the
moment, but will be fixed soon.

Also, settings aren't currently being saved with Qt 3.1 Beta 1.  It's a
bug in Qt that I've already patched and sent to qt-bugs.

Added features; fixed bugs.  ;-)

svn path=/trunk/kdemultimedia/juk/; revision=181429
parent 59a684a1
......@@ -57,7 +57,7 @@ CollectionListItem *CollectionList::lookup(const QString &file)
return(itemsDict.find(file));
}
PlaylistItem *CollectionList::createItem(const QFileInfo &file)
PlaylistItem *CollectionList::createItem(const QFileInfo &file, QListViewItem *)
{
if(itemsDict.find(file.absFilePath()))
return(0);
......
......@@ -48,7 +48,7 @@ public:
QStringList albums() const;
CollectionListItem *lookup(const QString &file);
virtual PlaylistItem *createItem(const QFileInfo &file);
virtual PlaylistItem *createItem(const QFileInfo &file, QListViewItem *);
protected:
CollectionList(QWidget *parent = 0);
......
......@@ -31,7 +31,7 @@
// public members
////////////////////////////////////////////////////////////////////////////////
JuK::JuK(QWidget *parent, const char *name) : KMainWindow(parent, name)
JuK::JuK(QWidget *parent, const char *name) : KMainWindow(parent, name, WDestructiveClose)
{
// Expect segfaults if you change this order.
......@@ -54,9 +54,6 @@ JuK::~JuK()
void JuK::setupLayout()
{
// automagically save and restore settings
setAutoSaveSettings();
PlaylistSplitter::initialize(this);
splitter = PlaylistSplitter::instance();
setCentralWidget(splitter);
......@@ -152,6 +149,9 @@ void JuK::processArgs()
void JuK::readConfig()
{
// Automagically save and restore many window settings.
setAutoSaveSettings();
KConfig *config = KGlobal::config();
{ // player settings
KConfigGroupSaver saver(config, "Player");
......@@ -227,7 +227,9 @@ void JuK::remove()
void JuK::quit()
{
kapp->quit();
// delete(this);
// kapp->quit();
close();
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -35,7 +35,7 @@ public:
}
Qt::Orientation orientation;
int lineCount;
uint lineCount;
};
ListBoxPixmap::ListBoxPixmap(QListBox *listbox, const QPixmap &pixmap)
......
......@@ -43,7 +43,7 @@ Playlist::Playlist(QWidget *parent, const char *name) : KListView(parent, name)
{
setup();
internalFile = true;
fileName = QString::null;
playlistFileName = QString::null;
}
Playlist::Playlist(const QFileInfo &playlistFile, QWidget *parent, const char *name) : KListView(parent, name)
......@@ -51,22 +51,32 @@ Playlist::Playlist(const QFileInfo &playlistFile, QWidget *parent, const char *n
setup();
internalFile = false;
fileName = playlistFile.absFilePath();
playlistFileName = playlistFile.absFilePath();
QFile file(fileName);
file.open(IO_ReadOnly);
QFile file(playlistFileName);
if(!file.open(IO_ReadOnly))
return;
QTextStream stream(&file);
// turn off non-explicit sorting
setSorting(columns() + 1);
PlaylistItem *after = 0;
while(!stream.atEnd()) {
QString itemName = (stream.readLine()).stripWhiteSpace();
QFileInfo item(itemName);
if(item.isRelative())
item.setFile(QDir::cleanDirPath(playlistFile.dirPath(true) + "/" + itemName));
if(item.exists() && item.isFile() && item.isReadable())
createItem(item);
if(item.exists() && item.isFile() && item.isReadable()) {
if(after)
after = createItem(item, after);
else
after = createItem(item);
}
}
file.close();
......@@ -79,14 +89,49 @@ Playlist::~Playlist()
void Playlist::save()
{
kdDebug() << "Playlist::save() -- Not yet implemented!" << endl;
// needs an implementation to write to m3u files
if(internalFile || playlistFileName == QString::null)
return saveAs();
QFile file(playlistFileName);
if(!file.open(IO_WriteOnly))
return KMessageBox::error(this, i18n("Could not save to file %1.").arg(playlistFileName));
QTextStream stream(&file);
QStringList fileList = files();
for(QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it)
stream << *it << endl;
file.close();
}
void Playlist::saveAs()
{
kdDebug() << "Playlist::saveAs() -- Not yet implemented!" << endl;
// needs an implementation to write to m3u files
// 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 = PlaylistSplitter::playlistExtensions();
playlistFileName = KFileDialog::getSaveFileName(QString::null,
PlaylistSplitter::extensionsString(PlaylistSplitter::playlistExtensions(),
i18n("Playlists")));
playlistFileName = playlistFileName.stripWhiteSpace();
internalFile = false;
if(playlistFileName != QString::null) {
if(!PlaylistSplitter::playlistExtensions().contains(playlistFileName.section('.', -1)))
playlistFileName.append('.' + PlaylistSplitter::playlistExtensions().first());
emit(fileNameChanged(playlistFileName));
save();
}
}
void Playlist::refresh()
......@@ -190,9 +235,9 @@ bool Playlist::isInternalFile() const
return(internalFile);
}
QString Playlist::file() const
QString Playlist::fileName() const
{
return(fileName);
return(playlistFileName);
}
////////////////////////////////////////////////////////////////////////////////
......@@ -256,7 +301,7 @@ void Playlist::contentsDragMoveEvent(QDragMoveEvent *e)
e->accept(false);
}
PlaylistItem *Playlist::createItem(const QFileInfo &file)
PlaylistItem *Playlist::createItem(const QFileInfo &file, QListViewItem *after)
{
CollectionListItem *item = CollectionList::instance()->lookup(file.absFilePath());
......@@ -265,8 +310,10 @@ PlaylistItem *Playlist::createItem(const QFileInfo &file)
if(item && members.contains(file.absFilePath()) == 0 || allowDuplicates) {
members.append(file.absFilePath());
PlaylistItem *newItem = new PlaylistItem(item, this);
return(newItem);
if(after)
return(new PlaylistItem(item, this, after));
else
return(new PlaylistItem(item, this));
}
else
return(0);
......
......@@ -33,36 +33,44 @@ public:
Playlist(const QFileInfo &playlistFile, QWidget *parent = 0, const char *name = 0);
virtual ~Playlist();
// "File Menu" like operations. "Open" is the constructor above.
virtual void save();
virtual void saveAs();
virtual void refresh();
virtual void clearItems(const PlaylistItemList &items);
/** All of the files in the list. */
/**
* All of the (media) files in the list.
*/
QStringList files() const;
/** All of the items in the list. */
/**
* All of the items in the list.
*/
PlaylistItemList items() const;
PlaylistItemList selectedItems() const;
void remove();
void remove(const PlaylistItemList &items);
/** Allow duplicate files in the playlist. */
/**
* Allow duplicate files in the playlist.
*/
void setAllowDuplicates(bool allow);
/** This gets the next item to be played in the playlist. */
static PlaylistItem *nextItem(PlaylistItem *current, bool random = false);
/** This is being used as a mini-factory of sorts to make the construction
of PlaylistItems virtual. */
virtual PlaylistItem *createItem(const QFileInfo &file);
/**
* This is being used as a mini-factory of sorts to make the construction
* of PlaylistItems virtual.
*/
virtual PlaylistItem *createItem(const QFileInfo &file, QListViewItem *after = 0);
bool isInternalFile() const;
QString file() const;
QString fileName() const;
// static methods
/**
* This gets the next item to be played in the specified playlist.
*/
static PlaylistItem *nextItem(PlaylistItem *current, bool random = false);
protected:
virtual QDragObject *dragObject();
......@@ -76,30 +84,37 @@ 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.
/**
* 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 fileName;
QString playlistFileName;
private slots:
void emitSelected();
signals:
/** This signal is connected to PlaylistItem::refreshed() in the
PlaylistItem class. */
/**
* This signal is connected to PlaylistItem::refreshed() in the
* PlaylistItem class.
*/
void dataChanged();
/** 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. */
/**
* 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();
/** This is emitted when the playlist selection is changed. This is used
primarily to notify the TagEditor of the new data. */
/**
* This is emitted when the playlist selection is changed. This is used
* primarily to notify the TagEditor of the new data.
*/
void selectionChanged(const PlaylistItemList &selection);
void fileNameChanged(const QString &fileName);
};
#endif
......@@ -149,14 +149,14 @@ void PlaylistBox::deleteItem(PlaylistBoxItem *item)
// the file then delete it. Otherwise, just remove the file from the
// PlaylistBox.
if(item->playlist()->file() != QString::null) {
if(item->playlist()->fileName() != QString::null) {
if(item->playlist()->isInternalFile())
QFile::remove(item->playlist()->file());
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()->file()))
if(!QFile::remove(item->playlist()->fileName()))
KMessageBox::sorry(this, i18n("Could not delete the specified file."));
}
else if(remove == KMessageBox::Cancel)
......@@ -317,11 +317,13 @@ void PlaylistBox::contextDeleteItem()
////////////////////////////////////////////////////////////////////////////////
PlaylistBoxItem::PlaylistBoxItem(PlaylistBox *listbox, const QPixmap &pix, const QString &text, Playlist *l)
: ListBoxPixmap(listbox, pix, text)
: QObject(listbox), ListBoxPixmap(listbox, pix, text)
{
list = l;
setOrientation(Qt::Vertical);
listbox->addName(text);
connect(l, SIGNAL(fileNameChanged(const QString &)), this, SLOT(changeFile(const QString &)));
}
PlaylistBoxItem::PlaylistBoxItem(PlaylistBox *listbox, const QString &text, Playlist *l)
......@@ -341,4 +343,21 @@ Playlist *PlaylistBoxItem::playlist() const
return(list);
}
void PlaylistBoxItem::changeFile(const QString &file)
{
QStringList extensions = PlaylistSplitter::playlistExtensions();
// get just the filename, not the path
QString text = file.section(QDir::separator(), -1);
for(QStringList::Iterator it = extensions.begin(); it != extensions.end(); ++it) {
if(text.endsWith(*it)) {
// remove the extension
text = text.left(text.length() - (*it).length() - 1);
setText(text);
return;
}
}
}
#include "playlistbox.moc"
......@@ -39,6 +39,7 @@ class PlaylistBox : public KListBox
friend class PlaylistBoxItem;
Q_OBJECT
public:
PlaylistBox(PlaylistSplitter *parent = 0, const char *name = 0);
virtual ~PlaylistBox();
......@@ -46,6 +47,7 @@ public:
QStringList names() const;
public slots:
// All of the slots without parameters default to the selected item.
void save();
void save(PlaylistBoxItem *item);
void saveAs();
......@@ -56,15 +58,16 @@ public slots:
void duplicate(PlaylistBoxItem *item);
void deleteItem();
void deleteItem(PlaylistBoxItem *item);
private:
virtual void resizeEvent(QResizeEvent *e);
virtual void dropEvent(QDropEvent *e);
virtual void dragMoveEvent(QDragMoveEvent *e);
virtual void mousePressEvent(QMouseEvent *e);
/** This is used by PlaylistItemBox (a friend class) to add names to the name
list returned by names(). */
/**
* This is used by PlaylistItemBox (a friend class) to add names to the name
* list returned by names().
*/
void addName(const QString &name);
PlaylistSplitter *splitter;
......@@ -74,18 +77,20 @@ private:
PlaylistBoxItem *contextMenuOn;
private slots:
/** Catches QListBox::currentChanged(QListBoxItem *), does a cast and then re-emits
the signal as currentChanged(PlaylistBoxItem *). */
/**
* Catches QListBox::currentChanged(QListBoxItem *), does a cast and then re-emits
* the signal as currentChanged(PlaylistBoxItem *).
*/
void playlistChanged(QListBoxItem *item);
void playlistDoubleClicked(QListBoxItem *item);
void drawContextMenu(QListBoxItem *item, const QPoint &point);
// context menu entries
void contextSave();
void contextSaveAs();
void contextRename();
void contextDuplicate();
void contextDeleteItem();
signals:
void currentChanged(PlaylistBoxItem *);
......@@ -101,16 +106,21 @@ public:
class PlaylistBoxItem : public ListBoxPixmap
class PlaylistBoxItem : public QObject, public ListBoxPixmap
{
friend class PlaylistBox;
Q_OBJECT
public:
PlaylistBoxItem(PlaylistBox *listbox, const QPixmap &pix, const QString &text, Playlist *l = 0);
PlaylistBoxItem(PlaylistBox *listbox, const QString &text, Playlist *l = 0);
virtual ~PlaylistBoxItem();
Playlist *playlist() const;
public slots:
void changeFile(const QString &file);
private:
Playlist *list;
......
......@@ -81,7 +81,7 @@ PlaylistItem::PlaylistItem(CollectionListItem *item, Playlist *parent) : QObject
setup(item, parent);
}
PlaylistItem::PlaylistItem(CollectionListItem *item, Playlist *parent, PlaylistItem *after) : QObject(parent), KListViewItem(parent, after)
PlaylistItem::PlaylistItem(CollectionListItem *item, Playlist *parent, QListViewItem *after) : QObject(parent), KListViewItem(parent, after)
{
setup(item, parent);
}
......
......@@ -69,7 +69,7 @@ protected:
/** Items should always be created using Playlist::createItem() or through a
subclss or friend class. */
PlaylistItem(CollectionListItem *item, Playlist *parent);
PlaylistItem(CollectionListItem *item, Playlist *parent, PlaylistItem *after);
PlaylistItem(CollectionListItem *item, Playlist *parent, QListViewItem *after);
PlaylistItem(Playlist *parent);
class Data;
......
......@@ -36,7 +36,7 @@ void processEvents()
static int processed = 0;
if(processed == 0)
kapp->processEvents();
processed = (processed + 1) % 10;
processed = (processed + 1) % 5;
}
////////////////////////////////////////////////////////////////////////////////
......@@ -104,13 +104,39 @@ PlaylistItem *PlaylistSplitter::playlistFirstItem() const
return(i);
}
QString PlaylistSplitter::extensionsString(const QStringList &extensions, const QString &type)
{
QStringList l;
for(QStringList::ConstIterator it = extensions.begin(); it != extensions.end(); ++it)
l.append(QString("*." + (*it)));
// i.e. "*.m3u, *.mp3|Media Files"
QString s = l.join(" ");
if(type != QString::null)
s = + "|" + type;
return(s);
}
QStringList PlaylistSplitter::playlistExtensions()
{
if(splitter)
return(splitter->listExtensions);
else
return(QString::null);
}
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////
void PlaylistSplitter::open()
{
QStringList files = KFileDialog::getOpenFileNames(QString::null, "*.mp3 *.m3u|Media Files");
QStringList files = KFileDialog::getOpenFileNames(QString::null,
extensionsString((mediaExtensions + listExtensions), i18n("Media Files")));
open(files);
}
......@@ -193,6 +219,7 @@ Playlist *PlaylistSplitter::createPlaylist(const QString &name)
{
Playlist *p = new Playlist(playlistStack, name.latin1());
new PlaylistBoxItem(playlistBox, SmallIcon("midi", 32), name, p);
connect(p, SIGNAL(selectionChanged(const PlaylistItemList &)), editor, SLOT(setItems(const PlaylistItemList &)));
connect(p, SIGNAL(doubleClicked(QListViewItem *)), this, SIGNAL(playlistDoubleClicked(QListViewItem *)));
connect(p, SIGNAL(collectionChanged()), editor, SLOT(updateCollection()));
......@@ -204,7 +231,7 @@ void PlaylistSplitter::openPlaylist()
QStringList files = KFileDialog::getOpenFileNames(QString::null, "*.m3u");
for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
Playlist *p = openPlaylist(*it);
openPlaylist(*it);
}
Playlist *PlaylistSplitter::openPlaylist(const QString &playlistFile)
......@@ -252,12 +279,13 @@ PlaylistSplitter::PlaylistSplitter(QWidget *parent) : QSplitter(Qt::Horizontal,
setupLayout();
readConfig();
mediaExtensions.append("mp3");
mediaExtensions.append("ogg");
listExtensions.append("m3u");
}
PlaylistSplitter::~PlaylistSplitter()
{
saveConfig();
}
////////////////////////////////////////////////////////////////////////////////
......@@ -312,7 +340,54 @@ void PlaylistSplitter::setupLayout()
void PlaylistSplitter::readConfig()
{
KConfig *config = KGlobal::config();
{ // block for Playlists group
KConfigGroupSaver saver(config, "Playlists");
QStringList external = config->readListEntry("ExternalPlaylists");
for(QStringList::Iterator it = external.begin(); it != external.end(); ++it)
openPlaylist(*it);
QStringList internal = config->readListEntry("InternalPlaylists");
for(QStringList::Iterator it = internal.begin(); it != internal.end(); ++it)
openPlaylist(*it);
}
}
void PlaylistSplitter::saveConfig()
{
kdDebug() << "PlaylistSplitter::saveConfig()" << endl;
KConfig *config = KGlobal::config();
// Save the list of open playlists.
if(playlistBox) {
QStringList internalPlaylists;
QStringList externalPlaylists;
for(uint i = 0; i < playlistBox->count(); i++) {
PlaylistBoxItem *item = static_cast<PlaylistBoxItem *>(playlistBox->item(i));
if(item && item->playlist()) {
Playlist *p = item->playlist();
if(p->isInternalFile()) {
if(p->fileName().isEmpty())
; // save the file
if(!p->fileName().isEmpty())
internalPlaylists.append(p->fileName());
}
else
externalPlaylists.append(p->fileName());
}
}
{ // block for Playlists group
KConfigGroupSaver saver(config, "Playlists");
if(!internalPlaylists.isEmpty())
config->writeEntry("InternalPlaylists", internalPlaylists);
if(!externalPlaylists.isEmpty())
config->writeEntry("ExternalPlaylists", externalPlaylists);
}
}
}
void PlaylistSplitter::addImpl(const QString &file, Playlist *list)
......
......@@ -32,34 +32,76 @@ class PlaylistBoxItem;
class CollectionList;
class TagEditor;
/** This is the main layout class of JuK. It should contain a PlaylistBox and
a QWidgetStack of the Playlists. This like CollectionList, has been
implemented as a pseudo-singleton to provide global access without passing
pointers all over the place. */
/**
* This is the main layout class of JuK. It should contain a PlaylistBox and
* a QWidgetStack of the Playlists. This like CollectionList, has been
* implemented as a pseudo-singleton to provide global access without passing
* pointers all over the place. */
class PlaylistSplitter : public QSplitter
{
Q_OBJECT
public:
/**
* Since this class is something of a singleton, all instances should be
* refered to by the instance() method. Also see initialize().
*/
static PlaylistSplitter *instance();
/**
* This method must be called to construct the object. It creates the
* instance of the object that can be referred to by instance().
*/
static void initialize(QWidget *parent = 0);
/** Returns a unique string to be used as new playlist names. */
QString uniquePlaylistName();
/**
* Returns a unique string to be used as new playlist names. This follows
* the format "[startingWith] i" where "i" is the first integer greater than 0
* that does not currently exist in the PlaylistBox.
*/
QString uniquePlaylistName(const QString &startingWith, bool useParentheses = false);
/* This calls the above method with startingWith == i18n("Playlist") to
* produce "Playlist 1", "Playlist 2", ...
*/
QString uniquePlaylistName();
/**
* Returns a QPtrList of the selected PlaylistItems in the top playlist in
* the QWidgetStack of playlists.
*/
PlaylistItemList playlistSelection() const;
/**
* This returns a pointer to the first item in the playlist on the top
* of the QWidgetStack of playlists.
*/
PlaylistItem *playlistFirstItem() const;
// static (non-initialization) methods
/**
* Merges a list of file extensions, and a description of those types into a
* format that makes sense to KFileDialog. If type = QString::null then no
* description is appended.
*/
static QString extensionsString(const QStringList &extensions, const QString &type = QString::null);
/**
* Returns a lif of the extensions that are used for playlists.
*/
static QStringList playlistExtensions();
public slots:
void open();
void openDirectory();
void open(const QStringList &files);
void open(const QString &file);
void save();
/** Deletes the selected items from the hard disk. */
/**
* Deletes the selected items from the hard disk.
*/
void remove();
void refresh();
/** Removes the selected items from the playlist. */
/**
* Removes the selected items from the playlist.
*/
void clearSelectedItems();
void selectAll(bool select = true);
......@@ -87,6 +129,7 @@ protected:
private:
void setupLayout();
void readConfig();
void saveConfig();
void addImpl(const QString &file, Playlist *list);
static PlaylistSplitter *splitter;
......
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