Page 1 of 1

multi-OS Preferences wrapper

Posted: Sat Aug 22, 2015 12:20 pm
by Keya
just my wrapper of PB's Preference functions (just the string one) for easy save and read of ini-style settings. OS: Windows + Mac + Linux
CFG.PBI:

Code: Select all

Procedure.i FindLastDotInFilename(a.s)
  CompilerIf #PB_Compiler_Unicode = 1
    CompilerError "UNICODE NOT SUPPORTED - FindLastDotInFilename() function"
  CompilerEndIf
  Protected l=Len(a);    using a global variable for 'l' would speed up the procedure
  While l
    l-1
    Select PeekA(@a+1)
      Case '.':  ProcedureReturn l
      Case '/','\': ProcedureReturn 0   ;we've found the folder root before the dot, so there is no dot
    EndSelect
  Wend
  ProcedureReturn 0
EndProcedure

Procedure.s AppPath()  ;IMPORTANT: Always includes a trailing slash (\ in Windows, / in Linux&Mac)
  Static init, sVal.s
  If init = 0
    sVal = GetPathPart(ProgramFilename())
    If Right(sVal,1) <> "\" And Right(sVal,1) <> "/"
      CompilerIf #PB_Compiler_OS = #PB_OS_Windows
        sVal = sVal + "\"
      CompilerElse
        sVal = sVal + "/"
      CompilerEndIf
    EndIf
    init = 1
  EndIf    
  ProcedureReturn sVal
EndProcedure

Procedure.s AppBaseFilename()
  Static init, sVal.s
  Protected aDot.i
  If init = 0
    sVal = ProgramFilename()
    aDot = FindLastDotInFilename(sVal)
    If aDot: sVal = Left(sVal, aDot): EndIf
    init = 1
  EndIf    
  ProcedureReturn sVal
EndProcedure 

;------------------------------------------------------------------

Procedure SaveCFG(sGroup.s, sKey.s, sVal.s)
  Protected sIniFile.s = AppBaseFilename() + ".cfg"
  If OpenPreferences(sIniFile, #PB_Preference_NoSpace | #PB_Preference_GroupSeparator)
  Else
    If CreatePreferences(sIniFile, #PB_Preference_NoSpace | #PB_Preference_GroupSeparator)
    Else
      MessageRequester("Error", "Unable to open settings file " + sIniFile)
      ProcedureReturn
    EndIf
  EndIf
  PreferenceGroup(sGroup)
  WritePreferenceString (sKey, sVal)
  ClosePreferences()
EndProcedure


Procedure.s ReadCFG(sGroup.s, sKey.s)
  Protected sIniFile.s = AppBaseFilename() + ".cfg"
  Protected sResult.s
  If OpenPreferences(sIniFile)
    PreferenceGroup(sGroup)
    sResult = ReadPreferenceString(sKey, "")
    ClosePreferences()
  Else
    MessageRequester("Error", "Unable to open settings file " + sIniFile)
    ProcedureReturn    
  EndIf
  ProcedureReturn sResult
EndProcedure


Procedure.i CFGExists()
  Protected sIniFile.s = AppBaseFilename() + ".cfg"
  If FileSize(sIniFile) > 0: ProcedureReturn 1: EndIf
EndProcedure

Example:

Code: Select all

XIncludeFile("cfg.pbi")

;Save settings
SaveCFG("Settings", "My First Option", "My first value")
SaveCFG("Settings", "My Second Option", "My second value")

;Read setting
Debug("Stored value = " + ReadCFG("Settings", "My First Option"))
Debug("Stored value = " + ReadCFG("Settings", "My Second Option"))

Re: multi-OS Preferences wrapper

Posted: Sat Aug 22, 2015 12:41 pm
by ts-soft
I would not write preference in ProgramFiles Dir. You should use on Windows the APPDATA, on Linux the HOMEDIR with . (dot) in front (Hidden).

Greetings Thomas

Re: multi-OS Preferences wrapper

Posted: Sat Aug 22, 2015 12:58 pm
by Keya
Thankyou for feedback! My intention for this is a portable app, so it just writes wherever you're running the program from (c:\temp\myapp.exe creates c:\temp\myapp.cfg)
But even as a portable app should I be writing to APPDATA/HOMEDIR instead of application home path?

Re: multi-OS Preferences wrapper

Posted: Sat Aug 22, 2015 1:09 pm
by Kukulkan
Try checking if you are running from a portable device (USB) with read/write access. In this case, write it there, otherwise to %APPDATA%.

Re: multi-OS Preferences wrapper

Posted: Sat Aug 22, 2015 2:46 pm
by Little John
For instance CCleaner, as well as several other programs, look for a special file in the program directory (e.g. named "portable.dat").
If the file is there, it's considered a portable version of the program, and the preferences are written to the program directory, else they are written to a subdirectory of %appdata% (on Windows). I'm also doing it this way with PureBasic for years, and everything works fine.

Re: multi-OS Preferences wrapper

Posted: Wed Oct 14, 2015 5:09 am
by Keya
Heya guys just getting back to this, Ive put together some code to determine where the location for our applications preferences/ini file

First it checks to see if the preferences file exists in the same folder the app is running from ... perhaps the root of a USB drive, or C:\Program Files\Mycompany\MyApp\, or...
Anyway if it doesnt find it there, it defaults to EnvironVar("APPDATA") for Windows, and EnvironVar("HOME") for Mac + Linux.

Im not so sure at the moment because if it's running from C:\Program Files\xxx\ and the prefs.ini file is there it'll write to that, instead of C:\Documents and Settings\Application Data\xxx\
so maybe a commandline argument will solve that
(also im trying to avoid lowlevel api such to detect if the drive is portable, if possible)

So is this the right way to go??? any feedback positive or negative much appreciated! thankyou

Code: Select all

EnableExplicit

Procedure.s GetPrefsFolder(Prefs.s)  
  Protected CfgPath.s = GetPathPart(ProgramFilename())
  If FileSize(CfgPath + Prefs) = -1          ;Not found in application folder
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      CfgPath = GetEnvironmentVariable("APPDATA") + "\MyCompany\MyAppTitle\"
    CompilerElse  ;#PB_OS_Linux and #PB_OS_MacOS
      CfgPath = GetEnvironmentVariable("HOME") + "/.MyCompany/.MyAppTitle/"       ;<- do we need the dots?
    CompilerEndIf
  EndIf
  ProcedureReturn CfgPath
EndProcedure


Debug(GetPrefsFolder("prefs.ini"))

;Typical output:
;WIN:   C:\Documents and Settings\USERNAME\Application Data\MyCompany\MyAppTitle\   (older)
;WIN:   C:\Users\USERNAME\AppData\Roaming\MyCompany\MyAppTitle\                     (newer)
;LNX:   /home/USERNAME/.MyCompany/.MyAppTitle/
;MAC:   /Users/USERNAME/.MyCompany/.MyAppTitle/