playlistbox.cpp 26.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/**
 * Copyright (C) 2002-2004 Scott Wheeler <wheeler@kde.org>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
16

17 18
#include "playlistbox.h"

19
#include <kicon.h>
20
#include <kiconloader.h>
21
#include <kmessagebox.h>
Laurent Montel's avatar
Laurent Montel committed
22
#include <kmenu.h>
23
#include <kaction.h>
24
#include <kactioncollection.h>
25
#include <kdebug.h>
Laurent Montel's avatar
Poirt  
Laurent Montel committed
26 27
#include <ktoggleaction.h>
#include <kselectaction.h>
28
#include <kconfiggroup.h>
29 30
#include <kglobal.h>
#include <kurl.h>
31

32
#include <QPainter>
33
#include <QTimer>
Laurent Montel's avatar
Laurent Montel committed
34
#include <QDragLeaveEvent>
35
#include <QList>
Laurent Montel's avatar
Laurent Montel committed
36 37 38 39
#include <QDragMoveEvent>
#include <QKeyEvent>
#include <QDropEvent>
#include <QMouseEvent>
40
#include <QFileInfo>
41
#include <QTime>
42 43
#include <QApplication>
#include <QClipboard>
44
#include <QHeaderView>
45

46
#include "playlist.h"
47 48
#include "collectionlist.h"
#include "dynamicplaylist.h"
49
#include "upcomingplaylist.h"
50
#include "historyplaylist.h"
51
#include "viewmode.h"
52
#include "searchplaylist.h"
53
#include "treeviewitemplaylist.h"
54
#include "actioncollection.h"
55
#include "cache.h"
56
#include "k3bexporter.h"
57
#include "tracksequencemanager.h"
58
#include "tagtransactionmanager.h"
59
#include "playermanager.h"
60
#include "dbuscollectionproxy.h"
61 62

using namespace ActionCollection;
63 64 65 66 67

////////////////////////////////////////////////////////////////////////////////
// PlaylistBox public methods
////////////////////////////////////////////////////////////////////////////////

68
PlaylistBox::PlaylistBox(PlayerManager *player, QWidget *parent, QStackedWidget *playlistStack) :
69
    QTreeWidget(parent),
70
    PlaylistCollection(player, playlistStack),
71
    m_viewModeIndex(0),
72
    m_hasSelection(false),
73
    m_doingMultiSelect(false),
74
    m_dropItem(0),
75
    m_showTimer(0)
76
{
77
    readConfig();
78
    setHeaderLabel("Playlists");
79 80
    setRootIsDecorated(false);
    setContextMenuPolicy(Qt::CustomContextMenu);
81
    setDropIndicatorShown(true);
82

83
    header()->blockSignals(true);
84
    header()->hide();
85
    header()->blockSignals(false);
86

87 88 89
    sortByColumn(0);
    // FIXME ?
    //setFullWidth(true);
Laurent Montel's avatar
Laurent Montel committed
90

91 92
    viewport()->setAcceptDrops(true);
    setDropIndicatorShown(true);
93
    setSelectionMode(QAbstractItemView::ExtendedSelection);
Laurent Montel's avatar
Laurent Montel committed
94

95
    m_contextMenu = new KMenu(this);
96

97 98
    K3bPlaylistExporter *exporter = new K3bPlaylistExporter(this);
    m_k3bAction = exporter->action();
99

100 101 102 103 104 105 106 107
    m_contextMenu->addAction( action("file_new") );
    m_contextMenu->addAction( action("renamePlaylist") );
    m_contextMenu->addAction( action("editSearch") );
    m_contextMenu->addAction( action("duplicatePlaylist") );
    m_contextMenu->addAction( action("reloadPlaylist") );
    m_contextMenu->addAction( action("deleteItemPlaylist") );
    m_contextMenu->addAction( action("file_save") );
    m_contextMenu->addAction( action("file_save_as") );
108
    if(m_k3bAction)
109
        m_contextMenu->addAction( m_k3bAction );
Laurent Montel's avatar
Laurent Montel committed
110

111
    m_contextMenu->addSeparator();
112

113
    // add the view modes stuff
Laurent Montel's avatar
Laurent Montel committed
114

115
    KSelectAction *viewModeAction =
Pino Toscano's avatar
Pino Toscano committed
116
        new KSelectAction( KIcon("view-choose"), i18n("View Modes"), ActionCollection::actions());
117
    ActionCollection::actions()->addAction("viewModeMenu", viewModeAction);
Laurent Montel's avatar
Laurent Montel committed
118

119 120 121
    ViewMode* viewmode = new ViewMode(this);
    m_viewModes.append(viewmode);
    viewModeAction->addAction(KIcon("view-list-details"), viewmode->name());
122

123 124 125
    CompactViewMode* compactviewmode = new CompactViewMode(this);
    m_viewModes.append(compactviewmode);
    viewModeAction->addAction(KIcon("view-list-text"), compactviewmode->name());
126

127 128 129
    TreeViewMode* treeviewmode = new TreeViewMode(this);
    m_viewModes.append(treeviewmode);
    viewModeAction->addAction(KIcon("view-list-tree"), treeviewmode->name());
130

131 132
    CollectionList::initialize(this);

133
    viewModeAction->setCurrentItem(m_viewModeIndex);
134
    m_viewModes[m_viewModeIndex]->setShown(true);
Laurent Montel's avatar
Laurent Montel committed
135

136 137 138
    TrackSequenceManager::instance()->setCurrentPlaylist(CollectionList::instance());
    raise(CollectionList::instance());

139
    m_contextMenu->addAction( viewModeAction );
140
    connect(viewModeAction, SIGNAL(triggered(int)), this, SLOT(slotSetViewMode(int)));
141

142
    connect(this, SIGNAL(itemSelectionChanged()),
143
            this, SLOT(slotPlaylistChanged()));
Laurent Montel's avatar
Laurent Montel committed
144

145 146
    connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
            this, SLOT(slotDoubleClicked(QTreeWidgetItem*)));
Laurent Montel's avatar
Laurent Montel committed
147

148 149
    connect(this, SIGNAL(customContextMenuRequested(QPoint)),
            this, SLOT(slotShowContextMenu(QPoint)));
150

151 152 153 154
    TagTransactionManager *tagManager = TagTransactionManager::instance();
    connect(tagManager, SIGNAL(signalAboutToModifyTags()), SLOT(slotFreezePlaylists()));
    connect(tagManager, SIGNAL(signalDoneModifyingTags()), SLOT(slotUnfreezePlaylists()));

155
    setupUpcomingPlaylist();
156

Laurent Montel's avatar
Laurent Montel committed
157 158 159 160
    connect(CollectionList::instance(), SIGNAL(signalNewTag(QString,uint)),
            this, SLOT(slotAddItem(QString,uint)));
    connect(CollectionList::instance(), SIGNAL(signalRemovedTag(QString,uint)),
            this, SLOT(slotRemoveItem(QString,uint)));
161 162
    connect(CollectionList::instance(), SIGNAL(cachedItemsLoaded()),
            this, SLOT(slotLoadCachedPlaylists()));
163

164
    m_savePlaylistTimer = 0;
165

166 167 168 169 170 171
    KToggleAction *historyAction =
        new KToggleAction(KIcon("view-history"), i18n("Show &History"), ActionCollection::actions());
    ActionCollection::actions()->addAction("showHistory", historyAction);
    connect(historyAction, SIGNAL(triggered(bool)),
            this, SLOT(slotSetHistoryPlaylistEnabled(bool)));

172 173
    m_showTimer = new QTimer(this);
    connect(m_showTimer, SIGNAL(timeout()), SLOT(slotShowDropTarget()));
174 175 176

    // hook up to the D-Bus
    (void) new DBusCollectionProxy(this, this);
177 178 179 180
}

PlaylistBox::~PlaylistBox()
{
181 182
    PlaylistList l;
    CollectionList *collection = CollectionList::instance();
183 184
    for(QTreeWidgetItemIterator it(topLevelItem(0)); *it; ++it) {
        Item *item = static_cast<Item *>(*it);
185 186
        if(item->playlist() && item->playlist() != collection)
            l.append(item->playlist());
187
    }
188

189 190
    Cache::savePlaylists(l);
    saveConfig();
191 192
}

193 194 195
void PlaylistBox::raise(Playlist *playlist)
{
    if(!playlist)
196
        return;
197

198
    Item *i = m_playlistDict.value(playlist, 0);
199

200
    if(i) {
201
        clearSelection();
202
        setCurrentItem(i);
203

204
        setSingleItem(i);
205
        scrollToItem(currentItem());
206 207
    }
    else
208 209
        PlaylistCollection::raise(playlist);

210
    slotPlaylistChanged();
211 212
}

213
void PlaylistBox::duplicate()
214
{
215 216
    Item *item = static_cast<Item *>(currentItem());
    if(!item || !item->playlist())
217
        return;
218

219
    QString name = playlistNameDialog(i18nc("verb, copy the playlist", "Duplicate"), item->text(0));
220

221
    if(name.isNull())
222
        return;
223

224 225
    Playlist *p = new Playlist(this, name);
    p->createItems(item->playlist()->items());
226 227
}

228 229 230 231 232 233 234 235 236 237 238 239
void PlaylistBox::scanFolders()
{
    kDebug() << "Starting folder scan";
    QTime stopwatch; stopwatch.start();

    PlaylistCollection::scanFolders();

    kDebug() << "Folder scan complete, took" << stopwatch.elapsed() << "ms";
    kDebug() << "Startup complete!";
    emit startupComplete();
}

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

244 245
void PlaylistBox::paste()
{
246
    Item *i = static_cast<Item *>(currentItem());
247
    decode(QApplication::clipboard()->mimeData(), i);
248 249
}

250 251 252 253
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox protected methods
////////////////////////////////////////////////////////////////////////////////

254 255
void PlaylistBox::slotFreezePlaylists()
{
256
    setDynamicListsFrozen(true);
257 258 259 260
}

void PlaylistBox::slotUnfreezePlaylists()
{
261
    setDynamicListsFrozen(false);
262 263
}

264 265
void PlaylistBox::slotPlaylistDataChanged()
{
266 267
    if(m_savePlaylistTimer)
        m_savePlaylistTimer->start(); // Restarts the timer if it's already running.
268 269
}

270 271 272 273 274
void PlaylistBox::slotSetHistoryPlaylistEnabled(bool enable)
{
    setHistoryPlaylistEnabled(enable);
}

275 276
void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName)
{
277 278 279 280 281
    setupPlaylist(playlist, iconName, 0);
}

void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName, Item *parentItem)
{
Laurent Montel's avatar
Laurent Montel committed
282 283
    connect(playlist, SIGNAL(signalPlaylistItemsDropped(Playlist*)),
            SLOT(slotPlaylistItemsDropped(Playlist*)));
284

285
    PlaylistCollection::setupPlaylist(playlist, iconName);
286 287

    if(parentItem)
288
        new Item(parentItem, iconName, playlist->name(), playlist);
289
    else
290
        new Item(this, iconName, playlist->name(), playlist);
291 292
}

293
void PlaylistBox::removePlaylist(Playlist *playlist)
294
{
295
    // Could be false if setup() wasn't run yet.
296
    if(m_playlistDict.contains(playlist)) {
297
        removeNameFromDict(m_playlistDict[playlist]->text(0));
298 299
        delete m_playlistDict[playlist]; // Delete the Item*
    }
300

301 302
    removeFileFromDict(playlist->fileName());
    m_playlistDict.remove(playlist);
303
}
304

305 306 307 308
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private methods
////////////////////////////////////////////////////////////////////////////////

309 310
void PlaylistBox::readConfig()
{
311
    KConfigGroup config(KGlobal::config(), "PlaylistBox");
312
    m_viewModeIndex = config.readEntry("ViewMode", 0);
313 314 315 316
}

void PlaylistBox::saveConfig()
{
317
    KConfigGroup config(KGlobal::config(), "PlaylistBox");
318
    config.writeEntry("ViewMode", action<KSelectAction>("viewModeMenu")->currentItem());
319
    KGlobal::config()->sync();
320 321
}

322
void PlaylistBox::remove()
323
{
324
    ItemList items = selectedBoxItems();
325

326
    if(items.isEmpty())
327
        return;
328

329
    QStringList files;
330
    QStringList names;
331

332
    foreach(Item *item, items) {
333
        if(item && item->playlist())
334
        {
335 336 337
           if (!item->playlist()->fileName().isEmpty() &&
               QFileInfo(item->playlist()->fileName()).exists())
           {
338
            files.append(item->playlist()->fileName());
339
           }
340

341 342
           names.append(item->playlist()->name());
        }
343 344
    }

345
    if(!files.isEmpty()) {
346
        int remove = KMessageBox::warningYesNoCancelList(
347
            this, i18n("Do you want to delete these files from the disk as well?"), files, QString(), KStandardGuiItem::del(), KGuiItem(i18n("Keep")));
348 349 350

        if(remove == KMessageBox::Yes) {
            QStringList couldNotDelete;
Laurent Montel's avatar
Laurent Montel committed
351
            for(QStringList::ConstIterator it = files.constBegin(); it != files.constEnd(); ++it) {
352 353 354 355 356 357 358 359 360
                if(!QFile::remove(*it))
                    couldNotDelete.append(*it);
            }

            if(!couldNotDelete.isEmpty())
                KMessageBox::errorList(this, i18n("Could not delete these files."), couldNotDelete);
        }
        else if(remove == KMessageBox::Cancel)
            return;
361
    }
362
    else if(items.count() > 1 || items.front()->playlist() != upcomingPlaylist()) {
363 364 365 366 367
        if(KMessageBox::warningContinueCancelList(this,
                                                  i18n("Are you sure you want to remove these "
                                                       "playlists from your collection?"),
                                                  names,
                                                  i18n("Remove Items?"),
Pino Toscano's avatar
Pino Toscano committed
368
                                                  KGuiItem(i18n("&Remove"), "user-trash")) == KMessageBox::Cancel)
369 370 371
        {
            return;
        }
372
    }
373

374
    PlaylistList removeQueue;
375

Laurent Montel's avatar
Laurent Montel committed
376
    for(ItemList::ConstIterator it = items.constBegin(); it != items.constEnd(); ++it) {
377 378 379 380 381 382
        if(*it != Item::collectionItem() &&
           (*it)->playlist() &&
           (!(*it)->playlist()->readOnly()))
        {
            removeQueue.append((*it)->playlist());
        }
383
    }
384

385
    // FIXME removing items
386
    /*if(items.back()->nextSibling() && static_cast<Item *>(items.back()->nextSibling())->playlist())
387
        setSingleItem(items.back()->nextSibling());
388
    else {
389 390 391
        Item *i = static_cast<Item *>(items.front()->itemAbove());
        while(i && !i->playlist())
            i = static_cast<Item *>(i->itemAbove());
392

393 394
        if(!i)
            i = Item::collectionItem();
395

396
        setSingleItem(i);
397
    }*/
