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

25 26
#include <qinputdialog.h>

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

33 34
QStringList *PlaylistSplitter::m_mediaExtensions = 0;
QStringList *PlaylistSplitter::m_listExtensions = 0;
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 53
PlaylistSplitter::PlaylistSplitter(QWidget *parent, bool restore, const char *name) : QSplitter(Qt::Horizontal, parent, name), 
											    m_playingItem(0), m_restore(restore)
54
{
55 56 57 58 59 60 61 62
    if(!m_mediaExtensions && !m_listExtensions) {
	m_mediaExtensions = new QStringList();
	m_listExtensions = new QStringList();

	m_mediaExtensions->append("mp3");
	m_mediaExtensions->append("ogg");
	m_listExtensions->append("m3u");
    }
63 64 65

    setupLayout();
    readConfig();
66
}
67

68
PlaylistSplitter::~PlaylistSplitter()
69
{
70
    saveConfig();
71 72
}

73
QString PlaylistSplitter::uniquePlaylistName(const QString &startingWith, bool useParenthesis)
74
{
75
    if(!m_playlistBox)
76
	return QString::null;
77

78
    QStringList names = m_playlistBox->names();
79 80 81 82 83 84

    int playlistNumber = 1;

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

85 86 87 88
    if(useParenthesis) {
	while(names.contains(startingWith + " (" + QString::number(playlistNumber) + ")") != 0)
	    playlistNumber++;
	
89
	return startingWith + " (" + QString::number(playlistNumber) + ")";	
90 91 92 93 94 95
    }
    else
    {
	while(names.contains(startingWith + ' ' + QString::number(playlistNumber)) != 0)
	    playlistNumber++;
	
96
	return startingWith + " " + QString::number(playlistNumber);
97
    }
98 99
}

100
QString PlaylistSplitter::playNextFile(bool random)
101
{
102 103 104
    Playlist *p;
    PlaylistItem *i;

105 106
    if(m_playingItem) {
	m_playingItem->setPixmap(0, 0);
107

108 109
	p = static_cast<Playlist *>(m_playingItem->listView());
	i = p->nextItem(m_playingItem, random);
110 111 112 113 114 115 116 117 118 119 120 121 122
    }
    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")));
123
	m_playingItem = i;
124
	return i->absFilePath();
125 126
    }
    else
127
	return QString::null;
128 129
}

130
QString PlaylistSplitter::playPreviousFile(bool random)
131
{
132 133 134
    if(m_playingItem) {
	Playlist *p = static_cast<Playlist *>(m_playingItem->listView());
	PlaylistItem *i = p->previousItem(m_playingItem, random);
135

136
	m_playingItem->setPixmap(0, 0);
137 138
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	
139
	m_playingItem = i;
140
	return i->absFilePath();
141 142
    }
    else
143
	return QString::null;
144 145
}

146
QString PlaylistSplitter::playSelectedFile()
147
{
148
    stop();
149

150
    PlaylistItemList items = playlistSelection();
151

152 153 154 155
    if(!items.isEmpty()) {
	PlaylistItem *i = items.first();
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	
156
	m_playingItem = i;
157
	return i->absFilePath();
158 159
    }
    else
160
	return QString::null;
161
}
162

163 164 165
QString PlaylistSplitter::playFirstFile()
{
    stop();
166

167 168
    Playlist *p = visiblePlaylist();
    PlaylistItem *i = static_cast<PlaylistItem *>(p->firstChild());
169

170 171 172
    if(i) {
	i->setPixmap(0, QPixmap(UserIcon("playing")));
	i->setPixmap(0, QPixmap(UserIcon("playing")));
173
	m_playingItem = i;
174

175
	return i->absFilePath();
176 177
    }
    else
178
	return QString::null;
179 180
}

181 182
void PlaylistSplitter::stop()
{
183 184 185
    if(m_playingItem) {
	m_playingItem->setPixmap(0, 0);
	m_playingItem = 0;
186 187 188 189 190
    }
}

QString PlaylistSplitter::playingArtist() const
{
191 192
    if(m_playingItem)
	return m_playingItem->text(PlaylistItem::ArtistColumn);
193
    else
194
	return QString::null;
195 196 197 198
}

