Commit de9543f4 authored by Scott Wheeler's avatar Scott Wheeler

More updates including drag and drop support between playlists, from one

playlist to another, and from other KURL (i.e. Konq) drag sources.

svn path=/trunk/kdemultimedia/juk/; revision=178830
parent c3525dc2
......@@ -541,12 +541,7 @@ GTAGS:
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH
#>- DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
#>+ 4
KDE_DIST=playlistwidget.h playlistwidget.cpp taggerwidget.h taggerwidget.cpp
DISTFILES= $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) $(KDE_DIST)
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
top_distdir = ..
distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
......@@ -718,10 +713,6 @@ customaction.moc: $(srcdir)/customaction.h
playlistitem.moc: $(srcdir)/playlistitem.h
$(MOC) $(srcdir)/playlistitem.h -o playlistitem.moc
#>+ 3
taggerwidget.moc: $(srcdir)/taggerwidget.h
$(MOC) $(srcdir)/taggerwidget.h -o taggerwidget.moc
#>+ 3
playlist.moc: $(srcdir)/playlist.h
$(MOC) $(srcdir)/playlist.h -o playlist.moc
......@@ -740,7 +731,7 @@ tageditor.moc: $(srcdir)/tageditor.h
#>+ 3
clean-metasources:
-rm -f slideraction.moc collectionlist.moc playlistbox.moc customaction.moc playlistitem.moc taggerwidget.moc playlist.moc playlistsplitter.moc juk.moc tageditor.moc
-rm -f slideraction.moc collectionlist.moc playlistbox.moc customaction.moc playlistitem.moc playlist.moc playlistsplitter.moc juk.moc tageditor.moc
#>+ 2
docs-am:
......@@ -784,7 +775,7 @@ clean-moc-classes:
-rm -f juk_meta_unload.cpp
#>+ 11
juk.all_cpp.cpp: $(srcdir)/Makefile.in $(srcdir)/collectionlist.cpp $(srcdir)/playlistitem.cpp $(srcdir)/playlist.cpp $(srcdir)/playlistsplitter.cpp $(srcdir)/listboxpixmap.cpp $(srcdir)/playlistbox.cpp $(srcdir)/tageditor.cpp $(srcdir)/cache.cpp $(srcdir)/audiodata.cpp $(srcdir)/genrelistreader.cpp $(srcdir)/genrelistlist.cpp $(srcdir)/genrelist.cpp $(srcdir)/genre.cpp $(srcdir)/player.cpp $(srcdir)/tag.cpp $(srcdir)/customaction.cpp $(srcdir)/slideraction.cpp $(srcdir)/juk.cpp $(srcdir)/main.cpp juk_meta_unload.cpp slideraction.moc collectionlist.moc playlistbox.moc customaction.moc playlistitem.moc taggerwidget.moc playlist.moc playlistsplitter.moc juk.moc tageditor.moc
juk.all_cpp.cpp: $(srcdir)/Makefile.in $(srcdir)/collectionlist.cpp $(srcdir)/playlistitem.cpp $(srcdir)/playlist.cpp $(srcdir)/playlistsplitter.cpp $(srcdir)/listboxpixmap.cpp $(srcdir)/playlistbox.cpp $(srcdir)/tageditor.cpp $(srcdir)/cache.cpp $(srcdir)/audiodata.cpp $(srcdir)/genrelistreader.cpp $(srcdir)/genrelistlist.cpp $(srcdir)/genrelist.cpp $(srcdir)/genre.cpp $(srcdir)/player.cpp $(srcdir)/tag.cpp $(srcdir)/customaction.cpp $(srcdir)/slideraction.cpp $(srcdir)/juk.cpp $(srcdir)/main.cpp juk_meta_unload.cpp slideraction.moc collectionlist.moc playlistbox.moc customaction.moc playlistitem.moc playlist.moc playlistsplitter.moc juk.moc tageditor.moc
@echo 'creating juk.all_cpp.cpp ...'; \
rm -f juk.all_cpp.files juk.all_cpp.final; \
echo "#define KDE_USE_FINAL 1" >> juk.all_cpp.final; \
......@@ -819,7 +810,7 @@ cvs-clean:
kde-rpo-clean:
-rm -f *.rpo
#>+ 11
#>+ 10
$(srcdir)/playlist.cpp: playlist.moc
$(srcdir)/slideraction.cpp: slideraction.moc
$(srcdir)/playlistbox.cpp: playlistbox.moc
......@@ -828,5 +819,4 @@ $(srcdir)/playlistitem.cpp: playlistitem.moc
$(srcdir)/collectionlist.cpp: collectionlist.moc
$(srcdir)/playlistsplitter.cpp: playlistsplitter.moc
$(srcdir)/customaction.cpp: customaction.moc
$(srcdir)/taggerwidget.cpp: taggerwidget.moc
$(srcdir)/juk.cpp: juk.moc
......@@ -15,6 +15,8 @@
* *
***************************************************************************/
#include <kurl.h>
#include <kurldrag.h>
#include <kdebug.h>
#include "collectionlist.h"
......@@ -87,6 +89,31 @@ CollectionList::~CollectionList()
}
void CollectionList::contentsDropEvent(QDropEvent *e)
{
if(e->source() != this) {
KURL::List urls;
if(KURLDrag::decode(e, urls) && !urls.isEmpty()) {
QStringList files;
for(KURL::List::Iterator it = urls.begin(); it != urls.end(); it++)
files.append((*it).path());
append(files);
}
}
}
void CollectionList::contentsDragMoveEvent(QDragMoveEvent *e)
{
if(KURLDrag::canDecode(e) && e->source() != this)
e->accept(true);
else
e->accept(false);
}
void CollectionList::appendImpl(const QString &item)
{
if(!lookup(item))
......
......@@ -54,6 +54,9 @@ protected:
CollectionList(QWidget *parent = 0);
virtual ~CollectionList();
virtual void contentsDropEvent(QDropEvent *e);
virtual void contentsDragMoveEvent(QDragMoveEvent *e);
virtual void appendImpl(const QString &item);
void addToDict(const QString &file, CollectionListItem *item);
......
......@@ -51,12 +51,13 @@
<genre id3v1="124">Euro-House</genre>
<genre id3v1="25">Euro-Techno</genre>
<genre id3v1="54">Eurodance</genre>
<genre id3v1="84">Fast Fusion</genre>
<genre id3v1="80">Folk</genre>
<genre id3v1="81">Folk/Rock</genre>
<genre id3v1="115">Folklore</genre>
<genre id3v1="119">Freestyle</genre>
<genre id3v1="5">Funk</genre>
<genre id3v1="84">Fusion</genre>
<genre id3v1="30">Fusion</genre>
<genre id3v1="36">Game</genre>
<genre id3v1="59">Gangsta</genre>
<genre id3v1="126">Goa</genre>
......
......@@ -55,7 +55,7 @@ void GenreList::load(const QString &file)
QString GenreList::name(int ID3v1)
{
if(hasIndex && ID3v1 >= 0 && ID3v1 <= int(index.size()))
if(hasIndex && ID3v1 >= 0 && ID3v1 < int(index.count()))
return(index[ID3v1]);
else
return(QString::null);
......@@ -63,7 +63,6 @@ QString GenreList::name(int ID3v1)
int GenreList::findIndex(const QString &item)
{
// cache the previous search -- since there are a lot of "two in a row"
// searchs this should optimize things a little (this is also why this
// method isn't a "const" method)
......@@ -94,15 +93,9 @@ int GenreList::findIndex(const QString &item)
void GenreList::initializeIndex()
{
// kdDebug() << "initializeIndex()" << endl;
index.clear();
// kdDebug() << "Cleared size: " << index.size() << endl;
index.resize(count() + 1);
for(GenreList::Iterator it = begin(); it != end(); ++it) {
if((*it).getID3v1() >= 0 && (*it).getID3v1() <= int(index.size())) {
// kdDebug() << "initializeIndex() - " << (*it).getID3v1() << " - "
// << index.size() << " - " << count() << " - " << (*it) << endl;
index.resize(count());
for(GenreList::Iterator it = begin(); it != end(); ++it)
if((*it).getID3v1() >= 0 && (*it).getID3v1() < int(index.count()))
index[(*it).getID3v1()] = QString(*it);
}
}
}
......@@ -38,6 +38,9 @@ public:
private:
QValueVector<QString> index;
bool hasIndex;
/** This is used for creating a mapping between ID3v1 genre numbers and the
name that is associated with those genres. There is no reason that this
should be called for GenreLists other than the ID3v1 GenreList. */
void initializeIndex();
};
......
......@@ -39,12 +39,12 @@ bool GenreListReader::startElement(const QString &, const QString &, const QStri
if(element.lower() == "genre") {
inGenreTag = true;
if(attributes.index("id3v1") != -1)
id3v1 = attributes.value("id3v1").toInt();
ID3v1 = attributes.value("id3v1").toInt();
else
id3v1 = 255;
ID3v1 = 255;
}
else {
id3v1 = 255;
ID3v1 = 255;
}
return(true);
};
......@@ -59,7 +59,7 @@ bool GenreListReader::endElement(const QString &, const QString &, const QString
bool GenreListReader::characters(const QString& content)
{
if(inGenreTag)
list->append(Genre(content, id3v1));
list->append(Genre(content, ID3v1));
return(true);
};
......@@ -36,7 +36,7 @@ public:
private:
GenreList *list;
bool inGenreTag;
int id3v1;
int ID3v1;
};
#endif
......@@ -21,6 +21,8 @@
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <qinputdialog.h>
#include "juk.h"
////////////////////////////////////////////////////////////////////////////////
......@@ -71,6 +73,9 @@ void JuK::setupActions()
pauseAction = new KAction(i18n("P&ause"), "player_pause", 0, this, SLOT(pauseFile()), actionCollection(), "pauseFile");
stopAction = new KAction(i18n("&Stop"), "player_stop", 0, this, SLOT(stopFile()), actionCollection(), "stopFile");
// playlist menu
(void) new KAction(i18n("New Playlist..."), "filenew", 0, this, SLOT(createPlaylist()), actionCollection(), "createPlaylist");
// just in the toolbar
sliderAction = new SliderAction(i18n("Track Position"), actionCollection(), "trackPositionAction");
......@@ -271,6 +276,25 @@ void JuK::stopFile()
playingItem->setPixmap(0, 0);
}
////////////////////////////////////////////////////////////////////////////////
// playlist menu
////////////////////////////////////////////////////////////////////////////////
void JuK::createPlaylist()
{
if(splitter) {
bool ok;
QString name = QInputDialog::getText(i18n("New Playlist..."), i18n("Please enter a name for the new playlist:"),
QLineEdit::Normal, splitter->uniquePlaylistName(), &ok);
if(ok)
splitter->createPlaylist(name);
}
}
////////////////////////////////////////////////////////////////////////////////
// additional player slots
////////////////////////////////////////////////////////////////////////////////
void JuK::trackPositionSliderClick()
{
trackPositionDragging=true;
......
......@@ -81,10 +81,14 @@ private slots:
void paste() {};
void selectAll(bool select = true);
// player menu
void playFile();
void pauseFile();
void stopFile();
// playlist menu
void createPlaylist();
// additional player slots
void trackPositionSliderClick();
void trackPositionSliderRelease();
......
......@@ -19,6 +19,9 @@
<Action name="pauseFile"/>
<Action name="stopFile"/>
</Menu>
<Menu name="playlist"><text>Play&amp;list</text>
<Action name="createPlaylist"/>
</Menu>
</MenuBar>
<ToolBar name="mainToolBar" noMerge="1"><text>Main Toolbar</text>
......
......@@ -16,6 +16,9 @@
***************************************************************************/
#include <kmessagebox.h>
#include <kurl.h>
#include <kurldrag.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kdebug.h>
......@@ -35,6 +38,7 @@
Playlist::Playlist(QWidget *parent, const char *name) : KListView(parent, name)
{
setup();
setAcceptDrops(true);
allowDuplicates = false;
}
......@@ -43,7 +47,7 @@ Playlist::~Playlist()
}
void Playlist::append(const QString &item)
void Playlist::append(const QString &item, bool sorted)
{
collectionListChanged = false;
......@@ -55,7 +59,7 @@ void Playlist::append(const QString &item)
emit(collectionChanged());
}
void Playlist::append(const QStringList &items)
void Playlist::append(const QStringList &items, bool sorted)
{
collectionListChanged = false;
......@@ -127,6 +131,62 @@ QStringList &Playlist::getAlbumList()
// protected members
////////////////////////////////////////////////////////////////////////////////
QDragObject *Playlist::dragObject()
{
QPtrList<PlaylistItem> items = selectedItems();
KURL::List urls;
for(PlaylistItem *i = items.first(); i; i = items.next()) {
KURL url;
url.setPath(i->absFilePath());
urls.append(url);
}
KURLDrag *drag = new KURLDrag(urls, this, "Playlist Items");
drag->setPixmap(SmallIcon("sound"));
return(drag);
}
void Playlist::contentsDropEvent(QDropEvent *e)
{
QListViewItem *moveAfter = itemAt(e->pos());
if(!moveAfter)
moveAfter = lastItem();
// This is slightly more efficient since it doesn't have to cast everything
// to PlaylistItem.
if(e->source() == this) {
QPtrList<QListViewItem> items = KListView::selectedItems();
for(QPtrListIterator<QListViewItem> it(items); it.current() != 0; ++it) {
(*it)->moveItem(moveAfter);
moveAfter = *it;
}
}
else {
KURL::List urls;
if(KURLDrag::decode(e, urls) && !urls.isEmpty()) {
QStringList files;
for(KURL::List::Iterator it = urls.begin(); it != urls.end(); it++)
files.append((*it).path());
append(files);
}
}
}
void Playlist::contentsDragMoveEvent(QDragMoveEvent *e)
{
if(KURLDrag::canDecode(e))
e->accept(true);
else
e->accept(false);
}
PlaylistItem *Playlist::createItem(const QFileInfo &file)
{
CollectionListItem *item = CollectionList::instance()->lookup(file.absFilePath());
......@@ -142,7 +202,7 @@ PlaylistItem *Playlist::createItem(const QFileInfo &file)
return(0);
}
void Playlist::appendImpl(const QString &item)
void Playlist::appendImpl(const QString &item, bool sorted)
{
processEvents();
QFileInfo file(QDir::cleanDirPath(item));
......@@ -191,6 +251,10 @@ void Playlist::setup()
setSorting(1);
connect(this, SIGNAL(selectionChanged()), this, SLOT(emitSelected()));
addColumn(QString::null);
setResizeMode(QListView::LastColumn);
// setFullWidth(true);
}
void Playlist::processEvents()
......
......@@ -31,8 +31,10 @@ public:
Playlist(QWidget *parent = 0, const char *name = 0);
virtual ~Playlist();
virtual void append(const QString &item);
virtual void append(const QStringList &items);
/** Set sorted = false to append the items at the end of the list rather
than adding them into the list in their sorted place. */
virtual void append(const QString &item, bool sorted = true);
virtual void append(const QStringList &items, bool sorted = true);
virtual void clearItems(const QPtrList<PlaylistItem> &items);
......@@ -54,10 +56,13 @@ public:
QStringList &getAlbumList();
protected:
virtual QDragObject *dragObject();
virtual void contentsDropEvent(QDropEvent *e);
virtual void contentsDragMoveEvent(QDragMoveEvent *e);
/** 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);
virtual void appendImpl(const QString &item, bool sorted = true);
private:
void setup();
......
......@@ -15,12 +15,15 @@
* *
***************************************************************************/
#include <klocale.h>
#include <kiconloader.h>
#include <kurldrag.h>
#include <klocale.h>
#include <kdebug.h>
#include <qdrawutil.h>
#include "playlistbox.h"
#include "collectionlist.h"
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox public methods
......@@ -28,6 +31,7 @@
PlaylistBox::PlaylistBox(QWidget *parent, const char *name) : KListBox(parent, name)
{
setAcceptDrops(true);
connect(this, SIGNAL(currentChanged(QListBoxItem *)), this, SLOT(currentItemChanged(QListBoxItem *)));
}
......@@ -36,6 +40,15 @@ PlaylistBox::~PlaylistBox()
}
QStringList PlaylistBox::names() const
{
return(nameList);
}
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox protected methods
////////////////////////////////////////////////////////////////////////////////
void PlaylistBox::resizeEvent(QResizeEvent *e)
{
// hack-ish, but functional
......@@ -46,6 +59,58 @@ void PlaylistBox::resizeEvent(QResizeEvent *e)
KListBox::resizeEvent(e);
}
void PlaylistBox::dropEvent(QDropEvent *e)
{
KURL::List urls;
if(KURLDrag::decode(e, urls) && !urls.isEmpty()) {
QStringList files;
for(KURL::List::Iterator it = urls.begin(); it != urls.end(); it++)
files.append((*it).path());
PlaylistBoxItem *i = static_cast<PlaylistBoxItem *>(itemAt(e->pos()));
if(i && i->playlist())
i->playlist()->append(files);
}
}
void PlaylistBox::dragMoveEvent(QDragMoveEvent *e)
{
// If we can decode the input source, there is a non-null item at the "move"
// position, the playlist for that PlaylistBoxItem is non-null, is not the
// selected playlist and is not the CollectionList, then accept the event.
//
// Otherwise, do not accept the event.
// At the moment this does not account for dragging from sources outside of
// JuK onto the CollectionList or to the selected PlaylistBoxItem. It makes
// sense to not allow this from the top widget of the QWidgetStack of
// playlists, however, it should allow dragging from other applications, i.e.
// Konq to these PlaylistBoxItems. This just involves checking the source
// in the logic below.
if(KURLDrag::canDecode(e) && itemAt(e->pos())) {
PlaylistBoxItem *i = static_cast<PlaylistBoxItem *>(itemAt(e->pos()));
if(i->playlist() && i->playlist() != CollectionList::instance())
if(selectedItem() && i != selectedItem())
e->accept(true);
else
e->accept(false);
else
e->accept(false);
}
else
e->accept(false);
}
void PlaylistBox::addName(const QString &name)
{
nameList.append(name);
}
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private slots
////////////////////////////////////////////////////////////////////////////////
......@@ -61,13 +126,14 @@ void PlaylistBox::currentItemChanged(QListBoxItem *item)
// PlaylistBoxItem public methods
////////////////////////////////////////////////////////////////////////////////
PlaylistBoxItem::PlaylistBoxItem(QListBox *listbox, const QPixmap &pix, const QString &text, Playlist *l) : ListBoxPixmap(listbox, pix, text)
PlaylistBoxItem::PlaylistBoxItem(PlaylistBox *listbox, const QPixmap &pix, const QString &text, Playlist *l) : ListBoxPixmap(listbox, pix, text)
{
list = l;
setOrientation(Qt::Vertical);
listbox->addName(text);
}
PlaylistBoxItem::PlaylistBoxItem(QListBox *listbox, const QString &text, Playlist *l) : ListBoxPixmap(listbox, SmallIcon("midi", 32), text)
PlaylistBoxItem::PlaylistBoxItem(PlaylistBox *listbox, const QString &text, Playlist *l) : ListBoxPixmap(listbox, SmallIcon("midi", 32), text)
{
list = l;
setOrientation(Qt::Vertical);
......
......@@ -32,16 +32,26 @@ class PlaylistBoxItem;
class PlaylistBox : public KListBox
{
friend class PlaylistBoxItem;
Q_OBJECT
public:
PlaylistBox(QWidget *parent = 0, const char *name = 0);
virtual ~PlaylistBox();
QStringList names() const;
protected:
virtual void resizeEvent(QResizeEvent *e);
virtual void dropEvent(QDropEvent *e);
virtual void dragMoveEvent(QDragMoveEvent *e);
/** This is used by PlaylistItemBox (a friend class) to add names to the name
list returned by names(). */
void addName(const QString &name);
private:
QWidgetStack *stack;
QStringList nameList;
private slots:
/** Catches QListBox::clicked(QListBoxItem *), does a cast and then re-emits
......@@ -55,13 +65,10 @@ signals:
class PlaylistBoxItem : public ListBoxPixmap
{
public:
PlaylistBoxItem(QListBox *listbox, const QPixmap &pix, const QString &text, Playlist *l = 0);
PlaylistBoxItem(QListBox *listbox, const QString &text, Playlist *l = 0);
PlaylistBoxItem(PlaylistBox *listbox, const QPixmap &pix, const QString &text, Playlist *l = 0);
PlaylistBoxItem(PlaylistBox *listbox, const QString &text, Playlist *l = 0);
virtual ~PlaylistBoxItem();
// This (and the playlist member variable) should be switched to the Playlist class once
// the design is ready for that.
Playlist *playlist() const;
private:
......
......@@ -16,6 +16,7 @@
***************************************************************************/
#include <kiconloader.h>
#include <klocale.h>
#include <kdebug.h>
#include "playlistsplitter.h"
......@@ -44,6 +45,25 @@ void PlaylistSplitter::createPlaylist(const QString &name)
connect(p, SIGNAL(collectionChanged()), editor, SLOT(updateCollection()));
}
QString PlaylistSplitter::uniquePlaylistName()
{
if(!playlistBox)
return(QString::null);
QStringList names = playlistBox->names();
QString newName = i18n("Playlist");
int playlistNumber = 1;
// while the list contains more than zero instances of the generated
// string...
while(names.contains(newName + ' ' + QString::number(playlistNumber)) != 0)
playlistNumber++;
return(newName + " " + QString::number(playlistNumber));
}
QPtrList<PlaylistItem> PlaylistSplitter::playlistSelection() const
{
Playlist *p = static_cast<Playlist *>(playlistStack->visibleWidget());
......@@ -152,9 +172,6 @@ void PlaylistSplitter::setupLayout()
// Show the collection on startup.
playlistBox->setSelected(collectionBoxItem, true);
// just for testing -- until I get the dialog/menu entry for adding playlists
createPlaylist(i18n("Playlist 1"));
}
void PlaylistSplitter::readConfig()
......
......@@ -39,6 +39,8 @@ public:
virtual ~PlaylistSplitter();
void createPlaylist(const QString &name);
/** Returns a unique string to be used as new playlist names. */
QString uniquePlaylistName();
QPtrList<PlaylistItem> playlistSelection() const;
PlaylistItem *playlistFirstItem() const;
......
......@@ -16,6 +16,7 @@
***************************************************************************/
#include <kmessagebox.h>
#include <kconfig.h>
#include <klocale.h>
#include <kdebug.h>
......@@ -44,6 +45,20 @@ TagEditor::~TagEditor()
void TagEditor::readConfig()
{
KConfig *config = KGlobal::config();
{ // combo box completion modes
KConfigGroupSaver saver(config, "TagEditor");
if(artistNameBox && albumNameBox) {
KGlobalSettings::Completion artistNameBoxMode =
KGlobalSettings::Completion(config->readNumEntry("ArtistNameBoxMode", KGlobalSettings::CompletionAuto));
artistNameBox->setCompletionMode(artistNameBoxMode);
KGlobalSettings::Completion albumNameBoxMode =
KGlobalSettings::Completion(config->readNumEntry("AlbumNameBoxMode", KGlobalSettings::CompletionAuto));
albumNameBox->setCompletionMode(albumNameBoxMode);
}
}
genreList = GenreListList::ID3v1List(); // this should later be read from a config file
if(genreList && genreBox) {
genreBox->clear();
......@@ -56,6 +71,14 @@ void TagEditor::readConfig()
void TagEditor::saveConfig()
{
KConfig *config = KGlobal::config();