mirror of
https://github.com/libretro/RetroArch
synced 2024-12-29 12:31:05 +00:00
1624 lines
52 KiB
C++
1624 lines
52 KiB
C++
#include <QFileInfo>
|
|
#include <QListWidgetItem>
|
|
#include <QApplication>
|
|
#include <QProgressDialog>
|
|
#include <QDir>
|
|
#include <QMenu>
|
|
#include <QSettings>
|
|
#include <QInputDialog>
|
|
#include <QLayout>
|
|
#include <QScreen>
|
|
#include <QRegularExpression>
|
|
#include <QImageReader>
|
|
#include <QtConcurrent>
|
|
|
|
#include "../ui_qt.h"
|
|
#include "qt_dialogs.h"
|
|
|
|
#ifndef CXX_BUILD
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../../../config.h"
|
|
#endif
|
|
|
|
#include <file/file_path.h>
|
|
#include <file/archive_file.h>
|
|
#include <lists/string_list.h>
|
|
#include <string/stdstring.h>
|
|
|
|
#ifdef HAVE_MENU
|
|
#include "../../../menu/menu_displaylist.h"
|
|
#endif
|
|
|
|
#include "../../../file_path_special.h"
|
|
#include "../../../playlist.h"
|
|
#include "../../../setting_list.h"
|
|
#include "../../../configuration.h"
|
|
#include "../../../core_info.h"
|
|
#include "../../../verbosity.h"
|
|
|
|
#ifndef CXX_BUILD
|
|
}
|
|
#endif
|
|
|
|
PlaylistModel::PlaylistModel(QObject *parent)
|
|
: QAbstractListModel(parent)
|
|
{
|
|
m_imageFormats = QVector<QByteArray>::fromList(QImageReader::supportedImageFormats());
|
|
m_fileSanitizerRegex = QRegularExpression("[&*/:`<>?\\|]");
|
|
setThumbnailCacheLimit(500);
|
|
connect(this, &PlaylistModel::imageLoaded, this, &PlaylistModel::onImageLoaded);
|
|
}
|
|
|
|
int PlaylistModel::rowCount(const QModelIndex & /* parent */) const
|
|
{
|
|
return m_contents.count();
|
|
}
|
|
|
|
int PlaylistModel::columnCount(const QModelIndex & /* parent */) const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
QVariant PlaylistModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (index.column() == 0)
|
|
{
|
|
if (!index.isValid() || index.row() >= m_contents.size() || index.row() < 0)
|
|
return QVariant();
|
|
|
|
switch (role)
|
|
{
|
|
case Qt::DisplayRole:
|
|
case Qt::EditRole:
|
|
case Qt::ToolTipRole:
|
|
return m_contents.at(index.row())["label_noext"];
|
|
case HASH:
|
|
return QVariant::fromValue(m_contents.at(index.row()));
|
|
case THUMBNAIL:
|
|
{
|
|
QPixmap *cachedPreview = m_cache.object(getCurrentTypeThumbnailPath(index));
|
|
if (cachedPreview)
|
|
return *cachedPreview;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags PlaylistModel::flags(const QModelIndex &index) const
|
|
{
|
|
if (!index.isValid())
|
|
return Qt::ItemIsEnabled;
|
|
|
|
return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
|
|
}
|
|
|
|
bool PlaylistModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
if (index.isValid() && role == Qt::EditRole)
|
|
{
|
|
QHash<QString, QString> hash = m_contents.at(index.row());
|
|
|
|
hash["label"] = value.toString();
|
|
hash["label_noext"] = QFileInfo(value.toString()).completeBaseName();
|
|
|
|
m_contents.replace(index.row(), hash);
|
|
emit dataChanged(index, index, { role });
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QVariant PlaylistModel::headerData(int section,
|
|
Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole)
|
|
return QVariant();
|
|
|
|
if (orientation == Qt::Horizontal)
|
|
return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME);
|
|
return section + 1;
|
|
}
|
|
|
|
void PlaylistModel::setThumbnailType(const ThumbnailType type)
|
|
{
|
|
m_thumbnailType = type;
|
|
}
|
|
|
|
void PlaylistModel::setThumbnailCacheLimit(int limit)
|
|
{
|
|
m_cache.setMaxCost(limit * 1024);
|
|
}
|
|
|
|
QString PlaylistModel::getThumbnailPath(const QModelIndex &index,
|
|
QString type) const
|
|
{
|
|
return getThumbnailPath(m_contents.at(index.row()), type);
|
|
}
|
|
|
|
QString PlaylistModel::getPlaylistThumbnailsDir(
|
|
const QString playlistName) const
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_thumbnails = settings->paths.directory_thumbnails;
|
|
return QDir::cleanPath(QString(path_dir_thumbnails)) + "/" + playlistName;
|
|
}
|
|
|
|
bool PlaylistModel::isSupportedImage(const QString path) const
|
|
{
|
|
QByteArray extension;
|
|
QString extensionStr;
|
|
int lastIndex = path.lastIndexOf('.');
|
|
|
|
if (lastIndex >= 0)
|
|
{
|
|
extensionStr = path.mid(lastIndex + 1);
|
|
|
|
if (!extensionStr.isEmpty())
|
|
extension = extensionStr.toLower().toUtf8();
|
|
}
|
|
|
|
if (!extension.isEmpty() && m_imageFormats.contains(extension))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
QString PlaylistModel::getSanitizedThumbnailName(QString label) const
|
|
{
|
|
return label.replace(m_fileSanitizerRegex, "_") + ".png";
|
|
}
|
|
|
|
QString PlaylistModel::getThumbnailPath(const QHash<QString, QString> &hash, QString type) const
|
|
{
|
|
/* use thumbnail widgets to show regular image files */
|
|
if (isSupportedImage(hash["path"]))
|
|
return hash["path"];
|
|
|
|
return getPlaylistThumbnailsDir(hash.value("db_name"))
|
|
+ "/" + type + "/" + getSanitizedThumbnailName(hash["label_noext"]);
|
|
}
|
|
|
|
QString PlaylistModel::getCurrentTypeThumbnailPath(const QModelIndex &index) const
|
|
{
|
|
switch (m_thumbnailType)
|
|
{
|
|
case THUMBNAIL_TYPE_BOXART:
|
|
return getThumbnailPath(index, THUMBNAIL_BOXART);
|
|
case THUMBNAIL_TYPE_SCREENSHOT:
|
|
return getThumbnailPath(index, THUMBNAIL_SCREENSHOT);
|
|
case THUMBNAIL_TYPE_TITLE_SCREEN:
|
|
return getThumbnailPath(index, THUMBNAIL_TITLE);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
void PlaylistModel::reloadThumbnail(const QModelIndex &index)
|
|
{
|
|
if (index.isValid())
|
|
{
|
|
reloadThumbnailPath(getCurrentTypeThumbnailPath(index));
|
|
loadThumbnail(index);
|
|
}
|
|
}
|
|
|
|
void PlaylistModel::reloadSystemThumbnails(const QString system)
|
|
{
|
|
int i = 0;
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_thumbnails = settings->paths.directory_thumbnails;
|
|
QString path = QDir::cleanPath(QString(path_dir_thumbnails)) + "/" + system;
|
|
QList<QString> keys = m_cache.keys();
|
|
QList<QString> pending = m_pendingImages.values();
|
|
|
|
for (i = 0; i < keys.size(); i++)
|
|
{
|
|
QString key = keys.at(i);
|
|
if (key.startsWith(path))
|
|
m_cache.remove(key);
|
|
}
|
|
|
|
for (i = 0; i < pending.size(); i++)
|
|
{
|
|
QString key = pending.at(i);
|
|
if (key.startsWith(path))
|
|
m_pendingImages.remove(key);
|
|
}
|
|
}
|
|
|
|
void PlaylistModel::reloadThumbnailPath(const QString path)
|
|
{
|
|
m_cache.remove(path);
|
|
m_pendingImages.remove(path);
|
|
}
|
|
|
|
void PlaylistModel::loadThumbnail(const QModelIndex &index)
|
|
{
|
|
QString path = getCurrentTypeThumbnailPath(index);
|
|
|
|
if (!m_pendingImages.contains(path) && !m_cache.contains(path))
|
|
{
|
|
m_pendingImages.insert(path);
|
|
QtConcurrent::run(this, &PlaylistModel::loadImage, index, path);
|
|
}
|
|
}
|
|
|
|
void PlaylistModel::loadImage(const QModelIndex &index, const QString &path)
|
|
{
|
|
const QImage image = QImage(path);
|
|
if (!image.isNull())
|
|
emit imageLoaded(image, index, path);
|
|
}
|
|
|
|
void PlaylistModel::onImageLoaded(const QImage image, const QModelIndex &index, const QString &path)
|
|
{
|
|
QPixmap *pixmap = new QPixmap(QPixmap::fromImage(image));
|
|
const int cost = pixmap->width() * pixmap->height() * pixmap->depth() / (8 * 1024);
|
|
m_cache.insert(path, pixmap, cost);
|
|
if (index.isValid())
|
|
emit dataChanged(index, index, { THUMBNAIL });
|
|
m_pendingImages.remove(path);
|
|
}
|
|
|
|
static inline bool comp_hash_name_key_lower(const QHash<QString, QString> &lhs, const QHash<QString, QString> &rhs)
|
|
{
|
|
return lhs.value("name").toLower() < rhs.value("name").toLower();
|
|
}
|
|
|
|
bool MainWindow::addDirectoryFilesToList(QProgressDialog *dialog,
|
|
QStringList &list, QDir &dir, QStringList &extensions)
|
|
{
|
|
int i;
|
|
PlaylistEntryDialog *playlistDialog = playlistEntryDialog();
|
|
QStringList dirList = dir.entryList(QStringList(), QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, QDir::Name);
|
|
|
|
for (i = 0; i < dirList.count(); i++)
|
|
{
|
|
QString path(dir.path() + "/" + dirList.at(i));
|
|
QByteArray pathArray = path.toUtf8();
|
|
QFileInfo fileInfo(path);
|
|
const char *pathData = pathArray.constData();
|
|
|
|
if (dialog->wasCanceled())
|
|
return false;
|
|
|
|
/* Needed to update progress dialog while doing
|
|
* a lot of stuff on the main thread. */
|
|
if (i % 25 == 0)
|
|
qApp->processEvents();
|
|
|
|
if (fileInfo.isDir())
|
|
{
|
|
QDir fileInfoDir(path);
|
|
bool success = addDirectoryFilesToList(
|
|
dialog, list, fileInfoDir, extensions);
|
|
|
|
if (!success)
|
|
return false;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (fileInfo.isFile())
|
|
{
|
|
bool add = false;
|
|
|
|
if (extensions.isEmpty())
|
|
add = true;
|
|
else
|
|
{
|
|
if (extensions.contains(fileInfo.suffix()))
|
|
add = true;
|
|
else
|
|
{
|
|
if (path_is_compressed_file(pathData))
|
|
{
|
|
struct string_list *archive_list =
|
|
file_archive_get_file_list(pathData, NULL);
|
|
|
|
if (archive_list)
|
|
{
|
|
if (archive_list->size == 1)
|
|
{
|
|
/* Assume archives with one file should have
|
|
* that file loaded directly.
|
|
* Don't just extend this to add all files
|
|
* in a ZIP, because we might hit something like
|
|
* MAME/FBA where only the archives themselves
|
|
* are valid content. */
|
|
pathArray = (QString(pathData) + "#"
|
|
+ archive_list->elems[0].data).toUtf8();
|
|
pathData = pathArray.constData();
|
|
|
|
if (!extensions.isEmpty() && playlistDialog->filterInArchive())
|
|
{
|
|
/* If the user chose to filter extensions
|
|
* inside archives, and this particular file
|
|
* inside the archive
|
|
* doesn't have one of the chosen extensions,
|
|
* then we skip it. */
|
|
if (extensions.contains(QFileInfo(pathData).suffix()))
|
|
add = true;
|
|
}
|
|
}
|
|
|
|
string_list_free(archive_list);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (add)
|
|
list.append(fileInfo.absoluteFilePath());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::onPlaylistFilesDropped(QStringList files)
|
|
{
|
|
addFilesToPlaylist(files);
|
|
}
|
|
|
|
/* Takes a list of files and folders and adds them to the
|
|
* currently selected playlist. Folders will have their
|
|
* contents added recursively. */
|
|
void MainWindow::addFilesToPlaylist(QStringList files)
|
|
{
|
|
int i;
|
|
QStringList list;
|
|
QString currentPlaylistPath;
|
|
QByteArray currentPlaylistArray;
|
|
QScopedPointer<QProgressDialog> dialog(NULL);
|
|
QHash<QString, QString> selectedCore;
|
|
QHash<QString, QString> itemToAdd;
|
|
QString selectedDatabase;
|
|
QString selectedName;
|
|
QString selectedPath;
|
|
QStringList selectedExtensions;
|
|
playlist_config_t playlist_config;
|
|
QListWidgetItem *currentItem = m_listWidget->currentItem();
|
|
PlaylistEntryDialog *playlistDialog = playlistEntryDialog();
|
|
const char *currentPlaylistData = NULL;
|
|
playlist_t *playlist = NULL;
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
playlist_config.capacity = COLLECTION_SIZE;
|
|
playlist_config.old_format = settings->bools.playlist_use_old_format;
|
|
playlist_config.compress = settings->bools.playlist_compression;
|
|
playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
|
|
playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
|
|
|
|
/* Assume a blank list means we will manually enter in all fields. */
|
|
if (files.isEmpty())
|
|
{
|
|
/* Make sure hash isn't blank, that would mean there's
|
|
* multiple entries to add at once. */
|
|
itemToAdd["label"] = "";
|
|
itemToAdd["path"] = "";
|
|
}
|
|
else if (files.count() == 1)
|
|
{
|
|
QString path = files.at(0);
|
|
QFileInfo info(path);
|
|
|
|
if (info.isFile())
|
|
{
|
|
itemToAdd["label"] = info.completeBaseName();
|
|
itemToAdd["path"] = path;
|
|
}
|
|
}
|
|
|
|
if (currentItem)
|
|
{
|
|
currentPlaylistPath = currentItem->data(Qt::UserRole).toString();
|
|
|
|
if (!currentPlaylistPath.isEmpty())
|
|
{
|
|
currentPlaylistArray = currentPlaylistPath.toUtf8();
|
|
currentPlaylistData = currentPlaylistArray.constData();
|
|
}
|
|
}
|
|
|
|
if (currentPlaylistPath == ALL_PLAYLISTS_TOKEN)
|
|
{
|
|
showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CANNOT_ADD_TO_ALL_PLAYLISTS), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
|
|
return;
|
|
}
|
|
|
|
/* a blank itemToAdd means there will be multiple */
|
|
if (!playlistDialog->showDialog(itemToAdd))
|
|
return;
|
|
|
|
selectedName = m_playlistEntryDialog->getSelectedName();
|
|
selectedPath = m_playlistEntryDialog->getSelectedPath();
|
|
selectedCore = m_playlistEntryDialog->getSelectedCore();
|
|
selectedDatabase = m_playlistEntryDialog->getSelectedDatabase();
|
|
selectedExtensions = m_playlistEntryDialog->getSelectedExtensions();
|
|
|
|
if (!selectedExtensions.isEmpty())
|
|
selectedExtensions.replaceInStrings(QRegularExpression("^\\."), "");
|
|
|
|
if (selectedDatabase.isEmpty())
|
|
selectedDatabase = QFileInfo(currentPlaylistPath).fileName();
|
|
else
|
|
selectedDatabase.append(".lpl");
|
|
|
|
dialog.reset(new QProgressDialog(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_GATHERING_LIST_OF_FILES), "Cancel", 0, 0, this));
|
|
dialog->setWindowModality(Qt::ApplicationModal);
|
|
dialog->show();
|
|
|
|
qApp->processEvents();
|
|
|
|
if ( selectedName.isEmpty()
|
|
|| selectedPath.isEmpty()
|
|
|| selectedDatabase.isEmpty())
|
|
{
|
|
showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLEASE_FILL_OUT_REQUIRED_FIELDS), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
|
|
return;
|
|
}
|
|
|
|
if (files.isEmpty())
|
|
files.append(selectedPath);
|
|
|
|
for (i = 0; i < files.count(); i++)
|
|
{
|
|
QString path(files.at(i));
|
|
QFileInfo fileInfo(path);
|
|
|
|
if (dialog->wasCanceled())
|
|
return;
|
|
|
|
/* Needed to update progress dialog while
|
|
* doing a lot of stuff on the main thread. */
|
|
if (i % 25 == 0)
|
|
qApp->processEvents();
|
|
|
|
if (fileInfo.isDir())
|
|
{
|
|
QDir dir(path);
|
|
bool success = addDirectoryFilesToList(
|
|
dialog.data(), list, dir, selectedExtensions);
|
|
|
|
if (!success)
|
|
return;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (fileInfo.isFile())
|
|
{
|
|
bool add = false;
|
|
|
|
if (selectedExtensions.isEmpty())
|
|
add = true;
|
|
else
|
|
{
|
|
QByteArray pathArray = path.toUtf8();
|
|
const char *pathData = pathArray.constData();
|
|
|
|
if (selectedExtensions.contains(fileInfo.suffix()))
|
|
add = true;
|
|
else if (playlistDialog->filterInArchive()
|
|
&& path_is_compressed_file(pathData))
|
|
{
|
|
/* We'll add it here, but really just delay
|
|
* the check until later when the archive
|
|
* contents are iterated. */
|
|
add = true;
|
|
}
|
|
}
|
|
|
|
if (add)
|
|
list.append(fileInfo.absoluteFilePath());
|
|
}
|
|
else if (files.count() == 1)
|
|
{
|
|
/* If adding a single file, tell user that it doesn't exist. */
|
|
showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
dialog->setLabelText(msg_hash_to_str(
|
|
MENU_ENUM_LABEL_VALUE_QT_ADDING_FILES_TO_PLAYLIST));
|
|
dialog->setMaximum(list.count());
|
|
|
|
playlist_config_set_path(&playlist_config, currentPlaylistData);
|
|
playlist = playlist_init(&playlist_config);
|
|
|
|
for (i = 0; i < list.count(); i++)
|
|
{
|
|
QFileInfo fileInfo;
|
|
QByteArray fileBaseNameArray;
|
|
QByteArray pathArray;
|
|
QByteArray corePathArray;
|
|
QByteArray coreNameArray;
|
|
QByteArray databaseArray;
|
|
QString fileName = list.at(i);
|
|
const char *pathData = NULL;
|
|
const char *fileNameNoExten = NULL;
|
|
const char *corePathData = NULL;
|
|
const char *coreNameData = NULL;
|
|
const char *databaseData = NULL;
|
|
|
|
/* Cancel out of everything, the
|
|
* current progress will not be written
|
|
* to the playlist at all. */
|
|
if (dialog->wasCanceled())
|
|
{
|
|
playlist_free(playlist);
|
|
return;
|
|
}
|
|
|
|
if (fileName.isEmpty())
|
|
continue;
|
|
|
|
/* a modal QProgressDialog calls processEvents()
|
|
* automatically in setValue() */
|
|
dialog->setValue(i + 1);
|
|
|
|
fileInfo = fileName;
|
|
|
|
/* Make sure we're looking at a user-specified field
|
|
* and not just "<multiple>"
|
|
* in case it was a folder with one file in it */
|
|
if ( files.count() == 1
|
|
&& list.count() == 1
|
|
&& i == 0
|
|
&& playlistDialog->nameFieldEnabled())
|
|
{
|
|
fileBaseNameArray = selectedName.toUtf8();
|
|
pathArray = QDir::toNativeSeparators(selectedPath).toUtf8();
|
|
}
|
|
/* Otherwise just use the file name itself (minus extension)
|
|
* for the playlist entry title */
|
|
else
|
|
{
|
|
fileBaseNameArray = fileInfo.completeBaseName().toUtf8();
|
|
pathArray = QDir::toNativeSeparators(fileName).toUtf8();
|
|
}
|
|
|
|
fileNameNoExten = fileBaseNameArray.constData();
|
|
|
|
pathData = pathArray.constData();
|
|
|
|
if (selectedCore.isEmpty())
|
|
{
|
|
corePathData = "DETECT";
|
|
coreNameData = "DETECT";
|
|
}
|
|
else
|
|
{
|
|
corePathArray = QDir::toNativeSeparators(
|
|
selectedCore.value("core_path")).toUtf8();
|
|
coreNameArray = selectedCore.value("core_name").toUtf8();
|
|
corePathData = corePathArray.constData();
|
|
coreNameData = coreNameArray.constData();
|
|
}
|
|
|
|
databaseArray = selectedDatabase.toUtf8();
|
|
databaseData = databaseArray.constData();
|
|
|
|
if (path_is_compressed_file(pathData))
|
|
{
|
|
struct string_list *list = file_archive_get_file_list(pathData, NULL);
|
|
|
|
if (list)
|
|
{
|
|
if (list->size == 1)
|
|
{
|
|
/* Assume archives with one file should have that
|
|
* file loaded directly.
|
|
* Don't just extend this to add all files in a zip,
|
|
* because we might hit
|
|
* something like MAME/FBA where only the archives
|
|
* themselves are valid content. */
|
|
pathArray = QDir::toNativeSeparators(QString(pathData)
|
|
+ "#" + list->elems[0].data).toUtf8();
|
|
pathData = pathArray.constData();
|
|
|
|
if ( !selectedExtensions.isEmpty()
|
|
&& playlistDialog->filterInArchive())
|
|
{
|
|
/* If the user chose to filter extensions inside archives,
|
|
* and this particular file inside the archive
|
|
* doesn't have one of the chosen extensions,
|
|
* then we skip it. */
|
|
if (!selectedExtensions.contains(
|
|
QFileInfo(pathData).suffix()))
|
|
{
|
|
string_list_free(list);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
string_list_free(list);
|
|
}
|
|
}
|
|
|
|
{
|
|
struct playlist_entry entry = {0};
|
|
|
|
/* the push function reads our entry as const,
|
|
* so these casts are safe */
|
|
entry.path = const_cast<char*>(pathData);
|
|
entry.label = const_cast<char*>(fileNameNoExten);
|
|
entry.core_path = const_cast<char*>(corePathData);
|
|
entry.core_name = const_cast<char*>(coreNameData);
|
|
entry.crc32 = const_cast<char*>("00000000|crc");
|
|
entry.db_name = const_cast<char*>(databaseData);
|
|
|
|
playlist_push(playlist, &entry);
|
|
}
|
|
}
|
|
|
|
playlist_write_file(playlist);
|
|
playlist_free(playlist);
|
|
|
|
reloadPlaylists();
|
|
}
|
|
|
|
bool MainWindow::updateCurrentPlaylistEntry(
|
|
const QHash<QString, QString> &contentHash)
|
|
{
|
|
QString path;
|
|
QString label;
|
|
QString corePath;
|
|
QString coreName;
|
|
QString dbName;
|
|
QString crc32;
|
|
QByteArray playlistPathArray;
|
|
QByteArray pathArray;
|
|
QByteArray labelArray;
|
|
QByteArray corePathArray;
|
|
QByteArray coreNameArray;
|
|
QByteArray dbNameArray;
|
|
QByteArray crc32Array;
|
|
playlist_config_t playlist_config;
|
|
QString playlistPath = getCurrentPlaylistPath();
|
|
const char *playlistPathData = NULL;
|
|
const char *pathData = NULL;
|
|
const char *labelData = NULL;
|
|
const char *corePathData = NULL;
|
|
const char *coreNameData = NULL;
|
|
const char *dbNameData = NULL;
|
|
const char *crc32Data = NULL;
|
|
playlist_t *playlist = NULL;
|
|
unsigned index = 0;
|
|
bool ok = false;
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
playlist_config.capacity = COLLECTION_SIZE;
|
|
playlist_config.old_format = settings->bools.playlist_use_old_format;
|
|
playlist_config.compress = settings->bools.playlist_compression;
|
|
playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
|
|
playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
|
|
|
|
if ( playlistPath.isEmpty()
|
|
|| contentHash.isEmpty()
|
|
|| !contentHash.contains("index"))
|
|
return false;
|
|
|
|
index = contentHash.value("index").toUInt(&ok);
|
|
|
|
if (!ok)
|
|
return false;
|
|
|
|
path = contentHash.value("path");
|
|
label = contentHash.value("label");
|
|
coreName = contentHash.value("core_name");
|
|
corePath = contentHash.value("core_path");
|
|
dbName = contentHash.value("db_name");
|
|
crc32 = contentHash.value("crc32");
|
|
|
|
if ( path.isEmpty()
|
|
|| label.isEmpty()
|
|
|| coreName.isEmpty()
|
|
|| corePath.isEmpty()
|
|
)
|
|
return false;
|
|
|
|
playlistPathArray = playlistPath.toUtf8();
|
|
pathArray = QDir::toNativeSeparators(path).toUtf8();
|
|
labelArray = label.toUtf8();
|
|
coreNameArray = coreName.toUtf8();
|
|
corePathArray = QDir::toNativeSeparators(corePath).toUtf8();
|
|
|
|
if (!dbName.isEmpty())
|
|
{
|
|
dbNameArray = (dbName + ".lpl").toUtf8();
|
|
dbNameData = dbNameArray.constData();
|
|
}
|
|
|
|
playlistPathData = playlistPathArray.constData();
|
|
pathData = pathArray.constData();
|
|
labelData = labelArray.constData();
|
|
coreNameData = coreNameArray.constData();
|
|
corePathData = corePathArray.constData();
|
|
|
|
if (!crc32.isEmpty())
|
|
{
|
|
crc32Array = crc32.toUtf8();
|
|
crc32Data = crc32Array.constData();
|
|
}
|
|
|
|
if (path_is_compressed_file(pathData))
|
|
{
|
|
struct string_list *list = file_archive_get_file_list(pathData, NULL);
|
|
|
|
if (list)
|
|
{
|
|
if (list->size == 1)
|
|
{
|
|
/* assume archives with one file should have that file loaded directly */
|
|
pathArray = QDir::toNativeSeparators(QString(pathData) + "#" + list->elems[0].data).toUtf8();
|
|
pathData = pathArray.constData();
|
|
}
|
|
|
|
string_list_free(list);
|
|
}
|
|
}
|
|
|
|
playlist_config_set_path(&playlist_config, playlistPathData);
|
|
playlist = playlist_init(&playlist_config);
|
|
|
|
{
|
|
struct playlist_entry entry = {0};
|
|
|
|
/* the update function reads our entry as const, so these casts are safe */
|
|
entry.path = const_cast<char*>(pathData);
|
|
entry.label = const_cast<char*>(labelData);
|
|
entry.core_path = const_cast<char*>(corePathData);
|
|
entry.core_name = const_cast<char*>(coreNameData);
|
|
entry.crc32 = const_cast<char*>(crc32Data);
|
|
entry.db_name = const_cast<char*>(dbNameData);
|
|
|
|
playlist_update(playlist, index, &entry);
|
|
}
|
|
|
|
playlist_write_file(playlist);
|
|
playlist_free(playlist);
|
|
|
|
reloadPlaylists();
|
|
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&)
|
|
{
|
|
QString currentPlaylistDirPath;
|
|
QString currentPlaylistPath;
|
|
QString currentPlaylistFileName;
|
|
QFile currentPlaylistFile;
|
|
QFileInfo currentPlaylistFileInfo;
|
|
QMap<QString, const core_info_t*> coreList;
|
|
QScopedPointer<QMenu> menu;
|
|
QScopedPointer<QMenu> associateMenu;
|
|
QScopedPointer<QMenu> hiddenPlaylistsMenu;
|
|
QScopedPointer<QMenu> downloadAllThumbnailsMenu;
|
|
QScopedPointer<QAction> hideAction;
|
|
QScopedPointer<QAction> newPlaylistAction;
|
|
QScopedPointer<QAction> deletePlaylistAction;
|
|
QScopedPointer<QAction> renamePlaylistAction;
|
|
QScopedPointer<QAction> downloadAllThumbnailsEntireSystemAction;
|
|
QScopedPointer<QAction> downloadAllThumbnailsThisPlaylistAction;
|
|
QPointer<QAction> selectedAction;
|
|
playlist_config_t playlist_config;
|
|
QPoint cursorPos = QCursor::pos();
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_playlist = settings->paths.directory_playlist;
|
|
QDir playlistDir(path_dir_playlist);
|
|
QListWidgetItem *selectedItem = m_listWidget->itemAt(
|
|
m_listWidget->viewport()->mapFromGlobal(cursorPos));
|
|
QString playlistDirAbsPath = playlistDir.absolutePath();
|
|
core_info_list_t *core_info_list = NULL;
|
|
unsigned i = 0;
|
|
int j = 0;
|
|
bool specialPlaylist = false;
|
|
bool foundHiddenPlaylist = false;
|
|
|
|
playlist_config.capacity = COLLECTION_SIZE;
|
|
playlist_config.old_format = settings->bools.playlist_use_old_format;
|
|
playlist_config.compress = settings->bools.playlist_compression;
|
|
playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
|
|
playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
|
|
|
|
if (selectedItem)
|
|
{
|
|
currentPlaylistPath = selectedItem->data(Qt::UserRole).toString();
|
|
currentPlaylistFile.setFileName(currentPlaylistPath);
|
|
|
|
currentPlaylistFileInfo = QFileInfo(currentPlaylistPath);
|
|
currentPlaylistFileName = currentPlaylistFileInfo.fileName();
|
|
currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath();
|
|
}
|
|
|
|
menu.reset(new QMenu(this));
|
|
menu->setObjectName("menu");
|
|
|
|
hiddenPlaylistsMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS), this));
|
|
newPlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST)) + "...", this));
|
|
|
|
hiddenPlaylistsMenu->setObjectName("hiddenPlaylistsMenu");
|
|
|
|
menu->addAction(newPlaylistAction.data());
|
|
|
|
if (currentPlaylistFile.exists())
|
|
{
|
|
deletePlaylistAction.reset(new QAction(
|
|
QString(msg_hash_to_str(
|
|
MENU_ENUM_LABEL_VALUE_QT_DELETE_PLAYLIST)) + "...",
|
|
this));
|
|
menu->addAction(deletePlaylistAction.data());
|
|
|
|
renamePlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RENAME_PLAYLIST)) + "...", this));
|
|
menu->addAction(renamePlaylistAction.data());
|
|
}
|
|
|
|
if (selectedItem)
|
|
{
|
|
hideAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDE), this));
|
|
menu->addAction(hideAction.data());
|
|
}
|
|
|
|
for (j = 0; j < m_listWidget->count(); j++)
|
|
{
|
|
QListWidgetItem *item = m_listWidget->item(j);
|
|
bool hidden = m_listWidget->isItemHidden(item);
|
|
|
|
if (hidden)
|
|
{
|
|
QAction *action = hiddenPlaylistsMenu->addAction(item->text());
|
|
action->setProperty("row", j);
|
|
action->setProperty("core_path", item->data(Qt::UserRole).toString());
|
|
foundHiddenPlaylist = true;
|
|
}
|
|
}
|
|
|
|
if (!foundHiddenPlaylist)
|
|
{
|
|
QAction *action = hiddenPlaylistsMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE));
|
|
action->setProperty("row", -1);
|
|
}
|
|
|
|
menu->addMenu(hiddenPlaylistsMenu.data());
|
|
|
|
/* Don't just compare strings in case there are case differences on Windows that should be ignored.
|
|
special playlists like history etc. can't have an association
|
|
*/
|
|
if (QDir(currentPlaylistDirPath) != QDir(playlistDirAbsPath))
|
|
specialPlaylist = true;
|
|
|
|
if (!specialPlaylist)
|
|
{
|
|
associateMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ASSOCIATE_CORE), this));
|
|
associateMenu->setObjectName("associateMenu");
|
|
|
|
core_info_get_list(&core_info_list);
|
|
|
|
for (i = 0; i < core_info_list->count && core_info_list->count > 0; i++)
|
|
{
|
|
const core_info_t *core = &core_info_list->list[i];
|
|
coreList[core->core_name] = core;
|
|
}
|
|
|
|
{
|
|
QMapIterator<QString, const core_info_t*> coreListIterator(coreList);
|
|
QVector<QHash<QString, QString> > cores;
|
|
|
|
while (coreListIterator.hasNext())
|
|
{
|
|
QString key, name;
|
|
const core_info_t *core = NULL;
|
|
QHash<QString, QString> hash;
|
|
|
|
coreListIterator.next();
|
|
|
|
key = coreListIterator.key();
|
|
core = coreList.value(key);
|
|
|
|
if (string_is_empty(core->core_name))
|
|
name = core->display_name;
|
|
else
|
|
name = core->core_name;
|
|
|
|
if (name.isEmpty())
|
|
continue;
|
|
|
|
hash["name"] = name;
|
|
hash["core_path"] = core->path;
|
|
|
|
cores.append(hash);
|
|
}
|
|
|
|
std::sort(cores.begin(), cores.end(), comp_hash_name_key_lower);
|
|
|
|
for (j = 0; j < cores.count(); j++)
|
|
{
|
|
const QHash<QString, QString> &hash = cores.at(j);
|
|
QAction *action = associateMenu->addAction(hash.value("name"));
|
|
|
|
action->setProperty("core_path", hash.value("core_path"));
|
|
}
|
|
}
|
|
|
|
menu->addMenu(associateMenu.data());
|
|
}
|
|
|
|
if (!specialPlaylist)
|
|
{
|
|
downloadAllThumbnailsMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS), this));
|
|
downloadAllThumbnailsMenu->setObjectName("downloadAllThumbnailsMenu");
|
|
|
|
downloadAllThumbnailsThisPlaylistAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS_THIS_PLAYLIST), downloadAllThumbnailsMenu.data()));
|
|
downloadAllThumbnailsEntireSystemAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS_ENTIRE_SYSTEM), downloadAllThumbnailsMenu.data()));
|
|
|
|
downloadAllThumbnailsMenu->addAction(downloadAllThumbnailsThisPlaylistAction.data());
|
|
downloadAllThumbnailsMenu->addAction(downloadAllThumbnailsEntireSystemAction.data());
|
|
|
|
menu->addMenu(downloadAllThumbnailsMenu.data());
|
|
}
|
|
|
|
selectedAction = menu->exec(cursorPos);
|
|
|
|
if (!selectedAction)
|
|
return;
|
|
|
|
if (!specialPlaylist && selectedAction->parent() == associateMenu.data())
|
|
{
|
|
core_info_t *coreInfo = NULL;
|
|
playlist_t *cachedPlaylist = playlist_get_cached();
|
|
playlist_t *playlist = NULL;
|
|
bool loadPlaylist = true;
|
|
QByteArray currentPlaylistPathByteArray = currentPlaylistPath.toUtf8();
|
|
const char *currentPlaylistPathCString = currentPlaylistPathByteArray.data();
|
|
QByteArray corePathByteArray = selectedAction->property("core_path").toString().toUtf8();
|
|
const char *corePath = corePathByteArray.data();
|
|
|
|
/* Load playlist, if required */
|
|
if (cachedPlaylist)
|
|
{
|
|
if (string_is_equal(currentPlaylistPathCString,
|
|
playlist_get_conf_path(cachedPlaylist)))
|
|
{
|
|
playlist = cachedPlaylist;
|
|
loadPlaylist = false;
|
|
}
|
|
}
|
|
|
|
if (loadPlaylist)
|
|
{
|
|
playlist_config_set_path(&playlist_config, currentPlaylistPathCString);
|
|
playlist = playlist_init(&playlist_config);
|
|
}
|
|
|
|
if (playlist)
|
|
{
|
|
/* Get core info */
|
|
if (core_info_find(corePath, &coreInfo))
|
|
{
|
|
/* Set new core association */
|
|
playlist_set_default_core_path(playlist, coreInfo->path);
|
|
playlist_set_default_core_name(playlist, coreInfo->display_name);
|
|
}
|
|
else
|
|
{
|
|
playlist_set_default_core_path(playlist, "DETECT");
|
|
playlist_set_default_core_name(playlist, "DETECT");
|
|
}
|
|
|
|
/* Write changes to disk */
|
|
playlist_write_file(playlist);
|
|
|
|
/* Free playlist, if required */
|
|
if (loadPlaylist)
|
|
playlist_free(playlist);
|
|
}
|
|
}
|
|
else if (selectedItem && selectedAction == deletePlaylistAction.data())
|
|
{
|
|
if (currentPlaylistFile.exists())
|
|
{
|
|
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))
|
|
{
|
|
if (currentPlaylistFile.remove())
|
|
reloadPlaylists();
|
|
else
|
|
showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
|
|
}
|
|
}
|
|
}
|
|
else if (selectedItem && selectedAction == renamePlaylistAction.data())
|
|
{
|
|
if (currentPlaylistFile.exists())
|
|
{
|
|
QString oldName = selectedItem->text();
|
|
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);
|
|
|
|
if (!name.isEmpty())
|
|
{
|
|
renamePlaylistItem(selectedItem, name);
|
|
reloadPlaylists();
|
|
}
|
|
}
|
|
}
|
|
else if (selectedAction == newPlaylistAction.data())
|
|
{
|
|
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));
|
|
QString newPlaylistPath = playlistDirAbsPath + "/" + name + ".lpl";
|
|
QFile file(newPlaylistPath);
|
|
|
|
if (!name.isEmpty())
|
|
{
|
|
if (file.open(QIODevice::WriteOnly))
|
|
file.close();
|
|
|
|
reloadPlaylists();
|
|
}
|
|
}
|
|
else if (selectedItem && selectedAction == hideAction.data())
|
|
{
|
|
int row = m_listWidget->row(selectedItem);
|
|
|
|
if (row >= 0)
|
|
{
|
|
QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList();
|
|
|
|
if (!hiddenPlaylists.contains(currentPlaylistFileName))
|
|
{
|
|
hiddenPlaylists.append(currentPlaylistFileName);
|
|
m_settings->setValue("hidden_playlists", hiddenPlaylists);
|
|
}
|
|
|
|
m_listWidget->setRowHidden(row, true);
|
|
}
|
|
}
|
|
else if (selectedAction->parent() == hiddenPlaylistsMenu.data())
|
|
{
|
|
QVariant rowVariant = selectedAction->property("row");
|
|
|
|
if (rowVariant.isValid())
|
|
{
|
|
QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList();
|
|
int row = rowVariant.toInt();
|
|
|
|
if (row >= 0)
|
|
{
|
|
QString playlistPath = selectedAction->property("core_path").toString();
|
|
QFileInfo playlistFileInfo(playlistPath);
|
|
QString playlistFileName = playlistFileInfo.fileName();
|
|
|
|
if (hiddenPlaylists.contains(playlistFileName))
|
|
{
|
|
hiddenPlaylists.removeOne(playlistFileName);
|
|
m_settings->setValue("hidden_playlists", hiddenPlaylists);
|
|
}
|
|
|
|
m_listWidget->setRowHidden(row, false);
|
|
}
|
|
}
|
|
}
|
|
else if (selectedItem && !specialPlaylist && selectedAction->parent() == downloadAllThumbnailsMenu.data())
|
|
{
|
|
if (selectedAction == downloadAllThumbnailsEntireSystemAction.data())
|
|
{
|
|
int row = m_listWidget->row(selectedItem);
|
|
|
|
if (row >= 0)
|
|
downloadAllThumbnails(currentPlaylistFileInfo.completeBaseName());
|
|
}
|
|
else if (selectedAction == downloadAllThumbnailsThisPlaylistAction.data())
|
|
downloadPlaylistThumbnails(currentPlaylistPath);
|
|
}
|
|
|
|
setCoreActions();
|
|
}
|
|
|
|
void MainWindow::deferReloadPlaylists()
|
|
{
|
|
emit gotReloadPlaylists();
|
|
}
|
|
|
|
void MainWindow::onGotReloadPlaylists()
|
|
{
|
|
reloadPlaylists();
|
|
}
|
|
|
|
void MainWindow::reloadPlaylists()
|
|
{
|
|
int i = 0;
|
|
QString currentPlaylistPath;
|
|
QListWidgetItem *allPlaylistsItem = NULL;
|
|
QListWidgetItem *favoritesPlaylistsItem = NULL;
|
|
QListWidgetItem *imagePlaylistsItem = NULL;
|
|
QListWidgetItem *musicPlaylistsItem = NULL;
|
|
QListWidgetItem *videoPlaylistsItem = NULL;
|
|
QListWidgetItem *firstItem = NULL;
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_playlist = settings->paths.directory_playlist;
|
|
QDir playlistDir(path_dir_playlist);
|
|
QStringList hiddenPlaylists = m_settings->value(
|
|
"hidden_playlists").toStringList();
|
|
|
|
QListWidgetItem *currentItem = m_listWidget->currentItem();
|
|
|
|
if (currentItem)
|
|
currentPlaylistPath = currentItem->data(Qt::UserRole).toString();
|
|
|
|
getPlaylistFiles();
|
|
|
|
m_listWidget->clear();
|
|
m_listWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
m_listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
m_listWidget->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed);
|
|
|
|
allPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS));
|
|
allPlaylistsItem->setData(Qt::UserRole, ALL_PLAYLISTS_TOKEN);
|
|
|
|
favoritesPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES_TAB));
|
|
favoritesPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_favorites);
|
|
|
|
m_historyPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_HISTORY_TAB));
|
|
m_historyPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_history);
|
|
|
|
imagePlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_IMAGES_TAB));
|
|
imagePlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_image_history);
|
|
|
|
musicPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MUSIC_TAB));
|
|
musicPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_music_history);
|
|
|
|
videoPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_TAB));
|
|
videoPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_video_history);
|
|
|
|
m_listWidget->addItem(allPlaylistsItem);
|
|
m_listWidget->addItem(favoritesPlaylistsItem);
|
|
m_listWidget->addItem(m_historyPlaylistsItem);
|
|
m_listWidget->addItem(imagePlaylistsItem);
|
|
m_listWidget->addItem(musicPlaylistsItem);
|
|
m_listWidget->addItem(videoPlaylistsItem);
|
|
|
|
if (hiddenPlaylists.contains(ALL_PLAYLISTS_TOKEN))
|
|
m_listWidget->setRowHidden(m_listWidget->row(allPlaylistsItem), true);
|
|
if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_favorites).fileName()))
|
|
m_listWidget->setRowHidden(m_listWidget->row(favoritesPlaylistsItem), true);
|
|
if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_history).fileName()))
|
|
m_listWidget->setRowHidden(m_listWidget->row(m_historyPlaylistsItem), true);
|
|
if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_image_history).fileName()))
|
|
m_listWidget->setRowHidden(m_listWidget->row(imagePlaylistsItem), true);
|
|
if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_music_history).fileName()))
|
|
m_listWidget->setRowHidden(m_listWidget->row(musicPlaylistsItem), true);
|
|
if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_video_history).fileName()))
|
|
m_listWidget->setRowHidden(m_listWidget->row(videoPlaylistsItem), true);
|
|
|
|
for (i = 0; i < m_playlistFiles.count(); i++)
|
|
{
|
|
QIcon icon;
|
|
QString iconPath;
|
|
QListWidgetItem *item = NULL;
|
|
const QString &file = m_playlistFiles.at(i);
|
|
|
|
/* don't show view files */
|
|
if (file.endsWith(".lvw", Qt::CaseInsensitive))
|
|
continue;
|
|
|
|
QString fileDisplayName = file;
|
|
QString fileName = file;
|
|
bool hasIcon = false;
|
|
|
|
fileDisplayName.remove(".lpl");
|
|
|
|
iconPath = QString(
|
|
settings->paths.directory_assets)
|
|
+ ICON_PATH
|
|
+ fileDisplayName
|
|
+ ".png";
|
|
|
|
hasIcon = QFile::exists(iconPath);
|
|
|
|
if (hasIcon)
|
|
icon = QIcon(iconPath);
|
|
else
|
|
icon = m_folderIcon;
|
|
|
|
item = new QListWidgetItem(icon, fileDisplayName);
|
|
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
|
item->setData(Qt::UserRole, playlistDir.absoluteFilePath(file));
|
|
|
|
m_listWidget->addItem(item);
|
|
|
|
if (hiddenPlaylists.contains(fileName))
|
|
{
|
|
int row = m_listWidget->row(item);
|
|
|
|
if (row >= 0)
|
|
m_listWidget->setRowHidden(row, true);
|
|
}
|
|
}
|
|
|
|
if (m_listWidget->count() > 0)
|
|
{
|
|
firstItem = m_listWidget->item(0);
|
|
|
|
if (firstItem)
|
|
{
|
|
bool foundCurrent = false;
|
|
bool foundInitial = false;
|
|
QString initialPlaylist = m_settings->value("initial_playlist", m_historyPlaylistsItem->data(Qt::UserRole).toString()).toString();
|
|
QListWidgetItem *initialItem = NULL;
|
|
|
|
for (i = 0; i < m_listWidget->count(); i++)
|
|
{
|
|
QString path;
|
|
QListWidgetItem *item = m_listWidget->item(i);
|
|
|
|
if (item)
|
|
{
|
|
path = item->data(Qt::UserRole).toString();
|
|
|
|
if (!path.isEmpty())
|
|
{
|
|
/* don't break early here since we want
|
|
* to make sure we've found both initial
|
|
* and current items if they exist */
|
|
if (!foundInitial && path == initialPlaylist)
|
|
{
|
|
foundInitial = true;
|
|
initialItem = item;
|
|
}
|
|
if ( !foundCurrent
|
|
&& !currentPlaylistPath.isEmpty()
|
|
&& path == currentPlaylistPath)
|
|
{
|
|
foundCurrent = true;
|
|
m_listWidget->setCurrentItem(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundCurrent)
|
|
{
|
|
if (foundInitial && initialItem)
|
|
m_listWidget->setCurrentItem(initialItem);
|
|
else
|
|
{
|
|
/* the previous playlist must be gone now,
|
|
* just select the first one */
|
|
m_listWidget->setCurrentItem(firstItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
QString MainWindow::getCurrentPlaylistPath()
|
|
{
|
|
QString playlistPath;
|
|
QListWidgetItem *playlistItem = m_listWidget->currentItem();
|
|
|
|
if (!playlistItem)
|
|
return playlistPath;
|
|
|
|
playlistPath = playlistItem->data(Qt::UserRole).toString();
|
|
|
|
return playlistPath;
|
|
}
|
|
|
|
bool MainWindow::currentPlaylistIsSpecial()
|
|
{
|
|
QFileInfo currentPlaylistFileInfo;
|
|
QString currentPlaylistPath;
|
|
QString currentPlaylistDirPath;
|
|
settings_t *settings = config_get_ptr();
|
|
QDir playlistDir(settings->paths.directory_playlist);
|
|
QString playlistDirAbsPath = playlistDir.absolutePath();
|
|
QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem();
|
|
|
|
if (!currentPlaylistItem)
|
|
return false;
|
|
|
|
currentPlaylistPath = currentPlaylistItem->data(Qt::UserRole).toString();
|
|
currentPlaylistFileInfo = QFileInfo(currentPlaylistPath);
|
|
currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath();
|
|
|
|
/* Don't just compare strings in case there are
|
|
* case differences on Windows that should be ignored. */
|
|
if (QDir(currentPlaylistDirPath) != QDir(playlistDirAbsPath))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool MainWindow::currentPlaylistIsAll()
|
|
{
|
|
QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem();
|
|
if (
|
|
currentPlaylistItem
|
|
&& currentPlaylistItem->data(Qt::UserRole).toString()
|
|
== ALL_PLAYLISTS_TOKEN)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void MainWindow::deleteCurrentPlaylistItem()
|
|
{
|
|
QByteArray playlistArray;
|
|
playlist_config_t playlist_config;
|
|
QString playlistPath = getCurrentPlaylistPath();
|
|
QHash<QString, QString> contentHash = getCurrentContentHash();
|
|
playlist_t *playlist = NULL;
|
|
const char *playlistData = NULL;
|
|
unsigned index = 0;
|
|
bool ok = false;
|
|
bool isAllPlaylist = currentPlaylistIsAll();
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
playlist_config.capacity = COLLECTION_SIZE;
|
|
playlist_config.old_format = settings->bools.playlist_use_old_format;
|
|
playlist_config.compress = settings->bools.playlist_compression;
|
|
playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
|
|
playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
|
|
|
|
if (isAllPlaylist)
|
|
return;
|
|
|
|
if (playlistPath.isEmpty())
|
|
return;
|
|
|
|
if (contentHash.isEmpty())
|
|
return;
|
|
|
|
playlistArray = playlistPath.toUtf8();
|
|
playlistData = playlistArray.constData();
|
|
|
|
index = contentHash.value("index").toUInt(&ok);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
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))
|
|
return;
|
|
|
|
playlist_config_set_path(&playlist_config, playlistData);
|
|
playlist = playlist_init(&playlist_config);
|
|
|
|
playlist_delete_index(playlist, index);
|
|
playlist_write_file(playlist);
|
|
playlist_free(playlist);
|
|
|
|
reloadPlaylists();
|
|
}
|
|
|
|
QString MainWindow::getPlaylistDefaultCore(QString plName)
|
|
{
|
|
size_t len;
|
|
playlist_config_t playlist_config;
|
|
char playlist_path[PATH_MAX_LENGTH];
|
|
QByteArray plNameByteArray = plName.toUtf8();
|
|
const char *plNameCString = plNameByteArray.data();
|
|
playlist_t *cachedPlaylist = playlist_get_cached();
|
|
playlist_t *playlist = NULL;
|
|
bool loadPlaylist = true;
|
|
QString corePath = QString();
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
playlist_config.capacity = COLLECTION_SIZE;
|
|
playlist_config.old_format = settings->bools.playlist_use_old_format;
|
|
playlist_config.compress = settings->bools.playlist_compression;
|
|
playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
|
|
playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
|
|
|
|
if (!settings || string_is_empty(plNameCString))
|
|
return corePath;
|
|
|
|
/* Get playlist path */
|
|
len = fill_pathname_join_special(
|
|
playlist_path, settings->paths.directory_playlist,
|
|
plNameCString, sizeof(playlist_path));
|
|
strlcpy(playlist_path + len,
|
|
".lpl",
|
|
sizeof(playlist_path) - len);
|
|
|
|
/* Load playlist, if required */
|
|
if (cachedPlaylist)
|
|
{
|
|
if (string_is_equal(playlist_path,
|
|
playlist_get_conf_path(cachedPlaylist)))
|
|
{
|
|
playlist = cachedPlaylist;
|
|
loadPlaylist = false;
|
|
}
|
|
}
|
|
|
|
if (loadPlaylist)
|
|
{
|
|
playlist_config_set_path(&playlist_config, playlist_path);
|
|
playlist = playlist_init(&playlist_config);
|
|
}
|
|
|
|
if (playlist)
|
|
{
|
|
const char *defaultCorePath = playlist_get_default_core_path(playlist);
|
|
|
|
/* Get default core path */
|
|
if (!string_is_empty(defaultCorePath) &&
|
|
!string_is_equal(defaultCorePath, "DETECT"))
|
|
corePath = QString::fromUtf8(defaultCorePath);
|
|
|
|
/* Free playlist, if required */
|
|
if (loadPlaylist)
|
|
playlist_free(playlist);
|
|
}
|
|
|
|
return corePath;
|
|
}
|
|
|
|
void MainWindow::getPlaylistFiles()
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
QDir playlistDir(settings->paths.directory_playlist);
|
|
|
|
m_playlistFiles = playlistDir.entryList(
|
|
QDir::NoDotAndDotDot | QDir::Readable | QDir::Files, QDir::Name);
|
|
}
|
|
|
|
void PlaylistModel::getPlaylistItems(QString path)
|
|
{
|
|
QByteArray pathArray;
|
|
playlist_config_t playlist_config;
|
|
const char *pathData = NULL;
|
|
const char *playlistName = NULL;
|
|
playlist_t *playlist = NULL;
|
|
unsigned playlistSize = 0;
|
|
unsigned i = 0;
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
playlist_config.capacity = COLLECTION_SIZE;
|
|
playlist_config.old_format = settings->bools.playlist_use_old_format;
|
|
playlist_config.compress = settings->bools.playlist_compression;
|
|
playlist_config.fuzzy_archive_match = settings->bools.playlist_fuzzy_archive_match;
|
|
playlist_config_set_base_content_directory(&playlist_config, settings->bools.playlist_portable_paths ? settings->paths.directory_menu_content : NULL);
|
|
|
|
pathArray.append(path);
|
|
pathData = pathArray.constData();
|
|
if (!string_is_empty(pathData))
|
|
playlistName = path_basename(pathData);
|
|
|
|
playlist_config_set_path(&playlist_config, pathData);
|
|
playlist = playlist_init(&playlist_config);
|
|
playlistSize = playlist_get_size(playlist);
|
|
|
|
for (i = 0; i < playlistSize; i++)
|
|
{
|
|
QHash<QString, QString> hash;
|
|
const struct playlist_entry *entry = NULL;
|
|
|
|
playlist_get_index(playlist, i, &entry);
|
|
|
|
if (string_is_empty(entry->path))
|
|
continue;
|
|
|
|
hash["path"] = entry->path;
|
|
hash["index"] = QString::number(i);
|
|
|
|
if (string_is_empty(entry->label))
|
|
{
|
|
hash["label"] = entry->path;
|
|
hash["label_noext"] = entry->path;
|
|
}
|
|
else
|
|
{
|
|
hash["label"] = entry->label;
|
|
hash["label_noext"] = entry->label;
|
|
}
|
|
|
|
if (!string_is_empty(entry->core_path))
|
|
hash["core_path"] = entry->core_path;
|
|
|
|
if (!string_is_empty(entry->core_name))
|
|
hash["core_name"] = entry->core_name;
|
|
|
|
if (!string_is_empty(entry->crc32))
|
|
hash["crc32"] = entry->crc32;
|
|
|
|
if (!string_is_empty(entry->db_name))
|
|
{
|
|
hash["db_name"] = entry->db_name;
|
|
hash["db_name"].remove(".lpl");
|
|
}
|
|
|
|
if (!string_is_empty(playlistName))
|
|
{
|
|
hash["pl_name"] = playlistName;
|
|
hash["pl_name"].remove(".lpl");
|
|
}
|
|
|
|
m_contents.append(hash);
|
|
}
|
|
|
|
playlist_free(playlist);
|
|
playlist = NULL;
|
|
}
|
|
|
|
void PlaylistModel::addPlaylistItems(const QStringList &paths, bool add)
|
|
{
|
|
int i;
|
|
|
|
if (paths.isEmpty())
|
|
return;
|
|
|
|
beginResetModel();
|
|
|
|
m_contents.clear();
|
|
|
|
for (i = 0; i < paths.size(); i++)
|
|
getPlaylistItems(paths.at(i));
|
|
|
|
endResetModel();
|
|
}
|
|
|
|
void PlaylistModel::addDir(QString path, QFlags<QDir::Filter> showHidden)
|
|
{
|
|
QDir dir = path;
|
|
int i = 0;
|
|
QStringList dirList =
|
|
dir.entryList(QDir::NoDotAndDotDot |
|
|
QDir::Readable |
|
|
QDir::Files |
|
|
showHidden,
|
|
QDir::Name);
|
|
|
|
if (dirList.count() == 0)
|
|
return;
|
|
|
|
beginResetModel();
|
|
|
|
m_contents.clear();
|
|
|
|
for (i = 0; i < dirList.count(); i++)
|
|
{
|
|
QHash<QString, QString> hash;
|
|
QString fileName = dirList.at(i);
|
|
QString filePath(
|
|
QDir::toNativeSeparators(dir.absoluteFilePath(fileName)));
|
|
QFileInfo fileInfo(filePath);
|
|
|
|
hash["path"] = filePath;
|
|
hash["label"] = hash["path"];
|
|
hash["label_noext"] = fileInfo.completeBaseName();
|
|
hash["db_name"] = fileInfo.dir().dirName();
|
|
|
|
m_contents.append(hash);
|
|
}
|
|
|
|
endResetModel();
|
|
}
|
|
|
|
void MainWindow::setAllPlaylistsListMaxCount(int count)
|
|
{
|
|
if (count < 1)
|
|
count = 0;
|
|
|
|
m_allPlaylistsListMaxCount = count;
|
|
}
|
|
|
|
void MainWindow::setAllPlaylistsGridMaxCount(int count)
|
|
{
|
|
if (count < 1)
|
|
count = 0;
|
|
|
|
m_allPlaylistsGridMaxCount = count;
|
|
}
|