playlist.cpp 39 KB
Newer Older
1 2 3 4
/***************************************************************************
                          playlist.cpp  -  description
                             -------------------
    begin                : Sat Feb 16 2002
5
    copyright            : (C) 2002, 2003 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.                                   *
 *                                                                         *
 ***************************************************************************/

Scott Wheeler's avatar
Scott Wheeler committed
18
#include <kconfig.h>
19
#include <kmessagebox.h>
20 21
#include <kurldrag.h>
#include <kiconloader.h>
22
#include <klineedit.h>
Scott Wheeler's avatar
Scott Wheeler committed
23
#include <kaction.h>
24
#include <kmainwindow.h>
Nadeem Hasan's avatar
Nadeem Hasan committed
25
#include <kpopupmenu.h>
Scott Wheeler's avatar
Scott Wheeler committed
26
#include <klocale.h>
27 28
#include <kdebug.h>

29 30
#include <qheader.h>
#include <qcursor.h>
Scott Wheeler's avatar
Scott Wheeler committed
31
#include <qdir.h>
32
#include <qeventloop.h>
33

34 35
#include <id3v1genres.h>

36
#include <stdlib.h>
37
#include <limits.h>
38
#include <time.h>
39
#include <math.h>
40

41
#include "playlist.h"
Scott Wheeler's avatar
Scott Wheeler committed
42 43
#include "playlistitem.h"
#include "playlistsearch.h"
44
#include "mediafiles.h"
45
#include "collectionlist.h"
46
#include "filerenamer.h"
47

48 49 50 51
////////////////////////////////////////////////////////////////////////////////
// Playlist::SharedSettings definition
////////////////////////////////////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
52
bool Playlist::m_visibleChanged = false;
53

54 55 56 57 58 59 60 61 62 63 64 65 66
/**
 * Shared settings between the playlists.
 */

class Playlist::SharedSettings
{
public:
    static SharedSettings *instance();
    /**
     * Sets the default column order to that of Playlist @param p.
     */
    void setColumnOrder(const Playlist *l);
    void toggleColumnVisible(int column);
67
    void setInlineCompletionMode(KGlobalSettings::Completion mode);
68 69 70

    /**
     * Apply the settings.
71
     */
72 73 74 75 76 77 78 79 80 81 82 83
    void apply(Playlist *l) const;

protected:
    SharedSettings();
    ~SharedSettings() {}

private:
    void writeConfig();

    static SharedSettings *m_instance;
    QValueList<int> m_columnOrder;
    QValueVector<bool> m_columnsVisible;
84
    KGlobalSettings::Completion m_inlineCompletion;
85 86
};

Scott Wheeler's avatar
Scott Wheeler committed
87 88
Playlist::SharedSettings *Playlist::SharedSettings::m_instance = 0;

89 90 91 92
////////////////////////////////////////////////////////////////////////////////
// Playlist::SharedSettings public members
////////////////////////////////////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
93 94 95 96 97 98 99 100
Playlist::SharedSettings *Playlist::SharedSettings::instance()
{
    if(!m_instance)
	m_instance = new SharedSettings;
    return m_instance;
}

void Playlist::SharedSettings::setColumnOrder(const Playlist *l)
101
{
Scott Wheeler's avatar
Scott Wheeler committed
102
    if(!l)
103 104
	return;

Scott Wheeler's avatar
Scott Wheeler committed
105
    m_columnOrder.clear();
106

107
    for(int i = l->columnOffset(); i < l->columns(); ++i)
Scott Wheeler's avatar
Scott Wheeler committed
108 109
	m_columnOrder.append(l->header()->mapToIndex(i));

110
    writeConfig();
Scott Wheeler's avatar
Scott Wheeler committed
111
}
112

113 114 115 116 117 118 119 120 121 122
void Playlist::SharedSettings::toggleColumnVisible(int column)
{
    if(column >= int(m_columnsVisible.size()))
	m_columnsVisible.resize(column + 1, true);

    m_columnsVisible[column] = !m_columnsVisible[column];

    writeConfig();
}

123 124 125 126 127 128 129
void Playlist::SharedSettings::setInlineCompletionMode(KGlobalSettings::Completion mode)
{
    m_inlineCompletion = mode;
    writeConfig();
}


