File indexing completed on 2024-05-19 04:53:23

0001 /*
0002  * playlisttab.cpp
0003  *
0004  * Copyright (C) 2009-2011 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #include "../log.h"
0022 
0023 #include <KActionCollection>
0024 #include <KFileWidget>
0025 #include <QAction>
0026 #include <QBoxLayout>
0027 #include <QFileDialog>
0028 #include <QKeyEvent>
0029 #include <QListView>
0030 #include <QMenu>
0031 #include <QSplitter>
0032 #include <QStandardPaths>
0033 #include <QTimer>
0034 #include <QToolButton>
0035 
0036 #if QT_VERSION >= 0x050a00
0037 #  include <QRandomGenerator>
0038 #endif
0039 
0040 #include "playlistmodel.h"
0041 #include "playlisttab.h"
0042 
0043 PlaylistBrowserModel::PlaylistBrowserModel(PlaylistModel *playlistModel_,
0044     Playlist *temporaryPlaylist, QObject *parent) : QAbstractListModel(parent),
0045     playlistModel(playlistModel_), currentPlaylist(-1)
0046 {
0047     playlists.append(temporaryPlaylist);
0048 
0049     QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/playlistsK4"));
0050 
0051     if (!file.open(QIODevice::ReadOnly)) {
0052         file.setFileName(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/playlists"));
0053 
0054         if (!file.open(QIODevice::ReadOnly)) {
0055             qCWarning(logPlaylist, "Cannot open file %s", qPrintable(file.fileName()));
0056             return;
0057         }
0058     }
0059 
0060     QDataStream stream(&file);
0061     stream.setVersion(QDataStream::Qt_4_4);
0062 
0063     unsigned int version;
0064     stream >> version;
0065     bool hasMetadata = true;
0066     bool hasSubtitles = true;
0067 
0068     if (version == 0xc39637a1) {
0069         // compatibility code
0070         hasMetadata = false;
0071         hasSubtitles = false;
0072     } else if (version == 0x2e00f3ea) {
0073         // compatibility code
0074         hasSubtitles = false;
0075     } else if (version != 0x361c4a3c) {
0076         qCWarning(logPlaylist, "Cannot read file %s", qPrintable(file.fileName()));
0077         return;
0078     }
0079 
0080     while (!stream.atEnd()) {
0081         Playlist *playlist = new Playlist();
0082         stream >> playlist->title;
0083         QString urlString;
0084         stream >> urlString;
0085         playlist->url = urlString;
0086         int count;
0087         stream >> count;
0088 
0089         for (int i = 0; (i < count) && !stream.atEnd(); ++i) {
0090             PlaylistTrack track;
0091             stream >> urlString;
0092             track.url = urlString;
0093 
0094             if (hasMetadata) {
0095                 stream >> track.title;
0096                 stream >> track.artist;
0097                 stream >> track.album;
0098                 stream >> track.trackNumber;
0099                 stream >> track.length;
0100             } else {
0101                 track.title = track.url.fileName();
0102             }
0103 
0104             if (hasSubtitles) {
0105                 QStringList subtitleStrings;
0106                 stream >> subtitleStrings;
0107 
0108                 foreach (const QString &subtitleString, subtitleStrings) {
0109                     track.subtitles.append(subtitleString);
0110                 }
0111 
0112                 stream >> track.currentSubtitle;
0113             }
0114 
0115             playlist->tracks.append(track);
0116         }
0117 
0118         if (stream.status() != QDataStream::Ok) {
0119             qCWarning(logPlaylist, "Cannot read file %s", qPrintable(file.fileName()));
0120             delete playlist;
0121             break;
0122         }
0123 
0124         playlists.append(playlist);
0125     }
0126 }
0127 
0128 PlaylistBrowserModel::~PlaylistBrowserModel()
0129 {
0130     QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/playlistsK4"));
0131 
0132     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0133         qCWarning(logPlaylist, "Cannot open file %s", qPrintable(file.fileName()));
0134         return;
0135     }
0136 
0137     QDataStream stream(&file);
0138     stream.setVersion(QDataStream::Qt_4_4);
0139 
0140     int version = 0x361c4a3c;
0141     stream << version;
0142 
0143     for (int i = 1; i < playlists.size(); ++i) {
0144         const Playlist *playlist = playlists.at(i);
0145         stream << playlist->title;
0146         stream << playlist->url.url();
0147         stream << playlist->tracks.size();
0148 
0149         foreach (const PlaylistTrack &track, playlist->tracks) {
0150             stream << track.url.url();
0151             stream << track.title;
0152             stream << track.artist;
0153             stream << track.album;
0154             stream << track.trackNumber;
0155             stream << track.length;
0156 
0157             QStringList subtitleStrings;
0158 
0159             for (int j = 0; j < track.subtitles.size(); ++j) {
0160                 const QUrl &url = track.subtitles.at(j);
0161                 subtitleStrings.append(url.url());
0162             }
0163 
0164             stream << subtitleStrings;
0165             stream << track.currentSubtitle;
0166         }
0167     }
0168 
0169     qDeleteAll(playlists);
0170 }
0171 
0172 int PlaylistBrowserModel::rowCount(const QModelIndex &parent) const
0173 {
0174     if (parent.isValid()) {
0175         return 0;
0176     }
0177 
0178     return playlists.size();
0179 }
0180 
0181 QVariant PlaylistBrowserModel::data(const QModelIndex &index, int role) const
0182 {
0183     if (role == Qt::DecorationRole) {
0184         if (index.row() == currentPlaylist) {
0185             return QIcon::fromTheme(QLatin1String("arrow-right"), QIcon(":arrow-right"));
0186         }
0187     } else if (role == Qt::DisplayRole) {
0188         return playlists.at(index.row())->title;
0189     }
0190 
0191     return QVariant();
0192 }
0193 
0194 bool PlaylistBrowserModel::removeRows(int row, int count, const QModelIndex &parent)
0195 {
0196     if (parent.isValid()) {
0197         return false;
0198     }
0199 
0200     if (row == 0) {
0201         ++row;
0202 
0203         if ((--count) == 0) {
0204             return false;
0205         }
0206     }
0207 
0208     beginRemoveRows(QModelIndex(), row, row + count - 1);
0209     Playlist *visiblePlaylist = playlistModel->getVisiblePlaylist();
0210 
0211     for (int i = row; i < (row + count); ++i) {
0212         if (playlists.at(i) == visiblePlaylist) {
0213             if ((row + count) < playlists.size()) {
0214                 playlistModel->setVisiblePlaylist(playlists.at(row + count));
0215             } else {
0216                 playlistModel->setVisiblePlaylist(playlists.at(row - 1));
0217             }
0218         }
0219     }
0220 
0221     QList<Playlist *>::Iterator begin = playlists.begin() + row;
0222     QList<Playlist *>::Iterator end = begin + count;
0223     qDeleteAll(begin, end);
0224     playlists.erase(begin, end);
0225 
0226     if (currentPlaylist >= row) {
0227         if (currentPlaylist >= (row + count)) {
0228             currentPlaylist -= count;
0229         } else {
0230             currentPlaylist = -1;
0231             emit playTrack(NULL, -1);
0232         }
0233     }
0234 
0235     endRemoveRows();
0236     return true;
0237 }
0238 
0239 Qt::ItemFlags PlaylistBrowserModel::flags(const QModelIndex &index) const
0240 {
0241     return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
0242 }
0243 
0244 bool PlaylistBrowserModel::setData(const QModelIndex &index, const QVariant &value, int role)
0245 {
0246     if (role == Qt::EditRole) {
0247         QString title = value.toString();
0248 
0249         if (title.isEmpty()) {
0250             return false;
0251         }
0252 
0253         playlists.at(index.row())->title = title;
0254         emit dataChanged(index, index);
0255         return true;
0256     }
0257 
0258     return false;
0259 }
0260 
0261 void PlaylistBrowserModel::append(Playlist *playlist)
0262 {
0263     beginInsertRows(QModelIndex(), playlists.size(), playlists.size());
0264     playlists.append(playlist);
0265     endInsertRows();
0266 }
0267 
0268 Playlist *PlaylistBrowserModel::getPlaylist(int row) const
0269 {
0270     return playlists.at(row);
0271 }
0272 
0273 void PlaylistBrowserModel::setCurrentPlaylist(Playlist *playlist)
0274 {
0275     int oldPlaylist = currentPlaylist;
0276     currentPlaylist = playlists.indexOf(playlist);
0277 
0278     if (oldPlaylist == currentPlaylist) {
0279         return;
0280     }
0281 
0282     if (oldPlaylist != -1) {
0283         playlistModel->setCurrentTrack(playlists.at(oldPlaylist), -1);
0284         QModelIndex modelIndex = index(oldPlaylist, 0);
0285         emit dataChanged(modelIndex, modelIndex);
0286     }
0287 
0288     if (currentPlaylist != -1) {
0289         QModelIndex modelIndex = index(currentPlaylist, 0);
0290         emit dataChanged(modelIndex, modelIndex);
0291     }
0292 }
0293 
0294 Playlist *PlaylistBrowserModel::getCurrentPlaylist() const
0295 {
0296     if (currentPlaylist >= 0) {
0297         return playlists.at(currentPlaylist);
0298     } else {
0299         return playlistModel->getVisiblePlaylist();
0300     }
0301 }
0302 
0303 class PlaylistBrowserView : public QListView
0304 {
0305 public:
0306     explicit PlaylistBrowserView(QWidget *parent) : QListView(parent) { }
0307     ~PlaylistBrowserView() { }
0308 
0309 protected:
0310     void keyPressEvent(QKeyEvent *event) override;
0311 };
0312 
0313 void PlaylistBrowserView::keyPressEvent(QKeyEvent *event)
0314 {
0315     if (event->key() == Qt::Key_Delete) {
0316         QModelIndexList selectedRows = selectionModel()->selectedRows();
0317         std::sort(selectedRows.begin(), selectedRows.end());
0318 
0319         for (int i = selectedRows.size() - 1; i >= 0; --i) {
0320             // FIXME compress
0321             model()->removeRows(selectedRows.at(i).row(), 1);
0322         }
0323 
0324         return;
0325     }
0326 
0327     QListView::keyPressEvent(event);
0328 }
0329 
0330 PlaylistView::PlaylistView(QWidget *parent) : QTreeView(parent)
0331 {
0332 }
0333 
0334 PlaylistView::~PlaylistView()
0335 {
0336 }
0337 
0338 void PlaylistView::removeSelectedRows()
0339 {
0340     QModelIndexList selectedRows = selectionModel()->selectedRows();
0341     std::sort(selectedRows.begin(), selectedRows.end());
0342 
0343     for (int i = selectedRows.size() - 1; i >= 0; --i) {
0344         // FIXME compress
0345         model()->removeRows(selectedRows.at(i).row(), 1);
0346     }
0347 }
0348 
0349 void PlaylistView::contextMenuEvent(QContextMenuEvent *event)
0350 {
0351     if (!currentIndex().isValid()) {
0352           return;
0353     }
0354 
0355     QMenu::exec(actions(), event->globalPos());
0356 }
0357 
0358 void PlaylistView::keyPressEvent(QKeyEvent *event)
0359 {
0360     if (event->key() == Qt::Key_Delete) {
0361         removeSelectedRows();
0362         return;
0363     }
0364 
0365     QTreeView::keyPressEvent(event);
0366 }
0367 
0368 PlaylistTab::PlaylistTab(QMenu *menu, KActionCollection *collection, MediaWidget *mediaWidget_) :
0369     mediaWidget(mediaWidget_)
0370 {
0371     Playlist *temporaryPlaylist = new Playlist();
0372     temporaryPlaylist->title = i18nc("playlist browser", "Temporary Playlist");
0373 
0374     playlistModel = new PlaylistModel(temporaryPlaylist, this);
0375     connect(playlistModel, SIGNAL(playTrack(Playlist*,int)),
0376         this, SLOT(playTrack(Playlist*,int)));
0377     connect(playlistModel, SIGNAL(appendPlaylist(Playlist*,bool)),
0378         this, SLOT(appendPlaylist(Playlist*,bool)));
0379 
0380     playlistBrowserModel = new PlaylistBrowserModel(playlistModel, temporaryPlaylist, this);
0381     playlistModel->setVisiblePlaylist(playlistBrowserModel->getPlaylist(0));
0382     connect(playlistBrowserModel, SIGNAL(playTrack(Playlist*,int)),
0383         this, SLOT(playTrack(Playlist*,int)));
0384 
0385     connect(mediaWidget, SIGNAL(playlistPrevious()), this, SLOT(playPreviousTrack()));
0386     connect(mediaWidget, SIGNAL(playlistNext()), this, SLOT(playNextTrack()));
0387     connect(mediaWidget, SIGNAL(playlistUrlsDropped(QList<QUrl>)),
0388         this, SLOT(appendUrls(QList<QUrl>)));
0389     connect(mediaWidget, SIGNAL(playlistTrackLengthChanged(int)),
0390         this, SLOT(updateTrackLength(int)));
0391     connect(mediaWidget,
0392         SIGNAL(playlistTrackMetadataChanged(QMap<MediaWidget::MetadataType,QString>)),
0393         this, SLOT(updateTrackMetadata(QMap<MediaWidget::MetadataType,QString>)));
0394 
0395     repeatAction = new QAction(QIcon::fromTheme(QLatin1String("media-playlist-repeat"), QIcon(":media-playlist-repeat")),
0396         i18nc("playlist menu", "Repeat"), this);
0397     repeatAction->setCheckable(true);
0398     menu->addAction(collection->addAction(QLatin1String("playlist_repeat"), repeatAction));
0399 
0400     randomAction = new QAction(QIcon::fromTheme(QLatin1String("media-playlist-shuffle"), QIcon(":media-playlist-shuffle")),
0401         i18nc("playlist menu", "Random"), this);
0402     randomAction->setCheckable(true);
0403     menu->addAction(collection->addAction(QLatin1String("playlist_random"), randomAction));
0404 
0405     QAction *addSubtitleAction =
0406         new QAction(QIcon::fromTheme(QLatin1String("application-x-subrip"), QIcon(":application-x-subrip")), i18n("Add Subtitle"), this);
0407     collection->addAction(QLatin1String("playlist_add_subtitle"), addSubtitleAction);
0408 
0409     QAction *removeTrackAction =
0410         new QAction(QIcon::fromTheme(QLatin1String("edit-delete"), QIcon(":edit-delete")), i18nc("@action", "Remove"), this);
0411     collection->addAction(QLatin1String("playlist_remove_track"), removeTrackAction);
0412 
0413     QAction *clearAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear-list"), QIcon(":edit-clear-list")),
0414         i18nc("remove all items from a list", "Clear"), this);
0415     connect(clearAction, SIGNAL(triggered(bool)), playlistModel, SLOT(clearVisiblePlaylist()));
0416     menu->addAction(collection->addAction(QLatin1String("playlist_clear"), clearAction));
0417 
0418     menu->addSeparator();
0419 
0420     QAction *newAction = new QAction(QIcon::fromTheme(QLatin1String("list-add"), QIcon(":list-add")), i18nc("@action:inmenu playlist", "New"), this);
0421     connect(newAction, SIGNAL(triggered(bool)), this, SLOT(newPlaylist()));
0422     menu->addAction(collection->addAction(QLatin1String("playlist_new"), newAction));
0423 
0424     QAction *renameAction = new QAction(QIcon::fromTheme(QLatin1String("edit-rename"), QIcon(":edit-rename")),
0425         i18nc("rename an entry in a list", "Rename"), this);
0426     connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(renamePlaylist()));
0427     menu->addAction(collection->addAction(QLatin1String("playlist_rename"), renameAction));
0428 
0429     QAction *removePlaylistAction =
0430         new QAction(QIcon::fromTheme(QLatin1String("edit-delete"), QIcon(":edit-delete")), i18nc("@action", "Remove"), this);
0431     connect(removePlaylistAction, SIGNAL(triggered(bool)), this, SLOT(removePlaylist()));
0432     menu->addAction(collection->addAction(QLatin1String("playlist_remove"), removePlaylistAction));
0433 
0434     QAction *savePlaylistAction = KStandardAction::save(this, SLOT(savePlaylist()), this);
0435     menu->addAction(collection->addAction(QLatin1String("playlist_save"), savePlaylistAction));
0436 
0437     QAction *savePlaylistAsAction =
0438         KStandardAction::saveAs(this, SLOT(savePlaylistAs()), this);
0439     menu->addAction(collection->addAction(QLatin1String("playlist_save_as"), savePlaylistAsAction));
0440 
0441     QBoxLayout *widgetLayout = new QHBoxLayout(this);
0442     widgetLayout->setMargin(0);
0443 
0444     QSplitter *horizontalSplitter = new QSplitter(this);
0445     widgetLayout->addWidget(horizontalSplitter);
0446 
0447     QSplitter *verticalSplitter = new QSplitter(Qt::Vertical, horizontalSplitter);
0448 
0449     QWidget *widget = new QWidget(verticalSplitter);
0450     QBoxLayout *sideLayout = new QVBoxLayout(widget);
0451     sideLayout->setMargin(0);
0452 
0453     QBoxLayout *boxLayout = new QHBoxLayout();
0454 
0455     QToolButton *toolButton = new QToolButton(widget);
0456     toolButton->setDefaultAction(newAction);
0457     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0458     boxLayout->addWidget(toolButton);
0459 
0460     toolButton = new QToolButton(widget);
0461     toolButton->setDefaultAction(renameAction);
0462     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0463     boxLayout->addWidget(toolButton);
0464 
0465     toolButton = new QToolButton(widget);
0466     toolButton->setDefaultAction(removePlaylistAction);
0467     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0468     boxLayout->addWidget(toolButton);
0469 
0470     toolButton = new QToolButton(widget);
0471     toolButton->setDefaultAction(savePlaylistAction);
0472     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0473     boxLayout->addWidget(toolButton);
0474 
0475     toolButton = new QToolButton(widget);
0476     toolButton->setDefaultAction(savePlaylistAsAction);
0477     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0478     boxLayout->addWidget(toolButton);
0479 
0480     boxLayout->addStretch();
0481     sideLayout->addLayout(boxLayout);
0482 
0483     playlistBrowserView = new PlaylistBrowserView(widget);
0484     playlistBrowserView->addAction(newAction);
0485     playlistBrowserView->addAction(renameAction);
0486     playlistBrowserView->addAction(removePlaylistAction);
0487     playlistBrowserView->addAction(savePlaylistAction);
0488     playlistBrowserView->addAction(savePlaylistAsAction);
0489     playlistBrowserView->setContextMenuPolicy(Qt::ActionsContextMenu);
0490     playlistBrowserView->setModel(playlistBrowserModel);
0491     connect(playlistBrowserView, SIGNAL(activated(QModelIndex)),
0492         this, SLOT(playlistActivated(QModelIndex)));
0493     sideLayout->addWidget(playlistBrowserView);
0494 
0495     // KFileWidget creates a local event loop which can cause bad side
0496     // effects (because the main window isn't fully constructed yet)
0497     fileWidgetSplitter = verticalSplitter;
0498     QTimer::singleShot(0, this, SLOT(createFileWidget()));
0499 
0500     verticalSplitter = new QSplitter(Qt::Vertical, horizontalSplitter);
0501 
0502     widget = new QWidget(verticalSplitter);
0503     sideLayout = new QVBoxLayout(widget);
0504     sideLayout->setMargin(0);
0505 
0506     boxLayout = new QHBoxLayout();
0507 
0508     toolButton = new QToolButton(widget);
0509     toolButton->setDefaultAction(repeatAction);
0510     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0511     boxLayout->addWidget(toolButton);
0512 
0513     toolButton = new QToolButton(widget);
0514     toolButton->setDefaultAction(randomAction);
0515     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0516     boxLayout->addWidget(toolButton);
0517 
0518     toolButton = new QToolButton(widget);
0519     toolButton->setDefaultAction(removeTrackAction);
0520     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0521     boxLayout->addWidget(toolButton);
0522 
0523     toolButton = new QToolButton(widget);
0524     toolButton->setDefaultAction(clearAction);
0525     toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0526     boxLayout->addWidget(toolButton);
0527 
0528     boxLayout->addStretch();
0529     sideLayout->addLayout(boxLayout);
0530 
0531     playlistView = new PlaylistView(widget);
0532     playlistView->setAlternatingRowColors(true);
0533     playlistView->setDragDropMode(QAbstractItemView::DragDrop);
0534     playlistView->setRootIsDecorated(false);
0535     playlistView->setSelectionMode(QAbstractItemView::ExtendedSelection);
0536     playlistView->setModel(playlistModel);
0537     playlistView->sortByColumn(-1, Qt::AscendingOrder);
0538     playlistView->setSortingEnabled(true);
0539     playlistView->addAction(addSubtitleAction);
0540     connect(addSubtitleAction, SIGNAL(triggered(bool)), this, SLOT(addSubtitle()));
0541     playlistView->addAction(removeTrackAction);
0542     connect(removeTrackAction, SIGNAL(triggered(bool)),
0543         playlistView, SLOT(removeSelectedRows()));
0544     connect(playlistView, SIGNAL(activated(QModelIndex)),
0545         this, SLOT(playTrack(QModelIndex)));
0546     sideLayout->addWidget(playlistView);
0547 
0548     QWidget *mediaContainer = new QWidget(verticalSplitter);
0549     mediaLayout = new QHBoxLayout(mediaContainer);
0550     mediaLayout->setMargin(0);
0551 
0552     verticalSplitter->setStretchFactor(1, 1);
0553     horizontalSplitter->setStretchFactor(1, 1);
0554 }
0555 
0556 PlaylistTab::~PlaylistTab()
0557 {
0558 }
0559 
0560 void PlaylistTab::appendToCurrentPlaylist(const QList<QUrl> &urls, bool playImmediately)
0561 {
0562     playlistModel->appendUrls(playlistBrowserModel->getCurrentPlaylist(), urls,
0563         playImmediately);
0564 }
0565 
0566 void PlaylistTab::appendToVisiblePlaylist(const QList<QUrl> &urls, bool playImmediately)
0567 {
0568     playlistModel->appendUrls(playlistModel->getVisiblePlaylist(), urls, playImmediately);
0569 }
0570 
0571 void PlaylistTab::removeTrack(int row)
0572 {
0573     Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist();
0574 
0575     if ((row >= 0) && (row < currentPlaylist->tracks.size())) {
0576         playlistModel->removeRows(currentPlaylist, row, 1);
0577     }
0578 }
0579 
0580 void PlaylistTab::setRandom(bool random)
0581 {
0582     randomAction->setChecked(random);
0583 }
0584 
0585 void PlaylistTab::setRepeat(bool repeat)
0586 {
0587     repeatAction->setChecked(repeat);
0588 }
0589 
0590 int PlaylistTab::getCurrentTrack() const
0591 {
0592     const Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist();
0593 
0594     if (currentPlaylist->currentTrack >= 0) {
0595         return currentPlaylist->currentTrack;
0596     } else {
0597         return 0;
0598     }
0599 }
0600 
0601 int PlaylistTab::getTrackCount() const
0602 {
0603     return playlistBrowserModel->getCurrentPlaylist()->tracks.size();
0604 }
0605 
0606 bool PlaylistTab::getRandom() const
0607 {
0608     return randomAction->isChecked();
0609 }
0610 
0611 bool PlaylistTab::getRepeat() const
0612 {
0613     return repeatAction->isChecked();
0614 }
0615 
0616 void PlaylistTab::createFileWidget()
0617 {
0618     KFileWidget *fileWidget = new KFileWidget(QUrl(), fileWidgetSplitter);
0619     fileWidget->setFilter(MediaWidget::extensionFilter());
0620     fileWidget->setMode(KFile::Files | KFile::ExistingOnly);
0621     fileWidgetSplitter->setStretchFactor(1, 1);
0622 
0623     // KFileWidget creates a QUrlComboBox without layout (!), which steals the focus:
0624     //     kDebug() << QApplication::focusWidget();
0625     //     kDebug() << QApplication::focusWidget()->layout();
0626     // Let's reclaim the focus (and give it back to the main window).
0627     // FIXME report issue
0628     window()->setFocus();
0629 }
0630 
0631 void PlaylistTab::newPlaylist()
0632 {
0633     Playlist *playlist = new Playlist();
0634     playlist->title = i18nc("playlist browser", "Unnamed Playlist");
0635     playlistBrowserModel->append(playlist);
0636 }
0637 
0638 void PlaylistTab::renamePlaylist()
0639 {
0640     QModelIndex index = playlistBrowserView->currentIndex();
0641 
0642     if (!index.isValid() || (index.row() == 0)) {
0643         return;
0644     }
0645 
0646     playlistBrowserView->edit(index);
0647 }
0648 
0649 void PlaylistTab::removePlaylist()
0650 {
0651     QModelIndexList selectedRows = playlistBrowserView->selectionModel()->selectedRows();
0652     std::sort(selectedRows.begin(), selectedRows.end());
0653 
0654     for (int i = selectedRows.size() - 1; i >= 0; --i) {
0655         // FIXME compress
0656         playlistBrowserModel->removeRows(selectedRows.at(i).row(), 1);
0657     }
0658 }
0659 
0660 void PlaylistTab::savePlaylist()
0661 {
0662     savePlaylist(false);
0663 }
0664 
0665 void PlaylistTab::savePlaylistAs()
0666 {
0667     savePlaylist(true);
0668 }
0669 
0670 void PlaylistTab::addSubtitle()
0671 {
0672     QModelIndexList selectedRows = playlistView->selectionModel()->selectedRows();
0673 
0674     if (selectedRows.size() != 1) {
0675         return;
0676     }
0677 
0678     int row = selectedRows.at(0).row();
0679     Playlist *playlist = playlistModel->getVisiblePlaylist();
0680     QList<QUrl> urls = QFileDialog::getOpenFileUrls(this, "", QUrl(), subtitleExtensionFilter());
0681 
0682     if ((row < playlist->tracks.size()) && !urls.isEmpty()) {
0683         PlaylistTrack &track = playlist->tracks[row];
0684         track.subtitles += urls;
0685 
0686         if (track.subtitles.size() == urls.size()) {
0687             track.currentSubtitle = 0;
0688         }
0689 
0690         if ((playlist == playlistBrowserModel->getCurrentPlaylist()) &&
0691             (playlist->currentTrack == row)) {
0692             // FIXME !
0693         }
0694     }
0695 }
0696 
0697 void PlaylistTab::playlistActivated(const QModelIndex &index)
0698 {
0699     playlistModel->setVisiblePlaylist(playlistBrowserModel->getPlaylist(index.row()));
0700 }
0701 
0702 void PlaylistTab::playPreviousTrack()
0703 {
0704     Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist();
0705     playTrack(currentPlaylist, currentPlaylist->currentTrack - 1);
0706 }
0707 
0708 void PlaylistTab::playCurrentTrack()
0709 {
0710     Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist();
0711 
0712     if (currentPlaylist->currentTrack >= 0) {
0713         playTrack(currentPlaylist, currentPlaylist->currentTrack);
0714     } else {
0715         playNextTrack();
0716     }
0717 }
0718 
0719 void PlaylistTab::playNextTrack()
0720 {
0721     Playlist *currentPlaylist = playlistBrowserModel->getCurrentPlaylist();
0722 
0723     if (randomAction->isChecked()) {
0724         int size = currentPlaylist->tracks.size();
0725 
0726         if (size < 2) {
0727             playTrack(currentPlaylist, 0);
0728         } else if (currentPlaylist->currentTrack != -1) {
0729 #if QT_VERSION >= 0x050a00
0730             quint32 rand = QRandomGenerator::global()->generate();
0731 #else
0732             quint32 rand = qrand();
0733 #endif
0734             int track = (rand % (size - 1));
0735 
0736             if (track >= currentPlaylist->currentTrack) {
0737                 ++track;
0738             }
0739 
0740             playTrack(currentPlaylist, track);
0741         } else {
0742 #if QT_VERSION >= 0x050a00
0743             quint32 rand = QRandomGenerator::global()->generate();
0744 #else
0745             quint32 rand = qrand();
0746 #endif
0747             playTrack(currentPlaylist, rand % size);
0748         }
0749     } else if (((currentPlaylist->currentTrack + 1) == currentPlaylist->tracks.size()) &&
0750            repeatAction->isChecked()) {
0751         playTrack(currentPlaylist, 0);
0752     } else {
0753         playTrack(currentPlaylist, currentPlaylist->currentTrack + 1);
0754     }
0755 }
0756 
0757 void PlaylistTab::activate()
0758 {
0759     mediaLayout->addWidget(mediaWidget);
0760 }
0761 
0762 void PlaylistTab::playTrack(const QModelIndex &index)
0763 {
0764     playTrack(playlistModel->getVisiblePlaylist(), index.row());
0765 }
0766 
0767 void PlaylistTab::playTrack(Playlist *playlist, int track)
0768 {
0769     if ((track < 0) || (playlist == NULL) || (track >= playlist->tracks.size())) {
0770         track = -1;
0771     }
0772 
0773     if (track != -1) {
0774         PlaylistTrack &playlistTrack = playlist->tracks[track];
0775         QUrl subtitleUrl;
0776 
0777         if ((playlistTrack.currentSubtitle >= 0) &&
0778             (playlistTrack.currentSubtitle < playlistTrack.subtitles.size())) {
0779             subtitleUrl = playlistTrack.subtitles.at(playlistTrack.currentSubtitle);
0780         } else if (playlistTrack.subtitles.isEmpty()) {
0781             // check whether there's a possible subtitle file candidate
0782             QString localFile = playlistTrack.url.toLocalFile();
0783             localFile.truncate(localFile.lastIndexOf(QLatin1Char('.')));
0784 
0785             if (!localFile.isEmpty()) {
0786                 if (QFile::exists(localFile + QLatin1String(".asc"))) {
0787                     subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".asc"));
0788                 } else if (QFile::exists(localFile + QLatin1String(".smi"))) {
0789                     subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".smi"));
0790                 } else if (QFile::exists(localFile + QLatin1String(".srt"))) {
0791                     subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".srt"));
0792                 } else if (QFile::exists(localFile + QLatin1String(".ssa"))) {
0793                     subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".ssa"));
0794                 } else if (QFile::exists(localFile + QLatin1String(".sub"))) {
0795                     subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".sub"));
0796                 } else if (QFile::exists(localFile + QLatin1String(".txt"))) {
0797                     subtitleUrl = QUrl::fromLocalFile(localFile + QLatin1String(".txt"));
0798                 }
0799             }
0800 
0801             if (subtitleUrl.isValid()) {
0802                 playlistTrack.subtitles += subtitleUrl;
0803                 playlistTrack.currentSubtitle = 0;
0804             }
0805         }
0806 
0807         mediaWidget->play(playlistTrack.url, subtitleUrl);
0808         // FIXME !
0809         playlistBrowserModel->setCurrentPlaylist(playlist);
0810     } else {
0811         mediaWidget->stop();
0812     }
0813 
0814     if (playlist != NULL) {
0815         playlistModel->setCurrentTrack(playlist, track);
0816     }
0817 }
0818 
0819 void PlaylistTab::appendUrls(const QList<QUrl> &urls)
0820 {
0821     playlistModel->appendUrls(playlistModel->getVisiblePlaylist(), urls, true);
0822 }
0823 
0824 void PlaylistTab::appendPlaylist(Playlist *playlist, bool playImmediately)
0825 {
0826     playlistBrowserModel->append(playlist);
0827 
0828     if (playImmediately) {
0829         playlistBrowserModel->setCurrentPlaylist(playlist);
0830         playlistModel->setVisiblePlaylist(playlist);
0831         playNextTrack();
0832     }
0833 }
0834 
0835 void PlaylistTab::updateTrackLength(int length)
0836 {
0837     playlistModel->updateTrackLength(playlistBrowserModel->getCurrentPlaylist(), length);
0838 }
0839 
0840 void PlaylistTab::updateTrackMetadata(const QMap<MediaWidget::MetadataType, QString> &metadata)
0841 {
0842     playlistModel->updateTrackMetadata(playlistBrowserModel->getCurrentPlaylist(), metadata);
0843 }
0844 
0845 QString PlaylistTab::subtitleExtensionFilter()
0846 {
0847     return QString(QLatin1String("*.asc *.smi *.srt *.ssa *.sub *.txt|")) +
0848         i18nc("file filter", "Subtitle Files");
0849 }
0850 
0851 void PlaylistTab::savePlaylist(bool askName)
0852 {
0853     QModelIndex index = playlistBrowserView->currentIndex();
0854 
0855     if (!index.isValid()) {
0856         return;
0857     }
0858 
0859     Playlist *playlist = playlistBrowserModel->getPlaylist(index.row());
0860     QUrl url = playlist->url;
0861 
0862     if (askName || !url.isValid() ||
0863         url.fileName().endsWith(QLatin1String(".kaffeine"), Qt::CaseInsensitive)) {
0864         url = QFileDialog::getSaveFileUrl(this, "", QUrl(), i18nc("file filter",
0865             "*.xspf|XSPF Playlist\n*.m3u|M3U Playlist\n*.pls|PLS Playlist"));
0866 
0867         if (!url.isValid()) {
0868             return;
0869         }
0870 
0871         playlist->url = url;
0872     }
0873 
0874     QString fileName = url.fileName();
0875     Playlist::Format format = Playlist::Invalid;
0876 
0877     if (fileName.endsWith(QLatin1String(".m3u"), Qt::CaseInsensitive)) {
0878         format = Playlist::M3U;
0879     } else if (fileName.endsWith(QLatin1String(".pls"), Qt::CaseInsensitive)) {
0880         format = Playlist::PLS;
0881     } else if (fileName.endsWith(QLatin1String(".xspf"), Qt::CaseInsensitive)) {
0882         format = Playlist::XSPF;
0883     }
0884 
0885     if (format != Playlist::Invalid) {
0886         playlist->save(format);
0887     }
0888 }
0889 
0890 #include "moc_playlisttab.cpp"