Add wxFileConfig::MigrateLocalFile()
This can be useful for the existing applications switching to using XDG-compliant config files location as they can just call this function on startup to keep using the existing file.
This commit is contained in:
parent
ebe0847932
commit
486865b446
4 changed files with 208 additions and 1 deletions
|
|
@ -123,6 +123,22 @@ public:
|
|||
return GetLocalFile(szFile, style).GetFullPath();
|
||||
}
|
||||
|
||||
// Function to migrate, i.e. move, an existing local config file to another
|
||||
// location. Old and new style determine the existing and new file paths.
|
||||
struct MigrationResult
|
||||
{
|
||||
// If empty, it means the old file wasn't found and nothing was done.
|
||||
wxString oldPath;
|
||||
|
||||
// The name of the new file.
|
||||
wxString newPath;
|
||||
|
||||
// If empty, means the file was successfully migrated.
|
||||
wxString error;
|
||||
};
|
||||
static MigrationResult
|
||||
MigrateLocalFile(const wxString& name, int newStyle, int oldStyle = 0);
|
||||
|
||||
// ctor & dtor
|
||||
// New constructor: one size fits all. Specify wxCONFIG_USE_LOCAL_FILE or
|
||||
// wxCONFIG_USE_GLOBAL_FILE to say which files should be used.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@
|
|||
path is backwards-compatible but not recommended any more and it is advised
|
||||
to call wxStandardPaths::SetFileLayout() with
|
||||
wxStandardPaths::FileLayout_XDG parameter to change the default path to
|
||||
`~/.config/appname.conf`.
|
||||
`~/.config/appname.conf`. MigrateLocalFile() may be helpful for moving the
|
||||
existing configuration file to the new location.
|
||||
|
||||
Alternatively, it is possible to specify ::wxCONFIG_USE_XDG flag in the
|
||||
style parameter of the constructor to use this XDG-compliant path without
|
||||
|
|
@ -99,6 +100,70 @@ public:
|
|||
static wxString GetGlobalFileName(const wxString& szFile);
|
||||
static wxString GetLocalFileName(const wxString& szFile, int style = 0);
|
||||
|
||||
/**
|
||||
Contains return value of MigrateLocalFile().
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
struct MigrationResult
|
||||
{
|
||||
/// If empty, it means the old file wasn't found and nothing was done.
|
||||
wxString oldPath;
|
||||
|
||||
/// The name of the new file.
|
||||
wxString newPath;
|
||||
|
||||
/// If empty, means the file was successfully migrated.
|
||||
wxString error;
|
||||
};
|
||||
|
||||
/**
|
||||
Move the existing configuration file to a new location.
|
||||
|
||||
This function is useful for moving legacy configuration files in
|
||||
`~/.appname` or `~/.appname/appname.conf` to the XDG-compliant location
|
||||
under `~/.config`. To do this, simply specify ::wxCONFIG_USE_XDG as
|
||||
part of @a newStyle.
|
||||
|
||||
The returned MigrationResult object describes what, if anything, was
|
||||
done: if its `oldPath` member is empty, it means that the file
|
||||
corresponding to @a oldStyle was not found and nothing was done.
|
||||
Otherwise, if its `error` member is empty, the old file was found and
|
||||
moved to `newPath`. And if `error` is not empty, it contains the
|
||||
user-readable error message describing why moving the file failed.
|
||||
|
||||
Typical example of using this function is shown in the widgets sample:
|
||||
@code
|
||||
// Execute this early during the application startup, before the
|
||||
// global wxConfig object is created.
|
||||
const auto res = wxFileConfig::MigrateLocalFile("app", wxCONFIG_USE_XDG);
|
||||
if ( !res.oldPath.empty() ) {
|
||||
if ( res.error.empty() ) {
|
||||
wxLogMessage("Config file was migrated from \"%s\" to \"%s\"",
|
||||
res.oldPath, res.newPath);
|
||||
} else {
|
||||
wxLogWarning("Migrating old config failed: %s.", res.error);
|
||||
}
|
||||
}
|
||||
|
||||
// Note that this must be done after calling MigrateLocalFile(),
|
||||
// otherwise the old style would use XDG layout already and the actual
|
||||
// file at non-XDG-compliant location wouldn't be migrated.
|
||||
wxStandardPaths::Get().SetFileLayout(wxStandardPaths::FileLayout_XDG);
|
||||
@endcode
|
||||
|
||||
@param name Name of the configuration file.
|
||||
@param newStyle Style which is used by the current version of the
|
||||
program, typically including ::wxCONFIG_USE_XDG and possibly also
|
||||
including ::wxCONFIG_USE_SUBDIR.
|
||||
@param oldStyle Style which was used by the previous versions of the
|
||||
program, possibly including ::wxCONFIG_USE_SUBDIR.
|
||||
|
||||
@since 3.3.0
|
||||
*/
|
||||
static MigrationResult
|
||||
MigrateLocalFile(const wxString& name, int newStyle, int oldStyle = 0);
|
||||
|
||||
/**
|
||||
Saves all config data to the given stream, returns @true if data was saved
|
||||
successfully or @false on error.
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@
|
|||
#include "wx/msgdlg.h"
|
||||
#endif
|
||||
|
||||
#include "wx/config.h"
|
||||
#include "wx/stdpaths.h"
|
||||
|
||||
#include "wx/sysopt.h"
|
||||
#include "wx/bookctrl.h"
|
||||
#include "wx/treebook.h"
|
||||
|
|
@ -141,6 +144,30 @@ public:
|
|||
#if USE_LOG
|
||||
m_logTarget = nullptr;
|
||||
#endif // USE_LOG
|
||||
|
||||
#ifdef wxHAS_CONFIG_AS_FILECONFIG
|
||||
// We want to put our config file (implicitly created for persistent
|
||||
// controls settings) in XDG-compliant location, so we want to change
|
||||
// the default file layout, but before doing this migrate any existing
|
||||
// config files to the new location as the previous versions of this
|
||||
// sample didn't use XDG layout.
|
||||
const auto
|
||||
res = wxFileConfig::MigrateLocalFile("widgets", wxCONFIG_USE_XDG);
|
||||
if ( !res.oldPath.empty() )
|
||||
{
|
||||
if ( res.error.empty() )
|
||||
{
|
||||
wxLogMessage("Config file was migrated from \"%s\" to \"%s\"",
|
||||
res.oldPath, res.newPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogWarning("Migrating old config failed: %s.", res.error);
|
||||
}
|
||||
}
|
||||
|
||||
wxStandardPaths::Get().SetFileLayout(wxStandardPaths::FileLayout_XDG);
|
||||
#endif // wxHAS_CONFIG_AS_FILECONFIG
|
||||
}
|
||||
WidgetsApp(const WidgetsApp&) = delete;
|
||||
WidgetsApp& operator=(const WidgetsApp&) = delete;
|
||||
|
|
|
|||
|
|
@ -296,6 +296,105 @@ wxFileName wxFileConfig::GetLocalFile(const wxString& szFile, int style)
|
|||
return wxFileName(GetLocalDir(style), stdp.MakeConfigFileName(szFile, conv));
|
||||
}
|
||||
|
||||
wxFileConfig::MigrationResult
|
||||
wxFileConfig::MigrateLocalFile(const wxString& name, int newStyle, int oldStyle)
|
||||
{
|
||||
MigrationResult res;
|
||||
|
||||
const auto oldPath = GetLocalFile(name, oldStyle);
|
||||
if ( !oldPath.FileExists() )
|
||||
return res;
|
||||
|
||||
const auto newPath = GetLocalFile(name, newStyle);
|
||||
if ( newPath == oldPath )
|
||||
return res;
|
||||
|
||||
res.oldPath = oldPath.GetFullPath();
|
||||
res.newPath = newPath.GetFullPath();
|
||||
|
||||
// This class ensures that we (at least try to) rename the existing config
|
||||
// file back to its original name if we fail with an error.
|
||||
class RenameBackOnError
|
||||
{
|
||||
public:
|
||||
explicit RenameBackOnError(wxFileConfig::MigrationResult& res)
|
||||
: m_res(res)
|
||||
{
|
||||
}
|
||||
|
||||
void Init(const wxString& tempPath)
|
||||
{
|
||||
m_tempPath = tempPath;
|
||||
}
|
||||
|
||||
void Dismiss()
|
||||
{
|
||||
m_tempPath.clear();
|
||||
}
|
||||
|
||||
~RenameBackOnError()
|
||||
{
|
||||
if ( !m_tempPath.empty() )
|
||||
{
|
||||
if ( !wxRenameFile(m_tempPath, m_res.oldPath) )
|
||||
{
|
||||
// This should never happen, but if it does, do at least
|
||||
// let the user know that we moved their file to a wrong
|
||||
// place and couldn't put it back.
|
||||
m_res.error += wxString::Format(
|
||||
_(" and additionally, the existing configuration file"
|
||||
" was renamed to \"%s\" and couldn't be renamed back,"
|
||||
" please rename it to its original path \"%s\""),
|
||||
m_tempPath,
|
||||
m_res.oldPath
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MigrationResult& m_res;
|
||||
|
||||
wxString m_tempPath;
|
||||
} renameBackOnError{res};
|
||||
|
||||
wxString currentPath = res.oldPath;
|
||||
|
||||
const auto newDir = newPath.GetPath();
|
||||
if ( !wxFileName::DirExists(newDir) )
|
||||
{
|
||||
// There is an annoying failure mode here when the new directory can't
|
||||
// be created because its name is the same as the name of the existing
|
||||
// file, e.g. when oldStyle==0 and newStyle==wxCONFIG_USE_SUBDIR and
|
||||
// XDG layout is not used, so check for this specially.
|
||||
if ( newDir == res.oldPath )
|
||||
{
|
||||
currentPath = wxFileName::CreateTempFileName(currentPath);
|
||||
if ( !wxRenameFile(res.oldPath, currentPath) )
|
||||
{
|
||||
res.error = _("failed to rename the existing file");
|
||||
return res;
|
||||
}
|
||||
|
||||
renameBackOnError.Init(currentPath);
|
||||
}
|
||||
|
||||
if ( !wxFileName::Mkdir(newDir, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL) )
|
||||
{
|
||||
res.error = _("failed to create the new file directory");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !wxRenameFile(currentPath, res.newPath) )
|
||||
{
|
||||
res.error = _("failed to move the file to the new location");
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ctor
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue