playlistsplitter.cpp 25.1 KB
Newer Older
1 2 3 4 5
/***************************************************************************
                          playlistsplitter.cpp  -  description
                             -------------------
    begin                : Fri Sep 13 2002
    copyright            : (C) 2002 by Scott Wheeler
6
    email                : wheeler@kde.org
7 8 9 10 11 12 13 14 15 16 17
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

18
#include <kstandarddirs.h>
19
#include <kmessagebox.h>
20
#include <kinputdialog.h>
21
#include <kcmdlineargs.h>
22 23
#include <kdebug.h>

24
#include <qpopupmenu.h>
25

26
#include "playlistsplitter.h"
27
#include "searchwidget.h"
28
#include "directorylist.h"
29
#include "playlistsearch.h"
30
#include "dynamicplaylist.h"
31
#include "searchplaylist.h"
32
#include "historyplaylist.h"
33
#include "mediafiles.h"
34
#include "advancedsearchdialog.h"
35 36

////////////////////////////////////////////////////////////////////////////////
37
// helper functions
38 39
////////////////////////////////////////////////////////////////////////////////

40
void processEvents()
41
{
42 43 44
    static int processed = 0;
    if(processed == 0)
        kapp->processEvents();
45
    processed = (processed + 1) % 5;
46 47
}

48
////////////////////////////////////////////////////////////////////////////////
49
// public methods
50 51
////////////////////////////////////////////////////////////////////////////////

52
PlaylistSplitter::PlaylistSplitter(QWidget *parent, const char *name) :
53
    QSplitter(Qt::Horizontal, parent, name),
54 55
    m_playingItem(0), m_searchWidget(0), m_history(0),
    m_dynamicList(0), m_nextPlaylistItem(0)
56
{
57 58 59 60 61 62 63 64
#ifndef NO_DEBUG
    m_restore = KCmdLineArgs::parsedArgs()->isSet("restore");
#else
    m_restore = true;
#endif

    m_dirWatch = new KDirWatch();

65 66
    setupLayout();
    readConfig();
67 68

    m_editor->slotUpdateCollection();
69
}
70

71
PlaylistSplitter::~PlaylistSplitter()
72
{
Scott Wheeler's avatar
Scott Wheeler committed
73
    delete m_dirWatch;
74
    saveConfig();
75 76
}

77
QString PlaylistSplitter::uniquePlaylistName(const QString &startingWith, bool useParenthesis)
78
{
79
    if(!m_playlistBox)
80
	return QString::null;
81

82
    QStringList names = m_playlistBox->names();
83 84 85

    int playlistNumber = 1;

86
    // while the list contains more than zero instances of the generated
87 88
    // string...

89 90 91
    if(useParenthesis) {
	while(names.contains(startingWith + " (" + QString::number(playlistNumber) + ")") != 0)
	    playlistNumber++;
92 93

	return startingWith + " (" + QString::number(playlistNumber) + ")";
94
    }
95
    else {
96 97
	while(names.contains(startingWith + ' ' + QString::number(playlistNumber)) != 0)
	    playlistNumber++;
98

99
	return startingWith + " " + QString::number(playlistNumber);
100
    }
101 102
}

103
QString PlaylistSplitter::playNextFile(bool random, bool loopPlaylist)
104
{
105 106
    PlaylistItem *i;

107
    // Four basic cases here:  (1) We've asked for a specific next item, (2) play
108
    // the item that's after the currently playing item, (3) play the selected
109
    // item or (4) play the first item in the list.
110

111
    // (1) we've asked for a specific next item
112
    if(m_nextPlaylistItem && m_nextPlaylistItem != m_playingItem) {
113 114 115
        i = m_nextPlaylistItem;
        m_nextPlaylistItem = 0;
    }
116
    // (2) play the item after the currently selected item
117
    else if(m_playingItem) {
118 119
        Playlist *p = static_cast<Playlist *>(m_playingItem->listView());
        i = p->nextItem(m_playingItem, random);
120
        if(!i && loopPlaylist)
121
            i = static_cast<PlaylistItem *>(p->firstChild());
122
    }
123 124
    // (3) play the selected item
    else if(playlistSelection().size() > 0) {
125
        i = playlistSelection().first();
126 127
        if(!i)
            i = static_cast<PlaylistItem *>(visiblePlaylist()->firstChild());
128
    }
129 130 131
    // (4) play the first item in the list
    else
        i = static_cast<PlaylistItem *>(visiblePlaylist()->firstChild());
132

133
    return play(i);
134 135
}

136
QString PlaylistSplitter::playPreviousFile(bool random)
137
{
138
    if(!m_playingItem)
139
	return QString::null;
140

141 142
    Playlist *p = static_cast<Playlist *>(m_playingItem->listView());
    PlaylistItem *i = p->previousItem(m_playingItem, random);
143

144
    return play(i);
145 146
}

147 148 149 150 151 152
void PlaylistSplitter::populatePlayHistoryMenu(QPopupMenu* menu, bool random)
{
    Playlist *p = static_cast<Playlist *>(m_playingItem->listView());
    PlaylistItemList list = p->historyItems(m_playingItem, random);
    menu->clear();
    int i = 0;
153
    for(PlaylistItemList::Iterator it = list.begin(); it != list.end(); ++it)
154 155 156
        menu->insertItem((*it)->tag()->track(), ++i);
}

157 158 159 160 161 162 163 164
QString PlaylistSplitter::playSelectedFile()
{
    if(playlistSelection().isEmpty())
	return QString::null;
    else
	return play(playlistSelection().first());
}

165 166 167 168
QString PlaylistSplitter::playFirstFile()
{
    Playlist *p = visiblePlaylist();
    PlaylistItem *i = static_cast<PlaylistItem *>(p->firstChild());
169

170
    return play(i);
171 172
}

173 174 175 176 177 178 179 180 181
QString PlaylistSplitter::playRandomFile()
{
    Playlist *p = visiblePlaylist();
    PlaylistItem *i = static_cast<PlaylistItem *>(p->firstChild());

    // Not exactly random (the first item won't be taken into account)
    return play(p->nextItem(i, true));
}

182 183
void PlaylistSplitter::stop()
{
184 185
    m_nextPlaylistItem = 0;

186 187 188 189 190
    if(!m_playingItem)
	return;

    Playlist *p = static_cast<Playlist *>(m_playingItem->listView());

191 192 193
    if(p)
	p->setPlaying(m_playingItem, false);

194
    m_playingItem = 0;
195 196 197 198
}

QString PlaylistSplitter::playingArtist() const
{
199 200 201 202
    if(m_playingItem) {
	int offset =  static_cast<Playlist *>(m_playingItem->listView())->columnOffset();
	return m_playingItem->text(PlaylistItem::ArtistColumn + offset);
    }
203
    else
204
	return QString::null;
205 206 207 208
}

QString PlaylistSplitter::playingTrack() const
{
209 210 211 212
    if(m_playingItem) {
	int offset =  static_cast<Playlist *>(m_playingItem->listView())->columnOffset();
	return m_playingItem->text(PlaylistItem::TrackColumn + offset);
    }
213
    else
214
	return QString::null;
215 216 217 218
}

QString PlaylistSplitter::playingList() const
{
219 220
    if(m_playingItem)
	return static_cast<Playlist *>(m_playingItem->listView())->name();
221
    else
222
	return QString::null;
223 224
}

225
void PlaylistSplitter::open(const QString &file)
226
{
227 228 229
    if(file.isEmpty())
	return;

230 231 232 233 234
    if(visiblePlaylist() == m_collection ||
       KMessageBox::questionYesNo(this,
				  i18n("Do you want to add this item to the current list or to the collection list?"),
				  QString::null,
				  KGuiItem(i18n("Current")),
235 236
				  KGuiItem(i18n("Collection"))) == KMessageBox::No)
    {
237
	slotAddToPlaylist(file, m_collection);
238
    }
239
    else
240
	slotAddToPlaylist(file, visiblePlaylist());
241 242
}

243
void PlaylistSplitter::open(const QStringList &files)
244
{
245 246
    if(files.isEmpty())
	return;
247 248 249 250 251 252 253

    if(visiblePlaylist() == m_collection ||
       KMessageBox::questionYesNo(this,
				  i18n("Do you want to add these items to the current list or to the collection list?"),
				  QString::null,
				  KGuiItem(i18n("Current")),
				  KGuiItem(i18n("Collection"))) == KMessageBox::No)
254
    {
255
	slotAddToPlaylist(files, m_collection);
256
    }
257
    else
258
	slotAddToPlaylist(files, visiblePlaylist());
259 260 261 262
}

Playlist *PlaylistSplitter::createPlaylist(const QString &name)
{
263
    Playlist *p = new Playlist(m_playlistStack, name);
264 265
    setupPlaylist(p, true);
    return p;
266 267
}

268 269 270 271
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

272
void PlaylistSplitter::slotOpen()
273
{
274
    open(MediaFiles::openDialog(this));
275 276
}

277
void PlaylistSplitter::slotOpenDirectory()
278
{
279
    DirectoryList *l = new DirectoryList(m_directoryList, this, "directoryList");
280

281
    m_directoryQueue.clear();
282
    m_directoryQueueRemove.clear();
283

284
    connect(l, SIGNAL(signalDirectoryAdded(const QString &)),
285
	    this, SLOT(slotQueueDirectory(const QString &)));
286
    connect(l, SIGNAL(signalDirectoryRemoved(const QString &)),
287
	    this, SLOT(slotQueueDirectoryRemove(const QString &)));
288 289

    if(l->exec() == QDialog::Accepted) {
290
	open(m_directoryQueue);
291 292
	for(QStringList::Iterator it = m_directoryQueue.begin(); it !=  m_directoryQueue.end(); it++)
	    m_dirWatch->addDir(*it, false, true);
293

294
	m_directoryList += m_directoryQueue;
295 296 297

	QStringList::Iterator it = m_directoryQueueRemove.begin();
	for(; it !=  m_directoryQueueRemove.end(); it++) {
298
	    m_dirWatch->removeDir(*it);
299
	    m_directoryList.remove(*it);
300
	}
301 302
    }
}
303

304
Playlist *PlaylistSplitter::slotCreatePlaylist(const QString &name_)
305 306 307
{
    bool ok;

308
    // If this text is changed, please also change it in PlaylistBox::duplicate().
309

310
    QString name = KInputDialog::getText(i18n("Create New Playlist"),
Nadeem Hasan's avatar
Nadeem Hasan committed
311
	i18n("Please enter a name for the new playlist:"),
312
	name_.isNull() ? uniquePlaylistName() : name_, &ok);
313
    if(ok)
314
	return createPlaylist(name);
315
    else
316
	return 0;
317 318
}

319 320 321 322 323 324
Playlist *PlaylistSplitter::slotCreatePlaylistFromDir()
{
    const QString dirName = KFileDialog::getExistingDirectory();
    if(dirName.isEmpty())
        return 0;

325
    Playlist *playlist = slotCreatePlaylist(dirName.mid(dirName.findRev('/') + 1));
326 327 328
    if(!playlist)
        return 0;

329
    slotAddToPlaylist(dirName, playlist);
330 331 332 333

    return playlist;
}

334
void PlaylistSplitter::slotSelectPlaying()
335
{
336
    if(!m_playingItem)
337 338
	return;

339
    Playlist *l = static_cast<Playlist *>(m_playingItem->listView());
340

341 342
    if(!l)
	return;
343

344
    l->clearSelection();
345 346
    l->setSelected(m_playingItem, true);
    l->ensureItemVisible(m_playingItem);
Nadeem Hasan's avatar
Nadeem Hasan committed
347

348 349
    if(l != visiblePlaylist())
	m_playlistBox->raise(l);
350 351
}

352
void PlaylistSplitter::slotDeleteSelectedItems()
353 354 355
{
    Playlist *p = visiblePlaylist();
    if(p)
356
	p->slotRemoveSelectedItems();
357 358
}

359
void PlaylistSplitter::slotAddToPlaylist(const QString &file, Playlist *list, PlaylistItem *after)
360
{
361 362 363
    if(!after)
	after = static_cast<PlaylistItem *>(list->lastItem());

364
    KApplication::setOverrideCursor(Qt::waitCursor);
365
    addImpl(file, list, after);
366
    list->emitCountChanged();
367
    KApplication::restoreOverrideCursor();
Nadeem Hasan's avatar
Nadeem Hasan committed
368

369 370 371 372
    if(m_editor)
	m_editor->slotUpdateCollection();
}

373 374 375 376 377
void PlaylistSplitter::slotDeletePlaylist()
{
    if(m_playingItem && m_playingItem->listView() == visiblePlaylist())
	m_playingItem = 0;

378
    m_playlistBox->deleteItems();
379 380
}

381
void PlaylistSplitter::slotAddToPlaylist(const QStringList &files, Playlist *list, PlaylistItem *after)
382
{
383 384 385
    if(!after)
	after = static_cast<PlaylistItem *>(list->lastItem());

386
    KApplication::setOverrideCursor(Qt::waitCursor);
387

388
    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
389 390
        after = addImpl(*it, list, after);

391
    list->emitCountChanged();
392

393 394 395 396 397 398
    KApplication::restoreOverrideCursor();

    if(m_editor)
	m_editor->slotUpdateCollection();
}

399 400 401 402 403 404
void PlaylistSplitter::slotSetSearchVisible(bool visible)
{
    m_searchWidget->setShown(visible);
    redisplaySearch();
}

405 406 407 408 409 410 411 412 413 414 415 416 417 418
void PlaylistSplitter::slotSetHistoryVisible(bool visible)
{
    if(visible && !m_history) {
	m_history = new HistoryPlaylist(m_playlistStack);
	setupPlaylist(m_history, false, "history", true);
	return;
    }

    if(!visible && m_history) {
	m_playlistBox->deleteItem(m_history);
	m_history = 0;
    }
}

419 420
void PlaylistSplitter::slotAdvancedSearch()
{
421 422
    AdvancedSearchDialog *d =
	new AdvancedSearchDialog(uniquePlaylistName(i18n("Search Playlist")), this);
423
    AdvancedSearchDialog::Result r = d->exec();
424
    delete d;
425 426

    if(r.result == AdvancedSearchDialog::Accepted) {
427
	SearchPlaylist *p = new SearchPlaylist(m_playlistStack, r.search, r.playlistName);
428 429
	setupPlaylist(p, true, "find");
    }
430
}
431

432
void PlaylistSplitter::slotGuessTagInfo(TagGuesser::Type type)
433
{
434
    visiblePlaylist()->slotGuessTagInfo(type);
435 436 437 438
    if(m_editor)
        m_editor->slotRefresh();
}

439 440 441 442 443 444 445
void PlaylistSplitter::slotRenameFile()
{
    visiblePlaylist()->slotRenameFile();
    if(m_editor)
        m_editor->slotRefresh();
}

446 447 448 449 450 451
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void PlaylistSplitter::setupLayout()
{
452 453
    setOpaqueResize(true);

454
    m_playlistBox = new PlaylistBox(this, "playlistBox");
455

456 457 458
    connect(m_playlistBox, SIGNAL(signalCreateSearchList(const PlaylistSearch &, const QString &, const QString &)),
            this, SLOT(slotCreateSearchList(const PlaylistSearch &, const QString &, const QString &)));

459 460 461
    connect(m_playlistBox, SIGNAL(signalCreatePlaylist(const QStringList &)),
	    this, SLOT(slotCreatePlaylist(const QStringList &)));

462 463 464 465 466
    // Create a splitter to go between the playlists and the editor.

    QSplitter *editorSplitter = new QSplitter(Qt::Vertical, this, "editorSplitter");

    // Create the playlist and the editor.
467

468 469
    m_playlistStack = new QWidgetStack(editorSplitter, "playlistStack");
    m_editor = new TagEditor(editorSplitter, "tagEditor");
470 471 472

    // Make the editor as small as possible (or at least as small as recommended)

473
    editorSplitter->setResizeMode(m_editor, QSplitter::FollowSizeHint);
474

475
    // Make the connection that will update the selected playlist when a
476 477
    // selection is made in the playlist box.

478
    connect(m_playlistBox, SIGNAL(signalCurrentChanged(const PlaylistList &)),
479
	    this, SLOT(slotChangePlaylist(const PlaylistList &)));
480

481
    connect(m_playlistBox, SIGNAL(signalDoubleClicked()), this, SIGNAL(signalListBoxDoubleClicked()));
482

483
    // Create the collection list; this should always exist.  This has a
484
    // slightly different creation process than normal playlists (since it in
485
    // fact is a subclass) so it is created here rather than by using
486
    // slotCreatePlaylist().
487

488
    CollectionList::initialize(m_playlistStack, m_restore);
489
    m_collection = CollectionList::instance();
490
    setupPlaylist(m_collection, true, "folder_sound", true);
491
    connect(m_collection, SIGNAL(signalCollectionChanged()), m_editor, SLOT(slotUpdateCollection()));
492 493

    // Create the search widget -- this must be done after the CollectionList is created.
494
    m_searchWidget = new SearchWidget(editorSplitter, "searchWidget");
495
    editorSplitter->moveToFirst(m_searchWidget);
496
    connect(m_searchWidget, SIGNAL(signalQueryChanged()), this, SLOT(slotShowSearchResults()));
497 498 499
    connect(CollectionList::instance(), SIGNAL(signalVisibleColumnsChanged()),
	    this, SLOT(slotVisibleColumnsChanged()));

500
    // Show the collection on startup.
501
    m_playlistBox->setSelected(0, true);
502 503 504 505
}

void PlaylistSplitter::readConfig()
{
506 507 508 509
    KConfig *config = KGlobal::config();
    { // block for Playlists group
	KConfigGroupSaver saver(config, "Playlists");

510 511 512 513 514 515
	QValueList<int> splitterSizes = config->readIntListEntry("PlaylistSplitterSizes");
	if(splitterSizes.isEmpty()) {
	    splitterSizes.append(100);
	    splitterSizes.append(640);
	}
	setSizes(splitterSizes);
516

517
	if(m_restore) {
518

519
	    readPlaylists();
520

521
	    m_directoryList = config->readListEntry("DirectoryList");
522
	    QTimer::singleShot(0, this, SLOT(slotScanDirectories()));
523

524
	    connect(m_dirWatch, SIGNAL(dirty(const QString &)),
525
		    this, SLOT(slotDirChanged(const QString &)));
526

527 528
	    QStringList::Iterator it = m_directoryList.begin();
            for(; it != m_directoryList.end(); ++it)
529 530 531
		m_dirWatch->addDir(*it, false, true);

	    m_dirWatch->startScan();
532
	}
533 534 535

	// restore the list of hidden and shown columns

536
	if(m_collection) {
537
	    for(int i = 0; i < m_collection->columns(); i++)
538
		m_columnNames.append(m_collection->columnText(i));
539
	}
540

541
    }
542
}
543 544 545 546

void PlaylistSplitter::saveConfig()
{
    KConfig *config = KGlobal::config();
547

548
    // Save the list of open playlists.
Nadeem Hasan's avatar
Nadeem Hasan committed
549

550
    if(m_restore && m_playlistBox) {
551

552
	savePlaylists();
553

554 555
	{ // block for Playlists group
	    KConfigGroupSaver saver(config, "Playlists");
556
	    config->writeEntry("DirectoryList", m_directoryList);
557
	    config->writeEntry("SortColumn", m_collection->sortColumn());
558
	    config->writeEntry("PlaylistSplitterSizes", sizes());
559 560
	}
    }
561 562
}

563
PlaylistItem *PlaylistSplitter::addImpl(const QString &file, Playlist *list, PlaylistItem *after)
564 565 566 567 568 569
{
    processEvents();
    QFileInfo fileInfo(QDir::cleanDirPath(file));
    if(fileInfo.exists()) {
        if(fileInfo.isDir()) {
            QDir dir(fileInfo.filePath());
570
            QStringList dirContents = dir.entryList();
571 572
            for(QStringList::Iterator it = dirContents.begin(); it != dirContents.end(); ++it)
                if(*it != "." && *it != "..")
573
                    after = addImpl(fileInfo.filePath() + QDir::separator() + *it, list, after);
574 575
        }
        else {
576
            if(MediaFiles::isMediaFile(file))
577
		after = list->createItem(fileInfo, QString::null, after, false);
578
	    else if(MediaFiles::isPlaylistFile(file))
579 580
		openPlaylist(fileInfo.absFilePath());
        }
Nadeem Hasan's avatar
Nadeem Hasan committed
581
    }
582
    return after;
583 584
}

585
void PlaylistSplitter::setupPlaylist(Playlist *p, bool raise, const char *icon, bool sortedFirst)
586
{
587 588
    connect(p, SIGNAL(signalSelectionChanged(const PlaylistItemList &)),
	    m_editor, SLOT(slotSetItems(const PlaylistItemList &)));
589 590 591 592 593 594 595

    connect(p, SIGNAL(doubleClicked(QListViewItem *)),
	    this, SIGNAL(signalActivated()));

    connect(p, SIGNAL(returnPressed(QListViewItem *)), 
	    this, SIGNAL(signalActivated()));

596
    connect(p, SIGNAL(signalCountChanged(Playlist *)),
597
	    this, SLOT(slotPlaylistCountChanged(Playlist *)));
598

599
    connect(p, SIGNAL(signalAboutToRemove(PlaylistItem *)),
600
	    this, SLOT(slotPlaylistItemRemoved(PlaylistItem *)));
601

602 603
    connect(p, SIGNAL(signalFilesDropped(const QStringList &, Playlist *, PlaylistItem *)),
	    this, SLOT(slotAddToPlaylist(const QStringList &, Playlist *, PlaylistItem *)));
604

605 606
    connect(p, SIGNAL(signalSetNext(PlaylistItem *)),
	    this, SLOT(slotSetNextItem(PlaylistItem *)));
607

608 609 610
    connect(p, SIGNAL(itemRenamed(QListViewItem *)),
	    m_editor, SLOT(slotRefresh()));

611 612 613
    connect(p, SIGNAL(signalCreatePlaylist(const PlaylistItemList &)),
	    this, SLOT(slotCreatePlaylist(const PlaylistItemList &)));

614
    if(icon)
615
	m_playlistBox->createItem(p, icon, raise, sortedFirst);
616

617 618 619 620 621
    if(raise) {
	PlaylistList l;
	l.append(p);
	slotChangePlaylist(l);
    }
622 623
}

624 625 626
Playlist *PlaylistSplitter::openPlaylist(const QString &file)
{
    QFileInfo fileInfo(file);
627 628 629
    if(!fileInfo.exists() ||
       !fileInfo.isFile() ||
       !fileInfo.isReadable() ||
630 631
       m_playlistFiles.insert(fileInfo.absFilePath()))
    {
632
	return 0;
633
    }
634

635
    Playlist *p = new Playlist(file, m_playlistStack, fileInfo.baseName(true));
636
    setupPlaylist(p);
637
    return p;
638 639
}

640 641 642 643 644 645 646
QString PlaylistSplitter::play(PlaylistItem *item)
{
    stop();

    if(!item)
	return QString::null;

647 648
    Playlist *p = static_cast<Playlist *>(item->listView());

649
    if(!p)
650 651
	return QString::null;

652 653
    p->setPlaying(item, true);

654 655
    m_playingItem = item;

656
    if(m_history && p != m_history) {
657 658 659 660 661
	PlaylistItemList l;
	l.append(item);
	m_history->createItems(l);
    }

662 663 664
    return item->absFilePath();
}

665 666 667 668 669 670 671 672 673 674
void PlaylistSplitter::redisplaySearch()
{
    if(!m_searchWidget->isVisible() || visiblePlaylist()->search().isEmpty())
	visiblePlaylist()->setItemsVisible(visiblePlaylist()->items(), true);
    else {
	Playlist::setItemsVisible(visiblePlaylist()->search().matchedItems(), true);
	Playlist::setItemsVisible(visiblePlaylist()->search().unmatchedItems(), false);
    }
}

675 676 677 678 679 680
void PlaylistSplitter::readPlaylists()
{
    QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";

    QFile f(playlistsFile);

681 682 683
    if(!f.open(IO_ReadOnly))
	return;

684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
    QDataStream fs(&f);

    Q_INT32 version;
    fs >> version;

    switch(version) {
    case 1:
    {
	// Our checksum is only for the values after the version and checksum so
	// we want to get a byte array with just the checksummed data.

	QByteArray data;
	Q_UINT16 checksum;
	fs >> checksum >> data;

	if(checksum != qChecksum(data.data(), data.size()))
	    return;
701

702
	// Create a new stream just based on the data.
703

704
	QDataStream s(data, IO_ReadOnly);
705

706
	while(!s.atEnd()) {
707

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
	    Q_INT32 playlistType;
	    s >> playlistType;

	    Playlist *p;

	    switch(playlistType) {
	    case Search:
		p = new SearchPlaylist(m_playlistStack);
		break;
	    case History:
		p = new HistoryPlaylist(m_playlistStack);
		break;
	    default:
		p = new Playlist(m_playlistStack);
		s >> *p;

		if(!p->fileName().isEmpty() && m_playlistFiles.insert(p->fileName())) {
		    delete p;
		    p = 0;
		}
		break;
	    }

	    if(p)
		setupPlaylist(p);
	}
	break;
735
    }
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
    default:
    {
	// Because the original version of the playlist cache did not contain a
	// version number, we want to revert to the beginning of the file before
	// reading the data.

	f.reset();

	while(!fs.atEnd()) {
	    Playlist *p = new Playlist(m_playlistStack);
	    fs >> *p;

	    // check to see if we've alredy loaded this item before continuing

	    if(p->fileName().isEmpty() || !m_playlistFiles.insert(p->fileName()))
		setupPlaylist(p);
	    else
		delete p;
	}
	break;
    }
    }

    f.close();
760 761 762 763 764 765 766
}

void PlaylistSplitter::savePlaylists()
{
    QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";
    QFile f(playlistsFile);

767 768
    if(!f.open(IO_WriteOnly))
	return;
769

770 771
    QByteArray data;
    QDataStream s(data, IO_WriteOnly);
772

773
    PlaylistList l = m_playlistBox->playlists();
774

775
    for(PlaylistList::Iterator it = l.begin(); it != l.end(); it++) {
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
	if(*it) {

	    // These first two aren't implemented yet.

	    if(*it == m_history) {
		// s << Q_INT32(History);
	    }
	    else if(dynamic_cast<SearchPlaylist *>(*it)) {
		// s << Q_INT32(Search);
	    }
	    else {
		s << Q_INT32(Normal);
		s << *(*it);
	    }
	}
791
    }
792

793 794 795 796 797
    QDataStream fs(&f);
    fs << Q_INT32(playlistCacheVersion);
    fs << qChecksum(data.data(), data.size());

    fs << data;
798
    f.close();
799 800
}

801 802 803 804
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

805
void PlaylistSplitter::slotChangePlaylist(const PlaylistList &l)
806
{
807 808
    if(l.isEmpty()) {
	emit signalPlaylistChanged();
809
	return;
810
    }
811

812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
    // Save the current dynamic list so that we can delete it when we're done
    // showing the next list.  The two situations are that we're either showing
    // an existing, non-dynamic list or that we're creating a dynamic list; in 
    // both cases we want to get rid of the current one.
    //
    // If in fact the currently visible list *is not* a dynamic list, then
    // m_dyanmicList will simply be zero, making deleting it at the end of this
    // method just a no-op.
    //
    // And finally, because we will end up doing a recursive call to this method
    // to show the dynamic playlist (second case calls the first case), we want
    // to make sure that in that case we don't delete the very thing we're
    // being asked to show.  (Hence the conditional assignment.)

    Playlist *current = l.first() != m_dynamicList ? m_dynamicList : 0;
827

828
    m_nextPlaylistItem = 0;
829 830 831

    // First case:  We're just showing one, currently existing list.

832 833 834
    if(l.count() == 1) {
	m_playlistStack->raiseWidget(l.first());
	m_editor->slotSetItems(playlistSelection());
835 836 837

	if(m_dynamicList != l.first())
	   m_dynamicList = 0;
838 839 840 841 842

	if(m_searchWidget) {
	    m_searchWidget->setSearch(l.first()->search());
	    redisplaySearch();
	}
843
    }
844 845 846 847

    // Second case: There are multiple playlists in our list, so we need to create
    // a new "dynamic list" that is the union of these playlists.

848
    else {
849
	m_dynamicList = new DynamicPlaylist(l, m_playlistStack, i18n("Dynamic List"));
850 851 852 853 854

	// Note that this call will end up making a recursive call to this
	// method, but in that call since there will only be one list, it will
	// take the "first case" above.
	
855 856 857
	setupPlaylist(m_dynamicList, true, 0);
    }

858 859 860 861
    if(current) {
	m_playingItem = 0;
	delete current;
    }
862 863

    emit signalPlaylistChanged();
864 865
}

866
void PlaylistSplitter::slotPlaylistCountChanged(Playlist *p)
867
{
868 869
    if(p && p == m_playlistStack->visibleWidget())
	emit signalSelectedPlaylistCountChanged(p->childCount());
870 871
}

872
void PlaylistSplitter::slotPlaylistItemRemoved(PlaylistItem *item)
873
{