playlistbox.cpp 24.1 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 <kmessagebox.h>
20
#include <kactioncollection.h>
Laurent Montel's avatar
Poirt  
Laurent Montel committed
21 22
#include <ktoggleaction.h>
#include <kselectaction.h>
23
#include <kconfiggroup.h>
Michael Pyne's avatar
Michael Pyne committed
24
#include <KSharedConfig>
25

Michael Pyne's avatar
Michael Pyne committed
26 27 28
#include <QAction>
#include <QIcon>
#include <QMenu>
29
#include <QPainter>
30
#include <QTimer>
Laurent Montel's avatar
Laurent Montel committed
31
#include <QDragLeaveEvent>
32
#include <QList>
Laurent Montel's avatar
Laurent Montel committed
33 34 35 36
#include <QDragMoveEvent>
#include <QKeyEvent>
#include <QDropEvent>
#include <QMouseEvent>
37
#include <QFileInfo>
38
#include <QTime>
39 40
#include <QApplication>
#include <QClipboard>
41
#include <QHeaderView>
Laurent Montel's avatar
Laurent Montel committed
42
#include <QElapsedTimer>
43

44
#include "playlist.h"
45 46
#include "collectionlist.h"
#include "dynamicplaylist.h"
47
#include "upcomingplaylist.h"
48
#include "historyplaylist.h"
49
#include "viewmode.h"
50
#include "searchplaylist.h"
51
#include "treeviewitemplaylist.h"
52
#include "actioncollection.h"
53
#include "cache.h"
54
#include "tracksequencemanager.h"
55
#include "tagtransactionmanager.h"
56
#include "playermanager.h"
57
#include "dbuscollectionproxy.h"
Michael Pyne's avatar
Michael Pyne committed
58
#include "juk_debug.h"
59 60

using namespace ActionCollection;
61 62 63 64 65

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

66
PlaylistBox::PlaylistBox(PlayerManager *player, QWidget *parent, QStackedWidget *playlistStack) :
67
    QTreeWidget(parent),
68
    PlaylistCollection(player, playlistStack),
69
    m_viewModeIndex(0),
70
    m_hasSelection(false),
71
    m_doingMultiSelect(false),
72
    m_dropItem(0),
73
    m_showTimer(0)
