Support for custom data folders, disable single instance when used.

This commit is contained in:
Martin Rotter 2020-11-14 09:47:15 +01:00
parent c5822369cc
commit 31f010290d
6 changed files with 112 additions and 52 deletions

View File

@ -83,6 +83,8 @@
#define CLI_LOG_SHORT "l"
#define CLI_LOG_LONG "log"
#define CLI_DAT_SHORT "d"
#define CLI_DAT_LONG "data"
#define HTTP_HEADERS_ACCEPT "Accept"
#define HTTP_HEADERS_CONTENT_TYPE "Content-Type"

View File

@ -26,6 +26,9 @@ void FormAbout::loadSettingsAndPaths() {
if (qApp->settings()->type() == SettingsProperties::SettingsType::Portable) {
m_ui.m_txtPathsSettingsType->setText(tr("FULLY portable"));
}
else if (qApp->settings()->type() == SettingsProperties::SettingsType::Custom) {
m_ui.m_txtPathsSettingsType->setText(tr("CUSTOM"));
}
else {
m_ui.m_txtPathsSettingsType->setText(tr("NOT portable"));
}

View File

@ -41,27 +41,26 @@
#endif
Application::Application(const QString& id, int& argc, char** argv)
: QtSingleApplication(id, argc, argv),
: QtSingleApplication(id, argc, argv), m_updateFeedsLock(new Mutex()) {
parseCmdArguments();
#if defined(USE_WEBENGINE)
m_urlInterceptor(new NetworkUrlInterceptor(this)),
m_urlInterceptor = new NetworkUrlInterceptor(this);
#endif
m_feedReader(nullptr),
m_quitLogicDone(false),
m_updateFeedsLock(new Mutex()),
m_mainForm(nullptr),
m_trayIcon(nullptr),
m_settings(Settings::setupSettings(this)),
m_webFactory(new WebFactory(this)),
m_system(new SystemFactory(this)),
m_skins(new SkinFactory(this)),
m_localization(new Localization(this)),
m_icons(new IconFactory(this)),
m_database(new DatabaseFactory(this)),
m_downloadManager(nullptr), m_shouldRestart(false) {
parseCmdArguments();
m_feedReader = nullptr;
m_quitLogicDone = false;
m_mainForm = nullptr;
m_trayIcon = nullptr;
m_settings = Settings::setupSettings(this);
m_webFactory = new WebFactory(this);
m_system = new SystemFactory(this);
m_skins = new SkinFactory(this);
m_localization = new Localization(this);
m_icons = new IconFactory(this);
m_database = new DatabaseFactory(this);
m_downloadManager = nullptr;
m_shouldRestart = false;
// Setup debug output system.
qInstallMessageHandler(performLogging);
@ -176,7 +175,9 @@ void Application::offerChanges() const {
}
bool Application::isAlreadyRunning() {
return sendMessage((QStringList() << APP_IS_RUNNING << Application::arguments().mid(1)).join(ARGUMENTS_LIST_SEPARATOR));
return m_allowMultipleInstances
? false
: sendMessage((QStringList() << APP_IS_RUNNING << Application::arguments().mid(1)).join(ARGUMENTS_LIST_SEPARATOR));
}
FeedReader* Application::feedReader() {
@ -271,18 +272,21 @@ void Application::setMainForm(FormMain* main_form) {
m_mainForm = main_form;
}
QString Application::configFolder() {
QString Application::configFolder() const {
return IOFactory::getSystemFolder(QStandardPaths::GenericConfigLocation);
}
QString Application::userDataAppFolder() {
QString Application::userDataAppFolder() const {
// In "app" folder, we would like to separate all user data into own subfolder,
// therefore stick to "data" folder in this mode.
return applicationDirPath() + QDir::separator() + QSL("data");
}
QString Application::userDataFolder() {
if (settings()->type() == SettingsProperties::SettingsType::Portable) {
if (settings()->type() == SettingsProperties::SettingsType::Custom) {
return customDataFolder();
}
else if (settings()->type() == SettingsProperties::SettingsType::Portable) {
return userDataAppFolder();
}
else {
@ -290,7 +294,7 @@ QString Application::userDataFolder() {
}
}
QString Application::userDataHomeFolder() {
QString Application::userDataHomeFolder() const {
// Fallback folder.
const QString home_folder = homeFolder() + QDir::separator() + QSL(APP_LOW_H_NAME) + QDir::separator() + QSL("data");
@ -306,15 +310,15 @@ QString Application::userDataHomeFolder() {
}
}
QString Application::tempFolder() {
QString Application::tempFolder() const {
return IOFactory::getSystemFolder(QStandardPaths::TempLocation);
}
QString Application::documentsFolder() {
QString Application::documentsFolder() const {
return IOFactory::getSystemFolder(QStandardPaths::DocumentsLocation);
}
QString Application::homeFolder() {
QString Application::homeFolder() const {
#if defined (Q_OS_ANDROID)
return IOFactory::getSystemFolder(QStandardPaths::GenericDataLocation);
#else
@ -551,6 +555,20 @@ void Application::onFeedUpdatesFinished(const FeedDownloadResults& results) {
}
}
void Application::setupCustomDataFolder(const QString& data_folder) {
if (!QDir().mkpath(data_folder)) {
qCriticalNN << "Failed to create custom data path" << QUOTE_W_SPACE(data_folder) << "thus falling back to standard setup.";
m_customDataFolder = QString();
return;
}
// Disable single instance mode.
m_allowMultipleInstances = true;
// Save custom data folder.
m_customDataFolder = data_folder;
}
void Application::determineFirstRuns() {
m_firstRunEver = settings()->value(GROUP(General),
SETTING(General::FirstRun)).toBool();
@ -564,8 +582,11 @@ void Application::determineFirstRuns() {
void Application::parseCmdArguments() {
QCommandLineOption log_file(QStringList() << CLI_LOG_SHORT << CLI_LOG_LONG,
"Write application debug log to file.", "log-file");
QCommandLineOption custom_data_folder(QStringList() << CLI_DAT_SHORT << CLI_DAT_LONG,
"Use custom folder for user data and disable single instance application mode.",
"user-data-folder");
m_cmdParser.addOption(log_file);
m_cmdParser.addOptions({ log_file, custom_data_folder });
m_cmdParser.addHelpOption();
m_cmdParser.addVersionOption();
m_cmdParser.setApplicationDescription(APP_NAME);
@ -573,4 +594,20 @@ void Application::parseCmdArguments() {
m_cmdParser.process(*this);
s_customLogFile = m_cmdParser.value(CLI_LOG_SHORT);
if (!m_cmdParser.value(CLI_DAT_SHORT).isEmpty()) {
auto data_folder = QDir::toNativeSeparators(m_cmdParser.value(CLI_DAT_SHORT));
qDebugNN << "User wants to use custom directory for user data (and disable single instance mode):"
<< QUOTE_W_SPACE_DOT(data_folder);
setupCustomDataFolder(data_folder);
}
else {
m_allowMultipleInstances = false;
}
}
QString Application::customDataFolder() const {
return m_customDataFolder;
}

View File

@ -85,14 +85,15 @@ class RSSGUARD_DLLSPEC Application : public QtSingleApplication {
NetworkUrlInterceptor* urlIinterceptor();
#endif
QString tempFolder();
QString documentsFolder();
QString homeFolder();
QString configFolder();
QString tempFolder() const;
QString documentsFolder() const;
QString homeFolder() const;
QString configFolder() const;
// These return user ready folders.
QString userDataAppFolder();
QString userDataHomeFolder();
QString userDataAppFolder() const;
QString userDataHomeFolder() const;
QString customDataFolder() const;
// Returns the base folder to which store user data, the "data" folder.
// NOTE: Use this to get correct path under which store user data.
@ -143,6 +144,7 @@ class RSSGUARD_DLLSPEC Application : public QtSingleApplication {
void onFeedUpdatesFinished(const FeedDownloadResults& results);
private:
void setupCustomDataFolder(const QString& data_folder);
void determineFirstRuns();
void eliminateFirstRuns();
void parseCmdArguments();
@ -186,6 +188,8 @@ class RSSGUARD_DLLSPEC Application : public QtSingleApplication {
bool m_shouldRestart;
bool m_firstRunEver;
bool m_firstRunCurrentVersion;
QString m_customDataFolder;
bool m_allowMultipleInstances;
};
inline Application* Application::instance() {

View File

@ -372,13 +372,18 @@ Settings* Settings::setupSettings(QObject* parent) {
// Portable settings are available, use them.
new_settings = new Settings(properties.m_absoluteSettingsFileName, QSettings::IniFormat, properties.m_type, parent);
// Check if portable settings are available.
if (properties.m_type == SettingsProperties::SettingsType::Portable) {
qDebugNN << LOGSEC_CORE
<< "Initializing settings in"
<< QUOTE_W_SPACE(QDir::toNativeSeparators(properties.m_absoluteSettingsFileName))
<< "(portable way).";
}
else if (properties.m_type == SettingsProperties::SettingsType::Custom) {
qDebugNN << LOGSEC_CORE
<< "Initializing settings in"
<< QUOTE_W_SPACE(QDir::toNativeSeparators(properties.m_absoluteSettingsFileName))
<< "(custom way).";
}
else {
qDebugNN << LOGSEC_CORE
<< "Initializing settings in"
@ -395,7 +400,14 @@ SettingsProperties Settings::determineProperties() {
properties.m_settingsSuffix = QDir::separator() + QSL(APP_CFG_PATH) + QDir::separator() + QSL(APP_CFG_FILE);
const QString app_path = qApp->userDataAppFolder();
const QString home_path = qApp->userDataHomeFolder();
const QString custom_path = qApp->customDataFolder();
if (!custom_path.isEmpty()) {
// User wants to have his user data in custom folder, okay.
properties.m_type = SettingsProperties::SettingsType::Custom;
properties.m_baseDirectory = custom_path;
}
else {
// We will use PORTABLE settings only and only if it is available and NON-PORTABLE
// settings was not initialized before.
#if defined (Q_OS_LINUX) || defined (Q_OS_ANDROID) || defined (Q_OS_MACOSOS)
@ -411,11 +423,12 @@ SettingsProperties Settings::determineProperties() {
if (will_we_use_portable_settings) {
properties.m_type = SettingsProperties::SettingsType::Portable;
properties.m_baseDirectory = app_path;
properties.m_baseDirectory = QDir::toNativeSeparators(app_path);
}
else {
properties.m_type = SettingsProperties::SettingsType::NonPortable;
properties.m_baseDirectory = home_path;
properties.m_baseDirectory = QDir::toNativeSeparators(home_path);
}
}
properties.m_absoluteSettingsFileName = properties.m_baseDirectory + properties.m_settingsSuffix;

View File

@ -9,7 +9,8 @@
struct SettingsProperties {
enum class SettingsType {
Portable,
NonPortable
NonPortable,
Custom
};
SettingsType m_type;