fix: cleanup UI, detect FAT and turn off links

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers 2023-02-09 16:19:38 -07:00
parent 397e7f0363
commit bc8336a4b1
9 changed files with 175 additions and 61 deletions

View File

@ -37,7 +37,6 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QProcess> #include <QProcess>
#include <QDebug> #include <QDebug>
//#include "Application.h"
/** /**
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing. * This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.

View File

@ -1053,4 +1053,32 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
return true; return true;
} }
/**
* @brief if the Filesystem is symlink capable
*
*/
bool canLinkOnFS(const QString& path)
{
FilesystemInfo info = statFS(path);
return canLinkOnFS(info);
}
bool canLinkOnFS(const FilesystemInfo& info)
{
return canLinkOnFS(info.fsType);
}
bool canLinkOnFS(FilesystemType type)
{
return !s_non_link_filesystems.contains(type);
}
/**
* @brief if the Filesystem is symlink capable on both ends
*
*/
bool canLink(const QString& src, const QString& dst)
{
return canLinkOnFS(src) && canLinkOnFS(dst);
}
} }

View File

@ -330,6 +330,7 @@ enum class FilesystemType {
HFSPLUS, HFSPLUS,
HFSX, HFSX,
FUSEBLK, FUSEBLK,
F2FS,
UNKNOWN UNKNOWN
}; };
@ -348,6 +349,7 @@ static const QMap<FilesystemType, QString> s_filesystem_type_names = {
{FilesystemType::HFSPLUS, QString("HFSPLUS")}, {FilesystemType::HFSPLUS, QString("HFSPLUS")},
{FilesystemType::HFSX, QString("HFSX")}, {FilesystemType::HFSX, QString("HFSX")},
{FilesystemType::FUSEBLK, QString("FUSEBLK")}, {FilesystemType::FUSEBLK, QString("FUSEBLK")},
{FilesystemType::F2FS, QString("F2FS")},
{FilesystemType::UNKNOWN, QString("UNKNOWN")} {FilesystemType::UNKNOWN, QString("UNKNOWN")}
}; };
@ -368,6 +370,7 @@ static const QMap<QString, FilesystemType> s_filesystem_type_names_inverse = {
{QString("HFSX"), FilesystemType::HFSX}, {QString("HFSX"), FilesystemType::HFSX},
{QString("HFS"), FilesystemType::HFS}, {QString("HFS"), FilesystemType::HFS},
{QString("FUSEBLK"), FilesystemType::FUSEBLK}, {QString("FUSEBLK"), FilesystemType::FUSEBLK},
{QString("F2FS"), FilesystemType::F2FS},
{QString("UNKNOWN"), FilesystemType::UNKNOWN} {QString("UNKNOWN"), FilesystemType::UNKNOWN}
}; };
@ -405,7 +408,7 @@ bool canCloneOnFS(const FilesystemInfo& info);
bool canCloneOnFS(FilesystemType type); bool canCloneOnFS(FilesystemType type);
/** /**
* @brief if the Filesystem is reflink/clone capable and both are on the same device * @brief if the Filesystems are reflink/clone capable and both are on the same device
* *
*/ */
bool canClone(const QString& src, const QString& dst); bool canClone(const QString& src, const QString& dst);
@ -457,4 +460,23 @@ class clone : public QObject {
*/ */
bool clone_file(const QString& src, const QString& dst, std::error_code& ec); bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
static const QList<FilesystemType> s_non_link_filesystems = {
FilesystemType::FAT,
};
/**
* @brief if the Filesystem is symlink capable
*
*/
bool canLinkOnFS(const QString& path);
bool canLinkOnFS(const FilesystemInfo& info);
bool canLinkOnFS(FilesystemType type);
/**
* @brief if the Filesystem is symlink capable on both ends
*
*/
bool canLink(const QString& src, const QString& dst);
} }

View File