74
{
75
    readConfig();
76
    setHeaderLabel("Playlists");
77 78
    setRootIsDecorated(false);
    setContextMenuPolicy(Qt::CustomContextMenu);
79 80
    viewport()->setAcceptDrops(true);
    setDragDropMode(QAbstractItemView::DropOnly);
Kacper Kasper's avatar
Kacper Kasper committed
81
    setDropIndicatorShown(true);
82

83 84
    setColumnCount(2); // Use fake column for sorting
    setColumnHidden(1, true);
85
    setSortingEnabled(true);
86
    sortByColumn(1, Qt::AscendingOrder);
87

88
    header()->blockSignals(true);
89
    header()->hide();
90
    header()->blockSignals(false);
91

92
    setSelectionMode(QAbstractItemView::ExtendedSelection);
Laurent Montel's avatar
Laurent Montel committed
93

Michael Pyne's avatar
Michael Pyne committed
94
    m_contextMenu = new QMenu(this);
95

96 97 98 99 100 101 102 103
    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") );
Laurent Montel's avatar
Laurent Montel committed
104

105
    m_contextMenu->addSeparator();
106

107
    // add the view modes stuff
Laurent Montel's avatar
Laurent Montel committed
108

109
    KSelectAction *viewModeAction =
Michael Pyne's avatar
Michael Pyne committed
110
        new KSelectAction( QIcon::fromTheme(QStringLiteral("view-choose")), i18n("View Modes"), ActionCollection::actions());
111
    ActionCollection::actions()->addAction("viewModeMenu", viewModeAction);
Laurent Montel's avatar
Laurent Montel committed
112

113 114
    ViewMode* viewmode = new ViewMode(this);
    m_viewModes.append(viewmode);
Michael Pyne's avatar
Michael Pyne committed
115
    viewModeAction->addAction(QIcon::fromTheme(QStringLiteral("view-list-details")), viewmode->name());
116

117 118
    CompactViewMode* compactviewmode = new CompactViewMode(this);
    m_viewModes.append(compactviewmode);
Michael Pyne's avatar
Michael Pyne committed
119
    viewModeAction->addAction(QIcon::fromTheme(QStringLiteral("view-list-text")), compactviewmode->name());
120

121 122
    // TODO: Fix the broken tree view mode
#if 0
123 124
    TreeViewMode* treeviewmode = new TreeViewMode(this);
    m_viewModes.append(treeviewmode);
Michael Pyne's avatar
Michael Pyne committed
125
    viewModeAction->addAction(QIcon::fromTheme(QStringLiteral("view-list-tree")), treeviewmode->name());
126
#endif
127

128 129
    CollectionList::initialize(this);

130
    viewModeAction->setCurrentItem(m_viewModeIndex);
131
    m_viewModes[m_viewModeIndex]->setShown(true);
Laurent Montel's avatar
Laurent Montel committed
132

133 134 135
    TrackSequenceManager::instance()->setCurrentPlaylist(CollectionList::instance());
    raise(CollectionList::instance());

136
    m_contextMenu->addAction( viewModeAction );
137
    connect(viewModeAction, SIGNAL(triggered(int)), this, SLOT(slotSetViewMode(int)));
138

139
    connect(this, SIGNAL(itemSelectionChanged()),
140
            this, SLOT(slotPlaylistChanged()));
Laurent Montel's avatar
Laurent Montel committed
141

142 143
    connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
            this, SLOT(slotDoubleClicked(QTreeWidgetItem*)));
Laurent Montel's avatar
Laurent Montel committed
144

145 146
    connect(this, SIGNAL(customContextMenuRequested(QPoint)),
            this, SLOT(slotShowContextMenu(QPoint)));
147

148 149 150 151
    TagTransactionManager *tagManager = TagTransactionManager::instance();
    connect(tagManager, SIGNAL(signalAboutToModifyTags()), SLOT(slotFreezePlaylists()));
    connect(tagManager, SIGNAL(signalDoneModifyingTags()), SLOT(slotUnfreezePlaylists()));

152
    setupUpcomingPlaylist();
153

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

161
    m_savePlaylistTimer = 0;
162

163
    KToggleAction *historyAction =
Michael Pyne's avatar
Michael Pyne committed
164
        new KToggleAction(QIcon::fromTheme(QStringLiteral("view-history")), i18n("Show &History"), ActionCollection::actions());
165 166 167 168
    ActionCollection::actions()->addAction("showHistory", historyAction);
    connect(historyAction, SIGNAL(triggered(bool)),
            this, SLOT(slotSetHistoryPlaylistEnabled(bool)));

169
    m_showTimer = new QTimer(this);
170 171
    m_showTimer->setSingleShot(true);
    m_showTimer->setInterval(500);
172
    connect(m_showTimer, SIGNAL(timeout()), SLOT(slotShowDropTarget()));
173 174 175

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

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

188 189
    Cache::savePlaylists(l);
    saveConfig();
190 191 192 193 194 195

    // Some view modes use event filters onto sibling widgets which may be
    // destroyed before the view mode.
    // Manually delete the view modes instead.
    qDeleteAll(m_viewModes);
    m_viewModes.clear();
196 197
}

198 199 200
void PlaylistBox::raise(Playlist *playlist)
{
    if(!playlist)
201
        return;
202

203
    Item *i = m_playlistDict.value(playlist, 0);
204

205
    if(i) {
206
        clearSelection();
207
        setCurrentItem(i);
208

209
        setSingleItem(i);
210
        scrollToItem(currentItem());
211 212
    }
    else
213 214
        PlaylistCollection::raise(playlist);

215
    slotPlaylistChanged();
216 217
}

