From 8d5d00db9dcab0bbb5c1734605921f415d2ac9ce Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Sun, 27 Nov 2016 14:31:02 +0100 Subject: [PATCH 01/10] Implement MakeConfigFileName --- include/wx/msw/stdpaths.h | 1 + include/wx/osx/cocoa/stdpaths.h | 1 + include/wx/stdpaths.h | 2 ++ include/wx/unix/stdpaths.h | 1 + interface/wx/stdpaths.h | 12 ++++++++++ src/common/fileconf.cpp | 41 ++------------------------------- src/msw/stdpaths.cpp | 8 +++++++ src/osx/cocoa/stdpaths.mm | 8 +++++++ src/unix/stdpaths.cpp | 11 +++++++++ 9 files changed, 46 insertions(+), 39 deletions(-) diff --git a/include/wx/msw/stdpaths.h b/include/wx/msw/stdpaths.h index e9e1e8b231..23564fc9cb 100644 --- a/include/wx/msw/stdpaths.h +++ b/include/wx/msw/stdpaths.h @@ -29,6 +29,7 @@ public: virtual wxString GetUserLocalDataDir() const wxOVERRIDE; virtual wxString GetPluginsDir() const wxOVERRIDE; virtual wxString GetUserDir(Dir userDir) const wxOVERRIDE; + virtual wxString MakeConfigFileName(const wxString& basename, int style) const wxOVERRIDE; // MSW-specific methods diff --git a/include/wx/osx/cocoa/stdpaths.h b/include/wx/osx/cocoa/stdpaths.h index 59c33f2019..d361d5f65d 100644 --- a/include/wx/osx/cocoa/stdpaths.h +++ b/include/wx/osx/cocoa/stdpaths.h @@ -32,6 +32,7 @@ public: GetLocalizedResourcesDir(const wxString& lang, ResourceCat category = ResourceCat_None) const wxOVERRIDE; virtual wxString GetUserDir(Dir userDir) const wxOVERRIDE; + virtual wxString MakeConfigFileName(const wxString& basename, int style) const wxOVERRIDE; protected: // Ctor is protected, use wxStandardPaths::Get() instead of instantiating diff --git a/include/wx/stdpaths.h b/include/wx/stdpaths.h index 8f7c2d43bb..382a99dfd6 100644 --- a/include/wx/stdpaths.h +++ b/include/wx/stdpaths.h @@ -155,6 +155,8 @@ public: virtual wxString GetUserDir(Dir userDir) const; + virtual wxString MakeConfigFileName(const wxString& basename, int style) const = 0; + // virtual dtor for the base class virtual ~wxStandardPathsBase(); diff --git a/include/wx/unix/stdpaths.h b/include/wx/unix/stdpaths.h index 74f915398c..d0ec1469e5 100644 --- a/include/wx/unix/stdpaths.h +++ b/include/wx/unix/stdpaths.h @@ -49,6 +49,7 @@ public: #ifndef __VMS virtual wxString GetUserDir(Dir userDir) const wxOVERRIDE; #endif + virtual wxString MakeConfigFileName(const wxString& basename, int style) const wxOVERRIDE; protected: // Ctor is protected, use wxStandardPaths::Get() instead of instantiating diff --git a/interface/wx/stdpaths.h b/interface/wx/stdpaths.h index 9a1f3cbf9f..6484c94cf1 100644 --- a/interface/wx/stdpaths.h +++ b/interface/wx/stdpaths.h @@ -444,6 +444,18 @@ public: */ void UseAppInfo(int info); + /** + Return the file name which would be used by wxFileConfig as local, + user-specific, file if it were constructed with @a basename. + + @a style has the same meaning as in @ref wxConfigBase::wxConfigBase "wxConfig constructor" + and can contain any combination of styles but only wxCONFIG_USE_SUBDIR bit is + examined by this function. + + Notice that this function cannot be used if @a basename is already a full path name. + */ + virtual wxString MakeConfigFileName(const wxString& basename, int style) const; + protected: /** Protected default constructor. diff --git a/src/common/fileconf.cpp b/src/common/fileconf.cpp index c87bcf82f0..6de548379d 100644 --- a/src/common/fileconf.cpp +++ b/src/common/fileconf.cpp @@ -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,30 +266,13 @@ wxString wxFileConfig::GetLocalDir(int style) wxFileName wxFileConfig::GetGlobalFile(const wxString& szFile) { - wxFileName fn(GetGlobalDir(), szFile); - - AddConfFileExtIfNeeded(fn); - + wxFileName fn(GetGlobalDir(), wxStandardPaths::Get().MakeConfigFileName(szFile, wxCONFIG_USE_SUBDIR)); return fn; } wxFileName wxFileConfig::GetLocalFile(const wxString& szFile, int style) { - wxFileName fn(GetLocalDir(style), szFile); - -#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); - } - + wxFileName fn(GetLocalDir(style), wxStandardPaths::Get().MakeConfigFileName(szFile, style)); return fn; } diff --git a/src/msw/stdpaths.cpp b/src/msw/stdpaths.cpp index 29aa6b9192..081ab600a7 100644 --- a/src/msw/stdpaths.cpp +++ b/src/msw/stdpaths.cpp @@ -369,6 +369,14 @@ wxString wxStandardPaths::GetPluginsDir() const return GetAppDir(); } + +wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int WXUNUSED(style)) const +{ + wxFileName fn(wxEmptyString, basename); + fn.SetExt(wxT("ini")); + return fn.GetFullName(); +} + // ============================================================================ // wxStandardPathsWin16 implementation // ============================================================================ diff --git a/src/osx/cocoa/stdpaths.mm b/src/osx/cocoa/stdpaths.mm index a8b8323d2a..cd9b75b46a 100644 --- a/src/osx/cocoa/stdpaths.mm +++ b/src/osx/cocoa/stdpaths.mm @@ -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,11 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const return GetFMDirectory(dirType, NSUserDomainMask); } +wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int WXUNUSED(style)) const +{ + wxFileName fn(wxEmptyString, basename); + fn.SetName(fn.GetName() + wxT(" Preferences")); + return fn.GetFullName(); +} + #endif // wxUSE_STDPATHS diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index 5f61f19ecb..fecde0c634 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -33,6 +33,7 @@ #include "wx/utils.h" #endif //WX_PRECOMP +#include "wx/fileconf.h" #include "wx/filename.h" #include "wx/log.h" #include "wx/textfile.h" @@ -308,4 +309,14 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const #endif // __VMS/!__VMS +wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int style) const +{ + wxFileName fn(wxEmptyString, basename); + if (style & wxCONFIG_USE_SUBDIR) + fn.SetExt(wxT("conf")); + else + fn.SetName(wxT('.') + fn.GetName()); + return fn.GetFullName(); +} + #endif // wxUSE_STDPATHS From d56d127d3f8385e02a52bb979116ac8672be9157 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Sat, 10 Dec 2016 17:56:04 +0100 Subject: [PATCH 02/10] Implement XDG file layout --- include/wx/stdpaths.h | 16 ++++++++++++++++ interface/wx/stdpaths.h | 26 ++++++++++++++++++++++++++ src/common/stdpbase.cpp | 1 + src/unix/stdpaths.cpp | 13 ++++++++++++- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/include/wx/stdpaths.h b/include/wx/stdpaths.h index 382a99dfd6..d048888ddb 100644 --- a/include/wx/stdpaths.h +++ b/include/wx/stdpaths.h @@ -60,6 +60,12 @@ public: Dir_Videos }; + enum + { + FileLayout_Classic = 0, + FileLayout_XDG = 1 + }; + // return the global standard paths object static wxStandardPaths& Get(); @@ -168,6 +174,15 @@ public: bool UsesAppInfo(int info) const { return (m_usedAppInfo & info) != 0; } + void SetFileLayout(int layout) + { + m_usedFileLayout = layout; + } + + int GetFileLayout() const + { + return m_usedFileLayout; + } protected: // Ctor is protected as this is a base class which should never be created @@ -184,6 +199,7 @@ protected: // combination of AppInfo_XXX flags used by AppendAppInfo() int m_usedAppInfo; + int m_usedFileLayout; }; #if wxUSE_STDPATHS diff --git a/interface/wx/stdpaths.h b/interface/wx/stdpaths.h index 6484c94cf1..81f218f4a0 100644 --- a/interface/wx/stdpaths.h +++ b/interface/wx/stdpaths.h @@ -135,6 +135,19 @@ public: Dir_Videos }; + enum + { + /** + Use the classic file layout + */ + FileLayout_Classic, + + /** + Use a XDG styled file layout (Unix) + */ + FileLayout_XDG + }; + /** MSW-specific function undoing the effect of IgnoreAppSubDir() calls. @@ -444,6 +457,19 @@ public: */ void UseAppInfo(int info); + /** + Returns the current file layout + Valid values for @a are: + - @c FileLayout_Classic, + - @c FileLayout_XDG + */ + void SetFileLayout(int layout); + + /** + Returns the current file layout + */ + int GetFileLayout() const; + /** Return the file name which would be used by wxFileConfig as local, user-specific, file if it were constructed with @a basename. diff --git a/src/common/stdpbase.cpp b/src/common/stdpbase.cpp index 143c4f117c..e29bae8d14 100644 --- a/src/common/stdpbase.cpp +++ b/src/common/stdpbase.cpp @@ -97,6 +97,7 @@ wxStandardPathsBase::wxStandardPathsBase() // Derived classes can call this in their constructors // to set the platform-specific settings UseAppInfo(AppInfo_AppName); + SetFileLayout(FileLayout_Classic); } wxStandardPathsBase::~wxStandardPathsBase() diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index fecde0c634..ec59b30e43 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -53,6 +53,16 @@ void wxStandardPaths::SetInstallPrefix(const wxString& prefix) wxString wxStandardPaths::GetUserConfigDir() const { + if (GetFileLayout() & FileLayout_XDG) + { + wxString configPath; + if (wxGetenv(wxT("XDG_CONFIG_HOME"))) + configPath = wxGetenv(wxT("XDG_CONFIG_HOME")); + else + configPath = wxFileName::GetHomeDir() + wxT("/.config"); + return configPath; + } + return wxFileName::GetHomeDir(); } @@ -236,6 +246,7 @@ wxStandardPaths::GetLocalizedResourcesDir(const wxString& lang, wxString wxStandardPaths::GetUserDir(Dir userDir) const { + if (GetFileLayout() & FileLayout_XDG) { wxLogNull logNull; wxString homeDir = wxFileName::GetHomeDir(); @@ -312,7 +323,7 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int style) const { wxFileName fn(wxEmptyString, basename); - if (style & wxCONFIG_USE_SUBDIR) + if (style & wxCONFIG_USE_SUBDIR || GetFileLayout() & FileLayout_XDG) fn.SetExt(wxT("conf")); else fn.SetName(wxT('.') + fn.GetName()); From 4bf6ae8d7cb50dd8274810b6b9bc636f43b2d459 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 17:23:20 +0100 Subject: [PATCH 03/10] Make FileLayout_XXX elements of an enum Slight improvement to the previous commit: make FileLayout_Classic and FileLayout_XDG elements of an enum instead of using an untyped "int" for them which didn't really make any sense because these values are not bit masks but exclusive choices for the layout. Also rewrite the checks for them to use "switch" instead of "if" to be warned by the compiler if we ever add another enum value but forget to update the code to handle it. Finally, improve the documentation (add missing "@since") and comments. --- include/wx/stdpaths.h | 19 +++-- interface/wx/stdpaths.h | 63 +++++++++++---- src/common/stdpbase.cpp | 2 + src/unix/stdpaths.cpp | 171 ++++++++++++++++++++++++---------------- 4 files changed, 160 insertions(+), 95 deletions(-) diff --git a/include/wx/stdpaths.h b/include/wx/stdpaths.h index d048888ddb..5cadcf1a8b 100644 --- a/include/wx/stdpaths.h +++ b/include/wx/stdpaths.h @@ -60,10 +60,11 @@ public: Dir_Videos }; - enum + // Layout to use for user config/data files under Unix. + enum FileLayout { - FileLayout_Classic = 0, - FileLayout_XDG = 1 + FileLayout_Classic, // Default: use home directory. + FileLayout_XDG // Recommended: use XDG specification. }; // return the global standard paths object @@ -174,14 +175,14 @@ public: bool UsesAppInfo(int info) const { return (m_usedAppInfo & info) != 0; } - void SetFileLayout(int layout) + void SetFileLayout(FileLayout layout) { - m_usedFileLayout = layout; + m_fileLayout = layout; } - int GetFileLayout() const + FileLayout GetFileLayout() const { - return m_usedFileLayout; + return m_fileLayout; } protected: @@ -199,7 +200,9 @@ protected: // combination of AppInfo_XXX flags used by AppendAppInfo() int m_usedAppInfo; - int m_usedFileLayout; + + // The file layout to use, currently only used under Unix. + FileLayout m_fileLayout; }; #if wxUSE_STDPATHS diff --git a/interface/wx/stdpaths.h b/interface/wx/stdpaths.h index 81f218f4a0..e148cf7749 100644 --- a/interface/wx/stdpaths.h +++ b/interface/wx/stdpaths.h @@ -135,16 +135,34 @@ public: Dir_Videos }; - enum + /** + 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 - */ + /** + 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 (Unix) - */ + /** + 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 }; @@ -313,8 +331,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 @@ -339,7 +360,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 */ @@ -458,17 +480,24 @@ public: void UseAppInfo(int info); /** - Returns the current file layout - Valid values for @a are: - - @c FileLayout_Classic, - - @c FileLayout_XDG + 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(int layout); + void SetFileLayout(FileLayout layout); /** - Returns the current file layout + Returns the current file layout. + + @see SetFileLayout() + + @since 3.1.1 */ - int GetFileLayout() const; + FileLayout GetFileLayout() const; /** Return the file name which would be used by wxFileConfig as local, diff --git a/src/common/stdpbase.cpp b/src/common/stdpbase.cpp index e29bae8d14..6f776c30d4 100644 --- a/src/common/stdpbase.cpp +++ b/src/common/stdpbase.cpp @@ -97,6 +97,8 @@ 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); } diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index ec59b30e43..c72b47a6f8 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -53,17 +53,23 @@ void wxStandardPaths::SetInstallPrefix(const wxString& prefix) wxString wxStandardPaths::GetUserConfigDir() const { - if (GetFileLayout() & FileLayout_XDG) + wxString dir; + + switch ( GetFileLayout() ) { - wxString configPath; - if (wxGetenv(wxT("XDG_CONFIG_HOME"))) - configPath = wxGetenv(wxT("XDG_CONFIG_HOME")); - else - configPath = wxFileName::GetHomeDir() + wxT("/.config"); - return configPath; + case FileLayout_Classic: + dir = wxFileName::GetHomeDir(); + break; + + case FileLayout_XDG: + if ( !wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() ) + dir = wxFileName::GetHomeDir() + wxS("/.config"); + break; } - return wxFileName::GetHomeDir(); + wxASSERT_MSG( !dir.empty(), wxS("unsupported file layout") ); + + return dir; } @@ -246,73 +252,81 @@ wxStandardPaths::GetLocalizedResourcesDir(const wxString& lang, wxString wxStandardPaths::GetUserDir(Dir userDir) const { - if (GetFileLayout() & FileLayout_XDG) + switch ( GetFileLayout() ) { - wxLogNull logNull; - wxString homeDir = wxFileName::GetHomeDir(); - if (userDir == Dir_Cache) - { - if (wxGetenv(wxT("XDG_CACHE_HOME"))) - return wxGetenv(wxT("XDG_CACHE_HOME")); - else - return homeDir + wxT("/.cache"); - } + case FileLayout_Classic: + // Fall back to the base class below. + 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)) - { - wxString userDirId; - switch (userDir) + case FileLayout_XDG: { - 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++) + wxLogNull logNull; + wxString homeDir = wxFileName::GetHomeDir(); + if (userDir == Dir_Cache) { - wxString line(textFile[i]); - int pos = line.Find(userDirId); - if (pos != wxNOT_FOUND) + if (wxGetenv(wxT("XDG_CACHE_HOME"))) + return wxGetenv(wxT("XDG_CACHE_HOME")); + else + return homeDir + wxT("/.cache"); + } + + 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)) + { + wxString userDirId; + switch (userDir) { - 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 + 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++) + { + 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; + } + } } } } - } + break; } return wxStandardPathsBase::GetUserDir(userDir); @@ -323,10 +337,27 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int style) const { wxFileName fn(wxEmptyString, basename); - if (style & wxCONFIG_USE_SUBDIR || GetFileLayout() & FileLayout_XDG) - fn.SetExt(wxT("conf")); - else - fn.SetName(wxT('.') + fn.GetName()); + switch ( GetFileLayout() ) + { + case FileLayout_Classic: + if ( !(style & wxCONFIG_USE_SUBDIR) ) + { + // The standard convention is to not use the extensions for the + // config files in the home directory and just prepend a dot to + // them instead. + fn.SetName(wxT('.') + fn.GetName()); + break; + } + //else: fall through to add the extension + wxFALLTHROUGH; + + case FileLayout_XDG: + // We always use the extension for the config files when using XDG + // layout as they don't go to the home directory anyhow. + fn.SetExt(wxS("conf")); + break; + } + return fn.GetFullName(); } From 5cf9fcbb1a4d718b66330e5568353e32833de800 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 17:48:42 +0100 Subject: [PATCH 04/10] Add wxStandardPaths::ConfigFileConv enum for clarity and safety Use an enum instead of type-unsafe "int" for the second parameter of the recently added wxStandardPaths::MakeConfigFileName(). This also avoids unnatural dependency of wxStandardPaths on wxCONFIG_USE_SUBDIR constant defined in a higher level wxFileConfig class. No real changes, but just make things a bit more robust and hopefully more clear. --- include/wx/msw/stdpaths.h | 3 +- include/wx/osx/cocoa/stdpaths.h | 3 +- include/wx/stdpaths.h | 12 +++++++- include/wx/unix/stdpaths.h | 3 +- interface/wx/stdpaths.h | 49 ++++++++++++++++++++++++++++----- src/common/fileconf.cpp | 22 ++++++++++++--- src/msw/stdpaths.cpp | 4 ++- src/osx/cocoa/stdpaths.mm | 4 ++- src/unix/stdpaths.cpp | 33 +++++++++++++--------- 9 files changed, 103 insertions(+), 30 deletions(-) diff --git a/include/wx/msw/stdpaths.h b/include/wx/msw/stdpaths.h index 23564fc9cb..3f733bc67f 100644 --- a/include/wx/msw/stdpaths.h +++ b/include/wx/msw/stdpaths.h @@ -29,7 +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, int style) const wxOVERRIDE; + virtual wxString MakeConfigFileName(const wxString& basename, + ConfigFileConv conv) const wxOVERRIDE; // MSW-specific methods diff --git a/include/wx/osx/cocoa/stdpaths.h b/include/wx/osx/cocoa/stdpaths.h index d361d5f65d..c77713da12 100644 --- a/include/wx/osx/cocoa/stdpaths.h +++ b/include/wx/osx/cocoa/stdpaths.h @@ -32,7 +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, int style) const wxOVERRIDE; + virtual wxString MakeConfigFileName(const wxString& basename, + ConfigFileConv conv) const wxOVERRIDE; protected: // Ctor is protected, use wxStandardPaths::Get() instead of instantiating diff --git a/include/wx/stdpaths.h b/include/wx/stdpaths.h index 5cadcf1a8b..a7aa61c892 100644 --- a/include/wx/stdpaths.h +++ b/include/wx/stdpaths.h @@ -67,6 +67,14 @@ public: 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(); @@ -162,7 +170,9 @@ public: virtual wxString GetUserDir(Dir userDir) const; - virtual wxString MakeConfigFileName(const wxString& basename, int style) const = 0; + virtual wxString + MakeConfigFileName(const wxString& basename, + ConfigFileConv conv = ConfigFileConv_Ext) const = 0; // virtual dtor for the base class virtual ~wxStandardPathsBase(); diff --git a/include/wx/unix/stdpaths.h b/include/wx/unix/stdpaths.h index d0ec1469e5..e67d9c6ea2 100644 --- a/include/wx/unix/stdpaths.h +++ b/include/wx/unix/stdpaths.h @@ -49,7 +49,8 @@ public: #ifndef __VMS virtual wxString GetUserDir(Dir userDir) const wxOVERRIDE; #endif - virtual wxString MakeConfigFileName(const wxString& basename, int style) const wxOVERRIDE; + virtual wxString MakeConfigFileName(const wxString& basename, + ConfigFileConv conv) const wxOVERRIDE; protected: // Ctor is protected, use wxStandardPaths::Get() instead of instantiating diff --git a/interface/wx/stdpaths.h b/interface/wx/stdpaths.h index e148cf7749..9540770cb4 100644 --- a/interface/wx/stdpaths.h +++ b/interface/wx/stdpaths.h @@ -166,6 +166,35 @@ public: 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. @@ -500,16 +529,22 @@ public: FileLayout GetFileLayout() const; /** - Return the file name which would be used by wxFileConfig as local, - user-specific, file if it were constructed with @a basename. + Return the file name which would be used by wxFileConfig if it were + constructed with @a basename. - @a style has the same meaning as in @ref wxConfigBase::wxConfigBase "wxConfig constructor" - and can contain any combination of styles but only wxCONFIG_USE_SUBDIR bit is - examined by this function. + @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. - Notice that this function cannot be used if @a basename is already a full path name. + @since 3.1.1 */ - virtual wxString MakeConfigFileName(const wxString& basename, int style) const; + virtual wxString + MakeConfigFileName(const wxString& basename, + ConfigFileConv conv = ConfigFileConv_Ext) const; protected: /** diff --git a/src/common/fileconf.cpp b/src/common/fileconf.cpp index 6de548379d..77fa9a8103 100644 --- a/src/common/fileconf.cpp +++ b/src/common/fileconf.cpp @@ -266,14 +266,28 @@ wxString wxFileConfig::GetLocalDir(int style) wxFileName wxFileConfig::GetGlobalFile(const wxString& szFile) { - wxFileName fn(GetGlobalDir(), wxStandardPaths::Get().MakeConfigFileName(szFile, wxCONFIG_USE_SUBDIR)); - return fn; + wxStandardPathsBase& stdp = wxStandardPaths::Get(); + + return wxFileName(GetGlobalDir(), stdp.MakeConfigFileName(szFile)); } wxFileName wxFileConfig::GetLocalFile(const wxString& szFile, int style) { - wxFileName fn(GetLocalDir(style), wxStandardPaths::Get().MakeConfigFileName(szFile, style)); - return fn; + wxStandardPathsBase& stdp = wxStandardPaths::Get(); + + // 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 wxFileName(GetLocalDir(style), stdp.MakeConfigFileName(szFile, conv)); } // ---------------------------------------------------------------------------- diff --git a/src/msw/stdpaths.cpp b/src/msw/stdpaths.cpp index 081ab600a7..afab737719 100644 --- a/src/msw/stdpaths.cpp +++ b/src/msw/stdpaths.cpp @@ -370,7 +370,9 @@ wxString wxStandardPaths::GetPluginsDir() const } -wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int WXUNUSED(style)) const +wxString +wxStandardPaths::MakeConfigFileName(const wxString& basename, + ConfigFileConv WXUNUSED(conv)) const { wxFileName fn(wxEmptyString, basename); fn.SetExt(wxT("ini")); diff --git a/src/osx/cocoa/stdpaths.mm b/src/osx/cocoa/stdpaths.mm index cd9b75b46a..85418fb5af 100644 --- a/src/osx/cocoa/stdpaths.mm +++ b/src/osx/cocoa/stdpaths.mm @@ -130,7 +130,9 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const return GetFMDirectory(dirType, NSUserDomainMask); } -wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int WXUNUSED(style)) const +wxString +wxStandardPaths::MakeConfigFileName(const wxString& basename, + ConfigFileConv WXUNUSED(conv)) const { wxFileName fn(wxEmptyString, basename); fn.SetName(fn.GetName() + wxT(" Preferences")); diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index c72b47a6f8..51e267916b 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -33,7 +33,6 @@ #include "wx/utils.h" #endif //WX_PRECOMP -#include "wx/fileconf.h" #include "wx/filename.h" #include "wx/log.h" #include "wx/textfile.h" @@ -334,30 +333,38 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const #endif // __VMS/!__VMS -wxString wxStandardPaths::MakeConfigFileName(const wxString& basename, int style) const +wxString +wxStandardPaths::MakeConfigFileName(const wxString& basename, + ConfigFileConv conv) const { wxFileName fn(wxEmptyString, basename); + + bool addExt = false; + switch ( GetFileLayout() ) { case FileLayout_Classic: - if ( !(style & wxCONFIG_USE_SUBDIR) ) + switch ( conv ) { - // The standard convention is to not use the extensions for the - // config files in the home directory and just prepend a dot to - // them instead. - fn.SetName(wxT('.') + fn.GetName()); - break; + case ConfigFileConv_Dot: + fn.SetName(wxT('.') + fn.GetName()); + break; + + case ConfigFileConv_Ext: + addExt = true; + break; } - //else: fall through to add the extension - wxFALLTHROUGH; + break; case FileLayout_XDG: - // We always use the extension for the config files when using XDG - // layout as they don't go to the home directory anyhow. - fn.SetExt(wxS("conf")); + // Dot files are never used in XDG mode. + addExt = true; break; } + if ( addExt ) + fn.SetExt(wxS("conf")); + return fn.GetFullName(); } From a44b61e97622ffdd37c3c33509b36d1db1f973f7 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 17:56:45 +0100 Subject: [PATCH 05/10] Revert incompatible change to Unix wxStandardPaths::GetUserDir() There is no reason to break the behaviour of the existing code, which doesn't call SetFileLayout(FileLayout_XDG), by not returning the correct directories from GetUserDir(Dir_Downloads) and similar any longer. --- src/unix/stdpaths.cpp | 134 ++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 69 deletions(-) diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index 51e267916b..de0c1d9555 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -251,81 +251,77 @@ wxStandardPaths::GetLocalizedResourcesDir(const wxString& lang, wxString wxStandardPaths::GetUserDir(Dir userDir) const { - switch ( GetFileLayout() ) + // 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; + wxString homeDir = wxFileName::GetHomeDir(); + if (userDir == Dir_Cache) { - case FileLayout_Classic: - // Fall back to the base class below. - break; + if (wxGetenv(wxT("XDG_CACHE_HOME"))) + return wxGetenv(wxT("XDG_CACHE_HOME")); + else + return homeDir + wxT("/.cache"); + } - case FileLayout_XDG: + 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)) + { + wxString userDirId; + switch (userDir) + { + 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++) { - wxLogNull logNull; - wxString homeDir = wxFileName::GetHomeDir(); - if (userDir == Dir_Cache) + wxString line(textFile[i]); + int pos = line.Find(userDirId); + if (pos != wxNOT_FOUND) { - if (wxGetenv(wxT("XDG_CACHE_HOME"))) - return wxGetenv(wxT("XDG_CACHE_HOME")); - else - return homeDir + wxT("/.cache"); - } - - 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)) - { - wxString userDirId; - switch (userDir) - { - 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++) - { - 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; } } - break; + } } return wxStandardPathsBase::GetUserDir(userDir); From 41fe019f3bf5edced73747c173103daead2cf7c8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 18:00:29 +0100 Subject: [PATCH 06/10] Refactor: extract GetXDGConfigHome() into a separate function No real changes, just avoid doing the same thing in 2 different places and do it in a single helper function instead. --- src/unix/stdpaths.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index de0c1d9555..51722274ab 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -50,6 +50,16 @@ 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 { wxString dir; @@ -61,8 +71,7 @@ wxString wxStandardPaths::GetUserConfigDir() const break; case FileLayout_XDG: - if ( !wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() ) - dir = wxFileName::GetHomeDir() + wxS("/.config"); + dir = GetXDGConfigHome(); break; } @@ -268,12 +277,7 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const return homeDir + wxT("/.cache"); } - 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"); + wxString dirsFile = GetXDGConfigHome() + wxT("/user-dirs.dirs"); if (wxFileExists(dirsFile)) { wxString userDirId; From 214aac6fd60747c0cd9fae1783b1ba343aaf47a8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 18:02:14 +0100 Subject: [PATCH 07/10] Avoid calling wxGetenv() twice unnecessarily Use wxGetEnv() helper function which allows to write the code in more natural and (slightly) more efficient way. No real changes. --- src/unix/stdpaths.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index 51722274ab..b6d9aa20bd 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -268,13 +268,14 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const // the version of wxWidgets used by the application. wxLogNull logNull; - wxString homeDir = wxFileName::GetHomeDir(); + const wxString homeDir = wxFileName::GetHomeDir(); if (userDir == Dir_Cache) { - if (wxGetenv(wxT("XDG_CACHE_HOME"))) - return wxGetenv(wxT("XDG_CACHE_HOME")); - else - return homeDir + wxT("/.cache"); + wxString cacheDir; + if ( !wxGetEnv(wxS("XDG_CACHE_HOME"), &cacheDir) ) + cacheDir = homeDir + wxS("/.cache"); + + return cacheDir; } wxString dirsFile = GetXDGConfigHome() + wxT("/user-dirs.dirs"); From 058f8c3c0ff732c4c30005e39dc9198e40ac8d6c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 18:04:44 +0100 Subject: [PATCH 08/10] Use wxFileName instead of manipulating strings directly Prefer to use wxFileName ctor to string concatenation. --- src/unix/stdpaths.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index b6d9aa20bd..76ed1d6b04 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -278,8 +278,8 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const return cacheDir; } - wxString dirsFile = GetXDGConfigHome() + wxT("/user-dirs.dirs"); - if (wxFileExists(dirsFile)) + const wxFileName dirsFile(GetXDGConfigHome(), wxS("user-dirs.dirs")); + if ( dirsFile.FileExists() ) { wxString userDirId; switch (userDir) @@ -305,7 +305,7 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const } wxTextFile textFile; - if (textFile.Open(dirsFile)) + if ( textFile.Open(dirsFile.GetFullPath()) ) { size_t i; for (i = 0; i < textFile.GetLineCount(); i++) From 3ad54f285295590d25a659b052f6f44517a02a31 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 18:06:33 +0100 Subject: [PATCH 09/10] Iterate over wxTextFile without using indices Using iterator methods is less error-prone (there is no danger of using an invalid index) and slightly shorter and more clear too. --- src/unix/stdpaths.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/unix/stdpaths.cpp b/src/unix/stdpaths.cpp index 76ed1d6b04..4b6140e2f6 100644 --- a/src/unix/stdpaths.cpp +++ b/src/unix/stdpaths.cpp @@ -307,10 +307,10 @@ wxString wxStandardPaths::GetUserDir(Dir userDir) const wxTextFile textFile; if ( textFile.Open(dirsFile.GetFullPath()) ) { - size_t i; - for (i = 0; i < textFile.GetLineCount(); i++) + for ( wxString line = textFile.GetFirstLine(); + !textFile.Eof(); + line = textFile.GetNextLine() ) { - wxString line(textFile[i]); int pos = line.Find(userDirId); if (pos != wxNOT_FOUND) { From f6b91ad14303e6913db0cd7db8ae77a84a4ac026 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 9 Mar 2017 18:08:23 +0100 Subject: [PATCH 10/10] Mention wxStandardPaths::SetFileLayout() in the change log --- docs/changes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changes.txt b/docs/changes.txt index 27a39e3bad..ce1cdac478 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -193,6 +193,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)