playlistcollection.cpp 27.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/***************************************************************************
    copyright            : (C) 2004 by Scott Wheeler
    email                : 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.                                   *
 *                                                                         *
 ***************************************************************************/

15
16
#include "playlistcollection.h"

17
#include <kurl.h>
18
19
20
21
22
23
24
25
26
27
28
29
#include <kicon.h>
#include <khbox.h>
#include <kiconloader.h>
#include <kapplication.h>
#include <kinputdialog.h>
#include <kmessagebox.h>
#include <kfiledialog.h>
#include <kaction.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include <kactionmenu.h>
#include <kconfiggroup.h>
30

31
#include <config.h>
32
#include <config-juk.h>
33

34
#include <QObject>
Laurent Montel's avatar
Laurent Montel committed
35
#include <QPixmap>
36
#include <Q3WidgetStack>
37

38
39
40
#include <sys/types.h>
#include <dirent.h>

41
42
43
#include "collectionlist.h"
#include "actioncollection.h"
#include "advancedsearchdialog.h"
Michael Pyne's avatar
Michael Pyne committed
44
#include "coverinfo.h"
45
46
#include "searchplaylist.h"
#include "folderplaylist.h"
47
#include "historyplaylist.h"
48
#include "upcomingplaylist.h"
49
50
51
#include "directorylist.h"
#include "mediafiles.h"
#include "playermanager.h"
52
#include "tracksequencemanager.h"
53
#include "juk.h"
54

Laurent Montel's avatar
Laurent Montel committed
55
56
//Laurent: readd it
//#include "collectionadaptor.h"
57

58
59
#define widget (kapp->mainWidget())

60
using namespace ActionCollection;
61

62
63
64
65
66
67
////////////////////////////////////////////////////////////////////////////////
// static methods
////////////////////////////////////////////////////////////////////////////////

PlaylistCollection *PlaylistCollection::m_instance = 0;

68
69
70
71
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////

Laurent Montel's avatar
Laurent Montel committed
72
PlaylistCollection::PlaylistCollection(Q3WidgetStack *playlistStack) :
73
    m_playlistStack(playlistStack),
74
    m_historyPlaylist(0),
75
    m_upcomingPlaylist(0),
76
    m_importPlaylists(true),
77
    m_searchEnabled(true),
78
79
    m_playing(false),
    m_showMorePlaylist(0),
80
    m_belowShowMorePlaylist(0),
81
82
83
    m_dynamicPlaylist(0),
    m_belowDistraction(0),
    m_distraction(0)
84
{
Laurent Montel's avatar
Laurent Montel committed
85
86
    //new CollectionAdaptor( this );
    //QDBus::sessionBus().registerObject("/Collection",this );
87
88
    m_instance = this;

89
90
    m_actionHandler = new ActionHandler(this);
    PlayerManager::instance()->setPlaylistInterface(this);
91
92
93
94
95

    // KDirLister's auto error handling seems to crash JuK during startup in
    // readConfig().

    m_dirLister.setAutoErrorHandlingEnabled(false, playlistStack);
96
    readConfig();
97
98
99
100
}

PlaylistCollection::~PlaylistCollection()
{
101
    saveConfig();
102
    delete m_actionHandler;
103
    PlayerManager::instance()->setPlaylistInterface(0);
104
    Playlist::setShuttingDown();
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
}

QString PlaylistCollection::name() const
{
    return currentPlaylist()->name();
}

FileHandle PlaylistCollection::currentFile() const
{
    return currentPlaylist()->currentFile();
}

int PlaylistCollection::count() const
{
    return currentPlaylist()->count();
}

int PlaylistCollection::time() const
{
    return currentPlaylist()->time();
}

127
128
void PlaylistCollection::playFirst()
{
129
    m_playing = true;
130
    currentPlaylist()->playFirst();
131
    currentChanged();
132
133
}

134
135
136
137
138
139
140
void PlaylistCollection::playNextAlbum()
{
    m_playing = true;
    currentPlaylist()->playNextAlbum();
    currentChanged();
}

141
142
void PlaylistCollection::playPrevious()
{
143
    m_playing = true;
144
    currentPlaylist()->playPrevious();
145
    currentChanged();
146
147
148
149
}

void PlaylistCollection::playNext()
{
150
    m_playing = true;
151
    currentPlaylist()->playNext();
152
    currentChanged();
153
154
155
156
}