218
void PlaylistBox::duplicate()
219
{
220 221
    Item *item = static_cast<Item *>(currentItem());
    if(!item || !item->playlist())
222
        return;
223

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

226
    if(name.isNull())
227
        return;
228

229 230
    Playlist *p = new Playlist(this, name);
    p->createItems(item->playlist()->items());
231 232
}

233 234 235 236 237 238
void PlaylistBox::scanFolders()
{
    PlaylistCollection::scanFolders();
    emit startupComplete();
}

239 240 241 242
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox public slots
////////////////////////////////////////////////////////////////////////////////

243 244
void PlaylistBox::paste()
{
245
    // TODO: Reimplement
246 247
}

248 249 250 251
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox protected methods
////////////////////////////////////////////////////////////////////////////////

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

void PlaylistBox::slotUnfreezePlaylists()
{
259
    setDynamicListsFrozen(false);
260 261
}

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

268 269 270 271 272
void PlaylistBox::slotSetHistoryPlaylistEnabled(bool enable)
{
    setHistoryPlaylistEnabled(enable);
}

273 274
void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName)
{
275
    setupPlaylist(playlist, iconName, nullptr);
276 277 278 279
}

void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName, Item *parentItem)
{
280 281 282 283
    connect(playlist, &Playlist::signalPlaylistItemsDropped,
            this,     &PlaylistBox::slotPlaylistItemsDropped);
    connect(playlist, &Playlist::signalMoveFocusAway,
            this,     &PlaylistBox::signalMoveFocusAway);
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 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
Qt::DropActions PlaylistBox::supportedDropActions() const
{
    return Qt::CopyAction;
}

bool PlaylistBox::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
{
    Q_UNUSED(index);

    // The *parent* item won't be null, but index should be zero except in the
    // still-broken "tree view" mode.

    if(!parent || action != Qt::CopyAction || !data->hasUrls()) {
        return false;
    }

    auto *playlistItem = static_cast<Item *>(parent);
    if(!playlistItem) {
        return false;
    }

    auto *playlist = playlistItem->playlist();
    const auto droppedUrls = data->urls();
    PlaylistItem *lastItem = nullptr;

    for(const auto &url : droppedUrls) {
        lastItem = playlist->createItem(FileHandle(url.toLocalFile()), lastItem);
    }

    return true;
}

QStringList PlaylistBox::mimeTypes() const
{
    auto result = QTreeWidget::mimeTypes();

    // Need to add Playlists's mime type to convince QTreeWidget to allow it as
    // a drop option.
    result.append(QLatin1String("text/uri-list"));

    return result;
}

348 349 350 351
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private methods
////////////////////////////////////////////////////////////////////////////////

352 353
void PlaylistBox::readConfig()
{
354
    KConfigGroup config(KSharedConfig::openConfig(), "PlaylistBox");
355
    m_viewModeIndex = config.readEntry("ViewMode", 0);
356 357 358 359 360

    // TODO Restore ability to use Tree View once fixed.
    if(m_viewModeIndex == 2) {
        m_viewModeIndex = 0;
    }
361 362 363 364
}

void PlaylistBox::saveConfig()
{
365
    KConfigGroup config(KSharedConfig::openConfig(), "PlaylistBox");
366
    config.writeEntry("ViewMode", action<KSelectAction>("viewModeMenu")->currentItem());
367
    KSharedConfig::openConfig()->sync();
368 369
}

370
void PlaylistBox::remove()
371
{
372
    ItemList items = selectedBoxItems();
373

374
    if(items.isEmpty())
375
        return;
376

377
    QStringList files;
378
    QStringList names;
379

380
    foreach(Item *item, items) {
381 382 383 384 385 386 387 388
        if(item && item->playlist()) {
            if (!item->playlist()->fileName().isEmpty() &&
                QFileInfo::exists(item->playlist()->fileName()))
            {
                files.append(item->playlist()->fileName());
            }

            names.append(item->playlist()->name());
389
        }
390 391
    }

392
    if(!files.isEmpty()) {
393
        int remove = KMessageBox::warningYesNoCancelList(
394
            this, i18n("Do you want to delete these files from the disk as well?"), files, QString(), KStandardGuiItem::del(), KGuiItem(i18n("Keep")));
395 396 397

        if(remove == KMessageBox::Yes) {
            QStringList couldNotDelete;
Laurent Montel's avatar
Laurent Montel committed
398
            for(QStringList::ConstIterator it = files.constBegin(); it != files.constEnd(); ++it) {
399 400 401 402 403 404 405 406 407
                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;
408
    }
409
    else if(items.count() > 1 || items.front()->playlist() != upcomingPlaylist()) {
410 411 412 413 414
        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
415
                                                  KGuiItem(i18n("&Remove"), "user-trash")) == KMessageBox::Cancel)
416 417 418
        {
            return;
        }
419
    }
420

421
    PlaylistList removeQueue;
422

Laurent Montel's avatar
Laurent Montel committed
423
    for(ItemList::ConstIterator it = items.constBegin(); it != items.constEnd(); ++it) {
424 425 426 427 428 429
        if(*it != Item::collectionItem() &&
           (*it)->playlist() &&
           (!(*it)->playlist()->readOnly()))
        {
            removeQueue.append((*it)->playlist());
        }
430
    }
431

432
    // FIXME removing items
433
    /*if(items.back()->nextSibling() && static_cast<Item *>(items.back()->nextSibling())->playlist())
434
        setSingleItem(items.back()->nextSibling());
435
    else {
436 437 438
        Item *i = static_cast<Item *>(items.front()->itemAbove());
        while(i && !i->playlist())
            i = static_cast<Item *>(i->itemAbove());
439

440 441
        if(!i)
            i = Item::collectionItem();
442

443
        setSingleItem(i);
444
    }*/
445

Laurent Montel's avatar
Laurent Montel committed
446
    for(PlaylistList::ConstIterator it = removeQueue.constBegin(); it != removeQueue.constEnd(); ++it) {
447 448 449 450 451 452
        if(*it != upcomingPlaylist())
            delete *it;
        else {
            action<KToggleAction>("showUpcoming")->setChecked(false);
            setUpcomingPlaylistEnabled(false);
        }
453
    }
454 455
}

456
void PlaylistBox::setDynamicListsFrozen(bool frozen)
457
{
458
    for(QList<ViewMode *>::Iterator it = m_viewModes.begin();
459 460
        it != m_viewModes.end();
        ++it)
461
    {
462
        (*it)->setDynamicListsFrozen(frozen);
463
    }
464 465
}

466 467
void PlaylistBox::slotSavePlaylists()
{
Michael Pyne's avatar
Michael Pyne committed
468
    qCDebug(JUK_LOG) << "Auto-saving playlists.\n";
469 470 471

    PlaylistList l;
    CollectionList *collection = CollectionList::instance();
472 473
    for(QTreeWidgetItemIterator it(topLevelItem(0)); *it; ++it) {
        Item *item = static_cast<Item *>(*it);
474 475
        if(item->playlist() && item->playlist() != collection)
            l.append(item->playlist());
476 477 478 479 480
    }

    Cache::savePlaylists(l);
}

481 482
void PlaylistBox::slotShowDropTarget()
{
483
    if(m_dropItem) raise(m_dropItem->playlist());
484 485
}

486 487
void PlaylistBox::slotAddItem(const QString &tag, unsigned column)
{
488 489
    for(QList<ViewMode *>::Iterator it = m_viewModes.begin(); it != m_viewModes.end(); ++it)
        (*it)->addItems(QStringList(tag), column);
490 491 492 493
}

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

498
void PlaylistBox::mousePressEvent(QMouseEvent *e)
499
{
500
    if(e->button() == Qt::LeftButton)
501
        m_doingMultiSelect = true;
502
    QTreeWidget::mousePressEvent(e);
503 504
}

505
void PlaylistBox::mouseReleaseEvent(QMouseEvent *e)
506
{
507
    if(e->button() == Qt::LeftButton) {
508 509
        m_doingMultiSelect = false;
        slotPlaylistChanged();
510
    }
511
    QTreeWidget::mouseReleaseEvent(e);
512 513
}

514 515
void PlaylistBox::keyPressEvent(QKeyEvent *e)
{
516
    if((e->key() == Qt::Key_Up || e->key() == Qt::Key_Down) && e->modifiers() == Qt::ShiftModifier)
517
        m_doingMultiSelect = true;
518
    QTreeWidget::keyPressEvent(e);
519 520 521 522
}

void PlaylistBox::keyReleaseEvent(QKeyEvent *e)
{
523
    if(m_doingMultiSelect && e->key() == Qt::Key_Shift) {
524 525
        m_doingMultiSelect = false;
        slotPlaylistChanged();
526
    }
527
    QTreeWidget::keyReleaseEvent(e);
528 529
}

530
PlaylistBox::ItemList PlaylistBox::selectedBoxItems() const
531
{
532
    ItemList l;
533

534 535
    for(QTreeWidgetItemIterator it(const_cast<PlaylistBox *>(this),
                                 QTreeWidgetItemIterator::Selected); *it; ++it)
536
        l.append(static_cast<Item *>(*it));
537

538 539 540
    return l;
}

541
void PlaylistBox::setSingleItem(QTreeWidgetItem *item)
542
{
543 544 545
    setSelectionMode(QAbstractItemView::SingleSelection);
    setCurrentItem(item);
    setSelectionMode(QAbstractItemView::ExtendedSelection);
546 547
}

548 549 550 551
void PlaylistBox::dragMoveEvent(QDragMoveEvent* event)
{
    QTreeWidget::dragMoveEvent(event);

552 553 554 555 556 557
    Item* hovered_item = static_cast<Item*>(itemAt(event->pos()));
    if(hovered_item != m_dropItem){
        m_dropItem = hovered_item;
        if(m_dropItem) m_showTimer->start();
        else m_showTimer->stop();
    };
558 559
}

560 561 562 563 564
void PlaylistBox::dragLeaveEvent(QDragLeaveEvent* event)
{
    QTreeWidget::dragLeaveEvent(event);
    m_showTimer->stop();
}
565

566 567 568 569
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox private slots
////////////////////////////////////////////////////////////////////////////////

570
void PlaylistBox::slotPlaylistChanged()
571
{
572 573
    // Don't update while the mouse is pressed down.

574
    if(m_doingMultiSelect)
575
        return;
576

577
    ItemList items = selectedBoxItems();
578 579
    m_hasSelection = !items.isEmpty();

580 581
    bool allowReload = false;

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

585 586 587 588 589 590
        Playlist *p = (*it)->playlist();
        if(p) {
            if(p->canReload())
                allowReload = true;
            playlists.append(p);
        }
591 592
    }

593 594
    bool singlePlaylist = playlists.count() == 1;

595
    if(playlists.isEmpty() ||
596
       (singlePlaylist &&
597 598
        (playlists.front() == CollectionList::instance() ||
         playlists.front()->readOnly())))
599 600 601 602 603 604 605 606 607 608 609
    {
        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);
610
    }