QString PlaylistSplitter::playingTrack() const
{
199 200
    if(m_playingItem)
	return m_playingItem->text(PlaylistItem::TrackColumn);
201
    else
202
	return QString::null;
203 204 205 206
}

QString PlaylistSplitter::playingList() const
{
207 208
    if(m_playingItem)
	return static_cast<Playlist *>(m_playingItem->listView())->name();
209
    else
210
	return QString::null;
211 212
}

213
void PlaylistSplitter::addToPlaylist(const QString &file, Playlist *list)
214
{
215 216 217 218
    KApplication::setOverrideCursor(Qt::waitCursor);
    addImpl(file, list);
    KApplication::restoreOverrideCursor();
    
219 220
    if(m_editor)
	m_editor->updateCollection();
221 222
}

223
void PlaylistSplitter::addToPlaylist(const QStringList &files, Playlist *list)
224
{
225 226 227 228 229
    KApplication::setOverrideCursor(Qt::waitCursor);
    for(QStringList::ConstIterator it = files.begin(); it != files.end(); ++it)
        addImpl(*it, list);
    KApplication::restoreOverrideCursor();

230 231
    if(m_editor)
	m_editor->updateCollection();
232 233
}

234
QString PlaylistSplitter::extensionsString(const QStringList &extensions, const QString &type) // static
235
{
236 237 238 239 240 241 242 243 244 245 246 247
    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(", ") + ")";

248
    return s;
249 250
}