void PlaylistCollection::stop()
{
157
    m_playing = false;
158
    currentPlaylist()->stop();
159
    dataChanged();
160
161
162
163
164
}

bool PlaylistCollection::playing() const
{
    return m_playing;
165
166
}

167
168
169
170
QStringList PlaylistCollection::playlists() const
{
    QStringList l;

171
172
173
    //(or qFindChildren() if you need MSVC 6 compatibility)
    QList<Playlist *> childList = m_playlistStack->findChildren<Playlist *>("Playlist");
    for(QList<Playlist *>::Iterator it = childList.begin(); it != childList.end(); ++it) {
174
        Playlist *p = static_cast<Playlist *>(*it);
175
        l.append(p->name());
176
177
178
179
180
181
182
183
184
185
    }

    return l;
}

void PlaylistCollection::createPlaylist(const QString &name)
{
    raise(new Playlist(this, name));
}

186
187
188
189
190
191
void PlaylistCollection::createDynamicPlaylist(const PlaylistList &playlists)
{
    if(m_dynamicPlaylist)
        m_dynamicPlaylist->setPlaylists(playlists);
    else
        m_dynamicPlaylist =
Pino Toscano's avatar
Pino Toscano committed
192
            new DynamicPlaylist(playlists, this, i18n("Dynamic List"), "audio-midi", false, true);
193

194
    PlaylistCollection::raise(m_dynamicPlaylist);
195
196
}

197
198
199
200
201
202
203
void PlaylistCollection::showMore(const QString &artist, const QString &album)
{

    PlaylistList playlists;
    PlaylistSearch::ComponentList components;

    if(currentPlaylist() != CollectionList::instance() &&
204
205
       currentPlaylist() != m_showMorePlaylist)
    {
206
        playlists.append(currentPlaylist());
207
208
209
    }

    playlists.append(CollectionList::instance());
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

    { // Just setting off the artist stuff in its own block.
        ColumnList columns;
        columns.append(PlaylistItem::ArtistColumn);
        PlaylistSearch::Component c(artist, false, columns,
                                    PlaylistSearch::Component::Exact);
        components.append(c);
    }

    if(!album.isNull()) {
        ColumnList columns;
        columns.append(PlaylistItem::AlbumColumn);
        PlaylistSearch::Component c(album, false, columns,
                                    PlaylistSearch::Component::Exact);
        components.append(c);
    }

    PlaylistSearch search(playlists, components, PlaylistSearch::MatchAll);

229
230
231
232
    if(m_showMorePlaylist)
        m_showMorePlaylist->setPlaylistSearch(search);
    else
        m_showMorePlaylist = new SearchPlaylist(this, search, i18n("Now Playing"), false, true);
233

234
235
236
    // The call to raise() below will end up clearing m_belowShowMorePlaylist,
    // so cache the value we want it to have now.
    Playlist *belowShowMore = visiblePlaylist();
237

238
    PlaylistCollection::setupPlaylist(m_showMorePlaylist, QString());
239
    PlaylistCollection::raise(m_showMorePlaylist);
240
241

    m_belowShowMorePlaylist = belowShowMore;
242
243
}

244
245
246
247
248
void PlaylistCollection::removeTrack(const QString &playlist, const QStringList &files)
{
    Playlist *p = playlistByName(playlist);
    PlaylistItemList itemList;
    if(!p)
249
        return;
250

251
252
    QStringList::ConstIterator it;
    for(it = files.begin(); it != files.end(); ++it) {
253
        CollectionListItem *item = CollectionList::instance()->lookup(*it);
254

255
256
257
258
259
        if(item) {
            PlaylistItem *playlistItem = item->itemForPlaylist(p);
            if(playlistItem)
                itemList.append(playlistItem);
        }
260
261
262
263
264
265
266
    }

    p->clearItems(itemList);
}

QString PlaylistCollection::playlist() const
{
267
    return visiblePlaylist() ? visiblePlaylist()->name() : QString();
268
269
270
271
}

QString PlaylistCollection::playingPlaylist() const
{
272
    return currentPlaylist() && m_playing ? currentPlaylist()->name() : QString();
273
274
275
276
277
278
}

void PlaylistCollection::setPlaylist(const QString &playlist)
{
    Playlist *p = playlistByName(playlist);
    if(p)
279
        raise(p);
280
281
282
283
284
285
286
}

QStringList PlaylistCollection::playlistTracks(const QString &playlist) const
{
    Playlist *p = playlistByName(playlist);

    if(p)
287
        return p->files();
288
289
290
291
292
293
294
295
    return QStringList();
}