398

Laurent Montel's avatar
Laurent Montel committed
399
    for(PlaylistList::ConstIterator it = removeQueue.constBegin(); it != removeQueue.constEnd(); ++it) {
400 401 402 403 404 405
        if(*it != upcomingPlaylist())
            delete *it;
        else {
            action<KToggleAction>("showUpcoming")->setChecked(false);
            setUpcomingPlaylistEnabled(false);
        }
406
    }
407 408
}

409
void PlaylistBox::setDynamicListsFrozen(bool frozen)
410
{
411
    for(QList<ViewMode *>::Iterator it = m_viewModes.begin();
412 413
        it != m_viewModes.end();
        ++it)
414
    {
415
        (*it)->setDynamicListsFrozen(frozen);
416
    }
417 418
}

419 420
void PlaylistBox::slotSavePlaylists()
{
421
    kDebug() << "Auto-saving playlists.\n";
422 423 424

    PlaylistList l;
    CollectionList *collection = CollectionList::instance();
425 426
    for(QTreeWidgetItemIterator it(topLevelItem(0)); *it; ++it) {
        Item *item = static_cast<Item *>(*it);
427 428
        if(item->playlist() && item->playlist() != collection)
            l.append(item->playlist());
429 430 431 432 433
    }

    Cache::savePlaylists(l);
}

