playlistsplitter.cpp 15.9 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 <klocale.h>
19
#include <kiconloader.h>
20
#include <kapplication.h>
21 22
#include <kdebug.h>

23 24
#include <qinputdialog.h>

25
#include "playlistsplitter.h"
26
#include "collectionlist.h"
27
#include "directorylist.h"
28 29

////////////////////////////////////////////////////////////////////////////////
30
// helper functions
31 32
////////////////////////////////////////////////////////////////////////////////

33
void processEvents()
34
{
35 36 37
    static int processed = 0;
    if(processed == 0)
        kapp->processEvents();
38
    processed = (processed + 1) % 5;
39 40
}

41
////////////////////////////////////////////////////////////////////////////////
42
// public methods
43 44
////////////////////////////////////////////////////////////////////////////////

45
PlaylistSplitter::PlaylistSplitter(QWidget *parent, bool restoreOnLoad, const char *name) : QSplitter(Qt::Horizontal, parent, name), 
46
											    playingItem(0), restore(restoreOnLoad)
47
{
48 49 50
    mediaExtensions.append("mp3");
    mediaExtensions.append("ogg");
    listExtensions.append("m3u");
51 52 53

    setupLayout();
    readConfig();
54
}
55

56
PlaylistSplitter::~PlaylistSplitter()
57
{
58
    saveConfig();
59 60
}

61
QString PlaylistSplitter::uniquePlaylistName(const QString &startingWith, bool useParenthesis)
62 63
{
    if(!playlistBox)
64

65 66 67 68 69 70 71 72 73
	return(QString::null);

    QStringList names = playlistBox->names();

    int playlistNumber = 1;

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

74 75 76 77 78 79 80 81 82 83 84 85 86
    if(useParenthesis) {
	while(names.contains(startingWith + " (" + QString::number(playlistNumber) + ")") != 0)
	    playlistNumber++;
	
	return(startingWith + " (" + QString::number(playlistNumber) + ")");	
    }
    else
    {
	while(names.contains(startingWith + ' ' + QString::number(playlistNumber)) != 0)
	    playlistNumber++;
	
	return(startingWith + " " + QString::number(playlistNumber));
    }
87 88
}

89
QString PlaylistSplitter::playNextFile(bool random)
90
{
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
    Playlist *p;
    PlaylistItem *i;

    if(playingItem) {
	playingItem->setPixmap(0, 0);

	p = static_cast<Playlist *>(playingItem->listView());
	i = p->nextItem(playingItem, random);
    }
    else {
	PlaylistItemList items = playlistSelection();
	if(!items.isEmpty())
	    i = items.first();
	else {
	    p = visiblePlaylist();
	    i = static_cast<PlaylistItem *>(p->firstChild());
	}
    }

    if(i) {
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	playingItem = i;
	return(i->absFilePath());
    }
    else
	return(QString::null);
117 118
}

119
QString PlaylistSplitter::playPreviousFile(bool random)
120
{
121 122 123 124 125 126 127 128 129 130 131 132
    if(playingItem) {
	Playlist *p = static_cast<Playlist *>(playingItem->listView());
	PlaylistItem *i = p->previousItem(playingItem, random);

	playingItem->setPixmap(0, 0);
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	
	playingItem = i;
	return(i->absFilePath());
    }
    else
	return(QString::null);
133 134
}

135
QString PlaylistSplitter::playSelectedFile()
136
{
137
    stop();
138

139
    PlaylistItemList items = playlistSelection();
140

141 142 143 144 145 146 147 148 149 150
    if(!items.isEmpty()) {
	PlaylistItem *i = items.first();
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	
	playingItem = i;
	return(i->absFilePath());
    }
    else
	return(QString::null);
}
151

152 153 154
QString PlaylistSplitter::playFirstFile()
{
    stop();
155

156 157
    Playlist *p = visiblePlaylist();
    PlaylistItem *i = static_cast<PlaylistItem *>(p->firstChild());
158

159 160 161 162 163 164 165 166 167
    if(i) {
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	playingItem = i;

	return(i->absFilePath());
    }
    else
	return(QString::null);
168 169
}

170
void PlaylistSplitter::add(const QString &file, Playlist *list)
171
{
172 173 174 175 176 177
    KApplication::setOverrideCursor(Qt::waitCursor);
    addImpl(file, list);
    KApplication::restoreOverrideCursor();
    
    if(editor)
	editor->updateCollection();
178 179
}