QString PlaylistCollection::trackProperty(const QString &file, const QString &property) const
{
    CollectionList *l = CollectionList::instance();
    CollectionListItem *item = l->lookup(file);

296
    return item ? item->file().property(property) : QString();
297
298
}

Michael Pyne's avatar
Michael Pyne committed
299
300
QPixmap PlaylistCollection::trackCover(const QString &file, const QString &size) const
{
301
    if(size.toLower() != "small" && size.toLower() != "large")
Michael Pyne's avatar
Michael Pyne committed
302
303
304
305
306
307
308
309
        return QPixmap();

    CollectionList *l = CollectionList::instance();
    CollectionListItem *item = l->lookup(file);

    if(!item)
        return QPixmap();

310
    if(size.toLower() == "small")
Michael Pyne's avatar
Michael Pyne committed
311
312
313
314
315
        return item->file().coverInfo()->pixmap(CoverInfo::Thumbnail);
    else
        return item->file().coverInfo()->pixmap(CoverInfo::FullSize);
}

316
317
318
319
320
void PlaylistCollection::open(const QStringList &l)
{
    QStringList files = l;

    if(files.isEmpty())
321
        files = MediaFiles::openDialog(JuK::JuKInstance());
322

323
324
    if(files.isEmpty())
        return;
325

326
327
328
329
330
331
    bool justPlaylists = true;

    for(QStringList::ConstIterator it = l.begin(); it != l.end() && justPlaylists; ++it)
        justPlaylists = !MediaFiles::isPlaylistFile(*it);

    if(visiblePlaylist() == CollectionList::instance() || justPlaylists ||
332
       KMessageBox::questionYesNo(
333
           JuK::JuKInstance(),
334
           i18n("Do you want to add these items to the current list or to the collection list?"),
335
           QString(),
336
           KGuiItem(i18nc("current playlist", "Current")),
337
338
           KGuiItem(i18n("Collection"))) == KMessageBox::No)
    {
339
        CollectionList::instance()->addFiles(files);
340
341
    }
    else
342
        visiblePlaylist()->addFiles(files);
343
344

    dataChanged();
345
346
}

347
348
349
350
351
void PlaylistCollection::open(const QString &playlist, const QStringList &files)
{
    Playlist *p = playlistByName(playlist);

    if(p)
352
        p->addFiles(files);
353
354
}

355
356
void PlaylistCollection::addFolder()
{
Thiago Macieira's avatar
Thiago Macieira committed
357
    kDebug(65432) ;
358
    DirectoryList l(m_folderList, m_importPlaylists, JuK::JuKInstance());
359
360
361
362
    DirectoryList::Result result = l.exec();

    if(result.status == QDialog::Accepted) {

363
364
365
366
367
        m_dirLister.blockSignals(true);

        const bool reload = m_importPlaylists != result.addPlaylists;
        m_importPlaylists = result.addPlaylists;

368
369
370
        for(QStringList::Iterator it = result.addedDirs.begin();
            it != result.addedDirs.end(); it++)
        {
371
            m_dirLister.openUrl(KUrl::fromPath(*it), KDirLister::Keep);
372
            m_folderList.append(*it);
373
374
375
376
377
        }

        for(QStringList::Iterator it = result.removedDirs.begin();
            it !=  result.removedDirs.end(); it++)
        {
378
            m_dirLister.stop(KUrl::fromPath(*it));
379
            m_folderList.removeAll(*it);
380
381
        }

382
        if(reload)
383
            open(m_folderList);
384
        else if(!result.addedDirs.isEmpty())
385
            open(result.addedDirs);
386
387

        saveConfig();
388
389

        m_dirLister.blockSignals(false);
390
391
392
393
394
    }
}

void PlaylistCollection::rename()
{
395
    QString old = visiblePlaylist()->name();
396
397
398
    QString name = playlistNameDialog(i18n("Rename"), old, false);

    m_playlistNames.remove(old);
399

400
    if(name.isEmpty())
401
402
        return;

403
    visiblePlaylist()->setName(name);
404
405
406
407
}

void PlaylistCollection::duplicate()
{
408
409
    QString name = playlistNameDialog(i18nc("verb, copy the playlist", "Duplicate"),
                                      visiblePlaylist()->name());
410
    if(name.isEmpty())
Scott Wheeler's avatar
Scott Wheeler committed
411
        return;
412
    raise(new Playlist(this, visiblePlaylist()->items(), name));
413
414
415
416
}