@ -93,9 +93,9 @@ bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const
return copyScreenshots; return copyScreenshots;
} }
bool InstanceCopyPrefs::isLinkFilesEnabled() const bool InstanceCopyPrefs::isUseSymLinksEnabled() const
{ {
return linkFiles; return useSymLinks;
} }
bool InstanceCopyPrefs::isUseHardLinksEnabled() const bool InstanceCopyPrefs::isUseHardLinksEnabled() const
@ -159,9 +159,9 @@ void InstanceCopyPrefs::enableCopyScreenshots(bool b)
copyScreenshots = b; copyScreenshots = b;
} }
void InstanceCopyPrefs::enableLinkFiles(bool b) void InstanceCopyPrefs::enableUseSymLinks(bool b)
{ {
linkFiles = b; useSymLinks = b;
} }
void InstanceCopyPrefs::enableLinkRecursively(bool b) void InstanceCopyPrefs::enableLinkRecursively(bool b)

View File

@ -19,7 +19,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyServersEnabled() const; [[nodiscard]] bool isCopyServersEnabled() const;
[[nodiscard]] bool isCopyModsEnabled() const; [[nodiscard]] bool isCopyModsEnabled() const;
[[nodiscard]] bool isCopyScreenshotsEnabled() const; [[nodiscard]] bool isCopyScreenshotsEnabled() const;
[[nodiscard]] bool isLinkFilesEnabled() const; [[nodiscard]] bool isUseSymLinksEnabled() const;
[[nodiscard]] bool isLinkRecursivelyEnabled() const; [[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const; [[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const; [[nodiscard]] bool isDontLinkSavesEnabled() const;
@ -33,7 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyServers(bool b); void enableCopyServers(bool b);
void enableCopyMods(bool b); void enableCopyMods(bool b);
void enableCopyScreenshots(bool b); void enableCopyScreenshots(bool b);
void enableLinkFiles(bool b); void enableUseSymLinks(bool b);
void enableLinkRecursively(bool b); void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b); void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b); void enableDontLinkSaves(bool b);
@ -48,7 +48,7 @@ struct InstanceCopyPrefs {
bool copyServers = true; bool copyServers = true;
bool copyMods = true; bool copyMods = true;
bool copyScreenshots = true; bool copyScreenshots = true;
bool linkFiles = false; bool useSymLinks = false;
bool linkRecursively = false; bool linkRecursively = false;
bool useHardLinks = false; bool useHardLinks = false;
bool dontLinkSaves = false; bool dontLinkSaves = false;

View File

@ -13,7 +13,7 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
QString filters = prefs.getSelectedFiltersAsRegex(); QString filters = prefs.getSelectedFiltersAsRegex();
m_useLinks = prefs.isLinkFilesEnabled(); m_useLinks = prefs.isUseSymLinksEnabled();
m_linkRecursively = prefs.isLinkRecursivelyEnabled(); m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled(); m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled(); m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();

View File

@ -87,21 +87,26 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled()); ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled()); ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled()); ui->symbolicLinksCheckbox->setChecked(m_selectedOptions.isUseSymLinksEnabled());
ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled()); ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled()); ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType; auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
m_cloneSupported = FS::canCloneOnFS(detectedOS); m_cloneSupported = FS::canCloneOnFS(detectedOS);
m_linkSupported = FS::canLinkOnFS(detectedOS);
if (m_cloneSupported) { if (m_cloneSupported) {
ui->cloneSupportedLabel->setText(tr("Clone / Reflink is supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS))); ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
} else { } else {
ui->cloneSupportedLabel->setText(tr("Clone / Reflink not supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS))); ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
} }
updateLinkOptions();
updateUseCloneCheckbox(); updateUseCloneCheckbox();
} }
CopyInstanceDialog::~CopyInstanceDialog() CopyInstanceDialog::~CopyInstanceDialog()
@ -170,6 +175,21 @@ void CopyInstanceDialog::updateUseCloneCheckbox()
ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled()); ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
} }
void CopyInstanceDialog::updateLinkOptions()
{
ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked());
ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked());
ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled());
ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled());
bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
ui->recursiveLinkCheckbox->setEnabled(m_linkSupported && linksInUse && !ui->hardLinksCheckbox->isChecked());
ui->dontLinkSavesCheckbox->setEnabled(m_linkSupported && linksInUse);
ui->recursiveLinkCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isDontLinkSavesEnabled());
}
void CopyInstanceDialog::on_iconButton_clicked() void CopyInstanceDialog::on_iconButton_clicked()
{ {
IconPickerDialog dlg(this); IconPickerDialog dlg(this);
@ -245,10 +265,20 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
updateSelectAllCheckbox(); updateSelectAllCheckbox();
} }
void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked) void CopyInstanceDialog::on_symbolicLinksCheckbox_stateChanged(int state)
{ {
m_selectedOptions.enableLinkFiles(checked); m_selectedOptions.enableUseSymLinks(state == Qt::Checked);
updateUseCloneCheckbox(); updateUseCloneCheckbox();
updateLinkOptions();
}
void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
{
m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
ui->recursiveLinkCheckbox->setChecked(true);
}
updateLinkOptions();
} }
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state) void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
@ -261,14 +291,6 @@ void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
} }
void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
{
m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
ui->recursiveLinkCheckbox->setChecked(true);
}
}
void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state) void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{ {
m_selectedOptions.enableDontLinkSaves(state == Qt::Checked); m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);

View File

@ -56,9 +56,9 @@ slots:
void on_copyServersCheckbox_stateChanged(int state); void on_copyServersCheckbox_stateChanged(int state);
void on_copyModsCheckbox_stateChanged(int state); void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state); void on_copyScreenshotsCheckbox_stateChanged(int state);
void on_linkFilesGroup_toggled(bool checked); void on_symbolicLinksCheckbox_stateChanged(int state);
void on_recursiveLinkCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state); void on_hardLinksCheckbox_stateChanged(int state);
void on_recursiveLinkCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state); void on_dontLinkSavesCheckbox_stateChanged(int state);
void on_useCloneCheckbox_stateChanged(int state); void on_useCloneCheckbox_stateChanged(int state);
@ -66,6 +66,7 @@ private:
void checkAllCheckboxes(const bool& b); void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox(); void updateSelectAllCheckbox();
void updateUseCloneCheckbox(); void updateUseCloneCheckbox();
void updateLinkOptions();
/* data */ /* data */
Ui::CopyInstanceDialog *ui; Ui::CopyInstanceDialog *ui;
@ -73,4 +74,5 @@ private:
InstancePtr m_original; InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions; InstanceCopyPrefs m_selectedOptions;
bool m_cloneSupported = false; bool m_cloneSupported = false;
bool m_linkSupported = false;
}; };

