diff options
| -rw-r--r-- | src/src/envshortcut.cpp | 158 | ||||
| -rw-r--r-- | src/src/loot.cpp | 35 | ||||
| -rw-r--r-- | src/src/mainwindow.cpp | 10 | ||||
| -rw-r--r-- | src/src/moshortcut.cpp | 12 | ||||
| -rw-r--r-- | src/src/pluginlistview.cpp | 2 |
5 files changed, 182 insertions, 35 deletions
diff --git a/src/src/envshortcut.cpp b/src/src/envshortcut.cpp index 1bb39f6..49a122b 100644 --- a/src/src/envshortcut.cpp +++ b/src/src/envshortcut.cpp @@ -155,7 +155,7 @@ Shortcut::Shortcut(const Executable& exe) : Shortcut() Shortcut& Shortcut::name(const QString& s)
{
- m_name = MOBase::sanitizeFileName(s);
+ m_name = s;
return *this;
}
@@ -353,16 +353,76 @@ QString toString(Shortcut::Locations loc) #include <QDir>
#include <QFile>
#include <QFileInfo>
+#include <QProcessEnvironment>
+#include <QStandardPaths>
#include <QString>
+#include <QTextStream>
-class Executable;
+#include "executableslist.h"
+#include "instancemanager.h"
namespace env
{
+// Returns the path to the AppImage file itself, or falls back to the
+// running binary if not running from an AppImage.
+static QString appImageOrBinary()
+{
+ QString appImage = QProcessEnvironment::systemEnvironment().value("APPIMAGE");
+ if (!appImage.isEmpty() && QFile::exists(appImage)) {
+ return appImage;
+ }
+ return QFileInfo(qApp->applicationFilePath()).absoluteFilePath();
+}
+
Shortcut::Shortcut() : m_iconIndex(0) {}
-Shortcut::Shortcut(const Executable&) : Shortcut() {}
+Shortcut::Shortcut(const Executable& exe) : Shortcut()
+{
+ const auto i = *InstanceManager::singleton().currentInstance();
+
+ m_name = exe.title();
+ m_target = appImageOrBinary();
+
+ // For portable instances, use the absolute directory path so MO2 can
+ // find it (line 595 in instancemanager.cpp handles abs path lookup).
+ // For global instances, use the display name.
+ QString instanceId = i.isPortable() ? QDir(i.directory()).absolutePath()
+ : i.displayName();
+ m_arguments = QString("\"moshortcut://%1:%2\"")
+ .arg(instanceId)
+ .arg(exe.title());
+
+ m_description = QString("Run %1 with Fluorine").arg(exe.title());
+
+ // .exe icons can't be used on Linux — only use native icon formats.
+ // Fall back to the Fluorine app icon bundled in the AppImage.
+ if (exe.usesOwnIcon()) {
+ QString iconPath = exe.binaryInfo().absoluteFilePath();
+ if (!iconPath.endsWith(".exe", Qt::CaseInsensitive)) {
+ m_icon = iconPath;
+ }
+ }
+ if (m_icon.isEmpty()) {
+ // Install the Fluorine icon to the user's icon theme so it persists
+ // after the AppImage unmounts, then reference it by icon name.
+ QString appDir = QProcessEnvironment::systemEnvironment().value("APPDIR");
+ if (!appDir.isEmpty()) {
+ QString bundled = appDir + "/usr/share/icons/hicolor/256x256/apps/com.fluorine.manager.png";
+ if (QFile::exists(bundled)) {
+ QString destDir = QDir::homePath() + "/.local/share/icons/hicolor/256x256/apps";
+ QString dest = destDir + "/com.fluorine.manager.png";
+ if (!QFile::exists(dest)) {
+ QDir().mkpath(destDir);
+ QFile::copy(bundled, dest);
+ }
+ }
+ }
+ m_icon = "com.fluorine.manager";
+ }
+
+ m_workingDirectory = QFileInfo(m_target).absolutePath();
+}
Shortcut& Shortcut::name(const QString& s)
{
@@ -401,41 +461,105 @@ Shortcut& Shortcut::workingDirectory(const QString& s) return *this;
}
-bool Shortcut::exists(Locations) const
+bool Shortcut::exists(Locations loc) const
{
- // .lnk shortcuts are a Windows concept; no-op on Linux
- return false;
+ return QFile::exists(shortcutPath(loc));
}
-bool Shortcut::toggle(Locations)
+bool Shortcut::toggle(Locations loc)
{
- return false;
+ if (exists(loc)) {
+ return remove(loc);
+ } else {
+ return add(loc);
+ }
}
-bool Shortcut::add(Locations)
+bool Shortcut::add(Locations loc)
{
- // TODO: could create .desktop files on Linux in the future
- return false;
+ if (loc != Desktop) {
+ return false;
+ }
+
+ const QString path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ QDir().mkpath(QFileInfo(path).absolutePath());
+
+ QFile file(path);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ return false;
+ }
+
+ QTextStream out(&file);
+ out << "[Desktop Entry]\n";
+ out << "Type=Application\n";
+ out << "Name=" << (m_name.isEmpty() ? "Fluorine" : m_name) << "\n";
+ if (!m_description.isEmpty()) {
+ out << "Comment=" << m_description << "\n";
+ }
+ // .desktop Exec values require quoting paths that contain spaces
+ out << "Exec=\"" << m_target << "\"";
+ if (!m_arguments.isEmpty()) {
+ out << " " << m_arguments;
+ }
+ out << "\n";
+ if (!m_workingDirectory.isEmpty()) {
+ out << "Path=" << m_workingDirectory << "\n";
+ }
+ if (!m_icon.isEmpty()) {
+ out << "Icon=" << m_icon << "\n";
+ }
+ out << "Terminal=false\n";
+ out << "Categories=Game;\n";
+
+ file.close();
+
+ // Make it executable (required by some desktop environments)
+ file.setPermissions(file.permissions() | QFileDevice::ExeUser);
+
+ return true;
}
-bool Shortcut::remove(Locations)
+bool Shortcut::remove(Locations loc)
{
- return false;
+ const QString path = shortcutPath(loc);
+ if (path.isEmpty()) {
+ return false;
+ }
+ return QFile::remove(path);
}
-QString Shortcut::shortcutPath(Locations) const
+QString Shortcut::shortcutPath(Locations loc) const
{
- return {};
+ const QString dir = shortcutDirectory(loc);
+ if (dir.isEmpty()) {
+ return {};
+ }
+ return dir + "/" + shortcutFilename();
}
-QString Shortcut::shortcutDirectory(Locations) const
+QString Shortcut::shortcutDirectory(Locations loc) const
{
+ if (loc == Desktop) {
+ // XDG desktop directory
+ QString desktop = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
+ if (!desktop.isEmpty()) {
+ return desktop;
+ }
+ return QDir::homePath() + "/Desktop";
+ }
return {};
}
QString Shortcut::shortcutFilename() const
{
- return {};
+ if (m_name.isEmpty()) {
+ return "fluorine.desktop";
+ }
+ return m_name + ".desktop";
}
QString toString(Shortcut::Locations loc)
diff --git a/src/src/loot.cpp b/src/src/loot.cpp index 5e796df..c96a26c 100644 --- a/src/src/loot.cpp +++ b/src/src/loot.cpp @@ -1496,30 +1496,37 @@ static bool launchLootGui(QWidget* parent, OrganizerCore& core) bool isFileTime = core.managedGame()->loadOrderMechanism() ==
MOBase::IPluginGame::LoadOrderMechanism::FileTime;
- // LOOT may write "Plugins.txt" (capital P, Windows convention) instead of
- // "plugins.txt", so check both case variants on case-sensitive Linux.
- QString lootPluginsActual = lootPlugins;
- if (!QFile::exists(lootPluginsActual)) {
- QString alt = localPath + "/Plugins.txt";
- if (QFile::exists(alt)) {
- lootPluginsActual = alt;
+ // LOOT may write "Plugins.txt" (capital P) while we deployed "plugins.txt"
+ // (lowercase). On case-sensitive Linux these are two different files — the
+ // lowercase one is our pre-LOOT copy, the capital-P one is LOOT's output.
+ // Prefer the capital-P variant when it exists; otherwise fall back to the
+ // lowercase file (in case a future LOOT version writes lowercase).
+ QString lootPluginsActual;
+ {
+ QString capitalP = localPath + "/Plugins.txt";
+ if (QFile::exists(capitalP)) {
+ lootPluginsActual = capitalP;
+ } else if (QFile::exists(lootPlugins)) {
+ lootPluginsActual = lootPlugins;
}
}
- if (QFile::exists(lootPluginsActual)) {
+ if (!lootPluginsActual.isEmpty()) {
QFile::remove(profilePlugins);
QFile::copy(lootPluginsActual, profilePlugins);
log::info("Copied LOOT {} back to profile", lootPluginsActual);
}
if (!isFileTime) {
- QString lootLoadOrderActual = lootLoadOrder;
- if (!QFile::exists(lootLoadOrderActual)) {
- QString alt = localPath + "/Loadorder.txt";
- if (QFile::exists(alt)) {
- lootLoadOrderActual = alt;
+ QString lootLoadOrderActual;
+ {
+ QString capitalL = localPath + "/Loadorder.txt";
+ if (QFile::exists(capitalL)) {
+ lootLoadOrderActual = capitalL;
+ } else if (QFile::exists(lootLoadOrder)) {
+ lootLoadOrderActual = lootLoadOrder;
}
}
- if (QFile::exists(lootLoadOrderActual)) {
+ if (!lootLoadOrderActual.isEmpty()) {
QFile::remove(profileLoadOrder);
QFile::copy(lootLoadOrderActual, profileLoadOrder);
log::info("Copied LOOT {} back to profile", lootLoadOrderActual);
diff --git a/src/src/mainwindow.cpp b/src/src/mainwindow.cpp index 1eb5e36..e94939f 100644 --- a/src/src/mainwindow.cpp +++ b/src/src/mainwindow.cpp @@ -372,8 +372,12 @@ MainWindow::MainWindow(Settings& settings, OrganizerCore& organizerCore, this, SLOT(linkToolbar()));
m_LinkDesktop = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Desktop"), this,
SLOT(linkDesktop()));
+#ifdef _WIN32
m_LinkStartMenu = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this,
SLOT(linkMenu()));
+#else
+ m_LinkStartMenu = nullptr;
+#endif
ui->linkButton->setMenu(linkMenu);
ui->listOptionsBtn->setMenu(
@@ -2823,8 +2827,10 @@ void MainWindow::on_linkButton_pressed() m_LinkDesktop->setIcon(shortcut.exists(env::Shortcut::Desktop) ? removeIcon
: addIcon);
- m_LinkStartMenu->setIcon(shortcut.exists(env::Shortcut::StartMenu) ? removeIcon
- : addIcon);
+ if (m_LinkStartMenu) {
+ m_LinkStartMenu->setIcon(shortcut.exists(env::Shortcut::StartMenu) ? removeIcon
+ : addIcon);
+ }
}
void MainWindow::on_actionSettings_triggered()
diff --git a/src/src/moshortcut.cpp b/src/src/moshortcut.cpp index 2dc5c05..0172f50 100644 --- a/src/src/moshortcut.cpp +++ b/src/src/moshortcut.cpp @@ -1,5 +1,7 @@ #include "moshortcut.h"
+#include <QDir>
+
MOShortcut::MOShortcut(const QString& link)
: m_valid(link.startsWith("moshortcut://")), m_hasInstance(false),
m_hasExecutable(false)
@@ -59,7 +61,15 @@ bool MOShortcut::isForInstance(const Instance& i) const // empty instance name means portable
return i.isPortable();
} else {
- return (i.displayName() == m_instance);
+ // Compare by display name, or by absolute directory path for portable
+ // instances identified by their filesystem path.
+ if (i.displayName() == m_instance) {
+ return true;
+ }
+ if (QDir::isAbsolutePath(m_instance) && i.isPortable()) {
+ return QDir(i.directory()).absolutePath() == QDir(m_instance).absolutePath();
+ }
+ return false;
}
}
diff --git a/src/src/pluginlistview.cpp b/src/src/pluginlistview.cpp index 58b0548..225c3bc 100644 --- a/src/src/pluginlistview.cpp +++ b/src/src/pluginlistview.cpp @@ -216,7 +216,7 @@ void PluginListView::onSortButtonClicked() m_didUpdateMasterList = true;
}
- m_core->refreshESPList(false);
+ m_core->refreshESPList(true);
m_core->savePluginList();
}
}
|
