1 /**
2 * projectM-qt -- Qt4 based projectM GUI
3 * Copyright (C)2003-2004 projectM Team
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * See 'LICENSE.txt' included within this release
19 *
20 */
21
22 #include <QIcon>
23 #include <QXmlStreamReader>
24 #include <QtDebug>
25 #include <QFileInfo>
26 #include <QDir>
27 #include <QMessageBox>
28
29 #include "qxmlplaylisthandler.hpp"
30 #include "qplaylistmodel.hpp"
31 #include <QMimeData>
32
33 QString QPlaylistModel::PRESET_MIME_TYPE("text/x-projectM-preset");
34
35 class XmlReadFunctor {
36 public:
XmlReadFunctor(QPlaylistModel & model)37 XmlReadFunctor(QPlaylistModel & model) : m_model(model) {}
38
setPlaylistDesc(const QString & desc)39 inline void setPlaylistDesc(const QString & desc) {
40 m_model.setPlaylistDesc(desc);
41 }
42
appendItem(const QString & url,const QString & name,int rating,int breedability)43 inline void appendItem(const QString & url, const QString & name, int rating, int breedability) {
44 m_model.appendRow(url, name, rating, breedability);
45 }
46
47
48 private:
49 QPlaylistModel & m_model;
50
51 };
52
53
54 class XmlWriteFunctor {
55 public:
XmlWriteFunctor(QPlaylistModel & model)56 XmlWriteFunctor(QPlaylistModel & model) : m_model(model), m_index(0) {}
57
58
playlistDesc() const59 inline const QString & playlistDesc() const {
60 return m_model.playlistDesc();
61 }
62
nextItem(QString & name,QString & url,int & rating,int & breedability)63 inline bool nextItem(QString & name, QString & url, int & rating, int & breedability) {
64 Q_UNUSED(name);
65
66 if (m_index >= m_model.rowCount())
67 return false;
68
69 QModelIndex modelIndex = m_model.index(m_index, 0);
70 url = m_model.data(modelIndex, QPlaylistModel::URLInfoRole).toString();
71 rating = m_model.data(modelIndex, QPlaylistModel::RatingRole).toInt();
72
73 breedability = m_model.data(modelIndex, QPlaylistModel::BreedabilityRole).toInt();
74
75 m_index++;
76 return true;
77 }
78 private:
79 QPlaylistModel & m_model;
80 int m_index;
81
82 };
83
softCutRatingsEnabled() const84 bool QPlaylistModel::softCutRatingsEnabled() const {
85 return m_projectM.settings().softCutRatingsEnabled;
86 }
87
QPlaylistModel(projectM & _projectM,QObject * parent)88 QPlaylistModel::QPlaylistModel ( projectM & _projectM, QObject * parent ) :
89 QAbstractTableModel ( parent ), m_projectM ( _projectM )
90 {
91
92 }
93
94
updateItemHighlights()95 void QPlaylistModel::updateItemHighlights()
96 {
97 if ( rowCount() == 0 )
98 return;
99
100 emit ( dataChanged ( this->index ( 0,0 ), this->index ( rowCount()-1,columnCount()-1 ) ) );
101 }
102
setData(const QModelIndex & index,const QVariant & value,int role)103 bool QPlaylistModel::setData ( const QModelIndex & index, const QVariant & value, int role )
104 {
105 if ( role == QPlaylistModel::RatingRole )
106 {
107 m_projectM.changePresetRating(index.row(), value.toInt(), HARD_CUT_RATING_TYPE);
108 return true;
109 } else if (role == QPlaylistModel::BreedabilityRole) {
110 m_projectM.changePresetRating(index.row(), value.toInt(), SOFT_CUT_RATING_TYPE);
111 return true;
112 } else if (role == QPlaylistModel::NameRole) {
113 m_projectM.changePresetName(index.row(), value.toString().toStdString());
114 return true;
115 }
116 else
117 return QAbstractTableModel::setData ( index, value, role );
118
119 }
120
flags(const QModelIndex & index) const121 Qt::ItemFlags QPlaylistModel::flags(const QModelIndex &index) const
122 {
123 Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
124
125 if (index.isValid())
126 return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
127 else
128 return Qt::ItemIsDropEnabled | defaultFlags;
129 }
130
131
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)132 bool QPlaylistModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
133 int row, int column, const QModelIndex &parent)
134 {
135 Q_UNUSED(row);
136 Q_UNUSED(parent);
137 if (!data->hasFormat(PRESET_MIME_TYPE))
138 return false;
139
140 if (action == Qt::IgnoreAction)
141 return true;
142
143 if (column > 0)
144 return false;
145
146 return true;
147 }
148
ratingToIcon(int rating) const149 QVariant QPlaylistModel::ratingToIcon ( int rating ) const
150 {
151 switch ( rating )
152 {
153 case 1:
154 return QVariant ();
155 case 2:
156 return QVariant ( QIcon ( ":/images/icons/rating-1.png" ) );
157 case 3:
158 return QVariant ( QIcon ( ":/images/icons/rating-2.png" ) );
159 case 4:
160 return QVariant ( QIcon ( ":/images/icons/rating-3.png" ) );
161 case 5:
162 return QVariant ( QIcon ( ":/images/icons/rating-4.png" ) );
163 case 6:
164 return QVariant ( QIcon ( ":/images/icons/rating-5.png" ) );
165 default:
166 if (rating <= 0)
167 return QVariant ();
168 else
169 return QVariant ( QIcon ( ":/images/icons/rating-5.png" ) );
170 }
171 }
172
173
breedabilityToIcon(int rating) const174 QVariant QPlaylistModel::breedabilityToIcon( int rating ) const
175 {
176 switch ( rating )
177 {
178 case 1:
179 return QVariant ();
180 case 2:
181 return QVariant ( QIcon ( ":/images/icons/rating-1.png" ) );
182 case 3:
183 return QVariant ( QIcon ( ":/images/icons/rating-2.png" ) );
184 case 4:
185 return QVariant ( QIcon ( ":/images/icons/rating-3.png" ) );
186 case 5:
187 return QVariant ( QIcon ( ":/images/icons/rating-4.png" ) );
188 case 6:
189 return QVariant ( QIcon ( ":/images/icons/rating-5.png" ) );
190 default:
191 if (rating <= 0)
192 return QVariant ();
193 else
194 return QVariant ( QIcon ( ":/images/icons/rating-5.png" ) );
195 }
196 }
197
getSillyRatingToolTip(int rating)198 QString QPlaylistModel::getSillyRatingToolTip(int rating) {
199
200 switch (rating) {
201 case 1:
202 return QString("Rather watch grass grow than watch this");
203 case 2:
204 return QString("A very poor preset");
205 case 3:
206 return QString("Tolerable");
207 case 4:
208 return QString("Pretty good");
209 case 5:
210 return QString("Trippy eye candy");
211 case 6:
212 return QString("Crafted by a psychotic deity");
213 default:
214 if (rating <= 0 )
215 return QString("So bad it literally makes other presets bad!");
216 else
217 return QString("Better than projectM itself!");
218 }
219 }
220
221
getBreedabilityToolTip(int rating) const222 QString QPlaylistModel::getBreedabilityToolTip(int rating) const {
223
224 switch (rating) {
225 case 1:
226 return QString("Hidious.");
227 case 2:
228 return QString("Ugly.");
229 case 3:
230 return QString("Doable.");
231 case 4:
232 return QString("Hot.");
233 case 5:
234 return QString("Preset Magnet.");
235 case 6:
236 return QString("Preset Whore.");
237 default:
238 if (rating <= 0 )
239 return QString("Infertile.");
240 else
241 return QString("Diseased slut.");
242 }
243 }
244
data(const QModelIndex & index,int role=Qt::DisplayRole) const245 QVariant QPlaylistModel::data ( const QModelIndex & index, int role = Qt::DisplayRole ) const
246 {
247
248 if (!index.isValid())
249 return QVariant();
250
251 unsigned int pos;
252 const int rowidx = index.row();
253
254 switch ( role )
255 {
256 case Qt::DisplayRole:
257 if ( index.column() == 0 )
258 return QVariant ( QString ( m_projectM.getPresetName ( rowidx ).c_str() ) );
259 else if (index.column() == 1)
260 return ratingToIcon ( m_projectM.getPresetRating(rowidx, HARD_CUT_RATING_TYPE) );
261 else
262 return ratingToIcon ( m_projectM.getPresetRating(rowidx, SOFT_CUT_RATING_TYPE) );
263 case Qt::ToolTipRole:
264 if ( index.column() == 0 )
265 return QVariant ( QString ( m_projectM.getPresetName ( rowidx ).c_str() ) );
266 else if (index.column() == 1)
267 return QString ( getSillyRatingToolTip(m_projectM.getPresetRating(rowidx, HARD_CUT_RATING_TYPE)));
268 else return getBreedabilityToolTip(m_projectM.getPresetRating(rowidx, SOFT_CUT_RATING_TYPE));
269 case Qt::DecorationRole:
270 if ( index.column() == 1 )
271 return ratingToIcon ( m_projectM.getPresetRating(rowidx, HARD_CUT_RATING_TYPE) );
272 else if (index.column() == 2)
273 return breedabilityToIcon ( m_projectM.getPresetRating(rowidx, SOFT_CUT_RATING_TYPE) );
274 else
275 return QVariant();
276 case QPlaylistModel::RatingRole:
277 return QVariant ( m_projectM.getPresetRating(rowidx, HARD_CUT_RATING_TYPE) );
278 case QPlaylistModel::BreedabilityRole:
279 return QVariant ( m_projectM.getPresetRating(rowidx, SOFT_CUT_RATING_TYPE) );
280 case Qt::BackgroundRole:
281
282 if (!m_projectM.selectedPresetIndex(pos))
283 return QVariant();
284 if (m_projectM.isPresetLocked() && rowidx >= 0 && static_cast<unsigned int>(rowidx) == pos )
285 return QColor(Qt::red);
286 if (!m_projectM.isPresetLocked() && rowidx >= 0 && static_cast<unsigned int>(rowidx) == pos )
287 return QColor(Qt::green);
288 return QVariant();
289 case QPlaylistModel::URLInfoRole:
290 return QVariant ( QString ( m_projectM.getPresetURL ( rowidx ).c_str() ) );
291 default:
292
293 return QVariant();
294 }
295 }
296
headerData(int section,Qt::Orientation orientation,int role) const297 QVariant QPlaylistModel::headerData ( int section, Qt::Orientation orientation, int role ) const
298 {
299
300 if ( orientation == Qt::Vertical )
301 return QAbstractTableModel::headerData ( section, orientation, role );
302 if ( ( section == 0 ) && ( role == Qt::DisplayRole ) )
303 return QString ( tr ( "Preset" ) );
304
305 /// @bug hack. this should be formalized like it is in libprojectM.
306 if ( ( section == 1 ) && ( role == Qt::DisplayRole )) {
307 if (columnCount() == 2)
308 return QString ( tr ( "Rating" ) );
309 else
310 return QString ( tr ( "Hard Rating" ) );
311 }
312 if ( ( section == 2 ) && ( role == Qt::DisplayRole ) )
313 return QString ( tr ( "Soft Rating" ) );
314
315 return QAbstractTableModel::headerData ( section, orientation, role );
316 }
317
rowCount(const QModelIndex & parent) const318 int QPlaylistModel::rowCount ( const QModelIndex & parent ) const
319 {
320 Q_UNUSED(parent);
321
322 return m_projectM.getPlaylistSize();
323 }
324
325
columnCount(const QModelIndex & parent) const326 int QPlaylistModel::columnCount ( const QModelIndex & parent ) const
327 {
328 Q_UNUSED(parent);
329
330 return softCutRatingsEnabled() ? 3 : 2;
331 }
332
appendRow(const QString & presetURL,const QString & presetName,int rating,int breedability)333 void QPlaylistModel::appendRow ( const QString & presetURL, const QString & presetName, int rating, int breedability )
334 {
335 RatingList ratings;
336 ratings.push_back(rating);
337 ratings.push_back(breedability);
338
339 beginInsertRows ( QModelIndex(), rowCount(), rowCount() );
340 m_projectM.addPresetURL ( presetURL.toStdString(), presetName.toStdString(), ratings);
341 endInsertRows();
342 }
343
insertRow(int index,const QString & presetURL,const QString & presetName,int rating,int breedability)344 void QPlaylistModel::insertRow (int index, const QString & presetURL, const QString & presetName, int rating, int breedability) {
345 RatingList ratings;
346 ratings.push_back(rating);
347 ratings.push_back(breedability);
348
349 beginInsertRows ( QModelIndex(), index, index);
350 m_projectM.insertPresetURL (index, presetURL.toStdString(), presetName.toStdString(), ratings);
351 endInsertRows();
352 }
353
removeRows(int row,int count,const QModelIndex & parent)354 bool QPlaylistModel::removeRows ( int row, int count, const QModelIndex & parent) {
355 Q_UNUSED(parent);
356
357 beginRemoveRows ( QModelIndex(), row, count);
358
359 for (int i = 0; i < count; i++) {
360 m_projectM.removePreset (row );
361 }
362 endRemoveRows();
363 return true;
364 }
365
removeRow(int index,const QModelIndex & parent)366 bool QPlaylistModel::removeRow ( int index, const QModelIndex & parent)
367 {
368 Q_UNUSED(parent);
369
370 beginRemoveRows ( QModelIndex(), index, index );
371 m_projectM.removePreset ( index );
372 endRemoveRows();
373 return true;
374 }
375
clear()376 void QPlaylistModel::clear()
377 {
378 clearItems();
379 m_playlistName = "";
380 m_playlistDesc = "";
381 }
382
clearItems()383 void QPlaylistModel::clearItems()
384 {
385 beginRemoveRows ( QModelIndex(), 0, rowCount()-1 );
386 m_projectM.clearPlaylist();
387 endRemoveRows();
388 }
389
390
writePlaylist(const QString & file)391 bool QPlaylistModel::writePlaylist ( const QString & file ) {
392
393 QFile qfile(file);
394
395 if (!qfile.open(QIODevice::WriteOnly)) {
396 QMessageBox::warning (0, "Playlist Save Error", QString("There was a problem trying to save the playlist \"%1\". You may not have permission to modify this file.").arg(file));
397 return false;
398 }
399
400 XmlWriteFunctor writeFunctor(*this);
401
402 QXmlPlaylistHandler::writePlaylist(&qfile, writeFunctor);
403 return true;
404 }
405
readPlaylist(const QString & file)406 bool QPlaylistModel::readPlaylist ( const QString & file )
407 {
408
409 if (QFileInfo(file).isDir()) {
410 if (!QDir(file).isReadable()) {
411 QMessageBox::warning (0, "Playlist Directory Error", QString(tr("There was a problem trying to open the playlist directory \"%1\". The directory doesn't exist or you may not have permission to open it. ")).arg(file));
412 return false;
413 }
414
415 foreach (QFileInfo info, QDir(file).entryInfoList()) {
416 if (info.fileName().toLower().endsWith(".prjm") || info.fileName().toLower().endsWith(".milk") || info.fileName().toLower().endsWith(".so"))
417 appendRow(info.absoluteFilePath(), info.fileName(), 3,3);
418 }
419 return true;
420 }
421
422 QFile qfile(file);
423 if (!qfile.open(QIODevice::ReadOnly)) {
424 QMessageBox::warning (0, "Playlist File Error", QString(tr("There was a problem trying to open the playlist \"%1\". The file may no longer exist or you may not have permission to read the file.")).arg(file));
425 return false;
426 }
427
428 XmlReadFunctor readFunctor(*this);
429
430 if (QXmlPlaylistHandler::readPlaylist(&qfile, readFunctor) != QXmlStreamReader::NoError) {
431 QMessageBox::warning ( 0, "Playlist Parse Error", QString(tr("There was a problem trying to parse the playlist \"%1\". Some of your playlist items may have loaded correctly, but don't expect miracles.")).arg(file));
432 }
433
434 return true;
435 }
436
notifyDataChanged(unsigned int ind)437 void QPlaylistModel::notifyDataChanged(unsigned int ind)
438 {
439
440
441 QModelIndex modelIndex = index (ind, 1);
442 emit ( dataChanged ( modelIndex, modelIndex));
443 }
444
445