playlistsplitter.cpp 14.4 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
#include <kstandarddirs.h>
22 23
#include <kdebug.h>

24 25
#include <qinputdialog.h>

26
#include "playlistitem.h"
27
#include "playlistsplitter.h"
28
#include "collectionlist.h"
29
#include "directorylist.h"
30 31
#include "playlist.h"

32 33

////////////////////////////////////////////////////////////////////////////////
34
// helper functions
35 36
////////////////////////////////////////////////////////////////////////////////

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

45
////////////////////////////////////////////////////////////////////////////////
46
// public methods
47 48
////////////////////////////////////////////////////////////////////////////////

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

    setupLayout();
    readConfig();
58
}
59

60
PlaylistSplitter::~PlaylistSplitter()
61
{
62
    saveConfig();
63 64
}

65
QString PlaylistSplitter::uniquePlaylistName(const QString &startingWith, bool useParenthesis)
66 67
{
    if(!playlistBox)
68

69
	return QString::null;
70 71 72 73 74 75 76 77

    QStringList names = playlistBox->names();

    int playlistNumber = 1;

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

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

93
QString PlaylistSplitter::playNextFile(bool random)
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;
117
	return i->absFilePath();
118 119
    }
    else
120
	return QString::null;
121 122
}

123
QString PlaylistSplitter::playPreviousFile(bool random)
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;
133
	return i->absFilePath();
134 135
    }
    else
136
	return QString::null;
137 138
}

139
QString PlaylistSplitter::playSelectedFile()
140
{
141
    stop();
142

143
    PlaylistItemList items = playlistSelection();
144

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

156 157 158
QString PlaylistSplitter::playFirstFile()
{
    stop();
159

160 161
    Playlist *p = visiblePlaylist();
    PlaylistItem *i = static_cast<PlaylistItem *>(p->firstChild());
162

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

168
	return i->absFilePath();
169 170
    }
    else
171
	return QString::null;
172 173
}

174 175 176 177 178 179 180 181 182 183 184
void PlaylistSplitter::stop()
{
    if(playingItem) {
	playingItem->setPixmap(0, 0);
	playingItem = 0;
    }
}

QString PlaylistSplitter::playingArtist() const
{
    if(playingItem)
185
	return playingItem->text(PlaylistItem::ArtistColumn);
186
    else
187
	return QString::null;
188 189 190 191 192
}

QString PlaylistSplitter::playingTrack() const
{
    if(playingItem)
193
	return playingItem->text(PlaylistItem::TrackColumn);
194
    else
195
	return QString::null;
196 197 198 199 200
}

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

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

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

    if(editor)
	editor->updateCollection();
225 226
}

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

241
    return s;
242 243
}

244 245 246 247
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

248 249
void PlaylistSplitter::open()
{
250 251
    QStringList files = KFileDialog::getOpenFileNames(QString::null, 
						      extensionsString((mediaExtensions + listExtensions), i18n("Media Files")));
252 253 254
    open(files);
}

255 256 257 258 259 260 261
void PlaylistSplitter::openDirectory()
{ 
    DirectoryList *l = new DirectoryList(directoryList, this, "directoryList");

    directoryQueue.clear();

    connect(l, SIGNAL(directoryAdded(const QString &)), this, SLOT(queueDirectory(const QString &)));
262
    connect(l, SIGNAL(directoryRemoved(const QString &)), this, SLOT(queueDirectoryRemove(const QString &)));
263 264 265 266 267 268 269 270

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

279 280 281 282
Playlist *PlaylistSplitter::createPlaylist()
{
    bool ok;

283
    // If this text is changed, please also change it in PlaylistBox::duplicate().
284 285 286 287

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

Playlist *PlaylistSplitter::createPlaylist(const QString &name)
{
295
    Playlist *p = new Playlist(this, playlistStack, name.latin1());
296
    setupPlaylist(p, true);
297
    return p;
298 299
}

300

301
void PlaylistSplitter::selectPlaying()
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 327 328 329 330
    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();
	}    
    }
331 332
}

