Move page specific code out of MainWindow class and it *Page class.
This commit is contained in:
@@ -29,6 +29,9 @@ SET( CANTATA_SRCS
|
||||
gui/serversettings.cpp
|
||||
gui/outputsettings.cpp
|
||||
gui/streamspage.cpp
|
||||
gui/librarypage.cpp
|
||||
gui/folderpage.cpp
|
||||
gui/playlistspage.cpp
|
||||
models/musiclibraryitem.cpp
|
||||
models/musiclibraryitemroot.cpp
|
||||
models/musiclibraryitemartist.cpp
|
||||
|
||||
175
gui/folderpage.cpp
Normal file
175
gui/folderpage.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Cantata
|
||||
*
|
||||
* Copyright (c) 2011 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
*
|
||||
* ----
|
||||
*
|
||||
* This program 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "folderpage.h"
|
||||
#include "mpdconnection.h"
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtGui/QToolButton>
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
#include <KAction>
|
||||
#include <KLocale>
|
||||
#include <KActionCollection>
|
||||
#else
|
||||
#include <QAction>
|
||||
#endif
|
||||
|
||||
FolderPage::FolderPage(MainWindow *p)
|
||||
: QWidget(p)
|
||||
{
|
||||
setupUi(this);
|
||||
addToPlaylist->setDefaultAction(p->addToPlaylistAction);
|
||||
replacePlaylist->setDefaultAction(p->replacePlaylistAction);
|
||||
libraryUpdate->setDefaultAction(p->updateDbAction);
|
||||
connect(view, SIGNAL(itemsSelected(bool)), addToPlaylist, SLOT(setEnabled(bool)));
|
||||
connect(view, SIGNAL(itemsSelected(bool)), replacePlaylist, SLOT(setEnabled(bool)));
|
||||
|
||||
addToPlaylist->setAutoRaise(true);
|
||||
replacePlaylist->setAutoRaise(true);
|
||||
libraryUpdate->setAutoRaise(true);
|
||||
addToPlaylist->setEnabled(false);
|
||||
replacePlaylist->setEnabled(false);
|
||||
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
search->setPlaceholderText(i18n("Search files..."));
|
||||
#else
|
||||
search->setPlaceholderText(tr("Search files..."));
|
||||
#endif
|
||||
view->setHeaderHidden(true);
|
||||
view->addAction(p->addToPlaylistAction);
|
||||
view->addAction(p->replacePlaylistAction);
|
||||
|
||||
proxy.setSourceModel(&model);
|
||||
view->setModel(&proxy);
|
||||
connect(MPDConnection::self(), SIGNAL(dirViewUpdated(DirViewItemRoot *)), &model, SLOT(updateDirView(DirViewItemRoot *)));
|
||||
connect(search, SIGNAL(returnPressed()), this, SLOT(searchItems()));
|
||||
connect(search, SIGNAL(textChanged(const QString)), this, SLOT(searchItems()));
|
||||
connect(this, SIGNAL(listAll()), MPDConnection::self(), SLOT(listAll()));
|
||||
connect(this, SIGNAL(add(const QStringList &)), MPDConnection::self(), SLOT(add(const QStringList &)));
|
||||
connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
|
||||
}
|
||||
|
||||
FolderPage::~FolderPage()
|
||||
{
|
||||
}
|
||||
|
||||
void FolderPage::refresh()
|
||||
{
|
||||
emit listAll();
|
||||
}
|
||||
|
||||
void FolderPage::clear()
|
||||
{
|
||||
model.clear();
|
||||
}
|
||||
|
||||
void FolderPage::searchItems()
|
||||
{
|
||||
if (search->text().isEmpty()) {
|
||||
proxy.setFilterRegExp("");
|
||||
return;
|
||||
}
|
||||
|
||||
proxy.setFilterRegExp(search->text());
|
||||
}
|
||||
|
||||
void FolderPage::itemActivated(const QModelIndex &)
|
||||
{
|
||||
DirViewItem *item;
|
||||
const QModelIndexList selected = view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
if (selectionSize != 1) {
|
||||
return; //doubleclick should only have one selected item
|
||||
}
|
||||
|
||||
const QModelIndex current = selected.at(0);
|
||||
item = static_cast<DirViewItem *>(proxy.mapToSource(current).internalPointer());
|
||||
if (item->type() == DirViewItem::Type_File) {
|
||||
addSelectionToPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderPage::addSelectionToPlaylist()
|
||||
{
|
||||
QModelIndex current;
|
||||
QStringList files;
|
||||
DirViewItem *item;
|
||||
|
||||
// Get selected view indexes
|
||||
const QModelIndexList selected = view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
|
||||
if (selectionSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int selectionPos = 0; selectionPos < selectionSize; selectionPos++) {
|
||||
current = selected.at(selectionPos);
|
||||
item = static_cast<DirViewItem *>(proxy.mapToSource(current).internalPointer());
|
||||
QStringList tmp;
|
||||
|
||||
switch (item->type()) {
|
||||
case DirViewItem::Type_Dir:
|
||||
tmp = walk(current);
|
||||
for (int i = 0; i < tmp.size(); i++) {
|
||||
if (!files.contains(tmp.at(i)))
|
||||
files << tmp.at(i);
|
||||
}
|
||||
break;
|
||||
case DirViewItem::Type_File:
|
||||
if (!files.contains(item->fileName()))
|
||||
files << item->fileName();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!files.isEmpty()) {
|
||||
emit add(files);
|
||||
view->selectionModel()->clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList FolderPage::walk(QModelIndex rootItem)
|
||||
{
|
||||
QStringList files;
|
||||
|
||||
DirViewItem *item = static_cast<DirViewItem *>(proxy.mapToSource(rootItem).internalPointer());
|
||||
|
||||
if (item->type() == DirViewItem::Type_File) {
|
||||
return QStringList(item->fileName());
|
||||
}
|
||||
|
||||
for (int i = 0; ; i++) {
|
||||
QModelIndex current = rootItem.child(i, 0);
|
||||
if (!current.isValid())
|
||||
return files;
|
||||
|
||||
QStringList tmp = walk(current);
|
||||
for (int j = 0; j < tmp.size(); j++) {
|
||||
if (!files.contains(tmp.at(j)))
|
||||
files << tmp.at(j);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
@@ -25,11 +25,36 @@
|
||||
#define FOLDERPAGE_H
|
||||
|
||||
#include "ui_folderpage.h"
|
||||
#include "mainwindow.h"
|
||||
#include "dirviewmodel.h"
|
||||
#include "dirviewproxymodel.h"
|
||||
|
||||
class FolderPage : public QWidget, public Ui::FolderPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FolderPage(QWidget *p) : QWidget(p) { setupUi(this); }
|
||||
FolderPage(MainWindow *p);
|
||||
virtual ~FolderPage();
|
||||
|
||||
void refresh();
|
||||
void clear();
|
||||
void addSelectionToPlaylist();
|
||||
|
||||
Q_SIGNALS:
|
||||
// These are for communicating with MPD object (which is in its own thread, so need to talk via singal/slots)
|
||||
void add(const QStringList &files);
|
||||
void listAll();
|
||||
|
||||
private Q_SLOTS:
|
||||
void searchItems();
|
||||
void itemActivated(const QModelIndex &);
|
||||
|
||||
private:
|
||||
QStringList walk(QModelIndex rootItem);
|
||||
|
||||
private:
|
||||
dirViewModel model;
|
||||
DirViewProxyModel proxy;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
235
gui/librarypage.cpp
Normal file
235
gui/librarypage.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Cantata
|
||||
*
|
||||
* Copyright (c) 2011 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
*
|
||||
* ----
|
||||
*
|
||||
* This program 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "librarypage.h"
|
||||
#include "mpdconnection.h"
|
||||
#include "covers.h"
|
||||
#include "musiclibraryitemsong.h"
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtGui/QToolButton>
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
#include <KAction>
|
||||
#include <KLocale>
|
||||
#include <KActionCollection>
|
||||
#else
|
||||
#include <QAction>
|
||||
#endif
|
||||
|
||||
LibraryPage::LibraryPage(MainWindow *p)
|
||||
: QWidget(p)
|
||||
{
|
||||
setupUi(this);
|
||||
addToPlaylist->setDefaultAction(p->addToPlaylistAction);
|
||||
replacePlaylist->setDefaultAction(p->replacePlaylistAction);
|
||||
libraryUpdate->setDefaultAction(p->updateDbAction);
|
||||
connect(view, SIGNAL(itemsSelected(bool)), addToPlaylist, SLOT(setEnabled(bool)));
|
||||
connect(view, SIGNAL(itemsSelected(bool)), replacePlaylist, SLOT(setEnabled(bool)));
|
||||
|
||||
addToPlaylist->setAutoRaise(true);
|
||||
replacePlaylist->setAutoRaise(true);
|
||||
libraryUpdate->setAutoRaise(true);
|
||||
addToPlaylist->setEnabled(false);
|
||||
replacePlaylist->setEnabled(false);
|
||||
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
search->setPlaceholderText(i18n("Search library..."));
|
||||
#else
|
||||
search->setPlaceholderText(tr("Search library..."));
|
||||
#endif
|
||||
view->setHeaderHidden(true);
|
||||
view->addAction(p->addToPlaylistAction);
|
||||
view->addAction(p->replacePlaylistAction);
|
||||
proxy.setSourceModel(&model);
|
||||
view->setModel(&proxy);
|
||||
connect(this, SIGNAL(add(const QStringList &)), MPDConnection::self(), SLOT(add(const QStringList &)));
|
||||
connect(search, SIGNAL(returnPressed()), this, SLOT(searchItems()));
|
||||
connect(search, SIGNAL(textChanged(const QString)), this, SLOT(searchItems()));
|
||||
connect(genreCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchItems()));
|
||||
connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
|
||||
connect(MPDConnection::self(), SIGNAL(musicLibraryUpdated(MusicLibraryItemRoot *, QDateTime)), &model, SLOT(updateMusicLibrary(MusicLibraryItemRoot *, QDateTime)));
|
||||
connect(Covers::self(), SIGNAL(cover(const QString &, const QString &, const QImage &)),
|
||||
&model, SLOT(setCover(const QString &, const QString &, const QImage &)));
|
||||
connect(&model, SIGNAL(updateGenres(const QStringList &)), this, SLOT(updateGenres(const QStringList &)));
|
||||
connect(this, SIGNAL(listAllInfo(const QDateTime &)), MPDConnection::self(), SLOT(listAllInfo(const QDateTime &)));
|
||||
}
|
||||
|
||||
LibraryPage::~LibraryPage()
|
||||
{
|
||||
}
|
||||
|
||||
void LibraryPage::refresh(Refresh type)
|
||||
{
|
||||
if (RefreshForce==type) {
|
||||
model.clearUpdateTime();
|
||||
}
|
||||
|
||||
if (RefreshFromCache!=type || !model.fromXML(MPDStats::self()->dbUpdate())) {
|
||||
emit listAllInfo(MPDStats::self()->dbUpdate());
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryPage::clear()
|
||||
{
|
||||
model.clear();
|
||||
}
|
||||
|
||||
void LibraryPage::addSelectionToPlaylist()
|
||||
{
|
||||
QStringList files;
|
||||
MusicLibraryItem *item;
|
||||
MusicLibraryItemSong *songItem;
|
||||
|
||||
// Get selected view indexes
|
||||
const QModelIndexList selected = view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
|
||||
if (selectionSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop over the selection. Only add files.
|
||||
for (int selectionPos = 0; selectionPos < selectionSize; selectionPos++) {
|
||||
const QModelIndex current = selected.at(selectionPos);
|
||||
item = static_cast<MusicLibraryItem *>(proxy.mapToSource(current).internalPointer());
|
||||
|
||||
switch (item->type()) {
|
||||
case MusicLibraryItem::Type_Artist: {
|
||||
for (quint32 i = 0; ; i++) {
|
||||
const QModelIndex album = current.child(i , 0);
|
||||
if (!album.isValid())
|
||||
break;
|
||||
|
||||
for (quint32 j = 0; ; j++) {
|
||||
const QModelIndex track = album.child(j, 0);
|
||||
if (!track.isValid())
|
||||
break;
|
||||
const QModelIndex mappedSongIndex = proxy.mapToSource(track);
|
||||
songItem = static_cast<MusicLibraryItemSong *>(mappedSongIndex.internalPointer());
|
||||
const QString fileName = songItem->file();
|
||||
if (!fileName.isEmpty() && !files.contains(fileName))
|
||||
files.append(fileName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MusicLibraryItem::Type_Album: {
|
||||
for (quint32 i = 0; ; i++) {
|
||||
QModelIndex track = current.child(i, 0);
|
||||
if (!track.isValid())
|
||||
break;
|
||||
const QModelIndex mappedSongIndex = proxy.mapToSource(track);
|
||||
songItem = static_cast<MusicLibraryItemSong *>(mappedSongIndex.internalPointer());
|
||||
const QString fileName = songItem->file();
|
||||
if (!fileName.isEmpty() && !files.contains(fileName))
|
||||
files.append(fileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MusicLibraryItem::Type_Song: {
|
||||
const QString fileName = static_cast<MusicLibraryItemSong *>(item)->file();
|
||||
if (!fileName.isEmpty() && !files.contains(fileName))
|
||||
files.append(fileName);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!files.isEmpty()) {
|
||||
emit add(files);
|
||||
view->selectionModel()->clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryPage::itemActivated(const QModelIndex &)
|
||||
{
|
||||
MusicLibraryItem *item;
|
||||
const QModelIndexList selected = view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
if (selectionSize != 1) return; //doubleclick should only have one selected item
|
||||
|
||||
const QModelIndex current = selected.at(0);
|
||||
item = static_cast<MusicLibraryItem *>(proxy.mapToSource(current).internalPointer());
|
||||
if (item->type() == MusicLibraryItem::Type_Song) {
|
||||
addSelectionToPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryPage::searchItems()
|
||||
{
|
||||
proxy.setFilterGenre(0==genreCombo->currentIndex() ? QString() : genreCombo->currentText());
|
||||
|
||||
if (search->text().isEmpty()) {
|
||||
proxy.setFilterRegExp(QString());
|
||||
return;
|
||||
}
|
||||
|
||||
proxy.setFilterRegExp(search->text());
|
||||
}
|
||||
|
||||
void LibraryPage::updateGenres(const QStringList &genres)
|
||||
{
|
||||
QStringList entries;
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
entries << i18n("All Genres");
|
||||
#else
|
||||
entries << tr("All Genres");
|
||||
#endif
|
||||
entries+=genres;
|
||||
|
||||
bool diff=genreCombo->count() != entries.count();
|
||||
if (!diff) {
|
||||
// Check items...
|
||||
for (int i=1; i<genreCombo->count() && !diff; ++i) {
|
||||
if (genreCombo->itemText(i) != entries.at(i)) {
|
||||
diff=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString currentFilter = genreCombo->currentIndex() ? genreCombo->currentText() : QString();
|
||||
|
||||
genreCombo->clear();
|
||||
if (genres.count()<2) {
|
||||
genreCombo->setCurrentIndex(0);
|
||||
} else {
|
||||
genreCombo->addItems(entries);
|
||||
if (!currentFilter.isEmpty()) {
|
||||
bool found=false;
|
||||
for (int i=1; i<genreCombo->count() && !found; ++i) {
|
||||
if (genreCombo->itemText(i) == currentFilter) {
|
||||
genreCombo->setCurrentIndex(i);
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
genreCombo->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,42 @@
|
||||
#define LIBRARYPAGE_H
|
||||
|
||||
#include "ui_librarypage.h"
|
||||
#include "mainwindow.h"
|
||||
#include "musiclibrarymodel.h"
|
||||
#include "musiclibraryproxymodel.h"
|
||||
|
||||
class LibraryPage : public QWidget, public Ui::LibraryPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LibraryPage(QWidget *p) : QWidget(p) { setupUi(this); }
|
||||
|
||||
enum Refresh
|
||||
{
|
||||
RefreshFromCache,
|
||||
RefreshForce,
|
||||
RefreshStandard
|
||||
};
|
||||
|
||||
LibraryPage(MainWindow *p);
|
||||
virtual ~LibraryPage();
|
||||
|
||||
void refresh(Refresh type);
|
||||
void clear();
|
||||
void addSelectionToPlaylist();
|
||||
|
||||
Q_SIGNALS:
|
||||
// These are for communicating with MPD object (which is in its own thread, so need to talk via singal/slots)
|
||||
void add(const QStringList &files);
|
||||
void listAllInfo(const QDateTime &);
|
||||
|
||||
public Q_SLOTS:
|
||||
void updateGenres(const QStringList &genres);
|
||||
void itemActivated(const QModelIndex &);
|
||||
void searchItems();
|
||||
|
||||
private:
|
||||
MusicLibraryModel model;
|
||||
MusicLibraryProxyModel proxy;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
|
||||
#include "covers.h"
|
||||
#include "mainwindow.h"
|
||||
#include "musiclibraryitemsong.h"
|
||||
#include "preferencesdialog.h"
|
||||
#include "mpdconnection.h"
|
||||
#include "mpdstats.h"
|
||||
@@ -396,8 +395,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
volumeButton->setIcon(QIcon::fromTheme("player-volume"));
|
||||
connect(Covers::self(), SIGNAL(cover(const QString &, const QString &, const QImage &)),
|
||||
SLOT(cover(const QString &, const QString &, const QImage &)));
|
||||
connect(Covers::self(), SIGNAL(cover(const QString &, const QString &, const QImage &)),
|
||||
&musicLibraryModel, SLOT(setCover(const QString &, const QString &, const QImage &)));
|
||||
|
||||
menuButton->setMenu(mainMenu);
|
||||
menuButton->setPopupMode(QToolButton::InstantPopup);
|
||||
@@ -415,22 +412,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
|
||||
setVisible(true);
|
||||
|
||||
libraryPage->addToPlaylist->setDefaultAction(addToPlaylistAction);
|
||||
libraryPage->replacePlaylist->setDefaultAction(replacePlaylistAction);
|
||||
folderPage->addToPlaylist->setDefaultAction(addToPlaylistAction);
|
||||
folderPage->replacePlaylist->setDefaultAction(replacePlaylistAction);
|
||||
savePlaylistPushButton->setDefaultAction(savePlaylistAction);
|
||||
removeAllFromPlaylistPushButton->setDefaultAction(clearPlaylistAction);
|
||||
removeFromPlaylistPushButton->setDefaultAction(removeFromPlaylistAction);
|
||||
playlistsPage->addToPlaylist->setDefaultAction(addToPlaylistAction);
|
||||
playlistsPage->replacePlaylist->setDefaultAction(replacePlaylistAction);
|
||||
playlistsPage->removePlaylist->setDefaultAction(removePlaylistAction);
|
||||
randomPushButton->setDefaultAction(randomPlaylistAction);
|
||||
repeatPushButton->setDefaultAction(repeatPlaylistAction);
|
||||
consumePushButton->setDefaultAction(consumePlaylistAction);
|
||||
libraryPage->libraryUpdate->setDefaultAction(updateDbAction);
|
||||
folderPage->libraryUpdate->setDefaultAction(updateDbAction);
|
||||
playlistsPage->libraryUpdate->setDefaultAction(updateDbAction);
|
||||
|
||||
tabWidget->AddTab(libraryPage, libraryTabAction->icon(), libraryTabAction->text());
|
||||
tabWidget->AddTab(folderPage, foldersTabAction->icon(), foldersTabAction->text());
|
||||
@@ -446,31 +433,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
repeatPlaylistAction->setCheckable(true);
|
||||
consumePlaylistAction->setCheckable(true);
|
||||
|
||||
libraryPage->addToPlaylist->setEnabled(false);
|
||||
libraryPage->replacePlaylist->setEnabled(false);
|
||||
folderPage->addToPlaylist->setEnabled(false);
|
||||
folderPage->replacePlaylist->setEnabled(false);
|
||||
playlistsPage->addToPlaylist->setEnabled(false);
|
||||
playlistsPage->replacePlaylist->setEnabled(false);
|
||||
playlistsPage->removePlaylist->setEnabled(false);
|
||||
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
libraryPage->search->setPlaceholderText(i18n("Search library..."));
|
||||
folderPage->search->setPlaceholderText(i18n("Search files..."));
|
||||
searchPlaylistLineEdit->setPlaceholderText(i18n("Search playlist..."));
|
||||
#else
|
||||
libraryPage->search->setPlaceholderText(tr("Search library..."));
|
||||
folderPage->search->setPlaceholderText(tr("Search files..."));
|
||||
searchPlaylistLineEdit->setPlaceholderText(tr("Search playlist..."));
|
||||
#endif
|
||||
QList<QToolButton *> btns;
|
||||
btns << prevTrackButton << stopTrackButton << playPauseTrackButton << nextTrackButton
|
||||
<< libraryPage->addToPlaylist << libraryPage->replacePlaylist
|
||||
<< folderPage->addToPlaylist << folderPage->replacePlaylist << playlistsPage->addToPlaylist
|
||||
<< playlistsPage->replacePlaylist << playlistsPage->removePlaylist << repeatPushButton << randomPushButton
|
||||
<< savePlaylistPushButton << removeAllFromPlaylistPushButton << removeFromPlaylistPushButton
|
||||
<< consumePushButton << libraryPage->libraryUpdate << folderPage->libraryUpdate << playlistsPage->libraryUpdate
|
||||
<< volumeButton << menuButton;
|
||||
<< repeatPushButton << randomPushButton << savePlaylistPushButton << removeAllFromPlaylistPushButton
|
||||
<< removeFromPlaylistPushButton << consumePushButton << volumeButton << menuButton;
|
||||
|
||||
foreach(QToolButton * b, btns) {
|
||||
b->setAutoRaise(true);
|
||||
@@ -483,18 +454,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
#endif
|
||||
artistLabel->setText("...");
|
||||
|
||||
playlistsProxyModel.setSourceModel(&playlistsModel);
|
||||
playlistsPage->view->setModel(&playlistsProxyModel);
|
||||
playlistsPage->view->sortByColumn(0, Qt::AscendingOrder);
|
||||
playlistsPage->view->addAction(addToPlaylistAction);
|
||||
playlistsPage->view->addAction(replacePlaylistAction);
|
||||
playlistsPage->view->addAction(removePlaylistAction);
|
||||
playlistsPage->view->addAction(renamePlaylistAction);
|
||||
|
||||
libraryPage->view->setHeaderHidden(true);
|
||||
folderPage->view->setHeaderHidden(true);
|
||||
playlistsPage->view->setHeaderHidden(true);
|
||||
|
||||
showPlaylistAction->setChecked(Settings::self()->showPlaylist());
|
||||
randomPlaylistAction->setChecked(false);
|
||||
repeatPlaylistAction->setChecked(false);
|
||||
@@ -541,22 +500,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
mainMenu->addAction(quitAction);
|
||||
|
||||
coverWidget->installEventFilter(coverEventHandler);
|
||||
libraryProxyModel.setSourceModel(&musicLibraryModel);
|
||||
libraryPage->view->setModel(&libraryProxyModel);
|
||||
libraryPage->view->addAction(addToPlaylistAction);
|
||||
libraryPage->view->addAction(replacePlaylistAction);
|
||||
|
||||
dirProxyModel.setSourceModel(&dirviewModel);
|
||||
folderPage->view->setModel(&dirProxyModel);
|
||||
folderPage->view->addAction(addToPlaylistAction);
|
||||
folderPage->view->addAction(replacePlaylistAction);
|
||||
|
||||
connect(MPDConnection::self(), SIGNAL(loaded(const QString &)), MPDConnection::self(), SLOT(startPlayingSong()));
|
||||
connect(MPDConnection::self(), SIGNAL(added(const QStringList &)), MPDConnection::self(), SLOT(startPlayingSong()));
|
||||
connect(this, SIGNAL(loadPlaylist(const QString &)), MPDConnection::self(), SLOT(load(const QString &)));
|
||||
connect(this, SIGNAL(removePlaylist(const QString &)), MPDConnection::self(), SLOT(rm(const QString &)));
|
||||
connect(this, SIGNAL(savePlaylist(const QString &)), MPDConnection::self(), SLOT(save(const QString &)));
|
||||
connect(this, SIGNAL(renamePlaylist(const QString &, const QString &)), MPDConnection::self(), SLOT(rename(const QString &, const QString &)));
|
||||
connect(this, SIGNAL(removeSongs(const QList<qint32> &)), MPDConnection::self(), SLOT(removeSongs(const QList<qint32> &)));
|
||||
connect(this, SIGNAL(pause(bool)), MPDConnection::self(), SLOT(setPause(bool)));
|
||||
connect(this, SIGNAL(play()), MPDConnection::self(), SLOT(startPlayingSong()));
|
||||
@@ -565,12 +511,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
connect(this, SIGNAL(getStats()), MPDConnection::self(), SLOT(getStats()));
|
||||
connect(this, SIGNAL(clear()), MPDConnection::self(), SLOT(clear()));
|
||||
connect(this, SIGNAL(playListInfo()), MPDConnection::self(), SLOT(playListInfo()));
|
||||
connect(this, SIGNAL(listAllInfo(const QDateTime &)), MPDConnection::self(), SLOT(listAllInfo(const QDateTime &)));
|
||||
connect(this, SIGNAL(listAll()), MPDConnection::self(), SLOT(listAll()));
|
||||
connect(this, SIGNAL(currentSong()), MPDConnection::self(), SLOT(currentSong()));
|
||||
connect(this, SIGNAL(setSeekId(quint32, quint32)), MPDConnection::self(), SLOT(setSeekId(quint32, quint32)));
|
||||
connect(this, SIGNAL(startPlayingSongId(quint32)), MPDConnection::self(), SLOT(startPlayingSongId(quint32)));
|
||||
connect(this, SIGNAL(add(const QStringList &)), MPDConnection::self(), SLOT(add(const QStringList &)));
|
||||
connect(this, SIGNAL(setDetails(const QString &, quint16, const QString &)), MPDConnection::self(), SLOT(setDetails(const QString &, quint16, const QString &)));
|
||||
|
||||
connect(&playlistModel, SIGNAL(filesAddedInPlaylist(const QStringList, quint32, quint32)),
|
||||
@@ -597,8 +540,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
connect(MPDConnection::self(), SIGNAL(playlistUpdated(const QList<Song> &)), this, SLOT(updatePlaylist(const QList<Song> &)));
|
||||
connect(MPDConnection::self(), SIGNAL(currentSongUpdated(const Song &)), this, SLOT(updateCurrentSong(const Song &)));
|
||||
connect(MPDConnection::self(), SIGNAL(mpdConnectionDied()), this, SLOT(mpdConnectionDied()));
|
||||
connect(MPDConnection::self(), SIGNAL(musicLibraryUpdated(MusicLibraryItemRoot *, QDateTime)), &musicLibraryModel, SLOT(updateMusicLibrary(MusicLibraryItemRoot *, QDateTime)));
|
||||
connect(MPDConnection::self(), SIGNAL(dirViewUpdated(DirViewItemRoot *)), &dirviewModel, SLOT(updateDirView(DirViewItemRoot *)));
|
||||
connect(MPDConnection::self(), SIGNAL(storedPlayListUpdated()), MPDConnection::self(), SLOT(listPlaylists()));
|
||||
connect(MPDConnection::self(), SIGNAL(stateChanged(bool)), SLOT(mpdConnectionStateChanged(bool)));
|
||||
connect(updateDbAction, SIGNAL(triggered(bool)), this, SLOT(updateDb()));
|
||||
connect(updateDbAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(update()));
|
||||
connect(prevTrackAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(goToPrevious()));
|
||||
@@ -616,22 +559,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
connect(randomPlaylistAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setRandom(bool)));
|
||||
connect(repeatPlaylistAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setRepeat(bool)));
|
||||
connect(consumePlaylistAction, SIGNAL(triggered(bool)), MPDConnection::self(), SLOT(setConsume(bool)));
|
||||
connect(libraryPage->search, SIGNAL(returnPressed()), this, SLOT(searchMusicLibrary()));
|
||||
connect(libraryPage->search, SIGNAL(textChanged(const QString)), this, SLOT(searchMusicLibrary()));
|
||||
connect(libraryPage->genreCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(searchMusicLibrary()));
|
||||
connect(folderPage->search, SIGNAL(returnPressed()), this, SLOT(searchDirView()));
|
||||
connect(folderPage->search, SIGNAL(textChanged(const QString)), this, SLOT(searchDirView()));
|
||||
connect(searchPlaylistLineEdit, SIGNAL(returnPressed()), this, SLOT(searchPlaylist()));
|
||||
connect(searchPlaylistLineEdit, SIGNAL(textChanged(const QString)), this, SLOT(searchPlaylist()));
|
||||
connect(playlistTableView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(playlistItemActivated(const QModelIndex &)));
|
||||
connect(libraryPage->view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(libraryItemActivated(const QModelIndex &)));
|
||||
connect(folderPage->view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(dirViewItemActivated(const QModelIndex &)));
|
||||
connect(addToPlaylistAction, SIGNAL(activated()), this, SLOT(addToPlaylist()));
|
||||
connect(replacePlaylistAction, SIGNAL(activated()), this, SLOT(replacePlaylist()));
|
||||
connect(removePlaylistAction, SIGNAL(activated()), this, SLOT(removePlaylist()));
|
||||
connect(savePlaylistAction, SIGNAL(activated()), this, SLOT(savePlaylist()));
|
||||
connect(removeFromPlaylistAction, SIGNAL(activated()), this, SLOT(removeFromPlaylist()));
|
||||
connect(renamePlaylistAction, SIGNAL(triggered()), this, SLOT(renamePlaylist()));
|
||||
connect(clearPlaylistAction, SIGNAL(activated()), searchPlaylistLineEdit, SLOT(clear()));
|
||||
connect(clearPlaylistAction, SIGNAL(activated()), MPDConnection::self(), SLOT(clear()));
|
||||
connect(copySongInfoAction, SIGNAL(activated()), this, SLOT(copySongInfo()));
|
||||
@@ -639,14 +572,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
connect(shufflePlaylistAction, SIGNAL(activated()), MPDConnection::self(), SLOT(shuffle()));
|
||||
connect(showPlaylistAction, SIGNAL(activated()), this, SLOT(togglePlaylist()));
|
||||
connect(&elapsedTimer, SIGNAL(timeout()), this, SLOT(updatePositionSilder()));
|
||||
connect(&musicLibraryModel, SIGNAL(updateGenres(const QStringList &)), this, SLOT(updateGenres(const QStringList &)));
|
||||
connect(libraryPage->view, SIGNAL(itemsSelected(bool)), libraryPage->addToPlaylist, SLOT(setEnabled(bool)));
|
||||
connect(libraryPage->view, SIGNAL(itemsSelected(bool)), libraryPage->replacePlaylist, SLOT(setEnabled(bool)));
|
||||
connect(folderPage->view, SIGNAL(itemsSelected(bool)), folderPage->addToPlaylist, SLOT(setEnabled(bool)));
|
||||
connect(folderPage->view, SIGNAL(itemsSelected(bool)), folderPage->replacePlaylist, SLOT(setEnabled(bool)));
|
||||
connect(playlistsPage->view, SIGNAL(itemsSelected(bool)), playlistsPage->addToPlaylist, SLOT(setEnabled(bool)));
|
||||
connect(playlistsPage->view, SIGNAL(itemsSelected(bool)), playlistsPage->replacePlaylist, SLOT(setEnabled(bool)));
|
||||
connect(playlistsPage->view, SIGNAL(itemsSelected(bool)), playlistsPage->removePlaylist, SLOT(setEnabled(bool)));
|
||||
connect(volumeButton, SIGNAL(clicked()), SLOT(showVolumeControl()));
|
||||
connect(libraryTabAction, SIGNAL(activated()), this, SLOT(showLibraryTab()));
|
||||
connect(foldersTabAction, SIGNAL(activated()), this, SLOT(showFoldersTab()));
|
||||
@@ -654,10 +579,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
|
||||
connect(lyricsTabAction, SIGNAL(activated()), this, SLOT(showLyricsTab()));
|
||||
connect(streamsTabAction, SIGNAL(activated()), this, SLOT(showStreamsTab()));
|
||||
|
||||
connect(playlistsPage->view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(playlistsViewItemDoubleClicked(const QModelIndex &)));
|
||||
connect(MPDConnection::self(), SIGNAL(storedPlayListUpdated()), MPDConnection::self(), SLOT(listPlaylists()));
|
||||
connect(MPDConnection::self(), SIGNAL(stateChanged(bool)), SLOT(mpdConnectionStateChanged(bool)));
|
||||
|
||||
elapsedTimer.setInterval(1000);
|
||||
|
||||
QByteArray state=Settings::self()->splitterState();
|
||||
@@ -715,9 +636,9 @@ void MainWindow::mpdConnectionStateChanged(bool connected)
|
||||
loaded=0;
|
||||
currentTabChanged(tabWidget->current_index());
|
||||
} else {
|
||||
musicLibraryModel.clear();
|
||||
dirviewModel.clear();
|
||||
playlistsModel.clear();
|
||||
libraryPage->clear();
|
||||
folderPage->clear();
|
||||
playlistsPage->clear();
|
||||
playlistModel.clear();
|
||||
lyricsPage->text->clear();
|
||||
showPreferencesDialog();
|
||||
@@ -813,8 +734,7 @@ void MainWindow::updateSettings()
|
||||
|
||||
if (((int)MusicLibraryItemAlbum::currentCoverSize())!=Settings::self()->coverSize()) {
|
||||
MusicLibraryItemAlbum::setCoverSize((MusicLibraryItemAlbum::CoverSize)Settings::self()->coverSize());
|
||||
musicLibraryModel.clearUpdateTime();
|
||||
emit listAllInfo(MPDStats::self()->dbUpdate());
|
||||
libraryPage->refresh(LibraryPage::RefreshForce);
|
||||
}
|
||||
|
||||
if (Settings::self()->useSystemTray()) {
|
||||
@@ -885,28 +805,6 @@ void MainWindow::decreaseVolume()
|
||||
volumeControl->sliderWidget()->triggerAction(QAbstractSlider::SliderPageStepSub);
|
||||
}
|
||||
|
||||
void MainWindow::searchMusicLibrary()
|
||||
{
|
||||
libraryProxyModel.setFilterGenre(0==libraryPage->genreCombo->currentIndex() ? QString() : libraryPage->genreCombo->currentText());
|
||||
|
||||
if (libraryPage->search->text().isEmpty()) {
|
||||
libraryProxyModel.setFilterRegExp(QString());
|
||||
return;
|
||||
}
|
||||
|
||||
libraryProxyModel.setFilterRegExp(libraryPage->search->text());
|
||||
}
|
||||
|
||||
void MainWindow::searchDirView()
|
||||
{
|
||||
if (folderPage->search->text().isEmpty()) {
|
||||
dirProxyModel.setFilterRegExp("");
|
||||
return;
|
||||
}
|
||||
|
||||
dirProxyModel.setFilterRegExp(folderPage->search->text());
|
||||
}
|
||||
|
||||
void MainWindow::searchPlaylist()
|
||||
{
|
||||
if (searchPlaylistLineEdit->text().isEmpty()) {
|
||||
@@ -1038,18 +936,16 @@ void MainWindow::updateCurrentSong(const Song &song)
|
||||
|
||||
void MainWindow::updateStats()
|
||||
{
|
||||
MPDStats * const stats = MPDStats::self();
|
||||
|
||||
/*
|
||||
* Check if remote db is more recent than local one
|
||||
* Also update the dirview
|
||||
*/
|
||||
if (lastDbUpdate.isValid() && stats->dbUpdate() > lastDbUpdate) {
|
||||
emit listAllInfo(stats->dbUpdate());
|
||||
emit listAll();
|
||||
if (lastDbUpdate.isValid() && MPDStats::self()->dbUpdate() > lastDbUpdate) {
|
||||
libraryPage->refresh(LibraryPage::RefreshStandard);
|
||||
folderPage->refresh();
|
||||
}
|
||||
|
||||
lastDbUpdate = stats->dbUpdate();
|
||||
lastDbUpdate = MPDStats::self()->dbUpdate();
|
||||
}
|
||||
|
||||
void MainWindow::updateStatus()
|
||||
@@ -1182,191 +1078,23 @@ void MainWindow::removeFromPlaylist()
|
||||
|
||||
emit removeSongs(toBeRemoved);
|
||||
}
|
||||
|
||||
void MainWindow::replacePlaylist()
|
||||
{
|
||||
emit clear();
|
||||
emit getStatus();
|
||||
searchPlaylistLineEdit->clear();
|
||||
if (libraryPage->view->isVisible()) {
|
||||
addLibrarySelectionToPlaylist();
|
||||
} else if (folderPage->view->isVisible()) {
|
||||
addDirViewSelectionToPlaylist();
|
||||
} else if (playlistsPage->view->isVisible()) {
|
||||
addPlaylistsSelectionToPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::addLibrarySelectionToPlaylist()
|
||||
{
|
||||
QStringList files;
|
||||
MusicLibraryItem *item;
|
||||
MusicLibraryItemSong *songItem;
|
||||
|
||||
// Get selected view indexes
|
||||
const QModelIndexList selected = libraryPage->view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
|
||||
if (selectionSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop over the selection. Only add files.
|
||||
for (int selectionPos = 0; selectionPos < selectionSize; selectionPos++) {
|
||||
const QModelIndex current = selected.at(selectionPos);
|
||||
item = static_cast<MusicLibraryItem *>(libraryProxyModel.mapToSource(current).internalPointer());
|
||||
|
||||
switch (item->type()) {
|
||||
case MusicLibraryItem::Type_Artist: {
|
||||
for (quint32 i = 0; ; i++) {
|
||||
const QModelIndex album = current.child(i , 0);
|
||||
if (!album.isValid())
|
||||
break;
|
||||
|
||||
for (quint32 j = 0; ; j++) {
|
||||
const QModelIndex track = album.child(j, 0);
|
||||
if (!track.isValid())
|
||||
break;
|
||||
const QModelIndex mappedSongIndex = libraryProxyModel.mapToSource(track);
|
||||
songItem = static_cast<MusicLibraryItemSong *>(mappedSongIndex.internalPointer());
|
||||
const QString fileName = songItem->file();
|
||||
if (!fileName.isEmpty() && !files.contains(fileName))
|
||||
files.append(fileName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MusicLibraryItem::Type_Album: {
|
||||
for (quint32 i = 0; ; i++) {
|
||||
QModelIndex track = current.child(i, 0);
|
||||
if (!track.isValid())
|
||||
break;
|
||||
const QModelIndex mappedSongIndex = libraryProxyModel.mapToSource(track);
|
||||
songItem = static_cast<MusicLibraryItemSong *>(mappedSongIndex.internalPointer());
|
||||
const QString fileName = songItem->file();
|
||||
if (!fileName.isEmpty() && !files.contains(fileName))
|
||||
files.append(fileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MusicLibraryItem::Type_Song: {
|
||||
const QString fileName = static_cast<MusicLibraryItemSong *>(item)->file();
|
||||
if (!fileName.isEmpty() && !files.contains(fileName))
|
||||
files.append(fileName);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!files.isEmpty()) {
|
||||
emit add(files);
|
||||
libraryPage->view->selectionModel()->clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList MainWindow::walkDirView(QModelIndex rootItem)
|
||||
{
|
||||
QStringList files;
|
||||
|
||||
DirViewItem *item = static_cast<DirViewItem *>(dirProxyModel.mapToSource(rootItem).internalPointer());
|
||||
|
||||
if (item->type() == DirViewItem::Type_File) {
|
||||
return QStringList(item->fileName());
|
||||
}
|
||||
|
||||
for (int i = 0; ; i++) {
|
||||
QModelIndex current = rootItem.child(i, 0);
|
||||
if (!current.isValid())
|
||||
return files;
|
||||
|
||||
QStringList tmp = walkDirView(current);
|
||||
for (int j = 0; j < tmp.size(); j++) {
|
||||
if (!files.contains(tmp.at(j)))
|
||||
files << tmp.at(j);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
void MainWindow::addDirViewSelectionToPlaylist()
|
||||
{
|
||||
QModelIndex current;
|
||||
QStringList files;
|
||||
DirViewItem *item;
|
||||
|
||||
// Get selected view indexes
|
||||
const QModelIndexList selected = folderPage->view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
|
||||
if (selectionSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int selectionPos = 0; selectionPos < selectionSize; selectionPos++) {
|
||||
current = selected.at(selectionPos);
|
||||
item = static_cast<DirViewItem *>(dirProxyModel.mapToSource(current).internalPointer());
|
||||
QStringList tmp;
|
||||
|
||||
switch (item->type()) {
|
||||
case DirViewItem::Type_Dir:
|
||||
tmp = walkDirView(current);
|
||||
for (int i = 0; i < tmp.size(); i++) {
|
||||
if (!files.contains(tmp.at(i)))
|
||||
files << tmp.at(i);
|
||||
}
|
||||
break;
|
||||
case DirViewItem::Type_File:
|
||||
if (!files.contains(item->fileName()))
|
||||
files << item->fileName();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!files.isEmpty()) {
|
||||
emit add(files);
|
||||
folderPage->view->selectionModel()->clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::libraryItemActivated(const QModelIndex & /*index*/)
|
||||
{
|
||||
MusicLibraryItem *item;
|
||||
const QModelIndexList selected = libraryPage->view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
if (selectionSize != 1) return; //doubleclick should only have one selected item
|
||||
|
||||
const QModelIndex current = selected.at(0);
|
||||
item = static_cast<MusicLibraryItem *>(libraryProxyModel.mapToSource(current).internalPointer());
|
||||
if (item->type() == MusicLibraryItem::Type_Song) {
|
||||
addLibrarySelectionToPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::dirViewItemActivated(const QModelIndex & /*index*/)
|
||||
{
|
||||
DirViewItem *item;
|
||||
const QModelIndexList selected = folderPage->view->selectionModel()->selectedIndexes();
|
||||
int selectionSize = selected.size();
|
||||
if (selectionSize != 1) return; //doubleclick should only have one selected item
|
||||
|
||||
const QModelIndex current = selected.at(0);
|
||||
item = static_cast<DirViewItem *>(dirProxyModel.mapToSource(current).internalPointer());
|
||||
if (item->type() == DirViewItem::Type_File) {
|
||||
addDirViewSelectionToPlaylist();
|
||||
}
|
||||
addToPlaylist();
|
||||
}
|
||||
|
||||
void MainWindow::addToPlaylist()
|
||||
{
|
||||
if (libraryPage->view->isVisible()) {
|
||||
addLibrarySelectionToPlaylist();
|
||||
libraryPage->addSelectionToPlaylist();
|
||||
} else if (folderPage->view->isVisible()) {
|
||||
addDirViewSelectionToPlaylist();
|
||||
folderPage->addSelectionToPlaylist();
|
||||
} else if (playlistTableView->isVisible()) {
|
||||
addPlaylistsSelectionToPlaylist();
|
||||
playlistsPage->addSelectionToPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1636,84 +1364,6 @@ void MainWindow::trayIconClicked(QSystemTrayIcon::ActivationReason reason)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::addPlaylistsSelectionToPlaylist()
|
||||
{
|
||||
const QModelIndexList items = playlistsPage->view->selectionModel()->selectedRows();
|
||||
|
||||
if (items.size() == 1) {
|
||||
QModelIndex sourceIndex = playlistsProxyModel.mapToSource(items.first());
|
||||
QString playlist_name = playlistsModel.data(sourceIndex, Qt::DisplayRole).toString();
|
||||
emit loadPlaylist(playlist_name);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::playlistsViewItemDoubleClicked(const QModelIndex &index)
|
||||
{
|
||||
QModelIndex sourceIndex = playlistsProxyModel.mapToSource(index);
|
||||
QString playlist_name = playlistsModel.data(sourceIndex, Qt::DisplayRole).toString();
|
||||
emit loadPlaylist(playlist_name);
|
||||
}
|
||||
|
||||
void MainWindow::removePlaylist()
|
||||
{
|
||||
const QModelIndexList items = playlistsPage->view->selectionModel()->selectedRows();
|
||||
|
||||
if (items.size() == 1) {
|
||||
QModelIndex sourceIndex = playlistsProxyModel.mapToSource(items.first());
|
||||
QString playlist_name = playlistsModel.data(sourceIndex, Qt::DisplayRole).toString();
|
||||
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
if (KMessageBox::Yes == KMessageBox::warningYesNo(this, i18n("Are you sure you want to delete playlist: %1?", playlist_name),
|
||||
i18n("Delete Playlist?"))) {
|
||||
emit removePlaylist(playlist_name);
|
||||
}
|
||||
#else
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setWindowTitle(tr("Delete Playlist?"));
|
||||
msgBox.setText(tr("Are you sure you want to delete playlist: %1?").arg(playlist_name));
|
||||
msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
|
||||
msgBox.setDefaultButton(QMessageBox::No);
|
||||
int ret = msgBox.exec();
|
||||
|
||||
if (ret == QMessageBox::Yes) {
|
||||
emit removePlaylist(playlist_name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::savePlaylist()
|
||||
{
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
QString name = QInputDialog::getText(this, i18n("Playlist Name"), i18n("Enter a name for the playlist:"));
|
||||
#else
|
||||
QString name = QInputDialog::getText(this, tr("Playlist Name"), tr("Enter a name for the playlist:"));
|
||||
#endif
|
||||
|
||||
if (!name.isEmpty())
|
||||
emit savePlaylist(name);
|
||||
}
|
||||
|
||||
void MainWindow::renamePlaylist()
|
||||
{
|
||||
const QModelIndexList items = playlistsPage->view->selectionModel()->selectedRows();
|
||||
|
||||
if (items.size() == 1) {
|
||||
QModelIndex sourceIndex = playlistsProxyModel.mapToSource(items.first());
|
||||
QString playlist_name = playlistsModel.data(sourceIndex, Qt::DisplayRole).toString();
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
QString new_name = QInputDialog::getText(this, i18n("Rename Playlist"), i18n("Enter new name for playlist: %1").arg(playlist_name));
|
||||
#else
|
||||
QString new_name = QInputDialog::getText(this, tr("Rename Playlist"), tr("Enter new name for playlist: %1").arg(playlist_name));
|
||||
#endif
|
||||
|
||||
if (!new_name.isEmpty()) {
|
||||
emit renamePlaylist(playlist_name, new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Tabs
|
||||
{
|
||||
TAB_LIBRARY = 0x01,
|
||||
@@ -1727,21 +1377,19 @@ void MainWindow::currentTabChanged(int index)
|
||||
case 0:
|
||||
if (!(loaded&TAB_LIBRARY)) {
|
||||
loaded|=TAB_LIBRARY;
|
||||
if (!musicLibraryModel.fromXML(MPDStats::self()->dbUpdate())) {
|
||||
emit listAllInfo(MPDStats::self()->dbUpdate());
|
||||
}
|
||||
libraryPage->refresh(LibraryPage::RefreshFromCache);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!(loaded&TAB_DIRS)) {
|
||||
loaded|=TAB_DIRS;
|
||||
emit listAll();
|
||||
folderPage->refresh();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!(loaded&TAB_PLAYLISTS)) {
|
||||
loaded|=TAB_PLAYLISTS;
|
||||
playlistsModel.getPlaylists();
|
||||
playlistsPage->refresh();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
@@ -1766,52 +1414,6 @@ void MainWindow::cover(const QString &artist, const QString &album, const QImage
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateGenres(const QStringList &genres)
|
||||
{
|
||||
QStringList entries;
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
entries << i18n("All Genres");
|
||||
#else
|
||||
entries << tr("All Genres");
|
||||
#endif
|
||||
entries+=genres;
|
||||
|
||||
bool diff=libraryPage->genreCombo->count() != entries.count();
|
||||
if (!diff) {
|
||||
// Check items...
|
||||
for (int i=1; i<libraryPage->genreCombo->count() && !diff; ++i) {
|
||||
if (libraryPage->genreCombo->itemText(i) != entries.at(i)) {
|
||||
diff=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString currentFilter = libraryPage->genreCombo->currentIndex() ? libraryPage->genreCombo->currentText() : QString();
|
||||
|
||||
libraryPage->genreCombo->clear();
|
||||
if (genres.count()<2) {
|
||||
libraryPage->genreCombo->setCurrentIndex(0);
|
||||
} else {
|
||||
libraryPage->genreCombo->addItems(entries);
|
||||
if (!currentFilter.isEmpty()) {
|
||||
bool found=false;
|
||||
for (int i=1; i<libraryPage->genreCombo->count() && !found; ++i) {
|
||||
if (libraryPage->genreCombo->itemText(i) == currentFilter) {
|
||||
libraryPage->genreCombo->setCurrentIndex(i);
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
libraryPage->genreCombo->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showLibraryTab()
|
||||
{
|
||||
tabWidget->SetCurrentIndex(0);
|
||||
@@ -1836,4 +1438,3 @@ void MainWindow::showLyricsTab()
|
||||
{
|
||||
tabWidget->SetCurrentIndex(4);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,14 +46,8 @@
|
||||
#include <QMoveEvent>
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
#include "musiclibrarymodel.h"
|
||||
#include "musiclibraryproxymodel.h"
|
||||
#include "playlistsmodel.h"
|
||||
#include "playlistsproxymodel.h"
|
||||
#include "playlisttablemodel.h"
|
||||
#include "playlisttableproxymodel.h"
|
||||
#include "dirviewmodel.h"
|
||||
#include "dirviewproxymodel.h"
|
||||
#include "mpdstatus.h"
|
||||
#include "song.h"
|
||||
|
||||
@@ -142,21 +136,13 @@ protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
private:
|
||||
void addLibrarySelectionToPlaylist();
|
||||
void addDirViewSelectionToPlaylist();
|
||||
QStringList walkDirView(QModelIndex rootItem);
|
||||
bool setupTrayIcon();
|
||||
void setupPlaylistView();
|
||||
|
||||
Q_SIGNALS:
|
||||
// These are for communicating with MPD object (which is in its own thread, so need to talk via singal/slots)
|
||||
void setDetails(const QString &host, quint16 port, const QString &pass);
|
||||
void loadPlaylist(const QString &name);
|
||||
void removePlaylist(const QString &name);
|
||||
void savePlaylist(const QString &name);
|
||||
void renamePlaylist(const QString &oldname, const QString &newname);
|
||||
void removeSongs(const QList<qint32> &);
|
||||
void add(const QStringList &files);
|
||||
void pause(bool p);
|
||||
void play();
|
||||
void stop();
|
||||
@@ -164,8 +150,6 @@ Q_SIGNALS:
|
||||
void getStats();
|
||||
void clear();
|
||||
void playListInfo();
|
||||
void listAllInfo(const QDateTime &);
|
||||
void listAll();
|
||||
void currentSong();
|
||||
void setSeekId(quint32, quint32);
|
||||
void startPlayingSongId(quint32);
|
||||
@@ -188,8 +172,6 @@ private Q_SLOTS:
|
||||
void increaseVolume();
|
||||
void decreaseVolume();
|
||||
void setPosition();
|
||||
void searchMusicLibrary();
|
||||
void searchDirView();
|
||||
void searchPlaylist();
|
||||
void updatePlaylist(const QList<Song> &songs);
|
||||
void updateCurrentSong(const Song &song);
|
||||
@@ -199,24 +181,16 @@ private Q_SLOTS:
|
||||
void playlistItemActivated(const QModelIndex &);
|
||||
void removeFromPlaylist();
|
||||
void replacePlaylist();
|
||||
void libraryItemActivated(const QModelIndex &);
|
||||
void dirViewItemActivated(const QModelIndex &);
|
||||
void addToPlaylist();
|
||||
void trayIconClicked(QSystemTrayIcon::ActivationReason reason);
|
||||
void playlistTableViewContextMenuClicked();
|
||||
void playListTableViewToggleItem(const bool visible);
|
||||
void cropPlaylist();
|
||||
void updatePlayListStatus();
|
||||
void addPlaylistsSelectionToPlaylist();
|
||||
void playlistsViewItemDoubleClicked(const QModelIndex &index);
|
||||
void removePlaylist();
|
||||
void savePlaylist();
|
||||
void renamePlaylist();
|
||||
void copySongInfo();
|
||||
void togglePlaylist();
|
||||
void currentTabChanged(int index);
|
||||
void cover(const QString &artist, const QString &album, const QImage &img);
|
||||
void updateGenres(const QStringList &genres);
|
||||
void showLibraryTab();
|
||||
void showFoldersTab();
|
||||
void showPlaylistsTab();
|
||||
@@ -232,14 +206,8 @@ private:
|
||||
QDateTime lastDbUpdate;
|
||||
int fetchStatsFactor;
|
||||
int nowPlayingFactor;
|
||||
PlaylistsModel playlistsModel;
|
||||
PlaylistsProxyModel playlistsProxyModel;
|
||||
PlaylistTableModel playlistModel;
|
||||
PlaylistTableProxyModel playlistProxyModel;
|
||||
dirViewModel dirviewModel;
|
||||
DirViewProxyModel dirProxyModel;
|
||||
MusicLibraryModel musicLibraryModel;
|
||||
MusicLibraryProxyModel libraryProxyModel;
|
||||
bool draggingPositionSlider;
|
||||
QIcon playbackPause;
|
||||
QIcon playbackPlay;
|
||||
@@ -293,7 +261,11 @@ private:
|
||||
QThread *mpdThread;
|
||||
friend class VolumeSliderEventHandler;
|
||||
friend class CoverEventHandler;
|
||||
friend class LibraryPage;
|
||||
friend class FolderPage;
|
||||
friend class PlaylistsPage;
|
||||
friend class StreamsPage;
|
||||
friend class LyricsPage;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
175
gui/playlistspage.cpp
Normal file
175
gui/playlistspage.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Cantata
|
||||
*
|
||||
* Copyright (c) 2011 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
*
|
||||
* ----
|
||||
*
|
||||
* This program 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "playlistspage.h"
|
||||
#include "mpdconnection.h"
|
||||
#include <QtGui/QIcon>
|
||||
#include <QtGui/QToolButton>
|
||||
#include <QtGui/QInputDialog>
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
#include <KAction>
|
||||
#include <KLocale>
|
||||
#include <KActionCollection>
|
||||
#include <KMessageBox>
|
||||
#else
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QMessageBox>
|
||||
#endif
|
||||
|
||||
PlaylistsPage::PlaylistsPage(MainWindow *p)
|
||||
: QWidget(p)
|
||||
{
|
||||
setupUi(this);
|
||||
addToPlaylist->setDefaultAction(p->addToPlaylistAction);
|
||||
replacePlaylist->setDefaultAction(p->replacePlaylistAction);
|
||||
libraryUpdate->setDefaultAction(p->updateDbAction);
|
||||
delPlaylist->setDefaultAction(p->removePlaylistAction);
|
||||
// renamePlaylist->setDefaultAction(p->renamePlaylistAction);
|
||||
connect(view, SIGNAL(itemsSelected(bool)), addToPlaylist, SLOT(setEnabled(bool)));
|
||||
connect(view, SIGNAL(itemsSelected(bool)), replacePlaylist, SLOT(setEnabled(bool)));
|
||||
connect(view, SIGNAL(itemsSelected(bool)), delPlaylist, SLOT(setEnabled(bool)));
|
||||
// connect(view, SIGNAL(itemsSelected(bool)), renamePlaylist, SLOT(setEnabled(bool)));
|
||||
|
||||
addToPlaylist->setAutoRaise(true);
|
||||
replacePlaylist->setAutoRaise(true);
|
||||
libraryUpdate->setAutoRaise(true);
|
||||
delPlaylist->setAutoRaise(true);
|
||||
// renamePlaylist->setAutoRaise(true);
|
||||
addToPlaylist->setEnabled(false);
|
||||
replacePlaylist->setEnabled(false);
|
||||
delPlaylist->setEnabled(false);
|
||||
// renamePlaylist->setEnabled(false);
|
||||
|
||||
// #ifdef ENABLE_KDE_SUPPORT
|
||||
// search->setPlaceholderText(i18n("Search playlists..."));
|
||||
// #else
|
||||
// search->setPlaceholderText(tr("Search playlists..."));
|
||||
// #endif
|
||||
view->setHeaderHidden(true);
|
||||
view->sortByColumn(0, Qt::AscendingOrder);
|
||||
view->addAction(p->addToPlaylistAction);
|
||||
view->addAction(p->replacePlaylistAction);
|
||||
view->addAction(p->removePlaylistAction);
|
||||
view->addAction(p->renamePlaylistAction);
|
||||
|
||||
proxy.setSourceModel(&model);
|
||||
view->setModel(&proxy);
|
||||
connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(itemDoubleClicked(const QModelIndex &)));
|
||||
connect(this, SIGNAL(loadPlaylist(const QString &)), MPDConnection::self(), SLOT(load(const QString &)));
|
||||
connect(this, SIGNAL(removePlaylist(const QString &)), MPDConnection::self(), SLOT(rm(const QString &)));
|
||||
connect(this, SIGNAL(savePlaylist(const QString &)), MPDConnection::self(), SLOT(save(const QString &)));
|
||||
connect(this, SIGNAL(renamePlaylist(const QString &, const QString &)), MPDConnection::self(), SLOT(rename(const QString &, const QString &)));
|
||||
connect(p->removePlaylistAction, SIGNAL(activated()), this, SLOT(removePlaylist()));
|
||||
connect(p->savePlaylistAction, SIGNAL(activated()), this, SLOT(savePlaylist()));
|
||||
connect(p->renamePlaylistAction, SIGNAL(triggered()), this, SLOT(renamePlaylist()));
|
||||
}
|
||||
|
||||
PlaylistsPage::~PlaylistsPage()
|
||||
{
|
||||
}
|
||||
|
||||
void PlaylistsPage::refresh()
|
||||
{
|
||||
model.getPlaylists();
|
||||
}
|
||||
|
||||
void PlaylistsPage::clear()
|
||||
{
|
||||
model.clear();
|
||||
}
|
||||
|
||||
void PlaylistsPage::addSelectionToPlaylist()
|
||||
{
|
||||
const QModelIndexList items = view->selectionModel()->selectedRows();
|
||||
|
||||
if (items.size() == 1) {
|
||||
emit loadPlaylist(model.data(proxy.mapToSource(items.first()), Qt::DisplayRole).toString());
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistsPage::removePlaylist()
|
||||
{
|
||||
const QModelIndexList items = view->selectionModel()->selectedRows();
|
||||
|
||||
if (items.size() == 1) {
|
||||
QModelIndex sourceIndex = proxy.mapToSource(items.first());
|
||||
QString name = model.data(sourceIndex, Qt::DisplayRole).toString();
|
||||
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
if (KMessageBox::Yes == KMessageBox::warningYesNo(this, i18n("Are you sure you want to delete playlist: %1?", name),
|
||||
i18n("Delete Playlist?"))) {
|
||||
emit removePlaylist(name);
|
||||
}
|
||||
#else
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setWindowTitle(tr("Delete Playlist?"));
|
||||
msgBox.setText(tr("Are you sure you want to delete playlist: %1?").arg(name));
|
||||
msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
|
||||
msgBox.setDefaultButton(QMessageBox::No);
|
||||
int ret = msgBox.exec();
|
||||
|
||||
if (ret == QMessageBox::Yes) {
|
||||
emit removePlaylist(name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistsPage::savePlaylist()
|
||||
{
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
QString name = QInputDialog::getText(this, i18n("Playlist Name"), i18n("Enter a name for the playlist:"));
|
||||
#else
|
||||
QString name = QInputDialog::getText(this, tr("Playlist Name"), tr("Enter a name for the playlist:"));
|
||||
#endif
|
||||
|
||||
if (!name.isEmpty())
|
||||
emit savePlaylist(name);
|
||||
}
|
||||
|
||||
void PlaylistsPage::renamePlaylist()
|
||||
{
|
||||
const QModelIndexList items = view->selectionModel()->selectedRows();
|
||||
|
||||
if (items.size() == 1) {
|
||||
QModelIndex sourceIndex = proxy.mapToSource(items.first());
|
||||
QString name = model.data(sourceIndex, Qt::DisplayRole).toString();
|
||||
#ifdef ENABLE_KDE_SUPPORT
|
||||
QString new_name = QInputDialog::getText(this, i18n("Rename Playlist"), i18n("Enter new name for playlist: %1").arg(name));
|
||||
#else
|
||||
QString new_name = QInputDialog::getText(this, tr("Rename Playlist"), tr("Enter new name for playlist: %1").arg(name));
|
||||
#endif
|
||||
|
||||
if (!new_name.isEmpty()) {
|
||||
emit renamePlaylist(name, new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistsPage::itemDoubleClicked(const QModelIndex &index)
|
||||
{
|
||||
QModelIndex sourceIndex = proxy.mapToSource(index);
|
||||
QString name = model.data(sourceIndex, Qt::DisplayRole).toString();
|
||||
emit loadPlaylist(name);
|
||||
}
|
||||
@@ -25,11 +25,38 @@
|
||||
#define PLAYLISTSPAGE_H
|
||||
|
||||
#include "ui_playlistspage.h"
|
||||
#include "mainwindow.h"
|
||||
#include "playlistsmodel.h"
|
||||
#include "playlistsproxymodel.h"
|
||||
|
||||
class PlaylistsPage : public QWidget, public Ui::PlaylistsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PlaylistsPage(QWidget *p) : QWidget(p) { setupUi(this); }
|
||||
PlaylistsPage(MainWindow *p);
|
||||
virtual ~PlaylistsPage();
|
||||
|
||||
void refresh();
|
||||
void clear();
|
||||
void addSelectionToPlaylist();
|
||||
|
||||
Q_SIGNALS:
|
||||
// These are for communicating with MPD object (which is in its own thread, so need to talk via singal/slots)
|
||||
void add(const QStringList &files);
|
||||
void loadPlaylist(const QString &name);
|
||||
void removePlaylist(const QString &name);
|
||||
void savePlaylist(const QString &name);
|
||||
void renamePlaylist(const QString &oldname, const QString &newname);
|
||||
|
||||
private Q_SLOTS:
|
||||
void removePlaylist();
|
||||
void savePlaylist();
|
||||
void renamePlaylist();
|
||||
void itemDoubleClicked(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
PlaylistsModel model;
|
||||
PlaylistsProxyModel proxy;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="removePlaylist"/>
|
||||
<widget class="QToolButton" name="delPlaylist"/>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QToolButton" name="addToPlaylist"/>
|
||||
|
||||
Reference in New Issue
Block a user