251 252
void PlaylistSplitter::open(const QString &file) 
{
253 254 255
    if(file.isEmpty())
	return;

256
    if(visiblePlaylist() == m_collection || 
257 258
       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")), KGuiItem(i18n("Collection"))) == KMessageBox::No)
259
	addToPlaylist(file, m_collection);
260
    else
261
	addToPlaylist(file, visiblePlaylist());
262 263 264 265
}

void PlaylistSplitter::open(const QStringList &files) 
{
266 267 268
    if(files.isEmpty())
	return;
    
269
    if(visiblePlaylist() == m_collection || 
270 271
       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)
272
	addToPlaylist(files, m_collection);
273
    else
274 275 276 277 278 279 280 281
	addToPlaylist(files, visiblePlaylist());
}

Playlist *PlaylistSplitter::createPlaylist(const QString &name)
{
    Playlist *p = new Playlist(this, m_playlistStack, name.latin1());
    setupPlaylist(p, true);
    return p;
282 283
}

284 285 286 287
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

288
void PlaylistSplitter::slotOpen()
289
{
290
    QStringList files = KFileDialog::getOpenFileNames(QString::null, 
291
						      extensionsString((*m_mediaExtensions + *m_listExtensions), i18n("Media Files")));
292 293 294
    open(files);
}

295
void PlaylistSplitter::slotOpenDirectory()
296
{ 
297
    DirectoryList *l = new DirectoryList(m_directoryList, this, "directoryList");
298

299
    m_directoryQueue.clear();
300

301 302
    connect(l, SIGNAL(directoryAdded(const QString &)), this, SLOT(slotQueueDirectory(const QString &)));
    connect(l, SIGNAL(directoryRemoved(const QString &)), this, SLOT(slotQueueDirectoryRemove(const QString &)));
303 304

    if(l->exec() == QDialog::Accepted) {
305 306 307 308
	open(m_directoryQueue);
	m_directoryList += m_directoryQueue;
	for(QStringList::Iterator it = m_directoryQueueRemove.begin(); it !=  m_directoryQueueRemove.end(); it++)
	    m_directoryList.remove(*it);
309 310
    }
}
311
void PlaylistSplitter::slotSetEditorVisible(bool visible)
312 313
{
    if(visible)
314
	m_editor->show();
315
    else
316
	m_editor->hide();
317 318
}

319
Playlist *PlaylistSplitter::slotCreatePlaylist()
320 321 322
{
    bool ok;

323
    // If this text is changed, please also change it in PlaylistBox::duplicate().
324 325 326 327

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

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

338
    Playlist *l = static_cast<Playlist *>(m_playingItem->listView());
339
	
340 341
    if(!l)
	return;
342

343
    l->clearSelection();
344 345
    l->setSelected(m_playingItem, true);
    l->ensureItemVisible(m_playingItem);
346
    
347
    m_playlistBox->raise(l);
348 349
}

350
void PlaylistSplitter::slotDeleteSelectedItems()
351 352 353 354 355 356 357 358
{
    PlaylistItemList items = playlistSelection();

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

359
void PlaylistSplitter::slotToggleColumnVisible(int column)
360
{
361 362 363
    m_visibleColumns[column] = ! m_visibleColumns[column];
    if(visiblePlaylist())
	setupColumns(visiblePlaylist());
364 365
}

366 367 368 369 370 371
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void PlaylistSplitter::setupLayout()
{
372
    m_playlistBox = new PlaylistBox(this, "playlistBox");
373 374 375 376 377 378 379

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

380 381
    m_playlistStack = new QWidgetStack(editorSplitter, "playlistStack");
    m_editor = new TagEditor(editorSplitter, "tagEditor");
382 383 384

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

385
    editorSplitter->setResizeMode(m_editor, QSplitter::FollowSizeHint);
386 387 388 389

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

390 391
    connect(m_playlistBox, SIGNAL(signalCurrentChanged(Playlist *)), 
	    this, SLOT(slotChangePlaylist(Playlist *)));
392

393
    connect(m_playlistBox, SIGNAL(signalDoubleClicked()), this, SIGNAL(signalListBoxDoubleClicked()));
394 395 396 397

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

400 401 402
    CollectionList::initialize(this, m_playlistStack, m_restore);
    m_collection = CollectionList::instance();
    setupPlaylist(m_collection, true, "folder_sound");
403 404

    // Show the collection on startup.
405
    m_playlistBox->setSelected(0, true);
406 407 408 409
}

void PlaylistSplitter::readConfig()
{
410 411 412 413
    KConfig *config = KGlobal::config();
    { // block for Playlists group
	KConfigGroupSaver saver(config, "Playlists");

414 415 416 417 418 419 420
	QValueList<int> splitterSizes = config->readIntListEntry("PlaylistSplitterSizes");
	if(splitterSizes.isEmpty()) {
	    splitterSizes.append(100);
	    splitterSizes.append(640);
	}
	setSizes(splitterSizes);
	
421
	if(m_restore) {
422 423 424 425

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

	    QFile f(playlistsFile);
426
	    
427 428 429
	    if(f.open(IO_ReadOnly)) {
		QDataStream s(&f);
		while(!s.atEnd()) {
430
		    Playlist *p = new Playlist(this, m_playlistStack);
431 432 433 434
		    s >> *p;

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

435
		    if(p->fileName().isEmpty() || !m_playlistFiles.insert(p->fileName()))
436 437 438 439
			setupPlaylist(p);
		    else
			delete p;
		}
440
	    }
441

442 443
	    m_directoryList = config->readListEntry("DirectoryList");
	    open(m_directoryList);
444
	}
445 446 447

	// restore the list of hidden and shown columns

448
	if(m_collection) {
449
	    // the last column is just a filler
450
	    m_visibleColumns.resize(m_collection->columns() - 1, true);
451 452 453 454 455
	    QValueList<int> l = config->readIntListEntry("VisibleColumns");

	    uint i = 0;
	    for(QValueList<int>::Iterator it = l.begin(); it != l.end(); ++it) {
		if(! bool(*it)) {
456
		    m_visibleColumns[i] = bool(*it);
457
		    m_collection->hideColumn(i);
458
		}
459

460
		// while we're looping go ahead and populate m_columnNames
461
		
462
		m_columnNames.append(m_collection->columnText(i));
463

464 465
		i++;
	    }
466
	    setupColumns(m_collection);
467
	}
468 469 470 471 472 473 474
    }
}	


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

476 477
    // Save the list of open playlists.
    
478
    if(m_restore && m_playlistBox) {
479 480 481

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

482 483 484 485 486 487 488
	QString playlistsFile = KGlobal::dirs()->saveLocation("appdata") + "playlists";
	QFile f(playlistsFile);
	
	if(f.open(IO_WriteOnly)) {

	    QDataStream s(&f);

489
	    QPtrList<Playlist> l = m_playlistBox->playlists();
490

491 492
	    for(Playlist *p = l.first(); p; p = l.next())
		s << *p;
493

494
	    f.close();
495 496 497
	}
	{ // block for Playlists group
	    KConfigGroupSaver saver(config, "Playlists");
498
	    config->writeEntry("DirectoryList", m_directoryList);
499 500

	    QValueList<int> l;
501 502
	    for(uint i = 0; i < m_visibleColumns.size(); i++)
		l.append(int(m_visibleColumns[i]));
503 504
	    
	    config->writeEntry("VisibleColumns", l);
505 506

	    config->writeEntry("PlaylistSplitterSizes", sizes());
507 508
	}
    }
509 510
}

511 512 513 514 515 516 517 518 519 520 521 522 523 524
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);
525
            if(m_mediaExtensions->contains(extension) > 0)
