Merge branch 'master' into 1180-local-fs-browsing
This commit is contained in:
@@ -56,7 +56,8 @@
|
||||
40. Add option to use 'Original Year' to display and sort albums.
|
||||
41. Sort folder view items, as MPD does not seem to sort playlist names.
|
||||
42. In folder view, allow to add folders and files at the same time.
|
||||
43. Add root and home local browse models, allowing to add local files to play
|
||||
43. Support dragging m3u and m3u8 playlists onto playqueue.
|
||||
44. Add root and home local browse models, allowing to add local files to play
|
||||
queue.
|
||||
|
||||
2.2.0
|
||||
|
||||
@@ -800,7 +800,7 @@ QList<LibraryDb::Album> LibraryDb::getAlbums(const QString &artistId, const QStr
|
||||
s.albumartist=Song::variousArtists();
|
||||
s.year = s.origYear = 0;
|
||||
}
|
||||
album=s.displayAlbum();
|
||||
album=s.albumName();
|
||||
int time=query.value(col++).toInt();
|
||||
int lastModified=wantModified ? query.value(col++).toInt() : 0;
|
||||
QString artist=wantArtist ? query.value(col++).toString() : QString();
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
#endif
|
||||
#include <QPalette>
|
||||
#include <QFont>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QModelIndex>
|
||||
#include <QMimeData>
|
||||
#include <QTextStream>
|
||||
@@ -78,17 +80,46 @@ static const QLatin1String constSortByPerformerKey("performer");
|
||||
static const QLatin1String constSortByTitleKey("title");
|
||||
static const QLatin1String constSortByNumberKey("track");
|
||||
|
||||
static QSet<QString> constPlaylists = QSet<QString>() << QLatin1String("m3u") << QLatin1String("m3u8");
|
||||
QSet<QString> PlayQueueModel::constFileExtensions = QSet<QString>()
|
||||
<< QLatin1String("mp3") << QLatin1String("ogg") << QLatin1String("flac") << QLatin1String("wma") << QLatin1String("m4a")
|
||||
<< QLatin1String("m4b") << QLatin1String("mp4") << QLatin1String("m4p") << QLatin1String("wav") << QLatin1String("wv")
|
||||
<< QLatin1String("wvp") << QLatin1String("aiff") << QLatin1String("aif") << QLatin1String("aifc") << QLatin1String("ape")
|
||||
<< QLatin1String("spx") << QLatin1String("tta") << QLatin1String("mpc") << QLatin1String("mpp") << QLatin1String("mp+")
|
||||
<< QLatin1String("dff") << QLatin1String("dsf");
|
||||
<< QLatin1String("dff") << QLatin1String("dsf")
|
||||
// And playlists...
|
||||
<< QLatin1String("m3u") << QLatin1String("m3u8");
|
||||
|
||||
static bool checkExtension(const QString &file)
|
||||
static bool checkExtension(const QString &file, const QSet<QString> &exts = PlayQueueModel::constFileExtensions)
|
||||
{
|
||||
int pos=file.lastIndexOf('.');
|
||||
return pos>1 ? PlayQueueModel::constFileExtensions.contains(file.mid(pos+1).toLower()) : false;
|
||||
return pos>1 ? exts.contains(file.mid(pos+1).toLower()) : false;
|
||||
}
|
||||
|
||||
static QStringList expandPlaylist(const QString &playlist)
|
||||
{
|
||||
QStringList files;
|
||||
QFile f(playlist);
|
||||
QDir dir(Utils::getDir(playlist));
|
||||
|
||||
if (f.open(QIODevice::ReadOnly|QIODevice::Text)) {
|
||||
QTextStream in(&f);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine();
|
||||
if (!line.startsWith(QLatin1Char('#'))) {
|
||||
if (checkExtension(line)) {
|
||||
QString path = dir.filePath(line);
|
||||
if (QFile::exists(path)) { // Relative
|
||||
files.append(path);
|
||||
} else if (QFile::exists(line)) { // Absolute
|
||||
files.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
static QStringList listFiles(const QDir &d, int level=5)
|
||||
@@ -98,6 +129,8 @@ static QStringList listFiles(const QDir &d, int level=5)
|
||||
for (const auto &f: d.entryInfoList(QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot|QDir::NoSymLinks)) {
|
||||
if (f.isDir()) {
|
||||
files += listFiles(QDir(f.absoluteFilePath()), level-1);
|
||||
} else if (checkExtension(f.fileName(), constPlaylists)) {
|
||||
files += expandPlaylist(f.absoluteFilePath());
|
||||
} else if (checkExtension(f.fileName())) {
|
||||
files.append(f.absoluteFilePath());
|
||||
}
|
||||
@@ -126,6 +159,8 @@ static QStringList parseUrls(const QStringList &urls, bool percentEncoded)
|
||||
|
||||
if (d.exists()) {
|
||||
files = listFiles(d);
|
||||
} else if (checkExtension(u.path(), constPlaylists)) {
|
||||
files += expandPlaylist(u.path());
|
||||
} else if (checkExtension(u.path())) {
|
||||
files.append(u.path());
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Cantata {
|
||||
Role_BriefMainText,
|
||||
Role_SubText,
|
||||
Role_TitleText,
|
||||
Role_TitleSubText,
|
||||
Role_TitleActions,
|
||||
Role_Image,
|
||||
Role_ListImage, // Should image been shown in list/tree view?
|
||||
|
||||
@@ -177,11 +177,10 @@ void SqlLibraryModel::libraryUpdated()
|
||||
QList<LibraryDb::Album> albums=db->getAlbums(QString(), QString(), albumSort);
|
||||
if (!albums.isEmpty()) {
|
||||
for (const LibraryDb::Album &album: albums) {
|
||||
QString trackInfo = tr("%n Tracks (%1)", "", album.trackCount).arg(Utils::formatTime(album.duration, true));
|
||||
root->add(new AlbumItem(T_Album==tl && album.identifyById ? QString() : album.artist,
|
||||
album.id, Song::displayAlbum(album.name, album.year),
|
||||
T_Album==tl
|
||||
? album.artist
|
||||
: tr("%n Tracks (%1)", "", album.trackCount).arg(Utils::formatTime(album.duration, true)), root));
|
||||
T_Album==tl ? album.artist : trackInfo, T_Album==tl ? trackInfo : QString(), root));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -366,11 +365,18 @@ QVariant SqlLibraryModel::data(const QModelIndex &index, int role) const
|
||||
(0==item->getChildCount()
|
||||
? item->getText()
|
||||
: (item->getText()+"<br/>"+data(index, Cantata::Role_SubText).toString()));
|
||||
case Cantata::Role_TitleSubText:
|
||||
if (T_Album==tl && T_Album==item->getType()) {
|
||||
return static_cast<AlbumItem *>(item)->getTitleSub();
|
||||
}
|
||||
case Cantata::Role_SubText:
|
||||
return item->getSubText();
|
||||
case Cantata::Role_ListImage:
|
||||
return T_Album==item->getType();
|
||||
case Cantata::Role_TitleText:
|
||||
if (T_Album==item->getType()) {
|
||||
return tr("%1 by %2").arg(item->getText()).arg(T_Album==tl ? item->getSubText() : item->getParent()->getText());
|
||||
}
|
||||
return item->getText();
|
||||
case Cantata::Role_TitleActions:
|
||||
switch (item->getType()) {
|
||||
|
||||
@@ -112,15 +112,18 @@ public:
|
||||
|
||||
class AlbumItem : public CollectionItem {
|
||||
public:
|
||||
AlbumItem(const QString &ar, const QString &i, const QString &txt=QString(), const QString &sub=QString(), CollectionItem *p=nullptr)
|
||||
: CollectionItem(T_Album, i, txt, sub, p), artistId(ar) { }
|
||||
AlbumItem(const QString &ar, const QString &i, const QString &txt=QString(), const QString &sub=QString(),
|
||||
const QString &tSub=QString(), CollectionItem *p=nullptr)
|
||||
: CollectionItem(T_Album, i, txt, sub, p), artistId(ar), titleSub(tSub) { }
|
||||
~AlbumItem() override { }
|
||||
|
||||
const QString & getArtistId() const { return artistId; }
|
||||
const QString getUniqueId() const override { return artistId+getId(); }
|
||||
const QString & getTitleSub() const {return titleSub; }
|
||||
|
||||
private:
|
||||
QString artistId;
|
||||
QString titleSub;
|
||||
};
|
||||
|
||||
SqlLibraryModel(LibraryDb *d, QObject *p, Type top=T_Artist);
|
||||
|
||||
@@ -1609,10 +1609,14 @@ void ItemView::setTitle()
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
QString sub = model->data(index, Cantata::Role_TitleSubText).toString();
|
||||
if (sub.isEmpty()) {
|
||||
sub = model->data(index, Cantata::Role_SubText).toString();
|
||||
}
|
||||
title->update(model->data(index, Mode_IconTop==mode ? Cantata::Role_GridCoverSong : Cantata::Role_CoverSong).value<Song>(),
|
||||
model->data(index, Qt::DecorationRole).value<QIcon>(),
|
||||
model->data(index, Cantata::Role_TitleText).toString(),
|
||||
model->data(index, Cantata::Role_SubText).toString(),
|
||||
sub,
|
||||
model->data(index, Cantata::Role_TitleActions).toBool());
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,11 @@ TitleWidget::TitleWidget(QWidget *p)
|
||||
textLayout->addWidget(mainText);
|
||||
textLayout->addWidget(subText);
|
||||
layout->addItem(textLayout, 0, 3, 2, 1);
|
||||
mainText->installEventFilter(this);
|
||||
subText->installEventFilter(this);
|
||||
image->installEventFilter(this);
|
||||
installEventFilter(this);
|
||||
setAttribute(Qt::WA_Hover);
|
||||
mainText->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
subText->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
image->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
viewport()->installEventFilter(this);
|
||||
viewport()->setAttribute(Qt::WA_Hover);
|
||||
connect(Covers::self(), SIGNAL(cover(Song,QImage,QString)), this, SLOT(coverRetrieved(Song,QImage,QString)));
|
||||
connect(Covers::self(), SIGNAL(coverUpdated(Song,QImage,QString)), this, SLOT(coverRetrieved(Song,QImage,QString)));
|
||||
connect(Covers::self(), SIGNAL(artistImage(Song,QImage,QString)), this, SLOT(coverRetrieved(Song,QImage,QString)));
|
||||
@@ -169,9 +169,9 @@ void TitleWidget::update(const Song &sng, const QIcon &icon, const QString &text
|
||||
|
||||
bool TitleWidget::eventFilter(QObject *o, QEvent *event)
|
||||
{
|
||||
switch(event->type()) {
|
||||
case QEvent::HoverEnter:
|
||||
if (isEnabled() && o==this) {
|
||||
if (isEnabled()) {
|
||||
switch(event->type()) {
|
||||
case QEvent::HoverEnter: {
|
||||
QPalette pal = qApp->palette();
|
||||
#ifdef Q_OS_MAC
|
||||
QColor col(OSXStyle::self()->viewPalette().color(QPalette::Highlight));
|
||||
@@ -181,38 +181,38 @@ bool TitleWidget::eventFilter(QObject *o, QEvent *event)
|
||||
col.setAlphaF(0.2);
|
||||
pal.setColor(QPalette::Base, col);
|
||||
setPalette(pal);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case QEvent::HoverLeave:
|
||||
if (isEnabled() && o==this) {
|
||||
case QEvent::HoverLeave: {
|
||||
QPalette pal = qApp->palette();
|
||||
QColor col(pal.color(QPalette::Base));
|
||||
pal.setColor(QPalette::Base, col);
|
||||
setPalette(pal);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonPress:
|
||||
if (Qt::LeftButton==static_cast<QMouseEvent *>(event)->button() && Qt::NoModifier==static_cast<QMouseEvent *>(event)->modifiers()) {
|
||||
QPalette pal = qApp->palette();
|
||||
#ifdef Q_OS_MAC
|
||||
QColor col(OSXStyle::self()->viewPalette().color(QPalette::Highlight));
|
||||
#else
|
||||
QColor col(pal.color(QPalette::Highlight));
|
||||
#endif
|
||||
col.setAlphaF(0.5);
|
||||
pal.setColor(QPalette::Base, col);
|
||||
setPalette(pal);
|
||||
pressed=true;
|
||||
case QEvent::MouseButtonPress:
|
||||
if (Qt::LeftButton==static_cast<QMouseEvent *>(event)->button() && Qt::NoModifier==static_cast<QMouseEvent *>(event)->modifiers()) {
|
||||
QPalette pal = qApp->palette();
|
||||
#ifdef Q_OS_MAC
|
||||
QColor col(OSXStyle::self()->viewPalette().color(QPalette::Highlight));
|
||||
#else
|
||||
QColor col(pal.color(QPalette::Highlight));
|
||||
#endif
|
||||
col.setAlphaF(0.5);
|
||||
pal.setColor(QPalette::Base, col);
|
||||
setPalette(pal);
|
||||
pressed=true;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
if (pressed && Qt::LeftButton==static_cast<QMouseEvent *>(event)->button() && !QApplication::overrideCursor()) {
|
||||
actions().first()->trigger();
|
||||
}
|
||||
pressed=false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
if (pressed && Qt::LeftButton==static_cast<QMouseEvent *>(event)->button() && !QApplication::overrideCursor()) {
|
||||
actions().first()->trigger();
|
||||
}
|
||||
pressed=false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QWidget::eventFilter(o, event);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user