2018-02-27 18:06:05 +01:00
|
|
|
/*
|
|
|
|
* Strawberry Music Player
|
|
|
|
* This file was part of Clementine.
|
|
|
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
2021-03-20 21:14:47 +01:00
|
|
|
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
2018-02-27 18:06:05 +01:00
|
|
|
*
|
|
|
|
* Strawberry 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.
|
|
|
|
*
|
|
|
|
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
2018-08-09 18:39:44 +02:00
|
|
|
*
|
2018-02-27 18:06:05 +01:00
|
|
|
*/
|
|
|
|
|
2020-02-12 00:07:05 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QApplication>
|
|
|
|
#include <QObject>
|
|
|
|
#include <QWidget>
|
2019-03-22 23:23:50 +01:00
|
|
|
#include <QItemSelectionModel>
|
|
|
|
#include <QSortFilterProxyModel>
|
2020-08-09 14:00:56 +02:00
|
|
|
#include <QScrollBar>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QAction>
|
|
|
|
#include <QPoint>
|
|
|
|
#include <QString>
|
|
|
|
#include <QSize>
|
|
|
|
#include <QFont>
|
|
|
|
#include <QIcon>
|
|
|
|
#include <QColor>
|
|
|
|
#include <QFrame>
|
|
|
|
#include <QPalette>
|
|
|
|
#include <QTimer>
|
|
|
|
#include <QTimeLine>
|
|
|
|
#include <QFileDialog>
|
|
|
|
#include <QLabel>
|
|
|
|
#include <QKeySequence>
|
|
|
|
#include <QToolButton>
|
|
|
|
#include <QUndoStack>
|
|
|
|
#include <QtEvents>
|
|
|
|
#include <QSettings>
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2023-07-21 05:55:24 +02:00
|
|
|
#include "core/shared_ptr.h"
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "core/iconloader.h"
|
|
|
|
#include "playlist.h"
|
|
|
|
#include "playlisttabbar.h"
|
|
|
|
#include "playlistview.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
#include "playlistcontainer.h"
|
|
|
|
#include "playlistmanager.h"
|
2022-08-09 17:23:46 +02:00
|
|
|
#include "playlistfilter.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
#include "playlistparsers/playlistparser.h"
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "ui_playlistcontainer.h"
|
2019-05-08 23:34:44 +02:00
|
|
|
#include "widgets/qsearchfield.h"
|
2020-06-28 18:36:48 +02:00
|
|
|
#include "settings/appearancesettingspage.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
const char *PlaylistContainer::kSettingsGroup = "Playlist";
|
|
|
|
const int PlaylistContainer::kFilterDelayMs = 100;
|
|
|
|
const int PlaylistContainer::kFilterDelayPlaylistSizeThreshold = 5000;
|
|
|
|
|
|
|
|
PlaylistContainer::PlaylistContainer(QWidget *parent)
|
|
|
|
: QWidget(parent),
|
|
|
|
ui_(new Ui_PlaylistContainer),
|
|
|
|
manager_(nullptr),
|
|
|
|
undo_(nullptr),
|
|
|
|
redo_(nullptr),
|
|
|
|
playlist_(nullptr),
|
|
|
|
starting_up_(true),
|
|
|
|
tab_bar_visible_(false),
|
|
|
|
tab_bar_animation_(new QTimeLine(500, this)),
|
|
|
|
no_matches_label_(nullptr),
|
|
|
|
filter_timer_(new QTimer(this)) {
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->setupUi(this);
|
|
|
|
|
|
|
|
no_matches_label_ = new QLabel(ui_->playlist);
|
|
|
|
no_matches_label_->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
|
|
|
no_matches_label_->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
no_matches_label_->setWordWrap(true);
|
|
|
|
no_matches_label_->raise();
|
|
|
|
no_matches_label_->hide();
|
|
|
|
|
|
|
|
// Set the colour of the no matches label to the disabled text colour
|
|
|
|
QPalette no_matches_palette = no_matches_label_->palette();
|
|
|
|
const QColor no_matches_color = no_matches_palette.color(QPalette::Disabled, QPalette::Text);
|
|
|
|
no_matches_palette.setColor(QPalette::Normal, QPalette::WindowText, no_matches_color);
|
|
|
|
no_matches_palette.setColor(QPalette::Inactive, QPalette::WindowText, no_matches_color);
|
|
|
|
no_matches_label_->setPalette(no_matches_palette);
|
|
|
|
|
|
|
|
// Remove QFrame border
|
|
|
|
ui_->toolbar->setStyleSheet("QFrame { border: 0px; }");
|
|
|
|
|
|
|
|
// Make it bold
|
|
|
|
QFont no_matches_font = no_matches_label_->font();
|
|
|
|
no_matches_font.setBold(true);
|
|
|
|
no_matches_label_->setFont(no_matches_font);
|
|
|
|
|
|
|
|
settings_.beginGroup(kSettingsGroup);
|
|
|
|
|
|
|
|
// Tab bar
|
|
|
|
ui_->tab_bar->setExpanding(false);
|
|
|
|
ui_->tab_bar->setMovable(true);
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(tab_bar_animation_, &QTimeLine::frameChanged, this, &PlaylistContainer::SetTabBarHeight);
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->tab_bar->setMaximumHeight(0);
|
|
|
|
|
|
|
|
// Connections
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(ui_->tab_bar, &PlaylistTabBar::currentChanged, this, &PlaylistContainer::Save);
|
|
|
|
QObject::connect(ui_->tab_bar, &PlaylistTabBar::Save, this, &PlaylistContainer::SaveCurrentPlaylist);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// set up timer for delayed filter updates
|
|
|
|
filter_timer_->setSingleShot(true);
|
|
|
|
filter_timer_->setInterval(kFilterDelayMs);
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(filter_timer_, &QTimer::timeout, this, &PlaylistContainer::UpdateFilter);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Replace playlist search filter with native search box.
|
2022-08-09 17:23:46 +02:00
|
|
|
QObject::connect(ui_->search_field, &QSearchField::textChanged, this, &PlaylistContainer::MaybeUpdateFilter);
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(ui_->playlist, &PlaylistView::FocusOnFilterSignal, this, &PlaylistContainer::FocusOnFilter);
|
2022-08-09 17:23:46 +02:00
|
|
|
ui_->search_field->installEventFilter(this);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2023-07-31 13:37:42 +02:00
|
|
|
QString available_fields = PlaylistFilter().column_names_.keys().join(", ");
|
|
|
|
ui_->search_field->setToolTip(
|
|
|
|
QString("<html><head/><body><p>") +
|
|
|
|
tr("Prefix a search term with a field name to limit the search to that field, e.g.:") +
|
|
|
|
QString(" ") +
|
|
|
|
QString("<span style=\"font-weight:600;\">") +
|
|
|
|
tr("artist") +
|
|
|
|
QString(":</span><span style=\"font-style:italic;\">Strawbs</span> ") +
|
|
|
|
tr("searches the collection for all artists that contain the word %1. ").arg("Strawbs") +
|
|
|
|
QString("</p><p>") +
|
|
|
|
|
|
|
|
tr("Search terms for numerical fields can be prefixed with %1 or %2 to refine the search, e.g.: ")
|
|
|
|
.arg(" =, !=, <, >, <=", ">=") +
|
|
|
|
QString("<span style=\"font-weight:600;\">") +
|
|
|
|
tr("rating") +
|
|
|
|
QString("</span>") +
|
|
|
|
QString(":>=") +
|
|
|
|
QString("<span style=\"font-weight:italic;\">4</span>") +
|
|
|
|
QString("</p><p>") +
|
|
|
|
|
|
|
|
tr("Multiple search terms can also be combined with \"%1\" (default) and \"%2\", as well as grouped with parentheses. ")
|
|
|
|
.arg("AND", "OR") +
|
|
|
|
|
|
|
|
QString("</p><p><span style=\"font-weight:600;\">") +
|
|
|
|
tr("Available fields") +
|
|
|
|
QString(": ") + QString("</span><span style=\"font-style:italic;\">") +
|
|
|
|
available_fields +
|
|
|
|
QString("</span>.") +
|
|
|
|
QString("</p></body></html>")
|
|
|
|
);
|
|
|
|
|
|
|
|
|
2020-01-04 06:38:25 +01:00
|
|
|
ReloadSettings();
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PlaylistContainer::~PlaylistContainer() { delete ui_; }
|
|
|
|
|
|
|
|
PlaylistView *PlaylistContainer::view() const { return ui_->playlist; }
|
|
|
|
|
2022-07-20 01:09:00 +02:00
|
|
|
void PlaylistContainer::SetActions(QAction *new_playlist, QAction *load_playlist, QAction *save_playlist, QAction *clear_playlist, QAction *next_playlist, QAction *previous_playlist, QAction *save_all_playlists) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
ui_->create_new->setDefaultAction(new_playlist);
|
|
|
|
ui_->load->setDefaultAction(load_playlist);
|
|
|
|
ui_->save->setDefaultAction(save_playlist);
|
|
|
|
ui_->clear->setDefaultAction(clear_playlist);
|
|
|
|
|
|
|
|
ui_->tab_bar->SetActions(new_playlist, load_playlist);
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(new_playlist, &QAction::triggered, this, &PlaylistContainer::NewPlaylist);
|
2021-02-04 00:38:10 +01:00
|
|
|
QObject::connect(save_playlist, &QAction::triggered, this, &PlaylistContainer::SaveCurrentPlaylist);
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(load_playlist, &QAction::triggered, this, &PlaylistContainer::LoadPlaylist);
|
|
|
|
QObject::connect(clear_playlist, &QAction::triggered, this, &PlaylistContainer::ClearPlaylist);
|
|
|
|
QObject::connect(next_playlist, &QAction::triggered, this, &PlaylistContainer::GoToNextPlaylistTab);
|
|
|
|
QObject::connect(previous_playlist, &QAction::triggered, this, &PlaylistContainer::GoToPreviousPlaylistTab);
|
|
|
|
QObject::connect(clear_playlist, &QAction::triggered, this, &PlaylistContainer::ClearPlaylist);
|
2023-07-21 05:55:24 +02:00
|
|
|
QObject::connect(save_all_playlists, &QAction::triggered, &*manager_, &PlaylistManager::SaveAllPlaylists);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-07-21 05:55:24 +02:00
|
|
|
void PlaylistContainer::SetManager(SharedPtr<PlaylistManager> manager) {
|
2018-05-01 00:41:33 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
manager_ = manager;
|
|
|
|
ui_->tab_bar->SetManager(manager);
|
|
|
|
|
2023-07-21 05:55:24 +02:00
|
|
|
QObject::connect(ui_->tab_bar, &PlaylistTabBar::CurrentIdChanged, &*manager, &PlaylistManager::SetCurrentPlaylist);
|
|
|
|
QObject::connect(ui_->tab_bar, &PlaylistTabBar::Rename, &*manager, &PlaylistManager::Rename);
|
|
|
|
QObject::connect(ui_->tab_bar, &PlaylistTabBar::Close, &*manager, &PlaylistManager::Close);
|
|
|
|
QObject::connect(ui_->tab_bar, &PlaylistTabBar::PlaylistFavorited, &*manager, &PlaylistManager::Favorite);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2023-07-21 05:55:24 +02:00
|
|
|
QObject::connect(ui_->tab_bar, &PlaylistTabBar::PlaylistOrderChanged, &*manager, &PlaylistManager::ChangePlaylistOrder);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2023-07-21 05:55:24 +02:00
|
|
|
QObject::connect(&*manager, &PlaylistManager::CurrentChanged, this, &PlaylistContainer::SetViewModel);
|
|
|
|
QObject::connect(&*manager, &PlaylistManager::PlaylistAdded, this, &PlaylistContainer::PlaylistAdded);
|
|
|
|
QObject::connect(&*manager, &PlaylistManager::PlaylistManagerInitialized, this, &PlaylistContainer::Started);
|
|
|
|
QObject::connect(&*manager, &PlaylistManager::PlaylistClosed, this, &PlaylistContainer::PlaylistClosed);
|
|
|
|
QObject::connect(&*manager, &PlaylistManager::PlaylistRenamed, this, &PlaylistContainer::PlaylistRenamed);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-09 14:00:56 +02:00
|
|
|
void PlaylistContainer::SetViewModel(Playlist *playlist, const int scroll_position) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (view()->selectionModel()) {
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::disconnect(view()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PlaylistContainer::SelectionChanged);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
2022-08-09 17:23:46 +02:00
|
|
|
if (playlist_ && playlist_->filter()) {
|
|
|
|
QObject::disconnect(playlist_->filter(), &QSortFilterProxyModel::modelReset, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::disconnect(playlist_->filter(), &QSortFilterProxyModel::rowsInserted, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::disconnect(playlist_->filter(), &QSortFilterProxyModel::rowsRemoved, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
if (playlist_) {
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::disconnect(playlist_, &Playlist::modelReset, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::disconnect(playlist_, &Playlist::rowsInserted, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::disconnect(playlist_, &Playlist::rowsRemoved, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
playlist_ = playlist;
|
|
|
|
|
|
|
|
// Set the view
|
|
|
|
playlist->IgnoreSorting(true);
|
2022-08-09 17:23:46 +02:00
|
|
|
view()->setModel(playlist->filter());
|
2018-02-27 18:06:05 +01:00
|
|
|
view()->SetPlaylist(playlist);
|
|
|
|
view()->selectionModel()->select(manager_->current_selection(), QItemSelectionModel::ClearAndSelect);
|
2020-08-09 14:00:56 +02:00
|
|
|
if (scroll_position != 0) view()->verticalScrollBar()->setValue(scroll_position);
|
2018-02-27 18:06:05 +01:00
|
|
|
playlist->IgnoreSorting(false);
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(view()->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PlaylistContainer::SelectionChanged);
|
2018-02-27 18:06:05 +01:00
|
|
|
emit ViewSelectionModelChanged();
|
|
|
|
|
|
|
|
// Update filter
|
2022-08-09 17:23:46 +02:00
|
|
|
ui_->search_field->setText(playlist->filter()->filter_text());
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Update the no matches label
|
2022-08-09 17:23:46 +02:00
|
|
|
QObject::connect(playlist_->filter(), &QSortFilterProxyModel::modelReset, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::connect(playlist_->filter(), &QSortFilterProxyModel::rowsInserted, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::connect(playlist_->filter(), &QSortFilterProxyModel::rowsRemoved, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(playlist_, &Playlist::modelReset, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::connect(playlist_, &Playlist::rowsInserted, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
|
|
|
QObject::connect(playlist_, &Playlist::rowsRemoved, this, &PlaylistContainer::UpdateNoMatchesLabel);
|
2018-02-27 18:06:05 +01:00
|
|
|
UpdateNoMatchesLabel();
|
|
|
|
|
|
|
|
// Ensure that tab is current
|
2021-08-23 21:21:08 +02:00
|
|
|
if (ui_->tab_bar->current_id() != manager_->current_id()) {
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->tab_bar->set_current_id(manager_->current_id());
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Sort out the undo/redo actions
|
|
|
|
delete undo_;
|
|
|
|
delete redo_;
|
2022-08-09 17:04:59 +02:00
|
|
|
undo_ = playlist->undo_stack()->createUndoAction(this, tr("Undo"));
|
|
|
|
redo_ = playlist->undo_stack()->createRedoAction(this, tr("Redo"));
|
2018-02-27 18:06:05 +01:00
|
|
|
undo_->setIcon(IconLoader::Load("edit-undo"));
|
|
|
|
undo_->setShortcut(QKeySequence::Undo);
|
|
|
|
redo_->setIcon(IconLoader::Load("edit-redo"));
|
|
|
|
redo_->setShortcut(QKeySequence::Redo);
|
|
|
|
|
|
|
|
ui_->undo->setDefaultAction(undo_);
|
|
|
|
ui_->redo->setDefaultAction(redo_);
|
|
|
|
|
|
|
|
emit UndoRedoActionsChanged(undo_, redo_);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-01-04 06:38:25 +01:00
|
|
|
void PlaylistContainer::ReloadSettings() {
|
|
|
|
|
2020-06-28 18:36:48 +02:00
|
|
|
QSettings s;
|
|
|
|
s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
|
2020-06-29 01:09:02 +02:00
|
|
|
int iconsize = s.value(AppearanceSettingsPage::kIconSizePlaylistButtons, 20).toInt();
|
2020-06-28 18:36:48 +02:00
|
|
|
s.endGroup();
|
|
|
|
|
2020-06-29 01:09:02 +02:00
|
|
|
ui_->create_new->setIconSize(QSize(iconsize, iconsize));
|
|
|
|
ui_->load->setIconSize(QSize(iconsize, iconsize));
|
|
|
|
ui_->save->setIconSize(QSize(iconsize, iconsize));
|
|
|
|
ui_->clear->setIconSize(QSize(iconsize, iconsize));
|
|
|
|
ui_->undo->setIconSize(QSize(iconsize, iconsize));
|
|
|
|
ui_->redo->setIconSize(QSize(iconsize, iconsize));
|
2022-08-09 17:23:46 +02:00
|
|
|
ui_->search_field->setIconSize(iconsize);
|
2020-06-28 18:36:48 +02:00
|
|
|
|
2020-01-04 06:38:25 +01:00
|
|
|
bool playlist_clear = settings_.value("playlist_clear", true).toBool();
|
|
|
|
if (playlist_clear) {
|
|
|
|
ui_->clear->show();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ui_->clear->hide();
|
|
|
|
}
|
|
|
|
|
2021-10-23 22:48:02 +02:00
|
|
|
bool show_toolbar = settings_.value("show_toolbar", true).toBool();
|
|
|
|
ui_->toolbar->setVisible(show_toolbar);
|
|
|
|
|
2022-08-09 17:23:46 +02:00
|
|
|
if (!show_toolbar) ui_->search_field->clear();
|
2021-11-07 19:24:01 +01:00
|
|
|
|
2020-01-04 06:38:25 +01:00
|
|
|
}
|
|
|
|
|
2021-09-27 21:42:30 +02:00
|
|
|
bool PlaylistContainer::SearchFieldHasFocus() const {
|
2022-10-12 00:11:12 +02:00
|
|
|
return ui_->toolbar->isVisible() && ui_->search_field->hasFocus();
|
2021-09-27 21:42:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::FocusSearchField() {
|
2022-08-09 17:23:46 +02:00
|
|
|
if (ui_->toolbar->isVisible()) ui_->search_field->setFocus();
|
2021-09-27 21:42:30 +02:00
|
|
|
}
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
void PlaylistContainer::ActivePlaying() {
|
|
|
|
UpdateActiveIcon(QIcon(":/pictures/tiny-play.png"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::ActivePaused() {
|
|
|
|
UpdateActiveIcon(QIcon(":/pictures/tiny-pause.png"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::ActiveStopped() { UpdateActiveIcon(QIcon()); }
|
|
|
|
|
|
|
|
void PlaylistContainer::UpdateActiveIcon(const QIcon &icon) {
|
|
|
|
|
|
|
|
// Unset all existing icons
|
|
|
|
for (int i = 0; i < ui_->tab_bar->count(); ++i) {
|
|
|
|
ui_->tab_bar->setTabIcon(i, QIcon());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set our icon
|
|
|
|
if (!icon.isNull()) ui_->tab_bar->set_icon_by_id(manager_->active_id(), icon);
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void PlaylistContainer::PlaylistAdded(const int id, const QString &name, const bool favorite) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
const int index = ui_->tab_bar->count();
|
|
|
|
ui_->tab_bar->InsertTab(id, index, name, favorite);
|
|
|
|
|
2022-08-28 02:44:37 +02:00
|
|
|
// Are we start up, should we select this tab?
|
2018-02-27 18:06:05 +01:00
|
|
|
if (starting_up_ && settings_.value("current_playlist", 1).toInt() == id) {
|
|
|
|
starting_up_ = false;
|
|
|
|
ui_->tab_bar->set_current_id(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ui_->tab_bar->count() > 1) {
|
2018-05-01 00:41:33 +02:00
|
|
|
// Have to do this here because sizeHint() is only valid when there's a tab in the bar.
|
2018-02-27 18:06:05 +01:00
|
|
|
tab_bar_animation_->setFrameRange(0, ui_->tab_bar->sizeHint().height());
|
|
|
|
|
|
|
|
if (!isVisible()) {
|
2022-08-28 02:44:37 +02:00
|
|
|
// Skip the animation since the window is hidden (e.g. if we're still loading the UI).
|
2018-02-27 18:06:05 +01:00
|
|
|
tab_bar_visible_ = true;
|
|
|
|
ui_->tab_bar->setMaximumHeight(tab_bar_animation_->endFrame());
|
2020-11-20 21:47:01 +01:00
|
|
|
}
|
|
|
|
else {
|
2018-02-27 18:06:05 +01:00
|
|
|
SetTabBarVisible(true);
|
|
|
|
}
|
|
|
|
}
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::Started() { starting_up_ = false; }
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void PlaylistContainer::PlaylistClosed(const int id) {
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->tab_bar->RemoveTab(id);
|
|
|
|
|
|
|
|
if (ui_->tab_bar->count() <= 1) SetTabBarVisible(false);
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void PlaylistContainer::PlaylistRenamed(const int id, const QString &new_name) {
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->tab_bar->set_text_by_id(id, new_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::NewPlaylist() { manager_->New(tr("Playlist")); }
|
|
|
|
|
|
|
|
void PlaylistContainer::LoadPlaylist() {
|
2018-10-02 00:38:52 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
QString filename = settings_.value("last_load_playlist").toString();
|
2023-02-18 14:09:27 +01:00
|
|
|
filename = QFileDialog::getOpenFileName(this, tr("Load playlist"), filename, manager_->parser()->filters(PlaylistParser::Type::Load));
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (filename.isNull()) return;
|
|
|
|
|
|
|
|
settings_.setValue("last_load_playlist", filename);
|
|
|
|
|
|
|
|
manager_->Load(filename);
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2021-02-06 00:30:40 +01:00
|
|
|
void PlaylistContainer::SavePlaylist(const int id) {
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
// Use the tab name as the suggested name
|
|
|
|
QString suggested_name = ui_->tab_bar->tabText(ui_->tab_bar->currentIndex());
|
|
|
|
|
|
|
|
manager_->SaveWithUI(id, suggested_name);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-11-20 21:47:01 +01:00
|
|
|
void PlaylistContainer::ClearPlaylist() {}
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
void PlaylistContainer::GoToNextPlaylistTab() {
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2022-08-28 02:44:37 +02:00
|
|
|
// Get the next tab's id
|
2018-05-01 00:41:33 +02:00
|
|
|
int id_next = ui_->tab_bar->id_of((ui_->tab_bar->currentIndex() + 1) % ui_->tab_bar->count());
|
2018-02-27 18:06:05 +01:00
|
|
|
// Switch to next tab
|
|
|
|
manager_->SetCurrentPlaylist(id_next);
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::GoToPreviousPlaylistTab() {
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2022-08-28 02:44:37 +02:00
|
|
|
// Get the next tab's id
|
2021-07-11 09:49:38 +02:00
|
|
|
int id_previous = ui_->tab_bar->id_of((ui_->tab_bar->currentIndex() + ui_->tab_bar->count() - 1) % ui_->tab_bar->count());
|
2018-02-27 18:06:05 +01:00
|
|
|
// Switch to next tab
|
|
|
|
manager_->SetCurrentPlaylist(id_previous);
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::Save() {
|
2018-10-02 00:38:52 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
if (starting_up_) return;
|
|
|
|
|
|
|
|
settings_.setValue("current_playlist", ui_->tab_bar->current_id());
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void PlaylistContainer::SetTabBarVisible(const bool visible) {
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
if (tab_bar_visible_ == visible) return;
|
|
|
|
tab_bar_visible_ = visible;
|
|
|
|
|
|
|
|
tab_bar_animation_->setDirection(visible ? QTimeLine::Forward : QTimeLine::Backward);
|
|
|
|
tab_bar_animation_->start();
|
2020-11-20 21:47:01 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void PlaylistContainer::SetTabBarHeight(const int height) {
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->tab_bar->setMaximumHeight(height);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::MaybeUpdateFilter() {
|
|
|
|
|
2022-10-12 00:11:12 +02:00
|
|
|
if (!ui_->toolbar->isVisible()) return;
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// delaying the filter update on small playlists is undesirable and an empty filter applies very quickly, too
|
2022-08-09 17:23:46 +02:00
|
|
|
if (manager_->current()->rowCount() < kFilterDelayPlaylistSizeThreshold || ui_->search_field->text().isEmpty()) {
|
2018-02-27 18:06:05 +01:00
|
|
|
UpdateFilter();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
filter_timer_->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::UpdateFilter() {
|
|
|
|
|
2022-10-12 00:11:12 +02:00
|
|
|
if (!ui_->toolbar->isVisible()) return;
|
|
|
|
|
2022-08-09 17:23:46 +02:00
|
|
|
manager_->current()->filter()->SetFilterText(ui_->search_field->text());
|
2018-02-27 18:06:05 +01:00
|
|
|
ui_->playlist->JumpToCurrentlyPlayingTrack();
|
|
|
|
|
|
|
|
UpdateNoMatchesLabel();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::UpdateNoMatchesLabel() {
|
|
|
|
|
|
|
|
Playlist *playlist = manager_->current();
|
|
|
|
const bool has_rows = playlist->rowCount() != 0;
|
2022-08-09 17:23:46 +02:00
|
|
|
const bool has_results = playlist->filter()->rowCount() != 0;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QString text;
|
|
|
|
if (has_rows && !has_results) {
|
2019-12-29 23:46:35 +01:00
|
|
|
text = tr("No matches found. Clear the search box to show the whole playlist again.");
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!text.isEmpty()) {
|
|
|
|
no_matches_label_->setText(text);
|
|
|
|
RepositionNoMatchesLabel(true);
|
|
|
|
no_matches_label_->show();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
no_matches_label_->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::resizeEvent(QResizeEvent *e) {
|
|
|
|
QWidget::resizeEvent(e);
|
|
|
|
RepositionNoMatchesLabel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::FocusOnFilter(QKeyEvent *event) {
|
|
|
|
|
2021-11-07 19:24:01 +01:00
|
|
|
if (ui_->toolbar->isVisible()) {
|
2022-08-09 17:23:46 +02:00
|
|
|
ui_->search_field->setFocus();
|
2021-11-07 19:24:01 +01:00
|
|
|
switch (event->key()) {
|
|
|
|
case Qt::Key_Backspace:
|
|
|
|
break;
|
|
|
|
case Qt::Key_Escape:
|
2022-08-09 17:23:46 +02:00
|
|
|
ui_->search_field->clear();
|
2021-11-07 19:24:01 +01:00
|
|
|
break;
|
|
|
|
default:
|
2022-08-09 17:23:46 +02:00
|
|
|
ui_->search_field->setText(ui_->search_field->text() + event->text());
|
2021-11-07 19:24:01 +01:00
|
|
|
break;
|
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
2021-09-27 21:42:30 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
void PlaylistContainer::RepositionNoMatchesLabel(const bool force) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!force && !no_matches_label_->isVisible()) return;
|
|
|
|
|
|
|
|
const int kBorder = 10;
|
|
|
|
|
|
|
|
QPoint pos = ui_->playlist->viewport()->mapTo(ui_->playlist, QPoint(kBorder, kBorder));
|
|
|
|
QSize size = ui_->playlist->viewport()->size();
|
|
|
|
size.setWidth(size.width() - kBorder * 2);
|
|
|
|
size.setHeight(size.height() - kBorder * 2);
|
|
|
|
|
|
|
|
no_matches_label_->move(pos);
|
|
|
|
no_matches_label_->resize(size);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlaylistContainer::SelectionChanged() {
|
|
|
|
manager_->SelectionChanged(view()->selectionModel()->selection());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PlaylistContainer::eventFilter(QObject *objectWatched, QEvent *event) {
|
|
|
|
|
2022-08-09 17:23:46 +02:00
|
|
|
if (objectWatched == ui_->search_field) {
|
2018-02-27 18:06:05 +01:00
|
|
|
if (event->type() == QEvent::KeyPress) {
|
|
|
|
QKeyEvent *e = static_cast<QKeyEvent*>(event);
|
|
|
|
switch (e->key()) {
|
|
|
|
//case Qt::Key_Up:
|
|
|
|
case Qt::Key_Down:
|
|
|
|
case Qt::Key_PageUp:
|
|
|
|
case Qt::Key_PageDown:
|
|
|
|
case Qt::Key_Return:
|
|
|
|
case Qt::Key_Enter:
|
|
|
|
view()->setFocus(Qt::OtherFocusReason);
|
|
|
|
QApplication::sendEvent(ui_->playlist, event);
|
|
|
|
return true;
|
|
|
|
case Qt::Key_Escape:
|
2022-08-09 17:23:46 +02:00
|
|
|
ui_->search_field->clear();
|
2018-02-27 18:06:05 +01:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QWidget::eventFilter(objectWatched, event);
|
|
|
|
|
|
|
|
}
|