434 435 436
void PlaylistBox::slotShowDropTarget()
{
    if(!m_dropItem) {
437
        kError() << "Trying to show the playlist of a null item!\n";
438
        return;
439 440
    }

441
    raise(m_dropItem->playlist());
442 443
}

444 445
void PlaylistBox::slotAddItem(const QString &tag, unsigned column)
{
446 447
    for(QList<ViewMode *>::Iterator it = m_viewModes.begin(); it != m_viewModes.end(); ++it)
        (*it)->addItems(QStringList(tag), column);
448 449 450 451
}

void PlaylistBox::slotRemoveItem(const QString &tag, unsigned column)
{
452
    for(QList<ViewMode *>::Iterator it = m_viewModes.begin(); it != m_viewModes.end(); ++it)
453
        (*it)->removeItem(tag, column);
454 455
}

Tim Beaulen's avatar
Tim Beaulen committed
456
void PlaylistBox::decode(const QMimeData *s, Item *item)
457
{
458
    if(!s || (item && item->playlist() && item->playlist()->readOnly()))
459
        return;
460

461
    const KUrl::List urls = KUrl::List::fromMimeData(s);
Laurent Montel's avatar
Laurent Montel committed
462

Tim Beaulen's avatar
Tim Beaulen committed
463
    if(!urls.isEmpty()) {
464
        QStringList files;
Laurent Montel's avatar
Laurent Montel committed
465
        for(KUrl::List::ConstIterator it = urls.constBegin(); it != urls.constEnd(); ++it)
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
            files.append((*it).path());

        if(item) {
            TreeViewItemPlaylist *playlistItem;
            playlistItem = dynamic_cast<TreeViewItemPlaylist *>(item->playlist());
            if(playlistItem) {
                playlistItem->retag(files, currentPlaylist());
                TagTransactionManager::instance()->commit();
                currentPlaylist()->update();
                return;
            }
        }

        if(item && item->playlist())
            item->playlist()->addFiles(files);
        else {
            QString name = playlistNameDialog();
            if(!name.isNull()) {
                Playlist *p = new Playlist(this, name);
                p->addFiles(files);
            }
        }
488 489 490
    }
}

