1 /* $BEGIN_LICENSE
2 
3 This file is part of Musique.
4 Copyright 2013, Flavio Tordini <flavio.tordini@gmail.com>
5 
6 Musique is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 Musique is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with Musique.  If not, see <http://www.gnu.org/licenses/>.
18 
19 $END_LICENSE */
20 
21 #include "playlistview.h"
22 #include "datautils.h"
23 #include "droparea.h"
24 #include "fontutils.h"
25 #include "globalshortcuts.h"
26 #include "mainwindow.h"
27 #include "model/track.h"
28 #include "playlistitemdelegate.h"
29 #include "playlistmodel.h"
30 
PlaylistView(QWidget * parent)31 PlaylistView::PlaylistView(QWidget *parent)
32     : QListView(parent), playlistModel(nullptr), overlayLabel(nullptr) {
33     // delegate
34     setItemDelegate(new PlaylistItemDelegate(this));
35 
36     // cosmetics
37     setMinimumWidth(fontInfo().pixelSize() * 25);
38     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
39     setFrameShape(QFrame::NoFrame);
40     setAttribute(Qt::WA_MacShowFocusRect, false);
41 
42     // behaviour
43     setSelectionMode(QAbstractItemView::ExtendedSelection);
44 
45     // dragndrop
46     setDropIndicatorShown(true);
47     setDragDropMode(QAbstractItemView::DragDrop);
48 
49     // actions
50     connect(MainWindow::instance()->getAction("remove"), SIGNAL(triggered()),
51             SLOT(removeSelected()));
52     connect(MainWindow::instance()->getAction("moveUp"), SIGNAL(triggered()),
53             SLOT(moveUpSelected()));
54     connect(MainWindow::instance()->getAction("moveDown"), SIGNAL(triggered()),
55             SLOT(moveDownSelected()));
56 
57     // respond to the user doubleclicking a playlist item
58     connect(this, SIGNAL(activated(const QModelIndex &)), SLOT(itemActivated(const QModelIndex &)));
59 }
60 
setPlaylistModel(PlaylistModel * playlistModel)61 void PlaylistView::setPlaylistModel(PlaylistModel *playlistModel) {
62     this->playlistModel = playlistModel;
63     setModel(playlistModel);
64 
65     // needed to restore the selection after dragndrop
66     connect(playlistModel, SIGNAL(needSelectionFor(QVector<Track *>)),
67             SLOT(selectTracks(QVector<Track *>)));
68 
69     connect(selectionModel(),
70             SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
71             SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
72     connect(playlistModel, SIGNAL(layoutChanged()), this, SLOT(updatePlaylistActions()));
73     connect(playlistModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this,
74             SLOT(updatePlaylistActions()));
75     connect(playlistModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this,
76             SLOT(updatePlaylistActions()));
77     connect(playlistModel, SIGNAL(modelReset()), SLOT(updatePlaylistActions()));
78     connect(MainWindow::instance()->getAction("clearPlaylist"), SIGNAL(triggered()), playlistModel,
79             SLOT(clear()));
80     connect(MainWindow::instance()->getAction("skip"), SIGNAL(triggered()), playlistModel,
81             SLOT(skipForward()));
82     connect(MainWindow::instance()->getAction("previous"), SIGNAL(triggered()), playlistModel,
83             SLOT(skipBackward()));
84 
85     GlobalShortcuts &shortcuts = GlobalShortcuts::instance();
86     connect(&shortcuts, SIGNAL(Next()), MainWindow::instance()->getAction("skip"), SLOT(trigger()));
87     connect(&shortcuts, SIGNAL(Previous()), MainWindow::instance()->getAction("previous"),
88             SLOT(trigger()));
89 }
90 
itemActivated(const QModelIndex & index)91 void PlaylistView::itemActivated(const QModelIndex &index) {
92     if (playlistModel->rowExists(index.row())) playlistModel->setActiveRow(index.row(), true);
93 }
94 
removeSelected()95 void PlaylistView::removeSelected() {
96     if (!this->selectionModel()->hasSelection()) return;
97     QModelIndexList indexes = this->selectionModel()->selectedIndexes();
98     playlistModel->removeIndexes(indexes);
99 }
100 
selectTracks(const QVector<Track * > & tracks)101 void PlaylistView::selectTracks(const QVector<Track *> &tracks) {
102     selectionModel()->clear();
103     for (Track *track : tracks) {
104         QModelIndex index = playlistModel->indexForTrack(track);
105         if (index.isValid())
106             selectionModel()->select(index, QItemSelectionModel::Select);
107         else
108             qDebug() << __PRETTY_FUNCTION__ << "Invalid index";
109     }
110 }
111 
selectionChanged(const QItemSelection & selected,const QItemSelection & deselected)112 void PlaylistView::selectionChanged(const QItemSelection &selected,
113                                     const QItemSelection &deselected) {
114     QListView::selectionChanged(selected, deselected);
115 
116     const bool gotSelection = this->selectionModel()->hasSelection();
117     MainWindow::instance()->getAction("remove")->setEnabled(gotSelection);
118     MainWindow::instance()->getAction("moveUp")->setEnabled(gotSelection);
119     MainWindow::instance()->getAction("moveDown")->setEnabled(gotSelection);
120 }
121 
updatePlaylistActions()122 void PlaylistView::updatePlaylistActions() {
123     const int rowCount = playlistModel->rowCount(QModelIndex());
124     const bool isPlaylistEmpty = rowCount < 1;
125     MainWindow::instance()->getAction("clearPlaylist")->setEnabled(!isPlaylistEmpty);
126     if (!isPlaylistEmpty) MainWindow::instance()->getAction("play")->setEnabled(true);
127 
128     // TODO also check if we're on first/last track
129     MainWindow::instance()->getAction("skip")->setEnabled(rowCount > 1);
130     MainWindow::instance()->getAction("previous")->setEnabled(rowCount > 1);
131 
132     if (isPlaylistEmpty) {
133         setStatusTip(tr("Playlist is empty"));
134     } else {
135         const int totalLength = playlistModel->getTotalLength();
136         QString playlistLength = DataUtils::formatDuration(totalLength);
137         setStatusTip(tr("%1 tracks - Total length is %2")
138                              .arg(QString::number(rowCount), playlistLength));
139     }
140 }
141 
moveUpSelected()142 void PlaylistView::moveUpSelected() {
143     if (!this->selectionModel()->hasSelection()) return;
144     QModelIndexList indexes = this->selectionModel()->selectedIndexes();
145     playlistModel->move(indexes, true);
146 }
147 
moveDownSelected()148 void PlaylistView::moveDownSelected() {
149     if (!this->selectionModel()->hasSelection()) return;
150     QModelIndexList indexes = this->selectionModel()->selectedIndexes();
151     playlistModel->move(indexes, false);
152 }
153 
154 /*
155 void PlaylistView::dragEnterEvent(QDragEnterEvent *event) {
156     QListView::dragEnterEvent(event);
157 
158     qDebug() << "dragEnter";
159     if (verticalScrollBar()->isVisible()) {
160         qDebug() << "need drop area";
161         // emit needDropArea();
162         dropArea->show();
163         willHideDropArea = false;
164     }
165 }
166 
167 
168 void PlaylistView::dragLeaveEvent(QDragLeaveEvent *event) {
169     QListView::dragLeaveEvent(event);
170 
171     qDebug() << "dragLeave";
172     // emit DropArea();
173     willHideDropArea = true;
174     QTimer::singleShot(1000, dropArea, SLOT(hide()));
175 }
176 */
177 
paintEvent(QPaintEvent * event)178 void PlaylistView::paintEvent(QPaintEvent *event) {
179     QListView::paintEvent(event);
180 
181     if (playlistModel->rowCount() == 0 && !emptyMessage.isEmpty()) {
182         event->accept();
183 
184         QPainter painter(this->viewport());
185         QPen textPen;
186         textPen.setBrush(palette().windowText());
187         painter.setOpacity(.5);
188         painter.setPen(textPen);
189         painter.setFont(FontUtils::big());
190 
191         QSize textSize(QFontMetrics(painter.font()).size(Qt::TextSingleLine, emptyMessage));
192         QPoint centerPoint((this->width() - textSize.width()) / 2,
193                            ((this->height() - textSize.height()) / 2));
194         QRect centerRect(centerPoint, textSize);
195         QRect boundRect;
196         painter.drawText(centerRect, Qt::AlignCenter, emptyMessage, &boundRect);
197     }
198 }
199