130
void Playlist::SharedSettings::apply(Playlist *l) const
Scott Wheeler's avatar
Scott Wheeler committed
131 132 133 134
{
    if(!l)
	return;

135
    int offset = l->columnOffset();
Scott Wheeler's avatar
Scott Wheeler committed
136
    int i = 0;
137
    for(QValueListConstIterator<int> it = m_columnOrder.begin(); it != m_columnOrder.end(); ++it)
138
	l->header()->moveSection(i++ + offset, *it + offset);
139 140

    for(uint i = 0; i < m_columnsVisible.size(); i++) {
141
	if(m_columnsVisible[i] && !l->isColumnVisible(i + offset))
142
	    l->showColumn(i + offset, false);
143
	else if(!m_columnsVisible[i] && l->isColumnVisible(i + offset))
144
	    l->hideColumn(i + offset, false);
145
    }
Scott Wheeler's avatar
Scott Wheeler committed
146 147

    l->updateLeftColumn();
148
    l->renameLineEdit()->setCompletionMode(m_inlineCompletion);
Scott Wheeler's avatar
Scott Wheeler committed
149 150
}

151 152 153 154
////////////////////////////////////////////////////////////////////////////////
// Playlist::ShareSettings protected members
////////////////////////////////////////////////////////////////////////////////

Scott Wheeler's avatar
Scott Wheeler committed
155 156 157 158
Playlist::SharedSettings::SharedSettings()
{
    KConfig *config = kapp->config();
    {
159
	KConfigGroupSaver saver(config, "PlaylistShared");
160 161

	// save column order
Scott Wheeler's avatar
Scott Wheeler committed
162
	m_columnOrder = config->readIntListEntry("ColumnOrder");
163 164

	QValueList<int> l = config->readIntListEntry("VisibleColumns");
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

	if(l.isEmpty()) {

	    // Provide some default values for column visibility if none were
	    // read from the configuration file.

	    for(int i = 0; i <= PlaylistItem::lastColumn(); i++) {
		switch(i) {
		case PlaylistItem::CommentColumn:
		case PlaylistItem::FileNameColumn:
		    m_columnsVisible.append(false);
		    break;
		default:
		    m_columnsVisible.append(true);
		}
	    }
	}
	else {
	    // Convert the int list into a bool list.

	    m_columnsVisible.resize(l.size(), true);
	    uint i = 0;
	    for(QValueList<int>::Iterator it = l.begin(); it != l.end(); ++it) {
		if(! bool(*it))
		    m_columnsVisible[i] = bool(*it);
		i++;
	    }
192
	}
193 194 195

	m_inlineCompletion = KGlobalSettings::Completion(
	    config->readNumEntry("InlineCompletionMode", KGlobalSettings::CompletionAuto));
196
    }
197
    CollectionList::instance()->emitVisibleColumnsChanged();
198 199 200 201 202 203 204 205 206 207
}

////////////////////////////////////////////////////////////////////////////////
// Playlist::SharedSettings private members
////////////////////////////////////////////////////////////////////////////////

void Playlist::SharedSettings::writeConfig()
{
    KConfig *config = kapp->config();
    {
208
	KConfigGroupSaver saver(config, "PlaylistShared");
209 210 211 212 213
	config->writeEntry("ColumnOrder", m_columnOrder);

	QValueList<int> l;
	for(uint i = 0; i < m_columnsVisible.size(); i++)
	    l.append(int(m_columnsVisible[i]));
214

215
	config->writeEntry("VisibleColumns", l);
216
	config->writeEntry("InlineCompletionMode", m_inlineCompletion);
Scott Wheeler's avatar
Scott Wheeler committed
217
    }
218 219

    config->sync();
Scott Wheeler's avatar
Scott Wheeler committed
220
}
221

222
////////////////////////////////////////////////////////////////////////////////
223
// public members
224 225
////////////////////////////////////////////////////////////////////////////////

226 227 228
PlaylistItem *Playlist::m_playingItem = 0;
int Playlist::m_leftColumn = 0;

229 230
Playlist::Playlist(QWidget *parent, const QString &name) :
    KListView(parent, name.latin1()),
231
    m_selectedCount(0),
232 233
    m_allowDuplicates(false),
    m_polished(false),
234
    m_applySharedSettings(true),
235
    m_disableColumnWidthUpdates(true),
236
    m_widthsDirty(true),
237 238 239
    m_lastSelected(0),
    m_playlistName(name),
    m_rmbMenu(0)
