2010-03-24 00:11:46 +01:00
|
|
|
/* This file is part of Clementine.
|
2010-11-20 14:27:10 +01:00
|
|
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
2010-03-24 00:11:46 +01:00
|
|
|
|
|
|
|
Clementine 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 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2012-05-27 22:15:58 +02:00
|
|
|
#include "config.h"
|
2010-11-20 21:00:40 +01:00
|
|
|
#include "dynamicplaylistcontrols.h"
|
2009-12-24 20:16:07 +01:00
|
|
|
#include "playlist.h"
|
2010-03-29 18:59:01 +02:00
|
|
|
#include "playlistdelegates.h"
|
2011-04-28 19:50:45 +02:00
|
|
|
#include "playlistheader.h"
|
|
|
|
#include "playlistview.h"
|
2012-03-03 01:20:37 +01:00
|
|
|
#include "core/application.h"
|
2011-07-24 13:46:31 +02:00
|
|
|
#include "core/logging.h"
|
2012-03-13 22:59:18 +01:00
|
|
|
#include "core/player.h"
|
|
|
|
#include "covers/currentartloader.h"
|
2012-11-10 08:04:55 +01:00
|
|
|
#include "ui/qt_blurimage.h"
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-09-07 00:49:15 +02:00
|
|
|
#include <QCleanlooksStyle>
|
2012-01-29 20:24:13 +01:00
|
|
|
#include <QClipboard>
|
2009-12-24 20:16:07 +01:00
|
|
|
#include <QPainter>
|
|
|
|
#include <QHeaderView>
|
|
|
|
#include <QSettings>
|
|
|
|
#include <QtDebug>
|
|
|
|
#include <QTimer>
|
|
|
|
#include <QKeyEvent>
|
2010-05-10 23:50:31 +02:00
|
|
|
#include <QApplication>
|
2010-05-22 18:36:13 +02:00
|
|
|
#include <QSortFilterProxyModel>
|
2010-11-21 00:20:27 +01:00
|
|
|
#include <QScrollBar>
|
2012-03-16 22:39:39 +01:00
|
|
|
#include <QTimeLine>
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
2012-05-27 22:15:58 +02:00
|
|
|
#ifdef HAVE_MOODBAR
|
2014-02-07 16:34:20 +01:00
|
|
|
#include "moodbar/moodbaritemdelegate.h"
|
2012-05-27 22:15:58 +02:00
|
|
|
#endif
|
|
|
|
|
2013-03-10 07:58:09 +01:00
|
|
|
const int PlaylistView::kStateVersion = 6;
|
2010-05-27 22:31:00 +02:00
|
|
|
const int PlaylistView::kGlowIntensitySteps = 24;
|
2014-02-07 16:34:20 +01:00
|
|
|
const int PlaylistView::kAutoscrollGraceTimeout = 30; // seconds
|
2010-04-24 14:18:42 +02:00
|
|
|
const int PlaylistView::kDropIndicatorWidth = 2;
|
|
|
|
const int PlaylistView::kDropIndicatorGradientWidth = 5;
|
2014-02-07 16:34:20 +01:00
|
|
|
const char* PlaylistView::kSettingBackgroundImageType =
|
|
|
|
"playlistview_background_type";
|
|
|
|
const char* PlaylistView::kSettingBackgroundImageFilename =
|
|
|
|
"playlistview_background_image_file";
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2013-02-17 04:03:39 +01:00
|
|
|
const int PlaylistView::kDefaultBlurRadius = 0;
|
|
|
|
const int PlaylistView::kDefaultOpacityLevel = 40;
|
|
|
|
|
2010-09-07 00:49:15 +02:00
|
|
|
PlaylistProxyStyle::PlaylistProxyStyle(QStyle* base)
|
2014-02-07 16:34:20 +01:00
|
|
|
: QProxyStyle(base), cleanlooks_(new QCleanlooksStyle) {}
|
2010-09-07 00:49:15 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistProxyStyle::drawControl(ControlElement element,
|
|
|
|
const QStyleOption* option,
|
|
|
|
QPainter* painter,
|
|
|
|
const QWidget* widget) const {
|
2011-07-06 15:22:42 +02:00
|
|
|
if (element == CE_Header) {
|
2014-02-07 16:34:20 +01:00
|
|
|
const QStyleOptionHeader* header_option =
|
|
|
|
qstyleoption_cast<const QStyleOptionHeader*>(option);
|
2011-07-06 15:22:42 +02:00
|
|
|
const QRect& rect = header_option->rect;
|
|
|
|
const QString& text = header_option->text;
|
|
|
|
const QFontMetrics& font_metrics = header_option->fontMetrics;
|
2012-01-04 16:43:28 +01:00
|
|
|
|
2011-07-06 15:22:42 +02:00
|
|
|
// spaces added to make transition less abrupt
|
|
|
|
if (rect.width() < font_metrics.width(text + " ")) {
|
2014-02-07 16:34:20 +01:00
|
|
|
const Playlist::Column column =
|
|
|
|
static_cast<Playlist::Column>(header_option->section);
|
2011-07-06 15:22:42 +02:00
|
|
|
QStyleOptionHeader new_option(*header_option);
|
|
|
|
new_option.text = Playlist::abbreviated_column_name(column);
|
|
|
|
QProxyStyle::drawControl(element, &new_option, painter, widget);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-07 00:49:15 +02:00
|
|
|
if (element == CE_ItemViewItem)
|
|
|
|
cleanlooks_->drawControl(element, option, painter, widget);
|
|
|
|
else
|
|
|
|
QProxyStyle::drawControl(element, option, painter, widget);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistProxyStyle::drawPrimitive(PrimitiveElement element,
|
|
|
|
const QStyleOption* option,
|
|
|
|
QPainter* painter,
|
|
|
|
const QWidget* widget) const {
|
2010-10-24 01:27:15 +02:00
|
|
|
if (element == QStyle::PE_PanelItemViewRow ||
|
|
|
|
element == QStyle::PE_PanelItemViewItem)
|
2010-09-07 00:49:15 +02:00
|
|
|
cleanlooks_->drawPrimitive(element, option, painter, widget);
|
|
|
|
else
|
|
|
|
QProxyStyle::drawPrimitive(element, option, painter, widget);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
PlaylistView::PlaylistView(QWidget* parent)
|
|
|
|
: QTreeView(parent),
|
|
|
|
app_(nullptr),
|
|
|
|
style_(new PlaylistProxyStyle(style())),
|
|
|
|
playlist_(nullptr),
|
|
|
|
header_(new PlaylistHeader(Qt::Horizontal, this, this)),
|
|
|
|
setting_initial_header_layout_(false),
|
|
|
|
upgrading_from_qheaderview_(false),
|
|
|
|
read_only_settings_(true),
|
|
|
|
upgrading_from_version_(-1),
|
|
|
|
background_image_type_(Default),
|
2015-10-04 13:26:35 +02:00
|
|
|
blur_radius_(kDefaultBlurRadius),
|
|
|
|
opacity_level_(kDefaultOpacityLevel),
|
2014-02-07 16:34:20 +01:00
|
|
|
previous_background_image_opacity_(0.0),
|
|
|
|
fade_animation_(new QTimeLine(1000, this)),
|
|
|
|
last_height_(-1),
|
|
|
|
last_width_(-1),
|
|
|
|
force_background_redraw_(false),
|
|
|
|
glow_enabled_(true),
|
|
|
|
currently_glowing_(false),
|
|
|
|
glow_intensity_step_(0),
|
|
|
|
rating_delegate_(nullptr),
|
|
|
|
inhibit_autoscroll_timer_(new QTimer(this)),
|
|
|
|
inhibit_autoscroll_(false),
|
|
|
|
currently_autoscrolling_(false),
|
|
|
|
row_height_(-1),
|
|
|
|
currenttrack_play_(":currenttrack_play.png"),
|
|
|
|
currenttrack_pause_(":currenttrack_pause.png"),
|
|
|
|
cached_current_row_row_(-1),
|
|
|
|
drop_indicator_row_(-1),
|
|
|
|
drag_over_(false),
|
|
|
|
dynamic_controls_(new DynamicPlaylistControls(this)) {
|
2010-08-27 14:42:06 +02:00
|
|
|
setHeader(header_);
|
|
|
|
header_->setMovable(true);
|
2010-09-07 00:49:15 +02:00
|
|
|
setStyle(style_);
|
2010-10-17 23:56:19 +02:00
|
|
|
setMouseTracking(true);
|
2010-08-27 14:42:06 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(header_, SIGNAL(sectionResized(int, int, int)), SLOT(SaveGeometry()));
|
|
|
|
connect(header_, SIGNAL(sectionMoved(int, int, int)), SLOT(SaveGeometry()));
|
|
|
|
connect(header_, SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
|
|
|
|
SLOT(SaveGeometry()));
|
|
|
|
connect(header_, SIGNAL(SectionVisibilityChanged(int, bool)),
|
|
|
|
SLOT(SaveGeometry()));
|
2015-10-15 01:50:34 +02:00
|
|
|
connect(header_, SIGNAL(SectionRatingLockStatusChanged(bool)),
|
|
|
|
SLOT(SetRatingLockStatus(bool)));
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(header_, SIGNAL(sectionResized(int, int, int)),
|
|
|
|
SLOT(InvalidateCachedCurrentPixmap()));
|
|
|
|
connect(header_, SIGNAL(sectionMoved(int, int, int)),
|
|
|
|
SLOT(InvalidateCachedCurrentPixmap()));
|
|
|
|
connect(header_, SIGNAL(SectionVisibilityChanged(int, bool)),
|
|
|
|
SLOT(InvalidateCachedCurrentPixmap()));
|
2010-08-27 14:42:06 +02:00
|
|
|
connect(header_, SIGNAL(StretchEnabledChanged(bool)), SLOT(SaveSettings()));
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(header_, SIGNAL(StretchEnabledChanged(bool)),
|
|
|
|
SLOT(StretchChanged(bool)));
|
2010-10-24 01:30:38 +02:00
|
|
|
connect(header_, SIGNAL(MouseEntered()), SLOT(RatingHoverOut()));
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-09 00:59:02 +02:00
|
|
|
inhibit_autoscroll_timer_->setInterval(kAutoscrollGraceTimeout * 1000);
|
|
|
|
inhibit_autoscroll_timer_->setSingleShot(true);
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(inhibit_autoscroll_timer_, SIGNAL(timeout()),
|
|
|
|
SLOT(InhibitAutoscrollTimeout()));
|
2010-04-09 00:59:02 +02:00
|
|
|
|
2010-11-21 00:20:27 +01:00
|
|
|
horizontalScrollBar()->installEventFilter(this);
|
|
|
|
verticalScrollBar()->installEventFilter(this);
|
|
|
|
|
2010-03-25 12:38:28 +01:00
|
|
|
setAlternatingRowColors(true);
|
2010-08-26 14:22:13 +02:00
|
|
|
|
|
|
|
setAttribute(Qt::WA_MacShowFocusRect, false);
|
2010-09-13 20:33:18 +02:00
|
|
|
|
2010-11-20 21:00:40 +01:00
|
|
|
dynamic_controls_->hide();
|
|
|
|
|
2010-09-13 20:33:18 +02:00
|
|
|
#ifdef Q_OS_DARWIN
|
|
|
|
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
|
|
|
#endif
|
2012-03-16 22:39:39 +01:00
|
|
|
// For fading
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(fade_animation_, SIGNAL(valueChanged(qreal)),
|
|
|
|
SLOT(FadePreviousBackgroundImage(qreal)));
|
|
|
|
fade_animation_->setDirection(QTimeLine::Backward); // 1.0 -> 0.0
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::SetApplication(Application* app) {
|
2012-03-03 01:20:37 +01:00
|
|
|
Q_ASSERT(app);
|
|
|
|
app_ = app;
|
|
|
|
connect(app_->current_art_loader(),
|
|
|
|
SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
|
|
|
|
SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
|
2012-03-13 22:59:18 +01:00
|
|
|
connect(app_->player(), SIGNAL(Paused()), SLOT(StopGlowing()));
|
|
|
|
connect(app_->player(), SIGNAL(Playing()), SLOT(StartGlowing()));
|
|
|
|
connect(app_->player(), SIGNAL(Stopped()), SLOT(StopGlowing()));
|
2012-03-13 23:15:53 +01:00
|
|
|
connect(app_->player(), SIGNAL(Stopped()), SLOT(PlayerStopped()));
|
2012-03-03 01:20:37 +01:00
|
|
|
}
|
|
|
|
|
2010-05-09 02:10:26 +02:00
|
|
|
void PlaylistView::SetItemDelegates(LibraryBackend* backend) {
|
2010-10-17 23:56:19 +02:00
|
|
|
rating_delegate_ = new RatingItemDelegate(this);
|
|
|
|
|
2010-03-28 00:45:46 +01:00
|
|
|
setItemDelegate(new PlaylistDelegateBase(this));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_Title, new TextItemDelegate(this));
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(
|
|
|
|
Playlist::Column_Album,
|
2010-05-09 02:10:26 +02:00
|
|
|
new TagCompletionItemDelegate(this, backend, Playlist::Column_Album));
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(
|
|
|
|
Playlist::Column_Artist,
|
2010-05-09 02:10:26 +02:00
|
|
|
new TagCompletionItemDelegate(this, backend, Playlist::Column_Artist));
|
2010-03-28 00:45:46 +01:00
|
|
|
setItemDelegateForColumn(Playlist::Column_AlbumArtist,
|
2014-02-07 16:34:20 +01:00
|
|
|
new TagCompletionItemDelegate(
|
|
|
|
this, backend, Playlist::Column_AlbumArtist));
|
|
|
|
setItemDelegateForColumn(
|
|
|
|
Playlist::Column_Genre,
|
2010-12-25 14:11:09 +01:00
|
|
|
new TagCompletionItemDelegate(this, backend, Playlist::Column_Genre));
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(
|
|
|
|
Playlist::Column_Composer,
|
2010-12-25 14:11:09 +01:00
|
|
|
new TagCompletionItemDelegate(this, backend, Playlist::Column_Composer));
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(
|
|
|
|
Playlist::Column_Performer,
|
2013-03-03 13:00:24 +01:00
|
|
|
new TagCompletionItemDelegate(this, backend, Playlist::Column_Performer));
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(
|
|
|
|
Playlist::Column_Grouping,
|
2013-03-03 13:00:24 +01:00
|
|
|
new TagCompletionItemDelegate(this, backend, Playlist::Column_Grouping));
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(Playlist::Column_Length,
|
|
|
|
new LengthItemDelegate(this));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_Filesize,
|
|
|
|
new SizeItemDelegate(this));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_Filetype,
|
|
|
|
new FileTypeItemDelegate(this));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_DateCreated,
|
|
|
|
new DateItemDelegate(this));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_DateModified,
|
|
|
|
new DateItemDelegate(this));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_BPM,
|
|
|
|
new PlaylistDelegateBase(this, tr("bpm")));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_Samplerate,
|
|
|
|
new PlaylistDelegateBase(this, ("Hz")));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_Bitrate,
|
|
|
|
new PlaylistDelegateBase(this, tr("kbps")));
|
|
|
|
setItemDelegateForColumn(Playlist::Column_Filename,
|
|
|
|
new NativeSeparatorsDelegate(this));
|
2010-10-17 23:56:19 +02:00
|
|
|
setItemDelegateForColumn(Playlist::Column_Rating, rating_delegate_);
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(Playlist::Column_LastPlayed,
|
|
|
|
new LastPlayedItemDelegate(this));
|
2012-05-27 22:15:58 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_MOODBAR
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(Playlist::Column_Mood,
|
|
|
|
new MoodbarItemDelegate(app_, this, this));
|
2012-05-27 22:15:58 +02:00
|
|
|
#endif
|
2013-02-15 13:41:56 +01:00
|
|
|
|
2012-03-03 01:20:37 +01:00
|
|
|
if (app_ && app_->player()) {
|
2014-02-07 16:34:20 +01:00
|
|
|
setItemDelegateForColumn(Playlist::Column_Source,
|
|
|
|
new SongSourceDelegate(this, app_->player()));
|
2012-01-06 12:22:17 +01:00
|
|
|
} else {
|
|
|
|
header_->HideSection(Playlist::Column_Source);
|
|
|
|
}
|
2010-03-28 00:45:46 +01:00
|
|
|
}
|
|
|
|
|
2011-07-25 17:53:58 +02:00
|
|
|
void PlaylistView::SetPlaylist(Playlist* playlist) {
|
2010-05-22 18:36:13 +02:00
|
|
|
if (playlist_) {
|
2014-02-07 16:34:20 +01:00
|
|
|
disconnect(playlist_, SIGNAL(CurrentSongChanged(Song)), this,
|
|
|
|
SLOT(MaybeAutoscroll()));
|
|
|
|
disconnect(playlist_, SIGNAL(DynamicModeChanged(bool)), this,
|
|
|
|
SLOT(DynamicModeChanged(bool)));
|
2010-07-11 22:23:34 +02:00
|
|
|
disconnect(playlist_, SIGNAL(destroyed()), this, SLOT(PlaylistDestroyed()));
|
2014-07-24 00:47:12 +02:00
|
|
|
disconnect(playlist_, SIGNAL(QueueChanged()), this, SLOT(update()));
|
2010-11-20 21:00:40 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
disconnect(dynamic_controls_, SIGNAL(Expand()), playlist_,
|
|
|
|
SLOT(ExpandDynamicPlaylist()));
|
|
|
|
disconnect(dynamic_controls_, SIGNAL(Repopulate()), playlist_,
|
|
|
|
SLOT(RepopulateDynamicPlaylist()));
|
|
|
|
disconnect(dynamic_controls_, SIGNAL(TurnOff()), playlist_,
|
|
|
|
SLOT(TurnOffDynamicPlaylist()));
|
2010-05-22 16:28:37 +02:00
|
|
|
}
|
2010-04-14 15:07:21 +02:00
|
|
|
|
2010-05-22 18:36:13 +02:00
|
|
|
playlist_ = playlist;
|
2009-12-24 20:16:07 +01:00
|
|
|
LoadGeometry();
|
2015-10-15 01:50:34 +02:00
|
|
|
LoadRatingLockStatus();
|
2010-08-27 12:36:01 +02:00
|
|
|
ReloadSettings();
|
2010-11-20 21:00:40 +01:00
|
|
|
DynamicModeChanged(playlist->is_dynamic());
|
2011-02-10 21:55:19 +01:00
|
|
|
setFocus();
|
2010-12-19 15:27:07 +01:00
|
|
|
read_only_settings_ = false;
|
2015-05-20 04:23:03 +02:00
|
|
|
JumpToLastPlayedTrack();
|
2010-04-14 15:07:21 +02:00
|
|
|
|
2011-02-10 21:55:19 +01:00
|
|
|
connect(playlist_, SIGNAL(RestoreFinished()), SLOT(JumpToLastPlayedTrack()));
|
2010-05-22 18:36:13 +02:00
|
|
|
connect(playlist_, SIGNAL(CurrentSongChanged(Song)), SLOT(MaybeAutoscroll()));
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(playlist_, SIGNAL(DynamicModeChanged(bool)),
|
|
|
|
SLOT(DynamicModeChanged(bool)));
|
2010-11-20 21:00:40 +01:00
|
|
|
connect(playlist_, SIGNAL(destroyed()), SLOT(PlaylistDestroyed()));
|
2014-07-24 00:47:12 +02:00
|
|
|
connect(playlist_, SIGNAL(QueueChanged()), SLOT(update()));
|
2014-07-23 01:15:06 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(dynamic_controls_, SIGNAL(Expand()), playlist_,
|
|
|
|
SLOT(ExpandDynamicPlaylist()));
|
|
|
|
connect(dynamic_controls_, SIGNAL(Repopulate()), playlist_,
|
|
|
|
SLOT(RepopulateDynamicPlaylist()));
|
|
|
|
connect(dynamic_controls_, SIGNAL(TurnOff()), playlist_,
|
|
|
|
SLOT(TurnOffDynamicPlaylist()));
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::setModel(QAbstractItemModel* m) {
|
2010-06-02 15:51:16 +02:00
|
|
|
if (model()) {
|
2014-02-07 16:34:20 +01:00
|
|
|
disconnect(model(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
|
|
|
|
SLOT(InvalidateCachedCurrentPixmap()));
|
|
|
|
disconnect(model(), SIGNAL(layoutAboutToBeChanged()), this,
|
|
|
|
SLOT(RatingHoverOut()));
|
2013-07-20 19:38:37 +02:00
|
|
|
// When changing the model, always invalidate the current pixmap.
|
|
|
|
// If a remote client uses "stop after", without invaliding the stop
|
|
|
|
// mark would not appear.
|
|
|
|
InvalidateCachedCurrentPixmap();
|
2010-06-02 15:51:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QTreeView::setModel(m);
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
connect(model(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
|
|
|
|
SLOT(InvalidateCachedCurrentPixmap()));
|
|
|
|
connect(model(), SIGNAL(layoutAboutToBeChanged()), this,
|
|
|
|
SLOT(RatingHoverOut()));
|
2010-06-02 15:51:16 +02:00
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
void PlaylistView::LoadGeometry() {
|
|
|
|
QSettings settings;
|
2011-03-19 10:41:00 +01:00
|
|
|
settings.beginGroup(Playlist::kSettingsGroup);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2011-07-24 13:46:31 +02:00
|
|
|
QByteArray state(settings.value("state").toByteArray());
|
|
|
|
if (!header_->RestoreState(state)) {
|
|
|
|
// Maybe we're upgrading from a version that persisted the state with
|
|
|
|
// QHeaderView.
|
|
|
|
if (!header_->restoreState(state)) {
|
|
|
|
header_->HideSection(Playlist::Column_Disc);
|
|
|
|
header_->HideSection(Playlist::Column_Year);
|
2015-06-30 19:25:38 +02:00
|
|
|
header_->HideSection(Playlist::Column_OriginalYear);
|
2011-07-24 13:46:31 +02:00
|
|
|
header_->HideSection(Playlist::Column_Genre);
|
|
|
|
header_->HideSection(Playlist::Column_BPM);
|
|
|
|
header_->HideSection(Playlist::Column_Bitrate);
|
|
|
|
header_->HideSection(Playlist::Column_Samplerate);
|
|
|
|
header_->HideSection(Playlist::Column_Filename);
|
|
|
|
header_->HideSection(Playlist::Column_Filesize);
|
|
|
|
header_->HideSection(Playlist::Column_Filetype);
|
|
|
|
header_->HideSection(Playlist::Column_DateCreated);
|
|
|
|
header_->HideSection(Playlist::Column_DateModified);
|
|
|
|
header_->HideSection(Playlist::Column_AlbumArtist);
|
|
|
|
header_->HideSection(Playlist::Column_Composer);
|
2013-03-03 13:00:24 +01:00
|
|
|
header_->HideSection(Playlist::Column_Performer);
|
|
|
|
header_->HideSection(Playlist::Column_Grouping);
|
2011-07-24 13:46:31 +02:00
|
|
|
header_->HideSection(Playlist::Column_Rating);
|
|
|
|
header_->HideSection(Playlist::Column_PlayCount);
|
|
|
|
header_->HideSection(Playlist::Column_SkipCount);
|
|
|
|
header_->HideSection(Playlist::Column_LastPlayed);
|
|
|
|
|
|
|
|
header_->moveSection(header_->visualIndex(Playlist::Column_Track), 0);
|
|
|
|
setting_initial_header_layout_ = true;
|
|
|
|
} else {
|
|
|
|
upgrading_from_qheaderview_ = true;
|
|
|
|
}
|
2010-06-16 19:15:13 +02:00
|
|
|
}
|
2010-10-17 20:21:30 +02:00
|
|
|
|
|
|
|
// New columns that we add are visible by default if the user has upgraded
|
|
|
|
// Clementine. Hide them again here
|
|
|
|
const int state_version = settings.value("state_version", 0).toInt();
|
2012-01-04 19:45:08 +01:00
|
|
|
upgrading_from_version_ = state_version;
|
2013-02-15 13:41:56 +01:00
|
|
|
|
2010-10-17 20:21:30 +02:00
|
|
|
if (state_version < 1) {
|
|
|
|
header_->HideSection(Playlist::Column_Rating);
|
|
|
|
header_->HideSection(Playlist::Column_PlayCount);
|
|
|
|
header_->HideSection(Playlist::Column_SkipCount);
|
|
|
|
header_->HideSection(Playlist::Column_LastPlayed);
|
|
|
|
}
|
2010-11-01 22:20:12 +01:00
|
|
|
if (state_version < 2) {
|
|
|
|
header_->HideSection(Playlist::Column_Score);
|
|
|
|
}
|
2010-11-28 16:22:48 +01:00
|
|
|
if (state_version < 3) {
|
|
|
|
header_->HideSection(Playlist::Column_Comment);
|
|
|
|
}
|
2012-05-27 22:15:58 +02:00
|
|
|
if (state_version < 5) {
|
|
|
|
header_->HideSection(Playlist::Column_Mood);
|
|
|
|
}
|
2013-03-10 07:58:09 +01:00
|
|
|
if (state_version < 6) {
|
|
|
|
header_->HideSection(Playlist::Column_Performer);
|
|
|
|
header_->HideSection(Playlist::Column_Grouping);
|
|
|
|
}
|
2010-12-11 15:26:49 +01:00
|
|
|
|
|
|
|
// Make sure at least one column is visible
|
|
|
|
bool all_hidden = true;
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0; i < header_->count(); ++i) {
|
2010-12-11 15:26:49 +01:00
|
|
|
if (!header_->isSectionHidden(i) && header_->sectionSize(i) > 0) {
|
|
|
|
all_hidden = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (all_hidden) {
|
|
|
|
header_->ShowSection(Playlist::Column_Title);
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2015-10-15 01:50:34 +02:00
|
|
|
void PlaylistView::LoadRatingLockStatus() {
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(Playlist::kSettingsGroup);
|
|
|
|
ratings_locked_ = s.value("RatingLocked", false).toBool();
|
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
void PlaylistView::SaveGeometry() {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (read_only_settings_) return;
|
2010-10-29 20:58:43 +02:00
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
QSettings settings;
|
2011-03-19 10:41:00 +01:00
|
|
|
settings.beginGroup(Playlist::kSettingsGroup);
|
2011-07-24 13:46:31 +02:00
|
|
|
settings.setValue("state", header_->SaveState());
|
2010-10-17 20:21:30 +02:00
|
|
|
settings.setValue("state_version", kStateVersion);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2015-10-15 01:50:34 +02:00
|
|
|
void PlaylistView::SetRatingLockStatus(bool state) {
|
|
|
|
if (read_only_settings_) return;
|
|
|
|
|
|
|
|
ratings_locked_ = state;
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(Playlist::kSettingsGroup);
|
|
|
|
s.setValue("RatingLocked", state);
|
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
void PlaylistView::ReloadBarPixmaps() {
|
|
|
|
currenttrack_bar_left_ = LoadBarPixmap(":currenttrack_bar_left.png");
|
|
|
|
currenttrack_bar_mid_ = LoadBarPixmap(":currenttrack_bar_mid.png");
|
|
|
|
currenttrack_bar_right_ = LoadBarPixmap(":currenttrack_bar_right.png");
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QPixmap> PlaylistView::LoadBarPixmap(const QString& filename) {
|
|
|
|
QImage image(filename);
|
|
|
|
image = image.scaledToHeight(row_height_, Qt::SmoothTransformation);
|
|
|
|
|
|
|
|
// Colour the bar with the palette colour
|
|
|
|
QPainter p(&image);
|
|
|
|
p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
|
2010-04-12 20:07:07 +02:00
|
|
|
p.setOpacity(0.7);
|
2010-04-22 22:33:05 +02:00
|
|
|
p.fillRect(image.rect(), QApplication::palette().color(QPalette::Highlight));
|
2009-12-24 20:16:07 +01:00
|
|
|
p.end();
|
|
|
|
|
|
|
|
// Animation steps
|
|
|
|
QList<QPixmap> ret;
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0; i < kGlowIntensitySteps; ++i) {
|
2009-12-24 20:16:07 +01:00
|
|
|
QImage step(image.copy());
|
|
|
|
p.begin(&step);
|
|
|
|
p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
|
2014-02-07 16:34:20 +01:00
|
|
|
p.setOpacity(0.4 - 0.6 * sin(float(i) / kGlowIntensitySteps * (M_PI / 2)));
|
2009-12-24 20:16:07 +01:00
|
|
|
p.fillRect(step.rect(), Qt::white);
|
|
|
|
p.end();
|
|
|
|
ret << QPixmap::fromImage(step);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-10-23 22:58:20 +02:00
|
|
|
void PlaylistView::drawTree(QPainter* painter, const QRegion& region) const {
|
|
|
|
const_cast<PlaylistView*>(this)->current_paint_region_ = region;
|
|
|
|
QTreeView::drawTree(painter, region);
|
|
|
|
const_cast<PlaylistView*>(this)->current_paint_region_ = QRegion();
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::drawRow(QPainter* painter,
|
|
|
|
const QStyleOptionViewItem& option,
|
|
|
|
const QModelIndex& index) const {
|
2009-12-24 20:16:07 +01:00
|
|
|
QStyleOptionViewItemV4 opt(option);
|
|
|
|
|
|
|
|
bool is_current = index.data(Playlist::Role_IsCurrent).toBool();
|
|
|
|
bool is_paused = index.data(Playlist::Role_IsPaused).toBool();
|
|
|
|
|
|
|
|
if (is_current) {
|
|
|
|
const_cast<PlaylistView*>(this)->last_current_item_ = index;
|
|
|
|
const_cast<PlaylistView*>(this)->last_glow_rect_ = opt.rect;
|
|
|
|
|
|
|
|
int step = glow_intensity_step_;
|
|
|
|
if (step >= kGlowIntensitySteps)
|
2014-02-07 16:34:20 +01:00
|
|
|
step = 2 * (kGlowIntensitySteps - 1) - step + 1;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
int row_height = opt.rect.height();
|
|
|
|
if (row_height != row_height_) {
|
|
|
|
// Recreate the pixmaps if the height changed since last time
|
|
|
|
const_cast<PlaylistView*>(this)->row_height_ = row_height;
|
|
|
|
const_cast<PlaylistView*>(this)->ReloadBarPixmaps();
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect middle(opt.rect);
|
|
|
|
middle.setLeft(middle.left() + currenttrack_bar_left_[0].width());
|
|
|
|
middle.setRight(middle.right() - currenttrack_bar_right_[0].width());
|
|
|
|
|
|
|
|
// Selection
|
|
|
|
if (selectionModel()->isSelected(index))
|
|
|
|
painter->fillRect(opt.rect, opt.palette.color(QPalette::Highlight));
|
|
|
|
|
|
|
|
// Draw the bar
|
|
|
|
painter->drawPixmap(opt.rect.topLeft(), currenttrack_bar_left_[step]);
|
2014-02-07 16:34:20 +01:00
|
|
|
painter->drawPixmap(
|
|
|
|
opt.rect.topRight() - currenttrack_bar_right_[0].rect().topRight(),
|
|
|
|
currenttrack_bar_right_[step]);
|
2009-12-24 20:16:07 +01:00
|
|
|
painter->drawPixmap(middle, currenttrack_bar_mid_[step]);
|
|
|
|
|
|
|
|
// Draw the play icon
|
|
|
|
QPoint play_pos(currenttrack_bar_left_[0].width() / 3 * 2,
|
|
|
|
(row_height - currenttrack_play_.height()) / 2);
|
|
|
|
painter->drawPixmap(opt.rect.topLeft() + play_pos,
|
|
|
|
is_paused ? currenttrack_pause_ : currenttrack_play_);
|
|
|
|
|
|
|
|
// Set the font
|
2014-02-07 16:34:20 +01:00
|
|
|
opt.palette.setColor(QPalette::Text, QApplication::palette().color(
|
|
|
|
QPalette::HighlightedText));
|
2009-12-24 20:16:07 +01:00
|
|
|
opt.palette.setColor(QPalette::Highlight, Qt::transparent);
|
2010-04-12 20:07:07 +02:00
|
|
|
opt.palette.setColor(QPalette::AlternateBase, Qt::transparent);
|
2014-02-07 16:34:20 +01:00
|
|
|
opt.decorationSize = QSize(20, 20);
|
2010-05-27 22:31:00 +02:00
|
|
|
|
|
|
|
// Draw the actual row data on top. We cache this, because it's fairly
|
|
|
|
// expensive (1-2ms), and we do it many times per second.
|
2010-10-23 22:58:20 +02:00
|
|
|
const bool cache_dirty = cached_current_row_rect_ != opt.rect ||
|
|
|
|
cached_current_row_row_ != index.row() ||
|
|
|
|
cached_current_row_.isNull();
|
|
|
|
|
|
|
|
// We can't update the cache if we're not drawing the entire region,
|
|
|
|
// QTreeView clips its drawing to only the columns in the region, so it
|
|
|
|
// wouldn't update the whole pixmap properly.
|
|
|
|
const bool whole_region =
|
|
|
|
current_paint_region_.boundingRect().width() == viewport()->width();
|
|
|
|
|
|
|
|
if (!cache_dirty) {
|
|
|
|
painter->drawPixmap(opt.rect, cached_current_row_);
|
|
|
|
} else {
|
|
|
|
if (whole_region) {
|
2014-02-07 16:34:20 +01:00
|
|
|
const_cast<PlaylistView*>(this)
|
|
|
|
->UpdateCachedCurrentRowPixmap(opt, index);
|
2010-10-23 22:58:20 +02:00
|
|
|
painter->drawPixmap(opt.rect, cached_current_row_);
|
|
|
|
} else {
|
|
|
|
QTreeView::drawRow(painter, opt, index);
|
|
|
|
}
|
2010-05-27 22:31:00 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QTreeView::drawRow(painter, opt, index);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-05-27 22:31:00 +02:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-09-07 00:49:15 +02:00
|
|
|
void PlaylistView::UpdateCachedCurrentRowPixmap(QStyleOptionViewItemV4 option,
|
2010-05-27 22:31:00 +02:00
|
|
|
const QModelIndex& index) {
|
|
|
|
cached_current_row_rect_ = option.rect;
|
|
|
|
cached_current_row_row_ = index.row();
|
|
|
|
|
|
|
|
option.rect.moveTo(0, 0);
|
|
|
|
cached_current_row_ = QPixmap(option.rect.size());
|
|
|
|
cached_current_row_.fill(Qt::transparent);
|
|
|
|
|
|
|
|
QPainter p(&cached_current_row_);
|
|
|
|
QTreeView::drawRow(&p, option, index);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-06-02 15:51:16 +02:00
|
|
|
void PlaylistView::InvalidateCachedCurrentPixmap() {
|
|
|
|
cached_current_row_ = QPixmap();
|
|
|
|
}
|
|
|
|
|
2010-04-28 13:09:57 +02:00
|
|
|
void PlaylistView::timerEvent(QTimerEvent* event) {
|
2010-04-29 17:54:32 +02:00
|
|
|
QTreeView::timerEvent(event);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (event->timerId() == glow_timer_.timerId()) GlowIntensityChanged();
|
2010-04-28 13:09:57 +02:00
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
void PlaylistView::GlowIntensityChanged() {
|
|
|
|
glow_intensity_step_ = (glow_intensity_step_ + 1) % (kGlowIntensitySteps * 2);
|
|
|
|
|
|
|
|
viewport()->update(last_glow_rect_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::StopGlowing() {
|
2010-08-27 12:36:01 +02:00
|
|
|
currently_glowing_ = false;
|
2010-04-28 13:09:57 +02:00
|
|
|
glow_timer_.stop();
|
2009-12-24 20:16:07 +01:00
|
|
|
glow_intensity_step_ = kGlowIntensitySteps;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::StartGlowing() {
|
2010-08-27 12:36:01 +02:00
|
|
|
currently_glowing_ = true;
|
|
|
|
if (isVisible() && glow_enabled_)
|
2010-04-28 13:09:57 +02:00
|
|
|
glow_timer_.start(1500 / kGlowIntensitySteps, this);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::hideEvent(QHideEvent*) { glow_timer_.stop(); }
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
void PlaylistView::showEvent(QShowEvent*) {
|
2010-08-27 12:36:01 +02:00
|
|
|
if (currently_glowing_ && glow_enabled_)
|
2010-04-28 13:09:57 +02:00
|
|
|
glow_timer_.start(1500 / kGlowIntensitySteps, this);
|
2010-04-09 00:59:02 +02:00
|
|
|
MaybeAutoscroll();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
bool CompareSelectionRanges(const QItemSelectionRange& a,
|
|
|
|
const QItemSelectionRange& b) {
|
2009-12-24 20:16:07 +01:00
|
|
|
return b.bottom() < a.bottom();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::keyPressEvent(QKeyEvent* event) {
|
2012-01-29 20:24:13 +01:00
|
|
|
if (!model() || state() == QAbstractItemView::EditingState) {
|
2010-01-14 15:38:27 +01:00
|
|
|
QTreeView::keyPressEvent(event);
|
2012-01-29 20:24:13 +01:00
|
|
|
} else if (event == QKeySequence::Delete) {
|
2010-03-24 17:36:44 +01:00
|
|
|
RemoveSelected();
|
2010-01-14 15:38:27 +01:00
|
|
|
event->accept();
|
2011-05-26 16:34:22 +02:00
|
|
|
#ifdef Q_OS_DARWIN
|
|
|
|
} else if (event->key() == Qt::Key_Backspace) {
|
|
|
|
RemoveSelected();
|
|
|
|
event->accept();
|
|
|
|
#endif
|
2012-01-29 20:24:13 +01:00
|
|
|
} else if (event == QKeySequence::Copy) {
|
|
|
|
CopyCurrentSongToClipboard();
|
2014-02-07 16:34:20 +01:00
|
|
|
} else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
|
|
|
|
if (currentIndex().isValid()) emit PlayItem(currentIndex());
|
2011-02-24 00:59:12 +01:00
|
|
|
event->accept();
|
2014-02-11 13:18:08 +01:00
|
|
|
} else if (event->modifiers() != Qt::ControlModifier // Ctrl+Space selects
|
|
|
|
// the item
|
2014-04-26 01:41:04 +02:00
|
|
|
&&
|
|
|
|
event->key() == Qt::Key_Space) {
|
2011-02-24 00:59:12 +01:00
|
|
|
emit PlayPause();
|
2009-12-24 20:16:07 +01:00
|
|
|
event->accept();
|
2014-02-07 16:34:20 +01:00
|
|
|
} else if (event->key() == Qt::Key_Left) {
|
2015-07-02 14:59:30 +02:00
|
|
|
emit SeekBackward();
|
2011-02-24 00:59:12 +01:00
|
|
|
event->accept();
|
2014-02-07 16:34:20 +01:00
|
|
|
} else if (event->key() == Qt::Key_Right) {
|
2015-07-02 14:59:30 +02:00
|
|
|
emit SeekForward();
|
2011-02-24 00:59:12 +01:00
|
|
|
event->accept();
|
2014-02-07 16:34:20 +01:00
|
|
|
} else if (event->modifiers() ==
|
|
|
|
Qt::NoModifier // No modifier keys currently pressed...
|
|
|
|
// ... and key pressed is something related to text
|
|
|
|
&&
|
2014-10-04 18:08:53 +02:00
|
|
|
((event->key() >= Qt::Key_Exclam && event->key() <= Qt::Key_Z) ||
|
2014-02-07 16:34:20 +01:00
|
|
|
event->key() == Qt::Key_Backspace ||
|
|
|
|
event->key() == Qt::Key_Escape)) {
|
2011-02-24 00:59:12 +01:00
|
|
|
emit FocusOnFilterSignal(event);
|
|
|
|
event->accept();
|
2009-12-24 20:16:07 +01:00
|
|
|
} else {
|
|
|
|
QTreeView::keyPressEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::contextMenuEvent(QContextMenuEvent* e) {
|
2010-01-15 17:22:19 +01:00
|
|
|
emit RightClicked(e->globalPos(), indexAt(e->pos()));
|
2009-12-24 20:16:07 +01:00
|
|
|
e->accept();
|
|
|
|
}
|
2010-03-24 17:36:44 +01:00
|
|
|
|
|
|
|
void PlaylistView::RemoveSelected() {
|
2011-04-17 15:48:02 +02:00
|
|
|
int rows_removed = 0;
|
2010-03-24 17:36:44 +01:00
|
|
|
QItemSelection selection(selectionModel()->selection());
|
|
|
|
|
2011-07-05 22:24:59 +02:00
|
|
|
if (selection.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-10 16:51:43 +01:00
|
|
|
// Store the last selected row, which is the last in the list
|
|
|
|
int last_row = selection.last().top();
|
|
|
|
|
2010-03-24 17:36:44 +01:00
|
|
|
// Sort the selection so we remove the items at the *bottom* first, ensuring
|
|
|
|
// we don't have to mess around with changing row numbers
|
|
|
|
qSort(selection.begin(), selection.end(), CompareSelectionRanges);
|
|
|
|
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QItemSelectionRange& range : selection) {
|
2014-02-11 13:18:08 +01:00
|
|
|
if (range.top() < last_row) rows_removed += range.height();
|
2010-03-24 17:36:44 +01:00
|
|
|
model()->removeRows(range.top(), range.height(), range.parent());
|
|
|
|
}
|
|
|
|
|
2012-11-24 07:10:20 +01:00
|
|
|
int new_row = last_row - rows_removed;
|
2011-04-17 15:48:02 +02:00
|
|
|
// Index of the first column for the row to select
|
|
|
|
QModelIndex new_index = model()->index(new_row, 0);
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
// Select the new current item, we want always the item after the last
|
|
|
|
// selected
|
2011-04-17 15:48:02 +02:00
|
|
|
if (new_index.isValid()) {
|
2014-02-10 16:51:43 +01:00
|
|
|
// Workaround to update keyboard selected row, if it's not the first row
|
|
|
|
// (this also triggers selection)
|
2011-04-17 15:48:02 +02:00
|
|
|
if (new_row != 0)
|
2014-02-07 16:34:20 +01:00
|
|
|
keyPressEvent(
|
|
|
|
new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier));
|
2014-02-11 13:18:08 +01:00
|
|
|
// Update visual selection with the entire row
|
|
|
|
selectionModel()->select(new_index, QItemSelectionModel::ClearAndSelect |
|
|
|
|
QItemSelectionModel::Rows);
|
|
|
|
} else {
|
|
|
|
// We're removing the last item, select the new last row
|
|
|
|
selectionModel()->select(
|
|
|
|
model()->index(model()->rowCount() - 1, 0),
|
|
|
|
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
|
|
|
}
|
2010-03-24 17:36:44 +01:00
|
|
|
}
|
2010-03-26 00:48:58 +01:00
|
|
|
|
|
|
|
QList<int> PlaylistView::GetEditableColumns() {
|
|
|
|
QList<int> columns;
|
|
|
|
QHeaderView* h = header();
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int col = 0; col < h->count(); col++) {
|
|
|
|
if (h->isSectionHidden(col)) continue;
|
2010-03-26 00:48:58 +01:00
|
|
|
QModelIndex index = model()->index(0, col);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (index.flags() & Qt::ItemIsEditable) columns << h->visualIndex(col);
|
2010-03-26 00:48:58 +01:00
|
|
|
}
|
|
|
|
qSort(columns);
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex PlaylistView::NextEditableIndex(const QModelIndex& current) {
|
|
|
|
QList<int> columns = GetEditableColumns();
|
|
|
|
QHeaderView* h = header();
|
2010-03-26 01:32:44 +01:00
|
|
|
int index = columns.indexOf(h->visualIndex(current.column()));
|
2010-03-26 00:48:58 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (index + 1 >= columns.size())
|
|
|
|
return model()->index(current.row() + 1, h->logicalIndex(columns.first()));
|
2010-03-26 00:48:58 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
return model()->index(current.row(), h->logicalIndex(columns[index + 1]));
|
2010-03-26 00:48:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex PlaylistView::PrevEditableIndex(const QModelIndex& current) {
|
|
|
|
QList<int> columns = GetEditableColumns();
|
|
|
|
QHeaderView* h = header();
|
2010-03-26 01:32:44 +01:00
|
|
|
int index = columns.indexOf(h->visualIndex(current.column()));
|
2010-03-26 00:48:58 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (index - 1 < 0)
|
|
|
|
return model()->index(current.row() - 1, h->logicalIndex(columns.last()));
|
2010-03-26 00:48:58 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
return model()->index(current.row(), h->logicalIndex(columns[index - 1]));
|
2010-03-26 00:48:58 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::closeEditor(QWidget* editor,
|
|
|
|
QAbstractItemDelegate::EndEditHint hint) {
|
2010-03-26 00:48:58 +01:00
|
|
|
if (hint == QAbstractItemDelegate::NoHint) {
|
|
|
|
QTreeView::closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
|
|
|
|
} else if (hint == QAbstractItemDelegate::EditNextItem ||
|
|
|
|
hint == QAbstractItemDelegate::EditPreviousItem) {
|
|
|
|
QModelIndex index;
|
|
|
|
if (hint == QAbstractItemDelegate::EditNextItem)
|
|
|
|
index = NextEditableIndex(currentIndex());
|
|
|
|
else
|
|
|
|
index = PrevEditableIndex(currentIndex());
|
|
|
|
|
|
|
|
if (!index.isValid()) {
|
2010-04-27 15:46:44 +02:00
|
|
|
QTreeView::closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
|
2010-03-26 00:48:58 +01:00
|
|
|
} else {
|
2010-04-27 15:46:44 +02:00
|
|
|
QTreeView::closeEditor(editor, QAbstractItemDelegate::NoHint);
|
|
|
|
setCurrentIndex(index);
|
|
|
|
edit(index);
|
2010-03-26 00:48:58 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QTreeView::closeEditor(editor, hint);
|
|
|
|
}
|
|
|
|
}
|
2010-04-09 00:59:02 +02:00
|
|
|
|
2010-10-17 23:56:19 +02:00
|
|
|
void PlaylistView::mouseMoveEvent(QMouseEvent* event) {
|
2015-10-15 01:50:34 +02:00
|
|
|
// Check wheather rating section is locked by user or not
|
|
|
|
if (!ratings_locked_) {
|
|
|
|
QModelIndex index = indexAt(event->pos());
|
|
|
|
if (index.isValid() && index.data(Playlist::Role_CanSetRating).toBool()) {
|
|
|
|
RatingHoverIn(index, event->pos());
|
|
|
|
} else if (rating_delegate_->is_mouse_over()) {
|
|
|
|
RatingHoverOut();
|
|
|
|
}
|
2010-10-17 23:56:19 +02:00
|
|
|
}
|
2010-12-06 23:18:00 +01:00
|
|
|
if (!drag_over_) {
|
|
|
|
QTreeView::mouseMoveEvent(event);
|
|
|
|
}
|
2010-10-17 23:56:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::leaveEvent(QEvent* e) {
|
2015-10-15 01:50:34 +02:00
|
|
|
if (rating_delegate_->is_mouse_over() && !ratings_locked_) {
|
2010-10-23 22:58:20 +02:00
|
|
|
RatingHoverOut();
|
2010-10-17 23:56:19 +02:00
|
|
|
}
|
|
|
|
QTreeView::leaveEvent(e);
|
|
|
|
}
|
|
|
|
|
2010-10-23 22:58:20 +02:00
|
|
|
void PlaylistView::RatingHoverIn(const QModelIndex& index, const QPoint& pos) {
|
2014-09-06 17:54:50 +02:00
|
|
|
if (editTriggers() & QAbstractItemView::NoEditTriggers) {
|
2010-10-29 20:58:43 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-10-23 22:58:20 +02:00
|
|
|
const QModelIndex old_index = rating_delegate_->mouse_over_index();
|
2010-12-11 15:53:43 +01:00
|
|
|
rating_delegate_->set_mouse_over(index, selectedIndexes(), pos);
|
2010-10-23 22:58:20 +02:00
|
|
|
setCursor(Qt::PointingHandCursor);
|
|
|
|
|
2010-12-11 15:53:43 +01:00
|
|
|
update(index);
|
|
|
|
update(old_index);
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QModelIndex& index : selectedIndexes()) {
|
2010-12-11 15:53:43 +01:00
|
|
|
if (index.column() == Playlist::Column_Rating) {
|
|
|
|
update(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-23 22:58:20 +02:00
|
|
|
if (index.data(Playlist::Role_IsCurrent).toBool() ||
|
|
|
|
old_index.data(Playlist::Role_IsCurrent).toBool()) {
|
|
|
|
InvalidateCachedCurrentPixmap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::RatingHoverOut() {
|
2014-09-06 17:54:50 +02:00
|
|
|
if (editTriggers() & QAbstractItemView::NoEditTriggers) {
|
2010-10-29 20:58:43 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-10-23 22:58:20 +02:00
|
|
|
const QModelIndex old_index = rating_delegate_->mouse_over_index();
|
|
|
|
rating_delegate_->set_mouse_out();
|
|
|
|
setCursor(QCursor());
|
|
|
|
|
2010-12-11 15:53:43 +01:00
|
|
|
update(old_index);
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QModelIndex& index : selectedIndexes()) {
|
2010-12-11 15:53:43 +01:00
|
|
|
if (index.column() == Playlist::Column_Rating) {
|
|
|
|
update(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-23 22:58:20 +02:00
|
|
|
if (old_index.data(Playlist::Role_IsCurrent).toBool()) {
|
|
|
|
InvalidateCachedCurrentPixmap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-17 23:56:19 +02:00
|
|
|
void PlaylistView::mousePressEvent(QMouseEvent* event) {
|
2014-09-06 17:54:50 +02:00
|
|
|
if (editTriggers() & QAbstractItemView::NoEditTriggers) {
|
2010-10-29 21:26:37 +02:00
|
|
|
QTreeView::mousePressEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-15 01:50:34 +02:00
|
|
|
// Check wheather rating section is locked by user or not
|
|
|
|
if (!ratings_locked_) {
|
|
|
|
QModelIndex index = indexAt(event->pos());
|
|
|
|
if (event->button() == Qt::LeftButton && index.isValid() &&
|
|
|
|
index.data(Playlist::Role_CanSetRating).toBool()) {
|
|
|
|
// Calculate which star was clicked
|
|
|
|
double new_rating =
|
|
|
|
RatingPainter::RatingForPos(event->pos(), visualRect(index));
|
|
|
|
|
|
|
|
if (selectedIndexes().contains(index)) {
|
|
|
|
// Update all the selected items
|
|
|
|
QModelIndexList src_index_list;
|
|
|
|
for (const QModelIndex& index : selectedIndexes()) {
|
|
|
|
if (index.data(Playlist::Role_CanSetRating).toBool()) {
|
|
|
|
QModelIndex src_index = playlist_->proxy()->mapToSource(index);
|
|
|
|
src_index_list << src_index;
|
|
|
|
}
|
2010-12-11 15:53:43 +01:00
|
|
|
}
|
2015-10-15 01:50:34 +02:00
|
|
|
playlist_->RateSongs(src_index_list, new_rating);
|
|
|
|
} else {
|
|
|
|
// Update only this item
|
|
|
|
playlist_->RateSong(playlist_->proxy()->mapToSource(index), new_rating);
|
2010-12-11 15:53:43 +01:00
|
|
|
}
|
2015-10-15 01:50:34 +02:00
|
|
|
}
|
2010-10-17 23:56:19 +02:00
|
|
|
} else {
|
|
|
|
QTreeView::mousePressEvent(event);
|
|
|
|
}
|
|
|
|
|
2010-04-09 00:59:02 +02:00
|
|
|
inhibit_autoscroll_ = true;
|
|
|
|
inhibit_autoscroll_timer_->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::scrollContentsBy(int dx, int dy) {
|
2010-06-09 17:37:54 +02:00
|
|
|
if (dx) {
|
|
|
|
InvalidateCachedCurrentPixmap();
|
|
|
|
}
|
2010-06-20 21:02:19 +02:00
|
|
|
cached_tree_ = QPixmap();
|
2010-06-09 17:37:54 +02:00
|
|
|
|
2010-04-09 00:59:02 +02:00
|
|
|
QTreeView::scrollContentsBy(dx, dy);
|
2010-04-14 15:11:39 +02:00
|
|
|
|
|
|
|
if (!currently_autoscrolling_) {
|
|
|
|
// We only want to do this if the scroll was initiated by the user
|
|
|
|
inhibit_autoscroll_ = true;
|
|
|
|
inhibit_autoscroll_timer_->start();
|
|
|
|
}
|
2010-04-09 00:59:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::InhibitAutoscrollTimeout() {
|
2013-03-05 23:51:27 +01:00
|
|
|
// For 30 seconds after the user clicks on or scrolls the playlist we promise
|
2010-04-09 00:59:02 +02:00
|
|
|
// not to automatically scroll the view to keep up with a track change.
|
|
|
|
inhibit_autoscroll_ = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::MaybeAutoscroll() {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!inhibit_autoscroll_) JumpToCurrentlyPlayingTrack();
|
2010-05-17 01:55:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::JumpToCurrentlyPlayingTrack() {
|
2010-05-22 18:36:13 +02:00
|
|
|
Q_ASSERT(playlist_);
|
2010-04-09 00:59:02 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
// Usage of the "Jump to the currently playing track" action shall enable
|
|
|
|
// autoscroll
|
2012-11-24 11:21:49 +01:00
|
|
|
inhibit_autoscroll_ = false;
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (playlist_->current_row() == -1) return;
|
2010-04-09 00:59:02 +02:00
|
|
|
|
2010-05-25 21:39:04 +02:00
|
|
|
QModelIndex current = playlist_->proxy()->mapFromSource(
|
2010-12-17 01:21:20 +01:00
|
|
|
playlist_->index(playlist_->current_row(), 0));
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!current.isValid()) return;
|
2010-07-10 19:47:27 +02:00
|
|
|
|
2010-04-14 15:11:39 +02:00
|
|
|
currently_autoscrolling_ = true;
|
2010-05-18 20:19:19 +02:00
|
|
|
|
|
|
|
// Scroll to the item
|
2010-04-09 00:59:02 +02:00
|
|
|
scrollTo(current, QAbstractItemView::PositionAtCenter);
|
2010-05-18 20:19:19 +02:00
|
|
|
|
2010-04-14 15:11:39 +02:00
|
|
|
currently_autoscrolling_ = false;
|
2010-04-09 00:59:02 +02:00
|
|
|
}
|
2010-04-23 15:10:03 +02:00
|
|
|
|
2011-02-10 21:55:19 +01:00
|
|
|
void PlaylistView::JumpToLastPlayedTrack() {
|
|
|
|
Q_ASSERT(playlist_);
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (playlist_->last_played_row() == -1) return;
|
2011-02-10 21:55:19 +01:00
|
|
|
|
|
|
|
QModelIndex last_played = playlist_->proxy()->mapFromSource(
|
|
|
|
playlist_->index(playlist_->last_played_row(), 0));
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!last_played.isValid()) return;
|
2011-02-10 21:55:19 +01:00
|
|
|
|
2011-02-11 21:16:42 +01:00
|
|
|
// Select last played song
|
|
|
|
last_current_item_ = last_played;
|
|
|
|
setCurrentIndex(last_current_item_);
|
|
|
|
|
2011-02-10 21:55:19 +01:00
|
|
|
currently_autoscrolling_ = true;
|
|
|
|
|
|
|
|
// Scroll to the item
|
|
|
|
scrollTo(last_played, QAbstractItemView::PositionAtCenter);
|
|
|
|
|
|
|
|
currently_autoscrolling_ = false;
|
|
|
|
}
|
|
|
|
|
2010-04-23 15:10:03 +02:00
|
|
|
void PlaylistView::paintEvent(QPaintEvent* event) {
|
2012-02-18 19:57:36 +01:00
|
|
|
// Reimplemented to draw the background image.
|
|
|
|
// Reimplemented also to draw the drop indicator
|
2010-06-20 21:02:19 +02:00
|
|
|
// When the user is dragging some stuff over the playlist paintEvent gets
|
|
|
|
// called for the entire viewport every time the user moves the mouse.
|
|
|
|
// The drawTree is kinda expensive, so we cache the result and draw from the
|
|
|
|
// cache while the user is dragging. The cached pixmap gets invalidated in
|
|
|
|
// dragLeaveEvent, dropEvent and scrollContentsBy.
|
|
|
|
|
2012-02-18 19:57:36 +01:00
|
|
|
// Draw background
|
2014-02-07 16:34:20 +01:00
|
|
|
if (background_image_type_ == Custom ||
|
|
|
|
background_image_type_ == AlbumCover) {
|
2012-03-18 19:22:08 +01:00
|
|
|
if (!background_image_.isNull() || !previous_background_image_.isNull()) {
|
2012-02-18 19:57:36 +01:00
|
|
|
QPainter background_painter(viewport());
|
2012-03-08 22:25:29 +01:00
|
|
|
|
|
|
|
// Check if we should recompute the background image
|
2014-02-07 16:34:20 +01:00
|
|
|
if (height() != last_height_ || width() != last_width_ ||
|
|
|
|
force_background_redraw_) {
|
2013-02-17 04:07:46 +01:00
|
|
|
if (background_image_.isNull()) {
|
|
|
|
cached_scaled_background_image_ = QPixmap();
|
|
|
|
} else {
|
2014-02-07 16:34:20 +01:00
|
|
|
cached_scaled_background_image_ =
|
|
|
|
QPixmap::fromImage(background_image_.scaled(
|
|
|
|
width(), height(), Qt::KeepAspectRatioByExpanding,
|
2013-02-17 04:07:46 +01:00
|
|
|
Qt::SmoothTransformation));
|
|
|
|
}
|
2012-03-10 18:55:22 +01:00
|
|
|
|
|
|
|
last_height_ = height();
|
2014-02-07 16:34:20 +01:00
|
|
|
last_width_ = width();
|
2012-03-10 18:55:22 +01:00
|
|
|
force_background_redraw_ = false;
|
2012-02-18 19:57:36 +01:00
|
|
|
}
|
2012-03-08 22:25:29 +01:00
|
|
|
|
|
|
|
// Actually draw the background image
|
2012-03-18 19:22:08 +01:00
|
|
|
if (!cached_scaled_background_image_.isNull()) {
|
|
|
|
// Set opactiy only if needed, as this deactivate hardware acceleration
|
|
|
|
if (!qFuzzyCompare(previous_background_image_opacity_, qreal(0.0))) {
|
2014-02-07 16:34:20 +01:00
|
|
|
background_painter.setOpacity(1.0 -
|
|
|
|
previous_background_image_opacity_);
|
2012-03-18 19:22:08 +01:00
|
|
|
}
|
2014-02-07 16:34:20 +01:00
|
|
|
background_painter.drawPixmap(
|
|
|
|
(width() - cached_scaled_background_image_.width()) / 2,
|
|
|
|
(height() - cached_scaled_background_image_.height()) / 2,
|
|
|
|
cached_scaled_background_image_);
|
2012-03-18 19:22:08 +01:00
|
|
|
}
|
2012-03-16 22:39:39 +01:00
|
|
|
// Draw the previous background image if we're fading
|
|
|
|
if (!previous_background_image_.isNull()) {
|
|
|
|
background_painter.setOpacity(previous_background_image_opacity_);
|
2014-02-07 16:34:20 +01:00
|
|
|
background_painter.drawPixmap(
|
|
|
|
(width() - previous_background_image_.width()) / 2,
|
|
|
|
(height() - previous_background_image_.height()) / 2,
|
|
|
|
previous_background_image_);
|
2012-03-16 22:39:39 +01:00
|
|
|
}
|
2012-02-18 19:57:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-23 15:10:03 +02:00
|
|
|
QPainter p(viewport());
|
|
|
|
|
2010-06-20 21:02:19 +02:00
|
|
|
if (drop_indicator_row_ != -1) {
|
|
|
|
if (cached_tree_.isNull()) {
|
|
|
|
cached_tree_ = QPixmap(size());
|
|
|
|
cached_tree_.fill(Qt::transparent);
|
|
|
|
|
|
|
|
QPainter cache_painter(&cached_tree_);
|
|
|
|
drawTree(&cache_painter, event->region());
|
|
|
|
}
|
|
|
|
|
|
|
|
p.drawPixmap(0, 0, cached_tree_);
|
|
|
|
} else {
|
|
|
|
drawTree(&p, event->region());
|
2010-04-23 15:10:03 +02:00
|
|
|
return;
|
2010-06-20 21:02:19 +02:00
|
|
|
}
|
2013-02-15 13:41:56 +01:00
|
|
|
|
2012-06-15 11:18:38 +02:00
|
|
|
const int first_column = header_->logicalIndex(0);
|
2010-04-23 15:10:03 +02:00
|
|
|
|
|
|
|
// Find the y position of the drop indicator
|
2012-06-15 11:18:38 +02:00
|
|
|
QModelIndex drop_index = model()->index(drop_indicator_row_, first_column);
|
2010-04-23 15:10:03 +02:00
|
|
|
int drop_pos = -1;
|
|
|
|
switch (dropIndicatorPosition()) {
|
|
|
|
case QAbstractItemView::OnItem:
|
2014-02-07 16:34:20 +01:00
|
|
|
return; // Don't draw anything
|
2010-04-23 15:10:03 +02:00
|
|
|
|
|
|
|
case QAbstractItemView::AboveItem:
|
|
|
|
drop_pos = visualRect(drop_index).top();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QAbstractItemView::BelowItem:
|
|
|
|
drop_pos = visualRect(drop_index).bottom() + 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QAbstractItemView::OnViewport:
|
2010-04-24 14:18:42 +02:00
|
|
|
if (model()->rowCount() == 0)
|
|
|
|
drop_pos = 1;
|
|
|
|
else
|
2014-10-15 21:57:57 +02:00
|
|
|
drop_pos = 1 +
|
|
|
|
visualRect(model()->index(model()->rowCount() - 1,
|
|
|
|
first_column)).bottom();
|
2010-04-23 15:10:03 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw a nice gradient first
|
|
|
|
QColor line_color(QApplication::palette().color(QPalette::Highlight));
|
|
|
|
QColor shadow_color(line_color.lighter(140));
|
|
|
|
QColor shadow_fadeout_color(shadow_color);
|
2010-04-24 14:18:42 +02:00
|
|
|
shadow_color.setAlpha(255);
|
|
|
|
shadow_fadeout_color.setAlpha(0);
|
2010-04-23 15:10:03 +02:00
|
|
|
|
2010-04-24 14:18:42 +02:00
|
|
|
QLinearGradient gradient(QPoint(0, drop_pos - kDropIndicatorGradientWidth),
|
|
|
|
QPoint(0, drop_pos + kDropIndicatorGradientWidth));
|
2010-04-23 15:10:03 +02:00
|
|
|
gradient.setColorAt(0.0, shadow_fadeout_color);
|
|
|
|
gradient.setColorAt(0.5, shadow_color);
|
|
|
|
gradient.setColorAt(1.0, shadow_fadeout_color);
|
2010-04-24 14:18:42 +02:00
|
|
|
QPen gradient_pen(QBrush(gradient), kDropIndicatorGradientWidth * 2);
|
2010-04-23 15:10:03 +02:00
|
|
|
p.setPen(gradient_pen);
|
2014-02-07 16:34:20 +01:00
|
|
|
p.drawLine(QPoint(0, drop_pos), QPoint(width(), drop_pos));
|
2010-04-23 15:10:03 +02:00
|
|
|
|
|
|
|
// Now draw the line on top
|
2010-04-24 14:18:42 +02:00
|
|
|
QPen line_pen(line_color, kDropIndicatorWidth);
|
2010-04-23 15:10:03 +02:00
|
|
|
p.setPen(line_pen);
|
2014-02-07 16:34:20 +01:00
|
|
|
p.drawLine(QPoint(0, drop_pos), QPoint(width(), drop_pos));
|
2010-04-23 15:10:03 +02:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::dragMoveEvent(QDragMoveEvent* event) {
|
2010-04-23 15:10:03 +02:00
|
|
|
QTreeView::dragMoveEvent(event);
|
|
|
|
|
|
|
|
QModelIndex index(indexAt(event->pos()));
|
2010-04-24 14:18:42 +02:00
|
|
|
drop_indicator_row_ = index.isValid() ? index.row() : 0;
|
2010-04-23 15:10:03 +02:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::dragEnterEvent(QDragEnterEvent* event) {
|
2010-06-20 21:02:19 +02:00
|
|
|
QTreeView::dragEnterEvent(event);
|
|
|
|
cached_tree_ = QPixmap();
|
2010-12-06 23:18:00 +01:00
|
|
|
drag_over_ = true;
|
2010-06-20 21:02:19 +02:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::dragLeaveEvent(QDragLeaveEvent* event) {
|
2010-04-23 15:10:03 +02:00
|
|
|
QTreeView::dragLeaveEvent(event);
|
2010-06-20 21:02:19 +02:00
|
|
|
cached_tree_ = QPixmap();
|
2010-12-06 23:18:00 +01:00
|
|
|
drag_over_ = false;
|
2010-04-23 15:10:03 +02:00
|
|
|
drop_indicator_row_ = -1;
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::dropEvent(QDropEvent* event) {
|
2010-04-23 15:10:03 +02:00
|
|
|
QTreeView::dropEvent(event);
|
2010-06-20 21:02:19 +02:00
|
|
|
cached_tree_ = QPixmap();
|
2010-04-23 15:10:03 +02:00
|
|
|
drop_indicator_row_ = -1;
|
2010-12-06 23:18:00 +01:00
|
|
|
drag_over_ = false;
|
2010-04-23 15:10:03 +02:00
|
|
|
}
|
2010-07-11 22:23:34 +02:00
|
|
|
|
|
|
|
void PlaylistView::PlaylistDestroyed() {
|
2014-02-06 16:49:49 +01:00
|
|
|
playlist_ = nullptr;
|
2010-07-11 22:23:34 +02:00
|
|
|
// We'll get a SetPlaylist() soon
|
|
|
|
}
|
2010-08-27 12:36:01 +02:00
|
|
|
|
|
|
|
void PlaylistView::ReloadSettings() {
|
|
|
|
QSettings s;
|
2011-03-19 10:41:00 +01:00
|
|
|
s.beginGroup(Playlist::kSettingsGroup);
|
2010-08-27 12:36:01 +02:00
|
|
|
glow_enabled_ = s.value("glow_effect", true).toBool();
|
2012-02-18 19:57:36 +01:00
|
|
|
|
2011-07-24 13:46:31 +02:00
|
|
|
if (setting_initial_header_layout_ || upgrading_from_qheaderview_) {
|
|
|
|
header_->SetStretchEnabled(s.value("stretch", true).toBool());
|
|
|
|
upgrading_from_qheaderview_ = false;
|
|
|
|
}
|
2010-08-27 12:36:01 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (currently_glowing_ && glow_enabled_ && isVisible()) StartGlowing();
|
|
|
|
if (!glow_enabled_) StopGlowing();
|
2010-08-27 15:15:32 +02:00
|
|
|
|
|
|
|
if (setting_initial_header_layout_) {
|
|
|
|
header_->SetColumnWidth(Playlist::Column_Length, 0.06);
|
|
|
|
header_->SetColumnWidth(Playlist::Column_Track, 0.05);
|
|
|
|
setting_initial_header_layout_ = false;
|
|
|
|
}
|
2012-03-06 13:40:19 +01:00
|
|
|
|
2012-01-04 19:45:08 +01:00
|
|
|
if (upgrading_from_version_ != -1) {
|
|
|
|
if (upgrading_from_version_ < 4) {
|
|
|
|
header_->SetColumnWidth(Playlist::Column_Source, 0.05);
|
|
|
|
}
|
|
|
|
upgrading_from_version_ = -1;
|
|
|
|
}
|
2010-12-22 11:45:14 +01:00
|
|
|
|
2011-11-12 17:12:03 +01:00
|
|
|
column_alignment_ = s.value("column_alignments").value<ColumnAlignmentMap>();
|
|
|
|
if (column_alignment_.isEmpty()) {
|
|
|
|
column_alignment_ = DefaultColumnAlignment();
|
|
|
|
}
|
|
|
|
|
2012-03-16 22:39:39 +01:00
|
|
|
emit ColumnAlignmentChanged(column_alignment_);
|
|
|
|
|
|
|
|
// Background:
|
2014-02-07 16:34:20 +01:00
|
|
|
QVariant q_playlistview_background_type =
|
|
|
|
s.value(kSettingBackgroundImageType);
|
2012-03-17 13:34:29 +01:00
|
|
|
BackgroundImageType background_type(Default);
|
2012-03-16 22:39:39 +01:00
|
|
|
// bg_enabled should also be checked for backward compatibility (in releases
|
|
|
|
// <= 1.0, there was just a boolean to activate/deactivate the background)
|
|
|
|
QVariant bg_enabled = s.value("bg_enabled");
|
|
|
|
if (q_playlistview_background_type.isValid()) {
|
2014-02-07 16:34:20 +01:00
|
|
|
background_type = static_cast<BackgroundImageType>(
|
|
|
|
q_playlistview_background_type.toInt());
|
2012-03-16 22:39:39 +01:00
|
|
|
} else if (bg_enabled.isValid()) {
|
|
|
|
if (bg_enabled.toBool()) {
|
|
|
|
background_type = Default;
|
|
|
|
} else {
|
|
|
|
background_type = None;
|
|
|
|
}
|
|
|
|
}
|
2014-02-07 16:34:20 +01:00
|
|
|
QString background_image_filename =
|
|
|
|
s.value(kSettingBackgroundImageFilename).toString();
|
2013-02-17 04:03:39 +01:00
|
|
|
int blur_radius = s.value("blur_radius", kDefaultBlurRadius).toInt();
|
|
|
|
int opacity_level = s.value("opacity_level", kDefaultOpacityLevel).toInt();
|
2012-03-16 22:39:39 +01:00
|
|
|
// Check if background properties have changed.
|
|
|
|
// We change properties only if they have actually changed, to avoid to call
|
|
|
|
// set_background_image when it is not needed, as this will cause the fading
|
|
|
|
// animation to start again. This also avoid to do useless
|
|
|
|
// "force_background_redraw".
|
2012-11-09 20:30:32 +01:00
|
|
|
if (background_image_filename != background_image_filename_ ||
|
2013-02-15 13:41:56 +01:00
|
|
|
background_type != background_image_type_ ||
|
2014-02-07 16:34:20 +01:00
|
|
|
blur_radius_ != blur_radius || opacity_level_ != opacity_level) {
|
2012-03-16 22:39:39 +01:00
|
|
|
// Store background properties
|
|
|
|
background_image_type_ = background_type;
|
|
|
|
background_image_filename_ = background_image_filename;
|
2012-11-09 20:17:26 +01:00
|
|
|
blur_radius_ = blur_radius;
|
2013-02-15 13:41:56 +01:00
|
|
|
opacity_level_ = opacity_level;
|
2012-03-16 22:39:39 +01:00
|
|
|
if (background_image_type_ == Custom) {
|
|
|
|
set_background_image(QImage(background_image_filename));
|
|
|
|
} else if (background_image_type_ == AlbumCover) {
|
|
|
|
set_background_image(current_song_cover_art_);
|
2012-03-18 19:22:08 +01:00
|
|
|
} else {
|
|
|
|
// User changed background image type to something that will not be
|
|
|
|
// painted through paintEvent: reset all background images.
|
|
|
|
// This avoid to use old (deprecated) images for fading when selecting
|
|
|
|
// AlbumCover or Custom background image type later.
|
|
|
|
set_background_image(QImage());
|
|
|
|
cached_scaled_background_image_ = QPixmap();
|
|
|
|
previous_background_image_ = QPixmap();
|
2012-03-16 22:39:39 +01:00
|
|
|
}
|
2014-02-07 16:34:20 +01:00
|
|
|
setProperty("default_background_enabled",
|
|
|
|
background_image_type_ == Default);
|
2012-03-16 22:39:39 +01:00
|
|
|
emit BackgroundPropertyChanged();
|
|
|
|
force_background_redraw_ = true;
|
2012-02-18 19:57:36 +01:00
|
|
|
}
|
2014-06-23 07:30:02 +02:00
|
|
|
|
2014-10-15 21:57:57 +02:00
|
|
|
if (!s.value("click_edit_inline", true).toBool())
|
|
|
|
setEditTriggers(editTriggers() & ~QAbstractItemView::SelectedClicked);
|
2014-06-23 07:30:02 +02:00
|
|
|
else
|
2014-10-15 21:57:57 +02:00
|
|
|
setEditTriggers(editTriggers() | QAbstractItemView::SelectedClicked);
|
2010-08-27 12:36:01 +02:00
|
|
|
}
|
2010-08-27 14:42:06 +02:00
|
|
|
|
|
|
|
void PlaylistView::SaveSettings() {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (read_only_settings_) return;
|
2010-10-29 20:58:43 +02:00
|
|
|
|
2010-08-27 14:42:06 +02:00
|
|
|
QSettings s;
|
2011-03-19 10:41:00 +01:00
|
|
|
s.beginGroup(Playlist::kSettingsGroup);
|
2010-08-27 14:42:06 +02:00
|
|
|
s.setValue("glow_effect", glow_enabled_);
|
2011-11-12 17:12:03 +01:00
|
|
|
s.setValue("column_alignments", QVariant::fromValue(column_alignment_));
|
2012-02-18 19:57:36 +01:00
|
|
|
s.setValue(kSettingBackgroundImageType, background_image_type_);
|
2010-08-27 14:42:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::StretchChanged(bool stretch) {
|
2014-02-07 16:34:20 +01:00
|
|
|
setHorizontalScrollBarPolicy(stretch ? Qt::ScrollBarAlwaysOff
|
|
|
|
: Qt::ScrollBarAsNeeded);
|
2011-07-25 14:39:47 +02:00
|
|
|
SaveGeometry();
|
2010-08-27 14:42:06 +02:00
|
|
|
}
|
2010-11-20 21:00:40 +01:00
|
|
|
|
|
|
|
void PlaylistView::DynamicModeChanged(bool dynamic) {
|
|
|
|
if (!dynamic) {
|
|
|
|
dynamic_controls_->hide();
|
|
|
|
} else {
|
|
|
|
RepositionDynamicControls();
|
|
|
|
dynamic_controls_->show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::resizeEvent(QResizeEvent* e) {
|
|
|
|
QTreeView::resizeEvent(e);
|
|
|
|
if (dynamic_controls_->isVisible()) {
|
|
|
|
RepositionDynamicControls();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::RepositionDynamicControls() {
|
|
|
|
dynamic_controls_->resize(dynamic_controls_->sizeHint());
|
|
|
|
dynamic_controls_->move((width() - dynamic_controls_->width()) / 2,
|
|
|
|
height() - dynamic_controls_->height() - 20);
|
|
|
|
}
|
2010-11-21 00:20:27 +01:00
|
|
|
|
|
|
|
bool PlaylistView::eventFilter(QObject* object, QEvent* event) {
|
|
|
|
if (event->type() == QEvent::Enter &&
|
|
|
|
(object == horizontalScrollBar() || object == verticalScrollBar())) {
|
|
|
|
RatingHoverOut();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return QObject::eventFilter(object, event);
|
|
|
|
}
|
2011-09-27 23:28:12 +02:00
|
|
|
|
|
|
|
void PlaylistView::rowsInserted(const QModelIndex& parent, int start, int end) {
|
|
|
|
const bool at_end = end == model()->rowCount(parent) - 1;
|
|
|
|
|
|
|
|
QTreeView::rowsInserted(parent, start, end);
|
|
|
|
|
|
|
|
if (at_end) {
|
|
|
|
// If the rows were inserted at the end of the playlist then let's scroll
|
|
|
|
// the view so the user can see.
|
2014-02-07 16:34:20 +01:00
|
|
|
scrollTo(model()->index(start, 0, parent),
|
|
|
|
QAbstractItemView::PositionAtTop);
|
2011-09-27 23:28:12 +02:00
|
|
|
}
|
|
|
|
}
|
2011-11-12 17:12:03 +01:00
|
|
|
|
|
|
|
ColumnAlignmentMap PlaylistView::DefaultColumnAlignment() {
|
|
|
|
ColumnAlignmentMap ret;
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
ret[Playlist::Column_Length] = ret[Playlist::Column_Track] =
|
|
|
|
ret[Playlist::Column_Disc] = ret[Playlist::Column_Year] =
|
|
|
|
ret[Playlist::Column_BPM] = ret[Playlist::Column_Bitrate] =
|
|
|
|
ret[Playlist::Column_Samplerate] =
|
|
|
|
ret[Playlist::Column_Filesize] =
|
|
|
|
ret[Playlist::Column_PlayCount] =
|
|
|
|
ret[Playlist::Column_SkipCount] =
|
2015-06-30 19:25:38 +02:00
|
|
|
ret[Playlist::Column_OriginalYear] =
|
2015-07-02 14:59:30 +02:00
|
|
|
(Qt::AlignRight | Qt::AlignVCenter);
|
2014-02-07 16:34:20 +01:00
|
|
|
ret[Playlist::Column_Score] = (Qt::AlignCenter);
|
2011-11-12 17:12:03 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::SetColumnAlignment(int section, Qt::Alignment alignment) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (section < 0) return;
|
2011-11-12 17:12:03 +01:00
|
|
|
|
|
|
|
column_alignment_[section] = alignment;
|
|
|
|
emit ColumnAlignmentChanged(column_alignment_);
|
|
|
|
SaveSettings();
|
|
|
|
}
|
2011-11-12 17:23:41 +01:00
|
|
|
|
|
|
|
Qt::Alignment PlaylistView::column_alignment(int section) const {
|
|
|
|
return column_alignment_.value(section, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
|
}
|
2012-01-29 20:24:13 +01:00
|
|
|
|
|
|
|
void PlaylistView::CopyCurrentSongToClipboard() const {
|
|
|
|
// Get the display text of all visible columns.
|
|
|
|
QStringList columns;
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0; i < header()->count(); ++i) {
|
2012-01-29 20:24:13 +01:00
|
|
|
if (header()->isSectionHidden(i)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
const QVariant data =
|
|
|
|
model()->data(currentIndex().sibling(currentIndex().row(), i));
|
2012-01-29 20:24:13 +01:00
|
|
|
if (data.type() == QVariant::String) {
|
|
|
|
columns << data.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the song's URL
|
2014-02-07 16:34:20 +01:00
|
|
|
const QUrl url = model()
|
|
|
|
->data(currentIndex().sibling(currentIndex().row(),
|
|
|
|
Playlist::Column_Filename))
|
|
|
|
.toUrl();
|
2012-01-29 20:24:13 +01:00
|
|
|
|
|
|
|
QMimeData* mime_data = new QMimeData;
|
|
|
|
mime_data->setUrls(QList<QUrl>() << url);
|
|
|
|
mime_data->setText(columns.join(" - "));
|
|
|
|
|
|
|
|
QApplication::clipboard()->setMimeData(mime_data);
|
|
|
|
}
|
2012-03-03 01:20:37 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void PlaylistView::CurrentSongChanged(const Song& song, const QString& uri,
|
2012-03-03 01:20:37 +01:00
|
|
|
const QImage& song_art) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (current_song_cover_art_ == song_art) return;
|
2012-03-16 22:39:39 +01:00
|
|
|
|
2012-03-06 13:40:19 +01:00
|
|
|
current_song_cover_art_ = song_art;
|
2012-03-03 01:20:37 +01:00
|
|
|
if (background_image_type_ == AlbumCover) {
|
2012-03-08 23:42:34 +01:00
|
|
|
if (song.art_automatic().isEmpty() && song.art_manual().isEmpty()) {
|
2012-03-10 18:55:22 +01:00
|
|
|
set_background_image(QImage());
|
2012-03-08 23:42:34 +01:00
|
|
|
} else {
|
2012-03-10 18:55:22 +01:00
|
|
|
set_background_image(current_song_cover_art_);
|
2012-03-08 23:42:34 +01:00
|
|
|
}
|
2012-03-03 01:20:37 +01:00
|
|
|
force_background_redraw_ = true;
|
2012-03-06 13:40:19 +01:00
|
|
|
update();
|
2012-03-03 01:20:37 +01:00
|
|
|
}
|
|
|
|
}
|
2012-03-08 22:39:27 +01:00
|
|
|
|
2012-03-10 18:55:22 +01:00
|
|
|
void PlaylistView::set_background_image(const QImage& image) {
|
2012-03-28 00:45:14 +02:00
|
|
|
// Save previous image, for fading
|
|
|
|
previous_background_image_ = cached_scaled_background_image_;
|
2012-03-16 22:39:39 +01:00
|
|
|
|
2013-02-17 04:07:46 +01:00
|
|
|
if (image.isNull() || image.format() == QImage::Format_ARGB32) {
|
2012-03-10 18:55:22 +01:00
|
|
|
background_image_ = image;
|
2013-02-17 04:07:46 +01:00
|
|
|
} else {
|
|
|
|
background_image_ = image.convertToFormat(QImage::Format_ARGB32);
|
2012-03-08 22:39:27 +01:00
|
|
|
}
|
2012-03-16 22:39:39 +01:00
|
|
|
|
2013-02-17 04:07:46 +01:00
|
|
|
if (!background_image_.isNull()) {
|
|
|
|
// Apply opacity filter
|
|
|
|
uchar* bits = background_image_.bits();
|
2014-02-07 16:34:20 +01:00
|
|
|
for (int i = 0;
|
|
|
|
i < background_image_.height() * background_image_.bytesPerLine();
|
|
|
|
i += 4) {
|
|
|
|
bits[i + 3] = (opacity_level_ / 100.0) * 255;
|
2013-02-17 04:07:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (blur_radius_ != 0) {
|
2014-02-07 16:34:20 +01:00
|
|
|
QImage blurred(background_image_.size(),
|
|
|
|
QImage::Format_ARGB32_Premultiplied);
|
2013-02-17 04:07:46 +01:00
|
|
|
blurred.fill(Qt::transparent);
|
|
|
|
QPainter blur_painter(&blurred);
|
2013-02-17 02:02:13 +01:00
|
|
|
qt_blurImage(&blur_painter, background_image_, blur_radius_, true, false);
|
2013-02-17 04:07:46 +01:00
|
|
|
blur_painter.end();
|
2012-11-10 08:04:55 +01:00
|
|
|
|
2013-02-17 04:07:46 +01:00
|
|
|
background_image_ = blurred;
|
|
|
|
}
|
2012-11-10 08:04:55 +01:00
|
|
|
}
|
2012-11-09 20:17:26 +01:00
|
|
|
|
2012-03-28 00:45:14 +02:00
|
|
|
if (isVisible()) {
|
|
|
|
previous_background_image_opacity_ = 1.0;
|
2012-03-16 22:39:39 +01:00
|
|
|
fade_animation_->start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistView::FadePreviousBackgroundImage(qreal value) {
|
|
|
|
previous_background_image_opacity_ = value;
|
|
|
|
if (qFuzzyCompare(previous_background_image_opacity_, qreal(0.0))) {
|
|
|
|
previous_background_image_ = QPixmap();
|
2012-03-18 19:22:08 +01:00
|
|
|
previous_background_image_opacity_ = 0.0;
|
2012-03-16 22:39:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
update();
|
2012-03-08 22:39:27 +01:00
|
|
|
}
|
|
|
|
|
2012-03-13 23:15:53 +01:00
|
|
|
void PlaylistView::PlayerStopped() {
|
|
|
|
CurrentSongChanged(Song(), QString(), QImage());
|
|
|
|
}
|
2014-01-14 10:07:03 +01:00
|
|
|
|
|
|
|
void PlaylistView::focusInEvent(QFocusEvent* event) {
|
|
|
|
QTreeView::focusInEvent(event);
|
|
|
|
|
|
|
|
if (event->reason() == Qt::TabFocusReason ||
|
|
|
|
event->reason() == Qt::BacktabFocusReason) {
|
|
|
|
// If there's a current item but no selection it probably means the list was
|
|
|
|
// filtered, and the selected item does not match the filter. If there's
|
|
|
|
// only 1 item in the view it is now impossible to select that item without
|
|
|
|
// using the mouse.
|
|
|
|
const QModelIndex& current = selectionModel()->currentIndex();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (current.isValid() && selectionModel()->selectedIndexes().isEmpty()) {
|
2014-01-14 10:07:03 +01:00
|
|
|
QItemSelection new_selection(
|
|
|
|
current.sibling(current.row(), 0),
|
|
|
|
current.sibling(current.row(),
|
|
|
|
current.model()->columnCount(current.parent()) - 1));
|
|
|
|
selectionModel()->select(new_selection, QItemSelectionModel::Select);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|