Warning, file /multimedia/amarok/src/playlistgenerator/PresetModel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /**************************************************************************************** 0002 * Copyright (c) 2008-2012 Soren Harward <stharward@gmail.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "APG::PresetModel" 0018 0019 #include "PresetModel.h" 0020 0021 #include "amarokconfig.h" 0022 #include "core/logger/Logger.h" 0023 #include "core/collections/Collection.h" 0024 #include "core/support/Amarok.h" 0025 #include "core/support/Components.h" 0026 #include "core/support/Debug.h" 0027 #include "core-impl/collections/support/CollectionManager.h" 0028 #include "playlistgenerator/Preset.h" 0029 #include "playlistgenerator/PresetEditDialog.h" 0030 0031 #include <QAbstractItemModel> 0032 #include <QDesktopServices> 0033 #include <QDialog> 0034 #include <QDomDocument> 0035 #include <QDomElement> 0036 #include <QFile> 0037 #include <QList> 0038 #include <QUrl> 0039 #include <QVariant> 0040 0041 APG::PresetModel* APG::PresetModel::s_instance = nullptr; 0042 0043 APG::PresetModel* APG::PresetModel::instance() 0044 { 0045 if ( s_instance == nullptr ) { 0046 s_instance = new PresetModel(); 0047 } 0048 0049 return s_instance; 0050 } 0051 0052 void 0053 APG::PresetModel::destroy() 0054 { 0055 s_instance->savePresetsToXml( Amarok::saveLocation() + "playlistgenerator.xml", s_instance->m_presetList ); 0056 delete s_instance; 0057 s_instance = nullptr; 0058 } 0059 0060 APG::PresetModel::PresetModel() 0061 : QAbstractListModel() 0062 , m_activePresetIndex( nullptr ) 0063 { 0064 loadPresetsFromXml( Amarok::saveLocation() + "playlistgenerator.xml", true ); 0065 } 0066 0067 APG::PresetModel::~PresetModel() 0068 { 0069 while ( m_presetList.size() > 0 ) { 0070 m_presetList.takeFirst()->deleteLater(); 0071 } 0072 } 0073 0074 QVariant 0075 APG::PresetModel::data( const QModelIndex& idx, int role ) const 0076 { 0077 if ( !idx.isValid() ) 0078 return QVariant(); 0079 0080 if ( idx.row() >= m_presetList.size() ) 0081 return QVariant(); 0082 0083 APG::PresetPtr item = m_presetList.at( idx.row() ); 0084 0085 switch ( role ) { 0086 case Qt::DisplayRole: 0087 case Qt::EditRole: 0088 return item->title(); 0089 break; 0090 default: 0091 return QVariant(); 0092 } 0093 0094 return QVariant(); 0095 } 0096 0097 QModelIndex 0098 APG::PresetModel::index( int row, int column, const QModelIndex& ) const 0099 { 0100 if ( rowCount() <= row ) 0101 return QModelIndex(); 0102 0103 return createIndex( row, column); 0104 } 0105 0106 int 0107 APG::PresetModel::rowCount( const QModelIndex& ) const 0108 { 0109 return m_presetList.size(); 0110 } 0111 0112 APG::PresetPtr 0113 APG::PresetModel::activePreset() const 0114 { 0115 if ( m_activePresetIndex && m_activePresetIndex->isValid() ) 0116 return m_presetList.at( m_activePresetIndex->row() ); 0117 else 0118 return APG::PresetPtr(); 0119 } 0120 0121 void 0122 APG::PresetModel::addNew() 0123 { 0124 insertPreset( APG::Preset::createNew() ); 0125 } 0126 0127 void 0128 APG::PresetModel::edit() 0129 { 0130 editPreset( createIndex( m_activePresetIndex->row(), 0 ) ); 0131 } 0132 0133 void 0134 APG::PresetModel::editPreset( const QModelIndex& index ) 0135 { 0136 // TODO: possible enhancement: instead of using a modal dialog, use a QMap that allows 0137 // only one dialog per preset to be open at once 0138 PresetPtr ps = m_presetList.at( index.row() ); 0139 QDialog* d = new PresetEditDialog( ps ); 0140 d->exec(); 0141 } 0142 0143 void 0144 APG::PresetModel::exportActive() 0145 { 0146 auto d = new ExportDialog( activePreset() ); 0147 connect( d, &ExportDialog::pleaseExport, this, &PresetModel::savePresetsToXml ); 0148 d->exec(); 0149 } 0150 0151 void 0152 APG::PresetModel::import() 0153 { 0154 const QString filename = QFileDialog::getOpenFileName( nullptr, i18n("Import preset"), 0155 QStandardPaths::writableLocation( QStandardPaths::MusicLocation ), 0156 QStringLiteral("%1 (*.xml)").arg(i18n("Preset files") )); 0157 if( !filename.isEmpty() ) 0158 loadPresetsFromXml( filename ); 0159 } 0160 0161 void 0162 APG::PresetModel::removeActive() 0163 { 0164 if ( m_presetList.size() < 1 ) 0165 return; 0166 0167 if ( m_activePresetIndex && m_activePresetIndex->isValid() ) { 0168 int row = m_activePresetIndex->row(); 0169 beginRemoveRows( QModelIndex(), row, row ); 0170 APG::PresetPtr p = m_presetList.takeAt( row ); 0171 p->deleteLater(); 0172 endRemoveRows(); 0173 } 0174 } 0175 0176 void 0177 APG::PresetModel::runGenerator( int q ) 0178 { 0179 activePreset()->generate( q ); 0180 } 0181 0182 void 0183 APG::PresetModel::setActivePreset( const QModelIndex& index ) 0184 { 0185 if ( m_activePresetIndex ) 0186 delete m_activePresetIndex; 0187 m_activePresetIndex = new QPersistentModelIndex( index ); 0188 } 0189 0190 void 0191 APG::PresetModel::savePresetsToXmlDefault() const 0192 { 0193 savePresetsToXml( Amarok::saveLocation() + "playlistgenerator.xml", m_presetList ); 0194 } 0195 0196 void 0197 APG::PresetModel::savePresetsToXml( const QString& filename, const QList<APG::PresetPtr> &pl ) const 0198 { 0199 QDomDocument xmldoc; 0200 QDomElement base = xmldoc.createElement( QStringLiteral("playlistgenerator") ); 0201 QList<QDomNode*> nodes; 0202 foreach ( APG::PresetPtr ps, pl ) { 0203 QDomElement* elemPtr = ps->toXml( xmldoc ); 0204 base.appendChild( (*elemPtr) ); 0205 nodes << elemPtr; 0206 } 0207 0208 xmldoc.appendChild( base ); 0209 QFile file( filename ); 0210 if ( file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { 0211 QTextStream out( &file ); 0212 out.setCodec( "UTF-8" ); 0213 xmldoc.save( out, 2, QDomNode::EncodingFromTextStream ); 0214 if( !filename.contains( QLatin1String("playlistgenerator.xml") ) ) 0215 { 0216 Amarok::Logger::longMessage( i18n("Preset exported to %1", filename), 0217 Amarok::Logger::Information ); 0218 } 0219 } 0220 else 0221 { 0222 Amarok::Logger::longMessage( 0223 i18n("Preset could not be exported to %1", filename), Amarok::Logger::Error ); 0224 error() << "Can not write presets to " << filename; 0225 } 0226 qDeleteAll( nodes ); 0227 } 0228 0229 void 0230 APG::PresetModel::loadPresetsFromXml( const QString& filename, bool createDefaults ) 0231 { 0232 QFile file( filename ); 0233 if ( file.open( QIODevice::ReadOnly ) ) { 0234 QDomDocument document; 0235 if ( document.setContent( &file ) ) { 0236 debug() << "Reading presets from" << filename; 0237 parseXmlToPresets( document ); 0238 } else { 0239 error() << "Failed to read" << filename; 0240 Amarok::Logger::longMessage( 0241 i18n("Presets could not be imported from %1", filename), 0242 Amarok::Logger::Error ); 0243 } 0244 file.close(); 0245 } else { 0246 if ( !createDefaults ) { 0247 Amarok::Logger::longMessage( 0248 i18n("%1 could not be opened for preset import", filename), 0249 Amarok::Logger::Error ); 0250 } else { 0251 QDomDocument document; 0252 QString translatedPresetExamples( presetExamples.arg( 0253 i18n("Example 1: new tracks added this week"), 0254 i18n("Example 2: rock or pop music"), 0255 i18n("Example 3: about one hour of tracks from different artists"), 0256 i18n("Example 4: like my favorite radio station"), 0257 i18n("Example 5: an 80-minute CD of rock, metal, and industrial") ) ); 0258 document.setContent( translatedPresetExamples ); 0259 debug() << "Reading built-in example presets"; 0260 parseXmlToPresets( document ); 0261 } 0262 error() << "Can not open" << filename; 0263 } 0264 } 0265 0266 void 0267 APG::PresetModel::insertPreset( const APG::PresetPtr &ps ) 0268 { 0269 if ( ps ) { 0270 int row = m_presetList.size(); 0271 beginInsertRows( QModelIndex(), row, row ); 0272 m_presetList.append( ps ); 0273 endInsertRows(); 0274 connect( ps.data(), &APG::Preset::lock, this, &PresetModel::lock ); 0275 } 0276 } 0277 0278 void 0279 APG::PresetModel::parseXmlToPresets( QDomDocument& document ) 0280 { 0281 QDomElement rootelem = document.documentElement(); 0282 for ( int i = 0; i < rootelem.childNodes().count(); i++ ) { 0283 QDomElement e = rootelem.childNodes().at( i ).toElement(); 0284 if ( e.tagName() == QLatin1String("generatorpreset") ) { 0285 debug() << "creating a new generator preset"; 0286 insertPreset( APG::Preset::createFromXml( e ) ); 0287 } else { 0288 debug() << "Don't know what to do with tag: " << e.tagName(); 0289 } 0290 } 0291 } 0292 0293 /* 0294 * ExportDialog nested class 0295 */ 0296 APG::PresetModel::ExportDialog::ExportDialog( APG::PresetPtr ps ) 0297 : QFileDialog( nullptr, i18n( "Export \"%1\" preset", ps->title() ), 0298 QStandardPaths::writableLocation( QStandardPaths::MusicLocation ), 0299 i18n("Preset files (*.xml)") ) 0300 { 0301 m_presetsToExportList.append( ps ); 0302 setFileMode( QFileDialog::AnyFile ); 0303 selectFile( ps->title() + ".xml" ); 0304 setAcceptMode( QFileDialog::AcceptSave ); 0305 connect( this, &ExportDialog::accepted, this, &ExportDialog::recvAccept ); 0306 } 0307 0308 APG::PresetModel::ExportDialog::~ExportDialog() {} 0309 0310 void 0311 APG::PresetModel::ExportDialog::recvAccept() const 0312 { 0313 Q_EMIT pleaseExport( selectedFiles().first(), m_presetsToExportList ); 0314 } 0315 0316 const QString APG::PresetModel::presetExamples = 0317 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 0318 "<playlistgenerator>" 0319 " <generatorpreset title=\"%1\">" 0320 " <constrainttree>" 0321 " <group matchtype=\"all\">" 0322 " <constraint field=\"create date\" comparison=\"3\" invert=\"false\" type=\"TagMatch\" value=\"7 days\" strictness=\"0.8\"/>" 0323 " <constraint field=\"play count\" comparison=\"1\" invert=\"false\" type=\"TagMatch\" value=\"\" strictness=\"1\"/>" 0324 " </group>" 0325 " </constrainttree>" 0326 " </generatorpreset>" 0327 " <generatorpreset title=\"%2\">" 0328 " <constrainttree>" 0329 " <group matchtype=\"any\">" 0330 " <constraint field=\"genre\" comparison=\"3\" invert=\"false\" type=\"TagMatch\" value=\"Rock\" strictness=\"1\"/>" 0331 " <constraint field=\"genre\" comparison=\"3\" invert=\"false\" type=\"TagMatch\" value=\"Pop\" strictness=\"1\"/>" 0332 " </group>" 0333 " </constrainttree>" 0334 " </generatorpreset>" 0335 " <generatorpreset title=\"%3\">" 0336 " <constrainttree>" 0337 " <group matchtype=\"all\">" 0338 " <constraint comparison=\"1\" duration=\"3600000\" type=\"PlaylistDuration\" strictness=\"0.3\"/>" 0339 " <constraint field=\"2\" type=\"PreventDuplicates\"/>" 0340 " </group>" 0341 " </constrainttree>" 0342 " </generatorpreset>" 0343 " <generatorpreset title=\"%4\">" 0344 " <constrainttree>" 0345 " <group matchtype=\"all\">" 0346 " <constraint field=\"0\" type=\"PreventDuplicates\"/>" 0347 " <constraint field=\"last played\" comparison=\"3\" invert=\"true\" type=\"TagMatch\" value=\"7 days\" strictness=\"0.4\"/>" 0348 " <constraint field=\"rating\" comparison=\"2\" invert=\"false\" type=\"TagMatch\" value=\"6\" strictness=\"1\"/>" 0349 " <constraint comparison=\"1\" duration=\"10800000\" type=\"PlaylistDuration\" strictness=\"0.3\"/>" 0350 " </group>" 0351 " </constrainttree>" 0352 " </generatorpreset>" 0353 " <generatorpreset title=\"%5\">" 0354 " <constrainttree>" 0355 " <group matchtype=\"all\">" 0356 " <group matchtype=\"any\">" 0357 " <constraint field=\"genre\" comparison=\"3\" invert=\"false\" type=\"TagMatch\" value=\"Rock\" strictness=\"1\"/>" 0358 " <constraint field=\"genre\" comparison=\"3\" invert=\"false\" type=\"TagMatch\" value=\"Metal\" strictness=\"1\"/>" 0359 " <constraint field=\"genre\" comparison=\"3\" invert=\"false\" type=\"TagMatch\" value=\"Industrial\" strictness=\"1\"/>" 0360 " </group>" 0361 " <group matchtype=\"all\">" 0362 " <constraint comparison=\"2\" duration=\"4500000\" type=\"PlaylistDuration\" strictness=\"0.4\"/>" 0363 " <constraint comparison=\"0\" duration=\"4800000\" type=\"PlaylistDuration\" strictness=\"1\"/>" 0364 " </group>" 0365 " </group>" 0366 " </constrainttree>" 0367 " </generatorpreset>" 0368 "</playlistgenerator>";