240
{
241
    setup();
242 243
}

244
Playlist::Playlist(const QFileInfo &playlistFile, QWidget *parent, const QString &name) :
245
    KListView(parent, name.latin1()),
246 247 248
    m_selectedCount(0),
    m_allowDuplicates(false),
    m_polished(false),
249
    m_applySharedSettings(true),
250
    m_disableColumnWidthUpdates(true),
251
    m_widthsDirty(true),
252
    m_lastSelected(0),
253 254
    m_fileName(playlistFile.absFilePath()),
    m_rmbMenu(0)
255
{
256
    setup();
257
    loadFile(m_fileName, playlistFile);
258 259 260 261 262 263 264
}

Playlist::~Playlist()
{

}

265
void Playlist::save()
266
{
267
    if(m_fileName.isEmpty())
268
	return saveAs();
269

270
    QFile file(m_fileName);
271 272

    if(!file.open(IO_WriteOnly))
273
	return KMessageBox::error(this, i18n("Could not save to file %1.").arg(m_fileName));
274

275 276 277 278 279 280
    QTextStream stream(&file);

    QStringList fileList = files();

    for(QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it)
	stream << *it << endl;
281

282
    file.close();
283 284 285 286
}

void Playlist::saveAs()
{
287
    m_fileName = MediaFiles::savePlaylistDialog(name(), this);
288

289 290
    if(!m_fileName.isEmpty()) {
	// If there's no playlist name set, use the file name.
291
	if(m_playlistName.isEmpty())
292
	    emit signalNameChanged(name());
293

294 295
	save();
    }
296 297
}

298 299 300 301
void Playlist::clearItem(PlaylistItem *item, bool emitChanged)
{
    emit signalAboutToRemove(item);
    m_members.remove(item->absFilePath());
302
    if(!m_randomList.isEmpty() && !m_visibleChanged)
303
        m_randomList.remove(item);
304 305
    item->deleteLater();
    if(emitChanged)
306
	emit signalCountChanged(this);
307 308
}

309
void Playlist::clearItems(const PlaylistItemList &items)
310
{
311 312 313
    for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it)
	clearItem(*it, false);

314 315 316 317 318
    // Since we're using deleteLater() in the above call and calls to this will expect
    // those items to have actually gone away.

    kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput);

319
    emit signalCountChanged(this);
320 321
}

322
QStringList Playlist::files()
323
{
324
    QStringList list;
325 326 327

    for(QListViewItemIterator it(this); it.current(); ++it)
	list.append(static_cast<PlaylistItem *>(*it)->absFilePath());
328

329
    return list;
330 331
}

332 333
PlaylistItemList Playlist::items()
{
334
    return items(QListViewItemIterator::IteratorFlag(0));
335 336
}

337
PlaylistItemList Playlist::visibleItems()
338
{
339
    return items(QListViewItemIterator::Visible);
340 341
}

342
PlaylistItemList Playlist::selectedItems()
343 344
{
    PlaylistItemList list;
345 346 347 348

    switch(m_selectedCount) {
    case 0:
	break;
349 350 351
	// case 1:
	// list.append(m_lastSelected);
	// break;
352
    default:
353 354
	list = items(QListViewItemIterator::IteratorFlag(QListViewItemIterator::Selected |
							 QListViewItemIterator::Visible));
355 356
	break;
    }
357

358
    return list;
359 360
}

361 362 363 364
PlaylistItemList Playlist::historyItems(PlaylistItem *current, bool random) const
{
    PlaylistItemList list;

365
    if (random && !m_history.isEmpty()) {
Scott Wheeler's avatar
Scott Wheeler committed
366
        PlaylistItemList::ConstIterator it = m_history.end();
367
        --it;
Scott Wheeler's avatar
Scott Wheeler committed
368

369 370 371 372 373
        int j = 0;
        for(; it != m_history.begin() && j < 10; --it, ++j)
            list.append(*it);

        if (j < 10)
Scott Wheeler's avatar
Scott Wheeler committed
374
            list.append(*it);
375
    }
Scott Wheeler's avatar
Scott Wheeler committed
376
    else if(current) {
377
        current = static_cast<PlaylistItem *>(current->itemAbove());
Scott Wheeler's avatar
Scott Wheeler committed
378
        for(int j = 0; current && j < 10; ++j) {
379
            list.append(current);
380
            current = static_cast<PlaylistItem *>(current->itemAbove());
Scott Wheeler's avatar
Scott Wheeler committed
381
	}
382 383 384 385 386
    }

    return list;
}