180
void PlaylistSplitter::add(const QStringList &files, Playlist *list)
181
{
182 183 184 185 186 187 188
    KApplication::setOverrideCursor(Qt::waitCursor);
    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
        addImpl(*it, list);
    KApplication::restoreOverrideCursor();

    if(editor)
	editor->updateCollection();
189 190
}

191
QString PlaylistSplitter::extensionsString(const QStringList &extensions, const QString &type) // static
192
{
193 194 195 196 197 198 199 200 201 202 203 204 205
    QStringList l;

    for(QStringList::ConstIterator it = extensions.begin(); it != extensions.end(); ++it)
	l.append(QString("*." + (*it)));

    // i.e. "*.m3u, *.mp3|Media Files"

    QString s = l.join(" ");

    if(type != QString::null)
	s += "|" + type + " (" + l.join(", ") + ")";

    return(s);
206 207
}

208 209
/*
void PlaylistSplitter::setSelected(PlaylistItem *i) // static
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
{
    // Hu hu!  See how much crap I can pack into just one pointer to a 
    // PlaylistItem!  Sensitive viewers may want to close their eyes for the
    // next few lines.
    
    if(i) {
	
	// Get the playlist associated with the playing item and make set the
	// playing item to be both selected and visible.
	
	Playlist *l = dynamic_cast<Playlist *>(i->listView());
	
	if(l) {
	    l->clearSelection();
	    l->setSelected(i, true);
	    l->ensureItemVisible(i);
	    
	    // Now move on to the PlaylistBox.  The Playlist knows which
	    // PlaylistBoxItem that it is associated with, so we'll just get
	    // that and then figure out the PlaylistBox from there.
	    // 
	    // Once we have that we can set the appropriate Playlist to be
	    // visible.
	    
	    if(l->playlistBoxItem() && l->playlistBoxItem()->listBox()) {
		QListBox *b = l->playlistBoxItem()->listBox();
		
		b->clearSelection();
		b->setSelected(l->playlistBoxItem(), true);

		b->setCurrentItem(l->playlistBoxItem());
		b->ensureCurrentVisible();
	    }
	}
    }    
}
246
*/
247

248 249 250 251
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

252 253
void PlaylistSplitter::open()
{
254 255
    QStringList files = KFileDialog::getOpenFileNames(QString::null, 
						      extensionsString((mediaExtensions + listExtensions), i18n("Media Files")));
256 257 258
    open(files);
}

259 260 261 262 263 264 265
void PlaylistSplitter::openDirectory()
{ 
    DirectoryList *l = new DirectoryList(directoryList, this, "directoryList");

    directoryQueue.clear();

    connect(l, SIGNAL(directoryAdded(const QString &)), this, SLOT(queueDirectory(const QString &)));
266
    connect(l, SIGNAL(directoryRemoved(const QString &)), this, SLOT(queueDirectoryRemove(const QString &)));
267 268 269 270 271 272 273 274

    if(l->exec() == QDialog::Accepted) {
	open(directoryQueue);
	directoryList += directoryQueue;
	for(QStringList::Iterator it = directoryQueueRemove.begin(); it !=  directoryQueueRemove.end(); it++)
	    directoryList.remove(*it);
    }
}
275 276 277 278 279 280 281 282
void PlaylistSplitter::setEditorVisible(bool visible)
{
    if(visible)
	editor->show();
    else
	editor->hide();
}

283 284 285 286
Playlist *PlaylistSplitter::createPlaylist()
{
    bool ok;

287
    // If this text is changed, please also change it in PlaylistBox::duplicate().
288 289 290 291 292 293 294 295 296 297 298

    QString name = QInputDialog::getText(i18n("New Playlist..."), i18n("Please enter a name for the new playlist:"),
					 QLineEdit::Normal, uniquePlaylistName(), &ok);
    if(ok)
	return(createPlaylist(name));
    else
	return(0);
}

Playlist *PlaylistSplitter::createPlaylist(const QString &name)
{
299
    Playlist *p = new Playlist(this, playlistStack, name.latin1());
300
    setupPlaylist(p, true);
301 302 303 304 305 306 307 308
    return(p);
}