491
void PlaylistBox::dropEvent(QDropEvent *e)
492
{
493 494
    m_showTimer->stop();

495
    Item *i = static_cast<Item *>(itemAt(e->pos()));
Tim Beaulen's avatar
Tim Beaulen committed
496
    decode(e->mimeData(), i);
497 498

    if(m_dropItem) {
499 500
        Item *old = m_dropItem;
        m_dropItem = 0;
501 502 503 504 505 506 507 508
        //old->repaint();
    }
    e->acceptProposedAction();
}

void PlaylistBox::dragEnterEvent(QDragEnterEvent *e)
{
    e->acceptProposedAction();
509 510
}

511
void PlaylistBox::dragMoveEvent(QDragMoveEvent *e)
512 513
{
    // If we can decode the input source, there is a non-null item at the "move"
Laurent Montel's avatar
Laurent Montel committed
514
    // position, the playlist for that Item is non-null, is not the
515 516 517
    // selected playlist and is not the CollectionList, then accept the event.
    //
    // Otherwise, do not accept the event.
Laurent Montel's avatar
Laurent Montel committed
518

519
    if (!e->mimeData()->hasUrls()) {
520
        e->setAccepted(false);
521
        return;
522 523
    }

524
    Item *target = static_cast<Item *>(itemAt(e->pos()));
525 526

    if(target) {
527

528 529 530 531 532 533 534 535 536 537
        if(target->playlist() && target->playlist()->readOnly())
            return;

        // This is a semi-dirty hack to check if the items are coming from within
        // JuK.  If they are not coming from a Playlist (or subclass) then the
        // dynamic_cast will fail and we can safely assume that the item is
        // coming from outside of JuK.

        if(dynamic_cast<Playlist *>(e->source())) {
            if(target->playlist() &&
538 539
               target->playlist() != CollectionList::instance() /*&&
               !target->isSelected()*/)
540
            {
541
                e->setAccepted(true);
542 543
            }
            else
544
                e->setAccepted(false);
545 546
        }
        else // the dropped items are coming from outside of JuK
547
            e->setAccepted(true);
548 549 550 551 552 553 554

        if(m_dropItem != target) {
            Item *old = m_dropItem;
            m_showTimer->stop();

            if(e->isAccepted()) {
                m_dropItem = target;
555
                //target->repaint();
556 557
                m_showTimer->setSingleShot(true);
                m_showTimer->start(1500);
558 559 560 561
            }
            else
                m_dropItem = 0;

562 563
            /*if(old)
                old->repaint();*/
564
        }
565
    }
566 567
    else {

568 569
        // We're dragging over the whitespace.  We'll use this case to make it
        // possible to create new lists.
570

571
        e->setAccepted(true);
572
    }
573 574
}