526
		list->createItem(fileInfo);
527
	    else if(m_listExtensions->contains(extension) > 0)
528 529 530 531 532
		openPlaylist(fileInfo.absFilePath());
        }
    }    
}

533
void PlaylistSplitter::setupPlaylist(Playlist *p, bool raise, const char *icon)
534
{
535 536 537 538 539
    connect(p, SIGNAL(selectionChanged(const PlaylistItemList &)), m_editor, SLOT(setItems(const PlaylistItemList &)));
    connect(p, SIGNAL(doubleClicked()), this, SIGNAL(signalDoubleClicked()));
    connect(p, SIGNAL(collectionChanged()), m_editor, SLOT(updateCollection()));
    connect(p, SIGNAL(numberOfItemsChanged(Playlist *)), this, SLOT(slotPlaylistCountChanged(Playlist *)));
    connect(p, SIGNAL(aboutToRemove(PlaylistItem *)), this, SLOT(slotPlaylistItemRemoved(PlaylistItem *)));
540

541 542
    connect(p, SIGNAL(signalToggleColumnVisible(int)), this, SLOT(slotToggleColumnVisible(int)));

543
    m_playlistBox->createItem(p, icon, raise);
544

545
    if(raise) {
546
	m_playlistStack->raiseWidget(p);
547
	setupColumns(p);
548
    }
549 550
}

551 552 553
Playlist *PlaylistSplitter::openPlaylist(const QString &file)
{
    QFileInfo fileInfo(file);
554
    if(!fileInfo.exists() || !fileInfo.isFile() || !fileInfo.isReadable() || m_playlistFiles.insert(fileInfo.absFilePath()))
555
	return 0;
556

557
    Playlist *p = new Playlist(this, file, m_playlistStack, fileInfo.baseName(true).latin1());
558
    setupPlaylist(p);
559
    return p;
560 561
}

562 563 564 565 566
void PlaylistSplitter::setupColumns(Playlist *p)
{
    if(!p)
	return;
    
567 568
    for(uint i = 0; i < m_visibleColumns.size(); i++) {
	if(m_visibleColumns[i] && ! p->isColumnVisible(i))
569
	    p->showColumn(i);
570
	else if(! m_visibleColumns[i] && p->isColumnVisible(i))
571 572 573 574
	    p->hideColumn(i);
    }
}

575 576 577 578
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////

579
void PlaylistSplitter::slotChangePlaylist(Playlist *p)
580
{
581 582 583
    if(!p)
	return;
    
584 585
    m_playlistStack->raiseWidget(p);
    m_editor->setItems(playlistSelection());
586
    setupColumns(p);
587
    emit signalPlaylistChanged();
588 589
}

590
void PlaylistSplitter::slotPlaylistCountChanged(Playlist *p)
591
{
592 593
    if(p && p == m_playlistStack->visibleWidget())
	emit signalSelectedPlaylistCountChanged(p->childCount());
594 595
}

596
void PlaylistSplitter::slotPlaylistItemRemoved(PlaylistItem *item)
597
{
598 599
    if(item == m_playingItem)
	m_playingItem = 0;
600 601
}

602
#include "playlistsplitter.moc"