void PlaylistSplitter::openPlaylist()
{
    QStringList files = KFileDialog::getOpenFileNames(QString::null, "*.m3u");

    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
309
	openPlaylist(*it);
310 311 312 313 314
}

Playlist *PlaylistSplitter::openPlaylist(const QString &playlistFile)
{
    QFileInfo file(playlistFile);
315
    if(!file.exists() || !file.isFile() || !file.isReadable() || playlistFiles.insert(file.absFilePath()))
316
	return(0);
317

318
    Playlist *p = new Playlist(this, playlistFile, playlistStack, file.baseName(true).latin1());
319
    setupPlaylist(p);
320
    return(p);
321 322
}

323
void PlaylistSplitter::selectPlaying()
324
{
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    if(!playingItem)
	return;

    Playlist *l = static_cast<Playlist *>(playingItem->listView());
	
    if(l) {

	l->clearSelection();
	l->setSelected(playingItem, true);
	l->ensureItemVisible(playingItem);
	
	// Now move on to the PlaylistBox.  The Playlist knows which
	// PlaylistBoxItem that it is associated with, so we'll just get
	// that and then figure out the PlaylistBox from there.
	// 
	// Once we have that we can set the appropriate Playlist to be
	// visible.
	
	if(l->playlistBoxItem() && l->playlistBoxItem()->listBox()) {
	    QListBox *b = l->playlistBoxItem()->listBox();
	    
	    b->clearSelection();
	    b->setSelected(l->playlistBoxItem(), true);
	    
	    b->setCurrentItem(l->playlistBoxItem());
	    b->ensureCurrentVisible();
	}    
    }
353 354
}

355
QString PlaylistSplitter::playingArtist() const
356
{
357 358 359 360 361
    if(playingItem)
	return(playingItem->text(PlaylistItem::ArtistColumn));
    else
	return(QString::null);
}
362

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
QString PlaylistSplitter::playingTrack() const
{
    if(playingItem)
	return(playingItem->text(PlaylistItem::TrackColumn));
    else
	return(QString::null);
}

QString PlaylistSplitter::playingList() const
{
    if(playingItem)
	return(static_cast<Playlist *>(playingItem->listView())->name());
    else
	return(QString::null);
}

void PlaylistSplitter::stop()
{
    if(playingItem) {
	playingItem->setPixmap(0, 0);
	playingItem = 0;
    }
}


void PlaylistSplitter::removeSelectedItems()
{
    PlaylistItemList items = playlistSelection();

    checkPlayingItemBeforeRemove(items);

    Playlist *p = visiblePlaylist();
    if(p)
	p->remove(items);
}

void PlaylistSplitter::clearSelectedItems()
{
    PlaylistItemList items = playlistSelection();
    checkPlayingItemBeforeRemove(items);
 
    Playlist *p = visiblePlaylist();
    if(p)
	p->clearItems(items); 
407 408
}

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void PlaylistSplitter::setupLayout()
{
    playlistBox = new PlaylistBox(this, "playlistBox");

    // 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.

    playlistStack = new QWidgetStack(editorSplitter, "playlistStack");
    editor = new TagEditor(editorSplitter, "tagEditor");

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

    editorSplitter->setResizeMode(editor, QSplitter::FollowSizeHint);

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

433 434 435
    connect(playlistBox, SIGNAL(currentChanged(PlaylistBoxItem *)), 
	    this, SLOT(changePlaylist(PlaylistBoxItem *)));

436
    connect(playlistBox, SIGNAL(doubleClicked()), this, SIGNAL(listBoxDoubleClicked()));
437 438 439 440 441 442

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

443
    CollectionList::initialize(this, playlistStack, restore);
444
    collection = CollectionList::instance();
445
    setupPlaylist(collection, true, "folder_sound");
446 447

    // Show the collection on startup.
448
    playlistBox->setSelected(0, true);
449 450 451 452
}

void PlaylistSplitter::readConfig()
{
453 454 455 456
    KConfig *config = KGlobal::config();
    { // block for Playlists group
	KConfigGroupSaver saver(config, "Playlists");

457 458
	directoryList = config->readListEntry("DirectoryList");

459 460 461 462 463 464 465 466 467 468 469
	if(restore) {
	    QStringList external = config->readListEntry("ExternalPlaylists");
	    for(QStringList::Iterator it = external.begin(); it != external.end(); ++it)
		openPlaylist(*it);
	    
	    QStringList internal = config->readListEntry("InternalPlaylists");
	    for(QStringList::Iterator it = internal.begin(); it != internal.end(); ++it) {
		Playlist *p = openPlaylist(*it);
		if(p)
		    p->setInternal(true);
	    }
470 471

	    open(directoryList);
472
	}
473 474 475 476 477 478 479
    }
}	


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