611 612
    action("reloadPlaylist")->setEnabled(allowReload);
    action("duplicatePlaylist")->setEnabled(!playlists.isEmpty());
613

614
    action("editSearch")->setEnabled(singlePlaylist &&
615
                                     playlists.front()->searchIsEditable());
616

617
    if(singlePlaylist) {
618
        PlaylistCollection::raise(playlists.front());
619

620 621 622 623
        if(playlists.front() == upcomingPlaylist())
            action("deleteItemPlaylist")->setText(i18n("Hid&e"));
        else
            action("deleteItemPlaylist")->setText(i18n("R&emove"));
624
    }
625
    else if(!playlists.isEmpty())
626
        createDynamicPlaylist(playlists);
627 628
}

629
void PlaylistBox::slotDoubleClicked(QTreeWidgetItem *item)
630
{
631 632 633
    if(!item)
        return;

634 635 636
    TrackSequenceManager *manager = TrackSequenceManager::instance();
    Item *playlistItem = static_cast<Item *>(item);

637
    manager->setCurrentPlaylist(playlistItem->playlist());
638 639 640 641 642

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

    if(next) {
643
        emit startFilePlayback(next->file());
644 645 646 647
        playlistItem->playlist()->setPlaying(next);
    }
    else
        action("stop")->trigger();
648 649
}

