/* * Cantata * * Copyright (c) 2011-2021 Craig Drummond * * ---- * * 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 "dynamicplaylists.h" #include "config.h" #include "support/utils.h" #include "support/monoicon.h" #include "widgets/icons.h" #include "models/roles.h" #include "gui/settings.h" #include #include #include #include #include #include #include #include #include #include const QString RulesPlaylists::constExtension=QLatin1String(".rules"); const QString RulesPlaylists::constRuleKey=QLatin1String("Rule"); const QString RulesPlaylists::constArtistKey=QLatin1String("Artist"); const QString RulesPlaylists::constSimilarArtistsKey=QLatin1String("SimilarArtists"); const QString RulesPlaylists::constAlbumArtistKey=QLatin1String("AlbumArtist"); const QString RulesPlaylists::constComposerKey=QLatin1String("Composer"); const QString RulesPlaylists::constCommentKey=QLatin1String("Comment"); const QString RulesPlaylists::constAlbumKey=QLatin1String("Album"); const QString RulesPlaylists::constTitleKey=QLatin1String("Title"); const QString RulesPlaylists::constGenreKey=QLatin1String("Genre"); const QString RulesPlaylists::constDateKey=QLatin1String("Date"); const QString RulesPlaylists::constRatingKey=QLatin1String("Rating"); const QString RulesPlaylists::constIncludeUnratedKey=QLatin1String("IncludeUnrated"); const QString RulesPlaylists::constDurationKey=QLatin1String("Duration"); const QString RulesPlaylists::constNumTracksKey=QLatin1String("NumTracks"); const QString RulesPlaylists::constMaxAgeKey=QLatin1String("MaxAge"); const QString RulesPlaylists::constFileKey=QLatin1String("File"); const QString RulesPlaylists::constExactKey=QLatin1String("Exact"); const QString RulesPlaylists::constExcludeKey=QLatin1String("Exclude"); const QString RulesPlaylists::constOrderKey=QLatin1String("Order"); const QString RulesPlaylists::constOrderAscendingKey=QLatin1String("OrderAscending"); const QChar RulesPlaylists::constRangeSep=QLatin1Char('-'); const QChar RulesPlaylists::constKeyValSep=QLatin1Char(':'); RulesPlaylists::Order RulesPlaylists::toOrder(const QString &str) { for (int i=0; i=entryList.count()) { return QModelIndex(); } return createIndex(row, column); } QVariant RulesPlaylists::data(const QModelIndex &index, int role) const { if (!index.isValid()) { switch (role) { case Cantata::Role_TitleText: return title(); case Cantata::Role_SubText: return descr(); case Qt::DecorationRole: return icon(); } return QVariant(); } if (index.parent().isValid() || index.row()>=entryList.count()) { return QVariant(); } switch (role) { case Qt::ToolTipRole: if (!Settings::self()->infoTooltips()) { return QVariant(); } case Qt::DisplayRole: return entryList.at(index.row()).name; case Cantata::Role_SubText: { const Entry &e=entryList.at(index.row()); return tr("%n Rule(s)", "", e.rules.count())+(e.haveRating() ? tr(", Rating: %1..%2") .arg((double)e.ratingFrom/Song::Rating_Step).arg((double)e.ratingTo/Song::Rating_Step) : QString()) + (isDynamic() ? QString() : (QLatin1String(", ") + orderName(e.order))) + (isDynamic() || Order_Random==e.order ? QString() : (" ("+(e.orderAscending ? tr("Ascending") : tr("Descending"))+")")); } default: return QVariant(); } } Qt::ItemFlags RulesPlaylists::flags(const QModelIndex &index) const { if (index.isValid()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return Qt::NoItemFlags; } RulesPlaylists::Entry RulesPlaylists::entry(const QString &e) { if (!e.isEmpty()) { QList::Iterator it=find(e); if (it!=entryList.end()) { return *it; } } Entry def; def.numTracks=defaultNumTracks(); return def; } bool RulesPlaylists::save(const Entry &e) { if (e.name.isEmpty()) { return false; } QString string; QTextStream str(&string); if (e.numTracks >= minTracks() && e.numTracks <= maxTracks()) { str << constNumTracksKey << constKeyValSep << e.numTracks << '\n'; } if (0!=e.ratingFrom || 0!=e.ratingTo) { str << constRatingKey << constKeyValSep << e.ratingFrom << constRangeSep << e.ratingTo << '\n'; str << constIncludeUnratedKey << constKeyValSep << (e.includeUnrated ? "true" : "false") << '\n'; } if (0!=e.minDuration || 0!=e.maxDuration) { str << constDurationKey << constKeyValSep << e.minDuration << constRangeSep << e.maxDuration << '\n'; } if (0!=e.maxAge) { str << constMaxAgeKey << constKeyValSep << e.maxAge << '\n'; } if (Order_Random!=e.order) { str << constOrderKey << constKeyValSep << orderStr(e.order) << '\n' << constOrderAscendingKey << constKeyValSep << (e.orderAscending ? "true" : "false") << '\n'; } for (const Rule &rule: e.rules) { if (!rule.isEmpty()) { str << constRuleKey << '\n'; Rule::ConstIterator it(rule.constBegin()); Rule::ConstIterator end(rule.constEnd()); for (; it!=end; ++it) { str << it.key() << constKeyValSep << it.value() << '\n'; } } } if (isRemote()) { return saveRemote(string, e); } QFile f(Utils::dataDir(rulesDir, true)+e.name+constExtension); if (f.open(QIODevice::WriteOnly|QIODevice::Text)) { QTextStream out(&f); out.setCodec("UTF-8"); out << string; updateEntry(e); return true; } return false; } void RulesPlaylists::updateEntry(const Entry &e) { QList::Iterator it=find(e.name); if (it!=entryList.end()) { entryList.replace(it-entryList.begin(), e); QModelIndex idx=index(it-entryList.begin(), 0, QModelIndex()); emit dataChanged(idx, idx); } else { beginInsertRows(QModelIndex(), entryList.count(), entryList.count()); entryList.append(e); endInsertRows(); } } void RulesPlaylists::del(const QString &name) { QList::Iterator it=find(name); if (it==entryList.end()) { return; } QString fName(Utils::dataDir(rulesDir, false)+name+constExtension); bool isCurrent=currentEntry==name; if (!QFile::exists(fName) || QFile::remove(fName)) { if (isCurrent) { stop(); } beginRemoveRows(QModelIndex(), it-entryList.begin(), it-entryList.begin()); entryList.erase(it); endRemoveRows(); return; } } QList::Iterator RulesPlaylists::find(const QString &e) { QList::Iterator it(entryList.begin()); QList::Iterator end(entryList.end()); for (; it!=end; ++it) { if ((*it).name==e) { break; } } return it; } void RulesPlaylists::loadLocal() { beginResetModel(); entryList.clear(); currentEntry=QString(); // Load all current enttries... QString dirName=Utils::dataDir(rulesDir); QDir d(dirName); if (d.exists()) { QStringList rulesFiles=d.entryList(QStringList() << QChar('*')+constExtension); for (const QString &rf: rulesFiles) { QFile f(dirName+rf); if (f.open(QIODevice::ReadOnly|QIODevice::Text)) { QStringList keys=QStringList() << constArtistKey << constSimilarArtistsKey << constAlbumArtistKey << constDateKey << constExactKey << constAlbumKey << constTitleKey << constGenreKey << constFileKey << constExcludeKey; Entry e; e.name=rf.left(rf.length()-constExtension.length()); e.numTracks=defaultNumTracks(); Rule r; QTextStream in(&f); in.setCodec("UTF-8"); QStringList lines = in.readAll().split('\n', CANTATA_SKIP_EMPTY); for (const QString &line: lines) { QString str=line.trimmed(); if (str.isEmpty() || str.startsWith('#')) { continue; } if (str==constRuleKey) { if (!r.isEmpty()) { e.rules.append(r); r.clear(); } } else if (str.startsWith(constRatingKey+constKeyValSep)) { QStringList vals=str.mid(constRatingKey.length()+1).split(constRangeSep); if (2==vals.count()) { e.ratingFrom=vals.at(0).toUInt(); e.ratingTo=vals.at(1).toUInt(); } } else if (str.startsWith(constIncludeUnratedKey+constKeyValSep)) { e.includeUnrated="true"==str.mid(constIncludeUnratedKey.length()+1); } else if (str.startsWith(constDurationKey+constKeyValSep)) { QStringList vals=str.mid(constDurationKey.length()+1).split(constRangeSep); if (2==vals.count()) { e.minDuration=vals.at(0).toUInt(); e.maxDuration=vals.at(1).toUInt(); } } else if (str.startsWith(constOrderKey+constKeyValSep)) { e.order=toOrder(str.mid(constOrderKey.length()+1)); } else if (str.startsWith(constOrderAscendingKey+constKeyValSep)) { e.orderAscending="true"==str.mid(constOrderAscendingKey.length()+1); } else if (str.startsWith(constNumTracksKey+constKeyValSep)) { e.numTracks=str.mid(constNumTracksKey.length()+1).toUInt(); } else if (str.startsWith(constMaxAgeKey+constKeyValSep)) { e.maxAge=str.mid(constMaxAgeKey.length()+1).toUInt(); } else { for (const QString &k: keys) { if (str.startsWith(k+constKeyValSep)) { r.insert(k, str.mid(k.length()+1)); } } } } if (!r.isEmpty()) { e.rules.append(r); r.clear(); } entryList.append(e); } } } endResetModel(); } #include "moc_rulesplaylists.cpp"