void PlaylistCollection::save()
{
417
    visiblePlaylist()->save();
418
419
420
421
}

void PlaylistCollection::saveAs()
{
422
    visiblePlaylist()->saveAs();
423
424
425
426
}

void PlaylistCollection::reload()
{
427
    if(visiblePlaylist() == CollectionList::instance())
428
        CollectionList::instance()->addFiles(m_folderList);
429
    else
430
        visiblePlaylist()->slotReload();
431
432
433
434
435

}

void PlaylistCollection::editSearch()
{
436
    SearchPlaylist *p = dynamic_cast<SearchPlaylist *>(visiblePlaylist());
437
438

    if(!p)
439
        return;
440
441

    AdvancedSearchDialog::Result r =
442
        AdvancedSearchDialog(p->name(), p->playlistSearch(), JuK::JuKInstance()).exec();
443
444
445
446
447
448
449
450
451

    if(r.result == AdvancedSearchDialog::Accepted) {
        p->setPlaylistSearch(r.search);
        p->setName(r.playlistName);
    }
}

void PlaylistCollection::removeItems()
{
452
    visiblePlaylist()->slotRemoveSelectedItems();
453
454
455
456
}

void PlaylistCollection::refreshItems()
{
457
    visiblePlaylist()->slotRefresh();
458
459
460
461
}

void PlaylistCollection::renameItems()
{
462
    visiblePlaylist()->slotRenameFile();
463
464
}

465
466
void PlaylistCollection::addCovers(bool fromFile)
{
467
    visiblePlaylist()->slotAddCover(fromFile);
468
    dataChanged();
469
470
471
472
}

void PlaylistCollection::removeCovers()
{
473
    visiblePlaylist()->slotRemoveCover();
474
    dataChanged();
475
476
477
478
}

void PlaylistCollection::viewCovers()
{
479
    visiblePlaylist()->slotViewCover();
480
481
}

482
483
484
485
486
void PlaylistCollection::showCoverManager()
{
    visiblePlaylist()->slotShowCoverManager();
}

487
488
PlaylistItemList PlaylistCollection::selectedItems()
{
489
    return visiblePlaylist()->selectedItems();
490
491
492
493
}

void PlaylistCollection::scanFolders()
{
494
    CollectionList::instance()->addFiles(m_folderList);
495
496
497

    if(CollectionList::instance()->count() == 0)
        addFolder();
498
499
500
501
}

void PlaylistCollection::createPlaylist()
{
502
    QString name = playlistNameDialog();
503
    if(!name.isEmpty())
504
505
506
507
508
509
510
511
        raise(new Playlist(this, name));
}

void PlaylistCollection::createSearchPlaylist()
{
    QString name = uniquePlaylistName(i18n("Search Playlist"));

    AdvancedSearchDialog::Result r =
512
        AdvancedSearchDialog(name, PlaylistSearch(), JuK::JuKInstance()).exec();
513
514

    if(r.result == AdvancedSearchDialog::Accepted)
515
        raise(new SearchPlaylist(this, r.search, r.playlistName));
516
517
518
519
520
521
522
523
524
}

void PlaylistCollection::createFolderPlaylist()
{
    QString folder = KFileDialog::getExistingDirectory();

    if(folder.isEmpty())
        return;

Stephan Kulow's avatar
Stephan Kulow committed
525
    QString name = uniquePlaylistName(folder.mid(folder.lastIndexOf('/') + 1));
526
    name = playlistNameDialog(i18n("Create Folder Playlist"), name);
527

528
    if(!name.isEmpty())
529
530
531
532
533
        raise(new FolderPlaylist(this, folder, name));
}

void PlaylistCollection::guessTagFromFile()
{
534
    visiblePlaylist()->slotGuessTagInfo(TagGuesser::FileName);
535
536
537
538
}

void PlaylistCollection::guessTagFromInternet()
{
539
    visiblePlaylist()->slotGuessTagInfo(TagGuesser::MusicBrainz);
540
541
542
543
544
545
546
547
548
}

void PlaylistCollection::setSearchEnabled(bool enable)
{
    if(enable == m_searchEnabled)
        return;

    m_searchEnabled = enable;

549
    visiblePlaylist()->setSearchEnabled(enable);
550
551
}