650
void PlaylistBox::slotShowContextMenu(const QPoint &point)
651
{
652
    m_contextMenu->popup(mapToGlobal(point));
653 654
}

655 656 657 658 659
void PlaylistBox::slotPlaylistItemsDropped(Playlist *p)
{
    raise(p);
}

660
void PlaylistBox::slotSetViewMode(int index)
661
{
662
    if(index == m_viewModeIndex)
663
        return;
664 665 666

    viewMode()->setShown(false);
    m_viewModeIndex = index;
Laurent Montel's avatar
Laurent Montel committed
667
    viewMode()->setShown(true);
668 669
}

670
void PlaylistBox::setupItem(Item *item)
671
{
672 673
    m_playlistDict.insert(item->playlist(), item);
    viewMode()->queueRefresh();
674 675
}

676 677
void PlaylistBox::setupUpcomingPlaylist()
{
678
    KConfigGroup config(KSharedConfig::openConfig(), "Playlists");
679
    bool enable = config.readEntry("showUpcoming", false);
680 681 682 683 684

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

Michael Pyne's avatar
Michael Pyne committed
685 686 687

void PlaylistBox::slotLoadCachedPlaylists()
{
Michael Pyne's avatar
Michael Pyne committed
688
    qCDebug(JUK_LOG) << "Loading cached playlists.";
Laurent Montel's avatar
Laurent Montel committed
689
    QElapsedTimer stopwatch;
690 691
    stopwatch.start();

Michael Pyne's avatar
Michael Pyne committed
692
    Cache::loadPlaylists(this);
693

Michael Pyne's avatar
Michael Pyne committed
694
    qCDebug(JUK_LOG) << "Cached playlists loaded, took" << stopwatch.elapsed() << "ms";
695

696 697 698 699 700 701
    // 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
702
    clearSelection();
703
    setCurrentItem(m_playlistDict[CollectionList::instance()]);
704 705 706

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

709
////////////////////////////////////////////////////////////////////////////////
710
// PlaylistBox::Item protected methods
711 712
////////////////////////////////////////////////////////////////////////////////

713 714
PlaylistBox::Item *PlaylistBox::Item::m_collectionItem = 0;

715
PlaylistBox::Item::Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l)
716
    : QObject(listBox), QTreeWidgetItem(listBox, QStringList(text)),
717
      m_playlist(l), m_iconName(icon), m_sortedFirst(false)
718
{
719 720
    init();
}
721

722
PlaylistBox::Item::Item(Item *parent, const QString &icon, const QString &text, Playlist *l)
723
    : QObject(parent->listView()), QTreeWidgetItem(parent, QStringList(text)),
724
    m_playlist(l), m_iconName(icon), m_sortedFirst(false)
725 726
{
    init();
727 728
}

729
PlaylistBox::Item::~Item()
730 731 732 733
{

}

734 735 736 737 738
void PlaylistBox::Item::setup()
{
    listView()->viewMode()->setupItem(this);
}

739 740 741 742 743 744
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox::Item protected slots
////////////////////////////////////////////////////////////////////////////////

void PlaylistBox::Item::slotSetName(const QString &name)
{
745 746 747
    setText(0, name); // Display name
    setText(1, sortTextFor(name));
    setSelected(true);
748

749
    treeWidget()->scrollToItem(this);
750 751
}

752
void PlaylistBox::Item::playlistItemDataChanged()