Merge branch 'more-xdg-dirs'

Add possibility to use wxStandardPaths and, hence, wxFileConfig, in
XDG-compliant (but backwards-incompatible) mode.

Closes #17727.
This commit is contained in:
Vadim Zeitlin 2017-03-15 00:49:11 +01:00
commit 3eda902331
11 changed files with 299 additions and 98 deletions

View File

@ -195,6 +195,7 @@ wxOSX:
Unix:
- Support new gstreamer API in 1.7.2+ in wxMediaCtrl (Sebastian Dröge).
- Add wxStandardPaths::SetFileLayout(FileLayout_XDG) (Martin Koegler).
3.1.0: (released 2016-02-29)

View File

@ -29,6 +29,8 @@ public:
virtual wxString GetUserLocalDataDir() const wxOVERRIDE;
virtual wxString GetPluginsDir() const wxOVERRIDE;
virtual wxString GetUserDir(Dir userDir) const wxOVERRIDE;
virtual wxString MakeConfigFileName(const wxString& basename,
ConfigFileConv conv) const wxOVERRIDE;
// MSW-specific methods

View File

@ -32,6 +32,8 @@ public:
GetLocalizedResourcesDir(const wxString& lang,
ResourceCat category = ResourceCat_None) const wxOVERRIDE;
virtual wxString GetUserDir(Dir userDir) const wxOVERRIDE;
virtual wxString MakeConfigFileName(const wxString& basename,
ConfigFileConv conv) const wxOVERRIDE;
protected:
// Ctor is protected, use wxStandardPaths::Get() instead of instantiating

View File

@ -60,6 +60,21 @@ public:
Dir_Videos
};
// Layout to use for user config/data files under Unix.
enum FileLayout
{
FileLayout_Classic, // Default: use home directory.
FileLayout_XDG // Recommended: use XDG specification.
};
// Naming convention for the config files under Unix.
enum ConfigFileConv
{
ConfigFileConv_Dot, // Classic Unix dot-file convention.
ConfigFileConv_Ext // Use .conf extension.
};
// return the global standard paths object
static wxStandardPaths& Get();
@ -155,6 +170,10 @@ public:
virtual wxString GetUserDir(Dir userDir) const;
virtual wxString
MakeConfigFileName(const wxString& basename,
ConfigFileConv conv = ConfigFileConv_Ext) const = 0;
// virtual dtor for the base class
virtual ~wxStandardPathsBase();
@ -166,6 +185,15 @@ public:
bool UsesAppInfo(int info) const { return (m_usedAppInfo & info) != 0; }
void SetFileLayout(FileLayout layout)
{
m_fileLayout = layout;
}
FileLayout GetFileLayout() const
{
return m_fileLayout;
}
protected:
// Ctor is protected as this is a base class which should never be created
@ -182,6 +210,9 @@ protected:
// combination of AppInfo_XXX flags used by AppendAppInfo()
int m_usedAppInfo;
// The file layout to use, currently only used under Unix.
FileLayout m_fileLayout;
};
#if wxUSE_STDPATHS

View File

@ -49,6 +49,8 @@ public:
#ifndef __VMS
virtual wxString GetUserDir(Dir userDir) const wxOVERRIDE;
#endif
virtual wxString MakeConfigFileName(const wxString& basename,
ConfigFileConv conv) const wxOVERRIDE;
protected:
// Ctor is protected, use wxStandardPaths::Get() instead of instantiating

View File