575
void PlaylistBox::dragLeaveEvent(QDragLeaveEvent *e)
576
{
577
    if(m_dropItem) {
578 579
        Item *old = m_dropItem;
        m_dropItem = 0;
580
        //old->repaint();
581
    }
582
    QTreeWidget::dragLeaveEvent(e);
583 584
}

585
void PlaylistBox::mousePressEvent(QMouseEvent *e)
586
{
587
    if(e->button() == Qt::LeftButton)
588
        m_doingMultiSelect = true;
589
    QTreeWidget::mousePressEvent(e);
590 591
}

592
void PlaylistBox::mouseReleaseEvent(QMouseEvent *e)
593
{
594
    if(e->button() == Qt::LeftButton) {
595 596
        m_doingMultiSelect = false;
        slotPlaylistChanged();
597
    }
598
    QTreeWidget::mouseReleaseEvent(e);
599 600
}

601 602
void PlaylistBox::keyPressEvent(QKeyEvent *e)
{
603
    if((e->key() == Qt::Key_Up || e->key() == Qt::Key_Down) && e->modifiers() == Qt::ShiftModifier)
604
        m_doingMultiSelect = true;
605
    QTreeWidget::keyPressEvent(e);
606 607 608 609
}

void PlaylistBox::keyReleaseEvent(QKeyEvent *e)
{
610
    if(m_doingMultiSelect && e->key() == Qt::Key_Shift) {
611 612
        m_doingMultiSelect = false;
        slotPlaylistChanged();
613
    }
614
    QTreeWidget::keyReleaseEvent(e);
615 616
}

