Add src/xmbmodel.cpp
This commit is contained in:
parent
41816c0a16
commit
999da7ed86
176
src/xmbmodel.cpp
Normal file
176
src/xmbmodel.cpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include "xmbmodel.h"
|
||||||
|
#include "apppaths.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "xmbentry.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
static QString pickIcon(const QString& desired, bool category) {
|
||||||
|
if (!desired.isEmpty() && QFileInfo::exists(desired)) return QUrl::fromLocalFile(desired).toString();
|
||||||
|
return Util::fallbackIconQrc(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
static XmbEntry parseDesktop(const QString& filePath) {
|
||||||
|
// Minimal .desktop parsing via QSettings INI
|
||||||
|
QSettings s(filePath, QSettings::IniFormat);
|
||||||
|
s.setIniCodec("UTF-8");
|
||||||
|
|
||||||
|
XmbEntry e;
|
||||||
|
e.type = EntryType::Desktop;
|
||||||
|
e.path = filePath;
|
||||||
|
e.title = s.value("Desktop Entry/Name", QFileInfo(filePath).baseName()).toString();
|
||||||
|
e.subtitle = s.value("Desktop Entry/Comment", "").toString();
|
||||||
|
e.exec = s.value("Desktop Entry/Exec", "").toString();
|
||||||
|
// Icon field kann Theme-Icon sein; wir lassen es erstmal als String stehen (Fallback greift).
|
||||||
|
const auto iconField = s.value("Desktop Entry/Icon", "").toString();
|
||||||
|
e.icon = iconField.isEmpty() ? Util::fallbackIconQrc(false) : iconField;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static XmbEntry parseLinkFile(const QString& filePath) {
|
||||||
|
// Einfaches INI: [Link] title=..., icon=..., url=..., exec=..., args=...
|
||||||
|
QSettings s(filePath, QSettings::IniFormat);
|
||||||
|
s.setIniCodec("UTF-8");
|
||||||
|
|
||||||
|
XmbEntry e;
|
||||||
|
e.type = EntryType::Link;
|
||||||
|
e.path = filePath;
|
||||||
|
e.title = s.value("Link/title", QFileInfo(filePath).baseName()).toString();
|
||||||
|
e.subtitle = s.value("Link/subtitle", "").toString();
|
||||||
|
e.url = s.value("Link/url", "").toString();
|
||||||
|
e.exec = s.value("Link/exec", "").toString();
|
||||||
|
e.args = s.value("Link/args", "").toString().split(' ', Qt::SkipEmptyParts);
|
||||||
|
const auto icon = s.value("Link/icon", "").toString();
|
||||||
|
e.icon = icon.isEmpty() ? Util::fallbackIconQrc(false) : icon;
|
||||||
|
e.gameId = s.value("Link/gameId", "").toString(); // optional
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static XmbEntry parseJsonAction(const QString& filePath) {
|
||||||
|
QFile f(filePath);
|
||||||
|
XmbEntry e;
|
||||||
|
e.type = EntryType::JsonAction;
|
||||||
|
e.path = filePath;
|
||||||
|
|
||||||
|
if (!f.open(QIODevice::ReadOnly)) {
|
||||||
|
e.title = QFileInfo(filePath).baseName();
|
||||||
|
e.icon = Util::fallbackIconQrc(false);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto doc = QJsonDocument::fromJson(f.readAll());
|
||||||
|
const auto o = doc.object();
|
||||||
|
e.title = o.value("title").toString(QFileInfo(filePath).baseName());
|
||||||
|
e.subtitle = o.value("subtitle").toString();
|
||||||
|
e.exec = o.value("exec").toString();
|
||||||
|
e.url = o.value("url").toString();
|
||||||
|
e.gameId = o.value("gameId").toString();
|
||||||
|
const auto icon = o.value("icon").toString();
|
||||||
|
e.icon = icon.isEmpty() ? Util::fallbackIconQrc(false) : icon;
|
||||||
|
|
||||||
|
if (o.value("args").isArray()) {
|
||||||
|
for (const auto& v : o.value("args").toArray())
|
||||||
|
e.args << v.toString();
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
XmbModel::XmbModel(QObject* parent) : QObject(parent) {
|
||||||
|
m_cfg.load();
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString XmbModel::assetUrl(const QString& rel) const {
|
||||||
|
// rel: "sounds/move.wav" etc.
|
||||||
|
const QString base = AppPaths::assetsRoot();
|
||||||
|
const QString abs = QDir(base).filePath(rel);
|
||||||
|
if (QFileInfo::exists(abs)) return QUrl::fromLocalFile(abs).toString();
|
||||||
|
|
||||||
|
// Fallback: resources/
|
||||||
|
return QString("qrc:/%1").arg(rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString XmbModel::bootQml() const {
|
||||||
|
// 1) Config boot animation
|
||||||
|
const QString p = m_cfg.bootAnimation();
|
||||||
|
if (!p.isEmpty() && QFileInfo::exists(p)) return QUrl::fromLocalFile(p).toString();
|
||||||
|
// 2) bundled fallback
|
||||||
|
return "qrc:/qml/FallbackBoot.qml";
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList XmbModel::itemsForCategory(int idx) const {
|
||||||
|
if (idx < 0 || idx >= m_categories.size()) return {};
|
||||||
|
const auto cat = m_categories[idx].toMap();
|
||||||
|
return cat.value("items").toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmbModel::reload() {
|
||||||
|
m_cfg.load();
|
||||||
|
build();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmbModel::build() {
|
||||||
|
m_categories.clear();
|
||||||
|
|
||||||
|
const QDir xmbDir(AppPaths::xmbRoot());
|
||||||
|
xmbDir.mkpath("."); // ensure exists
|
||||||
|
|
||||||
|
// Order aus config; wenn Ordner fehlen: trotzdem als Kategorie anlegen
|
||||||
|
const auto order = m_cfg.order();
|
||||||
|
|
||||||
|
for (const auto& catNameRaw : order) {
|
||||||
|
const QString catName = catNameRaw.trimmed();
|
||||||
|
QVariantMap cat;
|
||||||
|
cat["title"] = catName;
|
||||||
|
cat["icon"] = Util::fallbackIconQrc(true);
|
||||||
|
|
||||||
|
const QString catPath = xmbDir.filePath(catName);
|
||||||
|
QDir catDir(catPath);
|
||||||
|
|
||||||
|
QVariantList items;
|
||||||
|
|
||||||
|
if (catDir.exists()) {
|
||||||
|
const auto entries = catDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot,
|
||||||
|
QDir::Name | QDir::IgnoreCase);
|
||||||
|
|
||||||
|
for (const auto& fi : entries) {
|
||||||
|
XmbEntry e;
|
||||||
|
|
||||||
|
if (fi.isDir()) {
|
||||||
|
e.type = EntryType::Folder;
|
||||||
|
e.title = Util::sanitizeTitle(fi.fileName());
|
||||||
|
e.subtitle = fi.absoluteFilePath();
|
||||||
|
e.icon = Util::fallbackIconQrc(false);
|
||||||
|
e.path = fi.absoluteFilePath();
|
||||||
|
items << e.toVariant();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString ext = fi.suffix().toLower();
|
||||||
|
if (ext == "desktop") {
|
||||||
|
e = parseDesktop(fi.absoluteFilePath());
|
||||||
|
items << e.toVariant();
|
||||||
|
} else if (ext == "link") {
|
||||||
|
e = parseLinkFile(fi.absoluteFilePath());
|
||||||
|
items << e.toVariant();
|
||||||
|
} else if (ext == "json") {
|
||||||
|
e = parseJsonAction(fi.absoluteFilePath());
|
||||||
|
items << e.toVariant();
|
||||||
|
} else {
|
||||||
|
// ignorieren oder als Unknown anzeigen
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cat["items"] = items;
|
||||||
|
m_categories << cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit categoriesChanged();
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user