diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | e2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch) | |
tree | 9047cf9e6b5c43878d5bf82660adae77ceee097a /noatun/modules/splitplaylist | |
download | tdemultimedia-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/LICENSE | 124 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/Makefile.am | 17 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/find.cpp | 63 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/find.h | 33 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/playlist.cpp | 281 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/playlist.h | 98 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/splitplaylist.cpp | 13 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/splitplaylist.plugin | 118 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/splui.rc | 34 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/view.cpp | 1009 | ||||
-rw-r--r-- | noatun/modules/splitplaylist/view.h | 165 |
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 |