mirror of https://github.com/mgba-emu/mgba.git
Qt: New library system (#549)
This commit is contained in:
parent
3851a8230b
commit
78e4083a56
1
CHANGES
1
CHANGES
|
@ -17,6 +17,7 @@ Features:
|
||||||
- GB: Video/audio channel enabling/disabling
|
- GB: Video/audio channel enabling/disabling
|
||||||
- Add option to lock video to integer scaling
|
- Add option to lock video to integer scaling
|
||||||
- Video log recording for testing and bug reporting
|
- Video log recording for testing and bug reporting
|
||||||
|
- Library view
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- LR35902: Fix core never exiting with certain event patterns
|
- LR35902: Fix core never exiting with certain event patterns
|
||||||
- GB Timer: Improve DIV reset behavior
|
- GB Timer: Improve DIV reset behavior
|
||||||
|
|
|
@ -13,11 +13,12 @@ ArchiveInspector::ArchiveInspector(const QString& filename, QWidget* parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
connect(m_ui.archiveView, &LibraryView::doneLoading, [this]() {
|
connect(m_ui.archiveView, &LibraryController::doneLoading, [this]() {
|
||||||
m_ui.loading->hide();
|
m_ui.loading->hide();
|
||||||
});
|
});
|
||||||
connect(m_ui.archiveView, SIGNAL(accepted()), this, SIGNAL(accepted()));
|
connect(m_ui.archiveView, &LibraryController::startGame, this, &ArchiveInspector::accepted);
|
||||||
m_ui.archiveView->setDirectory(filename);
|
m_ui.archiveView->setViewStyle(LibraryStyle::STYLE_LIST);
|
||||||
|
m_ui.archiveView->addDirectory(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
VFile* ArchiveInspector::selectedVFile() const {
|
VFile* ArchiveInspector::selectedVFile() const {
|
||||||
|
|
|
@ -29,15 +29,15 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="0" column="0" colspan="2">
|
||||||
<widget class="QGBA::LibraryView" name="archiveView" native="true"/>
|
<widget class="QGBA::LibraryController" name="archiveView" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>QGBA::LibraryView</class>
|
<class>QGBA::LibraryController</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>LibraryView.h</header>
|
<header>library/LibraryController.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
|
|
@ -105,6 +105,7 @@ set(SOURCE_FILES
|
||||||
Swatch.cpp
|
Swatch.cpp
|
||||||
TilePainter.cpp
|
TilePainter.cpp
|
||||||
TileView.cpp
|
TileView.cpp
|
||||||
|
utils.cpp
|
||||||
Window.cpp
|
Window.cpp
|
||||||
VFileDevice.cpp
|
VFileDevice.cpp
|
||||||
VideoView.cpp)
|
VideoView.cpp)
|
||||||
|
@ -117,7 +118,6 @@ set(UI_FILES
|
||||||
DebuggerConsole.ui
|
DebuggerConsole.ui
|
||||||
GIFView.ui
|
GIFView.ui
|
||||||
IOViewer.ui
|
IOViewer.ui
|
||||||
LibraryView.ui
|
|
||||||
LoadSaveState.ui
|
LoadSaveState.ui
|
||||||
LogView.ui
|
LogView.ui
|
||||||
MemoryView.ui
|
MemoryView.ui
|
||||||
|
@ -138,8 +138,6 @@ set(GBA_SRC
|
||||||
set(GB_SRC
|
set(GB_SRC
|
||||||
GBOverride.cpp)
|
GBOverride.cpp)
|
||||||
|
|
||||||
qt5_wrap_ui(UI_SRC ${UI_FILES})
|
|
||||||
|
|
||||||
set(QT_LIBRARIES)
|
set(QT_LIBRARIES)
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5")
|
||||||
|
|
||||||
|
@ -189,8 +187,9 @@ endif()
|
||||||
if(USE_SQLITE3)
|
if(USE_SQLITE3)
|
||||||
list(APPEND SOURCE_FILES
|
list(APPEND SOURCE_FILES
|
||||||
ArchiveInspector.cpp
|
ArchiveInspector.cpp
|
||||||
LibraryModel.cpp
|
library/LibraryController.cpp
|
||||||
LibraryView.cpp)
|
library/LibraryGrid.cpp
|
||||||
|
library/LibraryTree.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt5_add_resources(RESOURCES resources.qrc)
|
qt5_add_resources(RESOURCES resources.qrc)
|
||||||
|
@ -239,6 +238,8 @@ if(Qt5LinguistTools_FOUND)
|
||||||
list(APPEND RESOURCES ${TRANSLATION_RESOURCES})
|
list(APPEND RESOURCES ${TRANSLATION_RESOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
qt5_wrap_ui(UI_SRC ${UI_FILES})
|
||||||
|
|
||||||
add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES})
|
add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES})
|
||||||
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}")
|
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}")
|
||||||
|
|
||||||
|
|
|
@ -1,320 +0,0 @@
|
||||||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#include "LibraryModel.h"
|
|
||||||
|
|
||||||
#include <QFontMetrics>
|
|
||||||
|
|
||||||
#include <mgba-util/vfs.h>
|
|
||||||
|
|
||||||
using namespace QGBA;
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(mLibraryEntry);
|
|
||||||
|
|
||||||
QMap<QString, LibraryModel::LibraryHandle*> LibraryModel::s_handles;
|
|
||||||
QMap<QString, LibraryModel::LibraryColumn> LibraryModel::s_columns;
|
|
||||||
|
|
||||||
LibraryModel::LibraryModel(const QString& path, QObject* parent)
|
|
||||||
: QAbstractItemModel(parent)
|
|
||||||
{
|
|
||||||
if (s_columns.empty()) {
|
|
||||||
s_columns["name"] = {
|
|
||||||
tr("Name"),
|
|
||||||
[](const mLibraryEntry& e) -> QString {
|
|
||||||
if (e.title) {
|
|
||||||
return QString::fromUtf8(e.title);
|
|
||||||
}
|
|
||||||
return QString::fromUtf8(e.filename);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
s_columns["filename"] = {
|
|
||||||
tr("Filename"),
|
|
||||||
[](const mLibraryEntry& e) -> QString {
|
|
||||||
return QString::fromUtf8(e.filename);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
s_columns["size"] = {
|
|
||||||
tr("Size"),
|
|
||||||
[](const mLibraryEntry& e) -> QString {
|
|
||||||
double size = e.filesize;
|
|
||||||
QString unit = "B";
|
|
||||||
if (size >= 1024.0) {
|
|
||||||
size /= 1024.0;
|
|
||||||
unit = "kiB";
|
|
||||||
}
|
|
||||||
if (size >= 1024.0) {
|
|
||||||
size /= 1024.0;
|
|
||||||
unit = "MiB";
|
|
||||||
}
|
|
||||||
return QString("%0 %1").arg(size, 0, 'f', 1).arg(unit);
|
|
||||||
},
|
|
||||||
Qt::AlignRight
|
|
||||||
};
|
|
||||||
s_columns["platform"] = {
|
|
||||||
tr("Platform"),
|
|
||||||
[](const mLibraryEntry& e) -> QString {
|
|
||||||
int platform = e.platform;
|
|
||||||
switch (platform) {
|
|
||||||
#ifdef M_CORE_GBA
|
|
||||||
case PLATFORM_GBA:
|
|
||||||
return tr("GBA");
|
|
||||||
#endif
|
|
||||||
#ifdef M_CORE_GB
|
|
||||||
case PLATFORM_GB:
|
|
||||||
return tr("GB");
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
return tr("?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
s_columns["location"] = {
|
|
||||||
tr("Location"),
|
|
||||||
[](const mLibraryEntry& e) -> QString {
|
|
||||||
return QString::fromUtf8(e.base);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
s_columns["crc32"] = {
|
|
||||||
tr("CRC32"),
|
|
||||||
[](const mLibraryEntry& e) -> QString {
|
|
||||||
return QString("%0").arg(e.crc32, 8, 16, QChar('0'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!path.isNull()) {
|
|
||||||
if (s_handles.contains(path)) {
|
|
||||||
m_library = s_handles[path];
|
|
||||||
m_library->ref();
|
|
||||||
} else {
|
|
||||||
m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path);
|
|
||||||
if (m_library->library) {
|
|
||||||
s_handles[path] = m_library;
|
|
||||||
} else {
|
|
||||||
delete m_library;
|
|
||||||
m_library = new LibraryHandle(mLibraryCreateEmpty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_library = new LibraryHandle(mLibraryCreateEmpty());
|
|
||||||
}
|
|
||||||
mLibraryListingInit(&m_listings, 0);
|
|
||||||
memset(&m_constraints, 0, sizeof(m_constraints));
|
|
||||||
m_constraints.platform = PLATFORM_NONE;
|
|
||||||
m_columns.append(s_columns["name"]);
|
|
||||||
m_columns.append(s_columns["location"]);
|
|
||||||
m_columns.append(s_columns["platform"]);
|
|
||||||
m_columns.append(s_columns["size"]);
|
|
||||||
m_columns.append(s_columns["crc32"]);
|
|
||||||
|
|
||||||
connect(m_library->loader, SIGNAL(directoryLoaded(const QString&)), this, SLOT(directoryLoaded(const QString&)));
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryModel::~LibraryModel() {
|
|
||||||
clearConstraints();
|
|
||||||
mLibraryListingDeinit(&m_listings);
|
|
||||||
if (!m_library->deref()) {
|
|
||||||
s_handles.remove(m_library->path);
|
|
||||||
delete m_library;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryModel::loadDirectory(const QString& path) {
|
|
||||||
m_queue.append(path);
|
|
||||||
QMetaObject::invokeMethod(m_library->loader, "loadDirectory", Q_ARG(const QString&, path));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LibraryModel::entryAt(int row, mLibraryEntry* out) const {
|
|
||||||
if (mLibraryListingSize(&m_listings) <= row) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*out = *mLibraryListingGetConstPointer(&m_listings, row);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
VFile* LibraryModel::openVFile(const QModelIndex& index) const {
|
|
||||||
mLibraryEntry entry;
|
|
||||||
if (!entryAt(index.row(), &entry)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return mLibraryOpenVFile(m_library->library, &entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LibraryModel::filename(const QModelIndex& index) const {
|
|
||||||
mLibraryEntry entry;
|
|
||||||
if (!entryAt(index.row(), &entry)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return QString::fromUtf8(entry.filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString LibraryModel::location(const QModelIndex& index) const {
|
|
||||||
mLibraryEntry entry;
|
|
||||||
if (!entryAt(index.row(), &entry)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return QString::fromUtf8(entry.base);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant LibraryModel::data(const QModelIndex& index, int role) const {
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
mLibraryEntry entry;
|
|
||||||
if (!entryAt(index.row(), &entry)) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
if (role == Qt::UserRole) {
|
|
||||||
return QVariant::fromValue(entry);
|
|
||||||
}
|
|
||||||
if (index.column() >= m_columns.count()) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
switch (role) {
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
return m_columns[index.column()].value(entry);
|
|
||||||
case Qt::SizeHintRole: {
|
|
||||||
QFontMetrics fm((QFont()));
|
|
||||||
return fm.size(Qt::TextSingleLine, m_columns[index.column()].value(entry));
|
|
||||||
}
|
|
||||||
case Qt::TextAlignmentRole:
|
|
||||||
return m_columns[index.column()].alignment;
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
|
||||||
if (role != Qt::DisplayRole) {
|
|
||||||
return QAbstractItemModel::headerData(section, orientation, role);
|
|
||||||
}
|
|
||||||
if (orientation == Qt::Horizontal) {
|
|
||||||
if (section >= m_columns.count()) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
return m_columns[section].name;
|
|
||||||
}
|
|
||||||
return section;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex LibraryModel::index(int row, int column, const QModelIndex& parent) const {
|
|
||||||
if (parent.isValid()) {
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
return createIndex(row, column, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex LibraryModel::parent(const QModelIndex&) const {
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
int LibraryModel::columnCount(const QModelIndex& parent) const {
|
|
||||||
if (parent.isValid()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return m_columns.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
int LibraryModel::rowCount(const QModelIndex& parent) const {
|
|
||||||
if (parent.isValid()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return mLibraryCount(m_library->library, &m_constraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryModel::attachGameDB(const NoIntroDB* gameDB) {
|
|
||||||
mLibraryAttachGameDB(m_library->library, gameDB);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryModel::constrainBase(const QString& path) {
|
|
||||||
clearConstraints();
|
|
||||||
if (m_constraints.base) {
|
|
||||||
free(const_cast<char*>(m_constraints.base));
|
|
||||||
}
|
|
||||||
m_constraints.base = strdup(path.toUtf8().constData());
|
|
||||||
reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryModel::clearConstraints() {
|
|
||||||
if (m_constraints.base) {
|
|
||||||
free(const_cast<char*>(m_constraints.base));
|
|
||||||
}
|
|
||||||
if (m_constraints.filename) {
|
|
||||||
free(const_cast<char*>(m_constraints.filename));
|
|
||||||
}
|
|
||||||
if (m_constraints.title) {
|
|
||||||
free(const_cast<char*>(m_constraints.title));
|
|
||||||
}
|
|
||||||
memset(&m_constraints, 0, sizeof(m_constraints));
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < mLibraryListingSize(&m_listings); ++i) {
|
|
||||||
mLibraryEntryFree(mLibraryListingGetPointer(&m_listings, i));
|
|
||||||
}
|
|
||||||
mLibraryListingClear(&m_listings);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryModel::reload() {
|
|
||||||
mLibraryGetEntries(m_library->library, &m_listings, 0, 0, m_constraints.base ? &m_constraints : nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryModel::directoryLoaded(const QString& path) {
|
|
||||||
m_queue.removeOne(path);
|
|
||||||
beginResetModel();
|
|
||||||
endResetModel();
|
|
||||||
if (m_queue.empty()) {
|
|
||||||
emit doneLoading();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryModel::LibraryColumn::LibraryColumn() {
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryModel::LibraryColumn::LibraryColumn(const QString& name, std::function<QString(const mLibraryEntry&)> value, int alignment)
|
|
||||||
: name(name)
|
|
||||||
, value(value)
|
|
||||||
, alignment(alignment)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p)
|
|
||||||
: library(lib)
|
|
||||||
, loader(new LibraryLoader(library))
|
|
||||||
, path(p)
|
|
||||||
, m_ref(1)
|
|
||||||
{
|
|
||||||
if (!library) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loader->moveToThread(&m_loaderThread);
|
|
||||||
m_loaderThread.setObjectName("Library Loader Thread");
|
|
||||||
m_loaderThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryModel::LibraryHandle::~LibraryHandle() {
|
|
||||||
m_loaderThread.quit();
|
|
||||||
m_loaderThread.wait();
|
|
||||||
if (library) {
|
|
||||||
mLibraryDestroy(library);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryModel::LibraryHandle::ref() {
|
|
||||||
++m_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LibraryModel::LibraryHandle::deref() {
|
|
||||||
--m_ref;
|
|
||||||
return m_ref > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryLoader::LibraryLoader(mLibrary* library, QObject* parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_library(library)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryLoader::loadDirectory(const QString& path) {
|
|
||||||
mLibraryLoadDirectory(m_library, path.toUtf8().constData());
|
|
||||||
emit directoryLoaded(path);
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#ifndef QGBA_LIBRARY_MODEL
|
|
||||||
#define QGBA_LIBRARY_MODEL
|
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
#include <mgba/core/library.h>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
struct VDir;
|
|
||||||
struct VFile;
|
|
||||||
struct NoIntroDB;
|
|
||||||
|
|
||||||
namespace QGBA {
|
|
||||||
|
|
||||||
class LibraryLoader;
|
|
||||||
class LibraryModel : public QAbstractItemModel {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
LibraryModel(const QString& path, QObject* parent = nullptr);
|
|
||||||
virtual ~LibraryModel();
|
|
||||||
|
|
||||||
bool entryAt(int row, mLibraryEntry* out) const;
|
|
||||||
VFile* openVFile(const QModelIndex& index) const;
|
|
||||||
QString filename(const QModelIndex& index) const;
|
|
||||||
QString location(const QModelIndex& index) const;
|
|
||||||
|
|
||||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
|
||||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
|
||||||
|
|
||||||
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
|
||||||
virtual QModelIndex parent(const QModelIndex& index) const override;
|
|
||||||
|
|
||||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
|
||||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
void attachGameDB(const NoIntroDB* gameDB);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void doneLoading();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void loadDirectory(const QString& path);
|
|
||||||
|
|
||||||
void constrainBase(const QString& path);
|
|
||||||
void clearConstraints();
|
|
||||||
void reload();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void directoryLoaded(const QString& path);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct LibraryColumn {
|
|
||||||
LibraryColumn();
|
|
||||||
LibraryColumn(const QString&, std::function<QString(const mLibraryEntry&)>, int = Qt::AlignLeft);
|
|
||||||
QString name;
|
|
||||||
std::function<QString(const mLibraryEntry&)> value;
|
|
||||||
int alignment;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LibraryHandle {
|
|
||||||
public:
|
|
||||||
LibraryHandle(mLibrary*, const QString& path = QString());
|
|
||||||
~LibraryHandle();
|
|
||||||
|
|
||||||
mLibrary* const library;
|
|
||||||
LibraryLoader* const loader;
|
|
||||||
const QString path;
|
|
||||||
|
|
||||||
void ref();
|
|
||||||
bool deref();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QThread m_loaderThread;
|
|
||||||
size_t m_ref;
|
|
||||||
};
|
|
||||||
|
|
||||||
LibraryHandle* m_library;
|
|
||||||
static QMap<QString, LibraryHandle*> s_handles;
|
|
||||||
|
|
||||||
mLibraryEntry m_constraints;
|
|
||||||
mLibraryListing m_listings;
|
|
||||||
QStringList m_queue;
|
|
||||||
|
|
||||||
QList<LibraryColumn> m_columns;
|
|
||||||
static QMap<QString, LibraryColumn> s_columns;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LibraryLoader : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
LibraryLoader(mLibrary* library, QObject* parent = nullptr);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void loadDirectory(const QString& path);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void directoryLoaded(const QString& path);
|
|
||||||
|
|
||||||
private:
|
|
||||||
mLibrary* m_library;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,58 +0,0 @@
|
||||||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#include "LibraryView.h"
|
|
||||||
|
|
||||||
#include <mgba-util/vfs.h>
|
|
||||||
|
|
||||||
#include "ConfigController.h"
|
|
||||||
#include "GBAApp.h"
|
|
||||||
|
|
||||||
using namespace QGBA;
|
|
||||||
|
|
||||||
LibraryView::LibraryView(QWidget* parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, m_model(ConfigController::configDir() + "/library.sqlite3")
|
|
||||||
{
|
|
||||||
m_ui.setupUi(this);
|
|
||||||
m_model.attachGameDB(GBAApp::app()->gameDB());
|
|
||||||
connect(&m_model, SIGNAL(doneLoading()), this, SIGNAL(doneLoading()));
|
|
||||||
connect(&m_model, SIGNAL(doneLoading()), this, SLOT(resizeColumns()));
|
|
||||||
connect(m_ui.listing, SIGNAL(activated(const QModelIndex&)), this, SIGNAL(accepted()));
|
|
||||||
m_ui.listing->horizontalHeader()->setSectionsMovable(true);
|
|
||||||
m_ui.listing->setModel(&m_model);
|
|
||||||
m_ui.listing->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
||||||
m_model.reload();
|
|
||||||
resizeColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryView::setDirectory(const QString& filename) {
|
|
||||||
m_model.loadDirectory(filename);
|
|
||||||
m_model.constrainBase(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryView::addDirectory(const QString& filename) {
|
|
||||||
m_model.loadDirectory(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
VFile* LibraryView::selectedVFile() const {
|
|
||||||
QModelIndex index = m_ui.listing->selectionModel()->currentIndex();
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return m_model.openVFile(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPair<QString, QString> LibraryView::selectedPath() const {
|
|
||||||
QModelIndex index = m_ui.listing->selectionModel()->currentIndex();
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return qMakePair(QString(), QString());
|
|
||||||
}
|
|
||||||
return qMakePair(m_model.filename(index), m_model.location(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
void LibraryView::resizeColumns() {
|
|
||||||
m_ui.listing->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#ifndef QGBA_LIBRARY_VIEW
|
|
||||||
#define QGBA_LIBRARY_VIEW
|
|
||||||
|
|
||||||
#include "LibraryModel.h"
|
|
||||||
|
|
||||||
#include "ui_LibraryView.h"
|
|
||||||
|
|
||||||
struct VFile;
|
|
||||||
|
|
||||||
namespace QGBA {
|
|
||||||
|
|
||||||
class LibraryView : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
LibraryView(QWidget* parent = nullptr);
|
|
||||||
|
|
||||||
VFile* selectedVFile() const;
|
|
||||||
QPair<QString, QString> selectedPath() const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void doneLoading();
|
|
||||||
void accepted();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setDirectory(const QString&);
|
|
||||||
void addDirectory(const QString&);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void resizeColumns();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::LibraryView m_ui;
|
|
||||||
|
|
||||||
LibraryModel m_model;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,49 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>LibraryView</class>
|
|
||||||
<widget class="QWidget" name="LibraryView">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>300</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Library</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout">
|
|
||||||
<property name="margin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QTableView" name="listing">
|
|
||||||
<property name="editTriggers">
|
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionBehavior">
|
|
||||||
<enum>QAbstractItemView::SelectRows</enum>
|
|
||||||
</property>
|
|
||||||
<property name="showGrid">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<attribute name="horizontalHeaderStretchLastSection">
|
|
||||||
<bool>true</bool>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="verticalHeaderVisible">
|
|
||||||
<bool>false</bool>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="verticalHeaderMinimumSectionSize">
|
|
||||||
<number>0</number>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
|
@ -204,6 +204,7 @@ void SettingsView::updateConfig() {
|
||||||
saveSetting("savestatePath", m_ui.savestatePath);
|
saveSetting("savestatePath", m_ui.savestatePath);
|
||||||
saveSetting("screenshotPath", m_ui.screenshotPath);
|
saveSetting("screenshotPath", m_ui.screenshotPath);
|
||||||
saveSetting("patchPath", m_ui.patchPath);
|
saveSetting("patchPath", m_ui.patchPath);
|
||||||
|
saveSetting("libraryStyle", m_ui.libraryStyle->currentIndex());
|
||||||
saveSetting("showLibrary", m_ui.showLibrary);
|
saveSetting("showLibrary", m_ui.showLibrary);
|
||||||
saveSetting("preload", m_ui.preload);
|
saveSetting("preload", m_ui.preload);
|
||||||
|
|
||||||
|
|
|
@ -398,28 +398,28 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="interface_2">
|
<widget class="QWidget" name="interface_2">
|
||||||
<layout class="QFormLayout" name="formLayout_4">
|
<layout class="QFormLayout" name="formLayout_4">
|
||||||
<item row="3" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="allowOpposingDirections">
|
<widget class="QCheckBox" name="showLibrary">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Allow opposing input directions</string>
|
<string>Show when no game open</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QCheckBox" name="suspendScreensaver">
|
|
||||||
<property name="text">
|
|
||||||
<string>Suspend screensaver</string>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QCheckBox" name="pauseOnFocusLost">
|
<widget class="QComboBox" name="libraryStyle">
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Pause when inactive</string>
|
<string>List view</string>
|
||||||
</property>
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Tree view</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
|
@ -429,21 +429,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="showLibrary">
|
|
||||||
<property name="text">
|
|
||||||
<string>Show when no game open</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="Line" name="line_8">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QPushButton" name="clearCache">
|
<widget class="QPushButton" name="clearCache">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
@ -453,6 +439,37 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="Line" name="line_8">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QCheckBox" name="allowOpposingDirections">
|
||||||
|
<property name="text">
|
||||||
|
<string>Allow opposing input directions</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QCheckBox" name="suspendScreensaver">
|
||||||
|
<property name="text">
|
||||||
|
<string>Suspend screensaver</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QCheckBox" name="pauseOnFocusLost">
|
||||||
|
<property name="text">
|
||||||
|
<string>Pause when inactive</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="emulation">
|
<widget class="QWidget" name="emulation">
|
||||||
|
|
|
@ -14,10 +14,12 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QStackedLayout>
|
#include <QStackedLayout>
|
||||||
|
|
||||||
#include "AboutScreen.h"
|
|
||||||
#ifdef USE_SQLITE3
|
#ifdef USE_SQLITE3
|
||||||
#include "ArchiveInspector.h"
|
#include "ArchiveInspector.h"
|
||||||
|
#include "library/LibraryController.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "AboutScreen.h"
|
||||||
#include "CheatsView.h"
|
#include "CheatsView.h"
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
#include "DebuggerConsole.h"
|
#include "DebuggerConsole.h"
|
||||||
|
@ -108,7 +110,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
||||||
i = m_savedScale;
|
i = m_savedScale;
|
||||||
}
|
}
|
||||||
#ifdef USE_SQLITE3
|
#ifdef USE_SQLITE3
|
||||||
m_libraryView = new LibraryView();
|
m_libraryView = new LibraryController(nullptr, ConfigController::configDir() + "/library.sqlite3", m_config);
|
||||||
ConfigOption* showLibrary = m_config->addOption("showLibrary");
|
ConfigOption* showLibrary = m_config->addOption("showLibrary");
|
||||||
showLibrary->connect([this](const QVariant& value) {
|
showLibrary->connect([this](const QVariant& value) {
|
||||||
if (value.toBool()) {
|
if (value.toBool()) {
|
||||||
|
@ -122,12 +124,17 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
m_config->updateOption("showLibrary");
|
m_config->updateOption("showLibrary");
|
||||||
|
ConfigOption* libraryStyle = m_config->addOption("libraryStyle");
|
||||||
|
libraryStyle->connect([this](const QVariant& value) {
|
||||||
|
m_libraryView->setViewStyle(static_cast<LibraryStyle>(value.toInt()));
|
||||||
|
}, this);
|
||||||
|
m_config->updateOption("libraryStyle");
|
||||||
|
|
||||||
connect(m_libraryView, &LibraryView::accepted, [this]() {
|
connect(m_libraryView, &LibraryController::startGame, [this]() {
|
||||||
VFile* output = m_libraryView->selectedVFile();
|
VFile* output = m_libraryView->selectedVFile();
|
||||||
QPair<QString, QString> path = m_libraryView->selectedPath();
|
|
||||||
if (output) {
|
if (output) {
|
||||||
m_controller->loadGame(output, path.first, path.second);
|
QPair<QString, QString> path = m_libraryView->selectedPath();
|
||||||
|
m_controller->loadGame(output, path.second, path.first);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#elif defined(M_CORE_GBA)
|
#elif defined(M_CORE_GBA)
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Display;
|
||||||
class GameController;
|
class GameController;
|
||||||
class GDBController;
|
class GDBController;
|
||||||
class GIFView;
|
class GIFView;
|
||||||
class LibraryView;
|
class LibraryController;
|
||||||
class LogView;
|
class LogView;
|
||||||
class ShaderSelector;
|
class ShaderSelector;
|
||||||
class ShortcutController;
|
class ShortcutController;
|
||||||
|
@ -199,7 +199,7 @@ private:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SQLITE3
|
#ifdef USE_SQLITE3
|
||||||
LibraryView* m_libraryView;
|
LibraryController* m_libraryView;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
/* Copyright (c) 2014-2017 waddlesplash
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "LibraryController.h"
|
||||||
|
|
||||||
|
#include "../GBAApp.h"
|
||||||
|
#include "LibraryGrid.h"
|
||||||
|
#include "LibraryTree.h"
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
LibraryEntry::LibraryEntry(mLibraryEntry* entry)
|
||||||
|
: entry(entry)
|
||||||
|
, m_fullpath(QString("%1/%2").arg(entry->base, entry->filename))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractGameList::addEntries(QList<LibraryEntryRef> items) {
|
||||||
|
for (LibraryEntryRef o : items) {
|
||||||
|
addEntry(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) {
|
||||||
|
for (LibraryEntryRef o : items) {
|
||||||
|
addEntry(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryLoaderThread::LibraryLoaderThread(QObject* parent)
|
||||||
|
: QThread(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryLoaderThread::run() {
|
||||||
|
mLibraryLoadDirectory(m_library, m_directory.toUtf8().constData());
|
||||||
|
m_directory = QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config)
|
||||||
|
: QStackedWidget(parent)
|
||||||
|
, m_config(config)
|
||||||
|
{
|
||||||
|
mLibraryListingInit(&m_listing, 0);
|
||||||
|
|
||||||
|
if (!path.isNull()) {
|
||||||
|
m_library = mLibraryLoad(path.toUtf8().constData());
|
||||||
|
} else {
|
||||||
|
m_library = mLibraryCreateEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLibraryAttachGameDB(m_library, GBAApp::app()->gameDB());
|
||||||
|
|
||||||
|
m_libraryTree = new LibraryTree(this);
|
||||||
|
addWidget(m_libraryTree->widget());
|
||||||
|
|
||||||
|
m_libraryGrid = new LibraryGrid(this);
|
||||||
|
addWidget(m_libraryGrid->widget());
|
||||||
|
|
||||||
|
connect(&m_loaderThread, &QThread::finished, this, &LibraryController::refresh, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
setViewStyle(LibraryStyle::STYLE_LIST);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryController::~LibraryController() {
|
||||||
|
mLibraryListingDeinit(&m_listing);
|
||||||
|
|
||||||
|
if (m_loaderThread.isRunning()) {
|
||||||
|
m_loaderThread.wait();
|
||||||
|
}
|
||||||
|
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
|
||||||
|
m_library = m_loaderThread.m_library;
|
||||||
|
m_loaderThread.m_library = nullptr;
|
||||||
|
}
|
||||||
|
if (m_library) {
|
||||||
|
mLibraryDestroy(m_library);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryController::setViewStyle(LibraryStyle newStyle) {
|
||||||
|
m_currentStyle = newStyle;
|
||||||
|
|
||||||
|
AbstractGameList* newCurrentList = nullptr;
|
||||||
|
if (newStyle == LibraryStyle::STYLE_LIST || newStyle == LibraryStyle::STYLE_TREE) {
|
||||||
|
newCurrentList = m_libraryTree;
|
||||||
|
} else {
|
||||||
|
newCurrentList = m_libraryGrid;
|
||||||
|
}
|
||||||
|
newCurrentList->selectEntry(selectedEntry());
|
||||||
|
newCurrentList->setViewStyle(newStyle);
|
||||||
|
setCurrentWidget(newCurrentList->widget());
|
||||||
|
m_currentList = newCurrentList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryController::selectEntry(LibraryEntryRef entry) {
|
||||||
|
if (!m_currentList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_currentList->selectEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryEntryRef LibraryController::selectedEntry() {
|
||||||
|
if (!m_currentList) {
|
||||||
|
return LibraryEntryRef();
|
||||||
|
}
|
||||||
|
return m_currentList->selectedEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
VFile* LibraryController::selectedVFile() {
|
||||||
|
LibraryEntryRef entry = selectedEntry();
|
||||||
|
if (entry) {
|
||||||
|
return mLibraryOpenVFile(m_library, entry->entry);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> LibraryController::selectedPath() {
|
||||||
|
LibraryEntryRef e = selectedEntry();
|
||||||
|
return e ? qMakePair(e->base(), e->filename()) : qMakePair<QString, QString>("", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryController::addDirectory(const QString& dir) {
|
||||||
|
m_loaderThread.m_directory = dir;
|
||||||
|
m_loaderThread.m_library = m_library;
|
||||||
|
// The m_loaderThread temporarily owns the library
|
||||||
|
m_library = nullptr;
|
||||||
|
m_loaderThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryController::refresh() {
|
||||||
|
if (!m_library) {
|
||||||
|
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
|
||||||
|
m_library = m_loaderThread.m_library;
|
||||||
|
m_loaderThread.m_library = nullptr;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabled(true);
|
||||||
|
|
||||||
|
QStringList allEntries;
|
||||||
|
QList<LibraryEntryRef> newEntries;
|
||||||
|
|
||||||
|
mLibraryListingClear(&m_listing);
|
||||||
|
mLibraryGetEntries(m_library, &m_listing, 0, 0, nullptr);
|
||||||
|
for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) {
|
||||||
|
mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i);
|
||||||
|
QString fullpath = QString("%1/%2").arg(entry->base, entry->filename);
|
||||||
|
if (m_entries.contains(fullpath)) {
|
||||||
|
m_entries.value(fullpath)->entry = entry;
|
||||||
|
} else {
|
||||||
|
LibraryEntryRef libentry = std::make_shared<LibraryEntry>(entry);
|
||||||
|
m_entries.insert(fullpath, libentry);
|
||||||
|
newEntries.append(libentry);
|
||||||
|
}
|
||||||
|
allEntries.append(fullpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for entries that were removed
|
||||||
|
QList<LibraryEntryRef> removedEntries;
|
||||||
|
for (QString& path : m_entries.keys()) {
|
||||||
|
if (!allEntries.contains(path)) {
|
||||||
|
removedEntries.append(m_entries.value(path));
|
||||||
|
m_entries.remove(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_libraryTree->addEntries(newEntries);
|
||||||
|
m_libraryGrid->addEntries(newEntries);
|
||||||
|
|
||||||
|
m_libraryTree->removeEntries(removedEntries);
|
||||||
|
m_libraryGrid->removeEntries(removedEntries);
|
||||||
|
|
||||||
|
setDisabled(false);
|
||||||
|
selectLastBootedGame();
|
||||||
|
emit doneLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryController::selectLastBootedGame() {
|
||||||
|
if (!m_config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QString lastfile = m_config->getMRU().first();
|
||||||
|
if (m_entries.contains(lastfile)) {
|
||||||
|
selectEntry(m_entries.value(lastfile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/* Copyright (c) 2014-2017 waddlesplash
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef QGBA_LIBRARY_CONTROLLER
|
||||||
|
#define QGBA_LIBRARY_CONTROLLER
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QStackedWidget>
|
||||||
|
|
||||||
|
#include <mgba/core/library.h>
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
// Predefinitions
|
||||||
|
class LibraryGrid;
|
||||||
|
class LibraryTree;
|
||||||
|
class ConfigController;
|
||||||
|
|
||||||
|
enum class LibraryStyle {
|
||||||
|
STYLE_LIST = 0,
|
||||||
|
STYLE_TREE,
|
||||||
|
STYLE_GRID,
|
||||||
|
STYLE_ICON
|
||||||
|
};
|
||||||
|
|
||||||
|
class LibraryEntry final {
|
||||||
|
public:
|
||||||
|
LibraryEntry(mLibraryEntry* entry);
|
||||||
|
|
||||||
|
QString displayTitle() const { return title().isNull() ? filename() : title(); }
|
||||||
|
|
||||||
|
QString base() const { return QString(entry->base); }
|
||||||
|
QString filename() const { return QString(entry->filename); }
|
||||||
|
QString fullpath() const { return m_fullpath; }
|
||||||
|
QString title() const { return QString(entry->title); }
|
||||||
|
QByteArray internalTitle() const { return QByteArray(entry->internalTitle); }
|
||||||
|
QByteArray internalCode() const { return QByteArray(entry->internalCode); }
|
||||||
|
mPlatform platform() const { return entry->platform; }
|
||||||
|
size_t filesize() const { return entry->filesize; }
|
||||||
|
uint32_t crc32() const { return entry->crc32; }
|
||||||
|
|
||||||
|
const mLibraryEntry* entry;
|
||||||
|
private:
|
||||||
|
const QString m_fullpath;
|
||||||
|
};
|
||||||
|
typedef std::shared_ptr<LibraryEntry> LibraryEntryRef;
|
||||||
|
|
||||||
|
class AbstractGameList {
|
||||||
|
public:
|
||||||
|
virtual LibraryEntryRef selectedEntry() = 0;
|
||||||
|
virtual void selectEntry(LibraryEntryRef game) = 0;
|
||||||
|
|
||||||
|
virtual void setViewStyle(LibraryStyle newStyle) = 0;
|
||||||
|
|
||||||
|
virtual void addEntry(LibraryEntryRef item) = 0;
|
||||||
|
virtual void addEntries(QList<LibraryEntryRef> items);
|
||||||
|
|
||||||
|
virtual void removeEntry(LibraryEntryRef item) = 0;
|
||||||
|
virtual void removeEntries(QList<LibraryEntryRef> items);
|
||||||
|
|
||||||
|
virtual QWidget* widget() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LibraryLoaderThread final : public QThread {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LibraryLoaderThread(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
mLibrary* m_library = nullptr;
|
||||||
|
QString m_directory;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void run() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LibraryController final : public QStackedWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LibraryController(QWidget* parent = nullptr, const QString& path = QString(),
|
||||||
|
ConfigController* config = nullptr);
|
||||||
|
~LibraryController();
|
||||||
|
|
||||||
|
LibraryStyle viewStyle() const { return m_currentStyle; }
|
||||||
|
void setViewStyle(LibraryStyle newStyle);
|
||||||
|
|
||||||
|
void selectEntry(LibraryEntryRef entry);
|
||||||
|
LibraryEntryRef selectedEntry();
|
||||||
|
VFile* selectedVFile();
|
||||||
|
QPair<QString, QString> selectedPath();
|
||||||
|
|
||||||
|
void selectLastBootedGame();
|
||||||
|
|
||||||
|
void addDirectory(const QString& dir);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void startGame();
|
||||||
|
void doneLoading();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConfigController* m_config = nullptr;
|
||||||
|
LibraryLoaderThread m_loaderThread;
|
||||||
|
mLibrary* m_library = nullptr;
|
||||||
|
mLibraryListing m_listing;
|
||||||
|
QMap<QString, LibraryEntryRef> m_entries;
|
||||||
|
|
||||||
|
LibraryStyle m_currentStyle;
|
||||||
|
AbstractGameList* m_currentList = nullptr;
|
||||||
|
|
||||||
|
LibraryGrid* m_libraryGrid = nullptr;
|
||||||
|
LibraryTree* m_libraryTree = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,79 @@
|
||||||
|
/* Copyright (c) 2014-2017 waddlesplash
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "LibraryGrid.h"
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
LibraryGrid::LibraryGrid(LibraryController* parent)
|
||||||
|
: m_widget(new QListWidget(parent))
|
||||||
|
{
|
||||||
|
m_widget->setObjectName("LibraryGrid");
|
||||||
|
m_widget->setWrapping(true);
|
||||||
|
m_widget->setResizeMode(QListView::Adjust);
|
||||||
|
m_widget->setUniformItemSizes(true);
|
||||||
|
setViewStyle(LibraryStyle::STYLE_GRID);
|
||||||
|
|
||||||
|
QObject::connect(m_widget, &QListWidget::itemActivated, parent, &LibraryController::startGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryGrid::~LibraryGrid() {
|
||||||
|
delete m_widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryEntryRef LibraryGrid::selectedEntry() {
|
||||||
|
if (!m_widget->selectedItems().empty()) {
|
||||||
|
return m_items.key(m_widget->selectedItems().at(0));
|
||||||
|
} else {
|
||||||
|
return LibraryEntryRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryGrid::selectEntry(LibraryEntryRef game) {
|
||||||
|
if (!game) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_widget->selectedItems().empty()) {
|
||||||
|
m_widget->selectedItems().at(0)->setSelected(false);
|
||||||
|
}
|
||||||
|
m_items.value(game)->setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryGrid::setViewStyle(LibraryStyle newStyle) {
|
||||||
|
if (newStyle == LibraryStyle::STYLE_GRID) {
|
||||||
|
m_currentStyle = LibraryStyle::STYLE_GRID;
|
||||||
|
m_widget->setIconSize(QSize(GRID_BANNER_WIDTH, GRID_BANNER_HEIGHT));
|
||||||
|
m_widget->setViewMode(QListView::IconMode);
|
||||||
|
} else {
|
||||||
|
m_currentStyle = LibraryStyle::STYLE_ICON;
|
||||||
|
m_widget->setIconSize(QSize(ICON_BANNER_WIDTH, ICON_BANNER_HEIGHT));
|
||||||
|
m_widget->setViewMode(QListView::ListMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// QListView resets this when you change the view mode, so let's set it again
|
||||||
|
m_widget->setDragEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryGrid::addEntry(LibraryEntryRef item) {
|
||||||
|
if (m_items.contains(item)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidgetItem* i = new QListWidgetItem;
|
||||||
|
i->setText(item->displayTitle());
|
||||||
|
|
||||||
|
m_widget->addItem(i);
|
||||||
|
m_items.insert(item, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryGrid::removeEntry(LibraryEntryRef entry) {
|
||||||
|
if (!m_items.contains(entry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete m_items.take(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* Copyright (c) 2014-2017 waddlesplash
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef QGBA_LIBRARY_GRID
|
||||||
|
#define QGBA_LIBRARY_GRID
|
||||||
|
|
||||||
|
#include <QListWidget>
|
||||||
|
|
||||||
|
#include "LibraryController.h"
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class LibraryGrid final : public AbstractGameList {
|
||||||
|
public:
|
||||||
|
explicit LibraryGrid(LibraryController* parent = nullptr);
|
||||||
|
~LibraryGrid();
|
||||||
|
|
||||||
|
// AbstractGameList stuff
|
||||||
|
virtual LibraryEntryRef selectedEntry() override;
|
||||||
|
virtual void selectEntry(LibraryEntryRef game) override;
|
||||||
|
|
||||||
|
virtual void setViewStyle(LibraryStyle newStyle) override;
|
||||||
|
|
||||||
|
virtual void addEntry(LibraryEntryRef item) override;
|
||||||
|
virtual void removeEntry(LibraryEntryRef entry) override;
|
||||||
|
|
||||||
|
virtual QWidget* widget() override { return m_widget; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void startGame();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QListWidget* m_widget;
|
||||||
|
|
||||||
|
// Game banner image size
|
||||||
|
const quint32 GRID_BANNER_WIDTH = 320;
|
||||||
|
const quint32 GRID_BANNER_HEIGHT = 240;
|
||||||
|
|
||||||
|
const quint32 ICON_BANNER_WIDTH = 64;
|
||||||
|
const quint32 ICON_BANNER_HEIGHT = 64;
|
||||||
|
|
||||||
|
QMap<LibraryEntryRef, QListWidgetItem*> m_items;
|
||||||
|
LibraryStyle m_currentStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,179 @@
|
||||||
|
/* Copyright (c) 2014-2017 waddlesplash
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "LibraryTree.h"
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class TreeWidgetItem : public QTreeWidgetItem {
|
||||||
|
public:
|
||||||
|
TreeWidgetItem(QTreeWidget* parent = nullptr) : QTreeWidgetItem(parent) {}
|
||||||
|
void setFilesize(size_t size);
|
||||||
|
|
||||||
|
virtual bool operator<(const QTreeWidgetItem& other) const override;
|
||||||
|
protected:
|
||||||
|
size_t m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void TreeWidgetItem::setFilesize(size_t size) {
|
||||||
|
m_size = size;
|
||||||
|
setText(LibraryTree::COL_SIZE, niceSizeFormat(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TreeWidgetItem::operator<(const QTreeWidgetItem& other) const {
|
||||||
|
const int column = treeWidget()->sortColumn();
|
||||||
|
return ((column == LibraryTree::COL_SIZE) ?
|
||||||
|
m_size < dynamic_cast<const TreeWidgetItem*>(&other)->m_size :
|
||||||
|
QTreeWidgetItem::operator<(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryTree::LibraryTree(LibraryController* parent)
|
||||||
|
: m_widget(new QTreeWidget(parent))
|
||||||
|
, m_controller(parent)
|
||||||
|
{
|
||||||
|
m_widget->setObjectName("LibraryTree");
|
||||||
|
m_widget->setSortingEnabled(true);
|
||||||
|
m_widget->setAlternatingRowColors(true);
|
||||||
|
|
||||||
|
QTreeWidgetItem* header = new QTreeWidgetItem({
|
||||||
|
QApplication::translate("LibraryTree", "Name", nullptr),
|
||||||
|
QApplication::translate("LibraryTree", "Location", nullptr),
|
||||||
|
QApplication::translate("LibraryTree", "Platform", nullptr),
|
||||||
|
QApplication::translate("LibraryTree", "Size", nullptr),
|
||||||
|
QApplication::translate("LibraryTree", "CRC32", nullptr),
|
||||||
|
});
|
||||||
|
header->setTextAlignment(3, Qt::AlignTrailing | Qt::AlignVCenter);
|
||||||
|
m_widget->setHeaderItem(header);
|
||||||
|
|
||||||
|
setViewStyle(LibraryStyle::STYLE_TREE);
|
||||||
|
m_widget->sortByColumn(COL_NAME, Qt::AscendingOrder);
|
||||||
|
|
||||||
|
QObject::connect(m_widget, &QTreeWidget::itemActivated, [this](QTreeWidgetItem* item, int) -> void {
|
||||||
|
if (!m_pathNodes.values().contains(item)) {
|
||||||
|
emit m_controller->startGame();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryTree::resizeAllCols() {
|
||||||
|
for (int i = 0; i < m_widget->columnCount(); i++) {
|
||||||
|
m_widget->resizeColumnToContents(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryEntryRef LibraryTree::selectedEntry() {
|
||||||
|
if (!m_widget->selectedItems().empty()) {
|
||||||
|
return m_items.key(m_widget->selectedItems().at(0));
|
||||||
|
} else {
|
||||||
|
return LibraryEntryRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryTree::selectEntry(LibraryEntryRef game) {
|
||||||
|
if (!game) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_widget->selectedItems().empty()) {
|
||||||
|
m_widget->selectedItems().at(0)->setSelected(false);
|
||||||
|
}
|
||||||
|
m_items.value(game)->setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryTree::setViewStyle(LibraryStyle newStyle) {
|
||||||
|
if (newStyle == LibraryStyle::STYLE_LIST) {
|
||||||
|
m_currentStyle = LibraryStyle::STYLE_LIST;
|
||||||
|
m_widget->setIndentation(0);
|
||||||
|
rebuildTree();
|
||||||
|
} else {
|
||||||
|
m_currentStyle = LibraryStyle::STYLE_TREE;
|
||||||
|
m_widget->setIndentation(20);
|
||||||
|
rebuildTree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryTree::addEntries(QList<LibraryEntryRef> items) {
|
||||||
|
m_deferredTreeRebuild = true;
|
||||||
|
AbstractGameList::addEntries(items);
|
||||||
|
m_deferredTreeRebuild = false;
|
||||||
|
rebuildTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryTree::addEntry(LibraryEntryRef item) {
|
||||||
|
if (m_items.contains(item)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString folder = item->base();
|
||||||
|
if (!m_pathNodes.contains(folder)) {
|
||||||
|
QTreeWidgetItem* i = new TreeWidgetItem;
|
||||||
|
i->setText(0, folder.section("/", -1));
|
||||||
|
m_pathNodes.insert(folder, i);
|
||||||
|
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
|
||||||
|
m_widget->addTopLevelItem(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeWidgetItem* i = new TreeWidgetItem;
|
||||||
|
i->setText(COL_NAME, item->displayTitle());
|
||||||
|
i->setText(COL_LOCATION, QDir::toNativeSeparators(item->base()));
|
||||||
|
i->setText(COL_PLATFORM, nicePlatformFormat(item->platform()));
|
||||||
|
i->setFilesize(item->filesize());
|
||||||
|
i->setTextAlignment(COL_SIZE, Qt::AlignRight);
|
||||||
|
i->setText(COL_CRC32, QString("%0").arg(item->crc32(), 8, 16, QChar('0')));
|
||||||
|
m_items.insert(item, i);
|
||||||
|
|
||||||
|
rebuildTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryTree::removeEntry(LibraryEntryRef item) {
|
||||||
|
if (!m_items.contains(item)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete m_items.take(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryTree::rebuildTree() {
|
||||||
|
if (m_deferredTreeRebuild) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryEntryRef currentGame = selectedEntry();
|
||||||
|
|
||||||
|
int count = m_widget->topLevelItemCount();
|
||||||
|
for (int a = 0; a < count; a++) {
|
||||||
|
m_widget->takeTopLevelItem(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QTreeWidgetItem* i : m_pathNodes.values()) {
|
||||||
|
count = i->childCount();
|
||||||
|
for (int a = 0; a < count; a++) {
|
||||||
|
i->takeChild(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_currentStyle == LibraryStyle::STYLE_TREE) {
|
||||||
|
for (QTreeWidgetItem* i : m_pathNodes.values()) {
|
||||||
|
m_widget->addTopLevelItem(i);
|
||||||
|
}
|
||||||
|
for (QTreeWidgetItem* i : m_items.values()) {
|
||||||
|
m_pathNodes.value(m_items.key(i)->base())->addChild(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (QTreeWidgetItem* i : m_items.values()) {
|
||||||
|
m_widget->addTopLevelItem(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_widget->expandAll();
|
||||||
|
resizeAllCols();
|
||||||
|
selectEntry(currentGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* Copyright (c) 2014-2017 waddlesplash
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef QGBA_LIBRARY_TREE
|
||||||
|
#define QGBA_LIBRARY_TREE
|
||||||
|
|
||||||
|
#include <QTreeWidget>
|
||||||
|
|
||||||
|
#include "LibraryController.h"
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class LibraryTree final : public AbstractGameList {
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Columns {
|
||||||
|
COL_NAME = 0,
|
||||||
|
COL_LOCATION = 1,
|
||||||
|
COL_PLATFORM = 2,
|
||||||
|
COL_SIZE = 3,
|
||||||
|
COL_CRC32 = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit LibraryTree(LibraryController* parent = nullptr);
|
||||||
|
~LibraryTree();
|
||||||
|
|
||||||
|
// AbstractGameList stuff
|
||||||
|
virtual LibraryEntryRef selectedEntry() override;
|
||||||
|
virtual void selectEntry(LibraryEntryRef game) override;
|
||||||
|
|
||||||
|
virtual void setViewStyle(LibraryStyle newStyle) override;
|
||||||
|
|
||||||
|
virtual void addEntries(QList<LibraryEntryRef> items) override;
|
||||||
|
virtual void addEntry(LibraryEntryRef item) override;
|
||||||
|
virtual void removeEntry(LibraryEntryRef item) override;
|
||||||
|
|
||||||
|
virtual QWidget* widget() override { return m_widget; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTreeWidget* m_widget;
|
||||||
|
LibraryStyle m_currentStyle;
|
||||||
|
|
||||||
|
LibraryController* m_controller;
|
||||||
|
|
||||||
|
bool m_deferredTreeRebuild = false;
|
||||||
|
QMap<LibraryEntryRef, QTreeWidgetItem*> m_items;
|
||||||
|
QMap<QString, QTreeWidgetItem*> m_pathNodes;
|
||||||
|
|
||||||
|
void rebuildTree();
|
||||||
|
void resizeAllCols();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
QString niceSizeFormat(size_t filesize) {
|
||||||
|
double size = filesize;
|
||||||
|
QString unit = "B";
|
||||||
|
if (size >= 1024.0) {
|
||||||
|
size /= 1024.0;
|
||||||
|
unit = "kiB";
|
||||||
|
}
|
||||||
|
if (size >= 1024.0) {
|
||||||
|
size /= 1024.0;
|
||||||
|
unit = "MiB";
|
||||||
|
}
|
||||||
|
return QString("%0 %1").arg(size, 0, 'f', 1).arg(unit);
|
||||||
|
}
|
||||||
|
QString nicePlatformFormat(mPlatform platform) {
|
||||||
|
switch (platform) {
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
case PLATFORM_GBA:
|
||||||
|
return QObject::tr("GBA");
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
case PLATFORM_GB:
|
||||||
|
return QObject::tr("GB");
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return QObject::tr("?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef QGBA_UTILS_H
|
||||||
|
#define QGBA_UTILS_H
|
||||||
|
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
QString niceSizeFormat(size_t filesize);
|
||||||
|
QString nicePlatformFormat(mPlatform platform);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue