1 #include <QFileInfo>
2 #include <QListWidgetItem>
3 #include <QApplication>
4 #include <QProgressDialog>
5 #include <QDir>
6 #include <QMenu>
7 #include <QSettings>
8 #include <QInputDialog>
9 #include <QLayout>
10 #include <QScreen>
11 #include <QRegularExpression>
12 #include <QImageReader>
13 #include <QtConcurrent>
14
15 #include "../ui_qt.h"
16 #include "playlistentrydialog.h"
17
18 #ifndef CXX_BUILD
19 extern "C" {
20 #endif
21
22 #ifdef HAVE_CONFIG_H
23 #include "../../../config.h"
24 #endif
25
26 #include <file/file_path.h>
27 #include <file/archive_file.h>
28 #include <lists/string_list.h>
29 #include <string/stdstring.h>
30
31 #ifdef HAVE_MENU
32 #include "../../../menu/menu_displaylist.h"
33 #endif
34
35 #include "../../../file_path_special.h"
36 #include "../../../playlist.h"
37 #include "../../../setting_list.h"
38 #include "../../../configuration.h"
39 #include "../../../core_info.h"
40 #include "../../../verbosity.h"
41
42 #ifndef CXX_BUILD
43 }
44 #endif
45
PlaylistModel(QObject * parent)46 PlaylistModel::PlaylistModel(QObject *parent)
47 : QAbstractListModel(parent)
48 {
49 m_imageFormats = QVector<QByteArray>::fromList(QImageReader::supportedImageFormats());
50 m_fileSanitizerRegex = QRegularExpression("[&*/:`<>?\\|]");
51 setThumbnailCacheLimit(500);
52 connect(this, &PlaylistModel::imageLoaded, this, &PlaylistModel::onImageLoaded);
53 }
54
rowCount(const QModelIndex &) const55 int PlaylistModel::rowCount(const QModelIndex & /* parent */) const
56 {
57 return m_contents.count();
58 }
59
columnCount(const QModelIndex &) const60 int PlaylistModel::columnCount(const QModelIndex & /* parent */) const
61 {
62 return 1;
63 }
64
data(const QModelIndex & index,int role) const65 QVariant PlaylistModel::data(const QModelIndex &index, int role) const
66 {
67 if (index.column() == 0)
68 {
69 if (!index.isValid())
70 return QVariant();
71
72 if (index.row() >= m_contents.size() || index.row() < 0)
73 return QVariant();
74
75 switch (role)
76 {
77 case Qt::DisplayRole:
78 case Qt::EditRole:
79 case Qt::ToolTipRole:
80 return m_contents.at(index.row())["label_noext"];
81 case HASH:
82 return QVariant::fromValue(m_contents.at(index.row()));
83 case THUMBNAIL:
84 {
85 QPixmap *cachedPreview = m_cache.object(getCurrentTypeThumbnailPath(index));
86 if (cachedPreview)
87 return *cachedPreview;
88 return QVariant();
89 }
90 }
91 }
92 return QVariant();
93 }
94
flags(const QModelIndex & index) const95 Qt::ItemFlags PlaylistModel::flags(const QModelIndex &index) const
96 {
97 if (!index.isValid())
98 return Qt::ItemIsEnabled;
99
100 return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
101 }
102
setData(const QModelIndex & index,const QVariant & value,int role)103 bool PlaylistModel::setData(const QModelIndex &index, const QVariant &value, int role)
104 {
105 if (index.isValid() && role == Qt::EditRole)
106 {
107 QHash<QString, QString> hash = m_contents.at(index.row());
108
109 hash["label"] = value.toString();
110 hash["label_noext"] = QFileInfo(value.toString()).completeBaseName();
111
112 m_contents.replace(index.row(), hash);
113 emit dataChanged(index, index, { role });
114 return true;
115 }
116 return false;
117 }
118
headerData(int section,Qt::Orientation orientation,int role) const119 QVariant PlaylistModel::headerData(int section,
120 Qt::Orientation orientation, int role) const
121 {
122 if (role != Qt::DisplayRole)
123 return QVariant();
124
125 if (orientation == Qt::Horizontal)
126 return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME);
127 return section + 1;
128 }
129
setThumbnailType(const ThumbnailType type)130 void PlaylistModel::setThumbnailType(const ThumbnailType type)
131 {
132 m_thumbnailType = type;
133 }
134
setThumbnailCacheLimit(int limit)135 void PlaylistModel::setThumbnailCacheLimit(int limit)
136 {
137 m_cache.setMaxCost(limit * 1024);
138 }
139
getThumbnailPath(const QModelIndex & index,QString type) const140 QString PlaylistModel::getThumbnailPath(const QModelIndex &index,
141 QString type) const
142 {
143 return getThumbnailPath(m_contents.at(index.row()), type);
144 }
145
getPlaylistThumbnailsDir(const QString playlistName) const146 QString PlaylistModel::getPlaylistThumbnailsDir(
147 const QString playlistName) const
148 {
149 settings_t *settings = config_get_ptr();
150 const char *path_dir_thumbnails = settings->paths.directory_thumbnails;
151 return QDir::cleanPath(QString(path_dir_thumbnails)) + "/" + playlistName;
152 }
153
isSupportedImage(const QString path) const154 bool PlaylistModel::isSupportedImage(const QString path) const
155 {
156 QByteArray extension;
157 QString extensionStr;
158 int lastIndex = path.lastIndexOf('.');
159
160 if (lastIndex >= 0)
161 {
162 extensionStr = path.mid(lastIndex + 1);
163
164 if (!extensionStr.isEmpty())
165 extension = extensionStr.toLower().toUtf8();
166 }
167
168 if (!extension.isEmpty() && m_imageFormats.contains(extension))
169 return true;
170
171 return false;
172 }
173
getSanitizedThumbnailName(QString label) const174 QString PlaylistModel::getSanitizedThumbnailName(QString label) const
175 {
176 return label.replace(m_fileSanitizerRegex, "_") + ".png";
177 }
178
getThumbnailPath(const QHash<QString,QString> & hash,QString type) const179 QString PlaylistModel::getThumbnailPath(const QHash<QString, QString> &hash, QString type) const
180 {
181 /* use thumbnail widgets to show regular image files */
182 if (isSupportedImage(hash["path"]))
183 return hash["path"];
184
185 return getPlaylistThumbnailsDir(hash.value("db_name"))
186 + "/" + type + "/" + getSanitizedThumbnailName(hash["label_noext"]);
187 }
188
getCurrentTypeThumbnailPath(const QModelIndex & index) const189 QString PlaylistModel::getCurrentTypeThumbnailPath(const QModelIndex &index) const
190 {
191 switch (m_thumbnailType)
192 {
193 case THUMBNAIL_TYPE_BOXART:
194 return getThumbnailPath(index, THUMBNAIL_BOXART);
195 case THUMBNAIL_TYPE_SCREENSHOT:
196 return getThumbnailPath(index, THUMBNAIL_SCREENSHOT);
197 case THUMBNAIL_TYPE_TITLE_SCREEN:
198 return getThumbnailPath(index, THUMBNAIL_TITLE);
199 default:
200 break;
201 }
202
203 return QString();
204 }
205
reloadThumbnail(const QModelIndex & index)206 void PlaylistModel::reloadThumbnail(const QModelIndex &index)
207 {
208 if (index.isValid())
209 {
210 reloadThumbnailPath(getCurrentTypeThumbnailPath(index));
211 loadThumbnail(index);
212 }
213 }
214
reloadSystemThumbnails(const QString system)215 void PlaylistModel::reloadSystemThumbnails(const QString system)
216 {
217 int i = 0;
218 QString key;
219 settings_t *settings = config_get_ptr();
220 const char *path_dir_thumbnails = settings->paths.directory_thumbnails;
221 QString path = QDir::cleanPath(QString(path_dir_thumbnails)) + "/" + system;
222 QList<QString> keys = m_cache.keys();
223 QList<QString> pending = m_pendingImages.values();
224
225 for (i = 0; i < keys.size(); i++)
226 {
227 key = keys.at(i);
228 if (key.startsWith(path))
229 m_cache.remove(key);
230 }
231
232 for (i = 0; i < pending.size(); i++)
233 {
234 key = pending.at(i);
235 if (key.startsWith(path))
236 m_pendingImages.remove(key);
237 }
238 }
239
reloadThumbnailPath(const QString path)240 void PlaylistModel::reloadThumbnailPath(const QString path)
241 {
242 m_cache.remove(path);
243 m_pendingImages.remove(path);
244 }
245
loadThumbnail(const QModelIndex & index)246 void PlaylistModel::loadThumbnail(const QModelIndex &index)
247 {
248 QString path = getCurrentTypeThumbnailPath(index);
249
250 if (!m_pendingImages.contains(path) && !m_cache.contains(path))
251 {
252 m_pendingImages.insert(path);
253 QtConcurrent::run(this, &PlaylistModel::loadImage, index, path);
254 }
255 }
256
loadImage(const QModelIndex & index,const QString & path)257 void PlaylistModel::loadImage(const QModelIndex &index, const QString &path)
258 {
259 const QImage image = QImage(path);
260 if (!image.isNull())
261 emit imageLoaded(image, index, path);
262 }
263
onImageLoaded(const QImage image,const QModelIndex & index,const QString & path)264 void PlaylistModel::onImageLoaded(const QImage image, const QModelIndex &index, const QString &path)
265 {
266 QPixmap *pixmap = new QPixmap(QPixmap::fromImage(image));
267 const int cost = pixmap->width() * pixmap->height() * pixmap->depth() / (8 * 1024);
268 m_cache.insert(path, pixmap, cost);
269 if (index.isValid())
270 emit dataChanged(index, index, { THUMBNAIL });
271 m_pendingImages.remove(path);
272 }
273
comp_hash_name_key_lower(const QHash<QString,QString> & lhs,const QHash<QString,QString> & rhs)274 static inline bool comp_hash_name_key_lower(const QHash<QString, QString> &lhs, const QHash<QString, QString> &rhs)
275 {
276 return lhs.value("name").toLower() < rhs.value("name").toLower();
277 }
278
addDirectoryFilesToList(QProgressDialog * dialog,QStringList & list,QDir & dir,QStringList & extensions)279 bool MainWindow::addDirectoryFilesToList(QProgressDialog *dialog,
280 QStringList &list, QDir &dir, QStringList &extensions)
281 {
282 int i;
283 PlaylistEntryDialog *playlistDialog = playlistEntryDialog();
284 QStringList dirList = dir.entryList(QStringList(), QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, QDir::Name);
285
286 for (i = 0; i < dirList.count(); i++)
287 {
288 QString path(dir.path() + "/" + dirList.at(i));
289 QByteArray pathArray = path.toUtf8();
290 QFileInfo fileInfo(path);
291 const char *pathData = pathArray.constData();
292
293 if (dialog->wasCanceled())
294 return false;
295
296 /* Needed to update progress dialog while doing
297 * a lot of stuff on the main thread. */
298 if (i % 25 == 0)
299 qApp->processEvents();
300
301 if (fileInfo.isDir())
302 {
303 QDir fileInfoDir(path);
304 bool success = addDirectoryFilesToList(
305 dialog, list, fileInfoDir, extensions);
306
307 if (!success)
308 return false;
309
310 continue;
311 }
312
313 if (fileInfo.isFile())
314 {
315 bool add = false;
316
317 if (extensions.isEmpty())
318 add = true;
319 else
320 {
321 if (extensions.contains(fileInfo.suffix()))
322 add = true;
323 else
324 {
325 if (path_is_compressed_file(pathData))
326 {
327 struct string_list *archive_list =
328 file_archive_get_file_list(pathData, NULL);
329
330 if (archive_list)
331 {
332 if (archive_list->size == 1)
333 {
334 /* Assume archives with one file should have
335 * that file loaded directly.
336 * Don't just extend this to add all files
337 * in a ZIP, because we might hit something like
338 * MAME/FBA where only the archives themselves
339 * are valid content. */
340 pathArray = (QString(pathData) + "#"
341 + archive_list->elems[0].data).toUtf8();
342 pathData = pathArray.constData();
343
344 if (!extensions.isEmpty() && playlistDialog->filterInArchive())
345 {
346 /* If the user chose to filter extensions
347 * inside archives, and this particular file
348 * inside the archive
349 * doesn't have one of the chosen extensions,
350 * then we skip it. */
351 if (extensions.contains(QFileInfo(pathData).suffix()))
352 add = true;
353 }
354 }
355
356 string_list_free(archive_list);
357 }
358 }
359 }
360 }
361
362 if (add)
363 list.append(fileInfo.absoluteFilePath());
364 }
365 }
366
367 return true;
368 }
369
onPlaylistFilesDropped(QStringList files)370 void MainWindow::onPlaylistFilesDropped(QStringList files)
371 {
372 addFilesToPlaylist(files);
373 }
374
375 /* Takes a list of files and folders and adds them to the
376 * currently selected playlist. Folders will have their
377 * contents added recursively. */
addFilesToPlaylist(QStringList files)378 void MainWindow::addFilesToPlaylist(QStringList files)
379 {
380 int i;
381 QStringList list;
382 QString currentPlaylistPath;
383 QByteArray currentPlaylistArray;
384 QScopedPointer<QProgressDialog> dialog(NULL);
385 QHash<QString, QString> selectedCore;
386 QHash<QString, QString> itemToAdd;
387 QString selectedDatabase;
388 QString selectedName;
389 QString selectedPath;
390 QStringList selectedExtensions;
391 playlist_config_t playlist_config;
392 QListWidgetItem *currentItem = m_listWidget->currentItem();
393 PlaylistEntryDialog *playlistDialog = playlistEntryDialog();
394 const char *currentPlaylistData = NULL;
395 playlist_t *playlist = NULL;
396 settings_t *settings = config_get_ptr();
397
398 playlist_config.capacity = COLLECTION_SIZE;
399 playlist_config.old_format = settings->bools.playlist_use_old_format;
400 playlist_config.compress = settings->bools.playlist_compression;
401 playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
402 playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
403
404 /* Assume a blank list means we will manually enter in all fields. */
405 if (files.isEmpty())
406 {
407 /* Make sure hash isn't blank, that would mean there's
408 * multiple entries to add at once. */
409 itemToAdd["label"] = "";
410 itemToAdd["path"] = "";
411 }
412 else if (files.count() == 1)
413 {
414 QString path = files.at(0);
415 QFileInfo info(path);
416
417 if (info.isFile())
418 {
419 itemToAdd["label"] = info.completeBaseName();
420 itemToAdd["path"] = path;
421 }
422 }
423
424 if (currentItem)
425 {
426 currentPlaylistPath = currentItem->data(Qt::UserRole).toString();
427
428 if (!currentPlaylistPath.isEmpty())
429 {
430 currentPlaylistArray = currentPlaylistPath.toUtf8();
431 currentPlaylistData = currentPlaylistArray.constData();
432 }
433 }
434
435 if (currentPlaylistPath == ALL_PLAYLISTS_TOKEN)
436 {
437 showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CANNOT_ADD_TO_ALL_PLAYLISTS), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
438 return;
439 }
440
441 /* a blank itemToAdd means there will be multiple */
442 if (!playlistDialog->showDialog(itemToAdd))
443 return;
444
445 selectedName = m_playlistEntryDialog->getSelectedName();
446 selectedPath = m_playlistEntryDialog->getSelectedPath();
447 selectedCore = m_playlistEntryDialog->getSelectedCore();
448 selectedDatabase = m_playlistEntryDialog->getSelectedDatabase();
449 selectedExtensions = m_playlistEntryDialog->getSelectedExtensions();
450
451 if (!selectedExtensions.isEmpty())
452 selectedExtensions.replaceInStrings(QRegularExpression("^\\."), "");
453
454 if (selectedDatabase.isEmpty())
455 selectedDatabase = QFileInfo(currentPlaylistPath).fileName();
456 else
457 selectedDatabase.append(".lpl");
458
459 dialog.reset(new QProgressDialog(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_GATHERING_LIST_OF_FILES), "Cancel", 0, 0, this));
460 dialog->setWindowModality(Qt::ApplicationModal);
461 dialog->show();
462
463 qApp->processEvents();
464
465 if ( selectedName.isEmpty()
466 || selectedPath.isEmpty()
467 || selectedDatabase.isEmpty())
468 {
469 showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLEASE_FILL_OUT_REQUIRED_FIELDS), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
470 return;
471 }
472
473 if (files.isEmpty())
474 files.append(selectedPath);
475
476 for (i = 0; i < files.count(); i++)
477 {
478 QString path(files.at(i));
479 QFileInfo fileInfo(path);
480
481 if (dialog->wasCanceled())
482 return;
483
484 /* Needed to update progress dialog while
485 * doing a lot of stuff on the main thread. */
486 if (i % 25 == 0)
487 qApp->processEvents();
488
489 if (fileInfo.isDir())
490 {
491 QDir dir(path);
492 bool success = addDirectoryFilesToList(
493 dialog.data(), list, dir, selectedExtensions);
494
495 if (!success)
496 return;
497
498 continue;
499 }
500
501 if (fileInfo.isFile())
502 {
503 bool add = false;
504
505 if (selectedExtensions.isEmpty())
506 add = true;
507 else
508 {
509 QByteArray pathArray = path.toUtf8();
510 const char *pathData = pathArray.constData();
511
512 if (selectedExtensions.contains(fileInfo.suffix()))
513 add = true;
514 else if (playlistDialog->filterInArchive()
515 && path_is_compressed_file(pathData))
516 {
517 /* We'll add it here, but really just delay
518 * the check until later when the archive
519 * contents are iterated. */
520 add = true;
521 }
522 }
523
524 if (add)
525 list.append(fileInfo.absoluteFilePath());
526 }
527 else if (files.count() == 1)
528 {
529 /* If adding a single file, tell user that it doesn't exist. */
530 showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
531 return;
532 }
533 }
534
535 dialog->setLabelText(msg_hash_to_str(
536 MENU_ENUM_LABEL_VALUE_QT_ADDING_FILES_TO_PLAYLIST));
537 dialog->setMaximum(list.count());
538
539 playlist_config_set_path(&playlist_config, currentPlaylistData);
540 playlist = playlist_init(&playlist_config);
541
542 for (i = 0; i < list.count(); i++)
543 {
544 QFileInfo fileInfo;
545 QByteArray fileBaseNameArray;
546 QByteArray pathArray;
547 QByteArray corePathArray;
548 QByteArray coreNameArray;
549 QByteArray databaseArray;
550 QString fileName = list.at(i);
551 const char *pathData = NULL;
552 const char *fileNameNoExten = NULL;
553 const char *corePathData = NULL;
554 const char *coreNameData = NULL;
555 const char *databaseData = NULL;
556
557 /* Cancel out of everything, the
558 * current progress will not be written
559 * to the playlist at all. */
560 if (dialog->wasCanceled())
561 {
562 playlist_free(playlist);
563 return;
564 }
565
566 if (fileName.isEmpty())
567 continue;
568
569 /* a modal QProgressDialog calls processEvents()
570 * automatically in setValue() */
571 dialog->setValue(i + 1);
572
573 fileInfo = fileName;
574
575 /* Make sure we're looking at a user-specified field
576 * and not just "<multiple>"
577 * in case it was a folder with one file in it */
578 if ( files.count() == 1
579 && list.count() == 1
580 && i == 0
581 && playlistDialog->nameFieldEnabled())
582 {
583 fileBaseNameArray = selectedName.toUtf8();
584 pathArray = QDir::toNativeSeparators(selectedPath).toUtf8();
585 }
586 /* Otherwise just use the file name itself (minus extension)
587 * for the playlist entry title */
588 else
589 {
590 fileBaseNameArray = fileInfo.completeBaseName().toUtf8();
591 pathArray = QDir::toNativeSeparators(fileName).toUtf8();
592 }
593
594 fileNameNoExten = fileBaseNameArray.constData();
595
596 pathData = pathArray.constData();
597
598 if (selectedCore.isEmpty())
599 {
600 corePathData = "DETECT";
601 coreNameData = "DETECT";
602 }
603 else
604 {
605 corePathArray = QDir::toNativeSeparators(
606 selectedCore.value("core_path")).toUtf8();
607 coreNameArray = selectedCore.value("core_name").toUtf8();
608 corePathData = corePathArray.constData();
609 coreNameData = coreNameArray.constData();
610 }
611
612 databaseArray = selectedDatabase.toUtf8();
613 databaseData = databaseArray.constData();
614
615 if (path_is_compressed_file(pathData))
616 {
617 struct string_list *list = file_archive_get_file_list(pathData, NULL);
618
619 if (list)
620 {
621 if (list->size == 1)
622 {
623 /* Assume archives with one file should have that
624 * file loaded directly.
625 * Don't just extend this to add all files in a zip,
626 * because we might hit
627 * something like MAME/FBA where only the archives
628 * themselves are valid content. */
629 pathArray = QDir::toNativeSeparators(QString(pathData)
630 + "#" + list->elems[0].data).toUtf8();
631 pathData = pathArray.constData();
632
633 if ( !selectedExtensions.isEmpty()
634 && playlistDialog->filterInArchive())
635 {
636 /* If the user chose to filter extensions inside archives,
637 * and this particular file inside the archive
638 * doesn't have one of the chosen extensions,
639 * then we skip it. */
640 if (!selectedExtensions.contains(
641 QFileInfo(pathData).suffix()))
642 {
643 string_list_free(list);
644 continue;
645 }
646 }
647 }
648
649 string_list_free(list);
650 }
651 }
652
653 {
654 struct playlist_entry entry = {0};
655
656 /* the push function reads our entry as const,
657 * so these casts are safe */
658 entry.path = const_cast<char*>(pathData);
659 entry.label = const_cast<char*>(fileNameNoExten);
660 entry.core_path = const_cast<char*>(corePathData);
661 entry.core_name = const_cast<char*>(coreNameData);
662 entry.crc32 = const_cast<char*>("00000000|crc");
663 entry.db_name = const_cast<char*>(databaseData);
664
665 playlist_push(playlist, &entry);
666 }
667 }
668
669 playlist_write_file(playlist);
670 playlist_free(playlist);
671
672 reloadPlaylists();
673 }
674
updateCurrentPlaylistEntry(const QHash<QString,QString> & contentHash)675 bool MainWindow::updateCurrentPlaylistEntry(
676 const QHash<QString, QString> &contentHash)
677 {
678 QString path;
679 QString label;
680 QString corePath;
681 QString coreName;
682 QString dbName;
683 QString crc32;
684 QByteArray playlistPathArray;
685 QByteArray pathArray;
686 QByteArray labelArray;
687 QByteArray corePathArray;
688 QByteArray coreNameArray;
689 QByteArray dbNameArray;
690 QByteArray crc32Array;
691 playlist_config_t playlist_config;
692 QString playlistPath = getCurrentPlaylistPath();
693 const char *playlistPathData = NULL;
694 const char *pathData = NULL;
695 const char *labelData = NULL;
696 const char *corePathData = NULL;
697 const char *coreNameData = NULL;
698 const char *dbNameData = NULL;
699 const char *crc32Data = NULL;
700 playlist_t *playlist = NULL;
701 unsigned index = 0;
702 bool ok = false;
703 settings_t *settings = config_get_ptr();
704
705 playlist_config.capacity = COLLECTION_SIZE;
706 playlist_config.old_format = settings->bools.playlist_use_old_format;
707 playlist_config.compress = settings->bools.playlist_compression;
708 playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
709 playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
710
711 if ( playlistPath.isEmpty() ||
712 contentHash.isEmpty() ||
713 !contentHash.contains("index"))
714 return false;
715
716 index = contentHash.value("index").toUInt(&ok);
717
718 if (!ok)
719 return false;
720
721 path = contentHash.value("path");
722 label = contentHash.value("label");
723 coreName = contentHash.value("core_name");
724 corePath = contentHash.value("core_path");
725 dbName = contentHash.value("db_name");
726 crc32 = contentHash.value("crc32");
727
728 if (path.isEmpty() ||
729 label.isEmpty() ||
730 coreName.isEmpty() ||
731 corePath.isEmpty()
732 )
733 return false;
734
735 playlistPathArray = playlistPath.toUtf8();
736 pathArray = QDir::toNativeSeparators(path).toUtf8();
737 labelArray = label.toUtf8();
738 coreNameArray = coreName.toUtf8();
739 corePathArray = QDir::toNativeSeparators(corePath).toUtf8();
740
741 if (!dbName.isEmpty())
742 {
743 dbNameArray = (dbName + ".lpl").toUtf8();
744 dbNameData = dbNameArray.constData();
745 }
746
747 playlistPathData = playlistPathArray.constData();
748 pathData = pathArray.constData();
749 labelData = labelArray.constData();
750 coreNameData = coreNameArray.constData();
751 corePathData = corePathArray.constData();
752
753 if (!crc32.isEmpty())
754 {
755 crc32Array = crc32.toUtf8();
756 crc32Data = crc32Array.constData();
757 }
758
759 if (path_is_compressed_file(pathData))
760 {
761 struct string_list *list = file_archive_get_file_list(pathData, NULL);
762
763 if (list)
764 {
765 if (list->size == 1)
766 {
767 /* assume archives with one file should have that file loaded directly */
768 pathArray = QDir::toNativeSeparators(QString(pathData) + "#" + list->elems[0].data).toUtf8();
769 pathData = pathArray.constData();
770 }
771
772 string_list_free(list);
773 }
774 }
775
776 playlist_config_set_path(&playlist_config, playlistPathData);
777 playlist = playlist_init(&playlist_config);
778
779 {
780 struct playlist_entry entry = {0};
781
782 /* the update function reads our entry as const, so these casts are safe */
783 entry.path = const_cast<char*>(pathData);
784 entry.label = const_cast<char*>(labelData);
785 entry.core_path = const_cast<char*>(corePathData);
786 entry.core_name = const_cast<char*>(coreNameData);
787 entry.crc32 = const_cast<char*>(crc32Data);
788 entry.db_name = const_cast<char*>(dbNameData);
789
790 playlist_update(playlist, index, &entry);
791 }
792
793 playlist_write_file(playlist);
794 playlist_free(playlist);
795
796 reloadPlaylists();
797
798 return true;
799 }
800
onPlaylistWidgetContextMenuRequested(const QPoint &)801 void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&)
802 {
803 QString currentPlaylistDirPath;
804 QString currentPlaylistPath;
805 QString currentPlaylistFileName;
806 QFile currentPlaylistFile;
807 QFileInfo currentPlaylistFileInfo;
808 QMap<QString, const core_info_t*> coreList;
809 QScopedPointer<QMenu> menu;
810 QScopedPointer<QMenu> associateMenu;
811 QScopedPointer<QMenu> hiddenPlaylistsMenu;
812 QScopedPointer<QMenu> downloadAllThumbnailsMenu;
813 QScopedPointer<QAction> hideAction;
814 QScopedPointer<QAction> newPlaylistAction;
815 QScopedPointer<QAction> deletePlaylistAction;
816 QScopedPointer<QAction> renamePlaylistAction;
817 QScopedPointer<QAction> downloadAllThumbnailsEntireSystemAction;
818 QScopedPointer<QAction> downloadAllThumbnailsThisPlaylistAction;
819 QPointer<QAction> selectedAction;
820 playlist_config_t playlist_config;
821 QPoint cursorPos = QCursor::pos();
822 settings_t *settings = config_get_ptr();
823 const char *path_dir_playlist = settings->paths.directory_playlist;
824 QDir playlistDir(path_dir_playlist);
825 QListWidgetItem *selectedItem = m_listWidget->itemAt(
826 m_listWidget->viewport()->mapFromGlobal(cursorPos));
827 QString playlistDirAbsPath = playlistDir.absolutePath();
828 core_info_list_t *core_info_list = NULL;
829 unsigned i = 0;
830 int j = 0;
831 bool specialPlaylist = false;
832 bool foundHiddenPlaylist = false;
833
834 playlist_config.capacity = COLLECTION_SIZE;
835 playlist_config.old_format = settings->bools.playlist_use_old_format;
836 playlist_config.compress = settings->bools.playlist_compression;
837 playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
838 playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
839
840 if (selectedItem)
841 {
842 currentPlaylistPath = selectedItem->data(Qt::UserRole).toString();
843 currentPlaylistFile.setFileName(currentPlaylistPath);
844
845 currentPlaylistFileInfo = QFileInfo(currentPlaylistPath);
846 currentPlaylistFileName = currentPlaylistFileInfo.fileName();
847 currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath();
848 }
849
850 menu.reset(new QMenu(this));
851 menu->setObjectName("menu");
852
853 hiddenPlaylistsMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS), this));
854 newPlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST)) + "...", this));
855
856 hiddenPlaylistsMenu->setObjectName("hiddenPlaylistsMenu");
857
858 menu->addAction(newPlaylistAction.data());
859
860 if (currentPlaylistFile.exists())
861 {
862 deletePlaylistAction.reset(new QAction(
863 QString(msg_hash_to_str(
864 MENU_ENUM_LABEL_VALUE_QT_DELETE_PLAYLIST)) + "...",
865 this));
866 menu->addAction(deletePlaylistAction.data());
867
868 renamePlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RENAME_PLAYLIST)) + "...", this));
869 menu->addAction(renamePlaylistAction.data());
870 }
871
872 if (selectedItem)
873 {
874 hideAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDE), this));
875 menu->addAction(hideAction.data());
876 }
877
878 for (j = 0; j < m_listWidget->count(); j++)
879 {
880 QListWidgetItem *item = m_listWidget->item(j);
881 bool hidden = m_listWidget->isItemHidden(item);
882
883 if (hidden)
884 {
885 QAction *action = hiddenPlaylistsMenu->addAction(item->text());
886 action->setProperty("row", j);
887 action->setProperty("core_path", item->data(Qt::UserRole).toString());
888 foundHiddenPlaylist = true;
889 }
890 }
891
892 if (!foundHiddenPlaylist)
893 {
894 QAction *action = hiddenPlaylistsMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE));
895 action->setProperty("row", -1);
896 }
897
898 menu->addMenu(hiddenPlaylistsMenu.data());
899
900 /* Don't just compare strings in case there are case differences on Windows that should be ignored. */
901 if (QDir(currentPlaylistDirPath) != QDir(playlistDirAbsPath))
902 {
903 /* special playlists like history etc. can't have an association */
904 specialPlaylist = true;
905 }
906
907 if (!specialPlaylist)
908 {
909 associateMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ASSOCIATE_CORE), this));
910 associateMenu->setObjectName("associateMenu");
911
912 core_info_get_list(&core_info_list);
913
914 for (i = 0; i < core_info_list->count && core_info_list->count > 0; i++)
915 {
916 const core_info_t *core = &core_info_list->list[i];
917 coreList[core->core_name] = core;
918 }
919
920 {
921 QMapIterator<QString, const core_info_t*> coreListIterator(coreList);
922 QVector<QHash<QString, QString> > cores;
923
924 while (coreListIterator.hasNext())
925 {
926 QString key, name;
927 const core_info_t *core = NULL;
928 QHash<QString, QString> hash;
929
930 coreListIterator.next();
931
932 key = coreListIterator.key();
933 core = coreList.value(key);
934
935 if (string_is_empty(core->core_name))
936 name = core->display_name;
937 else
938 name = core->core_name;
939
940 if (name.isEmpty())
941 continue;
942
943 hash["name"] = name;
944 hash["core_path"] = core->path;
945
946 cores.append(hash);
947 }
948
949 std::sort(cores.begin(), cores.end(), comp_hash_name_key_lower);
950
951 for (j = 0; j < cores.count(); j++)
952 {
953 const QHash<QString, QString> &hash = cores.at(j);
954 QAction *action = associateMenu->addAction(hash.value("name"));
955
956 action->setProperty("core_path", hash.value("core_path"));
957 }
958 }
959
960 menu->addMenu(associateMenu.data());
961 }
962
963 if (!specialPlaylist)
964 {
965 downloadAllThumbnailsMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS), this));
966 downloadAllThumbnailsMenu->setObjectName("downloadAllThumbnailsMenu");
967
968 downloadAllThumbnailsThisPlaylistAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS_THIS_PLAYLIST), downloadAllThumbnailsMenu.data()));
969 downloadAllThumbnailsEntireSystemAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS_ENTIRE_SYSTEM), downloadAllThumbnailsMenu.data()));
970
971 downloadAllThumbnailsMenu->addAction(downloadAllThumbnailsThisPlaylistAction.data());
972 downloadAllThumbnailsMenu->addAction(downloadAllThumbnailsEntireSystemAction.data());
973
974 menu->addMenu(downloadAllThumbnailsMenu.data());
975 }
976
977 selectedAction = menu->exec(cursorPos);
978
979 if (!selectedAction)
980 return;
981
982 if (!specialPlaylist && selectedAction->parent() == associateMenu.data())
983 {
984 core_info_t *coreInfo = NULL;
985 playlist_t *cachedPlaylist = playlist_get_cached();
986 playlist_t *playlist = NULL;
987 bool loadPlaylist = true;
988 QByteArray currentPlaylistPathByteArray = currentPlaylistPath.toUtf8();
989 const char *currentPlaylistPathCString = currentPlaylistPathByteArray.data();
990 QByteArray corePathByteArray = selectedAction->property("core_path").toString().toUtf8();
991 const char *corePath = corePathByteArray.data();
992
993 /* Load playlist, if required */
994 if (cachedPlaylist)
995 {
996 if (string_is_equal(currentPlaylistPathCString,
997 playlist_get_conf_path(cachedPlaylist)))
998 {
999 playlist = cachedPlaylist;
1000 loadPlaylist = false;
1001 }
1002 }
1003
1004 if (loadPlaylist)
1005 {
1006 playlist_config_set_path(&playlist_config, currentPlaylistPathCString);
1007 playlist = playlist_init(&playlist_config);
1008 }
1009
1010 if (playlist)
1011 {
1012 /* Get core info */
1013 if (core_info_find(corePath, &coreInfo))
1014 {
1015 /* Set new core association */
1016 playlist_set_default_core_path(playlist, coreInfo->path);
1017 playlist_set_default_core_name(playlist, coreInfo->display_name);
1018 }
1019 else
1020 {
1021 playlist_set_default_core_path(playlist, "DETECT");
1022 playlist_set_default_core_name(playlist, "DETECT");
1023 }
1024
1025 /* Write changes to disk */
1026 playlist_write_file(playlist);
1027
1028 /* Free playlist, if required */
1029 if (loadPlaylist)
1030 playlist_free(playlist);
1031 }
1032 }
1033 else if (selectedItem && selectedAction == deletePlaylistAction.data())
1034 {
1035 if (currentPlaylistFile.exists())
1036 {
1037 if (showMessageBox(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST)).arg(selectedItem->text()), MainWindow::MSGBOX_TYPE_QUESTION_YESNO, Qt::ApplicationModal, false))
1038 {
1039 if (currentPlaylistFile.remove())
1040 reloadPlaylists();
1041 else
1042 showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
1043 }
1044 }
1045 }
1046 else if (selectedItem && selectedAction == renamePlaylistAction.data())
1047 {
1048 if (currentPlaylistFile.exists())
1049 {
1050 QString oldName = selectedItem->text();
1051 QString name = QInputDialog::getText(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RENAME_PLAYLIST), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ENTER_NEW_PLAYLIST_NAME), QLineEdit::Normal, oldName);
1052
1053 if (!name.isEmpty())
1054 {
1055 renamePlaylistItem(selectedItem, name);
1056 reloadPlaylists();
1057 }
1058 }
1059 }
1060 else if (selectedAction == newPlaylistAction.data())
1061 {
1062 QString name = QInputDialog::getText(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ENTER_NEW_PLAYLIST_NAME));
1063 QString newPlaylistPath = playlistDirAbsPath + "/" + name + ".lpl";
1064 QFile file(newPlaylistPath);
1065
1066 if (!name.isEmpty())
1067 {
1068 if (file.open(QIODevice::WriteOnly))
1069 file.close();
1070
1071 reloadPlaylists();
1072 }
1073 }
1074 else if (selectedItem && selectedAction == hideAction.data())
1075 {
1076 int row = m_listWidget->row(selectedItem);
1077
1078 if (row >= 0)
1079 {
1080 QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList();
1081
1082 if (!hiddenPlaylists.contains(currentPlaylistFileName))
1083 {
1084 hiddenPlaylists.append(currentPlaylistFileName);
1085 m_settings->setValue("hidden_playlists", hiddenPlaylists);
1086 }
1087
1088 m_listWidget->setRowHidden(row, true);
1089 }
1090 }
1091 else if (selectedAction->parent() == hiddenPlaylistsMenu.data())
1092 {
1093 QVariant rowVariant = selectedAction->property("row");
1094
1095 if (rowVariant.isValid())
1096 {
1097 QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList();
1098 int row = rowVariant.toInt();
1099
1100 if (row >= 0)
1101 {
1102 QString playlistPath = selectedAction->property("core_path").toString();
1103 QFileInfo playlistFileInfo(playlistPath);
1104 QString playlistFileName = playlistFileInfo.fileName();
1105
1106 if (hiddenPlaylists.contains(playlistFileName))
1107 {
1108 hiddenPlaylists.removeOne(playlistFileName);
1109 m_settings->setValue("hidden_playlists", hiddenPlaylists);
1110 }
1111
1112 m_listWidget->setRowHidden(row, false);
1113 }
1114 }
1115 }
1116 else if (selectedItem && !specialPlaylist && selectedAction->parent() == downloadAllThumbnailsMenu.data())
1117 {
1118 if (selectedAction == downloadAllThumbnailsEntireSystemAction.data())
1119 {
1120 int row = m_listWidget->row(selectedItem);
1121
1122 if (row >= 0)
1123 downloadAllThumbnails(currentPlaylistFileInfo.completeBaseName());
1124 }
1125 else if (selectedAction == downloadAllThumbnailsThisPlaylistAction.data())
1126 {
1127 downloadPlaylistThumbnails(currentPlaylistPath);
1128 }
1129 }
1130
1131 setCoreActions();
1132 }
1133
deferReloadPlaylists()1134 void MainWindow::deferReloadPlaylists()
1135 {
1136 emit gotReloadPlaylists();
1137 }
1138
onGotReloadPlaylists()1139 void MainWindow::onGotReloadPlaylists()
1140 {
1141 reloadPlaylists();
1142 }
1143
reloadPlaylists()1144 void MainWindow::reloadPlaylists()
1145 {
1146 int i = 0;
1147 QString currentPlaylistPath;
1148 QListWidgetItem *allPlaylistsItem = NULL;
1149 QListWidgetItem *favoritesPlaylistsItem = NULL;
1150 QListWidgetItem *imagePlaylistsItem = NULL;
1151 QListWidgetItem *musicPlaylistsItem = NULL;
1152 QListWidgetItem *videoPlaylistsItem = NULL;
1153 QListWidgetItem *firstItem = NULL;
1154 settings_t *settings = config_get_ptr();
1155 const char *path_dir_playlist = settings->paths.directory_playlist;
1156 QDir playlistDir(path_dir_playlist);
1157 QStringList hiddenPlaylists = m_settings->value(
1158 "hidden_playlists").toStringList();
1159
1160 QListWidgetItem *currentItem = m_listWidget->currentItem();
1161
1162 if (currentItem)
1163 currentPlaylistPath = currentItem->data(Qt::UserRole).toString();
1164
1165 getPlaylistFiles();
1166
1167 m_listWidget->clear();
1168 m_listWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
1169 m_listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
1170 m_listWidget->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed);
1171
1172 allPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS));
1173 allPlaylistsItem->setData(Qt::UserRole, ALL_PLAYLISTS_TOKEN);
1174
1175 favoritesPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES_TAB));
1176 favoritesPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_favorites);
1177
1178 m_historyPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_HISTORY_TAB));
1179 m_historyPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_history);
1180
1181 imagePlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_IMAGES_TAB));
1182 imagePlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_image_history);
1183
1184 musicPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MUSIC_TAB));
1185 musicPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_music_history);
1186
1187 videoPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_TAB));
1188 videoPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_video_history);
1189
1190 m_listWidget->addItem(allPlaylistsItem);
1191 m_listWidget->addItem(favoritesPlaylistsItem);
1192 m_listWidget->addItem(m_historyPlaylistsItem);
1193 m_listWidget->addItem(imagePlaylistsItem);
1194 m_listWidget->addItem(musicPlaylistsItem);
1195 m_listWidget->addItem(videoPlaylistsItem);
1196
1197 if (hiddenPlaylists.contains(ALL_PLAYLISTS_TOKEN))
1198 m_listWidget->setRowHidden(m_listWidget->row(allPlaylistsItem), true);
1199 if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_favorites).fileName()))
1200 m_listWidget->setRowHidden(m_listWidget->row(favoritesPlaylistsItem), true);
1201 if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_history).fileName()))
1202 m_listWidget->setRowHidden(m_listWidget->row(m_historyPlaylistsItem), true);
1203 if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_image_history).fileName()))
1204 m_listWidget->setRowHidden(m_listWidget->row(imagePlaylistsItem), true);
1205 if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_music_history).fileName()))
1206 m_listWidget->setRowHidden(m_listWidget->row(musicPlaylistsItem), true);
1207 if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_video_history).fileName()))
1208 m_listWidget->setRowHidden(m_listWidget->row(videoPlaylistsItem), true);
1209
1210 for (i = 0; i < m_playlistFiles.count(); i++)
1211 {
1212 QIcon icon;
1213 QString iconPath;
1214 QListWidgetItem *item = NULL;
1215 const QString &file = m_playlistFiles.at(i);
1216 QString fileDisplayName = file;
1217 QString fileName = file;
1218 bool hasIcon = false;
1219
1220 fileDisplayName.remove(".lpl");
1221
1222 iconPath = QString(
1223 settings->paths.directory_assets)
1224 + ICON_PATH
1225 + fileDisplayName
1226 + ".png";
1227
1228 hasIcon = QFile::exists(iconPath);
1229
1230 if (hasIcon)
1231 icon = QIcon(iconPath);
1232 else
1233 icon = m_folderIcon;
1234
1235 item = new QListWidgetItem(icon, fileDisplayName);
1236 item->setFlags(item->flags() | Qt::ItemIsEditable);
1237 item->setData(Qt::UserRole, playlistDir.absoluteFilePath(file));
1238
1239 m_listWidget->addItem(item);
1240
1241 if (hiddenPlaylists.contains(fileName))
1242 {
1243 int row = m_listWidget->row(item);
1244
1245 if (row >= 0)
1246 m_listWidget->setRowHidden(row, true);
1247 }
1248 }
1249
1250 if (m_listWidget->count() > 0)
1251 {
1252 firstItem = m_listWidget->item(0);
1253
1254 if (firstItem)
1255 {
1256 bool foundCurrent = false;
1257 bool foundInitial = false;
1258 QString initialPlaylist = m_settings->value("initial_playlist", m_historyPlaylistsItem->data(Qt::UserRole).toString()).toString();
1259 QListWidgetItem *initialItem = NULL;
1260
1261 for (i = 0; i < m_listWidget->count(); i++)
1262 {
1263 QString path;
1264 QListWidgetItem *item = m_listWidget->item(i);
1265
1266 if (item)
1267 {
1268 path = item->data(Qt::UserRole).toString();
1269
1270 if (!path.isEmpty())
1271 {
1272 /* don't break early here since we want
1273 * to make sure we've found both initial
1274 * and current items if they exist */
1275 if (!foundInitial && path == initialPlaylist)
1276 {
1277 foundInitial = true;
1278 initialItem = item;
1279 }
1280 if ( !foundCurrent
1281 && !currentPlaylistPath.isEmpty()
1282 && path == currentPlaylistPath)
1283 {
1284 foundCurrent = true;
1285 m_listWidget->setCurrentItem(item);
1286 }
1287 }
1288 }
1289 }
1290
1291 if (!foundCurrent)
1292 {
1293 if (foundInitial && initialItem)
1294 m_listWidget->setCurrentItem(initialItem);
1295 else
1296 {
1297 /* the previous playlist must be gone now,
1298 * just select the first one */
1299 m_listWidget->setCurrentItem(firstItem);
1300 }
1301 }
1302 }
1303 }
1304
1305 }
1306
getCurrentPlaylistPath()1307 QString MainWindow::getCurrentPlaylistPath()
1308 {
1309 QString playlistPath;
1310 QListWidgetItem *playlistItem = m_listWidget->currentItem();
1311
1312 if (!playlistItem)
1313 return playlistPath;
1314
1315 playlistPath = playlistItem->data(Qt::UserRole).toString();
1316
1317 return playlistPath;
1318 }
1319
currentPlaylistIsSpecial()1320 bool MainWindow::currentPlaylistIsSpecial()
1321 {
1322 QFileInfo currentPlaylistFileInfo;
1323 QString currentPlaylistPath;
1324 QString currentPlaylistDirPath;
1325 settings_t *settings = config_get_ptr();
1326 QDir playlistDir(settings->paths.directory_playlist);
1327 QString playlistDirAbsPath = playlistDir.absolutePath();
1328 QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem();
1329
1330 if (!currentPlaylistItem)
1331 return false;
1332
1333 currentPlaylistPath = currentPlaylistItem->data(Qt::UserRole).toString();
1334 currentPlaylistFileInfo = QFileInfo(currentPlaylistPath);
1335 currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath();
1336
1337 /* Don't just compare strings in case there are
1338 * case differences on Windows that should be ignored. */
1339 if (QDir(currentPlaylistDirPath) != QDir(playlistDirAbsPath))
1340 return true;
1341 return false;
1342 }
1343
currentPlaylistIsAll()1344 bool MainWindow::currentPlaylistIsAll()
1345 {
1346 QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem();
1347 if (
1348 currentPlaylistItem
1349 && currentPlaylistItem->data(Qt::UserRole).toString()
1350 == ALL_PLAYLISTS_TOKEN)
1351 return true;
1352 return false;
1353 }
1354
deleteCurrentPlaylistItem()1355 void MainWindow::deleteCurrentPlaylistItem()
1356 {
1357 QByteArray playlistArray;
1358 playlist_config_t playlist_config;
1359 QString playlistPath = getCurrentPlaylistPath();
1360 QHash<QString, QString> contentHash = getCurrentContentHash();
1361 playlist_t *playlist = NULL;
1362 const char *playlistData = NULL;
1363 unsigned index = 0;
1364 bool ok = false;
1365 bool isAllPlaylist = currentPlaylistIsAll();
1366 settings_t *settings = config_get_ptr();
1367
1368 playlist_config.capacity = COLLECTION_SIZE;
1369 playlist_config.old_format = settings->bools.playlist_use_old_format;
1370 playlist_config.compress = settings->bools.playlist_compression;
1371 playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
1372 playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
1373
1374 if (isAllPlaylist)
1375 return;
1376
1377 if (playlistPath.isEmpty())
1378 return;
1379
1380 if (contentHash.isEmpty())
1381 return;
1382
1383 playlistArray = playlistPath.toUtf8();
1384 playlistData = playlistArray.constData();
1385
1386 index = contentHash.value("index").toUInt(&ok);
1387
1388 if (!ok)
1389 return;
1390
1391 if (!showMessageBox(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST_ITEM)).arg(contentHash["label"]), MainWindow::MSGBOX_TYPE_QUESTION_YESNO, Qt::ApplicationModal, false))
1392 return;
1393
1394 playlist_config_set_path(&playlist_config, playlistData);
1395 playlist = playlist_init(&playlist_config);
1396
1397 playlist_delete_index(playlist, index);
1398 playlist_write_file(playlist);
1399 playlist_free(playlist);
1400
1401 reloadPlaylists();
1402 }
1403
getPlaylistDefaultCore(QString plName)1404 QString MainWindow::getPlaylistDefaultCore(QString plName)
1405 {
1406 playlist_config_t playlist_config;
1407 char playlistPath[PATH_MAX_LENGTH];
1408 QByteArray plNameByteArray = plName.toUtf8();
1409 const char *plNameCString = plNameByteArray.data();
1410 playlist_t *cachedPlaylist = playlist_get_cached();
1411 playlist_t *playlist = NULL;
1412 bool loadPlaylist = true;
1413 QString corePath = QString();
1414 settings_t *settings = config_get_ptr();
1415
1416 playlist_config.capacity = COLLECTION_SIZE;
1417 playlist_config.old_format = settings->bools.playlist_use_old_format;
1418 playlist_config.compress = settings->bools.playlist_compression;
1419 playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
1420 playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
1421
1422 playlistPath[0] = '\0';
1423
1424 if (!settings || string_is_empty(plNameCString))
1425 return corePath;
1426
1427 /* Get playlist path */
1428 fill_pathname_join(
1429 playlistPath,
1430 settings->paths.directory_playlist, plNameCString,
1431 sizeof(playlistPath));
1432 strlcat(playlistPath, ".lpl", sizeof(playlistPath));
1433
1434 /* Load playlist, if required */
1435 if (cachedPlaylist)
1436 {
1437 if (string_is_equal(playlistPath,
1438 playlist_get_conf_path(cachedPlaylist)))
1439 {
1440 playlist = cachedPlaylist;
1441 loadPlaylist = false;
1442 }
1443 }
1444
1445 if (loadPlaylist)
1446 {
1447 playlist_config_set_path(&playlist_config, playlistPath);
1448 playlist = playlist_init(&playlist_config);
1449 }
1450
1451 if (playlist)
1452 {
1453 const char *defaultCorePath = playlist_get_default_core_path(playlist);
1454
1455 /* Get default core path */
1456 if (!string_is_empty(defaultCorePath) &&
1457 !string_is_equal(defaultCorePath, "DETECT"))
1458 corePath = QString::fromUtf8(defaultCorePath);
1459
1460 /* Free playlist, if required */
1461 if (loadPlaylist)
1462 playlist_free(playlist);
1463 }
1464
1465 return corePath;
1466 }
1467
getPlaylistFiles()1468 void MainWindow::getPlaylistFiles()
1469 {
1470 settings_t *settings = config_get_ptr();
1471 QDir playlistDir(settings->paths.directory_playlist);
1472
1473 m_playlistFiles = playlistDir.entryList(
1474 QDir::NoDotAndDotDot | QDir::Readable | QDir::Files, QDir::Name);
1475 }
1476
getPlaylistItems(QString path)1477 void PlaylistModel::getPlaylistItems(QString path)
1478 {
1479 QByteArray pathArray;
1480 playlist_config_t playlist_config;
1481 const char *pathData = NULL;
1482 const char *playlistName = NULL;
1483 playlist_t *playlist = NULL;
1484 unsigned playlistSize = 0;
1485 unsigned i = 0;
1486 settings_t *settings = config_get_ptr();
1487
1488 playlist_config.capacity = COLLECTION_SIZE;
1489 playlist_config.old_format = settings->bools.playlist_use_old_format;
1490 playlist_config.compress = settings->bools.playlist_compression;
1491 playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
1492 playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
1493
1494 pathArray.append(path);
1495 pathData = pathArray.constData();
1496 if (!string_is_empty(pathData))
1497 playlistName = path_basename(pathData);
1498
1499 playlist_config_set_path(&playlist_config, pathData);
1500 playlist = playlist_init(&playlist_config);
1501 playlistSize = playlist_get_size(playlist);
1502
1503 for (i = 0; i < playlistSize; i++)
1504 {
1505 QHash<QString, QString> hash;
1506 const struct playlist_entry *entry = NULL;
1507
1508 playlist_get_index(playlist, i, &entry);
1509
1510 if (string_is_empty(entry->path))
1511 continue;
1512
1513 hash["path"] = entry->path;
1514 hash["index"] = QString::number(i);
1515
1516 if (string_is_empty(entry->label))
1517 {
1518 hash["label"] = entry->path;
1519 hash["label_noext"] = entry->path;
1520 }
1521 else
1522 {
1523 hash["label"] = entry->label;
1524 hash["label_noext"] = entry->label;
1525 }
1526
1527 if (!string_is_empty(entry->core_path))
1528 hash["core_path"] = entry->core_path;
1529
1530 if (!string_is_empty(entry->core_name))
1531 hash["core_name"] = entry->core_name;
1532
1533 if (!string_is_empty(entry->crc32))
1534 hash["crc32"] = entry->crc32;
1535
1536 if (!string_is_empty(entry->db_name))
1537 {
1538 hash["db_name"] = entry->db_name;
1539 hash["db_name"].remove(".lpl");
1540 }
1541
1542 if (!string_is_empty(playlistName))
1543 {
1544 hash["pl_name"] = playlistName;
1545 hash["pl_name"].remove(".lpl");
1546 }
1547
1548 m_contents.append(hash);
1549 }
1550
1551 playlist_free(playlist);
1552 playlist = NULL;
1553 }
1554
addPlaylistItems(const QStringList & paths,bool add)1555 void PlaylistModel::addPlaylistItems(const QStringList &paths, bool add)
1556 {
1557 int i;
1558
1559 if (paths.isEmpty())
1560 return;
1561
1562 beginResetModel();
1563
1564 m_contents.clear();
1565
1566 for (i = 0; i < paths.size(); i++)
1567 getPlaylistItems(paths.at(i));
1568
1569 endResetModel();
1570 }
1571
addDir(QString path,QFlags<QDir::Filter> showHidden)1572 void PlaylistModel::addDir(QString path, QFlags<QDir::Filter> showHidden)
1573 {
1574 QDir dir = path;
1575 int i = 0;
1576 QStringList dirList =
1577 dir.entryList(QDir::NoDotAndDotDot |
1578 QDir::Readable |
1579 QDir::Files |
1580 showHidden,
1581 QDir::Name);
1582
1583 if (dirList.count() == 0)
1584 return;
1585
1586 beginResetModel();
1587
1588 m_contents.clear();
1589
1590 for (i = 0; i < dirList.count(); i++)
1591 {
1592 QHash<QString, QString> hash;
1593 QString fileName = dirList.at(i);
1594 QString filePath(
1595 QDir::toNativeSeparators(dir.absoluteFilePath(fileName)));
1596 QFileInfo fileInfo(filePath);
1597
1598 hash["path"] = filePath;
1599 hash["label"] = hash["path"];
1600 hash["label_noext"] = fileInfo.completeBaseName();
1601 hash["db_name"] = fileInfo.dir().dirName();
1602
1603 m_contents.append(hash);
1604 }
1605
1606 endResetModel();
1607 }
1608
setAllPlaylistsListMaxCount(int count)1609 void MainWindow::setAllPlaylistsListMaxCount(int count)
1610 {
1611 if (count < 1)
1612 count = 0;
1613
1614 m_allPlaylistsListMaxCount = count;
1615 }
1616
setAllPlaylistsGridMaxCount(int count)1617 void MainWindow::setAllPlaylistsGridMaxCount(int count)
1618 {
1619 if (count < 1)
1620 count = 0;
1621
1622 m_allPlaylistsGridMaxCount = count;
1623 }
1624