View File

@ -9,8 +9,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>531</width> <width>527</width>
<height>640</height> <height>699</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -138,7 +138,7 @@
<item> <item>
<widget class="QGroupBox" name="copyOptionsGroup"> <widget class="QGroupBox" name="copyOptionsGroup">
<property name="title"> <property name="title">
<string>Instance copy options</string> <string>Instance Copy Options</string>
</property> </property>
<layout class="QGridLayout" name="copyOptionsLayout"> <layout class="QGridLayout" name="copyOptionsLayout">
<item row="6" column="1"> <item row="6" column="1">
@ -224,53 +224,92 @@
<item> <item>
<widget class="QGroupBox" name="linkFilesGroup"> <widget class="QGroupBox" name="linkFilesGroup">
<property name="toolTip"> <property name="toolTip">
<string>Use symbolic links instead of copying files.</string> <string>Use symbolic or hard links instead of copying files.</string>
</property> </property>
<property name="title"> <property name="title">
<string>Link files instead of copying them</string> <string>Symbolic and Hard Link Options</string>
</property> </property>
<property name="flat"> <property name="flat">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>false</bool>
</property> </property>
<property name="checked"> <property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
<layout class="QVBoxLayout" name="linkOptionsLayout"> <layout class="QVBoxLayout" name="linkOptionsLayout">
<item> <item>
<widget class="QCheckBox" name="recursiveLinkCheckbox"> <widget class="QLabel" name="linkOptionsLabel">
<property name="text"> <property name="text">
<string>Link files recursively</string> <string>Links are supported on most filesystems except FAT</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="hardLinksCheckbox"> <layout class="QGridLayout" name="linkOptionsGridLayout" rowstretch="0,0,0,0,0,0" columnstretch="0,0" rowminimumheight="0,0,0,0,0,0" columnminimumwidth="0,0">
<property name="enabled"> <property name="leftMargin">
<bool>false</bool> <number>6</number>
</property> </property>
<property name="toolTip"> <property name="topMargin">
<string>Use hard links instead of symbolic links</string> <number>6</number>
</property> </property>
<property name="text"> <property name="rightMargin">
<string>Use hard links</string> <number>6</number>
</property> </property>
</widget> <property name="bottomMargin">
</item> <number>6</number>
<item>
<widget class="QCheckBox" name="dontLinkSavesCheckbox">
<property name="toolTip">
<string>If &quot;copy saves&quot; is selected world save data will be copied instead of linked and thus not shared between instances.</string>
</property> </property>
<property name="text"> <item row="3" column="0">
<string>Don't link saves</string> <widget class="QCheckBox" name="hardLinksCheckbox">
</property> <property name="enabled">
<property name="checked"> <bool>true</bool>
<bool>false</bool> </property>
</property> <property name="toolTip">
</widget> <string>Use hard links instead of symbolic links</string>
</property>
<property name="text">
<string>Use hard links</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="recursiveLinkCheckbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Link files recursively</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="dontLinkSavesCheckbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>If &quot;copy saves&quot; is selected world save data will be copied instead of linked and thus not shared between instances.</string>
</property>
<property name="text">
<string>Don't link saves</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="symbolicLinksCheckbox">
<property name="text">
<string>Use symbloic links</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -278,7 +317,7 @@
<item> <item>
<widget class="QGroupBox" name="horizontalGroupBox"> <widget class="QGroupBox" name="horizontalGroupBox">
<property name="title"> <property name="title">
<string>Clone / Reflink (Copy On Write) Options</string> <string>CoW (Copy-on-Write) Options</string>
</property> </property>
<layout class="QHBoxLayout" name="useCloneLayout"> <layout class="QHBoxLayout" name="useCloneLayout">
<item> <item>
@ -287,7 +326,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Use Clone / Reflink</string> <string>Clone instead of copying</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -300,7 +339,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>Clone / Reflink not supported on this filesystem</string> <string>Your filesystem and/or OS doesn't support reflinks</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -340,9 +379,11 @@
<tabstop>copyServersCheckbox</tabstop> <tabstop>copyServersCheckbox</tabstop>
<tabstop>copyResPacksCheckbox</tabstop> <tabstop>copyResPacksCheckbox</tabstop>
<tabstop>copyModsCheckbox</tabstop> <tabstop>copyModsCheckbox</tabstop>
<tabstop>symbolicLinksCheckbox</tabstop>
<tabstop>recursiveLinkCheckbox</tabstop> <tabstop>recursiveLinkCheckbox</tabstop>
<tabstop>hardLinksCheckbox</tabstop> <tabstop>hardLinksCheckbox</tabstop>
<tabstop>dontLinkSavesCheckbox</tabstop> <tabstop>dontLinkSavesCheckbox</tabstop>
<tabstop>useCloneCheckbox</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections> <connections>
@ -353,8 +394,8 @@
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>263</x> <x>269</x>
<y>571</y> <y>692</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>157</x> <x>157</x>
@ -369,8 +410,8 @@
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>331</x> <x>337</x>
<y>571</y> <y>692</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>286</x> <x>286</x>