552
553
554
555
556
557
558
559
560
561
562
563
564
565
HistoryPlaylist *PlaylistCollection::historyPlaylist() const
{
    return m_historyPlaylist;
}

void PlaylistCollection::setHistoryPlaylistEnabled(bool enable)
{
    if((enable && m_historyPlaylist) || (!enable && !m_historyPlaylist))
        return;

    if(enable) {
        action<KToggleAction>("showHistory")->setChecked(true);
        m_historyPlaylist = new HistoryPlaylist(this);
        m_historyPlaylist->setName(i18n("History"));
Pino Toscano's avatar
Pino Toscano committed
566
        setupPlaylist(m_historyPlaylist, "view-history");
567
568
569
570
571
572
573
    }
    else {
        delete m_historyPlaylist;
        m_historyPlaylist = 0;
    }
}

574
575
576
577
578
579
580
UpcomingPlaylist *PlaylistCollection::upcomingPlaylist() const
{
    return m_upcomingPlaylist;
}

void PlaylistCollection::setUpcomingPlaylistEnabled(bool enable)
{
581
    if((enable && m_upcomingPlaylist) || (!enable && !m_upcomingPlaylist))
582
583
584
585
        return;

    if(enable) {
        action<KToggleAction>("showUpcoming")->setChecked(true);
586
587
        if(!m_upcomingPlaylist)
            m_upcomingPlaylist = new UpcomingPlaylist(this);
588

Pino Toscano's avatar
Pino Toscano committed
589
        setupPlaylist(m_upcomingPlaylist, "go-jump-today");
590
591
    }
    else {
592
593
        action<KToggleAction>("showUpcoming")->setChecked(false);
        bool raiseCollection = m_playlistStack->visibleWidget() == m_upcomingPlaylist;
594
595
596
        delete m_upcomingPlaylist;
        m_upcomingPlaylist = 0;

597
598
599
600
        if(raiseCollection) {
            kapp->processEvents(); // Seems to stop a crash, weird.
            raise(CollectionList::instance());
        }
601
602
603
    }
}

604
605
606
607
608
609
QObject *PlaylistCollection::object() const
{
    return m_actionHandler;
}

Playlist *PlaylistCollection::currentPlaylist() const
610
{
611
612
613
    if(m_belowDistraction)
        return m_belowDistraction;

614
    if(m_upcomingPlaylist && m_upcomingPlaylist->active())
615
616
        return m_upcomingPlaylist;

617
618
619
620
621
622
623
    if(Playlist::playingItem())
        return Playlist::playingItem()->playlist();
    else
        return visiblePlaylist();
}

Playlist *PlaylistCollection::visiblePlaylist() const
624
625
626
627
{
    return static_cast<Playlist *>(m_playlistStack->visibleWidget());
}

628
629
void PlaylistCollection::raise(Playlist *playlist)
{
630
631
632
633
634
635
636
    if(m_showMorePlaylist && currentPlaylist() == m_showMorePlaylist)
        m_showMorePlaylist->lower(playlist);
    if(m_dynamicPlaylist && currentPlaylist() == m_dynamicPlaylist)
        m_dynamicPlaylist->lower(playlist);

    TrackSequenceManager::instance()->setCurrentPlaylist(playlist);
    playlist->applySharedSettings();
637
638
    playlist->setSearchEnabled(m_searchEnabled);
    m_playlistStack->raiseWidget(playlist);
639
    clearShowMore(false);
640
641
642
    dataChanged();
}

643
644
645
646
647
648
649
650
void PlaylistCollection::raiseDistraction()
{
    if(m_belowDistraction)
        return;

    m_belowDistraction = currentPlaylist();

    if(!m_distraction) {
Laurent Montel's avatar
Laurent Montel committed
651
        m_distraction = new KHBox(m_playlistStack);
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
        m_playlistStack->addWidget(m_distraction);
    }

    m_playlistStack->raiseWidget(m_distraction);
}

void PlaylistCollection::lowerDistraction()
{
    if(!m_distraction)
        return;

    if(m_belowDistraction)
        m_playlistStack->raiseWidget(m_belowDistraction);

    m_belowDistraction = 0;
}

669
670
671
672
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////

Laurent Montel's avatar
Laurent Montel committed
673
Q3WidgetStack *PlaylistCollection::playlistStack() const
674
675
676
677
678
679
{
    return m_playlistStack;
}

