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