387
PlaylistItem *Playlist::nextItem(PlaylistItem *current, bool random)
388
{
389
    PlaylistItem *i = 0;
390 391

    if(random) {
392 393
	if(m_randomList.count() <= 1 || m_visibleChanged) {
	    m_randomList = visibleItems();
394
	    m_visibleChanged = false;
395
	}
396

397 398 399 400
	if(current) {
	    m_randomList.remove(current);
	    m_history.append(current);
	}
401 402

        i = current;
403
	if(!m_randomList.isEmpty()) {
Scott Wheeler's avatar
Scott Wheeler committed
404 405 406
	    while(i == current)
		i = m_randomList[KApplication::random() % m_randomList.count()];
	}
407
    }
408
    else {
409
        m_history.clear();
410

411 412
	if(current)
	    i = static_cast<PlaylistItem *>(current->itemBelow());
413 414 415 416
	else {
	    QListViewItemIterator it(this, QListViewItemIterator::Visible);
	    i = static_cast<PlaylistItem *>(it.current());
	}
417
    }
418

419
    return i;
420 421
}

422 423 424
PlaylistItem *Playlist::previousItem(PlaylistItem *current, bool random)
{
    if(!current)
425
        return 0;
426

Scott Wheeler's avatar
Scott Wheeler committed
427 428 429 430
    if(random && !m_history.isEmpty()) {
        PlaylistItemList::Iterator last = m_history.fromLast();
        PlaylistItem *item = *last;
        m_history.remove(last);
431 432
        return item;
    }
433

434
    m_history.clear();
435 436 437 438
    if(!current->itemAbove())
        return current;

    return static_cast<PlaylistItem *>(current->itemAbove());
439 440
}

441 442
QString Playlist::name() const
{
443
    if(m_playlistName.isNull())
444
	return m_fileName.section(QDir::separator(), -1).section('.', 0, -2);
445
    else
446
	return m_playlistName;
447 448 449 450
}

void Playlist::setName(const QString &n)
{
451
    m_playlistName = n;
452
    emit signalNameChanged(m_playlistName);
453 454
}

455 456 457 458 459 460 461 462 463 464 465 466 467
int Playlist::totalTime()
{
    int time = 0;
    QListViewItemIterator it(this);
    while (it.current()) {
	PlaylistItem *item = static_cast<PlaylistItem *>(it.current());
	if (item->tag()->current())
	    time += item->tag()->seconds();
	it++;
    }
    return time;
}

Scott Wheeler's avatar
Scott Wheeler committed
468 469 470 471 472 473 474
void Playlist::updateLeftColumn()
{
    int newLeftColumn = leftMostVisibleColumn();

    if(m_leftColumn != newLeftColumn) {
	if(m_playingItem) {
	    m_playingItem->setPixmap(m_leftColumn, QPixmap(0, 0));
475
	    m_playingItem->setPixmap(newLeftColumn, UserIcon("playing"));
Scott Wheeler's avatar
Scott Wheeler committed
476 477 478 479 480
	}
	m_leftColumn = newLeftColumn;
    }
}

481
void Playlist::setItemsVisible(const PlaylistItemList &items, bool visible) // static
482
{
483
    m_visibleChanged = true;
484 485 486 487
    for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it)
	(*it)->setVisible(visible);
}

488 489 490 491 492 493 494 495 496 497
void Playlist::markItemSelected(PlaylistItem *item, bool selected)
{
    if(selected && !item->isSelected()) {
	m_selectedCount++;
	m_lastSelected = item;
    }
    else if(!selected && item->isSelected())
	m_selectedCount--;
}

498 499 500 501
////////////////////////////////////////////////////////////////////////////////
// public slots
////////////////////////////////////////////////////////////////////////////////

502
void Playlist::slotSetNext()
503
{
504 505 506
    if(!selectedItems().isEmpty())
	emit signalSetNext(selectedItems().first());
}
507

508 509 510 511 512 513 514
void Playlist::copy()
{
    kapp->clipboard()->setData(dragObject(0), QClipboard::Clipboard);
}