333 334 335 336 337 338 339 340 341 342 343 344
void PlaylistSplitter::removeSelectedItems()
{
    PlaylistItemList items = playlistSelection();

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

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

346 347 348
    Playlist *p = visiblePlaylist();
    if(p)
	p->clearItems(items); 
349 350
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
////////////////////////////////////////////////////////////////////////////////
// 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.

375 376 377
    connect(playlistBox, SIGNAL(currentChanged(PlaylistBoxItem *)), 
	    this, SLOT(changePlaylist(PlaylistBoxItem *)));

378
    connect(playlistBox, SIGNAL(doubleClicked()), this, SIGNAL(listBoxDoubleClicked()));
379 380 381 382 383 384

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

385
    CollectionList::initialize(this, playlistStack, restore);
386
    collection = CollectionList::instance();
387
    setupPlaylist(collection, true, "folder_sound");
388 389

    // Show the collection on startup.
390
    playlistBox->setSelected(0, true);
391 392 393 394
}

void PlaylistSplitter::readConfig()
{
395 396 397 398
    KConfig *config = KGlobal::config();
    { // block for Playlists group
	KConfigGroupSaver saver(config, "Playlists");

399 400
	directoryList = config->readListEntry("DirectoryList");

401
	if(restore) {
402 403 404 405

	    QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";

	    QFile f(playlistsFile);
406
	    
407 408 409 410 411 412 413 414 415 416 417 418 419
	    if(f.open(IO_ReadOnly)) {
		QDataStream s(&f);
		while(!s.atEnd()) {
		    Playlist *p = new Playlist(this, playlistStack);
		    s >> *p;

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

		    if(!playlistFiles.insert(p->fileName()))
			setupPlaylist(p);
		    else
			delete p;
		}
420
	    }
421 422

	    open(directoryList);
423
	}
424 425 426 427 428 429 430
    }
}	


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

432 433
    // Save the list of open playlists.
    
434
    if(restore && playlistBox) {
435 436 437

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

438 439 440 441 442 443 444 445 446 447 448 449
	QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";
	QFile f(playlistsFile);
	
	if(f.open(IO_WriteOnly)) {

	    QDataStream s(&f);

	    for(uint i = 1; i < playlistBox->count(); i++) {
		PlaylistBoxItem *item = static_cast<PlaylistBoxItem *>(playlistBox->item(i));
		if(item && item->playlist()) {
		    Playlist *p = item->playlist();
		    s << *p;
450
		}
451 452 453 454
	    }

	    f.close();

455 456 457
	}
	{ // block for Playlists group
	    KConfigGroupSaver saver(config, "Playlists");
458
	    config->writeEntry("DirectoryList", directoryList);
459 460
	}
    }
461 462
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
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());
        }
    }    
}

485
void PlaylistSplitter::setupPlaylist(Playlist *p, bool raise, const char *icon)
486 487 488 489 490 491 492 493 494
{
    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 *)));
495
    connect(p, SIGNAL(aboutToRemove(PlaylistItem *)), this, SLOT(playlistItemRemoved(PlaylistItem *)));
496 497 498 499 500 501

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

504 505 506 507
Playlist *PlaylistSplitter::openPlaylist(const QString &file)
{
    QFileInfo fileInfo(file);
    if(!fileInfo.exists() || !fileInfo.isFile() || !fileInfo.isReadable() || playlistFiles.insert(fileInfo.absFilePath()))
508
	return 0;
509 510 511

    Playlist *p = new Playlist(this, file, playlistStack, fileInfo.baseName(true).latin1());
    setupPlaylist(p);
512
    return p;
513 514
}

515 516 517 518 519 520 521 522 523
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

void PlaylistSplitter::changePlaylist(PlaylistBoxItem *item)
{
    if(item && item->playlist()) {
	playlistStack->raiseWidget(item->playlist());
	editor->setItems(playlistSelection());
524
	emit(playlistChanged());
525 526 527
    }
}

528 529 530 531 532 533
void PlaylistSplitter::playlistCountChanged(Playlist *p)
{
    if(p && p == playlistStack->visibleWidget())
	emit(selectedPlaylistCountChanged(p->childCount()));
}

534 535 536 537 538 539
void PlaylistSplitter::playlistItemRemoved(PlaylistItem *item)
{
    if(item == playingItem)
	playingItem = 0;
}

540
#include "playlistsplitter.moc"