Preserve User Settings (code snippet)


It is often desirable to preserve user optional settings between successive runs of a plugin. These ‘sticky’ settings may include the plugin window position and size, default input/output folder, filter options, etc.

Some users run Family Historian on more than one PC and synchronise files across the PC. To support these users, some ‘sticky’ settings, such as window position or default folder, may need to be local to each PC, whereas others need to be global. The local settings are associated with the system ComputerName.

Both the fh Version 4 Project structure and Version 3 standalone GEDCOM mode are supported.

This code also illustrates a coding style where:

  • global names start with an upper-case letter, e.g. IntOption, StrFolder, DoLoadSettings()
  • local names start with a lower-case letter, e.g. tblField, strPublic, strLoadLocal()
  • names of data, and functions that return values start with a data type mnemonic:
    • Flg or flg for booleans, or alternatively Is or is sometimes works well, e.g. isValidData
    • Int or int for integers
    • Str or str for strings
    • Tbl or tbl for tables
    • etc.

Requires: iuplua and lfs and File Exists (code snippet) and Split a Line using a Separator (code snippet) and Split a String into Numbers using Separators (code snippet).


require "iuplua"		-- To access GUI window builder
require "lfs"			-- To access LUA filing system
-- Global Data Contants Definition --
function DoGlobalConstants()
	StrPlugin	= "Plugin Title"	-- Plugin title & version
	StrIssue	= " V1.1 "
	-- Filename Global Constants
	StrComputerName = os.getenv("COMPUTERNAME")
	StrStickyFile = fhGetPluginDataFileName()
	-- Allow plugins with variant filenames to use same plugin data files
	StrStickyFile = string.gsub(StrStickyFile,"\\"..StrPlugin..".+%.[D,d][A,a][T,t]$","\\"..StrPlugin..".dat")
	if StrStickyFile == "" then
		-- Use standalone GEDCOM path & filename..".fh_data\Plugin Data\" as the folder + the Plugin Filename..".dat"
		StrStickyFile = fhGetContextInfo("CI_GEDCOM_FILE")
		StrStickyFile = string.gsub(StrStickyFile, "%.[G,g][E,e][D,d]", ".fh_data")
		StrStickyFile = StrStickyFile.."\\Plugin Data"
		StrStickyFile = StrStickyFile.."\\"..StrPlugin..".dat"
	-- Plugin data folder path name
	StrPluginPath = string.gsub(StrStickyFile,"\\"..StrPlugin.."%.[D,d][A,a][T,t]$","")
	-- other global constants for other purposes may be preset here
end -- function DoGlobalConstants
-- Reset Sticky Settings to Default Values --
function DoDefaultSettings()
	IntMainX = iup.CENTER			-- GUI Main window position X & Y co-ordinate
	IntMainY = iup.CENTER
	IntHelpX = iup.CENTER			-- GUI Help window position X & Y co-ordinate
	IntHelpY = iup.CENTER
	local strPublic = fhGetContextInfo("CI_PROJECT_PUBLIC_FOLDER")
	if strPublic == "" then
		StrFolder = StrPluginPath		-- Output folder for standalone Gedcom
		StrFolder = strPublic			-- Output folder for Project structure
	IntOption = 1
	-- other option default values should be set here
end -- function DoDefaultSettings
-- Load Sticky Settings from File --
function DoLoadSettings(strFileName)
	local tblStickyData = {}
	-- Load Local Parameter for this PC --
	local function strLoadLocal(strParam,strDefault)
		return tblStickyData[StrComputerName.."-"..strParam] or strDefault
	-- Load Global Parameter for all PC --
	local function strLoadGlobal(strParam,strDefault)
		return tblStickyData[strParam] or strDefault
	-- Ensure Window Position is on Screen --
	local function intintCheckPosition(x,y)
		local tblScrn = iup.GetGlobal("VIRTUALSCREEN"):SplitNumbers()
		-- tblScrn[1] = origin x, tblScrn[2] = origin y, tblScrn[3] = width, tblScrn[4] = height
		if tonumber(x) == nil then
			x = iup.CENTER
		elseif tonumber(x) > tblScrn[3] then
			x = iup.CENTER
		if tonumber(y) == nil then
			y = iup.CENTER
		elseif tonumber(y) > tblScrn[4] then
			y = iup.CENTER
		return tonumber(x),tonumber(y)
	end -- local function intintCheckPosition
	if FileExists(strFileName) then
		-- Load Settings File in table lines with key & val fields
		local tblField = {}
		for strLine in io.lines(strFileName) do
			tblField = strLine:split("=")
			tblStickyData[tblField[1]] = tblField[2]
		IntMainX = tonumber(strLoadLocal("MainX",IntMainX))
		IntMainY = tonumber(strLoadLocal("MainY",IntMainY))
		IntHelpX = tonumber(strLoadLocal("HelpX",IntHelpX))
		IntHelpY = tonumber(strLoadLocal("HelpY",IntHelpY))
		StrFolder = strLoadLocal("Folder",StrFolder)
		IntOption = tonumber(strLoadGlobal("Option",IntOption))
		-- other option default values should be loaded here
	IntMainX,IntMainY = intintCheckPosition(IntMainX,IntMainY)
	IntHelpX,IntHelpY = intintCheckPosition(IntHelpX,IntHelpY)
end -- function DoLoadSettings
-- Save Sticky Settings to File --
function DoSaveSettings(strFileName)
	local tblStickyData = {}
	-- Save Local Parameter for this PC --
	local function doSaveLocal(strParam,param)
		tblStickyData[StrComputerName.."-"..strParam] = param
	-- Save Global Parameter for all PC --
	local function doSaveGlobal(strParam,param)
		tblStickyData[strParam] = param
	-- other option default values should be saved here
	local fileHandle, strError =,”w”)
	for strKey,strVal in pairs(tblStickyData) do
end -- function DoSaveSettings


	require "iuplua"		-- To access GUI window builder
	require "lfs"			-- To access LUA filing system
	DoGlobalConstants()		-- Preset global data constants
	DoDefaultSettings()		-- Preset default sticky settings
	DoLoadSettings(StrStickyFile)	-- Load sticky data settings
	-- invoke main plugin functions here
	DoSaveSettings(StrStickyFile)	-- Save sticky data settings