void Playlist::paste()
{
515
    decode(kapp->clipboard()->data(), static_cast<PlaylistItem *>(currentItem()));
516 517 518 519
}

void Playlist::clear()
{
520 521 522 523 524
    PlaylistItemList l = selectedItems();
    if(l.isEmpty())
	l = items();

    clearItems(l);
525 526
}

527 528 529 530 531 532 533 534 535 536
void Playlist::slotRefresh()
{
    PlaylistItemList l = selectedItems();
    if(l.isEmpty())
	l = visibleItems();

    KApplication::setOverrideCursor(Qt::waitCursor);
    int j = 0;
    for(PlaylistItemList::Iterator it = l.begin(); it != l.end(); ++it) {
	(*it)->slotRefreshFromDisk();
537 538 539 540 541 542 543 544

	if(!(*it)->tag()) {
	    kdDebug(65432) << "Error while trying to refresh the tag.  "
			   << "This file has probably been removed."
			   << endl;
	    delete (*it)->collectionItem();
	}

545 546 547 548 549 550 551
	if(j % 5 == 0)
	    kapp->processEvents();
	j = j % 5 + 1;
    }
    KApplication::restoreOverrideCursor();
}

552 553
void Playlist::slotRenameFile()
{
554
    FileRenamer renamer;
555
    PlaylistItemList items = selectedItems();
556
    if(items.count() == 1) {
557 558 559
        renamer.rename(items[0]);
    } else {
        renamer.rename(items);
560
    }
561 562
}

563
void Playlist::slotGuessTagInfo(TagGuesser::Type type)
564 565 566 567
{
    KApplication::setOverrideCursor(Qt::waitCursor);
    PlaylistItemList items = selectedItems();
    for(PlaylistItemList::Iterator it = items.begin(); it != items.end(); ++it)
568
        (*it)->guessTagInfo(type);
569
    KApplication::restoreOverrideCursor();
570 571
}

572 573 574 575 576
void Playlist::slotReload()
{
    QFileInfo fileInfo(m_fileName);
    if(!fileInfo.exists() || !fileInfo.isFile() || !fileInfo.isReadable())
	return;
577

578
    clearItems(items());
579 580 581
    loadFile(m_fileName, fileInfo);
}

582
void Playlist::slotWeightDirty(int column)
583 584
{
    if(column < 0) {
585
	m_weightDirty.clear();
586 587
	for(int i = 0; i < columns(); i++) {
	    if(isColumnVisible(i))
588
		m_weightDirty.append(i);
589 590 591 592
	}
	return;
    }

593 594
    if(m_weightDirty.find(column) == m_weightDirty.end())
	m_weightDirty.append(column);
595 596
}

597
////////////////////////////////////////////////////////////////////////////////
598
// protected members
599 600
////////////////////////////////////////////////////////////////////////////////

601
void Playlist::removeFromDisk(const PlaylistItemList &items)
602 603 604 605
{
    if(isVisible() && !items.isEmpty()) {

        QStringList files;
606 607
	for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it)
            files.append((*it)->fileName());
608 609 610 611 612 613 614

	QString message;

	if(files.count() == 1)
	    message = i18n("Do you really want to delete this item from your disk?");
	else
	    message = i18n("Do you really want to delete these %1 items from your disk?").arg(QString::number(files.count()));
615

616
	if(KMessageBox::warningYesNoList(this, message, files) == KMessageBox::Yes) {
617 618
	    for(PlaylistItemList::ConstIterator it = items.begin(); it != items.end(); ++it) {
		if(QFile::remove((*it)->filePath())) {
619
                    if(!m_randomList.isEmpty() && !m_visibleChanged)
620
                        m_randomList.remove(*it);
621
		    CollectionList::instance()->clearItem((*it)->collectionItem());
622 623
		}
		else
624
		    KMessageBox::sorry(this, i18n("Could not delete ") + (*it)->fileName() + ".");
625 626 627
	    }

	}
628
	emit signalCountChanged(this);
629 630 631
    }
}

632
QDragObject *Playlist::dragObject(QWidget *parent)
633
{
634
    PlaylistItemList items = selectedItems();
635
    KURL::List urls;
636
    for(PlaylistItemList::Iterator it = items.begin(); it != items.end(); ++it) {
637
	KURL url;
638
	url.setPath((*it)->absFilePath());
639 640
	urls.append(url);
    }
641

642
    KURLDrag *drag = new KURLDrag(urls, parent, "Playlist Items");
643
    drag->setPixmap(BarIcon("sound"));
644

645
    return drag;
646 647
}