617
PlaylistBox::ItemList PlaylistBox::selectedBoxItems() const
618
{
619
    ItemList l;
620

621 622
    for(QTreeWidgetItemIterator it(const_cast<PlaylistBox *>(this),
                                 QTreeWidgetItemIterator::Selected); *it; ++it)
623
        l.append(static_cast<Item *>(*it));
624

625 626 627
    return l;
}

628
void PlaylistBox::setSingleItem(QTreeWidgetItem *item)
629
{
630 631 632
    setSelectionMode(QAbstractItemView::SingleSelection);
    setCurrentItem(item);
    setSelectionMode(QAbstractItemView::ExtendedSelection);
633 634
}

635 636 637 638
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private slots
////////////////////////////////////////////////////////////////////////////////

639
void PlaylistBox::slotPlaylistChanged()
640
{
641 642
    // Don't update while the mouse is pressed down.

643
    if(m_doingMultiSelect)
644
        return;
645

646
    ItemList items = selectedBoxItems();
647 648
    m_hasSelection = !items.isEmpty();

649 650
    bool allowReload = false;

651
    PlaylistList playlists;
Laurent Montel's avatar
Laurent Montel committed
652
    for(ItemList::ConstIterator it = items.constBegin(); it != items.constEnd(); ++it) {
653

654 655 656 657 658 659
        Playlist *p = (*it)->playlist();
        if(p) {
            if(p->canReload())
                allowReload = true;
            playlists.append(p);
        }
660 661
    }

662 663
    bool singlePlaylist = playlists.count() == 1;

664
    if(playlists.isEmpty() ||
665
       (singlePlaylist &&
666 667
        (playlists.front() == CollectionList::instance() ||
         playlists.front()->readOnly())))
668 669 670 671 672 673 674 675 676 677 678
    {
        action("file_save")->setEnabled(false);
        action("file_save_as")->setEnabled(false);
        action("renamePlaylist")->setEnabled(false);
        action("deleteItemPlaylist")->setEnabled(false);
    }
    else {
        action("file_save")->setEnabled(true);
        action("file_save_as")->setEnabled(true);
        action("renamePlaylist")->setEnabled(playlists.count() == 1);
        action("deleteItemPlaylist")->setEnabled(true);
679
    }
680 681
    action("reloadPlaylist")->setEnabled(allowReload);
    action("duplicatePlaylist")->setEnabled(!playlists.isEmpty());
682 683

    if(m_k3bAction)
684
        m_k3bAction->setEnabled(!playlists.isEmpty());
685

686
    action("editSearch")->setEnabled(singlePlaylist &&
687
                                     playlists.front()->searchIsEditable());
688

689
    if(singlePlaylist) {
690
        PlaylistCollection::raise(playlists.front());
691

692 693 694 695
        if(playlists.front() == upcomingPlaylist())
            action("deleteItemPlaylist")->setText(i18n("Hid&e"));
        else
            action("deleteItemPlaylist")->setText(i18n("R&emove"));
696
    }
697
    else if(!playlists.isEmpty())
698
        createDynamicPlaylist(playlists);
699 700
}

