summaryrefslogtreecommitdiffstats
path: root/noatun/modules/splitplaylist
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commite2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch)
tree9047cf9e6b5c43878d5bf82660adae77ceee097a /noatun/modules/splitplaylist
downloadtdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz
tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'noatun/modules/splitplaylist')
-rw-r--r--noatun/modules/splitplaylist/LICENSE124
-rw-r--r--noatun/modules/splitplaylist/Makefile.am17
-rw-r--r--noatun/modules/splitplaylist/find.cpp63
-rw-r--r--noatun/modules/splitplaylist/find.h33
-rw-r--r--noatun/modules/splitplaylist/playlist.cpp281
-rw-r--r--noatun/modules/splitplaylist/playlist.h98
-rw-r--r--noatun/modules/splitplaylist/splitplaylist.cpp13
-rw-r--r--noatun/modules/splitplaylist/splitplaylist.plugin118
-rw-r--r--noatun/modules/splitplaylist/splui.rc34
-rw-r--r--noatun/modules/splitplaylist/view.cpp1009
-rw-r--r--noatun/modules/splitplaylist/view.h165
11 files changed, 1955 insertions, 0 deletions
diff --git a/noatun/modules/splitplaylist/LICENSE b/noatun/modules/splitplaylist/LICENSE
new file mode 100644
index 00000000..8f9bdefc
--- /dev/null
+++ b/noatun/modules/splitplaylist/LICENSE
@@ -0,0 +1,124 @@
+The "Artistic License"
+
+ Preamble
+
+ The intent of this document is to state the conditions under which a
+ Package may be copied, such that the Copyright Holder maintains some
+ semblance of artistic control over the development of the package,
+ while giving the users of the package the right to use and distribute
+ the Package in a more-or-less customary fashion, plus the right to
+ make reasonable modifications.
+
+ Definitions
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes of the
+ Copyright Holder as specified below.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing this
+ Package.
+
+ "Reasonable copying fee" is whatever you can justify on the basis
+ of media cost, duplication charges, time of people involved, and so
+ on. (You will not be required to justify it to the Copyright
+ Holder, but only to the computing community at large as a market
+ that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item. It
+ also means that recipients of the item may redistribute it under
+ the same conditions they received it.
+
+ 1. You may make and give away verbatim copies of the source form of
+ the Standard Version of this Package without restriction, provided
+ that you duplicate all of the original copyright notices and
+ associated disclaimers.
+ 2. You may apply bug fixes, portability fixes and other modifications
+ derived from the Public Domain or from the Copyright Holder. A
+ Package modified in such a way shall still be considered the
+ Standard Version.
+ 3. You may otherwise modify your copy of this Package in any way,
+ provided that you insert a prominent notice in each changed file
+ stating how and when you changed that file, and provided that you
+ do at least ONE of the following:
+
+ a. place your modifications in the Public Domain or otherwise make
+ them Freely Available, such as by posting said modifications to
+ Usenet or an equivalent medium, or placing the modifications on a
+ major archive site such as uunet.uu.net, or by allowing the
+ Copyright Holder to include your modifications in the Standard
+ Version of the Package.
+ b. use the modified Package only within your corporation or
+ organization.
+ c. rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and
+ provide a separate manual page for each non-standard executable
+ that clearly documents how it differs from the Standard Version.
+ d. make other distribution arrangements with the Copyright Holder.
+
+ You may distribute the programs of this Package in object code or
+ executable form, provided that you do at least ONE of the following:
+
+ a. distribute a Standard Version of the executables and library
+ files, together with instructions (in the manual page or
+ equivalent) on where to get the Standard Version.
+ b. accompany the distribution with the machine-readable source of the
+ Package with your modifications.
+ c. give non-standard executables non-standard names, and clearly
+ document the differences in manual pages (or equivalent), together
+ with instructions on where to get the Standard Version.
+ d. make other distribution arrangements with the Copyright Holder.
+
+ You may charge a reasonable copying fee for any distribution of this
+ Package. You may charge any fee you choose for support of this
+ Package. You may not charge a fee for this Package itself. However,
+ you may distribute this Package in aggregate with other (possibly
+ commercial) programs as part of a larger (possibly commercial)
+ software distribution provided that you do not advertise this Package
+ as a product of your own. You may embed this Package's interpreter
+ within an executable of yours (by linking); this shall be construed as
+ a mere form of aggregation, provided that the complete Standard
+ Version of the interpreter is so embedded.
+
+ The scripts and library files supplied as input to or produced as
+ output from the programs of this Package do not automatically fall
+ under the copyright of this Package, but belong to whomever generated
+ them, and may be sold commercially, and may be aggregated with this
+ Package. If such scripts or library files are aggregated with this
+ Package via the so-called "undump" or "unexec" methods of producing a
+ binary executable image, then distribution of such an image shall
+ neither be construed as a distribution of this Package nor shall it
+ fall under the restrictions of Paragraphs 3 and 4, provided that you
+ do not represent such an executable image as a Standard Version of
+ this Package.
+
+ C subroutines (or comparably compiled subroutines in other
+ languages) supplied by you and linked into this Package in order to
+ emulate subroutines and variables of the language defined by this
+ Package shall not be considered part of this Package, but are the
+ equivalent of input as in Paragraph 6, provided these subroutines do
+ not change the language in any way that would cause it to fail the
+ regression tests for the language.
+
+ Aggregation of this Package with a commercial distribution is always
+ permitted provided that the use of this Package is embedded; that is,
+ when no overt attempt is made to make this Package's interfaces
+ visible to the end user of the commercial distribution. Such use shall
+ not be construed as a distribution of this Package.
+
+ The name of the Copyright Holder may not be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+ THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
diff --git a/noatun/modules/splitplaylist/Makefile.am b/noatun/modules/splitplaylist/Makefile.am
new file mode 100644
index 00000000..4ea511ac
--- /dev/null
+++ b/noatun/modules/splitplaylist/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_splitplaylist.la
+
+noatun_splitplaylist_la_SOURCES = splitplaylist.cpp playlist.cpp view.cpp find.cpp
+
+noatun_splitplaylist_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_splitplaylist_la_LIBADD = $(LIB_KIO) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_splitplaylist_la_METASOURCES = AUTO
+
+noinst_HEADERS = playlist.h view.h find.h
+
+noatun_modules_splitplaylist_DATA = splitplaylist.plugin
+noatun_modules_splitplaylistdir = $(kde_datadir)/noatun
+
+rc_DATA = splui.rc
+rcdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/splitplaylist/find.cpp b/noatun/modules/splitplaylist/find.cpp
new file mode 100644
index 00000000..b6e196d9
--- /dev/null
+++ b/noatun/modules/splitplaylist/find.cpp
@@ -0,0 +1,63 @@
+#include "find.h"
+#include <qlayout.h>
+#include <kcombobox.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+#include <klocale.h>
+
+Finder::Finder(QWidget *parent) : KDialogBase(parent, 0, false, i18n("Find"), Close | User1, User1, false, KGuiItem(i18n("&Find"),"find"))
+{
+ QWidget *mainWidget = new QWidget(this);
+ mainWidget->setMinimumWidth(320);
+ setMainWidget(mainWidget);
+
+ QGridLayout *layout=new QGridLayout(mainWidget);
+ layout->setSpacing(KDialog::spacingHint());
+
+ mText=new KHistoryCombo(mainWidget);
+ mText->setMaxCount(10);
+
+ mText->setFocus();
+
+ mRegexp=new QCheckBox(i18n("&Regular expression"), mainWidget);
+ mBackwards=new QCheckBox(i18n("Find &backwards"), mainWidget);
+
+ layout->addMultiCellWidget(mText, 0, 0, 0, 1);
+ layout->addWidget(mRegexp, 1, 0);
+ layout->addWidget(mBackwards, 1, 1);
+
+ connect(this, SIGNAL(user1Clicked()), SLOT(clicked()));
+
+ connect(mText, SIGNAL(activated(int)), SLOT(clicked()));
+ connect(mText, SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &)));
+
+ enableButton(User1, false);
+}
+
+void Finder::textChanged(const QString &text) {
+ enableButton(User1, !text.isEmpty());
+}
+
+bool Finder::regexp() const
+{
+ return mRegexp->isChecked();
+}
+
+bool Finder::isForward() const
+{
+ return !mBackwards->isChecked();
+}
+
+void Finder::clicked()
+{
+ mText->addToHistory( mText->currentText() );
+ emit search(this);
+}
+
+QString Finder::string() const
+{
+ return mText->currentText();
+}
+
+
+#include "find.moc"
diff --git a/noatun/modules/splitplaylist/find.h b/noatun/modules/splitplaylist/find.h
new file mode 100644
index 00000000..a4791339
--- /dev/null
+++ b/noatun/modules/splitplaylist/find.h
@@ -0,0 +1,33 @@
+#ifndef FIND_H
+#define FIND_H
+
+#include <kdialogbase.h>
+
+class KHistoryCombo;
+class QCheckBox;
+class QPushButton;
+
+class Finder : public KDialogBase
+{
+Q_OBJECT
+public:
+ Finder(QWidget *parent);
+
+ bool regexp() const;
+ bool isForward() const;
+
+ QString string() const;
+signals:
+ void search(Finder *);
+
+public slots:
+ void textChanged(const QString &);
+ void clicked();
+
+private:
+ KHistoryCombo *mText;
+ QCheckBox *mRegexp, *mBackwards;
+};
+
+#endif
+
diff --git a/noatun/modules/splitplaylist/playlist.cpp b/noatun/modules/splitplaylist/playlist.cpp
new file mode 100644
index 00000000..57d6fb48
--- /dev/null
+++ b/noatun/modules/splitplaylist/playlist.cpp
@@ -0,0 +1,281 @@
+#include "playlist.h"
+#include "view.h"
+#include <noatun/player.h>
+
+#include <kapplication.h>
+#include <krandomsequence.h>
+#include <kdebug.h>
+#include <kwin.h>
+
+#include <kiconloader.h>
+
+SplitPlaylist *SplitPlaylist::Self=0;
+
+SplitPlaylist::SplitPlaylist()
+ : Playlist(0, "SplitPlaylist"), Plugin(), mExiting(false)
+{
+ Self=this;
+}
+
+void SplitPlaylist::init()
+{
+ view=new View(this); // 195
+ connect(view->listView(), SIGNAL(executed(QListViewItem*)), SLOT(listItemSelected(QListViewItem*)));
+ connect(view, SIGNAL(shown()), SIGNAL(listShown()));
+ connect(view, SIGNAL(hidden()), SIGNAL(listHidden()));
+
+ view->init(); // 1000
+}
+
+SplitPlaylist::~SplitPlaylist()
+{
+ mExiting=true;
+ delete view;
+}
+
+void SplitPlaylist::reset()
+{
+ SafeListViewItem *i;
+ setCurrent(i=static_cast<SafeListViewItem*>(view->listView()->firstChild()), false);
+ if (i && !i->isOn())
+ next(false);
+}
+
+PlaylistItem SplitPlaylist::next()
+{
+ return next(true);
+}
+
+PlaylistItem SplitPlaylist::next(bool play)
+{
+ PlaylistItem nextItem;
+
+ if (napp->player()->loopStyle() == Player::Random)
+ {
+ // Ignore all this order stuff and select a random item
+ List *lview = view->listView();
+
+ if (lview->childCount())
+ {
+ SafeListViewItem *slvi = static_cast<SafeListViewItem*>(
+ lview->itemAtIndex(KApplication::random() % lview->childCount())
+ );
+ nextItem = PlaylistItem(slvi);
+ }
+ else
+ {
+ nextItem = 0;
+ }
+ }
+ else
+ {
+ if(!current())
+ {
+ nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+ else
+ {
+ nextItem = static_cast<SafeListViewItem*>(
+ static_cast<SafeListViewItem*>(current().data())->itemBelow());
+ }
+ }
+
+ if (!nextItem) // don't set a null-item as current item
+ {
+ return 0;
+// nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+
+ PlaylistItem oldCurrent = currentItem;
+ setCurrent(nextItem, play);
+
+ // Hack for back button on randomized play
+ if (oldCurrent)
+ randomPrevious = oldCurrent;
+
+ if (currentItem)
+ if (!static_cast<SafeListViewItem*>(currentItem.data())->isOn())
+ return next(play);
+
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::current()
+{
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::previous()
+{
+ if (napp->player()->loopStyle() == Player::Random && randomPrevious)
+ {
+ List *list = view->listView();
+ // check if the item still exists (hackitude: 50%)
+ bool found=false;
+ for (QListViewItem *i = list->firstChild(); i; i = i->nextSibling())
+ {
+ if (i == static_cast<SafeListViewItem*>(randomPrevious.data()))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ // setCurrent modified randomPrevious, and setCurrent is pass-by-reference
+ PlaylistItem prev = randomPrevious;
+
+ setCurrent(prev);
+ return currentItem;
+ }
+ }
+ // there's a possibility that I will fall out to here
+ // from the above branch
+
+ PlaylistItem nextItem;
+ if(!current())
+ {
+ nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+ else
+ {
+ nextItem = static_cast<SafeListViewItem*>(
+ static_cast<SafeListViewItem*>(current().data())->itemAbove());
+ }
+ if (!nextItem) // don't set a null-item as current item
+ return 0;
+
+ setCurrent(nextItem);
+
+ if (currentItem)
+ if (!static_cast<SafeListViewItem*>(currentItem.data())->isOn())
+ return previous();
+
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::getFirst() const
+{
+ return static_cast<SafeListViewItem*>(view->listView()->firstChild());
+}
+
+PlaylistItem SplitPlaylist::getAfter(const PlaylistItem &item) const
+{
+ if (item)
+ return static_cast<SafeListViewItem*>(static_cast<const SafeListViewItem*>(item.data())->nextSibling());
+ return 0;
+}
+
+bool SplitPlaylist::listVisible() const
+{
+ KWin::WindowInfo info = KWin::windowInfo(view->winId());
+ return !(info.hasState(NET::Shaded) || info.hasState(NET::Hidden) || !info.valid() || !info.isOnCurrentDesktop());
+}
+
+void SplitPlaylist::showList()
+{
+ KWin::setOnDesktop(view->winId(), KWin::currentDesktop());
+ view->show();
+ if (view->isMinimized())
+ view->showNormal();
+ view->raise();
+}
+
+void SplitPlaylist::hideList()
+{
+ view->hide();
+}
+
+void SplitPlaylist::clear()
+{
+ view->listView()->clear();
+}
+
+void SplitPlaylist::addFile(const KURL &file, bool play)
+{
+ view->addFile(file, play);
+}
+
+void SplitPlaylist::setCurrent(const PlaylistItem &i)
+{
+ setCurrent(i, true);
+}
+
+void SplitPlaylist::setCurrent(const PlaylistItem &i, bool emitC)
+{
+ randomPrevious = PlaylistItem();
+ emitC = emitC && currentItem;
+ if (!i)
+ {
+ currentItem=0;
+ }
+ else
+ {
+ // remove the old icon
+ SafeListViewItem *now=static_cast<SafeListViewItem*>(current().data());
+ if (now)
+ now->setPixmap(0, QPixmap());
+
+ QRect rect(view->listView()->itemRect(static_cast<SafeListViewItem*>(current().data())));
+ rect.setWidth(view->listView()->viewport()->width());
+ currentItem=i;
+ view->listView()->viewport()->repaint(rect,true);
+
+ view->listView()->ensureItemVisible(static_cast<SafeListViewItem*>(current().data()));
+ QRect currentRect= view->listView()->itemRect(static_cast<SafeListViewItem*>(current().data()));
+ view->listView()->viewport()->repaint(currentRect);
+
+ now=static_cast<SafeListViewItem*>(current().data());
+ if(now)
+ now->setPixmap(0, ::SmallIcon("noatunplay"));
+ }
+
+ if (emitC && !exiting())
+ emit playCurrent();
+}
+
+void SplitPlaylist::remove(const PlaylistItem &)
+{
+// delete i;
+}
+
+void SplitPlaylist::listItemSelected(QListViewItem *i)
+{
+ setCurrent(PlaylistItem(static_cast<SafeListViewItem*>(i)), false);
+ emit playCurrent();
+}
+
+void SplitPlaylist::randomize()
+{
+ // turning off sorting is necessary
+ // otherwise, the list will get randomized and promptly sorted again
+ view->setSorting(false);
+ List *lview = view->listView();
+ // eeeeevil :)
+ QPtrList<void> list;
+ QPtrList<QListViewItem> items;
+ for(int i = 0; i < lview->childCount(); i++)
+ {
+ list.append( (void*) i );
+ items.append( lview->itemAtIndex( i ) );
+ }
+
+ KRandomSequence seq;
+ seq.randomize( &list );
+
+ for(int i = 0; i < lview->childCount(); i++)
+ {
+ items.take()->moveItem(lview->itemAtIndex((long) list.take()));
+ }
+
+ setCurrent(currentItem, false);
+}
+
+void SplitPlaylist::sort()
+{
+ view->setSorting(true);
+ setCurrent(currentItem, false);
+}
+
+#include "playlist.moc"
diff --git a/noatun/modules/splitplaylist/playlist.h b/noatun/modules/splitplaylist/playlist.h
new file mode 100644
index 00000000..04cb648d
--- /dev/null
+++ b/noatun/modules/splitplaylist/playlist.h
@@ -0,0 +1,98 @@
+#ifndef PLAYLIST_H
+#define PLAYLIST_H
+
+#include <noatun/playlist.h>
+#include <noatun/plugin.h>
+
+/*
+class PlaylistItem
+{
+ PlaylistItem(const KURL &u=0);
+ virtual ~PlaylistItem();
+
+ QString title() const;
+ virtual void setTitle(const QString &t);
+
+ KURL url() const;
+ virtual void setUrl(const KURL &u);
+
+ int length() const;
+ virtual void setLength(int l);
+};
+*/
+class SafeListViewItem;
+class View;
+class List;
+class QListViewItem;
+
+class SplitPlaylist : public Playlist, public Plugin
+{
+Q_OBJECT
+friend class SafeListViewItem;
+friend class List;
+public:
+ SplitPlaylist();
+ ~SplitPlaylist();
+
+ /**
+ * go to the front
+ **/
+ virtual void reset();
+
+ virtual void clear();
+ virtual void addFile(const KURL&, bool play=false);
+ /**
+ * Cycle everthing through forward
+ **/
+ virtual PlaylistItem next();
+ PlaylistItem next(bool play);
+ /**
+ * return the one that might/should be playing now
+ **/
+ virtual PlaylistItem current();
+ /**
+ * Cycle through backwards
+ **/
+ virtual PlaylistItem previous();
+
+ virtual PlaylistItem getFirst() const;
+ virtual PlaylistItem getAfter(const PlaylistItem &item) const;
+
+ virtual bool listVisible() const;
+ virtual void init();
+
+ virtual Playlist *playlist()
+ { return this; }
+
+ static SplitPlaylist *SPL() { return Self; }
+ inline bool exiting() const { return mExiting; }
+public slots:
+ virtual void showList();
+ virtual void hideList();
+ virtual void remove(const PlaylistItem&);
+ virtual void sort();
+
+
+public slots:
+ void setCurrent(const PlaylistItem &, bool emitC);
+ void setCurrent(const PlaylistItem &);
+
+ void listItemSelected(QListViewItem*);
+
+ void randomize();
+
+private:
+ PlaylistItem currentItem, randomPrevious;
+
+signals:
+ void play(PlaylistItem*);
+
+private:
+ bool mExiting; // HACK HACK HACK HACK!!!
+ View *view;
+// QRect currentRect;
+ static SplitPlaylist *Self;
+};
+
+
+#endif
diff --git a/noatun/modules/splitplaylist/splitplaylist.cpp b/noatun/modules/splitplaylist/splitplaylist.cpp
new file mode 100644
index 00000000..e86a3921
--- /dev/null
+++ b/noatun/modules/splitplaylist/splitplaylist.cpp
@@ -0,0 +1,13 @@
+#include <kcmodule.h>
+
+#include "playlist.h"
+
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new SplitPlaylist();
+ }
+}
+
diff --git a/noatun/modules/splitplaylist/splitplaylist.plugin b/noatun/modules/splitplaylist/splitplaylist.plugin
new file mode 100644
index 00000000..3474dbed
--- /dev/null
+++ b/noatun/modules/splitplaylist/splitplaylist.plugin
@@ -0,0 +1,118 @@
+Filename=noatun_splitplaylist.la
+Author=Charles Samuels
+Site=http://www.derkarl.org/noatun
+Email=charles@kde.org
+Type=playlist
+License=Artistic
+Name=Split Playlist
+Name[af]=Skei Liedjielys
+Name[az]=Çalma Siyahısını Ayır
+Name[bn]=বিভাজিত সঙ্গীত-তালিকা
+Name[br]=Didrochañ ar roll tonioù
+Name[bs]=Podijeli playlistu
+Name[ca]=Dividir la selecció de peces
+Name[cs]=Oddělovací seznam skladeb
+Name[cy]=Hollti Rhestr Chwarae
+Name[da]=Opdelt spilleliste
+Name[de]=Aufgeteilte Wiedergabeliste
+Name[el]=Split λίστα αναπαραγωγής
+Name[eo]=Dividu ludliston
+Name[es]=Lista de reproducción dividida
+Name[et]=Poolitatud nimekiri
+Name[eu]=Erreprodukzio-zerrenda zatitu
+Name[fa]=شکافتن فهرست پخش
+Name[fi]=Jaettu soittolista
+Name[fr]=Liste de lecture découpée
+Name[ga]=Roinn Seinmliosta
+Name[gl]=Dividir Lista de Reproducións
+Name[hi]=स्प्लिट प्लेलिस्ट
+Name[hr]=Razdvoji listu pjesama
+Name[hu]=Osztott lejátszási lista
+Name[is]=Tvískiptur lagalisti
+Name[it]=Split playlist
+Name[km]=ពុះ​បញ្ជី​ចាក់
+Name[ko]=재생 목록 나누기
+Name[lt]=Suskaidytas gaidaraštis
+Name[lv]=Dalīt Plejlistu
+Name[mk]=Раздели листа со нумери
+Name[mt]=Playlist Maqsum
+Name[nb]=Splittet spilleliste
+Name[nds]=Opdeelt Afspeellist
+Name[ne]=बजाउने सूची विभाजन गर्नुहोस्
+Name[nl]=Gesplitste speellijst
+Name[nn]=Delt speleliste
+Name[pa]=ਸੰਗੀਤ-ਸੂਚੀ ਵੰਡੋ
+Name[pl]=Zwykła lista odtwarzania
+Name[pt]=Lista de Músicas Split
+Name[pt_BR]=A lista de reprodução dividida
+Name[ro]=Împarte lista de redare
+Name[ru]=Разбить список произведений
+Name[se]=Juhkkojuvvon čuojahanlistu
+Name[sk]=Rozdeľovací playlist
+Name[sl]=Razdeli predvajalni seznam
+Name[sr]=Split листа нумера
+Name[sr@Latn]=Split lista numera
+Name[sv]=Delad spellista
+Name[ta]=பாடல் பட்டியலை பிரி
+Name[tg]=Рӯйхати бозикуниҳои Пора
+Name[th]=แยกรายการเล่น
+Name[tr]=Ayrılmış Parça Listesi
+Name[uk]=Розбитий список композицій
+Name[ven]=Mutevhe wa tshitambi tshau phadalala
+Name[xh]=Chaka uluhlu lomdlalo
+Name[zh_CN]=分割播放列表
+Name[zh_HK]=分割播放清單
+Name[zh_TW]=分割播放清單
+Name[zu]=Qhekeza uluhlu lomdlalo
+Comment=The inaccurately titled playlist
+Comment[bg]=Неподреден списък за поддръжка на файлове за изпълнение
+Comment[bs]=Neispravno naslovljena playlista
+Comment[ca]=Llista de reproducció titulada inexactament
+Comment[cs]=Nesprávně pojmenovaný seznam skladeb
+Comment[cy]=Y rhestr chwarae efo'r teitl gwallus
+Comment[da]=Den upræcist benævnte spilleliste
+Comment[de]=Die ungenaue Wiedergabeliste
+Comment[el]=Η ανακριβώς ονομαζόμενη λίστα αναπαραγωγής
+Comment[eo]=La neĝuste titolita ludlisto
+Comment[es]=La lista de reproducción con título inexacto
+Comment[et]=Ebaadekvaatselt nimetatud nimekiri
+Comment[eu]=Zehatza ez den izenburudun erreprodukzio-zerrenda
+Comment[fa]=فهرست پخش که با بی‌دقتی عنوان‌بندی شده است
+Comment[fi]=Epätarkasti nimetty soittolista
+Comment[fr]=La liste de lecture mal nommée
+Comment[ga]=An seinmliosta le teideal neamhchruinn
+Comment[gl]=A lista de reprodución con títulos inexactos
+Comment[he]=רשימת הניגון עם השם הלא מדוייק
+Comment[hi]=गलत शीर्षक युक्त गीत-सूची
+Comment[hu]=Nem pontosan feliratozott lejátszási lista
+Comment[is]=Lagalistinn með ónákvæma nafnið
+Comment[it]=Playlist con i titoli non accurati
+Comment[ja]=不正確なタイトルのプレイリスト (実際に分割はしません)
+Comment[kk]=Дұрыс аталмаған орындау тізімі
+Comment[km]=បញ្ជី​ចាក់​ដែល​មាន​ចំណងជើង​មិន​សុក្រឹត
+Comment[ko]=정확하지 않게 제목이 붙은 재생 목록
+Comment[lt]=Netiksliai pavadintas gaidaraštis
+Comment[mk]=Неточно насловена листа со нумери
+Comment[nb]=Den unøyaktig navngitte spillelisten
+Comment[nds]=De nich nau nöömte Afspeellist
+Comment[ne]=अशुद्ध तरिकाले शीर्षक दिइएको बजाउने सूची
+Comment[nl]=De inaccuraat getitelde afspeellijst
+Comment[nn]=Spelelista med unøyaktig namn
+Comment[pl]=Zwykła, prosta lista odtwarzania
+Comment[pt]=A lista mal intitulada
+Comment[pt_BR]=A lista de reprodução erroneamente intitulada
+Comment[ro]=Listă de redare incorect denumită
+Comment[ru]=Список песен с неточными названиями
+Comment[sk]=Nesprávne pomenovaný playlist
+Comment[sl]=Nenatančno naslovljen predvajalni seznam
+Comment[sr]=Непрецизно названа листа нумера
+Comment[sr@Latn]=Neprecizno nazvana lista numera
+Comment[sv]=Den felaktigt benämnda spellistan
+Comment[ta]=பிழையாக தலைப்பிட்ட பாடல் பட்டியல்
+Comment[tg]=Рӯйхати бозикуниҳои бетартибона номгузошташуда
+Comment[th]=รายการเล่นที่มีชื่อไม่ถูกต้อง
+Comment[tr]=Düzensiz başlıklı çalma listesi
+Comment[uk]=Неохайно підписаний список композицій
+Comment[zh_CN]=命名不确切的播放列表
+Comment[zh_HK]=未有正確命名的播放清單
+Comment[zh_TW]=未精確命名的撥放清單
diff --git a/noatun/modules/splitplaylist/splui.rc b/noatun/modules/splitplaylist/splui.rc
new file mode 100644
index 00000000..2b0b0a32
--- /dev/null
+++ b/noatun/modules/splitplaylist/splui.rc
@@ -0,0 +1,34 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="SPL" version="1">
+
+<MenuBar>
+
+<Menu name="file">
+ <Action name="add_files" />
+ <Action name="add_dir" />
+</Menu>
+
+<Menu name="edit">
+ <Action name="delete" />
+ <Action name="clear" />
+ <Separator />
+ <Action name="shuffle" />
+</Menu>
+
+</MenuBar>
+
+<ToolBar noMerge="1" name="mainToolBar">
+ <text>Main Toolbar</text>
+ <Action name="add_files" />
+ <Action name="add_dir" />
+ <Separator />
+ <Action name="delete" />
+ <Separator />
+ <Action name="file_open" />
+ <Action name="file_save" />
+ <Action name="file_save_as" />
+ <Separator />
+ <Action name="edit_find" />
+</ToolBar>
+
+</kpartgui>
diff --git a/noatun/modules/splitplaylist/view.cpp b/noatun/modules/splitplaylist/view.cpp
new file mode 100644
index 00000000..7f5584f9
--- /dev/null
+++ b/noatun/modules/splitplaylist/view.cpp
@@ -0,0 +1,1009 @@
+/**
+ * Copyright (c) 2000-2004 Charles Samuels <charles@kde.org>
+ * 2000-2001 Neil Stevens <neil@qualityassistant.com>
+ *
+ * Copyright (c) from the patches of:
+ * 2001 Klas Kalass <klas.kalass@gmx.de>
+ * 2001 Anno v. Heimburg <doktor.dos@gmx.de>
+ **/
+
+
+// Abandon All Hope, Ye Who Enter Here
+
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qlayout.h>
+#include <qmap.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+#include <qpainter.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kfileitem.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <kedittoolbar.h>
+#include <kurldrag.h>
+#include <kmessagebox.h>
+
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/playlistsaver.h>
+
+#include "playlist.h"
+#include "view.h"
+#include "find.h"
+
+#define SPL SplitPlaylist::SPL()
+
+SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text)
+ : QCheckListItem(parent,0, QCheckListItem::CheckBox), PlaylistItemData(), removed(false)
+{
+ addRef();
+ setUrl(text);
+
+ static_cast<KListView*>(parent)->moveItem(this, 0, after);
+ setOn(true);
+
+ // is this really needed, it makes the listview too wide for me :(
+// setText(0,text.filename());
+
+ // if (!isDownloaded()) setText(1, "0%");
+
+// mProperties.setAutoDelete(true);
+
+ if (!streamable() && enqueue(url()))
+ setUrl(KURL(localFilename()));
+
+ PlaylistItemData::added();
+}
+
+SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const QMap<QString,QString> &props)
+ : QCheckListItem(parent, 0, QCheckListItem::CheckBox), removed(false)
+{
+ addRef();
+
+ setOn(true);
+
+ // A version of setProperty that assumes a key is unique,
+ // and doesn't call modified for every new key.
+ // Ugly, but this function is a very hot path on playlist loading
+ for (QMap<QString,QString>::ConstIterator i=props.begin(); i!=props.end(); ++i )
+ {
+ QString n = i.key();
+ QString val = i.data();
+
+ if (n=="enabled")
+ {
+ setOn(val!="false" && val!="0");
+ }
+ else
+ {
+ Property p={n,val};
+ mProperties += p;
+ }
+ }
+
+ static_cast<KListView*>(parent)->moveItem(this, 0, after);
+ modified();
+
+ if (!streamable() && enqueue(url()))
+ {
+ KURL u;
+ u.setPath(localFilename());
+
+ setUrl(u);
+ }
+ PlaylistItemData::added();
+}
+
+SafeListViewItem::~SafeListViewItem()
+{
+ remove();
+}
+
+QString SafeListViewItem::file() const
+{
+ return localFilename();
+}
+
+static void pad(QString &str)
+{
+ int len=str.length();
+ int at = 0;
+ int blocklen=0;
+
+ static const int paddingsize=12;
+
+ // not static for reason
+ const QChar chars[paddingsize] =
+ {
+ QChar('0'), QChar('0'), QChar('0'), QChar('0'),
+ QChar('0'), QChar('0'), QChar('0'), QChar('0'),
+ QChar('0'), QChar('0'), QChar('0'), QChar('0')
+ };
+
+ for (int i=0; i < len; i++)
+ {
+ if (str[i].isNumber())
+ {
+ if (!blocklen)
+ at = i;
+ blocklen++;
+ }
+ else if (blocklen)
+ {
+ int pads=paddingsize;
+ pads -= blocklen;
+ str.insert(at, chars, pads);
+ i += pads;
+ blocklen = 0;
+ }
+ }
+ if (blocklen)
+ {
+ int pads=paddingsize;
+ pads -= blocklen;
+ str.insert(at, chars, pads);
+ }
+}
+
+int SafeListViewItem::compare(QListViewItem * i, int col, bool) const
+{
+ QString text1 = text(col);
+ QString text2 = i->text(col);
+
+ pad(text1);
+ pad(text2);
+ return text1.compare(text2);
+}
+
+QString SafeListViewItem::property(const QString &n, const QString &def) const
+{
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ return (*i).value;
+ }
+ if (n=="enabled")
+ {
+ if (isOn()) return "true";
+ else return "false";
+ }
+ return def;
+}
+
+void SafeListViewItem::setProperty(const QString &n, const QString &val)
+{
+ if (n=="enabled")
+ {
+ setOn(val!="false" && val!="0");
+ }
+ else
+ {
+ if ( property(n,"") == val )
+ {
+// kdDebug(66666) << "SafeListViewItem::setProperty(), property unchanged!" << endl;
+ return;
+ }
+
+ clearProperty(n);
+ Property p={n,val};
+ mProperties += p;
+ }
+ modified();
+}
+
+void SafeListViewItem::clearProperty(const QString &n)
+{
+ if (n=="enabled")
+ {
+ setOn(true);
+ modified();
+ return;
+ }
+
+ for (QValueList<Property>::Iterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ {
+ mProperties.remove(i);
+ modified();
+ break;
+ }
+ }
+}
+
+QStringList SafeListViewItem::properties() const
+{
+ QStringList list;
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ list += (*i).key;
+ list += "enabled";
+ return list;
+}
+
+bool SafeListViewItem::isProperty(const QString &n) const
+{
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ return true;
+ }
+ return n=="enabled";
+}
+
+void SafeListViewItem::downloaded(int percent)
+{
+ if (!removed)
+ setText(1, QString::number(percent)+'%');
+}
+
+void SafeListViewItem::downloadTimeout()
+{
+ if (!removed)
+ setText(1, "-");
+}
+
+void SafeListViewItem::downloadFinished()
+{
+ if (!removed)
+ setText(1, "");
+}
+
+void SafeListViewItem::modified()
+{
+ bool widthChangeNeeded = false;
+
+ if (text(0)!=title())
+ {
+ setText(0, title());
+ widthChangeNeeded = true;
+ }
+
+ if (isDownloaded() && length()!=-1 && text(1)!=lengthString())
+ {
+ setText(1, lengthString());
+ widthChangeNeeded = true;
+ }
+
+ if (widthChangeNeeded)
+ widthChanged(-1);
+
+ PlaylistItemData::modified();
+}
+
+void SafeListViewItem::stateChange(bool s)
+{
+ // if you uncheck this, uncheck thet others that
+ // are selected too
+
+ QPtrList<QListViewItem> list=SPL->view->listView()->selectedItems();
+
+ // but not if I'm not selected
+ if (list.containsRef(this))
+ for (QListViewItem *i=list.first(); i != 0; i=list.next())
+ static_cast<QCheckListItem*>(i)->setOn(s);
+ else
+ QCheckListItem::stateChange(s);
+}
+
+void SafeListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align)
+{
+ QCheckListItem::paintCell(p, cg, column, width, align);
+
+ if (SPL->current() == this)
+ {
+ p->save();
+ p->setRasterOp(XorROP);
+ p->fillRect(0, 0, width, height(), QColor(255,255,255));
+
+ p->restore();
+ }
+}
+
+void SafeListViewItem::remove()
+{
+ removed=true;
+ if (napp->player()->current()==this && !itemAbove() && !itemBelow())
+ {
+ napp->player()->stop();
+ SPL->setCurrent(0);
+ }
+ else if (napp->player()->current()==this)
+ {
+// SPL->setCurrent(0);
+// napp->player()->playCurrent();
+
+ if (napp->player()->isPlaying() && !SPL->exiting())
+ napp->player()->forward();
+ else
+ SPL->setCurrent(0);
+ }
+
+ if (listView())
+ {
+ if (SPL->currentItem==this) // just optimizing for least unreadably
+ SPL->setCurrent(static_cast<SafeListViewItem*>(itemBelow()));
+
+ listView()->takeItem(this);
+ }
+ else if (SPL->currentItem==this)
+ {
+ SPL->setCurrent(0);
+ }
+
+ dequeue();
+ PlaylistItemData::removed();
+}
+
+List::List(View *parent)
+ : KListView(parent), recursiveAddAfter(0), listJob(0)
+{
+ addColumn(i18n("File"));
+ addColumn(i18n("Time"));
+ setAcceptDrops(true);
+ setSorting(-1);
+ setDropVisualizer(true);
+ setDragEnabled(true);
+ setItemsMovable(true);
+ setSelectionMode(QListView::Extended);
+ connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), SLOT(dropEvent(QDropEvent*, QListViewItem*)));
+ connect(this, SIGNAL(moved()), SLOT(move()));
+ connect(this, SIGNAL(aboutToMove()), parent, SLOT(setNoSorting()));
+ connect(this, SIGNAL(deleteCurrentItem()), parent, SLOT(deleteSelected()));
+}
+
+List::~List()
+{
+}
+
+void List::move()
+{
+ emit modified();
+}
+
+bool List::acceptDrag(QDropEvent *event) const
+{
+ return KURLDrag::canDecode(event) || KListView::acceptDrag(event);
+}
+
+void List::dropEvent(QDropEvent *event, QListViewItem *after)
+{
+ static_cast<View*>(parent())->setNoSorting();
+ KURL::List textlist;
+ if (!KURLDrag::decode(event, textlist)) return;
+ event->acceptAction();
+
+ for (KURL::List::Iterator i=textlist.begin(); i != textlist.end(); ++i)
+ {
+ after= addFile(*i, false, after);
+ }
+
+ emit modified();
+}
+
+void List::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key()==Key_Enter || e->key()==Key_Return)
+ {
+ if (currentItem())
+ {
+ emit KListView::executed(currentItem());
+ }
+
+ return;
+ }
+
+ if (e->key()==Key_Delete)
+ {
+ if (currentItem())
+ {
+ emit deleteCurrentItem();
+ }
+
+ return;
+ }
+
+ KListView::keyPressEvent(e);
+
+
+}
+
+/**
+ * use this only once!!!
+ **/
+class NoatunSaver : public PlaylistSaver
+{
+ List *mList;
+ SafeListViewItem *after, *mFirst;
+public:
+ NoatunSaver(List *l, QListViewItem *after=0)
+ : mList(l)
+ {
+ this->after = static_cast<SafeListViewItem*>(after);
+ mFirst = 0;
+ }
+
+ QListViewItem *getAfter() { return after; }
+ QListViewItem *getFirst() { return mFirst; }
+
+protected:
+ virtual void readItem(const QMap<QString,QString> &properties)
+ {
+ after = new SafeListViewItem(mList, after, properties);
+ if (mFirst==0)
+ mFirst = after;
+ }
+
+ virtual PlaylistItem writeItem()
+ {
+ if (!after)
+ {
+ after=static_cast<SafeListViewItem*>(mList->firstChild());
+ }
+ else
+ {
+ after=static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(after)->nextSibling());
+ }
+ return PlaylistItem(after);
+ }
+};
+
+
+bool View::saveToURL(const KURL &url)
+{
+ NoatunSaver saver(list);
+ if(saver.save(url))
+ {
+ return true;
+ }
+ else
+ {
+ KMessageBox::error( this, i18n("Could not write to %1.").arg(url.prettyURL()) );
+ return false;
+ }
+}
+
+void View::exportTo(const KURL &url)
+{
+ QString local(napp->tempSaveName(url.path()));
+ QFile saver(local);
+ saver.open(IO_ReadWrite | IO_Truncate);
+ QTextStream t(&saver);
+ // navigate the list
+ for (SafeListViewItem *i=static_cast<SafeListViewItem*>(listView()->firstChild());
+ i != 0; i=static_cast<SafeListViewItem*>(i->itemBelow()))
+ {
+ KURL u=i->url();
+ if (u.isLocalFile())
+ t<< u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ saver.close();
+
+ KIO::NetAccess::upload(local, url, this);
+
+ saver.remove();
+}
+
+QListViewItem *List::openGlobal(const KURL &u, QListViewItem *after)
+{
+ clear();
+ NoatunSaver saver(this, after);
+ saver.metalist(u);
+
+ return saver.getAfter();
+}
+
+// for m3u files
+QListViewItem *List::importGlobal(const KURL &u, QListViewItem *after)
+{
+ NoatunSaver saver(this, after);
+ if (!saver.metalist(u))
+ {
+ after=new SafeListViewItem(this, after, u);
+// SPL->listItemSelected(after);
+ return after;
+ }
+
+ // return the first item added from this playlist
+ // that way noatun can start playing the first item
+ if (saver.getFirst())
+ return saver.getFirst();
+
+ // failsafe in case nothing was added, getFirst() may return 0
+ return saver.getAfter();
+}
+
+QListViewItem *List::addFile(const KURL& url, bool play, QListViewItem *after)
+{
+ // when a new item is added, we don't want to sort anymore
+ SPL->view->setNoSorting();
+
+ if (
+ url.path().right(4).lower()==".m3u"
+ || url.path().right(4).lower()==".pls"
+ || url.protocol().lower()=="http"
+ )
+ {
+ // a playlist is requested
+ QListViewItem *i = importGlobal(url, after);
+ if (play)
+ SPL->listItemSelected(i);
+ return i;
+ }
+ else
+ {
+ if (!after) after=lastItem();
+ KFileItem fileItem(KFileItem::Unknown,KFileItem::Unknown,url);
+ if (fileItem.isDir())
+ {
+ addDirectoryRecursive(url, after);
+ return after; // don't (and can't) know better!?
+ }
+ else
+ {
+ QListViewItem *i = new SafeListViewItem(this, after, url);
+ if (play)
+ SPL->listItemSelected(i);
+ return i;
+ }
+ }
+}
+
+// starts a new listJob if there is no active but work to do
+void List::addNextPendingDirectory()
+{
+ KURL::List::Iterator pendingIt= pendingAddDirectories.begin();
+ if (!listJob && (pendingIt!= pendingAddDirectories.end()))
+ {
+ currentJobURL= *pendingIt;
+ listJob= KIO::listRecursive(currentJobURL, false,false);
+ connect(
+ listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
+ SLOT(slotEntries(KIO::Job*, const KIO::UDSEntryList&))
+ );
+ connect(
+ listJob, SIGNAL(result(KIO::Job *)),
+ SLOT(slotResult(KIO::Job *))
+ );
+ connect(
+ listJob, SIGNAL(redirection(KIO::Job *, const KURL &)),
+ SLOT(slotRedirection(KIO::Job *, const KURL &))
+ );
+ pendingAddDirectories.remove(pendingIt);
+ }
+}
+
+void List::addDirectoryRecursive(const KURL &dir, QListViewItem *after)
+{
+ if (!after) after=lastItem();
+ recursiveAddAfter= after;
+ pendingAddDirectories.append(dir);
+ addNextPendingDirectory();
+}
+
+void List::slotResult(KIO::Job *job)
+{
+ listJob= 0;
+ if (job && job->error())
+ job->showErrorDialog();
+ addNextPendingDirectory();
+}
+
+void List::slotEntries(KIO::Job *, const KIO::UDSEntryList &entries)
+{
+ QMap<QString,KURL> __list; // temp list to sort entries
+
+ KIO::UDSEntryListConstIterator it = entries.begin();
+ KIO::UDSEntryListConstIterator end = entries.end();
+
+ for (; it != end; ++it)
+ {
+ KFileItem file(*it, currentJobURL, false /* no mimetype detection */, true);
+ // "prudhomm:
+ // insert the path + url in the map to sort automatically by path
+ // note also that you use audiocd to rip your CDs then it will be sorted the right way
+ // now it is an easy fix to have a nice sort BUT it is not the best
+ // we should sort based on the tracknumber"
+ // - copied over from old kdirlister hack <hans_meine@gmx.de>
+ if (!file.isDir())
+ __list.insert(file.url().path(), file.url());
+ }
+ QMap<QString,KURL>::Iterator __it;
+ for( __it = __list.begin(); __it != __list.end(); ++__it )
+ {
+ recursiveAddAfter= addFile(__it.data(), false, recursiveAddAfter);
+ }
+}
+
+void List::slotRedirection(KIO::Job *, const KURL & url)
+{
+ currentJobURL= url;
+}
+
+/////////////////////////////////
+
+View::View(SplitPlaylist *)
+ : KMainWindow(0, "NoatunSplitplaylistView")
+{
+ list=new List(this);
+ setCentralWidget(list);
+ connect(list, SIGNAL(modified(void)), this, SLOT(setModified(void)) );
+ // connect the click on the header with sorting
+ connect(list->header(),SIGNAL(clicked(int)),this,SLOT(headerClicked(int)) );
+
+ mOpen=new KAction(i18n("Add &Files..."), "queue", 0, this, SLOT(addFiles()), actionCollection(), "add_files");
+ (void) new KAction(i18n("Add Fol&ders..."), "folder", 0, this, SLOT(addDirectory()), actionCollection(), "add_dir");
+ mDelete=new KAction(i18n("Delete"), "editdelete", Key_Delete, this, SLOT(deleteSelected()), actionCollection(), "delete");
+
+ mClose=KStdAction::close(this, SLOT(close()), actionCollection());
+ mFind=KStdAction::find(this, SLOT(find()), actionCollection());
+
+ (void) KStdAction::configureToolbars(this, SLOT(configureToolBars()), actionCollection());
+ mOpenNew=KStdAction::openNew(this, SLOT(openNew()), actionCollection());
+ mOpenpl=KStdAction::open(this, SLOT(open()), actionCollection());
+ mSave=KStdAction::save(this, SLOT(save()), actionCollection());
+ mSaveAs=KStdAction::saveAs(this, SLOT(saveAs()), actionCollection());
+
+ (void) new KAction(i18n("Shuffle"), "misc", 0, SPL, SLOT( randomize() ), actionCollection(), "shuffle");
+ (void) new KAction(i18n("Clear"), "editclear", 0, list, SLOT( clear() ), actionCollection(), "clear");
+
+ createGUI("splui.rc");
+
+ mFinder = new Finder(this);
+
+ applyMainWindowSettings(KGlobal::config(), "SPL Window");
+ list->setFocus();
+}
+
+void View::find()
+{
+ mFinder->show();
+ connect(mFinder, SIGNAL(search(Finder*)), SLOT(findIt(Finder*)));
+}
+
+static bool testWord(QListViewItem *i, const QString &finder)
+{
+ PlaylistItemData *item=static_cast<SafeListViewItem*>(i);
+ if (item->title().find(finder, 0, false) >=0)
+ return true;
+ if (item->file().find(finder, 0, false) >=0)
+ return true;
+ if (item->url().path().find(finder.local8Bit(), 0, false) >=0)
+ return true;
+ if (item->lengthString().find(finder, 0, false) >=0)
+ return true;
+ if (item->mimetype().find(finder.local8Bit(), 0, false) >=0)
+ return true;
+ return false;
+}
+
+static bool testWord(QListViewItem *i, const QRegExp &finder)
+{
+ PlaylistItemData *item=static_cast<SafeListViewItem*>(i);
+ if (item->title().find(finder) >=0)
+ return true;
+ if (item->file().find(finder) >=0)
+ return true;
+ if (item->url().path().find(finder) >=0)
+ return true;
+ if (item->lengthString().find(finder) >=0)
+ return true;
+ if (item->mimetype().find(finder) >=0)
+ return true;
+ return false;
+}
+
+void View::findIt(Finder *f)
+{
+ QListViewItem *search=list->currentItem();
+
+ if (list->currentItem())
+ {
+ if (f->isForward())
+ search=list->currentItem()->itemBelow();
+ else
+ search=list->currentItem()->itemAbove();
+ }
+ else
+ {
+ if (f->isForward())
+ search=list->firstChild();
+ else
+ search=list->lastChild();
+ }
+
+
+ while (search)
+ {
+ if (f->regexp())
+ {
+ if (testWord(search, QRegExp(f->string(), false)))
+ break;
+ }
+ else
+ {
+ if (testWord(search, f->string()))
+ break;
+ }
+
+ if (f->isForward())
+ search=search->itemBelow();
+ else
+ search=search->itemAbove();
+
+ if (!search)
+ {
+ if (f->isForward())
+ {
+ if (KMessageBox::questionYesNo(this, i18n("End of playlist reached. Continue searching from beginning?"),QString::null,KStdGuiItem::cont(),KStdGuiItem::cancel()) == KMessageBox::Yes)
+ search=list->firstChild();
+ }
+ else
+ {
+ if (KMessageBox::questionYesNo(this, i18n("Beginning of playlist reached. Continue searching from end?"),QString::null,KStdGuiItem::cont(),KStdGuiItem::cancel()) == KMessageBox::Yes)
+ search=list->lastChild();
+ }
+ }
+ }
+
+ if (search)
+ {
+ { // select none
+ QPtrList<QListViewItem> sel=list->selectedItems();
+ for (QListViewItem *i=sel.first(); i!=0; i=sel.next())
+ list->setSelected(i, false);
+ }
+ list->setSelected(search, true);
+ list->setCurrentItem(search);
+ list->ensureItemVisible(search);
+ }
+}
+
+View::~View()
+{
+ napp->player()->stop();
+ hide();
+ saveState();
+ delete list;
+}
+
+void View::init()
+{
+ // see if we are importing an old-style list
+ bool importing= ! QFile(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml").exists();
+
+ if (importing)
+ {
+ KURL internalURL;
+ internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylistdata");
+ NoatunSaver saver(list, 0);
+ saver.load(internalURL, PlaylistSaver::M3U);
+ }
+ else
+ {
+ KURL internalURL;
+ internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml");
+ list->openGlobal(internalURL);
+ }
+
+ KConfig &config = *KGlobal::config();
+ config.setGroup("splitplaylist");
+
+ // this has to come after openGlobal, since openGlobal emits modified()
+ setModified(config.readBoolEntry("modified", false));
+ QString path = config.readPathEntry("file");
+ // don't call setPath with an empty path, that would make the url "valid"
+ if ( !path.isEmpty() )
+ mPlaylistFile.setPath(path);
+
+ SPL->reset();
+ int saved = config.readNumEntry("current", 0);
+
+ PlaylistItem item=SPL->getFirst();
+ for(int i = 0 ; i < saved ; i++)
+ {
+ item=SPL->getAfter(item);
+ }
+ if (item)
+ SPL->setCurrent(item);
+}
+
+void View::save()
+{
+ if(mPlaylistFile.isEmpty() || !mPlaylistFile.isValid())
+ {
+ saveAs();
+ return;
+ }
+
+ if(saveToURL(mPlaylistFile))
+ setModified(false);
+}
+
+void View::saveAs()
+{
+ KURL u=KFileDialog::getSaveURL(0, "*.xml splitplaylistdata *.pls *.m3u\n*", this, i18n("Save Playlist"));
+ if(!u.isValid())
+ return;
+ mPlaylistFile = u;
+ save();
+}
+
+void View::open()
+{
+ KURL u=KFileDialog::getOpenURL(0, "*.xml splitplaylistdata *.pls *.m3u\n*", this, i18n("Open Playlist"));
+ if(!u.isValid())
+ return;
+ mPlaylistFile = u;
+ list->openGlobal(u);
+ setModified(false);
+}
+
+void View::openNew()
+{
+ mPlaylistFile = "";
+ listView()->clear();
+}
+
+void List::clear()
+{
+ SPL->setCurrent(0);
+
+ QListView::clear();
+}
+
+void View::deleteSelected()
+{
+ QPtrList<QListViewItem> items(list->selectedItems());
+
+ bool stopped=false;
+ // noatun shouldn't play files for now
+ QListViewItem *afterLast=0;
+
+ for (QPtrListIterator<QListViewItem> it(items); it.current(); ++it)
+ {
+ SafeListViewItem *i = static_cast<SafeListViewItem*>(*it);
+ if (!stopped && SPL->current() == i)
+ {
+ napp->player()->stop();
+ SPL->setCurrent(0);
+ stopped = true;
+ }
+ i->remove();
+
+ afterLast = i->itemBelow();
+ }
+
+ if (stopped)
+ SPL->setCurrent(static_cast<SafeListViewItem*>(afterLast));
+
+ setModified(true);
+}
+
+void View::addFiles()
+{
+ KURL::List files=KFileDialog::getOpenURLs(":mediadir", napp->mimeTypes(), this, i18n("Select File to Play"));
+
+ QListViewItem *last = list->lastItem();
+ for(KURL::List::Iterator it=files.begin(); it!=files.end(); ++it)
+ last = addFile(KURL(*it), false);
+
+ setModified(true);
+}
+
+void View::addDirectory()
+{
+ QString file=KFileDialog::getExistingDirectory(0, this, i18n("Select Folder"));
+
+ if (!file) return;
+ KURL url;
+ url.setPath(file);
+ list->addDirectoryRecursive(url);
+
+ setModified(true);
+}
+
+void View::closeEvent(QCloseEvent*)
+{
+ hide();
+}
+
+void View::showEvent(QShowEvent *)
+{
+ emit shown();
+}
+
+void View::hideEvent(QHideEvent *)
+{
+ emit hidden();
+}
+
+void View::setModified(bool b)
+{
+ modified = b;
+ setCaption(i18n("Playlist"), modified);
+}
+
+void View::setModified(void)
+{
+ setModified(true);
+}
+
+void View::saveState(void)
+{
+ KConfig &config = *KGlobal::config();
+ config.setGroup("splitplaylist");
+
+ config.writeEntry("modified", modified);
+ config.writePathEntry("file", mPlaylistFile.path());
+ saveToURL(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml");
+
+ unsigned int i;
+ PlaylistItem item=SPL->getFirst();
+ for(i = 0; item && item != SPL->current(); )
+ item=SPL->getAfter(item), i++;
+
+ config.writeEntry("current", i);
+ saveMainWindowSettings(KGlobal::config(), "SPL Window");
+
+ config.sync();
+}
+
+void View::configureToolBars()
+{
+ saveMainWindowSettings(KGlobal::config(), "SPL Window");
+ KEditToolbar dlg(actionCollection(), "splui.rc");
+ connect(&dlg, SIGNAL(newToolbarConfig()), SLOT(newToolBarConfig()));
+ dlg.exec();
+}
+
+void View::newToolBarConfig()
+{
+ createGUI("splui.rc");
+ applyMainWindowSettings(KGlobal::config(), "SPL Window");
+}
+
+// turns the sorting on or off
+void View::setSorting(bool on, int column)
+{
+ if (on)
+ {
+ list->setSorting(column,true);
+ list->setShowSortIndicator(true);
+ }
+ else
+ {
+ list->setShowSortIndicator(false);
+ list->setSorting(-1);
+ }
+}
+
+void View::headerClicked(int column)
+{
+ // this is to avoid that if we already have it sorted,
+ // we sort it again ascendingly this way, clicking on
+ // the header a second time will correctly toggle
+ // ascending/descending sort
+ if (list->showSortIndicator())
+ {
+ return;
+ }
+ else
+ {
+ setSorting(true,column);
+ }
+}
+
+#include "view.moc"
+
diff --git a/noatun/modules/splitplaylist/view.h b/noatun/modules/splitplaylist/view.h
new file mode 100644
index 00000000..18dc917a
--- /dev/null
+++ b/noatun/modules/splitplaylist/view.h
@@ -0,0 +1,165 @@
+#ifndef VIEW_H
+#define VIEW_H
+
+#include <qevent.h>
+#include <qptrlist.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <qrect.h>
+#include <qdict.h>
+#include <kio/global.h>
+#include <noatun/downloader.h>
+
+class Finder;
+class View;
+namespace KIO { class ListJob; }
+
+
+class SafeListViewItem
+ : public QCheckListItem
+ , public PlaylistItemData
+ , public DownloadItem
+{
+public:
+ SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text);
+ SafeListViewItem(QListView *parent, QListViewItem *after, const QMap<QString,QString> &properties);
+ virtual ~SafeListViewItem();
+
+ virtual QString property(const QString &, const QString & = 0) const;
+ virtual void setProperty(const QString &, const QString &);
+ virtual void clearProperty(const QString &);
+ virtual QStringList properties() const;
+ virtual bool isProperty(const QString &) const;
+
+ virtual QString file() const;
+
+ int compare(QListViewItem * i, int col, bool ascending) const;
+ virtual void remove();
+
+protected:
+ virtual void downloaded(int percent);
+ virtual void downloadTimeout();
+ virtual void downloadFinished();
+ virtual void modified();
+ virtual void stateChange(bool s);
+
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align);
+
+private:
+ struct Property
+ {
+ QString key;
+ QString value;
+ };
+ QValueList<Property> mProperties;
+ bool removed;
+};
+
+class List : public KListView
+{
+Q_OBJECT
+friend class View;
+public:
+ List(View *parent);
+ virtual ~List();
+ QListViewItem *openGlobal(const KURL&, QListViewItem * =0);
+ QListViewItem *importGlobal(const KURL&, QListViewItem * =0);
+ QListViewItem *addFile(const KURL&, bool play=false, QListViewItem * =0);
+ void addDirectoryRecursive(const KURL &dir, QListViewItem *after= 0);
+
+public slots:
+ virtual void clear();
+
+signals:
+ void modified(void);
+ void deleteCurrentItem();
+
+protected:
+ virtual bool acceptDrag(QDropEvent *event) const;
+ virtual void keyPressEvent(QKeyEvent *e);
+
+protected slots:
+ virtual void dropEvent(QDropEvent *event, QListViewItem *after);
+ void move();
+
+protected:
+ QListViewItem *recursiveAddAfter;
+
+protected slots:
+ // used when adding directories via KIO::listRecursive
+ void slotResult(KIO::Job *job);
+ void slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries);
+ void slotRedirection(KIO::Job *, const KURL & url);
+
+protected:
+ void addNextPendingDirectory();
+ KURL::List pendingAddDirectories;
+ KIO::ListJob *listJob;
+ KURL currentJobURL;
+};
+
+class KFileDialog;
+class KToggleAction;
+class KToolBar;
+
+class View : public KMainWindow
+{
+Q_OBJECT
+public:
+ View(SplitPlaylist *mother);
+ // load the SM playlist
+ void init();
+ virtual ~View();
+ List *listView() const { return list; }
+ QListViewItem *addFile(const KURL &u, bool play=false)
+ { return list->addFile(u, play, list->lastItem()); }
+
+
+public slots:
+ void deleteSelected();
+ void addFiles();
+ void addDirectory();
+ void save();
+ void saveAs();
+ void open();
+ void openNew();
+ void setSorting(bool on, int column = 0);
+ void setNoSorting() { setSorting(false); }
+ void headerClicked(int column);void find();
+ void findIt(Finder *);
+
+
+private slots:
+ void setModified();
+ void saveState();
+
+ void configureToolBars();
+ void newToolBarConfig();
+
+protected:
+ void setupActions();
+
+ bool saveToURL(const KURL &);
+ void exportTo(const KURL &);
+
+ void setModified(bool);
+ virtual void closeEvent(QCloseEvent*e);
+ virtual void showEvent(QShowEvent *);
+ virtual void hideEvent(QHideEvent *);
+
+signals:
+ void hidden();
+ void shown();
+
+private:
+ List *list;
+ KAction *mOpen, *mDelete, *mSave, *mSaveAs, *mOpenpl, *mOpenNew;
+ KAction *mClose;
+ KAction *mFind;
+ Finder *mFinder;
+
+ KURL mPlaylistFile;
+ bool modified;
+};
+
+#endif