648 649 650 651 652 653
bool Playlist::canDecode(QMimeSource *s)
{
    KURL::List urls;
    return KURLDrag::decode(s, urls) && !urls.isEmpty();
}

654
void Playlist::decode(QMimeSource *s, PlaylistItem *after)
655 656
{
    KURL::List urls;
657

658 659
    if(!KURLDrag::decode(s, urls) || urls.isEmpty())
	return;
660

661
    QStringList fileList;
662

663 664
    for(KURL::List::Iterator it = urls.begin(); it != urls.end(); it++)
	fileList.append((*it).path());
665

666
    emit signalFilesDropped(fileList, this, after);
667 668
}

669
bool Playlist::eventFilter(QObject *watched, QEvent *e)
670
{
671
    if(watched == header()) {
672 673 674 675
	switch(e->type()) {
	case QEvent::MouseButtonPress:
	{
	    if(static_cast<QMouseEvent *>(e)->button() == RightButton)
676
		m_headerMenu->popup(QCursor::pos());
677 678 679 680 681 682 683 684 685 686 687
	    break;
	}
	case QEvent::MouseButtonRelease:
	{
	    if(m_widthsDirty)
		QTimer::singleShot(0, this, SLOT(slotUpdateColumnWidths()));
	    break;
	}

	default:
	    break;
688 689 690 691 692 693
	}
    }

    return KListView::eventFilter(watched, e);
}

694 695
void Playlist::contentsDropEvent(QDropEvent *e)
{
696
    QPoint vp = contentsToViewport(e->pos());
697
    PlaylistItem *moveAfter = static_cast<PlaylistItem *>(itemAt(vp));
698 699 700 701

    // When dropping on the upper half of an item, insert before this item.
    // This is what the user expects, and also allows the insertion at
    // top of the list
702

703
    if(!moveAfter)
704
	moveAfter = static_cast<PlaylistItem *>(lastItem());
705
    else if(vp.y() < moveAfter->itemPos() + moveAfter->height() / 2)
706
	moveAfter = static_cast<PlaylistItem *>(moveAfter->itemAbove());
707 708

    if(e->source() == this) {
709

710 711 712 713
	// Since we're trying to arrange things manually, turn off sorting.

	setSorting(columns() + 1);

714
	QPtrList<QListViewItem> items = KListView::selectedItems();
715

716
	for(QPtrListIterator<QListViewItem> it(items); it.current(); ++it) {
717 718 719 720 721 722 723 724 725 726 727
	    if(!moveAfter) {

		// Insert the item at the top of the list.  This is a bit ugly,
		// but I don't see another way.

		takeItem(it.current());
		insertItem(it.current());
	    }
	    else
		it.current()->moveItem(moveAfter);

728
	    moveAfter = static_cast<PlaylistItem *>(it.current());
729 730
	}
    }
731
    else
732
	decode(e, moveAfter);
733 734

    KListView::contentsDropEvent(e);
735 736
}

Scott Wheeler's avatar
Scott Wheeler committed
737 738
void Playlist::showEvent(QShowEvent *e)
{
739 740 741 742
    if(m_applySharedSettings) {
	SharedSettings::instance()->apply(this);
	m_applySharedSettings = false;
    }
Scott Wheeler's avatar
Scott Wheeler committed
743 744 745
    KListView::showEvent(e);
}

746 747 748 749 750
void Playlist::applySharedSettings()
{
    m_applySharedSettings = true;
}

751 752 753 754
void Playlist::viewportPaintEvent(QPaintEvent *pe)
{
    // If there are columns that need to be updated, well, update them.

755
    if(!m_weightDirty.isEmpty()) {
756
	calculateColumnWeights();
757
	slotUpdateColumnWidths();
758 759 760 761 762 763 764 765 766 767 768
    }

    KListView::viewportPaintEvent(pe);
}

void Playlist::viewportResizeEvent(QResizeEvent *re)
{
    // If the width of the view has changed, manually update the column
    // widths.

    if(re->size().width() != re->oldSize().width())
769
	slotUpdateColumnWidths();
770 771 772 773 774 775

    KListView::viewportResizeEvent(re);
}