701
void PlaylistBox::slotDoubleClicked(QTreeWidgetItem *item)
702
{
703 704 705
    if(!item)
        return;

706 707 708
    TrackSequenceManager *manager = TrackSequenceManager::instance();
    Item *playlistItem = static_cast<Item *>(item);

709
    manager->setCurrentPlaylist(playlistItem->playlist());
710 711 712 713 714

    manager->setCurrent(0); // Reset playback
    PlaylistItem *next = manager->nextItem(); // Allow manager to choose

    if(next) {
715
        emit startFilePlayback(next->file());
716 717 718 719
        playlistItem->playlist()->setPlaying(next);
    }
    else
        action("stop")->trigger();
720 721
}

722
void PlaylistBox::slotShowContextMenu(const QPoint &point)
723
{
724
    m_contextMenu->popup(mapToGlobal(point));
725 726
}

727 728 729 730 731
void PlaylistBox::slotPlaylistItemsDropped(Playlist *p)
{
    raise(p);
}

732
void PlaylistBox::slotSetViewMode(int index)
733
{
734
    if(index == m_viewModeIndex)
735
        return;
736 737 738

    viewMode()->setShown(false);
    m_viewModeIndex = index;
Laurent Montel's avatar
Laurent Montel committed
739
    viewMode()->setShown(true);
740 741
}

742
void PlaylistBox::setupItem(Item *item)
743
{
744 745
    m_playlistDict.insert(item->playlist(), item);
    viewMode()->queueRefresh();
746 747
}

748 749 750
void PlaylistBox::setupUpcomingPlaylist()
{
    KConfigGroup config(KGlobal::config(), "Playlists");
751
    bool enable = config.readEntry("showUpcoming", false);
752 753 754 755 756

    setUpcomingPlaylistEnabled(enable);
    action<KToggleAction>("showUpcoming")->setChecked(enable);
}

Michael Pyne's avatar
Michael Pyne committed
757 758 759

void PlaylistBox::slotLoadCachedPlaylists()
{
760 761 762 763
    kDebug() << "Loading cached playlists.";
    QTime stopwatch;
    stopwatch.start();

Michael Pyne's avatar
Michael Pyne committed
764
    Cache::loadPlaylists(this);
765

766 767
    kDebug() << "Cached playlists loaded, took" << stopwatch.elapsed() << "ms";

768 769 770 771 772 773
    // Auto-save playlists after they change.
    m_savePlaylistTimer = new QTimer(this);
    m_savePlaylistTimer->setInterval(3000); // 3 seconds with no change? -> commit
    m_savePlaylistTimer->setSingleShot(true);
    connect(m_savePlaylistTimer, SIGNAL(timeout()), SLOT(slotSavePlaylists()));

Michael Pyne's avatar
Michael Pyne committed
774
    clearSelection();
775
    setCurrentItem(m_playlistDict[CollectionList::instance()]);
776 777 778

    QTimer::singleShot(0, CollectionList::instance(), SLOT(slotCheckCache()));
    QTimer::singleShot(0, object(), SLOT(slotScanFolders()));
Michael Pyne's avatar
Michael Pyne committed
779
}
780

