playlistsplitter.cpp 14.5 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
	return QString::null;
66 67 68 69 70 71 72 73

    QStringList names = playlistBox->names();

    int playlistNumber = 1;

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

74 75 76 77
    if(useParenthesis) {
	while(names.contains(startingWith + " (" + QString::number(playlistNumber) + ")") != 0)
	    playlistNumber++;
	
78
	return startingWith + " (" + QString::number(playlistNumber) + ")";	
79 80 81 82 83 84
    }
    else
    {
	while(names.contains(startingWith + ' ' + QString::number(playlistNumber)) != 0)
	    playlistNumber++;
	
85
	return startingWith + " " + QString::number(playlistNumber);
86
    }
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
    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;
113
	return i->absFilePath();
114 115
    }
    else
116
	return QString::null;
117 118
}

119
QString PlaylistSplitter::playPreviousFile(bool random)
120
{
121 122 123 124 125 126 127 128
    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;
129
	return i->absFilePath();
130 131
    }
    else
132
	return QString::null;
133 134
}

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

139
    PlaylistItemList items = playlistSelection();
140

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

164
	return i->absFilePath();
165 166
    }
    else
167
	return QString::null;
168 169
}

170 171 172 173 174 175 176 177 178 179 180
void PlaylistSplitter::stop()
{
    if(playingItem) {
	playingItem->setPixmap(0, 0);
	playingItem = 0;
    }
}

QString PlaylistSplitter::playingArtist() const
{
    if(playingItem)
181
	return playingItem->text(PlaylistItem::ArtistColumn);
182
    else
183
	return QString::null;
184 185 186 187 188
}

QString PlaylistSplitter::playingTrack() const
{
    if(playingItem)
189
	return playingItem->text(PlaylistItem::TrackColumn);
190
    else
191
	return QString::null;
192 193 194 195 196
}

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

202
void PlaylistSplitter::add(const QString &file, Playlist *list)
203
{
204 205 206 207 208 209
    KApplication::setOverrideCursor(Qt::waitCursor);
    addImpl(file, list);
    KApplication::restoreOverrideCursor();
    
    if(editor)
	editor->updateCollection();
210 211
}

212
void PlaylistSplitter::add(const QStringList &files, Playlist *list)
213
{
214 215 216 217 218 219 220
    KApplication::setOverrideCursor(Qt::waitCursor);
    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
        addImpl(*it, list);
    KApplication::restoreOverrideCursor();

    if(editor)
	editor->updateCollection();
221 222
}

223
QString PlaylistSplitter::extensionsString(const QStringList &extensions, const QString &type) // static
224
{
225 226 227 228 229 230 231 232 233 234 235 236
    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(", ") + ")";

237
    return s;
238 239
}

240 241 242 243
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

244 245
void PlaylistSplitter::open()
{
246 247
    QStringList files = KFileDialog::getOpenFileNames(QString::null, 
						      extensionsString((mediaExtensions + listExtensions), i18n("Media Files")));
248 249 250
    open(files);
}

251 252 253 254 255 256 257
void PlaylistSplitter::openDirectory()
{ 
    DirectoryList *l = new DirectoryList(directoryList, this, "directoryList");

    directoryQueue.clear();

    connect(l, SIGNAL(directoryAdded(const QString &)), this, SLOT(queueDirectory(const QString &)));
258
    connect(l, SIGNAL(directoryRemoved(const QString &)), this, SLOT(queueDirectoryRemove(const QString &)));
259 260 261 262 263 264 265 266

    if(l->exec() == QDialog::Accepted) {
	open(directoryQueue);
	directoryList += directoryQueue;
	for(QStringList::Iterator it = directoryQueueRemove.begin(); it !=  directoryQueueRemove.end(); it++)
	    directoryList.remove(*it);
    }
}
267 268 269 270 271 272 273 274
void PlaylistSplitter::setEditorVisible(bool visible)
{
    if(visible)
	editor->show();
    else
	editor->hide();
}

275 276 277 278
Playlist *PlaylistSplitter::createPlaylist()
{
    bool ok;

279
    // If this text is changed, please also change it in PlaylistBox::duplicate().
280 281 282 283

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

Playlist *PlaylistSplitter::createPlaylist(const QString &name)
{
291
    Playlist *p = new Playlist(this, playlistStack, name.latin1());
292
    setupPlaylist(p, true);
293
    return p;
294 295
}

296

297
void PlaylistSplitter::selectPlaying()
298
{
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    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();
	}    
    }
