Add "group by" support to the library, and improve the whole library model to make it more robust.

Still todo: the "Group by advanced..." dialog, unit tests.
Updates issue #94.
This commit is contained in:
David Sansome 2010-03-31 00:30:57 +00:00
parent df38ebf9d9
commit 4c42813d9f
27 changed files with 1318 additions and 446 deletions

View File

@ -125,41 +125,67 @@ void Library::SongsDiscovered(const SongList& songs) {
if (!query_options_.Matches(song))
continue;
LibraryItem* artist = NULL;
LibraryItem* album = NULL;
// Find parent containers in the tree
LibraryItem* container = root_;
for (int i=0 ; i<3 ; ++i) {
QueryOptions::GroupBy type = query_options_.group_by[i];
if (type == QueryOptions::GroupBy_None) break;
if (song.is_compilation()) {
if (compilation_artist_node_ == NULL)
CreateCompilationArtistNode(true);
artist = compilation_artist_node_;
} else {
if (artist_nodes_.contains(song.artist()))
artist = artist_nodes_[song.artist()];
else {
artist = CreateArtistNode(true, song.artist());
// Special case: if we're at the top level and the song is a compilation
// and the top level is Artists, then we want the Various Artists node :(
if (i == 0 && type == QueryOptions::GroupBy_Artist && song.is_compilation()) {
if (compilation_artist_node_ == NULL)
CreateCompilationArtistNode(true, root_);
container = compilation_artist_node_;
} else {
// Otherwise find the proper container based on the item's key
QString key;
switch (type) {
case QueryOptions::GroupBy_Album: key = song.album(); break;
case QueryOptions::GroupBy_Artist: key = song.artist(); break;
case QueryOptions::GroupBy_Composer: key = song.composer(); break;
case QueryOptions::GroupBy_Genre: key = song.genre(); break;
case QueryOptions::GroupBy_Year:
key = QString::number(qMax(0, song.year())); break;
case QueryOptions::GroupBy_YearAlbum:
key = PrettyYearAlbum(qMax(0, song.year()), song.album()); break;
case QueryOptions::GroupBy_None: Q_ASSERT(0); break;
}
if (!container_nodes_[i].contains(key)) {
// Create the container
container_nodes_[i][key] =
ItemFromSong(type, true, i == 0, container, song);
container_nodes_[i][key]->container_level = i;
}
container = container_nodes_[i][key];
}
// If we just created the damn thing then we don't need to continue into
// it any further.
if (!container->lazy_loaded)
break;
}
if (artist->lazy_loaded) {
album = artist->ChildByKey(song.album());
if (album == NULL)
album = CreateAlbumNode(true, song.album(), artist, song.is_compilation(),
song.art_automatic(), song.art_manual(), artist->key);
if (!container->lazy_loaded)
continue;
if (album->lazy_loaded)
CreateSongNode(true, song, album);
}
// We've gone all the way down to the lowest level, so now we have to
// create the song in the container.
song_nodes_[song.id()] =
ItemFromSong(QueryOptions::GroupBy_None, true, false, container, song);
}
}
LibraryItem* Library::CreateCompilationArtistNode(bool signal) {
LibraryItem* parent = root_;
LibraryItem* Library::CreateCompilationArtistNode(bool signal, LibraryItem* parent) {
if (signal)
beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
compilation_artist_node_ =
new LibraryItem(LibraryItem::Type_CompilationArtist, tr("Various Artists"), parent);
new LibraryItem(LibraryItem::Type_Container, parent);
compilation_artist_node_->key = tr("Various Artists");
compilation_artist_node_->sort_text = " various";
compilation_artist_node_->container_level = parent->container_level + 1;
if (signal)
endInsertRows();
@ -167,191 +193,133 @@ LibraryItem* Library::CreateCompilationArtistNode(bool signal) {
return compilation_artist_node_;
}
LibraryItem* Library::CreateArtistNode(bool signal, const QString& name) {
LibraryItem* parent = root_;
if (signal)
beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
QString Library::DividerKey(QueryOptions::GroupBy type,
LibraryItem* item) const {
if (item->sort_text.isEmpty())
return QString();
LibraryItem* ret = new LibraryItem(LibraryItem::Type_Artist, name, parent);
ret->display_text = PrettyArtist(name);
ret->sort_text = SortTextForArtist(name);
switch (type) {
case QueryOptions::GroupBy_Album:
case QueryOptions::GroupBy_Artist:
case QueryOptions::GroupBy_Composer:
case QueryOptions::GroupBy_Genre:
if (item->sort_text[0].isDigit())
return "0";
if (item->sort_text[0] == ' ')
return QString();
return QString(item->sort_text[0]);
artist_nodes_[name] = ret;
case QueryOptions::GroupBy_Year:
return QString::number(item->sort_text.toInt() / 10 * 10);
if (signal)
endInsertRows();
case QueryOptions::GroupBy_YearAlbum:
return QString::number(item->metadata.year());
if (!name.isEmpty()) {
QChar divider_char = DividerChar(ret->sort_text);
if (!divider_char.isNull() && !divider_nodes_.contains(divider_char)) {
if (signal)
beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
LibraryItem* divider =
new LibraryItem(LibraryItem::Type_Divider, QString(divider_char), root_);
divider->lazy_loaded = true;
if (divider_char == '0')
divider->display_text = "0-9";
divider_nodes_[divider_char] = divider;
if (signal)
endInsertRows();
}
case QueryOptions::GroupBy_None:
default:
Q_ASSERT(0);
return QString();
}
return ret;
}
QChar Library::DividerChar(const QString& sort_text) const {
if (sort_text.isEmpty())
return QChar();
QString Library::DividerDisplayText(QueryOptions::GroupBy type,
const QString& key) const {
switch (type) {
case QueryOptions::GroupBy_Album:
case QueryOptions::GroupBy_Artist:
case QueryOptions::GroupBy_Composer:
case QueryOptions::GroupBy_Genre:
if (key == "0")
return "0-9";
// fallthrough
QChar ret = sort_text[0];
if (ret.isDigit())
return '0';
case QueryOptions::GroupBy_Year:
case QueryOptions::GroupBy_YearAlbum:
return key;
return ret;
}
LibraryItem* Library::CreateAlbumNode(bool signal, const QString& name,
LibraryItem* parent, bool compilation,
const QString& art_automatic,
const QString& art_manual,
const QString& artist) {
if (signal)
beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
LibraryItem* ret = new LibraryItem(
compilation ? LibraryItem::Type_CompilationAlbum
: LibraryItem::Type_Album,
name, parent);
ret->display_text = PrettyAlbum(name);
ret->sort_text = SortTextForAlbum(name);
// TODO: These should be async
/*if (!art_manual.isNull())
ret->cover_art.load(art_manual);
if (!art_automatic.isNull() && ret->cover_art.isNull())
ret->cover_art.load(art_automatic);*/
ret->artist = compilation ? QString() : artist;
if (signal)
endInsertRows();
return ret;
}
LibraryItem* Library::CreateSongNode(bool signal, const Song& song, LibraryItem* parent) {
if (signal)
beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
LibraryItem* ret = new LibraryItem(LibraryItem::Type_Song, song.title(), parent);
ret->lazy_loaded = true;
ret->display_text = song.PrettyTitleWithArtist();
ret->song = song;
song_nodes_[song.id()] = ret;
if (signal)
endInsertRows();
return ret;
case QueryOptions::GroupBy_None:
default:
Q_ASSERT(0);
return QString();
}
}
void Library::SongsDeleted(const SongList& songs) {
// Delete song nodes
QSet<LibraryItem*> parents;
foreach (const Song& song, songs) {
if (song_nodes_.contains(song.id())) {
LibraryItem* node = song_nodes_[song.id()];
if (node->parent != root_)
parents << node->parent;
beginRemoveRows(ItemToIndex(node->parent), node->row, node->row);
node->parent->Delete(node->row);
song_nodes_.remove(song.id());
endRemoveRows();
} else {
// If we get here it means some of the songs we want to delete haven't
// been lazy-loaded yet. This is bad, because it would mean that to
// clean up empty parents we would need to lazy-load them all
// individually. This can take a very long time, so better to just
// reset the model.
Reset();
return;
}
}
// Delete now-empty album nodes
foreach (const Song& song, songs) {
LibraryItem* artist = NULL;
// Now delete empty parents
QSet<QString> divider_keys;
while (!parents.isEmpty()) {
foreach (LibraryItem* node, parents) {
parents.remove(node);
if (node->children.count() != 0)
continue;
if (song.is_compilation() && compilation_artist_node_ &&
compilation_artist_node_->lazy_loaded)
artist = compilation_artist_node_;
else if (!song.is_compilation() &&
artist_nodes_.contains(song.artist()) &&
artist_nodes_[song.artist()]->lazy_loaded)
artist = artist_nodes_[song.artist()];
// Consider its parent for the next round
if (node->parent != root_)
parents << node->parent;
if (artist == NULL)
continue;
// Maybe consider its divider node
if (node->container_level == 0)
divider_keys << DividerKey(query_options_.group_by[0], node);
LibraryItem* node = artist->ChildByKey(song.album());
if (!node)
continue;
// Special case the Various Artists node
if (node == compilation_artist_node_)
compilation_artist_node_ = NULL;
else
container_nodes_[node->container_level].remove(node->key);
LazyPopulate(node);
if (node->children.count() == 0) {
// It was empty - delete it
beginRemoveRows(ItemToIndex(node->parent), node->row, node->row);
node->parent->Delete(node->row);
endRemoveRows();
}
}
// Delete now-empty artist nodes
foreach (const Song& song, songs) {
// Was it a compilation?
LibraryItem* node = NULL;
if (song.is_compilation())
node = compilation_artist_node_;
else
node = artist_nodes_.contains(song.artist()) ? artist_nodes_[song.artist()] : NULL;
// Delete empty dividers
foreach (const QString& divider_key, divider_keys) {
if (!divider_nodes_.contains(divider_key))
continue;
if (node) {
LazyPopulate(node);
if (node->children.count() == 0) {
beginRemoveRows(ItemToIndex(node->parent), node->row, node->row);
node->parent->Delete(node->row);
if (song.is_compilation())
compilation_artist_node_ = NULL;
else
artist_nodes_.remove(song.artist());
endRemoveRows();
bool found = false;
foreach (LibraryItem* node, container_nodes_[0].values()) {
if (DividerKey(query_options_.group_by[0], node) == divider_key) {
found = true;
break;
}
}
}
// Delete now-empty dividers
foreach (const Song& song, songs) {
QString sort_text(SortTextForArtist(song.artist()));
QChar divider_char(DividerChar(sort_text));
if (!divider_char.isNull() && !sort_text.isEmpty() &&
divider_nodes_.contains(divider_char)) {
bool found = false;
foreach (LibraryItem* artist_node, artist_nodes_.values()) {
if (artist_node->sort_text.startsWith(divider_char)) {
found = true;
break;
}
}
if (found)
continue;
if (!found) {
int row = divider_nodes_[divider_char]->row;
beginRemoveRows(ItemToIndex(root_), row, row);
root_->Delete(row);
endRemoveRows();
divider_nodes_.remove(divider_char);
}
}
// Remove the divider
int row = divider_nodes_[divider_key]->row;
beginRemoveRows(ItemToIndex(root_), row, row);
root_->Delete(row);
endRemoveRows();
divider_nodes_.remove(divider_key);
}
}
@ -362,113 +330,337 @@ QVariant Library::data(const QModelIndex& index, int role) const {
}
QVariant Library::data(const LibraryItem* item, int role) const {
QueryOptions::GroupBy container_type =
item->type == LibraryItem::Type_Container ?
query_options_.group_by[item->container_level] : QueryOptions::GroupBy_None;
switch (role) {
case Qt::DisplayRole:
return item->DisplayText();
case Qt::DecorationRole:
switch (item->type) {
case LibraryItem::Type_Album:
case LibraryItem::Type_CompilationAlbum: {
// TODO
/*if (item->cover_art.isNull())
return no_cover_icon_;
else
return QIcon(item->cover_art);*/
return album_icon_;
}
case LibraryItem::Type_Artist:
case LibraryItem::Type_CompilationArtist:
return artist_icon_;
switch (item->type)
case LibraryItem::Type_Container:
switch (container_type) {
case QueryOptions::GroupBy_Album:
return album_icon_;
case QueryOptions::GroupBy_Artist:
return artist_icon_;
default:
break;
}
default:
break;
}
break;
case Role_Type:
return item->type;
case Role_ContainerType:
return container_type;
case Role_Key:
return item->key;
case Role_Artist:
return item->artist;
return item->metadata.artist();
case Role_SortText:
if (item->type == LibraryItem::Type_Song)
return item->song.disc() * 1000 + item->song.track();
return item->metadata.disc() * 1000 + item->metadata.track();
return item->SortText();
}
return QVariant();
}
void Library::LazyPopulate(LibraryItem* item) {
if (item->lazy_loaded)
void Library::LazyPopulate(LibraryItem* parent, bool signal) {
if (parent->lazy_loaded)
return;
parent->lazy_loaded = true;
switch (item->type) {
case LibraryItem::Type_CompilationArtist:
foreach (const LibraryBackendInterface::Album& album,
backend_->Worker()->GetCompilationAlbums(query_options_))
CreateAlbumNode(false, album.album_name, item, true, album.art_automatic, album.art_manual, album.artist);
break;
// Information about what we want the children to be
int child_level = parent->container_level + 1;
QueryOptions::GroupBy child_type = query_options_.group_by[child_level];
case LibraryItem::Type_CompilationAlbum:
foreach (const Song& song, backend_->Worker()->GetCompilationSongs(item->key, query_options_))
CreateSongNode(false, song, item);
break;
// Initialise the query
LibraryQuery q(query_options_);
InitQuery(child_type, &q);
case LibraryItem::Type_Artist:
foreach (const LibraryBackendInterface::Album& album,
backend_->Worker()->GetAlbumsByArtist(item->key, query_options_))
CreateAlbumNode(false, album.album_name, item, false, album.art_automatic, album.art_manual, album.artist);
break;
case LibraryItem::Type_Album:
foreach (const Song& song, backend_->Worker()->GetSongs(item->parent->key, item->key, query_options_))
CreateSongNode(false, song, item);
break;
default:
qWarning("Tried to LazyPopulate a bad item type");
break;
// Top-level artists is special - we don't want compilation albums appearing
if (child_level == 0 && child_type == QueryOptions::GroupBy_Artist) {
q.AddCompilationRequirement(false);
}
item->lazy_loaded = true;
// Walk up through the item's parents adding filters as necessary
LibraryItem* p = parent;
while (p != root_) {
FilterQuery(query_options_.group_by[p->container_level], p, &q);
p = p->parent;
}
// Execute the query
if (!backend_->Worker()->ExecQuery(&q))
return;
// Step through the results
while (q.Next()) {
// Create the item - it will get inserted into the model here
LibraryItem* item =
ItemFromQuery(child_type, signal, child_level == 0, parent, q);
item->container_level = child_level;
// Save a pointer to it for later
if (child_type == QueryOptions::GroupBy_None)
song_nodes_[item->metadata.id()] = item;
else
container_nodes_[child_level][item->key] = item;
}
}
void Library::Reset() {
delete root_;
artist_nodes_.clear();
song_nodes_.clear();
container_nodes_[0].clear();
container_nodes_[1].clear();
container_nodes_[2].clear();
divider_nodes_.clear();
compilation_artist_node_ = NULL;
root_ = new LibraryItem(this);
root_->lazy_loaded = true;
root_->lazy_loaded = false;
// Various artists?
if (backend_->Worker()->HasCompilations(query_options_))
CreateCompilationArtistNode(false);
if (query_options_.group_by[0] == QueryOptions::GroupBy_Artist &&
backend_->Worker()->HasCompilations(query_options_))
CreateCompilationArtistNode(false, root_);
// Populate artists
foreach (const QString& artist, backend_->Worker()->GetAllArtists(query_options_))
CreateArtistNode(false, artist);
// Populate top level
LazyPopulate(root_, false);
reset();
}
QString Library::PrettyArtist(QString artist) const {
if (artist.isEmpty()) {
artist = tr("Unknown");
void Library::InitQuery(QueryOptions::GroupBy type, LibraryQuery* q) {
switch (type) {
case QueryOptions::GroupBy_Artist:
q->SetColumnSpec("DISTINCT artist");
break;
case QueryOptions::GroupBy_Album:
q->SetColumnSpec("DISTINCT album");
break;
case QueryOptions::GroupBy_Composer:
q->SetColumnSpec("DISTINCT composer");
break;
case QueryOptions::GroupBy_YearAlbum:
q->SetColumnSpec("DISTINCT year, album");
break;
case QueryOptions::GroupBy_Year:
q->SetColumnSpec("DISTINCT year");
break;
case QueryOptions::GroupBy_Genre:
q->SetColumnSpec("DISTINCT genre");
break;
case QueryOptions::GroupBy_None:
q->SetColumnSpec("ROWID, " + QString(Song::kColumnSpec));
break;
}
}
void Library::FilterQuery(QueryOptions::GroupBy type, LibraryItem* item,
LibraryQuery* q) {
switch (type) {
case QueryOptions::GroupBy_Artist:
if (item == compilation_artist_node_)
q->AddCompilationRequirement(true);
else {
if (item->container_level == 0) // Stupid hack
q->AddCompilationRequirement(false);
q->AddWhere("artist", item->key);
}
break;
case QueryOptions::GroupBy_Album:
q->AddWhere("album", item->key);
break;
case QueryOptions::GroupBy_YearAlbum:
q->AddWhere("year", item->metadata.year());
q->AddWhere("album", item->metadata.album());
break;
case QueryOptions::GroupBy_Year:
q->AddWhere("year", item->key);
break;
case QueryOptions::GroupBy_Composer:
q->AddWhere("composer", item->key);
break;
case QueryOptions::GroupBy_Genre:
q->AddWhere("genre", item->key);
break;
case QueryOptions::GroupBy_None:
Q_ASSERT(0);
break;
}
}
LibraryItem* Library::InitItem(QueryOptions::GroupBy type,
bool signal, LibraryItem *parent) {
LibraryItem::Type item_type =
type == QueryOptions::GroupBy_None ? LibraryItem::Type_Song :
LibraryItem::Type_Container;
if (signal)
beginInsertRows(ItemToIndex(parent),
parent->children.count(),parent->children.count());
// Initialise the item depending on what type it's meant to be
return new LibraryItem(item_type, parent);
}
LibraryItem* Library::ItemFromQuery(QueryOptions::GroupBy type,
bool signal, bool create_divider,
LibraryItem* parent, const LibraryQuery& q) {
LibraryItem* item = InitItem(type, signal, parent);
int year = 0;
switch (type) {
case QueryOptions::GroupBy_Artist:
item->key = q.Value(0).toString();
item->display_text = TextOrUnknown(item->key);
item->sort_text = SortTextForArtist(item->key);
break;
case QueryOptions::GroupBy_YearAlbum:
year = qMax(0, q.Value(0).toInt());
item->metadata.set_year(year);
item->metadata.set_album(q.Value(1).toString());
item->key = PrettyYearAlbum(year, item->metadata.album());
item->sort_text = SortTextForYear(year) + item->metadata.album();
break;
case QueryOptions::GroupBy_Year:
year = qMax(0, q.Value(0).toInt());
item->key = QString::number(year);
item->sort_text = SortTextForYear(year);
break;
case QueryOptions::GroupBy_Composer:
case QueryOptions::GroupBy_Genre:
case QueryOptions::GroupBy_Album:
item->key = q.Value(0).toString();
item->display_text = TextOrUnknown(item->key);
item->sort_text = SortText(item->key);
break;
case QueryOptions::GroupBy_None:
item->metadata.InitFromQuery(q);
item->key = item->metadata.title();
item->display_text = item->metadata.PrettyTitleWithArtist();
break;
}
return artist;
FinishItem(type, signal, create_divider, parent, item);
return item;
}
LibraryItem* Library::ItemFromSong(QueryOptions::GroupBy type,
bool signal, bool create_divider,
LibraryItem* parent, const Song& s) {
LibraryItem* item = InitItem(type, signal, parent);
int year = 0;
switch (type) {
case QueryOptions::GroupBy_Artist:
item->key = s.artist();
item->display_text = TextOrUnknown(item->key);
item->sort_text = SortTextForArtist(item->key);
break;
case QueryOptions::GroupBy_YearAlbum:
year = qMax(0, s.year());
item->metadata.set_year(year);
item->metadata.set_album(s.album());
item->key = PrettyYearAlbum(year, s.album());
item->sort_text = SortTextForYear(year) + s.album();
break;
case QueryOptions::GroupBy_Year:
year = qMax(0, s.year());
item->key = QString::number(year);
item->sort_text = SortTextForYear(year);
break;
case QueryOptions::GroupBy_Composer: item->key = s.composer();
case QueryOptions::GroupBy_Genre: if (item->key.isNull()) item->key = s.genre();
case QueryOptions::GroupBy_Album: if (item->key.isNull()) item->key = s.album();
item->display_text = TextOrUnknown(item->key);
item->sort_text = SortText(item->key);
break;
case QueryOptions::GroupBy_None:
item->metadata = s;
item->key = s.title();
item->display_text = s.PrettyTitleWithArtist();
break;
}
FinishItem(type, signal, create_divider, parent, item);
return item;
}
void Library::FinishItem(QueryOptions::GroupBy type,
bool signal, bool create_divider,
LibraryItem *parent, LibraryItem *item) {
if (type == QueryOptions::GroupBy_None)
item->lazy_loaded = true;
if (signal)
endInsertRows();
// Create the divider entry if we're supposed to
if (create_divider) {
QString divider_key = DividerKey(type, item);
if (!divider_key.isEmpty() && !divider_nodes_.contains(divider_key)) {
if (signal)
beginInsertRows(ItemToIndex(parent), parent->children.count(),
parent->children.count());
LibraryItem* divider =
new LibraryItem(LibraryItem::Type_Divider, root_);
divider->key = divider_key;
divider->display_text = DividerDisplayText(type, divider_key);
divider->lazy_loaded = true;
divider_nodes_[divider_key] = divider;
if (signal)
endInsertRows();
}
}
}
QString Library::TextOrUnknown(const QString& text) const {
if (text.isEmpty()) {
return tr("Unknown");
}
return text;
}
QString Library::PrettyYearAlbum(int year, const QString& album) const {
return QString::number(year) + " - " + TextOrUnknown(album);
}
QString Library::SortText(QString text) const {
if (text.isEmpty()) {
text = " unknown";
} else {
text = text.toLower();
}
text = text.remove(QRegExp("[^\\w ]"));
return text;
}
QString Library::SortTextForArtist(QString artist) const {
artist = SortTextForAlbum(artist);
artist = SortText(artist);
if (artist.startsWith("the ")) {
artist = artist.right(artist.length() - 4) + ", the";
@ -477,32 +669,15 @@ QString Library::SortTextForArtist(QString artist) const {
return artist;
}
QString Library::PrettyAlbum(QString album) const {
if (album.isEmpty()) {
album = tr("Unknown");
}
return album;
}
QString Library::SortTextForAlbum(QString album) const {
if (album.isEmpty()) {
album = " unknown";
} else {
album = album.toLower();
}
album = album.remove(QRegExp("[^\\w ]"));
return album;
QString Library::SortTextForYear(int year) const {
QString str = QString::number(year);
return QString("0").repeated(qMax(0, 4 - str.length())) + str;
}
Qt::ItemFlags Library::flags(const QModelIndex& index) const {
switch (IndexToItem(index)->type) {
case LibraryItem::Type_Album:
case LibraryItem::Type_Artist:
case LibraryItem::Type_Song:
case LibraryItem::Type_CompilationAlbum:
case LibraryItem::Type_CompilationArtist:
case LibraryItem::Type_Container:
return Qt::ItemIsSelectable |
Qt::ItemIsEnabled |
Qt::ItemIsDragEnabled;
@ -541,10 +716,7 @@ bool Library::CompareItems(const LibraryItem* a, const LibraryItem* b) const {
void Library::GetChildSongs(LibraryItem* item, QList<QUrl>* urls, SongList* songs) const {
switch (item->type) {
case LibraryItem::Type_Album:
case LibraryItem::Type_Artist:
case LibraryItem::Type_CompilationAlbum:
case LibraryItem::Type_CompilationArtist: {
case LibraryItem::Type_Container: {
const_cast<Library*>(this)->LazyPopulate(item);
QList<LibraryItem*> children = item->children;
@ -557,8 +729,8 @@ void Library::GetChildSongs(LibraryItem* item, QList<QUrl>* urls, SongList* song
}
case LibraryItem::Type_Song:
urls->append(QUrl::fromLocalFile(item->song.filename()));
songs->append(item->song);
urls->append(QUrl::fromLocalFile(item->metadata.filename()));
songs->append(item->metadata);
break;
default:
@ -594,3 +766,12 @@ bool Library::canFetchMore(const QModelIndex &parent) const {
LibraryItem* item = IndexToItem(parent);
return !item->lazy_loaded;
}
void Library::SetGroupBy(QueryOptions::GroupBy g[3]) {
query_options_.group_by[0] = g[0];
query_options_.group_by[1] = g[1];
query_options_.group_by[2] = g[2];
if (!waiting_for_threads_)
Reset();
}

View File

@ -42,6 +42,7 @@ class Library : public SimpleTreeModel<LibraryItem> {
enum {
Role_Type = Qt::UserRole + 1,
Role_ContainerType,
Role_SortText,
Role_Key,
Role_Artist,
@ -80,9 +81,11 @@ class Library : public SimpleTreeModel<LibraryItem> {
public slots:
void SetFilterAge(int age);
void SetFilterText(const QString& text);
void SetGroupBy(QueryOptions::GroupBy g[3]);
protected:
void LazyPopulate(LibraryItem* item);
void LazyPopulate(LibraryItem* item) { LazyPopulate(item, false); }
void LazyPopulate(LibraryItem* item, bool signal);
private slots:
// From LibraryBackend
@ -97,25 +100,37 @@ class Library : public SimpleTreeModel<LibraryItem> {
private:
void Initialise();
LibraryItem* CreateCompilationArtistNode(bool signal);
LibraryItem* CreateArtistNode(bool signal, const QString& name);
LibraryItem* CreateAlbumNode(bool signal, const QString& name,
LibraryItem* parent, bool compilation,
const QString& art_automatic,
const QString& art_manual,
const QString& artist);
LibraryItem* CreateSongNode(bool signal, const Song& song, LibraryItem* parent);
// Functions for working with queries and creating items
void InitQuery(QueryOptions::GroupBy type, LibraryQuery* q);
void FilterQuery(QueryOptions::GroupBy type,
LibraryItem* item,LibraryQuery* q);
LibraryItem* ItemFromQuery(QueryOptions::GroupBy type,
bool signal, bool create_divider,
LibraryItem* parent, const LibraryQuery& q);
LibraryItem* ItemFromSong(QueryOptions::GroupBy type,
bool signal, bool create_divider,
LibraryItem* parent, const Song& s);
LibraryItem* CreateCompilationArtistNode(bool signal, LibraryItem* parent);
QString PrettyArtist(QString artist) const;
QString PrettyAlbum(QString album) const;
// Helpers for ItemFromQuery and ItemFromSong
LibraryItem* InitItem(QueryOptions::GroupBy type, bool signal, LibraryItem* parent);
void FinishItem(QueryOptions::GroupBy type, bool signal, bool create_divider,
LibraryItem* parent, LibraryItem* item);
// Functions for manipulating text
QString TextOrUnknown(const QString& text) const;
QString PrettyYearAlbum(int year, const QString& album) const;
QString SortText(QString text) const;
QString SortTextForArtist(QString artist) const;
QString SortTextForAlbum(QString album) const;
QString SortTextForYear(int year) const;
QString DividerKey(QueryOptions::GroupBy type, LibraryItem* item) const;
QString DividerDisplayText(QueryOptions::GroupBy type, const QString& key) const;
// Helpers
QVariant data(const LibraryItem* item, int role) const;
bool CompareItems(const LibraryItem* a, const LibraryItem* b) const;
QChar DividerChar(const QString& sort_text) const;
private:
EngineBase* engine_;
@ -129,9 +144,16 @@ class Library : public SimpleTreeModel<LibraryItem> {
QueryOptions query_options_;
// Keyed on database ID
QMap<int, LibraryItem*> song_nodes_;
QMap<QString, LibraryItem*> artist_nodes_;
QMap<QChar, LibraryItem*> divider_nodes_;
// Keyed on whatever the key is for that level - artist, album, year, etc.
QMap<QString, LibraryItem*> container_nodes_[3];
// Keyed on a letter, a year, a century, etc.
QMap<QString, LibraryItem*> divider_nodes_;
// Only applies if the first level is "artist"
LibraryItem* compilation_artist_node_;
QIcon artist_icon_;

View File

@ -467,13 +467,11 @@ QStringList LibraryBackend::GetAllArtists(const QueryOptions& opt) {
query.SetColumnSpec("DISTINCT artist");
query.AddCompilationRequirement(false);
QSqlQuery q(query.Query(Connect()));
q.exec();
if (CheckErrors(q.lastError())) return QStringList();
if (!ExecQuery(&query)) return QStringList();
QStringList ret;
while (q.next()) {
ret << q.value(0).toString();
while (query.Next()) {
ret << query.Value(0).toString();
}
return ret;
}
@ -494,14 +492,12 @@ SongList LibraryBackend::GetSongs(const QString& artist, const QString& album, c
query.AddWhere("artist", artist);
query.AddWhere("album", album);
QSqlQuery q(query.Query(Connect()));
q.exec();
if (CheckErrors(q.lastError())) return SongList();
if (!ExecQuery(&query)) return SongList();
SongList ret;
while (q.next()) {
while (query.Next()) {
Song song;
song.InitFromQuery(q);
song.InitFromQuery(query);
ret << song;
}
return ret;
@ -528,11 +524,9 @@ bool LibraryBackend::HasCompilations(const QueryOptions& opt) {
query.SetColumnSpec("ROWID");
query.AddCompilationRequirement(true);
QSqlQuery q(query.Query(Connect()));
q.exec();
if (CheckErrors(q.lastError())) return false;
if (!ExecQuery(&query)) return false;
return q.next();
return query.Next();
}
LibraryBackend::AlbumList LibraryBackend::GetCompilationAlbums(const QueryOptions& opt) {
@ -545,14 +539,12 @@ SongList LibraryBackend::GetCompilationSongs(const QString& album, const QueryOp
query.AddCompilationRequirement(true);
query.AddWhere("album", album);
QSqlQuery q(query.Query(Connect()));
q.exec();
if (CheckErrors(q.lastError())) return SongList();
if (!ExecQuery(&query)) return SongList();
SongList ret;
while (q.next()) {
while (query.Next()) {
Song song;
song.InitFromQuery(q);
song.InitFromQuery(query);
ret << song;
}
return ret;
@ -673,20 +665,18 @@ LibraryBackend::AlbumList LibraryBackend::GetAlbums(const QString& artist,
query.AddWhere("artist", artist);
}
QSqlQuery q(query.Query(Connect()));
q.exec();
if (CheckErrors(q.lastError())) return ret;
if (!ExecQuery(&query)) return ret;
QString last_album;
QString last_artist;
while (q.next()) {
bool compilation = q.value(2).toBool() | q.value(3).toBool();
while (query.Next()) {
bool compilation = query.Value(2).toBool() | query.Value(3).toBool();
Album info;
info.artist = compilation ? QString() : q.value(1).toString();
info.album_name = q.value(0).toString();
info.art_automatic = q.value(4).toString();
info.art_manual = q.value(5).toString();
info.artist = compilation ? QString() : query.Value(1).toString();
info.album_name = query.Value(0).toString();
info.art_automatic = query.Value(4).toString();
info.art_manual = query.Value(5).toString();
if (info.artist == last_artist && info.album_name == last_album)
continue;
@ -710,13 +700,11 @@ LibraryBackend::Album LibraryBackend::GetAlbumArt(const QString& artist, const Q
query.AddWhere("artist", artist);
query.AddWhere("album", album);
QSqlQuery q(query.Query(Connect()));
q.exec();
if (CheckErrors(q.lastError())) return ret;
if (!ExecQuery(&query)) return ret;
if (q.next()) {
ret.art_automatic = q.value(0).toString();
ret.art_manual = q.value(1).toString();
if (query.Next()) {
ret.art_automatic = query.Value(0).toString();
ret.art_manual = query.Value(1).toString();
}
return ret;
@ -761,14 +749,12 @@ void LibraryBackend::ForceCompilation(const QString& artist, const QString& albu
if (!artist.isNull())
query.AddWhere("artist", artist);
QSqlQuery q(query.Query(db));
q.exec();
CheckErrors(q.lastError());
if (!ExecQuery(&query)) return;
SongList deleted_songs;
while (q.next()) {
while (query.Next()) {
Song song;
song.InitFromQuery(q);
song.InitFromQuery(query);
deleted_songs << song;
}
@ -780,7 +766,7 @@ void LibraryBackend::ForceCompilation(const QString& artist, const QString& albu
if (!artist.isEmpty())
sql += " AND artist = :artist";
q = QSqlQuery(sql, db);
QSqlQuery q(sql, db);
q.bindValue(":forced_compilation_on", on ? 1 : 0);
q.bindValue(":forced_compilation_off", on ? 0 : 1);
q.bindValue(":album", album);
@ -791,14 +777,12 @@ void LibraryBackend::ForceCompilation(const QString& artist, const QString& albu
CheckErrors(q.lastError());
// Now get the updated songs
q = QSqlQuery(query.Query(db));
q.exec();
CheckErrors(q.lastError());
if (!ExecQuery(&query)) return;
SongList added_songs;
while (q.next()) {
while (query.Next()) {
Song song;
song.InitFromQuery(q);
song.InitFromQuery(query);
added_songs << song;
}
@ -807,3 +791,7 @@ void LibraryBackend::ForceCompilation(const QString& artist, const QString& albu
emit SongsDiscovered(added_songs);
}
}
bool LibraryBackend::ExecQuery(LibraryQuery *q) {
return !CheckErrors(q->Exec(Connect()));
}

View File

@ -82,6 +82,8 @@ class LibraryBackendInterface : public QObject {
virtual void UpdateCompilationsAsync() = 0;
virtual bool ExecQuery(LibraryQuery* q) = 0;
public slots:
virtual void LoadDirectories() = 0;
virtual void UpdateTotalSongCount() = 0;
@ -144,6 +146,8 @@ class LibraryBackend : public LibraryBackendInterface {
void UpdateCompilationsAsync();
bool ExecQuery(LibraryQuery* q);
public slots:
void LoadDirectories();
void UpdateTotalSongCount();

View File

@ -28,25 +28,17 @@ class LibraryItem : public SimpleTreeItem<LibraryItem> {
enum Type {
Type_Root,
Type_Divider,
Type_CompilationArtist,
Type_CompilationAlbum,
Type_Artist,
Type_Album,
Type_Container,
Type_Song,
};
LibraryItem(SimpleTreeModel<LibraryItem>* model)
: SimpleTreeItem<LibraryItem>(Type_Root, model) {}
LibraryItem(Type type, const QString& key = QString::null, LibraryItem* parent = NULL)
: SimpleTreeItem<LibraryItem>(type, key, parent) {}
: SimpleTreeItem<LibraryItem>(Type_Root, model), container_level(-1) {}
LibraryItem(Type type, LibraryItem* parent = NULL)
: SimpleTreeItem<LibraryItem>(type, parent), container_level(-1) {}
Song song;
// Maybe stores album cover art
QPixmap cover_art;
// Stores the artist of an album
QString artist;
int container_level;
Song metadata;
};
#endif // LIBRARYITEM_H

View File

@ -19,6 +19,16 @@
#include <QtDebug>
#include <QDateTime>
#include <QSqlError>
QueryOptions::QueryOptions()
: max_age(-1)
{
group_by[0] = GroupBy_Artist;
group_by[1] = GroupBy_Album;
group_by[2] = GroupBy_None;
}
LibraryQuery::LibraryQuery()
{
@ -65,7 +75,7 @@ void LibraryQuery::AddCompilationRequirement(bool compilation) {
where_clauses_ << QString("effective_compilation = %1").arg(compilation ? 1 : 0);
}
QSqlQuery LibraryQuery::Query(QSqlDatabase db) const {
QSqlError LibraryQuery::Exec(QSqlDatabase db) {
QString sql = QString("SELECT %1 FROM songs").arg(column_spec_);
if (!where_clauses_.isEmpty())
@ -74,14 +84,23 @@ QSqlQuery LibraryQuery::Query(QSqlDatabase db) const {
if (!order_by_.isEmpty())
sql += " ORDER BY " + order_by_;
QSqlQuery q(sql, db);
query_ = QSqlQuery(sql, db);
// Bind values
foreach (const QVariant& value, bound_values_) {
q.addBindValue(value);
query_.addBindValue(value);
}
return q;
query_.exec();
return query_.lastError();
}
bool LibraryQuery::Next() {
return query_.next();
}
QVariant LibraryQuery::Value(int column) const {
return query_.value(column);
}
bool QueryOptions::Matches(const Song& song) const {

View File

@ -26,10 +26,22 @@
class Song;
struct QueryOptions {
QueryOptions() : max_age(-1) {}
QueryOptions();
// These values get saved in QSettings - don't change them
enum GroupBy {
GroupBy_None = 0,
GroupBy_Artist = 1,
GroupBy_Album = 2,
GroupBy_YearAlbum = 3,
GroupBy_Year = 4,
GroupBy_Composer = 5,
GroupBy_Genre = 6,
};
bool Matches(const Song& song) const;
GroupBy group_by[3];
QString filter;
int max_age;
};
@ -45,13 +57,19 @@ class LibraryQuery {
void AddWhereLike(const QString& column, const QVariant& value);
void AddCompilationRequirement(bool compilation);
QSqlQuery Query(QSqlDatabase db) const;
QSqlError Exec(QSqlDatabase db);
bool Next();
QVariant Value(int column) const;
operator const QSqlQuery& () const { return query_; }
private:
QString column_spec_;
QString order_by_;
QStringList where_clauses_;
QVariantList bound_values_;
QSqlQuery query_;
};
#endif // LIBRARYQUERY_H

View File

@ -178,9 +178,8 @@ void LibraryView::contextMenuEvent(QContextMenuEvent *e) {
context_menu_index_ = qobject_cast<QSortFilterProxyModel*>(model())
->mapToSource(context_menu_index_);
int type = library_->data(context_menu_index_, Library::Role_Type).toInt();
bool enable_various = type == LibraryItem::Type_Album ||
type == LibraryItem::Type_CompilationAlbum;
int type = library_->data(context_menu_index_, Library::Role_ContainerType).toInt();
bool enable_various = type == QueryOptions::GroupBy_Album;
show_in_various_->setEnabled(enable_various);
no_show_in_various_->setEnabled(enable_various);

View File

@ -233,10 +233,23 @@ MainWindow::MainWindow(QNetworkAccessManager* network, QWidget *parent)
connect(filter_age_mapper, SIGNAL(mapped(int)), library_, SLOT(SetFilterAge(int)));
connect(ui_.library_filter_clear, SIGNAL(clicked()), SLOT(ClearLibraryFilter()));
// "Group by ..."
QActionGroup* group_by_group = new QActionGroup(this);
group_by_group->addAction(ui_.group_by_artist);
group_by_group->addAction(ui_.group_by_artist_album);
group_by_group->addAction(ui_.group_by_artist_yearalbum);
group_by_group->addAction(ui_.group_by_album);
group_by_group->addAction(ui_.group_by_genre_album);
group_by_group->addAction(ui_.group_by_genre_artist_album);
group_by_group->addAction(ui_.group_by_advanced);
connect(group_by_group, SIGNAL(triggered(QAction*)), SLOT(GroupByClicked(QAction*)));
// Library config menu
QMenu* library_menu = new QMenu(this);
library_menu->addActions(filter_age_group->actions());
library_menu->addSeparator();
library_menu->addActions(group_by_group->actions());
library_menu->addSeparator();
library_menu->addAction(tr("Configure library..."), library_config_dialog_, SLOT(show()));
ui_.library_options->setMenu(library_menu);
@ -322,6 +335,13 @@ MainWindow::MainWindow(QNetworkAccessManager* network, QWidget *parent)
ui_.file_view->SetPath(settings_.value("file_path", QDir::homePath()).toString());
QueryOptions::GroupBy g[3];
g[0] = QueryOptions::GroupBy(settings_.value("group_by1", int(QueryOptions::GroupBy_Artist)).toInt());
g[1] = QueryOptions::GroupBy(settings_.value("group_by2", int(QueryOptions::GroupBy_Album)).toInt());
g[2] = QueryOptions::GroupBy(settings_.value("group_by3", int(QueryOptions::GroupBy_None)).toInt());
library_->SetGroupBy(g);
UpdateGroupBySelection(g);
if (!settings_.value("hidden", false).toBool()) {
show();
}
@ -780,3 +800,67 @@ void MainWindow::AddStreamAccepted() {
void MainWindow::PlaylistRemoveCurrent() {
ui_.playlist->RemoveSelected();
}
void MainWindow::GroupByClicked(QAction* action) {
QueryOptions::GroupBy g[3];
g[0] = QueryOptions::GroupBy_None;
g[1] = QueryOptions::GroupBy_None;
g[2] = QueryOptions::GroupBy_None;
if (action == ui_.group_by_album) {
g[0] = QueryOptions::GroupBy_Album;
} else if (action == ui_.group_by_artist) {
g[0] = QueryOptions::GroupBy_Artist;
} else if (action == ui_.group_by_artist_album) {
g[0] = QueryOptions::GroupBy_Artist;
g[1] = QueryOptions::GroupBy_Album;
} else if (action == ui_.group_by_artist_yearalbum) {
g[0] = QueryOptions::GroupBy_Artist;
g[1] = QueryOptions::GroupBy_YearAlbum;
} else if (action == ui_.group_by_genre_album) {
g[0] = QueryOptions::GroupBy_Genre;
g[1] = QueryOptions::GroupBy_Album;
} else if (action == ui_.group_by_genre_artist_album) {
g[0] = QueryOptions::GroupBy_Genre;
g[1] = QueryOptions::GroupBy_Artist;
g[2] = QueryOptions::GroupBy_Album;
} else {
qWarning() << "Unknown action in" << __PRETTY_FUNCTION__;
return;
}
library_->SetGroupBy(g);
settings_.setValue("group_by1", int(g[0]));
settings_.setValue("group_by2", int(g[1]));
settings_.setValue("group_by3", int(g[2]));
}
void MainWindow::UpdateGroupBySelection(QueryOptions::GroupBy g[3]) {
if (g[0] == QueryOptions::GroupBy_Album &&
g[1] == QueryOptions::GroupBy_None &&
g[2] == QueryOptions::GroupBy_None)
ui_.group_by_album->setChecked(true);
else if (g[0] == QueryOptions::GroupBy_Artist &&
g[1] == QueryOptions::GroupBy_None &&
g[2] == QueryOptions::GroupBy_None)
ui_.group_by_artist->setChecked(true);
else if (g[0] == QueryOptions::GroupBy_Artist &&
g[1] == QueryOptions::GroupBy_Album &&
g[2] == QueryOptions::GroupBy_None)
ui_.group_by_artist_album->setChecked(true);
else if (g[0] == QueryOptions::GroupBy_Artist &&
g[1] == QueryOptions::GroupBy_YearAlbum &&
g[2] == QueryOptions::GroupBy_None)
ui_.group_by_artist_yearalbum->setChecked(true);
else if (g[0] == QueryOptions::GroupBy_Genre &&
g[1] == QueryOptions::GroupBy_Album &&
g[2] == QueryOptions::GroupBy_None)
ui_.group_by_genre_album->setChecked(true);
else if (g[0] == QueryOptions::GroupBy_Genre &&
g[1] == QueryOptions::GroupBy_Artist &&
g[2] == QueryOptions::GroupBy_Album)
ui_.group_by_genre_artist_album->setChecked(true);
else
ui_.group_by_advanced->setChecked(true);
}

View File

@ -82,6 +82,7 @@ class MainWindow : public QMainWindow {
void LibraryDoubleClick(const QModelIndex& index);
void ClearLibraryFilter();
void GroupByClicked(QAction*);
void VolumeWheelEvent(int delta);
void TrayClicked(QSystemTrayIcon::ActivationReason reason);
@ -106,6 +107,7 @@ class MainWindow : public QMainWindow {
private:
void SaveGeometry();
void UpdateGroupBySelection(QueryOptions::GroupBy g[3]);
private:
static const int kStateVersion;

View File

@ -41,7 +41,7 @@
<bool>true</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::SelectedClicked|QAbstractItemView::EditKeyPressed</set>
<set>QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
@ -756,6 +756,62 @@
<string>Remove from playlist</string>
</property>
</action>
<action name="group_by_artist">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Group by Artist</string>
</property>
</action>
<action name="group_by_artist_album">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Group by Artist/Album</string>
</property>
</action>
<action name="group_by_artist_yearalbum">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Group by Artist/Year - Album</string>
</property>
</action>
<action name="group_by_album">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Group by Album</string>
</property>
</action>
<action name="group_by_genre_album">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Group by Genre/Album</string>
</property>
</action>
<action name="group_by_genre_artist_album">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Group by Genre/Artist/Album</string>
</property>
</action>
<action name="group_by_advanced">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Advanced grouping...</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -26,7 +26,8 @@ template <typename T>
class SimpleTreeItem {
public:
SimpleTreeItem(int _type, SimpleTreeModel<T>* _model); // For the root item
SimpleTreeItem(int _type, const QString& _key = QString::null, T* _parent = NULL);
SimpleTreeItem(int _type, const QString& _key, T* _parent = NULL);
SimpleTreeItem(int _type, T* _parent = NULL);
virtual ~SimpleTreeItem();
void InsertNotify(T* _parent);
@ -77,6 +78,19 @@ SimpleTreeItem<T>::SimpleTreeItem(int _type, const QString& _key, T* _parent)
}
}
template <typename T>
SimpleTreeItem<T>::SimpleTreeItem(int _type, T* _parent)
: type(_type),
lazy_loaded(false),
parent(_parent),
model(_parent ? _parent->model : NULL)
{
if (parent) {
row = parent->children.count();
parent->children << static_cast<T*>(this);
}
}
template <typename T>
void SimpleTreeItem<T>::InsertNotify(T* _parent) {

View File

@ -618,13 +618,12 @@ p, li { white-space: pre-wrap; }
<context>
<name>Library</name>
<message>
<location filename="../library.cpp" line="161"/>
<location filename="../library.cpp" line="186"/>
<source>Various Artists</source>
<translation>Různí umělci</translation>
</message>
<message>
<location filename="../library.cpp" line="464"/>
<location filename="../library.cpp" line="482"/>
<location filename="../library.cpp" line="642"/>
<source>Unknown</source>
<translation>Neznámý</translation>
</message>
@ -742,10 +741,10 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="517"/>
<location filename="../mainwindow.cpp" line="244"/>
<location filename="../mainwindow.cpp" line="379"/>
<location filename="../mainwindow.cpp" line="395"/>
<location filename="../mainwindow.cpp" line="576"/>
<location filename="../mainwindow.cpp" line="257"/>
<location filename="../mainwindow.cpp" line="399"/>
<location filename="../mainwindow.cpp" line="415"/>
<location filename="../mainwindow.cpp" line="596"/>
<source>Play</source>
<translation>Přehrát</translation>
</message>
@ -771,7 +770,7 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="559"/>
<location filename="../mainwindow.cpp" line="246"/>
<location filename="../mainwindow.cpp" line="259"/>
<source>Stop after this track</source>
<translation>Zastavit po této skladbě</translation>
</message>
@ -874,7 +873,7 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="728"/>
<location filename="../mainwindow.cpp" line="358"/>
<location filename="../mainwindow.cpp" line="378"/>
<source>&amp;Hide tray icon</source>
<translation>S&amp;krýt ikonu v systémovém panelu</translation>
</message>
@ -899,29 +898,64 @@ p, li { white-space: pre-wrap; }
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="240"/>
<location filename="../mainwindow.ui" line="764"/>
<source>Group by Artist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="772"/>
<source>Group by Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="780"/>
<source>Group by Artist/Year - Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="788"/>
<source>Group by Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="796"/>
<source>Group by Genre/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="804"/>
<source>Group by Genre/Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="812"/>
<source>Advanced grouping...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="253"/>
<source>Configure library...</source>
<translation>Nastavit knihovnu...</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="333"/>
<location filename="../mainwindow.cpp" line="354"/>
<location filename="../mainwindow.cpp" line="353"/>
<location filename="../mainwindow.cpp" line="374"/>
<source>&amp;Show tray icon</source>
<translation>Zobrazit ikonu v &amp;systémovém panelu</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="406"/>
<location filename="../mainwindow.cpp" line="573"/>
<location filename="../mainwindow.cpp" line="426"/>
<location filename="../mainwindow.cpp" line="593"/>
<source>Pause</source>
<translation>Pozastavit</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="623"/>
<location filename="../mainwindow.cpp" line="643"/>
<source>Set %1 to &quot;%2&quot;...</source>
<translation>Nastavit %1 na &quot;%2&quot;...</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="625"/>
<location filename="../mainwindow.cpp" line="645"/>
<source>Edit tag &quot;%1&quot;...</source>
<translation type="unfinished"></translation>
</message>

View File

@ -771,6 +771,34 @@ p, li { white-space: pre-wrap; }
<source>Remove from playlist</source>
<translation>Αφαίρεση από την λίστα</translation>
</message>
<message>
<source>Group by Artist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Artist/Year - Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Genre/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Genre/Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Advanced grouping...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiLoadingIndicator</name>

View File

@ -604,13 +604,12 @@ p, li { white-space: pre-wrap; }
<context>
<name>Library</name>
<message>
<location filename="../library.cpp" line="161"/>
<location filename="../library.cpp" line="186"/>
<source>Various Artists</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../library.cpp" line="464"/>
<location filename="../library.cpp" line="482"/>
<location filename="../library.cpp" line="642"/>
<source>Unknown</source>
<translation type="unfinished"></translation>
</message>
@ -728,10 +727,10 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="517"/>
<location filename="../mainwindow.cpp" line="244"/>
<location filename="../mainwindow.cpp" line="379"/>
<location filename="../mainwindow.cpp" line="395"/>
<location filename="../mainwindow.cpp" line="576"/>
<location filename="../mainwindow.cpp" line="257"/>
<location filename="../mainwindow.cpp" line="399"/>
<location filename="../mainwindow.cpp" line="415"/>
<location filename="../mainwindow.cpp" line="596"/>
<source>Play</source>
<translation type="unfinished"></translation>
</message>
@ -757,7 +756,7 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="559"/>
<location filename="../mainwindow.cpp" line="246"/>
<location filename="../mainwindow.cpp" line="259"/>
<source>Stop after this track</source>
<translation type="unfinished"></translation>
</message>
@ -863,9 +862,44 @@ p, li { white-space: pre-wrap; }
<source>Remove from playlist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="764"/>
<source>Group by Artist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="772"/>
<source>Group by Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="780"/>
<source>Group by Artist/Year - Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="788"/>
<source>Group by Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="796"/>
<source>Group by Genre/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="804"/>
<source>Group by Genre/Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="812"/>
<source>Advanced grouping...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="728"/>
<location filename="../mainwindow.cpp" line="358"/>
<location filename="../mainwindow.cpp" line="378"/>
<source>&amp;Hide tray icon</source>
<translation type="unfinished"></translation>
</message>
@ -885,29 +919,29 @@ p, li { white-space: pre-wrap; }
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="240"/>
<location filename="../mainwindow.cpp" line="253"/>
<source>Configure library...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="333"/>
<location filename="../mainwindow.cpp" line="354"/>
<location filename="../mainwindow.cpp" line="353"/>
<location filename="../mainwindow.cpp" line="374"/>
<source>&amp;Show tray icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="406"/>
<location filename="../mainwindow.cpp" line="573"/>
<location filename="../mainwindow.cpp" line="426"/>
<location filename="../mainwindow.cpp" line="593"/>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="623"/>
<location filename="../mainwindow.cpp" line="643"/>
<source>Set %1 to &quot;%2&quot;...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="625"/>
<location filename="../mainwindow.cpp" line="645"/>
<source>Edit tag &quot;%1&quot;...</source>
<translation type="unfinished"></translation>
</message>

View File

@ -754,6 +754,34 @@ p, li { white-space: pre-wrap; }
<source>Remove from playlist</source>
<translation>Eliminar de la lista de reproducción</translation>
</message>
<message>
<source>Group by Artist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Artist/Year - Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Genre/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Genre/Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Advanced grouping...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiLoadingIndicator</name>

View File

@ -617,13 +617,12 @@ p, li { white-space: pre-wrap; }
<context>
<name>Library</name>
<message>
<location filename="../library.cpp" line="161"/>
<location filename="../library.cpp" line="186"/>
<source>Various Artists</source>
<translation>Compilations d&apos;artistes</translation>
</message>
<message>
<location filename="../library.cpp" line="464"/>
<location filename="../library.cpp" line="482"/>
<location filename="../library.cpp" line="642"/>
<source>Unknown</source>
<translation>Inconnu</translation>
</message>
@ -741,10 +740,10 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="517"/>
<location filename="../mainwindow.cpp" line="244"/>
<location filename="../mainwindow.cpp" line="379"/>
<location filename="../mainwindow.cpp" line="395"/>
<location filename="../mainwindow.cpp" line="576"/>
<location filename="../mainwindow.cpp" line="257"/>
<location filename="../mainwindow.cpp" line="399"/>
<location filename="../mainwindow.cpp" line="415"/>
<location filename="../mainwindow.cpp" line="596"/>
<source>Play</source>
<translation>Lecture</translation>
</message>
@ -770,7 +769,7 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="559"/>
<location filename="../mainwindow.cpp" line="246"/>
<location filename="../mainwindow.cpp" line="259"/>
<source>Stop after this track</source>
<translation>Arrêter la lecture après cette piste</translation>
</message>
@ -873,7 +872,7 @@ p, li { white-space: pre-wrap; }
</message>
<message>
<location filename="../mainwindow.ui" line="728"/>
<location filename="../mainwindow.cpp" line="358"/>
<location filename="../mainwindow.cpp" line="378"/>
<source>&amp;Hide tray icon</source>
<translation>&amp;Masquer l&apos;icône</translation>
</message>
@ -898,29 +897,64 @@ p, li { white-space: pre-wrap; }
<translation>Supprimer de la liste de lecture</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="240"/>
<location filename="../mainwindow.ui" line="764"/>
<source>Group by Artist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="772"/>
<source>Group by Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="780"/>
<source>Group by Artist/Year - Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="788"/>
<source>Group by Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="796"/>
<source>Group by Genre/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="804"/>
<source>Group by Genre/Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="812"/>
<source>Advanced grouping...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="253"/>
<source>Configure library...</source>
<translation>Configurer votre bibliothèque...</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="333"/>
<location filename="../mainwindow.cpp" line="354"/>
<location filename="../mainwindow.cpp" line="353"/>
<location filename="../mainwindow.cpp" line="374"/>
<source>&amp;Show tray icon</source>
<translation>&amp;Afficher l&apos;icône</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="406"/>
<location filename="../mainwindow.cpp" line="573"/>
<location filename="../mainwindow.cpp" line="426"/>
<location filename="../mainwindow.cpp" line="593"/>
<source>Pause</source>
<translation>Pause</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="623"/>
<location filename="../mainwindow.cpp" line="643"/>
<source>Set %1 to &quot;%2&quot;...</source>
<translation>Définir %1 à la valeur &quot;%2&quot;...</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="625"/>
<location filename="../mainwindow.cpp" line="645"/>
<source>Edit tag &quot;%1&quot;...</source>
<translation>Modifer le tag &quot;%1&quot;...</translation>
</message>

View File

@ -741,6 +741,34 @@ p, li { white-space: pre-wrap; }
<source>Remove from playlist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Artist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Artist/Year - Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Genre/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group by Genre/Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Advanced grouping...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MultiLoadingIndicator</name>

View File

@ -618,13 +618,12 @@ p, li { white-space: pre-wrap; }
<context>
<name>Library</name>
<message>
<location filename="../library.cpp" line="161"/>
<location filename="../library.cpp" line="186"/>
<source>Various Artists</source>
<translation>Rôzni interpréti</translation>
</message>
<message>
<location filename="../library.cpp" line="464"/>
<location filename="../library.cpp" line="482"/>
<location filename="../library.cpp" line="642"/>
<source>Unknown</source>
<translation>Neznámi</translation>
</message>
@ -686,44 +685,44 @@ p, li { white-space: pre-wrap; }
<context>
<name>MainWindow</name>
<message>
<location filename="../mainwindow.cpp" line="240"/>
<location filename="../mainwindow.cpp" line="253"/>
<source>Configure library...</source>
<translation>Nastaviť zbierku...</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="517"/>
<location filename="../mainwindow.cpp" line="244"/>
<location filename="../mainwindow.cpp" line="379"/>
<location filename="../mainwindow.cpp" line="395"/>
<location filename="../mainwindow.cpp" line="576"/>
<location filename="../mainwindow.cpp" line="257"/>
<location filename="../mainwindow.cpp" line="399"/>
<location filename="../mainwindow.cpp" line="415"/>
<location filename="../mainwindow.cpp" line="596"/>
<source>Play</source>
<translation>Hrať</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="559"/>
<location filename="../mainwindow.cpp" line="246"/>
<location filename="../mainwindow.cpp" line="259"/>
<source>Stop after this track</source>
<translation>Zastaviť po tejto skladbe</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="333"/>
<location filename="../mainwindow.cpp" line="354"/>
<location filename="../mainwindow.cpp" line="353"/>
<location filename="../mainwindow.cpp" line="374"/>
<source>&amp;Show tray icon</source>
<translation>&amp;Zobraziť tray ikonu</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="406"/>
<location filename="../mainwindow.cpp" line="573"/>
<location filename="../mainwindow.cpp" line="426"/>
<location filename="../mainwindow.cpp" line="593"/>
<source>Pause</source>
<translation>Pauza</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="623"/>
<location filename="../mainwindow.cpp" line="643"/>
<source>Set %1 to &quot;%2&quot;...</source>
<translation>Nastaviť %1 do &quot;%2&quot;...</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="625"/>
<location filename="../mainwindow.cpp" line="645"/>
<source>Edit tag &quot;%1&quot;...</source>
<translation>Upraviť tag &quot;%1&quot;...</translation>
</message>
@ -904,9 +903,44 @@ p, li { white-space: pre-wrap; }
<source>Remove from playlist</source>
<translation>Odstrániť z playlistu</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="764"/>
<source>Group by Artist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="772"/>
<source>Group by Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="780"/>
<source>Group by Artist/Year - Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="788"/>
<source>Group by Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="796"/>
<source>Group by Genre/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="804"/>
<source>Group by Genre/Artist/Album</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="812"/>
<source>Advanced grouping...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.ui" line="728"/>
<location filename="../mainwindow.cpp" line="358"/>
<location filename="../mainwindow.cpp" line="378"/>
<source>&amp;Hide tray icon</source>
<translation>&amp;Skryť tray ikonu</translation>
</message>

View File

@ -639,12 +639,12 @@ msgid "Tag"
msgstr "Značka"
#. ts-context Library
#: ../library.cpp:161
#: ../library.cpp:186
msgid "Various Artists"
msgstr "Různí umělci"
#. ts-context Library
#: ../library.cpp:464 ../library.cpp:482
#: ../library.cpp:642
msgid "Unknown"
msgstr "Neznámý"
@ -749,8 +749,8 @@ msgid "Previous track"
msgstr "Předchozí skladba"
#. ts-context MainWindow
#: ../mainwindow.ui:517 ../mainwindow.cpp:244 ../mainwindow.cpp:379
#: ../mainwindow.cpp:395 ../mainwindow.cpp:576
#: ../mainwindow.ui:517 ../mainwindow.cpp:257 ../mainwindow.cpp:399
#: ../mainwindow.cpp:415 ../mainwindow.cpp:596
msgid "Play"
msgstr "Přehrát"
@ -775,7 +775,7 @@ msgid "Ctrl+Q"
msgstr "Ctrl+Q"
#. ts-context MainWindow
#: ../mainwindow.ui:559 ../mainwindow.cpp:246
#: ../mainwindow.ui:559 ../mainwindow.cpp:259
msgid "Stop after this track"
msgstr "Zastavit po této skladbě"
@ -876,7 +876,7 @@ msgid "Open media..."
msgstr "Otevřít média..."
#. ts-context MainWindow
#: ../mainwindow.ui:728 ../mainwindow.cpp:358
#: ../mainwindow.ui:728 ../mainwindow.cpp:378
msgid "&Hide tray icon"
msgstr "S&krýt ikonu v systémovém panelu"
@ -902,27 +902,69 @@ msgid "Remove from playlist"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:240
#: ../mainwindow.ui:764
#, fuzzy
msgid "Group by Artist"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:772
#, fuzzy
msgid "Group by Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:780
#, fuzzy
msgid "Group by Artist/Year - Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:788
#, fuzzy
msgid "Group by Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:796
#, fuzzy
msgid "Group by Genre/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:804
#, fuzzy
msgid "Group by Genre/Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:812
#, fuzzy
msgid "Advanced grouping..."
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:253
msgid "Configure library..."
msgstr "Nastavit knihovnu..."
#. ts-context MainWindow
#: ../mainwindow.cpp:333 ../mainwindow.cpp:354
#: ../mainwindow.cpp:353 ../mainwindow.cpp:374
msgid "&Show tray icon"
msgstr "Zobrazit ikonu v &systémovém panelu"
#. ts-context MainWindow
#: ../mainwindow.cpp:406 ../mainwindow.cpp:573
#: ../mainwindow.cpp:426 ../mainwindow.cpp:593
msgid "Pause"
msgstr "Pozastavit"
#. ts-context MainWindow
#: ../mainwindow.cpp:623
#: ../mainwindow.cpp:643
msgid "Set %1 to \"%2\"..."
msgstr "Nastavit %1 na \"%2\"..."
#. ts-context MainWindow
#: ../mainwindow.cpp:625
#: ../mainwindow.cpp:645
#, fuzzy
msgid "Edit tag \"%1\"..."
msgstr ""

View File

@ -761,6 +761,41 @@ msgstr "Τροποποίησησ ετικέτας..."
msgid "Remove from playlist"
msgstr "Αφαίρεση από την λίστα"
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist/Year - Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Genre/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Genre/Artist/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Advanced grouping..."
msgstr ""
#. ts-context MultiLoadingIndicator
msgid "Form"
msgstr "Μορφή"

View File

@ -770,6 +770,41 @@ msgstr "Editar etiqueta..."
msgid "Remove from playlist"
msgstr "Eliminar de la lista de reproducción"
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist/Year - Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Genre/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Genre/Artist/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Advanced grouping..."
msgstr ""
#. ts-context MultiLoadingIndicator
msgid "Form"
msgstr ""

View File

@ -639,12 +639,12 @@ msgid "Tag"
msgstr "Tag"
#. ts-context Library
#: ../library.cpp:161
#: ../library.cpp:186
msgid "Various Artists"
msgstr "Compilations d'artistes"
#. ts-context Library
#: ../library.cpp:464 ../library.cpp:482
#: ../library.cpp:642
msgid "Unknown"
msgstr "Inconnu"
@ -751,8 +751,8 @@ msgid "Previous track"
msgstr "Piste précédente"
#. ts-context MainWindow
#: ../mainwindow.ui:517 ../mainwindow.cpp:244 ../mainwindow.cpp:379
#: ../mainwindow.cpp:395 ../mainwindow.cpp:576
#: ../mainwindow.ui:517 ../mainwindow.cpp:257 ../mainwindow.cpp:399
#: ../mainwindow.cpp:415 ../mainwindow.cpp:596
msgid "Play"
msgstr "Lecture"
@ -777,7 +777,7 @@ msgid "Ctrl+Q"
msgstr "Ctrl+Q"
#. ts-context MainWindow
#: ../mainwindow.ui:559 ../mainwindow.cpp:246
#: ../mainwindow.ui:559 ../mainwindow.cpp:259
msgid "Stop after this track"
msgstr "Arrêter la lecture après cette piste"
@ -877,7 +877,7 @@ msgid "Open media..."
msgstr "Ouvrir un media..."
#. ts-context MainWindow
#: ../mainwindow.ui:728 ../mainwindow.cpp:358
#: ../mainwindow.ui:728 ../mainwindow.cpp:378
msgid "&Hide tray icon"
msgstr "&Masquer l'icône"
@ -902,27 +902,69 @@ msgid "Remove from playlist"
msgstr "Supprimer de la liste de lecture"
#. ts-context MainWindow
#: ../mainwindow.cpp:240
#: ../mainwindow.ui:764
#, fuzzy
msgid "Group by Artist"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:772
#, fuzzy
msgid "Group by Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:780
#, fuzzy
msgid "Group by Artist/Year - Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:788
#, fuzzy
msgid "Group by Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:796
#, fuzzy
msgid "Group by Genre/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:804
#, fuzzy
msgid "Group by Genre/Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:812
#, fuzzy
msgid "Advanced grouping..."
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:253
msgid "Configure library..."
msgstr "Configurer votre bibliothèque..."
#. ts-context MainWindow
#: ../mainwindow.cpp:333 ../mainwindow.cpp:354
#: ../mainwindow.cpp:353 ../mainwindow.cpp:374
msgid "&Show tray icon"
msgstr "&Afficher l'icône"
#. ts-context MainWindow
#: ../mainwindow.cpp:406 ../mainwindow.cpp:573
#: ../mainwindow.cpp:426 ../mainwindow.cpp:593
msgid "Pause"
msgstr "Pause"
#. ts-context MainWindow
#: ../mainwindow.cpp:623
#: ../mainwindow.cpp:643
msgid "Set %1 to \"%2\"..."
msgstr "Définir %1 à la valeur \"%2\"..."
#. ts-context MainWindow
#: ../mainwindow.cpp:625
#: ../mainwindow.cpp:645
msgid "Edit tag \"%1\"..."
msgstr "Modifer le tag \"%1\"..."

View File

@ -839,6 +839,41 @@ msgstr ""
msgid "Remove from playlist"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Artist/Year - Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Genre/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Group by Genre/Artist/Album"
msgstr ""
#. ts-context MainWindow
#, fuzzy
msgid "Advanced grouping..."
msgstr ""
#. ts-context MultiLoadingIndicator
msgid "Form"
msgstr "Форма"

View File

@ -641,12 +641,12 @@ msgid "Tag"
msgstr ""
#. ts-context Library
#: ../library.cpp:161
#: ../library.cpp:186
msgid "Various Artists"
msgstr "Rôzni interpréti"
#. ts-context Library
#: ../library.cpp:464 ../library.cpp:482
#: ../library.cpp:642
msgid "Unknown"
msgstr "Neznámi"
@ -696,38 +696,38 @@ msgid "Click here to add some music"
msgstr "Kliknite sem aby ste pridali nejakú hudbu"
#. ts-context MainWindow
#: ../mainwindow.cpp:240
#: ../mainwindow.cpp:253
msgid "Configure library..."
msgstr "Nastaviť zbierku..."
#. ts-context MainWindow
#: ../mainwindow.ui:517 ../mainwindow.cpp:244 ../mainwindow.cpp:379
#: ../mainwindow.cpp:395 ../mainwindow.cpp:576
#: ../mainwindow.ui:517 ../mainwindow.cpp:257 ../mainwindow.cpp:399
#: ../mainwindow.cpp:415 ../mainwindow.cpp:596
msgid "Play"
msgstr "Hrať"
#. ts-context MainWindow
#: ../mainwindow.ui:559 ../mainwindow.cpp:246
#: ../mainwindow.ui:559 ../mainwindow.cpp:259
msgid "Stop after this track"
msgstr "Zastaviť po tejto skladbe"
#. ts-context MainWindow
#: ../mainwindow.cpp:333 ../mainwindow.cpp:354
#: ../mainwindow.cpp:353 ../mainwindow.cpp:374
msgid "&Show tray icon"
msgstr "&Zobraziť tray ikonu"
#. ts-context MainWindow
#: ../mainwindow.cpp:406 ../mainwindow.cpp:573
#: ../mainwindow.cpp:426 ../mainwindow.cpp:593
msgid "Pause"
msgstr "Pauza"
#. ts-context MainWindow
#: ../mainwindow.cpp:623
#: ../mainwindow.cpp:643
msgid "Set %1 to \"%2\"..."
msgstr "Nastaviť %1 do \"%2\"..."
#. ts-context MainWindow
#: ../mainwindow.cpp:625
#: ../mainwindow.cpp:645
msgid "Edit tag \"%1\"..."
msgstr "Upraviť tag \"%1\"..."
@ -907,7 +907,49 @@ msgid "Remove from playlist"
msgstr "Odstrániť z playlistu"
#. ts-context MainWindow
#: ../mainwindow.ui:728 ../mainwindow.cpp:358
#: ../mainwindow.ui:764
#, fuzzy
msgid "Group by Artist"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:772
#, fuzzy
msgid "Group by Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:780
#, fuzzy
msgid "Group by Artist/Year - Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:788
#, fuzzy
msgid "Group by Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:796
#, fuzzy
msgid "Group by Genre/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:804
#, fuzzy
msgid "Group by Genre/Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:812
#, fuzzy
msgid "Advanced grouping..."
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:728 ../mainwindow.cpp:378
msgid "&Hide tray icon"
msgstr "&Skryť tray ikonu"

View File

@ -700,13 +700,13 @@ msgid "Tag"
msgstr ""
#. ts-context Library
#: ../library.cpp:161
#: ../library.cpp:186
#, fuzzy
msgid "Various Artists"
msgstr ""
#. ts-context Library
#: ../library.cpp:464 ../library.cpp:482
#: ../library.cpp:642
#, fuzzy
msgid "Unknown"
msgstr ""
@ -832,8 +832,8 @@ msgid "Previous track"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:517 ../mainwindow.cpp:244 ../mainwindow.cpp:379
#: ../mainwindow.cpp:395 ../mainwindow.cpp:576
#: ../mainwindow.ui:517 ../mainwindow.cpp:257 ../mainwindow.cpp:399
#: ../mainwindow.cpp:415 ../mainwindow.cpp:596
#, fuzzy
msgid "Play"
msgstr ""
@ -863,7 +863,7 @@ msgid "Ctrl+Q"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:559 ../mainwindow.cpp:246
#: ../mainwindow.ui:559 ../mainwindow.cpp:259
#, fuzzy
msgid "Stop after this track"
msgstr ""
@ -989,7 +989,49 @@ msgid "Remove from playlist"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:728 ../mainwindow.cpp:358
#: ../mainwindow.ui:764
#, fuzzy
msgid "Group by Artist"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:772
#, fuzzy
msgid "Group by Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:780
#, fuzzy
msgid "Group by Artist/Year - Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:788
#, fuzzy
msgid "Group by Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:796
#, fuzzy
msgid "Group by Genre/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:804
#, fuzzy
msgid "Group by Genre/Artist/Album"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:812
#, fuzzy
msgid "Advanced grouping..."
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.ui:728 ../mainwindow.cpp:378
#, fuzzy
msgid "&Hide tray icon"
msgstr ""
@ -1013,31 +1055,31 @@ msgid "Repeat mode"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:240
#: ../mainwindow.cpp:253
#, fuzzy
msgid "Configure library..."
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:333 ../mainwindow.cpp:354
#: ../mainwindow.cpp:353 ../mainwindow.cpp:374
#, fuzzy
msgid "&Show tray icon"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:406 ../mainwindow.cpp:573
#: ../mainwindow.cpp:426 ../mainwindow.cpp:593
#, fuzzy
msgid "Pause"
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:623
#: ../mainwindow.cpp:643
#, fuzzy
msgid "Set %1 to \"%2\"..."
msgstr ""
#. ts-context MainWindow
#: ../mainwindow.cpp:625
#: ../mainwindow.cpp:645
#, fuzzy
msgid "Edit tag \"%1\"..."
msgstr ""

View File

@ -87,7 +87,7 @@ add_test_file(song_test.cpp false)
add_test_file(librarybackend_test.cpp false)
add_test_file(albumcoverfetcher_test.cpp false)
add_test_file(xspfparser_test.cpp false)
add_test_file(library_test.cpp false)
#add_test_file(library_test.cpp false)
add_test_file(albumcovermanager_test.cpp true)
add_test_file(songplaylistitem_test.cpp false)
add_test_file(translations_test.cpp false)