2
* Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
3
* 2000-2001 Neil Stevens <neil@qualityassistant.com>
5
* Copyright (c) from the patches of:
6
* 2001 Klas Kalass <klas.kalass@gmx.de>
7
* 2001 Anno v. Heimburg <doktor.dos@gmx.de>
11
// Abandon All Hope, Ye Who Enter Here
20
#include <kstdaction.h>
22
#include <qdragobject.h>
25
#include <kfiledialog.h>
26
#include <qtextstream.h>
27
#include <noatunapp.h>
29
#include <kio/netaccess.h>
31
#include <ksimpleconfig.h>
39
#define SPL SplitPlaylist::SPL()
41
SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text)
42
: QCheckListItem(parent,0, QCheckListItem::CheckBox), PlaylistItem(text)
45
static_cast<KListView*>(parent)->moveItem(this, 0, after);
48
if (!isDownloaded()) setText(1, "0%");
51
if (!(p->currentItem || p->nextItem || p->previousItem))
53
if (p->currentItem==(SafeListViewItem*)itemAbove())
55
if (p->currentItem==(SafeListViewItem*)itemBelow())
59
SafeListViewItem::~SafeListViewItem()
61
if (SPL->nextItem==this)
62
SPL->setNext(static_cast<SafeListViewItem*>(itemBelow()));
63
if (SPL->currentItem==this)
64
SPL->setCurrent(0), SPL->setNext(static_cast<SafeListViewItem*>(itemBelow()));
65
if (SPL->previousItem==this)
66
SPL->setPrevious(static_cast<SafeListViewItem*>(itemAbove()));
69
void SafeListViewItem::downloaded(int percent)
71
setText(1, QString::number(percent)+'%');
74
void SafeListViewItem::modified()
77
if (isDownloaded() && length()!=-1)
78
setText(1, lengthString());
79
else if (isDownloaded())
83
void SafeListViewItem::stateChange(bool s)
85
// if you uncheck this, uncheck thet others that
88
QList<QListViewItem> list=SPL->view->listView()->selectedItems();
90
// but not if I'm not selected
91
if (list.containsRef(this))
92
for (QListViewItem *i=list.first(); i != 0; i=list.next())
93
static_cast<QCheckListItem*>(i)->setOn(s);
95
QCheckListItem::stateChange(s);
98
List::List(View *parent)
99
: KListView(parent), recursiveAddAfter(0), listJob(0)
101
addColumn(i18n("File"));
102
addColumn(i18n("Time"));
103
setAcceptDrops(true);
105
setDropVisualizer(true);
106
setDragEnabled(true);
107
setItemsMovable(true);
108
setSelectionMode(QListView::Extended);
109
connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), SLOT(dropEvent(QDropEvent*, QListViewItem*)));
110
connect(this, SIGNAL(moved(QList<QListViewItem>&,QList<QListViewItem>&,QList<QListViewItem>&)), SLOT(move(QList<QListViewItem>&,QList<QListViewItem>&,QList<QListViewItem>&)));
111
connect(this, SIGNAL(aboutToMove()), parent, SLOT(setNoSorting()));
118
void List::move(QList<QListViewItem>& item, QList<QListViewItem>&, QList<QListViewItem>&)
120
bool bidimerge=static_cast<bool>(item.containsRef(static_cast<SafeListViewItem*>(SPL->currentItem)));
121
bool current=SPL->currentItem;
124
if (bidimerge || item.containsRef(static_cast<SafeListViewItem*>(SPL->previousItem))
125
|| (current && item.containsRef(static_cast<SafeListViewItem*>(SPL->currentItem)->itemAbove())))
126
SPL->setPrevious(static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(SPL->currentItem)->itemAbove()));
128
if (bidimerge || item.containsRef(static_cast<SafeListViewItem*>(SPL->nextItem))
129
|| item.containsRef(static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(SPL->currentItem)->itemBelow())))
130
SPL->setNext(static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(SPL->currentItem)->itemBelow()));
137
bool List::acceptDrag(QDropEvent *event) const
139
return QUriDrag::canDecode(event) || KListView::acceptDrag(event);
142
void List::dropEvent(QDropEvent *event, QListViewItem *after)
144
static_cast<View*>(parent())->setNoSorting();
146
if (!KURLDrag::decode(event, textlist)) return;
147
event->acceptAction();
149
for (KURL::List::Iterator i=textlist.begin(); i != textlist.end(); ++i)
151
after= addFile(*i, false, after);
157
void List::viewportPaintEvent(QPaintEvent *e)
159
KListView::viewportPaintEvent(e);
161
static bool recurse=false;
164
QListViewItem *current=static_cast<SafeListViewItem*>(SPL->current());
165
QRect item(itemRect(current).intersect(e->rect()));
166
if (item.isEmpty()) return;
168
QPixmap pix(e->rect().width(), e->rect().height());
169
pix.fill(QColor(255,255,255));
172
viewport()->repaint(item,true);
175
bitBlt(viewport(), item.topLeft(), &pix, QRect(0,0, item.width(), item.height()), XorROP,false);
182
QListViewItem *List::openGlobal(const KURL &u, QListViewItem *after)
185
if(KIO::NetAccess::download(u, local))
188
saver.open(IO_ReadOnly);
189
QTextStream t(&saver);
197
if (file.find('/')) // we have to deal with a relative path
199
u1.setPath(u.path(0));
200
u1.setFileName(file);
204
after=addFile(u1, false, after);
208
KIO::NetAccess::removeTempFile(local);
213
QListViewItem *List::addFile(const KURL& url, bool play, QListViewItem *after)
215
// when a new item is added, we don't want to sort anymore
216
SPL->view->setNoSorting();
218
// TODO: there must be a better way.. this isn't windows
219
if (url.path().right(4).lower()==".m3u")
221
// a playlist is requested
222
return openGlobal(url, after);
226
if (!after) after=lastItem();
227
KFileItem fileItem(-1,-1,url);
228
if (fileItem.isDir())
230
addDirectoryRecursive(url, after);
231
return after; // don't (and can't) know better!?
235
QListViewItem *i=new SafeListViewItem(this, after, url);
237
SPL->listItemSelected(i);
244
// starts a new listJob if there is no active but work to do
245
void List::addNextPendingDirectory()
247
KURL::List::Iterator pendingIt= pendingAddDirectories.begin();
248
if (!listJob && (pendingIt!= pendingAddDirectories.end()))
250
currentJobURL= *pendingIt;
251
listJob= KIO::listRecursive(currentJobURL, false);
252
connect(listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
253
SLOT(slotEntries(KIO::Job*, const KIO::UDSEntryList&)));
254
connect(listJob, SIGNAL(result(KIO::Job *)),
255
SLOT(slotResult(KIO::Job *)));
256
connect(listJob, SIGNAL(redirection(KIO::Job *, const KURL &)),
257
SLOT(slotRedirection(KIO::Job *, const KURL &)));
258
pendingAddDirectories.remove(pendingIt);
262
void List::addDirectoryRecursive(const KURL &dir, QListViewItem *after)
264
if (!after) after=lastItem();
265
recursiveAddAfter= after;
266
pendingAddDirectories.append(dir);
267
addNextPendingDirectory();
270
void List::slotResult(KIO::Job *job)
273
if (job && job->error())
274
job->showErrorDialog();
275
addNextPendingDirectory();
278
void List::slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries)
280
QMap<QString,KURL> __list; // temp list to sort entries
282
KIO::UDSEntryListConstIterator it = entries.begin();
283
KIO::UDSEntryListConstIterator end = entries.end();
285
for (; it != end; ++it)
287
KFileItem file(*it, currentJobURL, false /* no mimetype detection */, true);
289
// insert the path + url in the map to sort automatically by path
290
// note also that you use audiocd to rip your CDs then it will be sorted the right way
291
// now it is an easy fix to have a nice sort BUT it is not the best
292
// we should sort based on the tracknumber"
293
// - copied over from old kdirlister hack <hans_meine@gmx.de>
295
__list.insert(file.url().path(), file.url());
297
QMap<QString,KURL>::Iterator __it;
298
for( __it = __list.begin(); __it != __list.end(); ++__it )
300
recursiveAddAfter= addFile(__it.data(), false, recursiveAddAfter);
304
void List::slotRedirection(KIO::Job *, const KURL & url)
309
View::View(SplitPlaylist *)
314
setCentralWidget(list);
315
connect(list, SIGNAL(modified(void)), this, SLOT(setModified(void)) );
316
// connect the click on the header with sorting
317
connect(list->header(),SIGNAL(clicked(int)),this,SLOT(headerClicked(int)) );
319
tb = new KToolBar( this );
322
(mOpen=new KAction(i18n("Add &Files"), "queue", 0, this, SLOT(addFiles()),this, "open"))->plug(tb);
323
(mDelete=new KAction(i18n("Delete"), "editdelete", Qt::Key_Delete, this, SLOT(deleteSelected()), this, "delete"))->plug(tb);
324
(new KAction(i18n("Add &Directories"), "folder", 0, this, SLOT(addDirectory()), this, "add_dir"))->plug(tb);
326
(new KActionSeparator(this))->plug(tb);
328
(mFind=KStdAction::find(this, SLOT(find()), this, "find"))->plug(tb);
330
(new KActionSeparator(this))->plug(tb);
332
(mSave=KStdAction::save(this, SLOT(save()), this, "save"))->plug(tb);
333
(mSaveAs=KStdAction::saveAs(this, SLOT(saveAs()), this, "saveas"))->plug(tb);
334
(mOpenpl=KStdAction::open(this, SLOT(open()), this, "open"))->plug(tb);
335
(mOpenNew=KStdAction::openNew(this, SLOT(openNew()), this, "openNew"))->plug(tb);
336
(new KAction(i18n("Shuffle"), "misc", 0, SPL, SLOT( randomize() ), this, "clear"))->plug(tb);
337
// (new KAction(i18n("Sort"),"queue",0,SPL,SLOT(sort()),this))->plug(tb);
338
(new KAction(i18n("Clear"), "fileclose", 0, this, SLOT( clearView() ), this, "clear"))->plug(tb);
344
Finder *f=new Finder(this);
346
connect(f, SIGNAL(search(Finder*)), SLOT(findIt(Finder*)));
349
static bool testWord(QListViewItem *i, const QString &finder)
351
PlaylistItem *item=static_cast<SafeListViewItem*>(i);
352
if (item->title().find(finder, 0, false) >=0)
354
if (item->file().find(finder, 0, false) >=0)
356
if (item->url().path().find(finder.local8Bit(), 0, false) >=0)
358
if (item->lengthString().find(finder, 0, false) >=0)
360
if (item->mimetype().find(finder.local8Bit(), 0, false) >=0)
365
static bool testWord(QListViewItem *i, const QRegExp &finder)
367
PlaylistItem *item=static_cast<SafeListViewItem*>(i);
368
if (item->title().find(finder) >=0)
370
if (item->file().find(finder) >=0)
372
if (item->url().path().find(finder) >=0)
374
if (item->lengthString().find(finder) >=0)
376
if (item->mimetype().find(finder) >=0)
381
void View::findIt(Finder *f)
383
QListViewItem *search=list->currentItem();
387
search=list->firstChild();
389
search=list->lastChild();
394
search=search->itemBelow();
396
search=search->itemAbove();
401
if (testWord(search, QRegExp(f->string(), false)))
406
if (testWord(search, f->string()))
415
QList<QListViewItem> sel=list->selectedItems();
416
for (QListViewItem *i=sel.first(); i!=0; i=sel.next())
417
list->setSelected(i, false);
419
list->setSelected(search, true);
420
list->setCurrentItem(search);
421
list->ensureItemVisible(search);
438
internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylistdata");
439
list->openGlobal(internalURL);
441
KConfig &config = *KGlobal::config();
442
config.setGroup("splitplaylist");
444
// this has to come after openGlobal, since openGlobal emits modified()
445
setModified(config.readBoolEntry("modified", false));
446
mPlaylistFile.setPath(config.readEntry("file", QString::null));
449
int saved = config.readNumEntry("current", 0);
451
PlaylistItem *item=SPL->getFirst();
452
for(int i = 0 ; i < saved ; i++)
454
item=SPL->getAfter(item);
457
SPL->setCurrent(item);
462
if (mPlaylistFile.isMalformed()) { saveAs(); return; }
463
saveToURL(mPlaylistFile);
469
KURL u=KFileDialog::getSaveURL(0, "*.m3u\n*", this, i18n("Save Playlist"));
470
if (u.isMalformed()) return;
477
KURL u=KFileDialog::getOpenURL(0, "*.m3u\n*", this, i18n("Insert Playlist"));
478
if (u.isMalformed()) return;
485
KURL u=KFileDialog::getOpenURL(0, "*.m3u\n*", this, i18n("Open New Playlist"));
486
if (u.isMalformed()) return;
490
list->openGlobal(mPlaylistFile);
495
void View::deleteSelected()
497
QList<QListViewItem> items(list->selectedItems());
498
for (QListIterator<QListViewItem> it(items); it.current(); ++it)
504
void View::addFiles()
506
KURL::List files=KFileDialog::getOpenURLs(0, napp->mimeTypes(), this, i18n("Select a File to Play"));
508
for(KURL::List::Iterator it=files.begin(); it!=files.end(); ++it)
514
void View::addDirectory()
516
QString file=KFileDialog::getExistingDirectory(0, this, i18n("Select a Directory"));
521
list->addDirectoryRecursive(url);
526
void View::closeEvent(QCloseEvent*)
531
void View::showEvent(QShowEvent *)
536
void View::hideEvent(QHideEvent *)
541
void View::setModified(bool b)
544
setCaption(i18n("Playlist"), modified);
547
void View::setModified(void)
552
void View::saveToURL(const KURL &url)
554
QString local(napp->tempSaveName(url.path()));
556
saver.open(IO_ReadWrite | IO_Truncate);
557
QTextStream t(&saver);
559
for (SafeListViewItem *i=static_cast<SafeListViewItem*>(listView()->firstChild());
560
i != 0; i=static_cast<SafeListViewItem*>(i->itemBelow()))
564
KIO::NetAccess::upload(local, url);
569
void View::saveState(void)
571
KConfig &config = *KGlobal::config();
572
config.setGroup("splitplaylist");
574
config.writeEntry("modified", modified);
575
config.writeEntry("file", mPlaylistFile.path());
576
saveToURL(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylistdata");
579
PlaylistItem *item=SPL->getFirst();
580
for(i = 0; item != SPL->current(); )
581
item=SPL->getAfter(item), i++;
583
config.writeEntry("current", i);
588
// turns the sorting on or off
589
void View::setSorting(bool on, int column)
593
list->setSorting(column,true);
594
list->setShowSortIndicator(true);
598
list->setShowSortIndicator(false);
599
list->setSorting(-1);
603
void View::headerClicked(int column)
605
/* this is to avoid that if we already have it sorted, we sort it again ascendingly
606
this way, clicking on the header a second time will correctly toggle ascending/descending sort */
607
if (list-> showSortIndicator())
613
setSorting(true,column);