327 328
}

329 330 331 332 333 334 335 336 337 338 339 340
void PlaylistSplitter::removeSelectedItems()
{
    PlaylistItemList items = playlistSelection();

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

void PlaylistSplitter::clearSelectedItems()
{
    PlaylistItemList items = playlistSelection();
341

342 343 344
    Playlist *p = visiblePlaylist();
    if(p)
	p->clearItems(items); 
345 346
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
////////////////////////////////////////////////////////////////////////////////
// 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.

371 372 373
    connect(playlistBox, SIGNAL(currentChanged(PlaylistBoxItem *)), 
	    this, SLOT(changePlaylist(PlaylistBoxItem *)));

374
    connect(playlistBox, SIGNAL(doubleClicked()), this, SIGNAL(listBoxDoubleClicked()));
375 376 377 378 379 380

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

381
    CollectionList::initialize(this, playlistStack, restore);
382
    collection = CollectionList::instance();
383
    setupPlaylist(collection, true, "folder_sound");
384 385

    // Show the collection on startup.
386
    playlistBox->setSelected(0, true);
387 388 389 390
}

void PlaylistSplitter::readConfig()
{
391 392 393 394
    KConfig *config = KGlobal::config();
    { // block for Playlists group
	KConfigGroupSaver saver(config, "Playlists");

395 396
	directoryList = config->readListEntry("DirectoryList");

397 398 399 400 401 402 403 404 405 406 407
	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);
	    }
408 409

	    open(directoryList);
410
	}
411 412 413 414 415 416 417
    }
}	


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

419 420
    // Save the list of open playlists.
    
421
    if(restore && playlistBox) {
422 423
	QStringList internalPlaylists;
	QStringList externalPlaylists;
424 425 426 427

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

	for(uint i = 1; i < playlistBox->count(); i++) {
428 429 430 431
	    PlaylistBoxItem *item = static_cast<PlaylistBoxItem *>(playlistBox->item(i));
	    if(item && item->playlist()) {
		Playlist *p = item->playlist();
		if(p->isInternalFile()) {
432 433
		    p->save(true);
		    internalPlaylists.append(p->fileName());
434 435 436 437 438 439 440
		}
		else
		    externalPlaylists.append(p->fileName());
	    }		
	}
	{ // block for Playlists group
	    KConfigGroupSaver saver(config, "Playlists");
441 442
	    config->writeEntry("InternalPlaylists", internalPlaylists);
	    config->writeEntry("ExternalPlaylists", externalPlaylists);
443
	    config->writeEntry("DirectoryList", directoryList);
444 445
	}
    }
446 447
}

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
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());
        }
    }    
}

470
void PlaylistSplitter::setupPlaylist(Playlist *p, bool raise, const char *icon)
471 472 473 474 475 476 477 478 479
{
    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 *)));
480
    connect(p, SIGNAL(aboutToRemove(PlaylistItem *)), this, SLOT(playlistItemRemoved(PlaylistItem *)));
481 482 483 484 485 486

    if(raise) {
	playlistStack->raiseWidget(p);
	playlistBox->setCurrentItem(i);
	playlistBox->ensureCurrentVisible();
    }
487 488
}

489 490 491 492
Playlist *PlaylistSplitter::openPlaylist(const QString &file)
{
    QFileInfo fileInfo(file);
    if(!fileInfo.exists() || !fileInfo.isFile() || !fileInfo.isReadable() || playlistFiles.insert(fileInfo.absFilePath()))
493
	return 0;
494 495 496

    Playlist *p = new Playlist(this, file, playlistStack, fileInfo.baseName(true).latin1());
    setupPlaylist(p);
497
    return p;
498 499
}

500 501 502 503 504 505 506 507 508
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

void PlaylistSplitter::changePlaylist(PlaylistBoxItem *item)
{
    if(item && item->playlist()) {
	playlistStack->raiseWidget(item->playlist());
	editor->setItems(playlistSelection());
509
	emit(playlistChanged());
510 511 512
    }
}

513 514 515 516 517 518
void PlaylistSplitter::playlistCountChanged(Playlist *p)
{
    if(p && p == playlistStack->visibleWidget())
	emit(selectedPlaylistCountChanged(p->childCount()));
}

519 520 521 522 523 524
void PlaylistSplitter::playlistItemRemoved(PlaylistItem *item)
{
    if(item == playingItem)
	playingItem = 0;
}

525
#include "playlistsplitter.moc"