void PlaylistCollection::setupPlaylist(Playlist *playlist, const QString &)
{
680
    if(!playlist->fileName().isEmpty())
681
682
        m_playlistFiles.insert(playlist->fileName());

683
    if(!playlist->name().isEmpty())
684
685
        m_playlistNames.insert(playlist->name());

686
687
688
689
690
691
692
693
694
    QObject::connect(playlist, SIGNAL(selectionChanged()),
                     object(), SIGNAL(signalSelectedItemsChanged()));
}

bool PlaylistCollection::importPlaylists() const
{
    return m_importPlaylists;
}

695
696
697
698
699
bool PlaylistCollection::containsPlaylistFile(const QString &file) const
{
    return m_playlistFiles.contains(file);
}

700
701
bool PlaylistCollection::showMoreActive() const
{
702
    return visiblePlaylist() == m_showMorePlaylist;
703
704
}

705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
void PlaylistCollection::clearShowMore(bool raisePlaylist)
{
    if(!m_showMorePlaylist)
        return;

    if(raisePlaylist) {
        if(m_belowShowMorePlaylist)
            raise(m_belowShowMorePlaylist);
        else
            raise(CollectionList::instance());
    }

    m_belowShowMorePlaylist = 0;
}

720
721
void PlaylistCollection::enableDirWatch(bool enable)
{
722
723
724
725
726
    QObject *collection = CollectionList::instance();

    m_dirLister.disconnect(object());
    if(enable) {
        QObject::connect(&m_dirLister, SIGNAL(newItems(const KFileItemList &)),
727
                object(), SLOT(slotNewItems(const KFileItemList &)));
728
729
730
731
        QObject::connect(&m_dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem, KFileItem> > &)),
                collection, SLOT(slotRefreshItems(const QList<QPair<KFileItem, KFileItem> > &)));
        QObject::connect(&m_dirLister, SIGNAL(deleteItem(const KFileItem &)),
                collection, SLOT(slotDeleteItem(const KFileItem &)));
732
    }
733
734
}

735
QString PlaylistCollection::playlistNameDialog(const QString &caption,
736
737
                                               const QString &suggest,
                                               bool forceUnique) const
738
739
740
741
742
{
    bool ok;

    QString name = KInputDialog::getText(
        caption,
743
744
745
        i18n("Please enter a name for this playlist:"),
        forceUnique ? uniquePlaylistName(suggest) : suggest,
        &ok);
746

747
    return ok ? uniquePlaylistName(name) : QString();
748
749
750
751
}


QString PlaylistCollection::uniquePlaylistName(const QString &suggest) const
752
{
753
754
755
    if(suggest.isEmpty())
        return uniquePlaylistName();

756
757
758
759
760
761
    if(!m_playlistNames.contains(suggest))
        return suggest;

    QString base = suggest;
    base.remove(QRegExp("\\s\\([0-9]+\\)$"));

762
    int count = 1;
763
    QString s = QString("%1 (%2)").arg(base).arg(count);
764

765
    while(m_playlistNames.contains(s)) {
766
        count++;
767
768
769
770
771
772
        s = QString("%1 (%2)").arg(base).arg(count);
    }

    return s;
}

773
void PlaylistCollection::addNameToDict(const QString &name)
774
775
776
{
    m_playlistNames.insert(name);
}
777

778
779
780
781
782
783
void PlaylistCollection::addFileToDict(const QString &file)
{
    m_playlistFiles.insert(file);
}

void PlaylistCollection::removeNameFromDict(const QString &name)
784
785
{
    m_playlistNames.remove(name);
786
787
}

788
789
790
791
792
void PlaylistCollection::removeFileFromDict(const QString &file)
{
    m_playlistFiles.remove(file);
}

793
794
void PlaylistCollection::dirChanged(const QString &path)
{
795
    CollectionList::instance()->addFiles(QStringList(path));
796
797
}

798
799
Playlist *PlaylistCollection::playlistByName(const QString &name) const
{
800
    QList<Playlist *> l = m_playlistStack->findChildren<Playlist *>("Playlist");
801
    Playlist *list = 0;
802

803
    for(QList<Playlist *>::Iterator it  = l.begin(); it != l.end(); ++it) {
804
        Playlist *p = static_cast<Playlist*>(*it);
805
806
807
808
809
810
811
812
813
        if(p->name() == name) {
            list = p;
            break;
        }
    }

    return list;
}

814
815
void PlaylistCollection::newItems(const KFileItemList &list) const
{
816
    CollectionList::instance()->slotNewItems(list);
817
}
818