void Playlist::addColumn(const QString &label)
{
776
    slotWeightDirty(columns());
777 778 779
    KListView::addColumn(label, 30);
}

780 781
PlaylistItem *Playlist::createItem(const QFileInfo &file, const QString &absFilePath,
				   QListViewItem *after, bool emitChanged)
782
{
783
    return createItem<PlaylistItem, CollectionListItem, CollectionList>(file, absFilePath, after, emitChanged);
784 785
}

786 787
void Playlist::createItems(const PlaylistItemList &siblings)
{
788
    createItems<CollectionListItem, PlaylistItem, PlaylistItem>(siblings);
789 790
}

791
void Playlist::hideColumn(int c, bool emitChanged)
792
{
793
    m_headerMenu->setItemChecked(c, false);
794

795 796 797
    if(!isColumnVisible(c))
	return;

798 799
    setColumnWidthMode(c, Manual);
    setColumnWidth(c, 0);
800 801 802 803

    if(c == m_leftColumn) {
	if(m_playingItem) {
	    m_playingItem->setPixmap(m_leftColumn, QPixmap(0, 0));
804
	    m_playingItem->setPixmap(leftMostVisibleColumn(), UserIcon("playing"));
805 806 807
	}
	m_leftColumn = leftMostVisibleColumn();
    }
808

809
    slotUpdateColumnWidths();
810
    triggerUpdate();
811 812

    if(this != CollectionList::instance())
813
	CollectionList::instance()->hideColumn(c, false);
814

815 816
    if(emitChanged)
	CollectionList::instance()->emitVisibleColumnsChanged();
817 818
}

819
void Playlist::showColumn(int c, bool emitChanged)
820
{
821
    m_headerMenu->setItemChecked(c, true);
822

823 824 825
    if(isColumnVisible(c))
	return;

826 827
    // Just set the width to one to mark the column as visible -- we'll update
    // the real size in the next call.
828

829
    setColumnWidth(c, 1);
830 831 832 833

    if(c == leftMostVisibleColumn()) {
	if(m_playingItem) {
	    m_playingItem->setPixmap(m_leftColumn, QPixmap(0, 0));
834
	    m_playingItem->setPixmap(leftMostVisibleColumn(), UserIcon("playing"));
835 836 837
	}
	m_leftColumn = leftMostVisibleColumn();
    }
838

839
    slotUpdateColumnWidths();
840
    triggerUpdate();
841 842

    if(this != CollectionList::instance())
843
	CollectionList::instance()->showColumn(c, false);
844

845 846
    if(emitChanged)
	CollectionList::instance()->emitVisibleColumnsChanged();
847 848 849 850
}

bool Playlist::isColumnVisible(int c) const
{
851
    return columnWidth(c) != 0;
852 853
}

854 855 856
// Though it's somewhat obvious, this function will stat the file, so only use it when
// you're out of a performance critical loop.

857
QString Playlist::resolveSymLinks(const QFileInfo &file) // static
858
{
859 860 861 862
    char real[PATH_MAX];
    if(file.exists() && realpath(QFile::encodeName(file.absFilePath()).data(), real))
	return QFile::decodeName(real);
    else
863
	return file.filePath();
864 865
}

866
void Playlist::polish()
867
{
868 869
    KListView::polish();

870 871 872 873 874
    if(m_polished)
	return;

    m_polished = true;

875 876 877 878 879 880 881
    addColumn(i18n("Track Name"));
    addColumn(i18n("Artist"));
    addColumn(i18n("Album"));
    addColumn(i18n("Track"));
    addColumn(i18n("Genre"));
    addColumn(i18n("Year"));
    addColumn(i18n("Length"));
882
    addColumn(i18n("Comment"));
883 884
    addColumn(i18n("File Name"));

885 886 887
    setRenameable(PlaylistItem::TrackColumn, true);
    setRenameable(PlaylistItem::ArtistColumn, true);
    setRenameable(PlaylistItem::AlbumColumn, true);
888
    setRenameable(PlaylistItem::TrackNumberColumn, true);
889
    setRenameable(PlaylistItem::GenreColumn, true);
890
    setRenameable(PlaylistItem::YearColumn, true);
891

892 893 894
    setAllColumnsShowFocus(true);
    setSelectionMode(QListView::Extended);
    setShowSortIndicator(true);
895
    setDropVisualizer(true);
896