481 482
    // Save the list of open playlists.
    
483
    if(restore && playlistBox) {
484 485
	QStringList internalPlaylists;
	QStringList externalPlaylists;
486 487 488 489

	// Start at item 1.  We want to skip the collection list.

	for(uint i = 1; i < playlistBox->count(); i++) {
490 491 492 493
	    PlaylistBoxItem *item = static_cast<PlaylistBoxItem *>(playlistBox->item(i));
	    if(item && item->playlist()) {
		Playlist *p = item->playlist();
		if(p->isInternalFile()) {
494 495
		    p->save(true);
		    internalPlaylists.append(p->fileName());
496 497 498 499 500 501 502
		}
		else
		    externalPlaylists.append(p->fileName());
	    }		
	}
	{ // block for Playlists group
	    KConfigGroupSaver saver(config, "Playlists");
503 504
	    config->writeEntry("InternalPlaylists", internalPlaylists);
	    config->writeEntry("ExternalPlaylists", externalPlaylists);
505
	    config->writeEntry("DirectoryList", directoryList);
506 507
	}
    }
508 509
}

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
void PlaylistSplitter::addImpl(const QString &file, Playlist *list)
{
    processEvents();
    QFileInfo fileInfo(QDir::cleanDirPath(file));
    if(fileInfo.exists()) {
        if(fileInfo.isDir()) {
            QDir dir(fileInfo.filePath());
            QStringList dirContents=dir.entryList();
            for(QStringList::Iterator it = dirContents.begin(); it != dirContents.end(); ++it)
                if(*it != "." && *it != "..")
                    addImpl(fileInfo.filePath() + QDir::separator() + *it, list);
        }
        else {
            QString extension = fileInfo.extension(false);
            if(mediaExtensions.contains(extension) > 0)
		list->createItem(fileInfo);
	    else if(listExtensions.contains(extension) > 0)
		openPlaylist(fileInfo.absFilePath());
        }
    }    
}

532
void PlaylistSplitter::setupPlaylist(Playlist *p, bool raise, const char *icon)
533 534 535 536 537 538 539 540 541
{
    PlaylistBoxItem *i = new PlaylistBoxItem(playlistBox, SmallIcon(icon, 32), p->name(), p);
    p->setPlaylistBoxItem(i);
    playlistBox->sort();

    connect(p, SIGNAL(selectionChanged(const PlaylistItemList &)), editor, SLOT(setItems(const PlaylistItemList &)));
    connect(p, SIGNAL(doubleClicked()), this, SIGNAL(doubleClicked()));
    connect(p, SIGNAL(collectionChanged()), editor, SLOT(updateCollection()));
    connect(p, SIGNAL(numberOfItemsChanged(Playlist *)), this, SLOT(playlistCountChanged(Playlist *)));
542 543 544 545 546 547

    if(raise) {
	playlistStack->raiseWidget(p);
	playlistBox->setCurrentItem(i);
	playlistBox->ensureCurrentVisible();
    }
548 549 550 551 552 553 554 555 556 557 558 559
}

void PlaylistSplitter::checkPlayingItemBeforeRemove(PlaylistItemList &items)
{
    PlaylistItem *item = items.first();
    while(item) {
	if(item == playingItem)
	    playingItem = 0;
	item = items.next();
    }
}

560 561 562 563 564 565 566 567 568
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

void PlaylistSplitter::changePlaylist(PlaylistBoxItem *item)
{
    if(item && item->playlist()) {
	playlistStack->raiseWidget(item->playlist());
	editor->setItems(playlistSelection());
569
	emit(playlistChanged());
570 571 572
    }
}

573 574 575 576 577 578
void PlaylistSplitter::playlistCountChanged(Playlist *p)
{
    if(p && p == playlistStack->visibleWidget())
	emit(selectedPlaylistCountChanged(p->childCount()));
}

579
#include "playlistsplitter.moc"