781
////////////////////////////////////////////////////////////////////////////////
782
// PlaylistBox::Item protected methods
783 784
////////////////////////////////////////////////////////////////////////////////

785 786
PlaylistBox::Item *PlaylistBox::Item::m_collectionItem = 0;

787
PlaylistBox::Item::Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l)
788
    : QObject(listBox), QTreeWidgetItem(listBox, QStringList(text)),
789
      PlaylistObserver(l),
790
      m_playlist(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
791
{
792 793
    init();
}
794

795
PlaylistBox::Item::Item(Item *parent, const QString &icon, const QString &text, Playlist *l)
796
    : QObject(parent->listView()), QTreeWidgetItem(parent, QStringList(text)),
797
    PlaylistObserver(l),
798
    m_playlist(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
799 800
{
    init();
801 802
}

803
PlaylistBox::Item::~Item()
804 805 806 807
{

}

808
int PlaylistBox::Item::compare(QTreeWidgetItem *i, int col, bool) const
809
{
810
    Item *otherItem = static_cast<Item *>(i);
811
    PlaylistBox *playlistBox = static_cast<PlaylistBox *>(treeWidget());
812 813

    if(m_playlist == playlistBox->upcomingPlaylist() && otherItem->m_playlist != CollectionList::instance())
814
        return -1;
815
    if(otherItem->m_playlist == playlistBox->upcomingPlaylist() && m_playlist != CollectionList::instance())
816
        return 1;
817 818

    if(m_sortedFirst && !otherItem->m_sortedFirst)
819
        return -1;
820
    else if(otherItem->m_sortedFirst && !m_sortedFirst)
821
        return 1;
822

Dirk Mueller's avatar
Dirk Mueller committed
823
    return text(col).toLower().localeAwareCompare(i->text(col).toLower());
824 825
}

826
    // FIXME paintcell
827
/*void PlaylistBox::Item::paintCell(QPainter *painter, const QColorGroup &colorGroup, int column, int width, int align)
828
{
829 830
    PlaylistBox *playlistBox = static_cast<PlaylistBox *>(listView());
    playlistBox->viewMode()->paintCell(this, painter, colorGroup, column, width, align);
831
}*/
832 833 834 835

void PlaylistBox::Item::setText(int column, const QString &text)
{
    m_text = text;
836
    QTreeWidgetItem::setText(column, text);
837 838
}

839 840 841 842 843
void PlaylistBox::Item::setup()
{
    listView()->viewMode()->setupItem(this);
}

844 845 846 847 848 849 850
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox::Item protected slots
////////////////////////////////////////////////////////////////////////////////

void PlaylistBox::Item::slotSetName(const QString &name)
{
    if(listView()) {
851 852
        setText(0, name);
        setSelected(true);
853

Kacper Kasper's avatar
Kacper Kasper committed
854 855
        treeWidget()->sortItems(0, Qt::AscendingOrder);
        treeWidget()->scrollToItem(treeWidget()->currentItem());
856
        //FIXME viewmode
Kacper Kasper's avatar
Kacper Kasper committed
857
        //listView()->viewMode()->queueRefresh();
858 859 860
    }
}

861 862 863 864 865 866 867 868 869
void PlaylistBox::Item::updateCurrent()
{
}

void PlaylistBox::Item::updateData()
{
    listView()->slotPlaylistDataChanged();
}

870 871 872 873 874 875
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox::Item private methods
////////////////////////////////////////////////////////////////////////////////

void PlaylistBox::Item::init()
{
876
    PlaylistBox *list = listView();
877 878 879 880

    list->setupItem(this);

    int iconSize = list->viewModeIndex() == 0 ? 32 : 16;
881
    setIcon(0, SmallIcon(m_iconName, iconSize));
882
    list->addNameToDict(m_text);
883

884
    if(m_playlist) {
Laurent Montel's avatar
Laurent Montel committed
885 886
        connect(m_playlist, SIGNAL(signalNameChanged(QString)),
                this, SLOT(slotSetName(QString)));