@ -135,6 +135,66 @@ public:
Dir_Videos
};
/**
Possible values for SetFileLayout() argument.
The elements of this enum correspond to the different file layout
standards under Unix systems.
@since 3.1.1
*/
enum FileLayout
{
/**
Use the classic file layout.
User configuration and data files are located directly in the home
directory.
This is the default behaviour for compatibility reasons.
*/
FileLayout_Classic,
/**
Use a XDG styled file layout.
File layout follows the XDG Base Directory Specification (see
https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html).
This is the recommended layout for new applications.
*/
FileLayout_XDG
};
/**
Possible values for MakeConfigFileName() naming convention argument.
The values in this enum are only used under Unix and only when using
the classic Unix convention for file layout, in XDG mode, XDG naming
convention is used unconditionally.
@since 3.1.1
*/
enum ConfigFileConv
{
/**
Use the class Unix dot-file convention.
Prepend the dot to the file base name.
This value is ignored when in XDG mode, where MakeConfigFileName()
always behaves as if ConfigFileConv_Ext was specified.
*/
ConfigFileConv_Dot,
/**
Use @c .conf extension for the file names.
This convention is always used in XDG mode.
*/
ConfigFileConv_Ext
};
/**
MSW-specific function undoing the effect of IgnoreAppSubDir() calls.
@ -300,8 +360,11 @@ public:
virtual wxString GetTempDir() const;
/**
Return the directory for the user config files:
- Unix: @c ~ (the home directory)
Return the directory for the user config files.
This directory is:
- Unix: @c ~ (the home directory) or @c XDG_CONFIG_HOME depending on
GetFileLayout() return value
- Windows: @c "C:\Users\username\AppData\Roaming" or
@c "C:\Documents and Settings\username\Application Data"
- Mac: @c ~/Library/Preferences
@ -326,7 +389,8 @@ public:
If the value could not be determined the users home directory is returned.
@note On Unix this supports the xdg user dirs specification.
@note On Unix this method respects the XDG base directory specification
only if SetFileLayout() with @c FileLayout_XDG had been called.
@since 3.1.0
*/
@ -444,6 +508,44 @@ public:
*/
void UseAppInfo(int info);
/**
Returns the current file layout.
The default layout is @c FileLayout_Classic for compatibility, however
newer applications are encouraged to set it to @c FileLayout_XDG on
program startup.
@since 3.1.1
*/
void SetFileLayout(FileLayout layout);
/**
Returns the current file layout.
@see SetFileLayout()
@since 3.1.1
*/
FileLayout GetFileLayout() const;
/**
Return the file name which would be used by wxFileConfig if it were
constructed with @a basename.
@a conv is used to construct the name of the file under Unix and only
matters when using the class file layout, i.e. if SetFileLayout() had
@e not been called with @c FileLayout_XDG argument. In this case, this
argument is used to determine whether to use an extension or a leading
dot. When following XDG specification, the function always appends the
extension, regardless of @a conv value. Finally, this argument is not
used at all under non-Unix platforms.
@since 3.1.1
*/
virtual wxString
MakeConfigFileName(const wxString& basename,
ConfigFileConv conv = ConfigFileConv_Ext) const;
protected:
/**
Protected default constructor.

View File

@ -247,26 +247,6 @@ public:
// static functions
// ----------------------------------------------------------------------------
// this function modifies in place the given wxFileName object if it doesn't
// already have an extension
//
// note that it's slightly misnamed under Mac as there it doesn't add an
// extension but modifies the file name instead, so you shouldn't suppose that
// fn.HasExt() is true after it returns
static void AddConfFileExtIfNeeded(wxFileName& fn)
{
if ( !fn.HasExt() )
{
#if defined( __WXMAC__ )
fn.SetName(fn.GetName() + wxT(" Preferences"));
#elif defined( __UNIX__ )
fn.SetExt(wxT("conf"));
#else // Windows
fn.SetExt(wxT("ini"));
#endif // UNIX/Win
}
}
wxString wxFileConfig::GetGlobalDir()
{
return wxStandardPaths::Get().GetConfigDir();
@ -286,31 +266,28 @@ wxString wxFileConfig::GetLocalDir(int style)
wxFileName wxFileConfig::GetGlobalFile(const wxString& szFile)
{
wxFileName fn(GetGlobalDir(), szFile);
wxStandardPathsBase& stdp = wxStandardPaths::Get();
AddConfFileExtIfNeeded(fn);
return fn;
return wxFileName(GetGlobalDir(), stdp.MakeConfigFileName(szFile));
}
wxFileName wxFileConfig::GetLocalFile(const wxString& szFile, int style)
{
wxFileName fn(GetLocalDir(style), szFile);
wxStandardPathsBase& stdp = wxStandardPaths::Get();
#if defined( __UNIX__ ) && !defined( __WXMAC__ )
if ( !(style & wxCONFIG_USE_SUBDIR) )
{
// dot-files under Unix start with, well, a dot (but OTOH they usually
// don't have any specific extension)
fn.SetName(wxT('.') + fn.GetName());
}
else // we do append ".conf" extension to config files in subdirectories
#endif // defined( __UNIX__ ) && !defined( __WXMAC__ )
{
AddConfFileExtIfNeeded(fn);
}
// If the config file is located in a subdirectory, we always use an
// extension for it, but we use just the leading dot if it is located
// directly in the home directory. Note that if wxStandardPaths is
// configured to follow XDG specification, all config files go to a
// subdirectory of XDG_CONFIG_HOME anyhow, so in this case we'll still end
// up using the extension even if wxCONFIG_USE_SUBDIR is not set, but this
// is the correct and expected (if a little confusing) behaviour.
const wxStandardPaths::ConfigFileConv
conv = style & wxCONFIG_USE_SUBDIR
? wxStandardPaths::ConfigFileConv_Ext
: wxStandardPaths::ConfigFileConv_Dot;
return fn;
return wxFileName(GetLocalDir(style), stdp.MakeConfigFileName(szFile, conv));
}
// ----------------------------------------------------------------------------

View File

@ -97,6 +97,9 @@ wxStandardPathsBase::wxStandardPathsBase()
// Derived classes can call this in their constructors
// to set the platform-specific settings
UseAppInfo(AppInfo_AppName);
// Default for compatibility with the existing config files.
SetFileLayout(FileLayout_Classic);
}
wxStandardPathsBase::~wxStandardPathsBase()

View File

@ -345,6 +345,16 @@ wxString wxStandardPaths::GetPluginsDir() const
return GetAppDir();
}
wxString
wxStandardPaths::MakeConfigFileName(const wxString& basename,
ConfigFileConv WXUNUSED(conv)) const
{
wxFileName fn(wxEmptyString, basename);
fn.SetExt(wxT("ini"));
return fn.GetFullName();
}
// ============================================================================
// wxStandardPathsWin16 implementation
// ============================================================================

View File

@ -19,6 +19,7 @@
#if wxUSE_STDPATHS
#include "wx/filename.h"
#include "wx/stdpaths.h"
#include "wx/osx/private.h"
#include "wx/osx/core/cfstring.h"
@ -129,4 +130,13 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const
return GetFMDirectory(dirType, NSUserDomainMask);
}
wxString
wxStandardPaths::MakeConfigFileName(const wxString& basename,
ConfigFileConv WXUNUSED(conv)) const
{
wxFileName fn(wxEmptyString, basename);
fn.SetName(fn.GetName() + wxT(" Preferences"));
return fn.GetFullName();
}
#endif // wxUSE_STDPATHS

View File

@ -50,9 +50,34 @@ void wxStandardPaths::SetInstallPrefix(const wxString& prefix)
m_prefix = prefix;
}
// Helper function returning the value of XDG_CONFIG_HOME environment variable
// or its default value if it is not defined.
static wxString GetXDGConfigHome()
{
wxString dir;
if ( !wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() )
dir = wxFileName::GetHomeDir() + wxS("/.config");
return dir;
}
wxString wxStandardPaths::GetUserConfigDir() const
{
return wxFileName::GetHomeDir();
wxString dir;
switch ( GetFileLayout() )
{
case FileLayout_Classic:
dir = wxFileName::GetHomeDir();
break;
case FileLayout_XDG:
dir = GetXDGConfigHome();
break;
}
wxASSERT_MSG( !dir.empty(), wxS("unsupported file layout") );
return dir;
}
@ -235,69 +260,70 @@ wxStandardPaths::GetLocalizedResourcesDir(const wxString& lang,
wxString wxStandardPaths::GetUserDir(Dir userDir) const
{
// Note that we do not use the file layout here because there is no reason
// not to respect the XDG convention even if SetFileLayout(FileLayout_XDG)
// hadn't been called: we're not bound by any backwards compatibility
// considerations as there can't be any pre-existing config or data files
// in the home directory that wouldn't be found any longer after updating
// the version of wxWidgets used by the application.
wxLogNull logNull;
const wxString homeDir = wxFileName::GetHomeDir();
if (userDir == Dir_Cache)
{
wxLogNull logNull;
wxString homeDir = wxFileName::GetHomeDir();
if (userDir == Dir_Cache)
wxString cacheDir;
if ( !wxGetEnv(wxS("XDG_CACHE_HOME"), &cacheDir) )
cacheDir = homeDir + wxS("/.cache");
return cacheDir;
}
const wxFileName dirsFile(GetXDGConfigHome(), wxS("user-dirs.dirs"));
if ( dirsFile.FileExists() )
{
wxString userDirId;
switch (userDir)
{
if (wxGetenv(wxT("XDG_CACHE_HOME")))
return wxGetenv(wxT("XDG_CACHE_HOME"));
else
return homeDir + wxT("/.cache");
case Dir_Desktop:
userDirId = "XDG_DESKTOP_DIR";
break;
case Dir_Downloads:
userDirId = "XDG_DOWNLOAD_DIR";
break;
case Dir_Music:
userDirId = "XDG_MUSIC_DIR";
break;
case Dir_Pictures:
userDirId = "XDG_PICTURES_DIR";
break;
case Dir_Videos:
userDirId = "XDG_VIDEOS_DIR";
break;
default:
userDirId = "XDG_DOCUMENTS_DIR";
break;
}
wxString configPath;
if (wxGetenv(wxT("XDG_CONFIG_HOME")))
configPath = wxGetenv(wxT("XDG_CONFIG_HOME"));
else
configPath = homeDir + wxT("/.config");
wxString dirsFile = configPath + wxT("/user-dirs.dirs");
if (wxFileExists(dirsFile))
wxTextFile textFile;
if ( textFile.Open(dirsFile.GetFullPath()) )
{
wxString userDirId;
switch (userDir)
for ( wxString line = textFile.GetFirstLine();
!textFile.Eof();
line = textFile.GetNextLine() )
{
case Dir_Desktop:
userDirId = "XDG_DESKTOP_DIR";
break;
case Dir_Downloads:
userDirId = "XDG_DOWNLOAD_DIR";
break;
case Dir_Music:
userDirId = "XDG_MUSIC_DIR";
break;
case Dir_Pictures:
userDirId = "XDG_PICTURES_DIR";
break;
case Dir_Videos:
userDirId = "XDG_VIDEOS_DIR";
break;
default:
userDirId = "XDG_DOCUMENTS_DIR";
break;
}
wxTextFile textFile;
if (textFile.Open(dirsFile))
{
size_t i;
for (i = 0; i < textFile.GetLineCount(); i++)
int pos = line.Find(userDirId);
if (pos != wxNOT_FOUND)
{
wxString line(textFile[i]);
int pos = line.Find(userDirId);
if (pos != wxNOT_FOUND)
{
wxString value = line.AfterFirst(wxT('='));
value.Replace(wxT("$HOME"), homeDir);
value.Trim(true);
value.Trim(false);
// Remove quotes
value.Replace("\"", "", true /* replace all */);
if (!value.IsEmpty() && wxDirExists(value))
return value;
else
break;
}
wxString value = line.AfterFirst(wxT('='));
value.Replace(wxT("$HOME"), homeDir);
value.Trim(true);
value.Trim(false);
// Remove quotes
value.Replace("\"", "", true /* replace all */);
if (!value.IsEmpty() && wxDirExists(value))
return value;
else
break;
}
}
}
@ -308,4 +334,39 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const
#endif // __VMS/!__VMS
wxString
wxStandardPaths::MakeConfigFileName(const wxString& basename,
ConfigFileConv conv) const
{
wxFileName fn(wxEmptyString, basename);
bool addExt = false;
switch ( GetFileLayout() )
{
case FileLayout_Classic:
switch ( conv )
{
case ConfigFileConv_Dot:
fn.SetName(wxT('.') + fn.GetName());
break;
case ConfigFileConv_Ext:
addExt = true;
break;
}
break;
case FileLayout_XDG:
// Dot files are never used in XDG mode.
addExt = true;
break;
}
if ( addExt )
fn.SetExt(wxS("conf"));
return fn.GetFullName();
}
#endif // wxUSE_STDPATHS