819
820
821
822
823
824
825
826
////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////

void PlaylistCollection::readConfig()
{
    KConfigGroup config(KGlobal::config(), "Playlists");

827
    m_importPlaylists  = config.readEntry("ImportPlaylists", true);
828
    m_folderList       = config.readEntry("DirectoryList", QStringList());
829

830
    for(QStringList::ConstIterator it = m_folderList.begin(); it != m_folderList.end(); ++it)
831
        m_dirLister.openUrl(*it, KDirLister::Keep);
832
833
834
835
836
837
}

void PlaylistCollection::saveConfig()
{
    KConfigGroup config(KGlobal::config(), "Playlists");
    config.writeEntry("ImportPlaylists", m_importPlaylists);
838
    config.writeEntry("showUpcoming", action("showUpcoming")->isChecked());
839
    config.writePathEntry("DirectoryList", m_folderList);
840
841

    config.sync();
842
843
844
845
846
847
848
}

////////////////////////////////////////////////////////////////////////////////
// ActionHanlder implementation
////////////////////////////////////////////////////////////////////////////////

PlaylistCollection::ActionHandler::ActionHandler(PlaylistCollection *collection) :
849
    QObject(0),
850
851
    m_collection(collection)
{
852
853
    setObjectName("ActionHandler");

854
855
856
857
    KActionMenu *menu;

    // "New" menu

858
    menu = new KActionMenu(KIcon("document-new"), i18nc("new playlist", "&New"), actions());
859
    actions()->addAction("file_new", menu);
860

Stephan Kulow's avatar
Stephan Kulow committed
861
    menu->addAction(createAction(i18n("&Empty Playlist..."), SLOT(slotCreatePlaylist()),
862
                              "newPlaylist", "window-new", KShortcut(Qt::CTRL + Qt::Key_N)));
Stephan Kulow's avatar
Stephan Kulow committed
863
    menu->addAction(createAction(i18n("&Search Playlist..."), SLOT(slotCreateSearchPlaylist()),
864
                              "newSearchPlaylist", "edit-find", KShortcut(Qt::CTRL + Qt::Key_F)));
Stephan Kulow's avatar
Stephan Kulow committed
865
    menu->addAction(createAction(i18n("Playlist From &Folder..."), SLOT(slotCreateFolderPlaylist()),
866
                              "newDirectoryPlaylist", "document-open", KShortcut(Qt::CTRL + Qt::Key_D)));
867
868
869

    // Guess tag info menu

870
#ifdef HAVE_TUNEPIMP
871
872
873
    menu = new KActionMenu(i18n("&Guess Tag Information"), actions());
    actions()->addAction("guessTag", menu);

874
    /* menu->setIcon(SmallIcon("wizard")); */
875

Stephan Kulow's avatar
Stephan Kulow committed
876
    menu->addAction(createAction(i18n("From &File Name"), SLOT(slotGuessTagFromFile()),
Pino Toscano's avatar
Pino Toscano committed
877
                              "guessTagFile", "document-import", KShortcut(Qt::CTRL + Qt::Key_G)));
Stephan Kulow's avatar
Stephan Kulow committed
878
    menu->addAction(createAction(i18n("From &Internet"), SLOT(slotGuessTagFromInternet()),
879
                              "guessTagInternet", "network-server", KShortcut(Qt::CTRL + Qt::Key_I)));
880
#else
881
    createAction(i18n("Guess Tag Information From &File Name"), SLOT(slotGuessTagFromFile()),
882
                 "guessTag", "file-import", KShortcut(Qt::CTRL + Qt::Key_F));
883
884
885
#endif


886
    createAction(i18n("Play First Track"),SLOT(slotPlayFirst()),     "playFirst");
887
    createAction(i18n("Play Next Album"), SLOT(slotPlayNextAlbum()), "forwardAlbum", "go-down-search");
888

889
890
891
892
    KStandardAction::open(this, SLOT(slotOpen()), actions());
    KStandardAction::save(this, SLOT(slotSave()), actions());
    KStandardAction::saveAs(this, SLOT(slotSaveAs()), actions());

Pino Toscano's avatar
Pino Toscano committed
893
894
    createAction(i18n("Add &Folder..."),  SLOT(slotAddFolder()),    "openDirectory", "folder-new");
    createAction(i18n("&Rename..."),      SLOT(slotRename()),       "renamePlaylist", "edit-rename");
895
896
    createAction(i18nc("verb, copy the playlist", "D&uplicate..."),
                 SLOT(slotDuplicate()),    "duplicatePlaylist", "edit-copy");
Pino Toscano's avatar
Pino Toscano committed
897
    createAction(i18n("R&emove"),         SLOT(slotRemove()),       "deleteItemPlaylist", "user-trash");
898
    createAction(i18n("Reload"),          SLOT(slotReload()),       "reloadPlaylist", "view-refresh");
Pino Toscano's avatar
Pino Toscano committed
899
    createAction(i18n("Edit Search..."),  SLOT(slotEditSearch()),   "editSearch");
900

901
902
903
    createAction(i18n("&Delete"),         SLOT(slotRemoveItems()),  "removeItem", "edit-delete");
    createAction(i18n("Refresh"),         SLOT(slotRefreshItems()), "refresh", "view-refresh");
    createAction(i18n("&Rename File"),    SLOT(slotRenameItems()),  "renameFile", "document-save-as", KShortcut(Qt::CTRL + Qt::Key_R));
904

905
906
    menu = new KActionMenu(i18n("Cover Manager"), actions());
    actions()->addAction("coverManager", menu);
907
    /* menu->setIcon(SmallIcon("image-x-generic")); */
Stephan Kulow's avatar
Stephan Kulow committed
908
    menu->addAction(createAction(i18n("&View Cover"),
909
        SLOT(slotViewCovers()), "viewCover", "document-preview"));
Stephan Kulow's avatar
Stephan Kulow committed
910
    menu->addAction(createAction(i18n("Get Cover From &File..."),
Pino Toscano's avatar
Pino Toscano committed
911
        SLOT(slotAddLocalCover()), "addCover", "document-import", KShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_F)));
Stephan Kulow's avatar
Stephan Kulow committed
912
    menu->addAction(createAction(i18n("Get Cover From &Internet..."),
913
        SLOT(slotAddInternetCover()), "webImageCover", "network-server", KShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_G)));
Stephan Kulow's avatar
Stephan Kulow committed
914
    menu->addAction(createAction(i18n("&Delete Cover"),
915
        SLOT(slotRemoveCovers()), "removeCover", "edit-delete"));
Stephan Kulow's avatar
Stephan Kulow committed
916
    menu->addAction(createAction(i18n("Show Cover &Manager"),
917
        SLOT(slotShowCoverManager()), "showCoverManager"));
918

919
    KToggleAction *historyAction =
Pino Toscano's avatar
Pino Toscano committed
920
        new KToggleAction(KIcon("view-history"), i18n("Show &History"), actions());
921
    actions()->addAction("showHistory", historyAction);
Laurent Montel's avatar
Laurent Montel committed
922
    historyAction->setCheckedState(KGuiItem(i18n("Hide &History")));
923

924
    KToggleAction *upcomingAction =
Pino Toscano's avatar
Pino Toscano committed
925
        new KToggleAction(KIcon("go-jump-today"), i18n("Show &Play Queue"), actions());
926
    actions()->addAction("showUpcoming", upcomingAction);
Laurent Montel's avatar
Laurent Montel committed
927
    upcomingAction->setCheckedState(KGuiItem(i18n("Hide &Play Queue")));
928

929
    connect(historyAction, SIGNAL(triggered(bool)),
930
            this, SLOT(slotSetHistoryPlaylistEnabled(bool)));
931
    connect(upcomingAction, SIGNAL(triggered(bool)),
932
            this, SLOT(slotSetUpcomingPlaylistEnabled(bool)));
933
934
935
936
937
938
939
940
}

KAction *PlaylistCollection::ActionHandler::createAction(const QString &text,
                                                         const char *slot,
                                                         const char *name,
                                                         const QString &icon,
                                                         const KShortcut &shortcut)
{
Stephan Kulow's avatar
Stephan Kulow committed
941
    KAction *action;
942
    if(icon.isNull())
943
        action = new KAction(text, actions());
944
    else
945
946
        action = new KAction(KIcon(icon), text, actions());
    actions()->addAction(name, action);
Stephan Kulow's avatar
Stephan Kulow committed
947
948
949
    connect( action, SIGNAL(triggered(bool)), slot);
    action->setShortcut(shortcut);
    return action;
950
951
}

Scott Wheeler's avatar
Scott Wheeler committed
952
#undef widget
953
#include "playlistcollection.moc"
954

955
// vim: set et sw=4 tw=0 sta: