GRO BMD Index Citations.fh_lua

--[[
@Title:			GRO BMD Index Citations
@Type:			Standard
@Author:		John Elvin
@Contributors:	Mike Tate
@Version:		1.2
@Keywords:		GRO 
@LastUpdated:	26 March 2025
@Licence:		This plugin is copyright (c) 2024 John Elvin & contributors and is licensed under the MIT License which
				is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description:	Add a GRO Index Source Citation to an birth, marriage or death with GRO reference in Where Within and
				additional information in Text from Source; event is created or optionally updated with the date and/or place.
				Also has option to set birth date from Age, year of birth or date of birth from Death citation.
				It cannot be used outside of a project (i.e. with standalone GEDCOM).
@V0.1:			Loosely based on Add GRO Index Source Citation
					• reference is stored in Where Within Source, additional information in Text from Source
					• choice of which parts of event details are to be updated if they already exist
					• offers to set birth date from age at death or date of birth given in death index
					• configuration of the labels for the fields in the citation text
					• district name can be first part only in the citation but the whole place is used when creating/updating events
					• many other usability enhancements
@V0.1.1		•	Correct comparison of death age
			•	Remove spurious leading space on text from source if text from source headers are blank
@V0.2		•	Copy existing name - surname. For a female picks up last spouse's surname (if present)
			•	Allow year only birth date for death events
@V0.2.1		•	Correct creation of birth event when adding death citation
			•	Correct substitution of ^ in text from source
			•	Amend tag in settings for District name only to clarify function
			•	Change way non-configured source records are handled - button(s) for associated events are disabled
			•	Disable help button pending writing of help
			•	Change label on Exit button to Close to match FH dialogs
			•	Change default setting for Full month names to Off
			•	Remove superfluous retry cancel after select individual/family dialogs - user can retry from main menu
			•	Correct trucated labels in settings dialog on systems with different font
			•	Clarify copying existing names by changing label and adding tips to boxes
@v0.2.2		•	Correct disabling of main menu buttons when source(s) not selected
			•	Move Name(s) section of main dialog above event specific options
			•	Allow death without Age or DOB (early FreeBMD (and others) had neither)
			•	Move Event label (Birth, Marriage or Death) to Where Within Source, not Text from Source
			•	Change Citation date text in settings to clarify functionality
			•	Support for user defined fonts
			•	Trailing space on end of italic tags to prevent last character being clipped
			•	Move button to reset main dialog screen position from settings to main menu. Button only active if dialog moved
@v0.2.3		•	Change validation of source settings to make sure templated sources are always detected and highlight
				invalid ids without wiping them
			•	Correct setting of Source to be used by each event
			•	Add Source Id to Confirmation of changes dialog
			•	Correct truncation of label in confirmation of changes dialog
@0.3		•	Add option to capitalise letters as keyed in Volume, District No. and Register No.
			•	Enable keying Ctrl ' to insert Mother's surname in Mother's Maiden Name field
			•	Death: If entered birth date (or implied birth date) is same as existing birth date disable option to update
			•	Prevent error if using option Only first field of district and entered data begins with a comma
			•	Correct error whereby for Death, a citation was always being added to the birth fact
			•	Put Citation Labels and Options in Settings dialog on to tabs to reduce height of dialog and make room
				for more options
			•	Move cursor to end of the name after inserting a name using CTRL ' (applies to given, surname and mother)
			•	Add option to add a citation to the Mother's name, including adding new individual as the mother and
				creating the family as required. Settings has an option to set the Mother's Given name to be used when
				it is not known.
			•	Allow alpha in the page number (have found entries with alpha page suffix)
			•	Change layout of Name panel
			•	Ctrl tab in any of the reference fields (Volume, Page etc.) will jump focus to the first field in the
				Name panel (Add Citation to Name)
			•	Death - Date of Birth: Allow part date of mmm yyyy (as well as year only)
			•	Death - Birth Date update options: change last option to 'Don't add citation' if the date matches
			•	Some code rationalisation
@0.4		•	Replace iup.messagesdlg with custom function - error messages will display using the same user's font
				as in all the dialogs
			•	Apply name format rule to mother's surname in the citation text from source
@0.4.1		•	Restructure to ensure all messages and dialogs are children of their parent dialog
			•	Prevent a family consisting of only one individual being given a marriage citation
			•	Give consistent titles to all message
			•	Improve all tips displayed on mouseover
			•	Add link to help and add button for settings help on settings dialog
			•	Improve display of current record selection in main menu
			•	GRO reference labels in Where Within: Don't add a space if label begins with / or -
			•	Improve validation of DOB on death event
			•	Ensure the main dialog is not off the top of the screen if the FH window is smaller than the main dialog
				and near the top of the screen
			•	Change CTRL TAB on reference fields to jump to first Given name
			•	Ensure & is displayed correctly in existing citations message and Places in the comparison of event details
@1.0			Release to plugin store
@1.1		•	Add option for using a list of GRO District names:
					List can be maintained from settings, or from a button beside the District name on the main entry screen.
					Optionally, the years the district was valid can be added. These will be shown in the drop down list
					and the year selected will be checked against the range(s)
			•	Add option to update name when adding Birth citation
			•	Allow selection of quarter by keying 1, 2, 3, or 4
			•	Ensure & is correctly displayed in messages
			•	Ensure & in an address is correctly displayed in existing event details dialog
			•	Improve display of existing citations (particularly when there are multiple citations)
@1.1.1		•	Correct selection of quarter to prevent error if control key used
@1.2		Add checking for updated version in plugin store (Check frequency configured in settings)
]]

Version = '1.2'
fhInitialise(7,0,0,'save_recommended')

fhfu = require('fhFileUtils')
fhu = require('fhutils')
fhu.setIupDefaults()
require('iupluaimglib')
require('luacom')

iup.SetGlobal('IMAGESTOCKSIZE', 32)

function main()
	if fhGetContextInfo('CI_APP_MODE') ~= 'Project Mode' then
		OkOnlyMessage(fhGetContextInfo('CI_PLUGIN_NAME'), 'This plugin does not support running in Gedcom Mode', 'ERROR')
		return
	end

	Initialise()

	function CheckSource(SourceId) 		-- Check source exists and is not templated
		if SourceId == 0 then
			return false
		end
		local ptrSource = fhNewItemPtr()
		ptrSource:MoveToRecordById('SOUR', SourceId)
		if ptrSource:IsNull() then
			return false
		end
		local ptrTemplate = fhGetItemPtr(ptrSource, '~._SRCT>')
		if ptrTemplate:IsNotNull() then
			return false
		end
		return true
	end				-- function CheckSource

	function SetSource(SourceId)
		gtblData.id = SourceId
		gtblData.ptr:MoveToRecordById('SOUR', SourceId)
		gtblData.name = fhGetDisplayText(gtblData.ptr)
	end

	repeat
		local Exit = false
		local selMain = Menu()
		if selMain == 1 then
			Exit = MainDialog('Birth', gtblData.arrInd1Details, nil, nil, gtblData.FirstYear, gtblData.LastYear)
		end
		if selMain == 2 then
			Exit = MainDialog('Marriage', gtblData.arrInd1Details, gtblData.arrInd2Details, gtblData.arrFamilyDetails,
							 gtblData.FirstYear, gtblData.LastYear)
		end
		if selMain == 3 then
			Exit = MainDialog('Death', gtblData.arrInd1Details, nil, nil, gtblData.FirstYear, gtblData.LastYear)
		end
		if selMain == 9 then
			config()
		end
		if Exit then			-- Task complete ar Exit selected
			return
		end
	until not selMain or selMain == 0

end			-- function Main
--======================================================================================================================

function Menu()			-- Main menu
	-- Get display name of current record (individual or family)
	local ptrList = fhGetCurrentRecordSel("INDI")
	local strCurrent
	if #ptrList == 1 then
		strCurrent = fhIndGetName(ptrList[1], false, false)
	elseif #ptrList > 1 then
		strCurrent = ''
	else
		local ptrList = fhGetCurrentRecordSel('FAM')
		if #ptrList ==1 then
			strCurrent = 'Family ' .. fhGetItemText(ptrList[1], '~'):sub(4)
		elseif #ptrList > 1 then
			strCurrent = ''
		else
			ptrList = fhGetCurrentRecordSel()
			if #ptrList == 0 then
				strCurrent = ''
			else
				if #ptrList ==1 then
					strCurrent = fhGetTypeInfo(ptrList[1], 'label') .. ': ' .. fhGetItemText(ptrList[1], '~')
				else
					strCurrent = ''
				end
			end
		end
	end

	local lblCurrentRecord = iup.flatlabel{title = strCurrent, textwrap = 'YES', size= '180x18', expand = 'VERTICAL'}
	lblCurrentRecord.fontstyle = 'Bold'

	local hboxCurrentRecord = iup.hbox{
		iup.label{title = 'Current Record:'}, lblCurrentRecord;
		gap = 4, margin = '0x0', alignment = 'ATOP'
		}

	local selection
	-- Main actions buttons
	-- Birth
	local btnBirth = iup.button{ title = 'Birth Citation', padding = '10x3'}
	if not CheckSource(gtblSettings.SourceIdBirth) then
		btnBirth.active = 'NO'
	end

	-- Marriage
	local btnMarriage = iup.button{ title = 'Marriage Citation', padding = '10x3'}
	if not CheckSource(gtblSettings.SourceIdMarriage) then
		btnMarriage.active = 'NO'
	end

	-- Death
	local btnDeath = iup.button{ title = 'Death Citation', padding = '10x3' }
	if not CheckSource(gtblSettings.SourceIdDeath) then
		btnDeath.active = 'NO'
	end

	local btnScreenPosReset = iup.button{
		title = 'Reset Dialog Position', padding = '16x0',
		tip = 'Reset position of the citation dialog to\ncentered on Family Historian Window',
		}

	if gtblSettings.ScreenPosX == iup.CENTERPARENT and gtblSettings.ScreenPosX == iup.CENTERPARENT then
		btnScreenPosReset.active = 'NO'
	end

	function btnScreenPosReset:action()
		gtblSettings.ScreenPosX = iup.CENTERPARENT
		gtblSettings.ScreenPosY = iup.CENTERPARENT
		SaveSettings()
		btnScreenPosReset.active = 'NO'					-- Stop it being pressed twice
	end

	local btnConfig = iup.button{ title = 'Settings', padding = '10x3',
		action = function(self) selection = 9 return iup.CLOSE end }

	-- Footer buttons
	local btnHelp = iup.button{ title = 'Help',padding = '10x3', tip = 'Display plugin help page'}
	function btnHelp:action()
		fhShellExecute('https://pluginstore.family-historian.co.uk/page/help/gro-bmd-index-citations')
	end

	local btnExit = iup.button{ title = 'Close', padding = '10x3', tip = 'Exit plugin' }
	function btnExit:action()
		selection = 0
		return iup.CLOSE
	end

	-- Assemble form
	local vboxMainForm = iup.vbox{
		hboxCurrentRecord,
		iup.frame{
			iup.vbox{
				iup.hbox{
					btnBirth, btnMarriage, btnDeath;
					normalizesize = 'BOTH', gap = 6, margin = '10x10'
					},
					btnScreenPosReset;
				alignment = 'ACENTER'
				}
			},
		iup.hbox{
			iup.fill{}, btnConfig, btnHelp, btnExit, iup.fill{};
			normalizesize = 'BOTH', margin = 'x10', gap = 50
			};
		gap = 10, margin = '10x10'
		}

	local dialogMenu = iup.dialog{
		vboxMainForm; resize = 'No', minbox = 'No', maxbox = 'No', title = gPluginName .. ' (v' .. Version .. ')',
		tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1'
		}
	iup.SetAttribute(dialogMenu, 'NATIVEPARENT', fhGetContextInfo('CI_PARENT_HWND'))

--*******************************  Action functions for Birth, Marriage, Death buttons  ********************************
	function btnBirth:action()
		local ptrInd = GetIndividual()
		if ptrInd == nil then
			return
		end
		if ExistingCitation(ptrInd, '~.BIRT', gtblSettings.SourceIdBirth) then	-- Check for existing citation to source
			return
		end

		local arrIndDetails = GetIndDetails(ptrInd)
		if arrIndDetails['LatestBirth'] < 1837 and arrIndDetails['LatestBirth'] ~= 0 then
			OkOnlyMessage('GRO Index Birth Citation', arrIndDetails['DispName'] .. ' was born before GRO index starts',
							'WARNING', dialogMenu)
			return
		end

		-- Range to be used for the year drop down
		gtblData.FirstYear = math.max(arrIndDetails['EarliestBirth'], 1837)
		gtblData.LastYear = math.min(arrIndDetails['LatestBirth'], gtblConstants.CurrYear)

		SetSource(gtblSettings.SourceIdBirth)
		gtblData.arrInd1Details = arrIndDetails
		selection = 1
		return iup.CLOSE
	end
------------------------------------------------------------------------------------------------------------------------
	function btnMarriage:action()
		local ptrFamily = GetFamily()
		if ptrFamily == nil then
			return
		end

		if ExistingCitation(ptrFamily, '~.MARR', gtblSettings.SourceIdMarriage) then	-- Check for existing citation to source
			return
		end

		-- Get details of the two individuals & predict date range for marriage
		local ptrInd1 = fhNewItemPtr()
		ptrInd1:MoveTo(ptrFamily,'~.~SPOU[1]>')
		if ptrInd1:IsNull() then
			local strMessage = 'Selected family has no parents'
			OkOnlyMessage('GRO Index Marriage Citation', strMessage, 'ERROR', dialogMenu)
			return
		end
		local arrIndDetails1 = GetIndDetails(ptrInd1)

		local ptrInd2 = fhNewItemPtr()
		local arrIndDetails2
		ptrInd2:MoveTo(ptrFamily,'~.~SPOU[2]>')
		if ptrInd2:IsNotNull() then
			arrIndDetails2 = GetIndDetails(ptrInd2)
		else
			local strMessage = 'Selected family only has one parent'
			OkOnlyMessage('GRO Index Marriage Citation', strMessage, 'ERROR', dialogMenu)
			return
		end

		local arrFamDetails = {}

		arrFamDetails['Ptr'] = ptrFamily
		arrFamDetails['DispName']			= fhGetItemText(ptrFamily, '~')
		arrFamDetails['MarriageDatePtr']	= fhGetItemPtr(ptrFamily, '~.MARR.DATE')

		local EarliestYear, LatestYear
		if arrFamDetails['MarriageDatePtr']:IsNotNull() then
			local dtDate = fhGetValueAsDate(arrFamDetails['MarriageDatePtr'])
			local dpDate = dtDate:GetDatePt1()
			EarliestYear = dpDate:GetYear()
			dpDate = dtDate:GetDatePt2()
			if dpDate:IsNull() then
				LatestYear = EarliestYear
			else
				LatestYear = dpDate:GetYear()
			end
		elseif arrIndDetails2 ~= nil then
			EarliestYear = math.max(arrIndDetails1['EarliestBirth'], arrIndDetails2['EarliestBirth']) + 12
			LatestYear = math.min(arrIndDetails1['LatestDeath'], arrIndDetails2['LatestDeath'])
		else
			EarliestYear = arrIndDetails1['EarliestBirth'] + 12
			LatestYear = arrIndDetails1['LatestDeath']
		end
		LatestYear = math.min(LatestYear, gtblConstants.CurrYear)
		if LatestYear < 1837 then
			OkOnlyMessage('GRO Index Marriage Citation',
				arrFamDetails['DispName']:sub(7) .. ' married before GRO index starts', 'WARNING', dialogMenu)
			return
		end

		gtblData.FirstYear = math.max(EarliestYear, 1837)
		gtblData.LastYear = LatestYear
		--return MainDialog('Marriage', arrIndDetails1, arrIndDetails2, arrFamDetails, EarliestYear, LatestYear)
		SetSource(gtblSettings.SourceIdMarriage)
		gtblData.arrInd1Details = arrIndDetails1
		gtblData.arrInd2Details = arrIndDetails2
		gtblData.arrFamilyDetails = arrFamDetails

		selection = 2
		return iup.CLOSE
	end
------------------------------------------------------------------------------------------------------------------------
	function btnDeath:action()
		local ptrInd = GetIndividual()
		if ptrInd == nil then
			return
		end
		if ExistingCitation(ptrInd, '~.DEAT', gtblSettings.SourceIdBirth) then	-- Check for existing citation to source
			return
		end
		local arrIndDetails = GetIndDetails(ptrInd)
		if arrIndDetails['LatestDeath'] < 1837  then
			OkOnlyMessage('GRO Index Death Citation' , arrIndDetails['DispName'] .. ' died before GRO index starts',
							'WARNING', dialogMenu)
			return
		end

		-- Range to be used for the year drop down
		gtblData.FirstYear = math.max(arrIndDetails['EarliestDeath'], 1837)
		gtblData.LastYear = math.min(arrIndDetails['LatestDeath'], gtblConstants.CurrYear)

		SetSource(gtblSettings.SourceIdDeath)
		gtblData.arrInd1Details = arrIndDetails
		selection = 3
		return iup.CLOSE
	end
-- ****************************  Common routines used by functions Birth, Marriage, Death  *****************************

	function GetIndividual() -- Get current individual or prompt if no current

		local ptrList = fhGetCurrentRecordSel("INDI")
		while #ptrList ~= 1 do
			ptrList = fhPromptUserForRecordSel("INDI", 1, dialogMenu.HWND)
			if #ptrList ~= 1 then
				return nil
			end
		end
		return ptrList[1]
	end					-- function GetIndividual
------------------------------------------------------------------------------------------------------------------------
	function GetFamily()	-- Get current family or family of current individual; if neither prompt

		local ptrListF = fhGetCurrentRecordSel('FAM')
		if #ptrListF == 1 then
			return ptrListF[1]					-- Just one family selected
		end
		local ptrListI = {} -- Initialise arrays so test to prompt for family below doesn't fail
		local arrFams = {}

		if #ptrListF == 0 then					-- No family selected
			ptrListI = fhGetCurrentRecordSel('INDI')
			if #ptrListI == 1 then				-- Just one individual seleted - find their family(s)
				local ptrFams = fhNewItemPtr()
				ptrFams:MoveTo(ptrListI[1],'~.FAMS')
				while ptrFams:IsNotNull() do
					table.insert(arrFams,fhGetValueAsLink(ptrFams))
					ptrFams:MoveNext('SAME_TAG')
				end
				if #arrFams == 1 then			-- Individual has only one family
					return arrFams[1]
				end
			end
		end
		-- If multiple families selected OR not just one individual selected OR selected individual has no families prompt for selection
		-- Prompt for family if nothing possible seleted
		if #ptrListF > 1 or #ptrListI ~= 1 or #arrFams == 0 then
			while #ptrListF ~= 1 do
				ptrListF = fhPromptUserForRecordSel('FAM', 1)
				if #ptrListF ~= 1 then
					return nil
				end
			end
			return ptrListF[1]
		end

		-- No family selected, a single individual is selected and they have multiple families
		-- We already have array of families (arrFams) - get spouses for them

		local listFamilies = iup.list{}
		local ptrSpouse = fhNewItemPtr()
		for i = 1, #arrFams do
			ptrSpouse:MoveTo(ptrListI[1],'~.~SPOU[' .. i .. ']>')
			if ptrSpouse:IsNotNull() then
				listFamilies[i] = fhGetDisplayText(ptrListI[1]) .. ' and ' .. fhGetDisplayText(ptrSpouse)
			else
				listFamilies[i] = fhGetDisplayText(ptrListI[1]) .. ' and <>'
			end
		end

		local btnOK =iup.button{title = 'OK', padding = '10x3'}

		local Continue
		local btnCancel = iup.button{
			title = 'Cancel', padding = '10x3',
			action = function(self) Continue = false return iup.CLOSE end
			}
		strTitle = 'GRO Index Marriage Citation'
		local dialogFamily = iup.dialog{
			iup.vbox{
				iup.label{title = fhGetDisplayText(ptrListI[1]) .. ' has multiple families\nSelect family for this marriage';
					padding = '0x4', alignment = 'ACENTER:ATOP', expand = 'YES'
					},
				listFamilies,
				iup.hbox{
					iup.fill{}, btnOK, btnCancel, iup.fill{};
					normalizesize = 'BOTH', margin = '0x0', gap = 40
					};
				gap = 10, margin = '10x10'
				};
			resize = 'No', minbox = 'No', maxbox = 'No', helpbutton = 'Yes',
			title = strTitle,
			defaultesc = btnCancel,
			PARENTDIALOG = dialogMenu,
			tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1'
			}

		function btnOK:action()
			if listFamilies.value == "0" then
				OkOnlyMessage(strTitle, 'Selection of family required', 'ERROR', dialogFamily)
				return
			end
			Continue = true
			return iup.CLOSE
		end

		dialogFamily:popup(iup.CENTERPARENT, iup.CENTERPARENT)

		local Selection = nil
		if Continue then
			Selection = arrFams[tonumber(listFamilies.value)]
		end
		dialogFamily:destroy()
		return Selection

	end			-- function GetFamily

------------------------------------------------------------------------------------------------------------------------
function GetIndDetails(ptrInd)

	local arrDetails = {}
	arrDetails['Ptr'] = ptrInd
	arrDetails['Id'] = fhGetRecordId(ptrInd)
	arrDetails['DispNameFull']	= fhIndGetName(ptrInd, true, false)
	arrDetails['DispName']		= fhIndGetName(ptrInd, false, false)
	local strNameStored =fhGetItemText(ptrInd,'INDI.NAME[1]:STORED')
	local strGiven
	if strNameStored:sub(-1) == '/' then
		strGiven = strNameStored:match('^[^/]*')
		arrDetails['Surname']		= strNameStored:match('%b//'):gsub('/','')
		arrDetails['NameOrder'] = 1
	elseif strNameStored:sub(1, 1) == '/' then
		strGiven = strNameStored:match('[^/]*$')
		arrDetails['Surname']		= strNameStored:match('%b//'):gsub('/','')
		arrDetails['NameOrder'] = 2
	else				-- Indeterminate name structure, put in given name so user controls capitalisation
		strGiven = strNameStored
		arrDetails['Surname']		= ''
		arrDetails['NameOrder'] = 3
	end
	arrDetails['GivenName']			= strGiven:match('^%s*(.-)%s*$')

	arrDetails['ptrMother'] 		= fhGetItemPtr(ptrInd,'~.FAMC>WIFE>')
	arrDetails['ptrFamily']			= fhGetItemPtr(ptrInd,'~.FAMC>')
	arrDetails['Mother']			= Capitalise(fhGetItemText(ptrInd, 'INDI.~MOTH[1]>NAME[1]:SURNAME'))

	arrDetails['EarliestBirth']	= fhCallBuiltInFunction('EstimatedBirthDate', ptrInd, 'EARLIEST',3):GetYear()
	arrDetails['LatestBirth']	= fhCallBuiltInFunction('EstimatedBirthDate', ptrInd, 'LATEST',3):GetYear()
	arrDetails['EarliestDeath']	= fhCallBuiltInFunction('EstimatedDeathDate', ptrInd, 'EARLIEST',3):GetYear()
	local LatestDeath = fhCallBuiltInFunction('EstimatedDeathDate', ptrInd, 'LATEST',3):GetYear()
	arrDetails['LatestDeath'] = math.max(LatestDeath, gtblConstants.CurrYear)  -- Latest estimated death can be zero
	return arrDetails
end			-- function GetIndDetails

------------------------------------------------------------------------------------------------------------------------
	function ExistingCitation (ptrRecord, EventRef, SourceId)		-- Checks for existing citation to source
																	-- Returns true if found and user cancels
		local ptrEventSource = fhNewItemPtr()
		local ptrEventSourceRecord = fhNewItemPtr()
		local ptrTemp = fhNewItemPtr()
		local bExisting = false
		local strCitations = ''
		ptrEventSource:MoveTo(ptrRecord, EventRef .. '.SOUR')
		while ptrEventSource:IsNotNull() do
			ptrEventSourceRecord = fhGetValueAsLink(ptrEventSource)
			if fhGetRecordId(ptrEventSourceRecord) == SourceId then
				bExisting = true
				strCitations = strCitations .. '\n'
				ptrTemp = fhGetItemPtr(ptrEventSource, '~.PAGE')
				if ptrTemp:IsNotNull() then
					strCitations = strCitations .. '\n' .. fhGetValueAsText(ptrTemp)
				end
				ptrTemp = fhGetItemPtr(ptrEventSource, '~.DATA.TEXT')
				if ptrTemp:IsNotNull() then
					strCitations = strCitations .. '\n' .. fhGetValueAsText(ptrTemp)
				end
			end
			ptrEventSource:MoveNext('SAME_TAG')
		end
		if bExisting then
			local strMessage = 'Existing ' .. GetSource(SourceId) .. ' citation(s) found:' .. strCitations
								.. '\n\nContinue?'
			if MessageBox(gPluginName, strMessage, 'OKCANCEL', 'QUESTION', 1, dialogMenu) == 2 then
				return true
			end
		end
		return false
	end
-- *************************End of Common routines used by functions Birth, Marriage, Death  ***************************

	dialogMenu:show()
	iup.MainLoop()
	dialogMenu:destroy()
	return selection

end			-- function Menu
--======================================================================================================================
function config()	-- Configuration dialog

	local dialogSettings			-- 'forward' declaration
	-- Sources
	-- Birth
	local lblBirth = iup.flatlabel{title = 'Birth', size = '40x11', alignment = 'ALEFT:ACENTRE'}
	local txtBirthSourceId = iup.text{
		visiblecolumns = 3, alignment = 'ACENTER', filter = 'NUMBER'
		}
	local lblBirthSource = iup.flatlabel{
		title = ' ', border = 'YES', size = '140x11', alignment = 'ALEFT:ACENTRE', padding = '4,0'
		}
	if gtblSettings.SourceIdBirth ~= 0 then
		txtBirthSourceId.value = gtblSettings.SourceIdBirth
		local SourceName, Templated = GetSource(gtblSettings.SourceIdBirth)
 		if SourceName ~= '' then
			if Templated then
				lblBirthSource.title = ''
				txtBirthSourceId.bgcolor = gtblConstants.Yellow
			else
				lblBirthSource.title = SourceName
			end
		else
			lblBirthSource.title = ''
			txtBirthSourceId.bgcolor = gtblConstants.Yellow
		end
	else
		lblBirthSource.title = ''
	end
	local btnBirthSource = iup.button{title = 'Select', padding = '2x0'}

	local hboxBirthSource = iup.hbox{
		lblBirth, txtBirthSourceId, lblBirthSource, btnBirthSource, iup.fill{};
		gap = 10, margin = '5x0'
		}

	-- Marriage
	local lblMarriage = iup.flatlabel{title = 'Marriage', size = '40x11', alignment = 'ALEFT:ACENTRE'}
	local txtMarriageSourceId = iup.text{
		visiblecolumns = 3, alignment = 'ACENTER', filter = 'NUMBER'
		}
	local lblMarriageSource = iup.flatlabel{
		title = ' ', border = 'YES', size = '140x11', alignment = 'ALEFT:ACENTRE', padding = '4,0'
		}
	if gtblSettings.SourceIdMarriage ~= 0 then
		txtMarriageSourceId.value = gtblSettings.SourceIdMarriage
		local SourceName, Templated = GetSource(gtblSettings.SourceIdMarriage)
 		if SourceName ~= '' then
			if Templated then
				lblMarriageSource.title = 'Source ' .. txtMarriageSourceId.value .. ' is templated'
				txtMarriageSourceId.bgcolor = gtblConstants.Yellow
			else
				lblMarriageSource.title = SourceName
			end
		else
			lblMarriageSource.title = ''
			txtMarriageSourceId.bgcolor = gtblConstants.Yellow
		end
	else
		lblMarriageSource.title = ''
	end
	local btnMarriageSource = iup.button{title = 'Select', padding = '2x0'}


	local hboxMarriageSource = iup.hbox{
		lblMarriage, txtMarriageSourceId, lblMarriageSource, btnMarriageSource, iup.fill{};
		gap = 10, margin = '5x0'
		}

	-- Death
	local lblDeath = iup.flatlabel{title = 'Death', size = '40x11', alignment = 'ALEFT:ACENTRE'}
	local txtDeathSourceId = iup.text{
		visiblecolumns = 3, alignment = 'ACENTER', filter = 'NUMBER'
		}
	local lblDeathSource = iup.flatlabel{
		title = ' ', border = 'YES', size = '140x11', alignment = 'ALEFT:ACENTRE', padding = '4,0'
		}
	if gtblSettings.SourceIdDeath ~= 0 then
		txtDeathSourceId.value = gtblSettings.SourceIdDeath
		local SourceName, Templated = GetSource(gtblSettings.SourceIdDeath)
 		if SourceName ~= '' then
			if Templated then
				lblDeathSource.title = 'Source ' .. txtDeathSourceId.value .. ' is templated'
				txtDeathSourceId.bgcolor = gtblConstants.Yellow
			else
				lblDeathSource.title = SourceName
			end
		else
			lblDeathSource.title = ''
			txtDeathSourceId.bgcolor = gtblConstants.Yellow
		end
	else
		lblDeathSource.title = ''
	end
	local btnDeathSource = iup.button{title = 'Select', padding = '2x0'}

	local hboxDeathSource = iup.hbox{
		lblDeath, txtDeathSourceId, lblDeathSource, btnDeathSource, iup.fill{};
		gap = 10, margin = '5x0'
		}

	local frameSources = iup.frame{
		iup.vbox{
			hboxBirthSource, hboxMarriageSource, hboxDeathSource;
			expandchildren = 'YES', gap = '2', margin = '6x3', fontstyle = ''
			},
		title = 'Sources', fontstyle = 'Bold'
		}

	-- Citation labels
-- Where Within Source Headers
	local txtLabelPrefixBirth = iup.text{value = gtblSettings.LabelPrefixBirth, visiblecolumns = '8'}
	local vboxLabelPrefixBirth = iup.vbox{iup.label{title = 'Birth'}, txtLabelPrefixBirth; gap = '2'}
	local txtLabelPrefixMarriage = iup.text{value = gtblSettings.LabelPrefixMarriage, visiblecolumns = '8'}
	local vboxLabelPrefixMarriage = iup.vbox{iup.label{title = 'Marriage'}, txtLabelPrefixMarriage; gap = '2'}
	local txtLabelPrefixDeath = iup.text{value = gtblSettings.LabelPrefixDeath, visiblecolumns = '8'}
	local vboxLabelPrefixDeath = iup.vbox{iup.label{title = 'Death'}, txtLabelPrefixDeath; gap = '2'}
	local lblTFSHeaders = iup.label{title = 'Where Within Source\nheaders', size = '0x0', multiline = 'yes', padding = '0x4', fgcolor = gtblConstants.Blue}
	local hboxCitatationLabels = iup.hbox{
		iup.frame{
			iup.hbox{
				lblTFSHeaders,
				vboxLabelPrefixBirth, iup.fill{},
				vboxLabelPrefixMarriage, iup.fill{},
				vboxLabelPrefixDeath;
				margin = '5x2', alignment = 'ABOTTOM', gap = '10'
				},
			sunken = 'YES'
			},
		margin = '5x0'
		}

	-- GRO Reference Labels
	local txtLabelDistrictName = iup.text{value = gtblSettings.LabelDistrictName, visiblecolumns = '6'}
	local hboxLabelDistrictName = iup.hbox{iup.label{title = 'District'}, iup.fill{}, txtLabelDistrictName; alignment = 'ACENTER'}
	local txtLabelVolume = iup.text{value = gtblSettings.LabelVolume, visiblecolumns = '6'}
	local hboxLabelVolume = iup.hbox{iup.label{title = 'Volume'}, iup.fill{}, txtLabelVolume; alignment = 'ACENTER'}
	local txtLabelPage = iup.text{value = gtblSettings.LabelPage, visiblecolumns = '6'}
	local hboxLabelPage = iup.hbox{iup.label{title = 'Page'}, iup.fill{}, txtLabelPage; alignment = 'ACENTER'}
	local txtLabelDistrictNo = iup.text{value = gtblSettings.LabelDistrictNo, visiblecolumns = '6'}
	local hboxLabelDistrictNo = iup.hbox{iup.label{title = 'District No.'}, iup.fill{}, txtLabelDistrictNo; alignment = 'ACENTER'}
	local txtLabelRegister = iup.text{value = gtblSettings.LabelRegister, visiblecolumns = '6'}
	local hboxLabelRegister = iup.hbox{iup.label{title = 'Register No.'}, iup.fill{}, txtLabelRegister; alignment = 'ACENTER'}
	local txtLabelEntry = iup.text{value = gtblSettings.LabelEntry, visiblecolumns = '6'}
	local hboxLabelEntry = iup.hbox{iup.label{title = 'Entry No.'}, iup.fill{}, txtLabelEntry; alignment = 'ACENTER'}

	local vboxGRORef1 = iup.vbox{hboxLabelDistrictName, hboxLabelVolume, hboxLabelPage; gap = '0', margin = '2x2'}
	local vboxGRORef2 = iup.vbox{hboxLabelDistrictNo, hboxLabelRegister, hboxLabelEntry; gap = '0', margin = '2x2'}

	local lblWWSLabels = iup.flatlabel{title = 'Where within Source\nLabels', size = '0x0', multiline='yes', padding = '0x4', fgcolor = gtblConstants.Blue}

	local hboxGRORefLabels = iup.hbox{
		iup.frame{
			iup.hbox{
				lblWWSLabels, vboxGRORef1, iup.fill{}, vboxGRORef2;
				margin = '5x2', alignment = 'ATOP', gap = '10'
				},
			sunken = 'YES'
			},
		margin = '5x0'
		}

		-- Event Specific Labels
	local txtLabelMother = iup.text{value = gtblSettings.LabelMother, expand = 'HORIZONTAL'}
	local hboxLabelMother = iup.hbox{
		iup.label{title = 'Birth', size = '30x0'},
		iup.label{title = 'Mother\'s Maiden Name'},
		txtLabelMother;
		gap = '10', margin = '0x0', alignment = 'ACENTER'
		}

	local txtLabelAge = iup.text{value = gtblSettings.LabelAge, visiblecolumns = '6'}
	local hboxLabelAge = iup.hbox{iup.label{title = 'Death', size = '30x0'}, iup.label{title= 'Age'}, txtLabelAge; alignment = 'ACENTER'}
	local txtLabelDOB = iup.text{value = gtblSettings.LabelDOB, visiblecolumns = '6'}
	local hboxLabelDOB = iup.hbox{iup.label{title = 'Date of Birth'}, txtLabelDOB; alignment = 'ACENTER'}
	local hboxLabelsDeath = iup.hbox{hboxLabelAge, iup.fill{}, hboxLabelDOB; gap = '10', margin = '0x0'}

	local lblTFSEventLabels = iup.flatlabel{title = 'Text from Source\nEvent Specific Labels',
									size = '0x0', multiline='yes', padding = '0x4', fgcolor = gtblConstants.Blue}

	local hboxEventLabels = iup.hbox{
		iup.frame{
			iup.hbox{
				lblTFSEventLabels,
				iup.vbox{
					hboxLabelMother,
					iup.label{separator = 'HORIZONTAL'},
					hboxLabelsDeath;
					gap = '4', margin = '3x0'
					};
				margin = '6x2', alignment = 'ATOP', gap = '10'
				},
			sunken = 'YES'
			},
		margin = '5x0'
		}

	local vboxLabels = iup.vbox{
		iup.hbox{
			iup.fill{},
			iup.label{
				title = 'Use ^ to specify trailing spaces on labels; $ to embed data within label ',
				fontstyle = 'Italic', alignment= 'ACENTRE'
				},
			iup.fill{}
			},
		hboxCitatationLabels, hboxGRORefLabels, hboxEventLabels;
		expandchildren = 'YES', margin = '5x2', gap = '2', fontstyle = '',
		tabtitle = ' Citation Labels '
		}

	-- Options settings

	function ItalicLabel (strTitle)
		local label = iup.label{title = strTitle}
		label.fontstyle = 'Italic'
		return label
	end			-- function ItalicLabel

	local lblQtrMonthNames = iup.label{title = 'Full Month Names && Quarter Months in Citation Text'}
	local tglQtrMonthNames = iup.toggle{value = ToggleValue(gtblSettings.QtrMonthNames),
		tipballoontitle = 'Month and quarter names in citation', tip = 'OFF: Q1, Jan etc.\nON: Q1 Jan-Feb-Mar, January etc.'}

	local lblUCRefs = iup.label{title = 'Make alpha characters in reference upper case'}
	local tglUCRefs = iup.toggle{value = ToggleValue(gtblSettings.UCRefs),
		tipballoontitle = 'Where within source references',
		tip = 'Convert lower case characters in Volume,\nDistrict, and Register numbers to upper case'}

	local lblUseGRODistricts = iup.label{title = 'Use GRO District List'}
	local tglUseGRODistricts = iup.toggle{value = ToggleValue(gtblSettings.UseGRODistricts),
		tipballoontitle = 'Use GRO District List',
		tip ='Use list of GRO District Names instead of all Places in project'}
	local btnGRODistricts = iup.button{
		title = 'Edit GRO District List', padding = '8x0',
		action = function(self) GRODistricts(dialogSettings) end
		}
	if not gtblSettings.UseGRODistricts then
		btnGRODistricts.visible = 'NO'
	else
		btnGRODistricts.visible = 'YES'
	end
	local hboxGRODistricts = iup.hbox{
		tglUseGRODistricts, btnGRODistricts;
		gap = '8', alignment = 'ACENTER', margin = '0x0'}

	function tglUseGRODistricts:action(state)
		if state == 0 then
			btnGRODistricts.visible = 'NO'
		else
			btnGRODistricts.visible = 'YES'
		end
	end

	local lblShortPlaces = iup.label{title = 'Only first field of District Name in Citation Text'}
	local tglShortPlaces = iup.toggle{value = ToggleValue(gtblSettings.ShortPlaces),
		tipballoontitle = 'Where within source Registration District Names',
		tip ='The district name should be entered as a full place\n'
			.. 'Enable this option to use the first field only in Where Within Source'}

	local lblIncNameInCitation = iup.hbox{
		iup.label{title ='Include Name(s) in Citation Text '}, ItalicLabel('(default) ');
		gap = 0, margin = '0x0' }
	local tglIncNameInCitation = iup.toggle{ ['3state'] = 'YES', value = gtblSettings.IncNameInCitation,
			tipballoontitle = 'Include name(s) in Citation Text',
			tip = 'This specifies the initial setting in the BMD dialogs\n'
				.. 'which can be changed for individual citations.\n'
				.. 'Options are: Always, Only if adding citation to name and Never'
			}
	local lblIncNameState = iup.label{title = ' ', size = '120x0'}
	local hboxIncNameInCitation = iup.hbox{tglIncNameInCitation, lblIncNameState, gap = 4, margin = '0x0'}

	local lblNameFormat = iup.label{title='Format of Names'}
	local listNameFormat = iup.list{
		'Surname, Given', 'SURNAME, Given', 'Given Surname', 'Given SURNAME',
		value = gtblSettings.NameFormat, dropdown = 'YES', size = '95x0',
		tipballoontitle = 'Format of names',
		tip = 'Select how names are to be capitalised in citations\n'
			.. 'Also applies to mother\'s maiden name'
		}

	local lblCitationForName = iup.hbox{
		iup.label{title ='Add Citation to Name(s) '}, ItalicLabel('(default) ');
		gap = 0, margin = '0x0' }
	local tglCitationNameBirth = iup.toggle{value = ToggleValue(gtblSettings.CitationNameBirth), title = 'Births',
		tipballoontitle = 'Add Citation to Name',
		tip = 'This specifies the initial setting in the Birth dialog\n'
				.. 'which can be changed for individual citations.\n'
		}
	local tglCitationNameMarriage = iup.toggle{value = ToggleValue(gtblSettings.CitationNameMarriage), title = 'Marriages',
		tipballoontitle = 'Add Citation to Names',
		tip = 'This specifies the initial setting in the Marriage dialog\n'
				.. 'which can be changed for individual citations.\n'
		}
	local tglCitationNameDeath = iup.toggle{value = ToggleValue(gtblSettings.CitationNameDeath), title = 'Deaths',
		tipballoontitle = 'Add Citation to Name',
		tip = 'This specifies the initial setting in the Death dialog\n'
				.. 'which can be changed for individual citations.\n'
		}
	local hboxCitationNames = iup.hbox{tglCitationNameBirth,tglCitationNameMarriage,tglCitationNameDeath;
								margin = '0x0'}

	local function SetlblIncNameState()
		local strState
		if tglIncNameInCitation.value == 'ON' then
			strState = 'Always'
		elseif tglIncNameInCitation.value == 'OFF' then
			strState = 'Never'
		else
			strState = 'Only if adding citation to name'
		end
		lblIncNameState.title = strState
	end

	SetlblIncNameState()

	function tglCitationNameBirth:action()
		if not ValidateCitationToName() then
			tglCitationNameBirth.value = 'OFF'
		end
	end
	function tglCitationNameMarriage:action()
		if not ValidateCitationToName() then
			tglCitationNameMarriage.value = 'OFF'
		end
	end
	function tglCitationNameDeath:action()
		if not ValidateCitationToName() then
			tglCitationNameDeath.value = 'OFF'
		end
	end

	local lblCitationToMotherDefault = iup.hbox{
		iup.label{title ='Add Birth Citation to Mother\'s Name '},
		ItalicLabel('(default) ');
		gap = 0, margin = '0x0'
		}
	local tglCitationToMotherDefault = iup.toggle{value = ToggleValue(gtblSettings.CitationToMotherDefault),
		tipballoontitle= 'Add Birth Citation to Mother\'s Name',
		tip = 'This specifies the initial setting in the Birth dialog\n'
			.. 'which can be changed for individual citations.\n'
		}

	local lblNewMotherGivenName = iup.label{title = 'Given name for new Birth Mother records'}
	local txtNewMotherGivenName = iup.text{
		value = gtblSettings.NewMotherGivenName, visiblecolumns = '8',
		tipballoontitle = 'Given name for new Birth Mother records',
		tip = 'If Add Birth Citation to Mother\'s Name is on and the Mother\'s record\n'
			.. 'is to be created (or existing record has no name)\n'
			.. 'this name will be combined with the entered Surname'
		}

	local lblCitationEntryDate = iup.label{title = 'Citation Date'}
	local listCitationEntryDate = iup.list{
		'No Date', 'Current Date', 'GRO Index Reg. Date',
		value = gtblSettings.CitationEntryDate,
		dropdown = 'YES', size = '95x0',
		tipballoontitle = 'Citation Date',
		tip = 'Date to be recorded in the Citation Specific Date'
		}

	local lblCitationAssess = iup.hbox{
		iup.label{title ='Citation Assessment '}, ItalicLabel('(default) ');
		gap = 0, margin = '0x0' }
 	local listCitationAssess = iup.list{
		dropdown = 'YES', size = '95x0', visibleitems = 6,
		tipballoontitle = 'Citation Assessment',
		tip = 'This specifies the initial setting in the BMD dialogs\n'
			.. 'which can be changed for individual citations.\n'
		}
	PopulateList(listCitationAssess, gtblConstants.CitationAssess)
	listCitationAssess.value = gtblSettings.CitationAssess

	local gridboxOptions = iup.gridbox{
		lblQtrMonthNames, tglQtrMonthNames,
		lblUCRefs, tglUCRefs,
		lblUseGRODistricts, hboxGRODistricts,
		lblShortPlaces, tglShortPlaces,
		lblIncNameInCitation, hboxIncNameInCitation,
		lblNameFormat, listNameFormat,
		lblCitationForName, hboxCitationNames,
		lblCitationToMotherDefault, tglCitationToMotherDefault,
		lblNewMotherGivenName, txtNewMotherGivenName,
		lblCitationEntryDate, listCitationEntryDate,
		lblCitationAssess, listCitationAssess;
		numdiv = 2, sizelin = -1, sizecol = -1, gapcol = 32, gaplin = 6, alignmentlin = 'ACENTER',
		padding = '0x0'										-- Set for inheritance
		}
	local hboxOptions = iup.hbox{
		iup.frame{
			iup.hbox{
				gridboxOptions;
				expandchildren = 'YES', margin = '3x2', fontstyle = ''
				},
			sunken = 'YES',
			};
		margin = '5x2',
		tabtitle = ' Options '
		}

	-- Update Checks

	function FormatYMDdate(Date)				-- Date yyyy-mm-dd
		return os.date('%d %b %Y',os.time(YMDtoTable(Date)))
	end

	local lblAutoCheck = iup.label{title = 'Auto Check for Plugin Updates'}
	local tglAutoCheck = iup.toggle{}
	if gtblSettings.VersionCheckInterval ~= 0 then
		tglAutoCheck.value = 'ON'
	end

	local lblCheckFrequency = iup.label{title = 'Auto Check Frequency'}
	local i
	for k, v in pairs(gtblConstants.VersionCheckFrequency) do
		if gtblSettings.VersionCheckInterval == v then
			i = k
			break
		end
	end
	local listCheckFrequency = iup.list{
		'Daily', 'Weekly', 'Fortnightly', 'Four Weekly',
		dropdown = 'YES', value = i
		}
	if gtblSettings.VersionCheckInterval == 0 then
		lblCheckFrequency.active = 'NO'
		listCheckFrequency.active = 'NO'
	end

	local lblLastCheck = iup.label{title = 'Last Auto Check Date'}
	local txtLastCheck = iup.text{
		value = FormatYMDdate(gtblSettings.VersionCheckedDate),
		visiblecolumns = 8, readonly = 'YES', canfocus = 'NO'
		}

	local lblNextCheck = iup.label{title = 'Next Auto Check Date'}
	local txtNextCheck = iup.text{
		visiblecolumns = 8, readonly = 'YES', canfocus = 'NO'
		}
	if gtblSettings.VersionCheckInterval ~= 0 then
		txtNextCheck.value = FormatYMDdate(gtblSettings.VersionNextCheck)
	end

	function NextCheck(Date, Interval)			-- Date yyyy-mm-dd
		while Date <= os.date('%Y-%m-%d') do
			Date = DateAdd(Date, Interval)
		end
		return Date
	end

	function tglAutoCheck:action(state)
		if state == 1 then
			lblCheckFrequency.active = 'YES'
			listCheckFrequency.active = 'YES'
			if listCheckFrequency.value == '0' then
				listCheckFrequency.value = 2
				local Interval = gtblConstants.VersionCheckFrequency[listCheckFrequency.value]
				txtNextCheck.value = FormatYMDdate(NextCheck(gtblSettings.VersionCheckedDate, Interval))
			end
		else
			lblCheckFrequency.active = 'NO'
			listCheckFrequency.active = 'NO'
			txtNextCheck.value = ''
		end
	end

	function listCheckFrequency:valuechanged_cb()
		local Interval = gtblConstants.VersionCheckFrequency[listCheckFrequency.value]
		txtNextCheck.value = FormatYMDdate(NextCheck(gtblSettings.VersionCheckedDate, Interval))
	end

	local gridboxUpdateCheck = iup.gridbox{
		lblAutoCheck, tglAutoCheck,
		lblCheckFrequency, listCheckFrequency,
		lblLastCheck, txtLastCheck,
		lblNextCheck, txtNextCheck;
		numdiv = 2, sizelin = -1, sizecol = -1, gapcol = 32, gaplin = 6, alignmentlin = 'ACENTER',
		padding = '0x0'										-- Set for inheritance
		}

	local btnUpdateCheck = iup.button{
		title = 'Check for Update', padding = '10x3'
		}
	function btnUpdateCheck:action()
		local bOK, strMsg = VersionCheck(dialogSettings)
		if not bOK then
			strMsg = 'Check for plugin update failed\n\n' .. strMsg
			OkOnlyMessage(gPluginName, strMsg, 'ERROR, dialogSettings')
		else
			if strMsg == 'Y' then
				strMsg = 'You are running the latest version'
				OkOnlyMessage(gPluginName, strMsg, 'INFORMATION', dialogSettings)
			end
		end
	end

	local hboxUpdateCheck = iup.hbox{
		iup.fill{},
		iup.frame{
			iup.vbox{
				gridboxUpdateCheck, iup.label{separator='HORIZONTAL'}, btnUpdateCheck;
				expandchildren = 'NO', margin = '3x2', fontstyle = '', cgap = 8
				},
			sunken = 'YES',
			},
		iup.fill{};
		margin = '5x2',
		tabtitle = ' Update Check '
		}

------------------------------------------------------------------------------------------------------------------------
	-- Footer Buttons
	local btnSave = iup.button{
		title = 'Save', image = 'IUP_FileSave', padding = '10x0'
		}
	function btnSave:action()
		if txtBirthSourceId.bgcolor == gtblConstants.Yellow or txtMarriageSourceId.bgcolor == gtblConstants.Yellow
				or txtDeathSourceId.bgcolor == gtblConstants.Yellow then
			OkOnlyMessage(gPluginName, 'Source(s) are invalid', 'ERROR', dialogSettings)
			return
		end
		local bAnyChanged = false

		-- Sources
		gtblSettings.SourceIdBirth, bAnyChanged = SettingUpdate(txtBirthSourceId.value, gtblSettings.SourceIdBirth, bAnyChanged)
		gtblSettings.SourceIdMarriage, bAnyChanged = SettingUpdate(txtMarriageSourceId.value, gtblSettings.SourceIdMarriage, bAnyChanged)
		gtblSettings.SourceIdDeath, bAnyChanged = SettingUpdate(txtDeathSourceId.value, gtblSettings.SourceIdDeath, bAnyChanged)

		-- Citation Prefixes
		gtblSettings.LabelPrefixBirth, bAnyChanged = SettingUpdate(txtLabelPrefixBirth.value, gtblSettings.LabelPrefixBirth, bAnyChanged)
		gtblSettings.LabelPrefixMarriage, bAnyChanged = SettingUpdate(txtLabelPrefixMarriage.value, gtblSettings.LabelPrefixMarriage, bAnyChanged)
		gtblSettings.LabelPrefixDeath, bAnyChanged = SettingUpdate(txtLabelPrefixDeath.value, gtblSettings.LabelPrefixDeath, bAnyChanged)

		-- GRO Reference Labels
		gtblSettings.LabelDistrictName, bAnyChanged = SettingUpdate(txtLabelDistrictName.value, gtblSettings.LabelDistrictName, bAnyChanged)
		gtblSettings.LabelVolume, bAnyChanged = SettingUpdate(txtLabelVolume.value, gtblSettings.LabelVolume, bAnyChanged)
		gtblSettings.LabelPage, bAnyChanged = SettingUpdate(txtLabelPage.value, gtblSettings.Page, bAnyChanged)
		gtblSettings.LabelDistrictNo, bAnyChanged = SettingUpdate(txtLabelDistrictNo.value, gtblSettings.LabelDistrictNo, bAnyChanged)
		gtblSettings.LabelRegister, bAnyChanged = SettingUpdate(txtLabelRegister.value, gtblSettings.LabelRegister, bAnyChanged)
		gtblSettings.LabelEntry, bAnyChanged = SettingUpdate(txtLabelEntry.value, gtblSettings.LabelEntry, bAnyChanged)

		-- Event Specific Labels
   		gtblSettings.LabelMother, bAnyChanged = SettingUpdate(txtLabelMother.value, gtblSettings.LabelMother, bAnyChanged)
		gtblSettings.LabelAge, bAnyChanged = SettingUpdate(txtLabelAge.value, gtblSettings.LabelAge, bAnyChanged)
		gtblSettings.LabelDOB, bAnyChanged = SettingUpdate(txtLabelDOB.value, gtblSettings.LabelDOB, bAnyChanged)

		-- Other settings
		gtblSettings.QtrMonthNames, bAnyChanged = SettingUpdate(tglQtrMonthNames.value, gtblSettings.QtrMonthNames, bAnyChanged)
		gtblSettings.UCRefs, bAnyChanged = SettingUpdate(tglUCRefs.value, gtblSettings.UCRefs, bAnyChanged)
		gtblSettings.UseGRODistricts, bAnyChanged = SettingUpdate(tglUseGRODistricts.value, gtblSettings.UseGRODistricts, bAnyChanged)
		gtblSettings.ShortPlaces, bAnyChanged = SettingUpdate(tglShortPlaces.value, gtblSettings.ShortPlaces, bAnyChanged)
		gtblSettings.IncNameInCitation, bAnyChanged = SettingUpdate(tglIncNameInCitation.value, gtblSettings.IncNameInCitation, bAnyChanged)
		gtblSettings.NameFormat, bAnyChanged = SettingUpdate(listNameFormat.value, gtblSettings.NameFormat, bAnyChanged)
		gtblSettings.CitationNameBirth, bAnyChanged = SettingUpdate(tglCitationNameBirth.value, gtblSettings.CitationNameBirth, bAnyChanged)
		gtblSettings.CitationNameMarriage, bAnyChanged = SettingUpdate(tglCitationNameMarriage.value, gtblSettings.CitationNameMarriage, bAnyChanged)
		gtblSettings.CitationNameDeath, bAnyChanged = SettingUpdate(tglCitationNameDeath.value, gtblSettings.CitationNameDeath, bAnyChanged)
		gtblSettings.CitationToMotherDefault, bAnyChanged = SettingUpdate(tglCitationToMotherDefault.value, gtblSettings.CitationToMotherDefault, bAnyChanged)
		gtblSettings.NewMotherGivenName, bAnyChanged = SettingUpdate(txtNewMotherGivenName.value, gtblSettings.NewMotherGivenName, bAnyChanged)
		gtblSettings.CitationEntryDate, bAnyChanged = SettingUpdate(listCitationEntryDate.value, gtblSettings.CitationEntryDate, bAnyChanged)
		gtblSettings.CitationAssess, bAnyChanged = SettingUpdate(listCitationAssess.value, gtblSettings.CitationAssess, bAnyChanged)

		-- Update Check
		local Interval, NextCheckDate
		if tglAutoCheck.value == 'OFF' then
			Interval = 0
			NextCheckDate = gtblSettings.VersionNextCheck
		else
			Interval = gtblConstants.VersionCheckFrequency[listCheckFrequency.value]
			NextCheckDate = NextCheck(gtblSettings.VersionCheckedDate, Interval)
		end
		gtblSettings.VersionCheckInterval, bAnyChanged = SettingUpdate(Interval, gtblSettings.VersionCheckInterval, bAnyChanged)
		gtblSettings.VersionNextCheck, bAnyChanged = SettingUpdate(NextCheckDate, gtblSettings.VersionNextCheck, bAnyChanged)

		if bAnyChanged then
			SaveSettings()
		end
		return iup.CLOSE
	end

	function SettingUpdate(NewValue, CurrSetting, bAnyChanged)
		-- Force new settings to same types as current settings
		local NewSetting
		if type(CurrSetting) == 'boolean' then
			NewSetting = false
			if NewValue == 'ON' then
				NewSetting = true
			end
		elseif type(CurrSetting) == 'number' then
			if NewValue ~= '' then
				NewSetting = tonumber(NewValue)
			else
				NewSetting = 0
			end
		else
			NewSetting = NewValue
		end
		if NewSetting ~= CurrSetting then
			return NewSetting, true
		else
			return CurrSetting, bAnyChanged
		end
	end
------------------------------------------------------------------------------------------------------------------------
	local btnCancel = iup.button{
		title = 'Cancel', padding = '10x3',
		action = function(self) return iup.CLOSE end
		}

	local btnHelp = iup.button{
		title = 'Help', padding = '10x3',
		tipballoontitle = 'Settings', tip = 'Display Settings help page'
		}
	function btnHelp:action()
		fhShellExecute('https://pluginstore.family-historian.co.uk/page/help/gro-bmd-index-citations-settings')
	end


------------------------------------------------------------------------------------------------------------------------
	dialogSettings = iup.dialog{
		iup.vbox{
			frameSources,
			iup.tabs{vboxLabels, hboxOptions, hboxUpdateCheck; fontstyle = 'Bold'},
			iup.hbox{
				iup.fill{}, btnSave, btnHelp, btnCancel, iup.fill{};
				normalizesize = 'BOTH', margin = '0x0', gap = 40
				};
			gap = 10, margin = '10x10'
			};
		resize = 'No', minbox = 'No', maxbox = 'No', helpbutton = 'Yes',
		title = gPluginName .. ' - Settings',
		defaultesc = btnCancel,
		taskbarbutton = 'SHOW',
		tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1'
		}
	iup.SetAttribute(dialogSettings, 'NATIVEPARENT', fhGetContextInfo('CI_PARENT_HWND'));

	function SourceCheck(SourceId)
		local SourceName, Templated = GetSource(tonumber(SourceId))
		if Templated then
			OkOnlyMessage(gPluginName, 'Templated sources are not currently supported', 'WARNING', dialogSettings)
			return false, ''
		end
		if SourceName ~= '' then
			return true, SourceName
		else
			OkOnlyMessage(gPluginName, 'Invalid Source Record Id', 'ERROR', dialogSettings)
			return false, ''
		end
	end
	------------------ Birth functions and call backs ------------------------------------------------------------------
	function txtBirthSourceId:valuechanged_cb()		-- Clear title if ID changed
		txtBirthSourceId.bgcolor = gtblConstants.White
		lblBirthSource.title = ''
	end
	function txtBirthSourceId:killfocus_cb()
		if lblBirthSource.title == '' then
			SetBirthTitle()
		end
	end
	function txtBirthSourceId:k_any(c)
		if c == iup.K_CR then
			if lblBirthSource.title == '' then
				SetBirthTitle()
			end
		end
	end

	function btnBirthSource:action()
		local ptrlist = fhPromptUserForRecordSel('SOUR', 1, dialogSettings.HWND)
		if #ptrlist ~= 0 then
			txtBirthSourceId.value = fhGetRecordId(ptrlist[1])
			txtBirthSourceId.bgcolor = gtblConstants.White
			SetBirthTitle()
		end
	end

	function SetBirthTitle()
		if txtBirthSourceId.value == 0 or txtBirthSourceId.value == '' then
			lblBirthSource.title = ''
			return
		end
		local SrcValid
		SrcValid, lblBirthSource.title = SourceCheck(txtBirthSourceId.value)
		if not SrcValid then
			txtBirthSourceId.bgcolor = gtblConstants.Yellow
		end
	end

	------------------ Marriage functions and call backs ---------------------------------------------------------------
	function txtMarriageSourceId:valuechanged_cb()		-- Clear title if ID changed
		txtMarriageSourceId.bgcolor = gtblConstants.White
		lblMarriageSource.title = ''
	end
	function txtMarriageSourceId:killfocus_cb()
		if lblMarriageSource.title == '' then
			SetMarriageTitle()
		end
	end
	function txtMarriageSourceId:k_any(c)
		if c == iup.K_CR then
			if lblMarriageSource.title == '' then
				SetMarriageTitle()
			end
		end
	end

	function btnMarriageSource:action()
		local ptrlist = fhPromptUserForRecordSel('SOUR', 1, dialogSettings.HWND)
		if #ptrlist ~= 0 then
			txtMarriageSourceId.value = fhGetRecordId(ptrlist[1])
			txtMarriageSourceId.bgcolor = gtblConstants.White
			SetMarriageTitle()
		end
	end

	function SetMarriageTitle()
		if txtMarriageSourceId.value == 0 or txtMarriageSourceId.value == '' then
			lblMarriageSource.title = ''
			return
		end
		local SrcValid
		SrcValid, lblMarriageSource.title =SourceCheck(txtMarriageSourceId.value)
			if not SrcValid then
				txtMarriageSourceId.bgcolor = gtblConstants.Yellow
		end
	end

	------------------ Death functions and call backs ------------------------------------------------------------------
	function txtDeathSourceId:valuechanged_cb()		-- Clear title if ID changed
		txtDeathSourceId.bgcolor = gtblConstants.White
		lblDeathSource.title = ''
	end
	function txtDeathSourceId:killfocus_cb()
		if lblDeathSource.title == '' then
			SetDeathTitle()
		end
	end
	function txtDeathSourceId:k_any(c)
		if c == iup.K_CR then
			if lblDeathSource.title == '' then
				SetDeathTitle()
			end
		end
	end

	function btnDeathSource:action()
		local ptrlist = fhPromptUserForRecordSel('SOUR', 1, dialogSettings.HWND)
		if #ptrlist ~= 0 then
			txtDeathSourceId.value = fhGetRecordId(ptrlist[1])
			txtDeathSourceId.bgcolor = gtblConstants.White
			SetDeathTitle()
		end
	end

	function SetDeathTitle()
		if txtDeathSourceId.value == 0 or txtDeathSourceId.value == '' then
			lblDeathSource.title = ''
			return
		end
		local SrcValid
		SrcValid, lblDeathSource.title = SourceCheck(txtDeathSourceId.value)
			if not SrcValid then
				txtDeathSourceId.bgcolor = gtblConstants.Yellow
		end
	end
	--------------------------------------------------------------------------------------------------------------------
	function tglIncNameInCitation:action()
		if tglIncNameInCitation.value == 'OFF' then
			if tglCitationNameBirth.value == 'ON'
					or tglCitationNameMarriage.value == 'ON'
					or tglCitationNameDeath.value == 'ON' then
				local strMsg = 'Include Name(s) in Citation Text must be set to\n\n'
								.. '"Always" or "Only if adding citation to name"\n\n'
								.. 'if any Add Citation to Name option is on'
				OkOnlyMessage(gPluginName, strMsg, 'WARNING',dialogSettings)
				tglIncNameInCitation.value = 'ON'
			end
		end
		SetlblIncNameState()
	end

	function ValidateCitationToName()
		if tglIncNameInCitation.value == 'OFF' then
			local strMsg = 'Not permitted if Include Name(s) in Citation Text\nis set to Never'
			OkOnlyMessage(gPluginName, strMsg, 'ERROR', dialogSettings)
			return false
		end
		return true
	end

	-- Equalise section label sizes in Citation Labels frame
	dialogSettings:map()
	local LabelSize = lblWWSLabels.size
	local LabelWidth = tonumber(LabelSize:match('^(%d*)'))
	LabelSize = lblTFSHeaders.size
	if tonumber(LabelSize:match('^(%d*)')) > LabelWidth then
		LabelWidth = tonumber(LabelSize:match('^(%d*)'))
	end
	LabelSize = lblTFSEventLabels.size
	if tonumber(LabelSize:match('^(%d*)')) > LabelWidth then
		LabelWidth = tonumber(LabelSize:match('^(%d*)'))
	end
	LabelSize = tostring(LabelWidth) .. 'x0'
	lblWWSLabels.size = LabelSize
	lblTFSHeaders.size = LabelSize
	lblTFSEventLabels.size = LabelSize

	dialogSettings:popup(IUP_CENTERPARENT, IUP_CENTERPARENT)
	dialogSettings:destroy()

end			-- function config

function GetSource(Id)	-- Check source exists, returns empty string if not found
						--						if found returns name and true/false if templated or not
	local ptrSource = fhNewItemPtr()
	local ptrTemplate = fhNewItemPtr()
	ptrSource:MoveToRecordById('SOUR',Id)
	if ptrSource:IsNull() then
		return '', false
	else
		ptrTemplate = fhGetItemPtr(ptrSource, '~._SRCT>')
		if ptrTemplate:IsNotNull() then
			return fhGetDisplayText(ptrSource), true
		else
			return fhGetDisplayText(ptrSource), false
		end
	end
end			-- fuction GetSource

--======================================================================================================================
--====								Main Dialog used for Births, Marriages and Deaths								====
--======================================================================================================================

function MainDialog(Event, arrInd1, arrInd2, arrFam, intFirstYear, intLastYear)
	-- arrInd2 and arrFam are nil for birth and death
	-- arrInd2 will be blank if family only has one individual

	local arrActions = {}
	local strTitle = 'GRO Index ' .. Event .. ' Citation'

	local strHeader = 'Citation for ' .. Event .. ' of ' .. arrInd1['DispNameFull']
	if Event == 'Marriage' then
		strHeader = strHeader .. '\nand '

		if arrInd2 ~= nil then
			strHeader = strHeader .. arrInd2['DispNameFull']
		else
			strHeader = strHeader .. '<>'
		end
	end
	local vboxFormHead = iup.vbox{
		iup.label{title = strHeader, fontstyle = 'Bold', padding = '0x0', alignment = 'ACENTER', multiline = 'YES'},
		alignment = 'ACENTER', normalizesize = 'horizontal', expandchildren = 'YES'
		}

	------------------------ Registration details panel --------------------
	-- Make array of years for listbox
	local addrange = 3				-- Number of years to extend range if very narrow
	local ExactYear = nil
	if intFirstYear == intLastYear then
		ExactYear = intFirstYear
	end
	if intLastYear - intFirstYear < addrange * 2 + 1 then
		intFirstYear = math.max(intFirstYear - addrange, 1837)
		intLastYear = math.min(intLastYear + addrange, gtblConstants.CurrYear)
	end

	local arrYears = {}
	for Yr = intFirstYear, intLastYear do
		table.insert(arrYears, Yr)
	end
	local listYear = iup.list{dropdown = 'YES'; editbox = 'YES', visibleitems = 10, visiblecolumns = 3}
	PopulateList(listYear, arrYears)
	if ExactYear ~= nil then
		listYear.value = ExactYear
	end

	local listQtrMonth = iup.list{ dropdown = 'YES', visibleitems = 10, visiblecolumns = 8, bgcolor = gtblConstants.White}
	PopulateList(listQtrMonth, gtblConstants.PeriodsLong)
	local hboxWhen = iup.hbox{
		iup.label{title = 'Year:', size = '35x0', padding = '2x0'}, listYear, iup.fill{},
		iup.label{title = 'Quarter or Month:'}, listQtrMonth, iup.fill{}, iup.fill{};
		gap = 4, margin = '0x0', alignment = 'ACENTER'
		}
	local listDistrictName = iup.list{dropdown = 'YES', visibleitems = 15, expand = 'HORIZONTAL'}
	if not gtblSettings.UseGRODistricts then
		listDistrictName.editbox = 'YES'
		for i,v in pairs(tblPlaces) do
			listDistrictName[tostring(i)] = v[1]
		end
	else
		listDistrictName.editbox = 'NO'
		PopulateListFromtblGROPlaces(listDistrictName)
	end
	local strDistrictNameFilter = ''

	local hboxDistrictName = iup.hbox{
		iup.label{title='District:', size = '35x0', padding = '2x0'}, listDistrictName;
		alignment = 'ACENTER'
		}

	local btnGRODistricts = iup.button{title = 'Add/Edit', padding = '4x1'}
	if gtblSettings.UseGRODistricts then
		hboxDistrictName:append(btnGRODistricts)
	end

	local txtVolume = iup.text {visiblecolumns = 4}
	local vboxVolume = iup.vbox{iup.label{title = 'Volume'}, txtVolume; alignment = 'ACENTER', gap = 2}

	local txtPage = iup.text {visiblecolumns = 4}
	local vboxPage = iup.vbox{iup.label{title = 'Page'}, txtPage; alignment = 'ACENTER', gap = 2}

	local txtDistNo = iup.text {visiblecolumns = 4}
	local vboxDistNo = iup.vbox{iup.label{title = 'District No.'}, txtDistNo; alignment = 'ACENTER', gap = 2}

	local txtRegister = iup.text {visiblecolumns = 4}
	local vboxRegister = iup.vbox{iup.label{title = 'Register No.'}, txtRegister; alignment = 'ACENTER', gap = 2}

	local txtEntry = iup.text {visiblecolumns = 6, filter = 'NUMBER'}
	local vboxEntry = iup.vbox{iup.label{title = 'Entry No.'}, txtEntry; alignment = 'ACENTER', gap = 2}

	local hboxRefs = iup.hbox{iup.label{size = '21x0'},vboxVolume, vboxPage, vboxDistNo, vboxRegister, vboxEntry;
			gap = 25, margin = '0x0', padding = '0x0'}

	local txtCitationNote = iup.text{expand = 'HORIZONTAL'}
	local hboxCitationNote = iup.hbox{
		iup.label{title = 'Citation Specific Note: ', padding = '2x0'}, txtCitationNote;
		alignment = 'ACENTER'
		}

	local frameRegistration = iup.frame{
		iup.hbox{
			iup.frame{
				iup.vbox{
					hboxWhen, hboxDistrictName, hboxRefs, hboxCitationNote;
					gap = 2, margin = '2x4', fontstyle= ''
					},
				sunken = 'YES'
				},
			padding = '5x0'
			},
		title='Registration Details', fontstyle= 'Bold'
		}

	--------- Event specific details - Birth (mother), Death (age/DOB) ---------

	local txtMother = iup.text{
		size = '115x0', padding = '0x0',
		tipballoontitle = 'Mother\'s Maiden Name',
		tip = "Ctrl ' (single quote) to copy '" .. arrInd1['Mother'] .. "'"}
	local tglCitationToMother = iup.toggle{
		title = 'Add Citation to Mother\'s Name:', value = ToggleValue(gtblSettings.CitationToMotherDefault), rightbutton = 'YES'
		}

	local txtAge = iup.text{visiblecolumns = 3, padding = '0x0', filter = 'NUMBER'}
	local txtDOB = iup.text{visiblecolumns = 10, padding = '0x0'}

	local boxEvent
	if Event == 'Birth' then
		boxEvent = iup.vbox{
			iup.hbox{
				iup.label{title = 'Mother\'s Maiden Name:'; padding = '0x0'}, txtMother,
				iup.label{title = '(Ctrl \' to copy Mother\'s name) ', fontstyle = 'Italic'}; iup.fill{};
		 		gap = 4, margin = '0x0', alignment='ACENTER'
				},
			iup.hbox{
				tglCitationToMother, iup.fill{};
				margin = '0x0'
				};
			margin = '4x2'
			}
	elseif Event == 'Death' then
		boxEvent = iup.hbox{
			iup.label{title = 'Age:', size = '35x0'}, txtAge, iup.fill{},
			iup.label{title = 'Date of Birth or birth year:', size = '0x0'}, txtDOB,
			iup.fill{},iup.fill{};
			gap = 4, margin = '0x4', alignment = 'ACENTER'
			}
	end

	local frameEvent
	if Event ~= 'Marriage' then
		frameEvent = iup.frame{
			iup.hbox{
				iup.frame{
					boxEvent;
					sunken = 'YES'
					},
				padding = '5x0', fontstyle = ''
				},
			title = Event, fontstyle = 'Bold'
			}
	end

	function ValidateDate(strDate, PartDate)		-- Returns true/false, date formated to FH preferred short date format
													-- and a date object if valid
		local dtDate = fhNewDate()
		if not dtDate:SetValueAsText(strDate, false) then
			return false
		end
		local strType = dtDate:GetType()
		if strType ~= 'Simple' then
			return false
		end
		local dpDatePt = dtDate:GetDatePt1()
		local Day = dpDatePt:GetDay()
		local Month = dpDatePt:GetMonth()
		local Year = dpDatePt:GetYear()
		if Year < 1730 then					-- Earliest DOB of a death in 1837 is ~100 years before
			return false
		end
		local FmtDate = dtDate:GetDisplayText('COMPACT')
		if PartDate and Day == 0 then
			return true, FmtDate, dtDate
		end
		if Day == 0 or Month == 0 or Month > 12 then
			return false
		end

		local DaysInMonth = {31,29,31,30,31,30,31,31,30,31,30,31}
		if Day > DaysInMonth[Month] then
			return false
		end
		if Month ==2 and Day == 29 then
			if Year%4 ~= 0 then
				return false
			end
			if Year%100 == 0 and Year%400 ~=0 then
				return false
			end
		end

		return true, FmtDate, dtDate
	end

	------------------------ Name(s) for citation panel ------------------------

	local titleFrame = 'Name'

	local CitationToNameDefault
	if Event == 'Birth' then
		CitationToNameDefault = gtblSettings.CitationNameBirth
	elseif Event == 'Marriage' then
		CitationToNameDefault = gtblSettings.CitationNameMarriage
	else
		CitationToNameDefault = gtblSettings.CitationNameDeath
	end
	local tglCitationToNames = iup.toggle{
		title = 'Add Citation to Name:', value = ToggleValue(CitationToNameDefault), rightbutton = 'YES'
		}

	local tglIncNameInCitation = iup.toggle{
		title ='Include Name in Citation:', margin = '0x0', rightbutton = 'YES', ['3state'] = 'YES',
		value = gtblSettings.IncNameInCitation
		}
	local lblIncNameState = iup.label{title = ' ', expand = 'HORIZONTAL'}

	function SurnameToCopy()	-- Get surname for first individual to be used if Ctrl ' keyed
								-- For death of a wife it will be her married name
		if Event ~= 'Death' then
			return arrInd1['Surname']
		else
			local ptrField = fhNewItemPtr()
			ptrField:MoveTo(arrInd1.Ptr, '~.SEX')
			if fhGetValueAsText(ptrField) ~= 'Female' then
				return arrInd1['Surname']
			else
				ptrField:MoveTo(arrInd1.Ptr, 'INDI.~SPOU[LAST]>')
				if ptrField:IsNull() then
					return arrInd1['Surname']
				else
					return fhu.getSurname(fhGetItemText(ptrField, '~.NAME:STORED'))
				end
			end
		end
	end

	local txtGiven = iup.text{size = '150x0', tip = "Ctrl ' (single quote) to copy '" .. arrInd1['GivenName'] .. "'"}
	local txtSurname = iup.text{size = '115x0', tip = "Ctrl ' (single quote) to copy '" .. SurnameToCopy() .. "'"}

	local txtGiven2 = iup.text{size = '150x0'}
	local txtSurname2 = iup.text{size = '115x0'}

	local lblRow = 'Citation Name'
	local hboxNameLabels = iup.hbox{
		iup.label{title = 'Given', size = '150x0', padding = '4x0'},
		iup.label{title = 'Surname', size = '0x0', padding = '4x0'};
		gap = 2, margin = '0x0'
		}
	local hboxCitationName1 = iup.hbox{txtGiven, txtSurname; gap = 2, margin = '0x1'}
	local hboxCitationName2 = iup.hbox{txtGiven2, txtSurname2; gap = 2, margin = '0x3'}

	local vboxCitationNames = iup.vbox{
		hboxNameLabels, hboxCitationName1;
		gap = 0, padding = '2x0',
		tipballoontitle = 'Citation names'
		}

	if arrInd2 ~= nil then
		lblRow = lblRow .. '(s)'
		tglIncNameInCitation.title = tglIncNameInCitation.title:gsub('Name','Name(s)')
		tglCitationToNames.title = tglCitationToNames.title:gsub('Name','Name(s)')
		titleFrame = titleFrame .. '(s)'
		txtGiven2.tip = "Ctrl ' (single quote) to copy '" .. arrInd2['GivenName'] .. "'"
		txtSurname2.tip = "Ctrl ' (single quote) to copy '" .. arrInd1['Surname'] .. "'"
		iup.Append(vboxCitationNames, hboxCitationName2)
	end

	local function SetlblIncNameState()
		local strState
		if tglIncNameInCitation.value == 'ON' then
			strState = 'Yes'
		elseif tglIncNameInCitation.value == 'OFF' then
			strState = 'No'
		else
			strState = 'Only if adding citation to name'
		end
		lblIncNameState.title = strState
	end

	SetlblIncNameState()

	local frameNames = iup.frame{
		iup.hbox{
			iup.frame {
				iup.vbox{
					iup.hbox{tglCitationToNames, iup.space{size = '12x0'}, tglIncNameInCitation, lblIncNameState;
						gap = 4, margin = '0x0'},
					iup.hbox{
						iup.vbox{
							iup.flatlabel{title = lblRow},
							iup.label{title = '(Ctrl \' to copy\nexisting name) ', fontstyle = 'Italic'};
							gap = 0, margin = '0x0'
							},
						iup.fill{}, vboxCitationNames;			--vboxGiven, vboxSurname;
						gap = 4, alignment = 'ACENTER', margin = '0x0'
						};
					--tglCitationToNames;
					gap = 4, alignment = 'ALEFT', margin = '4x2',
					padding = '0x0'									-- Set for inheritance
					},
				sunken = 'YES'
				};
			padding = '5x2', fontstyle = ''
			},
		title = titleFrame, fontstyle = 'Bold'
		}

	----------------------- Citation text preview panel ------------------------
	local lblWhereWithin = iup.flatlabel{
		title = ' ', size = '350x18', fgcolor = gtblConstants.Blue, padding = '2x0',
		textwrap = 'YES', alignment = 'ALEFT:ATOP', border = 'YES'
		}
	local vboxWhereWithin = iup.vbox{
		iup.label{title = 'Where Within', padding = '2x0'}, lblWhereWithin;
		gap = '0', margin = '0x0'
		}

	local lblTextFromSource = iup.flatlabel{
		title = ' ', size = '350x18', fgcolor = gtblConstants.Blue, padding = '2x0',
		textwrap = 'YES', alignment = 'ALEFT:ATOP', border = 'YES'
		}
	local vboxTextFromSource = iup.vbox{
		iup.label{title = 'Text from Source', padding = '2x0'}, lblTextFromSource;
		gap = '0', margin = '0x0'
		}

	local listCitationAssess = iup.list{
		dropdown = 'YES', size = '95x0', visibleitems = 6}
	PopulateList(listCitationAssess, gtblConstants.CitationAssess)
	listCitationAssess.value = gtblSettings.CitationAssess
	local hboxCitationAssess = iup.hbox{
		iup.label{title ='Citation Assessment: ', padding = '2x0'}, listCitationAssess;
		margin = '0x0', alignment = 'ACENTER'}

	local framePreview = iup.frame{
		iup.hbox{
			iup.frame{
				iup.vbox{
					vboxWhereWithin, vboxTextFromSource,
					hboxCitationAssess;
					gap = 6, margin = '4x2'
					},
				sunken = 'YES'
				},
			padding = '5x2', fontstyle = ''
			},
		title = 'Citation', fontstyle = 'Bold'
		}

	------------------------------ Footer buttons ------------------------------
	local btnContinue = iup.button{
		title = 'Continue', padding = '10x0'
		}
	local btnCancel = iup.button{
		title = 'Cancel', padding = '10x3',
		action = function(self) return iup.CLOSE end
		}

	local hboxFooter = iup.hbox{
		iup.fill{}, btnContinue, btnCancel, iup.fill{};
		normalizesize = 'BOTH', margin = '0x5', gap = 40
		};

	---------------------- Assemble and display the dialog ---------------------

	local vboxMain = iup.vbox{
			vboxFormHead,
			frameRegistration,
			frameNames,
			frameEvent,
			framePreview,
			hboxFooter;
			gap = 5, margin = '10x5', alighment = 'ACENTER'
			};

	local dialogMain = iup.dialog{
		vboxMain;
		resize = 'No', minbox = 'No', maxbox = 'No',
		title = 'GRO Index ' .. Event .. ' Citation',
		defaultesc = btnCancel,
		taskbarbutton = 'SHOW',
		tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1'
		}
	iup.SetAttribute(dialogMain, 'NATIVEPARENT', fhGetContextInfo('CI_PARENT_HWND'))
	--------------------------------------------------------------------------------------------------------------------
	function FormatName(Given, Surname)				-- 1 = Surname, Given		3 = Given Surname
		if gtblSettings.NameFormat%2 == 0 then		-- 2 = SURNAME, Given		4 = Given SURNAME
			Surname = Surname:upper()
		end
		if not Given then
			return Surname
		end
		if gtblSettings.NameFormat < 3 then
			return Surname .. ', ' .. Given
		else
			return Given .. ' ' .. Surname
		end
	end
	--------------------------------------------------------------------------------------------------------------------
	function WhereWithin()
		local WhereWithin = ''
		if Event == 'Birth' then
			WhereWithin = gtblSettings.LabelPrefixBirth
		elseif Event == 'Marriage' then
			WhereWithin = gtblSettings.LabelPrefixMarriage
		else
			WhereWithin = gtblSettings.LabelPrefixDeath
		end

		WhereWithin = WhereWithin .. listYear.value
		local intSel = tonumber(listQtrMonth.value)
		if intSel ~= 0 then
			if gtblSettings.QtrMonthNames then
				WhereWithin = WhereWithin .. ' ' .. gtblConstants.PeriodsLong[intSel]
			else
				WhereWithin = WhereWithin .. ' ' .. gtblConstants.Periods[intSel]
			end
		end
		if listDistrictName.value ~= '' then
			local strDistrict
			if not gtblSettings.UseGRODistricts then
				strDistrict = listDistrictName.value
			else
				if listDistrictName.value ~= '0' then
					strDistrict = listDistrictName.valuestring:match('^(.*)%s%[') or listDistrictName.valuestring
				else
					strDistrict = ''
				end
			end
			if gtblSettings.ShortPlaces then
				local strValue = strDistrict
				strValue = strValue:match('^([^,]+)')
				if strValue ~= null then
					WhereWithin = WhereWithin .. ' ' .. LabelData(gtblSettings.LabelDistrictName, strValue)
				end
			else
				WhereWithin = WhereWithin .. ' ' .. LabelData(gtblSettings.LabelDistrictName, strDistrict)
			end
		end
		if txtVolume.value ~= '' then
			WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelVolume, txtVolume.value))
		end
		if txtPage.value ~= '' then
			WhereWithin = WhereWithinRefs(WhereWithin , LabelData(gtblSettings.LabelPage, txtPage.value))
		end
		if txtDistNo.value ~= '' then
			WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelDistrictNo, txtDistNo.value))
		end
		if txtRegister.value ~= '' then
			WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelRegister, txtRegister.value))
		end
		if txtEntry.value ~= '' then
			WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelEntry, txtEntry.value))
		end

		WhereWithin = WhereWithin:gsub('%^',' ')
		return WhereWithin
	end				-- function WhereWithin

	function WhereWithinRefs(strWW, strLabelledData)
		if strLabelledData:find('^[-/]') then
			return strWW .. strLabelledData		-- No space before label beginning with - or /
		else
			return strWW .. ' ' .. strLabelledData
		end
	end

	function WhereWithinUpdate()
		lblWhereWithin.title = WhereWithin():gsub('&', '&&')
	end
	--------------------------------------------------------------------------------------------------------------------
	function TextFromSrc()

		local TextFromSrc = ''

		if tglIncNameInCitation.value == 'ON' or tglCitationToNames.value == 'ON' then
			TextFromSrc = TextFromSrc .. ' ' .. FormatName(txtGiven.value, txtSurname.value)
			if Event == 'Marriage' then
				if arrInd2 ~= nil then
					TextFromSrc = TextFromSrc .. ', ' .. FormatName(txtGiven2.value, txtSurname2.value)
				else
					TextFromSrc = TextFromSrc .. ', '
				end
			end
		end
		if Event == 'Birth' and txtMother.value ~= '' then
			TextFromSrc = TextFromSrc .. ' ' .. LabelData(gtblSettings.LabelMother, FormatName(nil, txtMother.value))
		end
		if Event == 'Death' then
			if txtAge.value ~= '' then
				TextFromSrc = TextFromSrc .. ' ' .. LabelData(gtblSettings.LabelAge, txtAge.value)
			end
			if txtDOB.value ~= '' and txtDOB.bgcolor ~= gtblConstants.Red then	-- Don't add if not valid
				TextFromSrc = TextFromSrc .. ' ' .. LabelData(gtblSettings.LabelDOB, txtDOB.value)
			end
		end

		TextFromSrc = TextFromSrc:sub(2)			-- drop leading space

		TextFromSrc = TextFromSrc:gsub('%^',' ')
		return TextFromSrc

	end				-- function TextFromSrc

	function TextFromSrcUpdate()
		lblTextFromSource.title = TextFromSrc():gsub('&','&&')
	end
	--------------------------------------------------------------------------------------------------------------------
	function LabelData(Label, Data) 		-- Combine label and data
		if Label:find('%$') then
			return Label:gsub('%$', Data)
		else
			return Label .. Data
		end
	end
	--------------------------------------------------------------------------------------------------------------------
	function listYear:valuechanged_cb()
		listYear.bgcolor = gtblConstants.White
		WhereWithinUpdate()
	end

	function listYear:killfocus_cb()
		if listYear.bgcolor == gtblConstants.Red then
			return
		end
		if listYear.value ~= '' then
			if listYear.value:find("%D")
					or tonumber(listYear.value) < 1837
					or tonumber(listYear.value) > gtblConstants.CurrYear then
				OkOnlyMessage(strTitle, 'Invalid Year (must be between 1837 and ' .. gtblConstants.CurrYear .. ')',
							 'ERROR', dialogMain)
				listYear.bgcolor = gtblConstants.Red
			end
			if gtblSettings.UseGRODistricts and not GRODistrictYear() then
				OkOnlyMessage(strTitle, 'GRO District was not active in ' .. listYear.value, 'WARNING', dialogMain)
			end
		end
	end

	function listYear:k_any()
		listYear.bgcolor = gtblConstants.White
	end

	function listQtrMonth:valuechanged_cb()
		WhereWithinUpdate()
	end

	function listQtrMonth:k_any(v)
		if iup.isprint(v) then
			local k = string.char(v)
			if k >= '1' and k <= '4' then
				listQtrMonth.value = tonumber(k)
			end
		end
	end

	function listDistrictName:valuechanged_cb()
		WhereWithinUpdate()
	end

	function listDistrictName:killfocus_cb()
		if gtblSettings.UseGRODistricts and not GRODistrictYear() then
			OkOnlyMessage(strTitle, 'GRO District was not active in ' .. listYear.value, 'WARNING', dialogMain)
		end
	end

	function listDistrictName:k_any(k)
		if gtblSettings.UseGRODistricts then
			if iup.isprint(k) then
				-- List will be on first entry with that starting letter
				strDistrictNameFilter = strDistrictNameFilter .. string.char(k):upper()
				if #strDistrictNameFilter > 1 then
					local i = tonumber(listDistrictName.value)			-- Must be forward from current line
					local InList = false
					while i <= tonumber(listDistrictName.count) do
						if listDistrictName[tostring(i)]:upper():find('^' .. strDistrictNameFilter) then
							iup.Flush()
							listDistrictName.value = i
							WhereWithinUpdate()
							InList = true
							break
						elseif listDistrictName[tostring(i)]:upper() > strDistrictNameFilter then
							break
						end
						i = i + 1
					end
					if not InList then
						strDistrictNameFilter = string.char(k):upper()		-- List will be on first entry with that starting letter
					end
				else
					iup.Flush()
					if listDistrictName.value == '0' then
						strDistrictNameFilter = ''		-- No districts matching initial letter keyed
					end
				end
			end
			if k == iup.K_BS then
				iup.Flush()
				if #strDistrictNameFilter <= 1 then
					listDistrictName.value = 0
					WhereWithinUpdate()
					strDistrictNameFilter = ''
				else
					strDistrictNameFilter = strDistrictNameFilter:sub(1, -2)
					local i = 1
					while i <= tonumber(listDistrictName.count) do
						if listDistrictName[tostring(i)]:upper():find('^' .. strDistrictNameFilter) then
							listDistrictName.value = i
							InList = true
							break
						elseif listDistrictName[tostring(i)]:upper() > strDistrictNameFilter then
							break
						end
						i = i + 1
					end
					if not InList then
						listDistrictName.value = 0
					end
					WhereWithinUpdate()
				end
			end
		end
	end

	function btnGRODistricts:action()
		SelectedGRODistrict = GRODistricts(dialogMain)
		PopulateListFromtblGROPlaces(listDistrictName)			-- Refresh in case change made
		listDistrictName.value = SelectedGRODistrict
	end

	function RefToUpper(txtField)
		if gtblSettings.UCRefs then
			if txtField.value ~= txtField.value:upper() then
				local pos = txtField.caret
				txtField.value = txtField.value:upper()
				txtField.caret = pos
			end
		end
	end

	function txtVolume:valuechanged_cb()
		RefToUpper(txtVolume)
		WhereWithinUpdate()
	end

	function txtVolume:k_any(c)
		if c == iup.XkeyCtrl(iup.K_TAB) then
			iup.SetFocus(txtGiven)
		end
	end

	function txtPage:valuechanged_cb()
		RefToUpper(txtPage)
		WhereWithinUpdate()
	end

	function txtPage:k_any(c)
		if c == iup.XkeyCtrl(iup.K_TAB) then
			iup.SetFocus(txtGiven)
		end
	end

	function txtDistNo:valuechanged_cb()
		RefToUpper(txtDistNo)
		WhereWithinUpdate()
	end

	function txtDistNo:k_any(c)
		if c == iup.XkeyCtrl(iup.K_TAB) then
			iup.SetFocus(txtGiven)
		end
	end

	function txtRegister:valuechanged_cb()
		RefToUpper(txtRegister)
		WhereWithinUpdate()
	end

	function txtRegister:k_any(c)
		if c == iup.XkeyCtrl(iup.K_TAB) then
			iup.SetFocus(txtGiven)
		end
	end

	function txtEntry:valuechanged_cb()
		WhereWithinUpdate()
	end

	function txtEntry:k_any(c)
		if c == iup.XkeyCtrl(iup.K_TAB) then
			iup.SetFocus(txtGiven)
		end
	end

	function txtMother:k_any(c)
		if c == iup.XkeyCtrl(iup.K_apostrophe) then
			txtMother.value = arrInd1['Mother']
			txtMother.bgcolor = gtblConstants.White
            txtMother.CaretPos = #arrInd1['Mother']
			TextFromSrcUpdate()
		end
	end

	function txtMother:valuechanged_cb()
		txtMother.bgcolor = gtblConstants.White
		TextFromSrcUpdate()
	end

	function txtAge:valuechanged_cb()
		TextFromSrcUpdate()
	end

	function GRODistrictYear()			-- If GRO has years active check if year is in range
		if listYear.value ~= '' and listDistrictName.value ~= '0' then
			local strYears = tblGROPlaces[tonumber(listDistrictName.value)][2]
			if strYears == '' then
				return true				-- No years specified - anything goes
			end
			local P = '(%d*%-?%d*)'						-- Pattern for matching yyyy-yyyy, second only or both can be blank
			local tblFromTo = {strYears:match(P .. ',?' .. P .. ',?' .. P .. ',?' .. P)}
			for i,v in pairs(tblFromTo) do
				From, To = v:match('(%d*)%-(%d*)')
				if From == nil or tonumber(listYear.value) < tonumber(From) then
					return false
				end
				if To == '' then
					return true			-- Open ended range
				end
				if tonumber(listYear.value) <= tonumber(To) then
					return true
				end
			end
			return false
		end
		return true
	end

	function ValidateDOB()
		if txtDOB.bgcolor ~= gtblConstants.White then
			return
		end
		if txtDOB.value == '' then
			txtDOB.bgcolor = gtblConstants.White
			TextFromSrcUpdate()
			return
		end
		local DateValid, Formatted, dtDate = ValidateDate(txtDOB.value, true)	-- true to allow year or month/year only

		if not DateValid then
			txtDOB.bgcolor = gtblConstants.Red
			OkOnlyMessage(strTitle, 'Invalid Date of Birth', 'ERROR', dialogMain)
			return
		end
		local DOBYear = dtDate:GetDatePt1():GetYear()
		if DOBYear < arrInd1.EarliestBirth or DOBYear > arrInd1.LatestBirth then
			txtDOB.bgcolor = gtblConstants.PaleYellow
			OkOnlyMessage(strTitle, 'Date outside of expected range', 'WARNING', dialogMain)
		end
		txtDOB.value = Formatted
		TextFromSrcUpdate()
	end

	function txtDOB:k_any(c)
		if c == iup.K_CR then
			ValidateDOB()
			txtDOB.caretpos = #txtDOB.value
			return IUP_CONTINUE
		end
		if c ~= iup.K_TAB then
			txtDOB.bgcolor = gtblConstants.White
			return IUP_CONTINUE
		else
			if txtDOB.bgcolor == gtblConstants.White then
				ValidateDOB()
				if txtDOB.bgcolor == gtblConstants.Red then
					iup.SetFocus(txtDOB)
					return
				end
			end
			return IUP_CONTINUE
		end
	end
	function txtDOB:killfocus_cb()
		if txtDOB.bgcolor ~= gtblConstants.White then
			return
		end
		ValidateDOB()
	end
	function txtDOB:getfocus_cb()
		txtDOB.caretpos = #txtDOB.value
	end

	function tglIncNameInCitation:action()
		if tglIncNameInCitation.value == 'OFF' and tglCitationToNames.value == 'ON' then
			local strMsg = 'Include Name(s) in Citation Text must be set to\n'
							.. '"Always" or "Only if adding citation to name"\n'
							.. 'if Add Citation to Name option is on'
			OkOnlyMessage(strTitle, strMsg, 'ERROR', dialogMain)
			tglIncNameInCitation.value = 'ON'
		end
		SetlblIncNameState()
		TextFromSrcUpdate()
	end

	function tglCitationToNames:action()
		if tglCitationToNames.value == 'ON' and tglIncNameInCitation.value == 'OFF' then
			local strMsg = 'Requires Include Name(s) in Citation Text\nnot to be set to "No"'
			OkOnlyMessage(strTitle, strMsg, 'ERROR', dialogMain)
			tglCitationToNames.value = 'OFF'
		end
		TextFromSrcUpdate()
	end

	function txtGiven:k_any(c)
		if c == iup.XkeyCtrl(iup.K_apostrophe) then
			txtGiven.value = arrInd1['GivenName']
			txtGiven.bgcolor = gtblConstants.White
			txtGiven.CaretPos = #arrInd1['GivenName']
			TextFromSrcUpdate()
		end
	end

	function txtGiven:valuechanged_cb()
		txtGiven.bgcolor = gtblConstants.White
		TextFromSrcUpdate()
	end

	function txtSurname:k_any(c)
		if c == iup.XkeyCtrl(iup.K_apostrophe) then
			txtSurname.value = SurnameToCopy()
			txtSurname.bgcolor = gtblConstants.White
            txtSurname.CaretPos = #txtSurname.value
			TextFromSrcUpdate()
		end
	end

	function txtSurname:valuechanged_cb()
		txtSurname.bgcolor = gtblConstants.White
		TextFromSrcUpdate()
	end

	function txtGiven2:k_any(c)
		if c == iup.XkeyCtrl(iup.K_apostrophe) then
			txtGiven2.value = arrInd2['GivenName']
			txtGiven2.bgcolor = gtblConstants.White
			txtGiven2.CaretPos = #arrInd2['GivenName']
			TextFromSrcUpdate()
		end
	end

	function txtGiven2:valuechanged_cb()
		if arrInd2 ~= nil then
			txtGiven2.bgcolor = gtblConstants.White
			TextFromSrcUpdate()
		end
	end

	function txtSurname2:k_any(c)
		if c == iup.XkeyCtrl(iup.K_apostrophe) then
			txtSurname2.value = arrInd2['Surname']
			txtSurname2.bgcolor = gtblConstants.White
			txtSurname2.CaretPos = #arrInd2['Surname']
			TextFromSrcUpdate()
		end
	end
	function txtSurname2:valuechanged_cb()
		if arrInd2 ~= nil then
			txtSurname2.bgcolor = gtblConstants.White
			TextFromSrcUpdate()
		end
	end
	----------------------------------- Validate data and if OK create citations etc. ----------------------------------
	function btnContinue:action()
	-- Validate input
		if listYear.value == '' or listYear.bgcolor == gtblConstants.Red then
			OkOnlyMessage(strTitle, 'Year not selected', 'WARNING', dialogMain)
			return
		end
		if listQtrMonth.value == '0' then
			OkOnlyMessage(strTitle, 'Quarter/Month not selected', 'WARNING', dialogMain)
			return
		end
		if listDistrictName.value == '' or listDistrictName.value == '0' then
			OkOnlyMessage(strTitle, 'District not entered/selected', 'WARNING', dialogMain)
			return
		end
		if gtblSettings.UseGRODistricts and not GRODistrictYear() then
			OkOnlyMessage(strTitle, 'GRO District was not active in ' .. listYear.value, 'ERROR', dialogMain)
			return
		end
		-- Count completed reference fields - there should be at least two
		local intCount = 0
		if txtVolume.value ~= '' then
			intCount = intCount + 1
		end
		if txtPage.value ~= '' then
			intCount = intCount + 1
		end
		if txtDistNo.value ~= '' then
			intCount = intCount + 1
		end
		if txtRegister.value ~= '' then
			intCount = intCount + 1
		end
		if txtEntry.value ~= '' then
			intCount = intCount + 1
		end
		if intCount < 2 then
			OkOnlyMessage(strTitle, 'GRO Reference details incomplete', 'WARNING', dialogMain)
			return
		end

		-- Birth: if mothers name is to be given citation it must be entered
		if Event == 'Birth' then
			if tglCitationToMother.value == 'ON' and txtMother.value == '' then
				txtMother.bgcolor = gtblConstants.Yellow
				OkOnlyMessage(strTitle, 'Mother\'s name required if being given citation', 'WARNING', dialogMain)
				return
			end
		end

		-- Death requires age or date of birth but not both
		if Event == 'Death' then
			if txtAge.value == '' then
				if txtDOB.value == '' then
					local strMessage = 'Neither Age nor Date of Birth entered\n\nContinue?\n\n'
										.. '(Tip: Age or Date of Birth can usually be found at '
										.. 'https://www.gro.gov.uk/gro/content/certificates/login.asp)'
					if MessageBox(gPluginName, strMessage, 'OKCANCEL', 'WARNING', 2, dialogMain) == 2 then
						return
					end
				elseif txtDOB.bgcolor == gtblConstants.Red then
					OkOnlyMessage(strTitle, 'Date of Birth invalid', 'WARNING', dialogMain)
					return
				end
			elseif txtDOB.value ~= '' then
				OkOnlyMessage(strTitle, 'Enter either Age or Date of Birth (not both)', 'WARNING', dialogMain)
				return
			end
		end

		if tglIncNameInCitation.value == 'ON' or tglCitationToNames.value == 'ON' then
			local BlankName = false
			if txtGiven.value == '' or txtSurname.value == '' then
				BlankName = true
			elseif arrInd2 ~= nil and txtGiven2.value == '' or txtSurname2.value =='' then
				BlankName = true
			end
			if BlankName then			-- At least one missing - flag all empty
				if txtGiven.value == '' then
					txtGiven.bgcolor = gtblConstants.Yellow
				end
				if txtSurname.value == '' then
					txtSurname.bgcolor = gtblConstants.Yellow
				end
				if  arrInd2 ~= nil then
					if txtGiven2.value == '' then
						txtGiven2.bgcolor = gtblConstants.Yellow
					end
					if txtSurname2.value == '' then
						txtSurname2.bgcolor = gtblConstants.Yellow
					end
				end
				OkOnlyMessage(strTitle, 'Names required for inclusion in citation', 'WARNING', dialogMain)
				return
			end
		end

		-- Determine actions to be taken
		local strEvent = ''
		local ptrRecord = fhNewItemPtr()
		if Event == 'Birth' then
			strEvent = 'BIRT'
			ptrRecord = arrInd1.Ptr
		elseif Event == 'Marriage' then
			strEvent = 'MARR'
			ptrRecord = arrFam.Ptr
		else
			strEvent = 'DEAT'
			ptrRecord = arrInd1.Ptr
		end

		local EventDate = gtblConstants.Periods[tonumber(listQtrMonth.value)] .. ' ' .. listYear.value
		local dteEventDate = fhNewDate()
		dteEventDate:SetValueAsText(EventDate)

		arrActions.ptrRecord = ptrRecord
		arrActions.strEvent = strEvent
		arrActions.dteEventDate = dteEventDate

		if not gtblSettings.UseGRODistricts then
			arrActions.txtEventPlace = listDistrictName.value
		else
			arrActions.txtEventPlace = listDistrictName.valuestring:match('^(.*)%s%[') or listDistrictName.valuestring
		end
		local bContinue
		bContinue, arrActions.Event, arrActions.EventDate, arrActions.EventPlace
				= CompareEvent(ptrRecord, Event, strEvent, EventDate, arrActions.txtEventPlace, dialogMain)
		if not bContinue then
			return				-- User cancelled
		end

		function CompareName(ExistingName, EnteredGiven, EnteredSurname)
			-- Compares names and offers to update. Does not offer if entered is shorter because it has initials
			-- that match instead of given names
			local ExistingGiven, ExistingSurname, EnteredFullName
			if ExistingName:sub(-1) == '/' then
				ExistingGiven = ExistingName:match('^[^/]*')
				ExistingSurname = ExistingName:match('%b//'):gsub('/','')
				EnteredFullName = EnteredGiven .. ' /' .. EnteredSurname .. '/'
			elseif ExistingName:sub(1, 1) == '/' then
				ExistingGiven = ExistingName:match('[^/]*$')
				ExistingSurname = ExistingName:match('%b//'):gsub('/','')
				EnteredFullName = '/' .. EnteredSurname .. '/ ' .. EnteredGiven
			else				-- Indeterminate name structure, can't do update
				return true, false			-- Continue, don't update
			end
			ExistingGiven = ExistingGiven:match('^%s*(.-)%s*$')			-- Remove leading/trailing spaces

			if ExistingName == EnteredFullName then
				return true, false			-- Continue, don't update
			end
			local bOfferUpdate = false
			if EnteredSurname == ExistingSurname then
				local ExistingGivenNames = {}
				local EnteredGivenNames = {}
				for str in ExistingGiven:gmatch("([^ ]+)") do
					table.insert(ExistingGivenNames, str)
				end
				for str in EnteredGiven:gmatch("([^ ]+)") do
					table.insert(EnteredGivenNames, str)
				end
				if #ExistingGivenNames > #EnteredGivenNames then		-- fewer given names entered so don't offer to update
					return true, false			-- Continue, don't update
				else
					local i = 0
					while i < #ExistingGivenNames do
						i = i + 1
						if ExistingGivenNames[i] ~= EnteredGivenNames[i]
								and ExistingGivenNames[i]:sub(1,1) ~= EnteredGivenNames[i] then
							bOfferUpdate = true
							break
						end
					end
					if bOfferUpdate ~= true and #ExistingGivenNames < #EnteredGivenNames then	-- More names/initials entered
						bOfferUpdate = true
					end
				end
			else
				bOfferUpdate = true				-- Surname doesn't match
			end

			if bOfferUpdate then
				local strMsg = 'Name entered does not match current name\n\n'
								.. 'Current: ' .. ExistingName .. '\n'
								.. 'Entered: ' .. EnteredFullName .. '\n\n'
								.. 'Update name?'
				local Ans = MessageBox(strTitle, strMsg, 'YESNOCANCEL', 'QUESTION', 3, dialogMain)
				if Ans == 3 then
					return false				-- Cancelled, don't continue
				elseif Ans == 1 then
					return true, true, EnteredFullName		-- Continue, update, new name
				else
					return true, false			-- Continue, don't update
				end
			end
			return true, false			-- Continue, don't update
		end
		-- Adding citation to names - check for multiple names and if so select name number
		local Ind1Name, Ind2Name
		if tglCitationToNames.value == 'ON' then
			if Event ~= 'Marriage' then
				bContinue, arrActions.Ind1NameNo, Ind1Name = MultNameCheck(ptrRecord, Event, 'Individual', dialogMain)
				if not bContinue then
					return				-- User cancelled
				end
				arrActions.ptrInd1 = ptrRecord
				-- Check if Birth name is different to stored
				if Event == 'Birth' then
					bContinue, arrActions.UpdateName, arrActions.NewName = CompareName(Ind1Name, txtGiven.value, txtSurname.value)
					if not bContinue then
						return				-- User cancelled
					end
				end
			else
				bContinue, arrActions.Ind1NameNo, Ind1Name = MultNameCheck(arrInd1.Ptr, Event, 'Husband', dialogMain)
				if not bContinue then
					return				-- User cancelled
				end
				arrActions.ptrInd1 = arrInd1.Ptr
				if arrInd2 ~= nil then
					bContinue, arrActions.Ind2NameNo, Ind2Name = MultNameCheck(arrInd2.Ptr, Event, 'Wife', dialogMain)
					if not bContinue then
						return				-- User cancelled
					end
					arrActions.ptrInd2 = arrInd2.Ptr
				else
					arrActions.Ind2NameNo = 0				-- Family only has one parent (odd but possible!)
				end
			end
		else
			arrActions.Ind1NameNo = 0
			arrActions.Ind2NameNo = 0
		end

		-- Adding birth citation to mother's name - check for mother's record
		if Event == 'Birth' and tglCitationToMother.value == 'ON' then
			arrActions.CitationMother = true
			if arrInd1.ptrMother:IsNull() then
				arrActions.MotherName = gtblSettings.NewMotherGivenName .. ' /'.. txtMother.value .. '/'
				local strMessage
				if arrInd1.ptrFamily:IsNull() then
					arrActions.Mother = 'F'
					strMessage = 'Create new family and add Mother?'
				else
					arrActions.Mother = 'I'
					strMessage = 'Add Mother to family ' .. fhGetItemText(arrInd1.ptrFamily, '~'):sub(7)
				end
				strMessage = strMessage .. '?\n\nName: ' .. arrActions.MotherName
				if MessageBox(gPluginName, strMessage, 'OKCANCEL', 'QUESTION', 1, dialogMain) == 2 then
					return
				end
				arrActions.MotherNameNo = 1
			elseif fhGetItemPtr(arrInd1.ptrMother, '~.NAME'):IsNull() then
				arrActions.Mother = 'A'
				arrActions.MotherName = gtblSettings.NewMotherGivenName .. ' /'.. txtMother.value .. '/'
				arrActions.MotherNameNo = 1
			else
				local strCurrMother
				bContinue, arrActions.MotherNameNo, strCurrMother = MultNameCheck(arrInd1.ptrMother, Event, 'Mother', dialogMain)
				if not bContinue then
					return				-- User cancelled
				end
				local MotherSurname = strCurrMother:match('/(.*)/')
				if txtMother.value:upper() ~= MotherSurname:upper() then
					local strNewMother = Replace(strCurrMother, '/' .. MotherSurname .. '/', '/' .. txtMother.value .. '/')	-- Replace family name
					local strMessage = 'Mother\'s maiden name entered does not match existing name\n\n'
									.. 'Existing name: ' .. strCurrMother .. '\n'
									..	'Maiden name entered: ' .. txtMother.value .. '\n\n'
									.. 'YES Change Mother\'s name to ' .. strNewMother .. '\n'
									.. 'NO Add citation without changing Mother\'s name'
					local response = MessageBox(gPluginName, strMessage, 'YESNOCANCEL', 'QUESTION', 1, dialogMain)
					if response == 1 then
						arrActions.Mother = 'U'
						arrActions.MotherName = strNewMother
					elseif response == 2 then
						arrActions.Mother = 'X'
						arrActions.MotherName = strCurrMother
					else
						return
					end
				else
					arrActions.Mother = 'X'
					arrActions.MotherName = strCurrMother
				end
			end
		end

		-- Death - decide if anything needs doing with age or birth date
		if Event == 'Death' then
			bContinue, arrActions.DeathAge, arrActions.ageDeathAge = DeathAge(ptrRecord, txtAge.value, dialogMain)
			if not bContinue then
				return				-- User cancelled
			end
			bContinue, arrActions.DeathDOB, arrActions.dteDOB = DeathDOB(ptrRecord, txtAge.value, txtDOB.value, dteEventDate, dialogMain)
			if not bContinue then
				return				-- User cancelled
			end
		end

		-- Have all the information - display what will be done & get confirmation to proceed
		local strHeader = 'Citation for ' .. Event .. ' of ' .. arrInd1['DispNameFull']
		if Event == 'Marriage' then
			strHeader = strHeader .. '\nand '

			if arrInd2 ~= nil then
				strHeader = strHeader .. arrInd2['DispNameFull']
			else
				strHeader = strHeader .. '<>'
			end
		end
		strHeader = strHeader .. '\n\nConfirmation of changes'
		local lblHeading = iup.label{title = strHeader, fontstyle = 'Bold', alignment = 'ACENTER'}

		-- Data changes
		local strDataChanges
		local bDataChanges = false
		if Event ~= 'Marriage' then
			strDataChanges = arrInd1.DispName
		else
			strDataChanges = 'Family ' .. arrFam.DispName:sub(4)
		end

		local NewLine = '\n' .. string.rep(' ', 4)
		if arrActions.UpdateName then
			local strNo = ''
			if arrActions.Ind1NameNo ~= 1 then
				strNo = '[' .. arrActions.Ind1NameNo .. ']'
			end
			strDataChanges = strDataChanges .. NewLine .. 'Change Name' .. strNo .. ' to: ' .. arrActions.NewName
			bDataChanges = true
		end
		if arrActions.Event then
			strDataChanges = strDataChanges .. NewLine .. 'Create ' .. Event .. ' Event'
			bDataChanges = true
		end
		if arrActions.EventDate == 'C' then
			strDataChanges = strDataChanges .. NewLine .. Event .. ' date: ' .. EventDate
			bDataChanges = true
		elseif arrActions.EventDate == 'U' then
			strDataChanges = strDataChanges .. NewLine .. 'Change ' .. Event .. ' date to: ' .. EventDate
			bDataChanges = true
		end
		if arrActions.EventPlace ~= 'X' then
			if arrActions.EventPlace == 'C' then
				strDataChanges = strDataChanges .. NewLine .. Event .. ' place: ' .. arrActions.txtEventPlace
				bDataChanges = true
			elseif arrActions.EventPlace == 'U' then
				strDataChanges = strDataChanges .. NewLine .. 'Change ' .. Event .. ' place to: ' .. arrActions.txtEventPlace
				bDataChanges = true
			end
			strDataChanges = strDataChanges .. NewLine .. Event .. ' address: Registration District'
			bDataChanges = true
		end

		if Event == 'Death' then
			if arrActions.DeathAge == 'A' then
				strDataChanges = strDataChanges .. NewLine .. 'Death Age: ' .. txtAge.value
				bDataChanges = true
			elseif arrActions.DeathAge == 'U' then
				strDataChanges = strDataChanges .. NewLine .. 'Change Death Age to: ' .. txtAge.value
				bDataChanges = true
			end

			if arrActions.DeathDOB == 'A' then
				strDataChanges = strDataChanges .. NewLine .. 'Birth Date: '
									.. arrActions.dteDOB:GetDisplayText('COMPACT')
				bDataChanges = true
			elseif arrActions.DeathDOB == 'U' then
				strDataChanges = strDataChanges .. NewLine .. 'Change Birth Date to: '
									.. arrActions.dteDOB:GetDisplayText('COMPACT')
				bDataChanges = true
			end

			-- Check for Living flag to be removed
			if fhGetItemText(ptrRecord, '~._FLGS.__LIVING') == 'Y' then
				arrActions.UnsetLiving = true
				strDataChanges = strDataChanges .. NewLine .. 'Unset Living flag'
				bDataChanges = true
			else
				arrActions.UnsetLiving = false
			end
		end

		if not bDataChanges then
			strDataChanges = strDataChanges .. NewLine .. 'No changes'
		end

		if Event == 'Birth' and arrActions.CitationMother and arrActions.Mother ~= 'X' then
			if arrActions.Mother == 'A' then
				strDataChanges = strDataChanges .. '\n[unnamed person] (Mother)'
									.. NewLine .. 'Add Name: ' .. arrActions.MotherName
				bDataChanges = true
			elseif arrActions.Mother == 'U' then
				strDataChanges = strDataChanges .. '\n' .. fhGetItemText(ptrRecord, '~.~MOTH[1]>NAME[' .. arrActions.MotherNameNo .. ']:STORED') .. ' (Mother)'
									.. NewLine .. 'Change Name to: ' .. arrActions.MotherName
				bDataChanges = true
			else		-- Mother to be added
				strDataChanges = strDataChanges .. '\nNew Individual (Mother)'
									.. NewLine .. 'Name: ' .. arrActions.MotherName
									.. NewLine .. 'Sex: Female'
									.. NewLine .. 'Family'
				if arrActions.Mother == 'F' then
					strDataChanges = strDataChanges .. ' (new): '
				else					-- arrActions.Mother == 'I'
					local ptrFather = fhGetItemPtr(ptrRecord,'~.FAMC>HUSB>')
					if not ptrFather:IsNull() then
						strDataChanges = strDataChanges .. ': Spouse ' .. fhGetItemText(ptrRecord, 'INDI.~FATH[1]>NAME[1]:STORED')
					else
						strDataChanges = strDataChanges .. ': '
					end
				end
			end
		end

		local boxWidth = tostring(tonumber(dialogMain.Size:match('(%d+)')) - 43)	-- Make this dialog the same width as dialogMain
		local frameActions = iup.frame{
			iup.hbox{
				iup.frame{
					iup.hbox{
						iup.label{title = strDataChanges};
						padding = '0x0', margin = '10x5', size = boxWidth .. 'x0'
						},
					bgcolor = gtblConstants.White, sunken = 'YES'
					},
				margin = '4x2', fontstyle = ''
				},
			title = 'Data Updates', fontstyle = 'Bold'
			}

		-- Citation details
		local dteCitationDate = fhNewDate()
		if gtblSettings.CitationEntryDate > 1 then
			arrActions.CitationDate = true
			if gtblSettings.CitationEntryDate == 2 then
				dteCitationDate:SetSimpleDate(fhCallBuiltInFunction("today"))
			else
				dteCitationDate:SetValueAsText(EventDate)
			end
			arrActions.dteCitationDate = dteCitationDate
		else
			arrActions.CitationDate = false
		end

		arrActions.txtCitationAssess = gtblConstants.CitationAssess[tonumber(listCitationAssess.value)]
		if listCitationAssess.value ~= '1' then
			arrActions.CitationAssess = true
		else
			arrActions.CitationAssess = false
		end

		if txtCitationNote.value ~= '' then
			arrActions.CitationNote = true
			arrActions.txtCitationNote = txtCitationNote.value
		else
			arrActions.CitationNote = false
		end

		local strTmp = WhereWithin()
		arrActions.WhereWithin = strTmp
		local lblWhereWithin = iup.flatlabel{					-- This may be resized after dialog mapped if multi-line
			title = strTmp:gsub('&', '&&'),
			textwrap = 'YES', expand = 'VERTICAL'
			}

		strTmp = TextFromSrc()
		arrActions.TextFromSrc = strTmp
		local lblTextFromSrc = iup.flatlabel{					-- This may be resized after dialog mapped if multi-line
			title = strTmp:gsub('&','&&'),
			textwrap = 'YES', expand = 'VERTICAL'
			}

		local frameCitation = iup.frame{
			iup.hbox{
				iup.frame{
					iup.gridbox{
						iup.label{title = 'Source:'},
						iup.label{title = gtblData.name .. '[' .. gtblData.id .. ']'},
						iup.label{title = 'Where within:'},
						lblWhereWithin,
						iup.label{title = 'Date:'},
						iup.label{title = dteCitationDate:GetDisplayText('COMPACT')},
						iup.label{title = 'Assessment:'},
						iup.label{title = arrActions.txtCitationAssess},
						iup.label{title = 'Note:'},
						iup.label{title = arrActions.txtCitationNote},
						iup.label{title = 'Text from Source:'},
						lblTextFromSrc;
						orientation = 'horizontal', numdiv = 2, sizelin = -1, sizecol = -1, cgapcol = 4,
						size = boxWidth .. 'x0'
						},
					bgcolor = gtblConstants.White, sunken = 'YES'
					},
				margin = '4x2', fontstyle = ''
				},
			title = 'Citation', fontstyle = 'Bold'
			}

		-- Facts to be given Citations
		local strFacts
		if Event ~= 'Marriage' then
			strFacts = Event:upper() .. ' : ' .. arrInd1.DispName
		else
			strFacts = 'MARRIAGE : ' .. arrFam.DispName:sub(7)
		end

		if tglCitationToNames.value == 'ON' then
			if arrActions.Ind1NameNo == 1 then
				strFacts = strFacts .. '\nNAME : ' .. Ind1Name
			else
				strFacts = strFacts .. '\nNAME[' .. arrActions.Ind1NameNo .. '] : ' .. Ind1Name
			end
			if Event == 'Marriage' then
				if arrActions.Ind2NameNo == 1 then
					strFacts = strFacts .. '\nNAME : ' .. Ind2Name
				elseif arrActions.Ind2NameNo > 1 then
					strFacts = strFacts .. '\nNAME[' .. arrActions.Ind2NameNo .. '] : ' .. Ind2Name
				end
			end
		end

		if Event == 'Birth' and arrActions.CitationMother then
			strFacts = strFacts .. '\nNAME'
			if arrActions.MotherNameNo ~= 1 then
				strFacts = strFacts .. '[' .. arrActions.MotherNameNo .. ']'
			end
			strFacts = strFacts .. ' : ' .. arrActions.MotherName .. ' (Mother)'
		end

		if Event == 'Death' and arrActions.DeathDOB ~= 'X' then
			strFacts = strFacts .. '\nBIRTH : ' .. arrInd1.DispName
		end

		local frameFacts = iup.frame{
			iup.hbox{
				iup.frame{
					iup.hbox{
						iup.label{title = strFacts};
						padding = '0x0', margin = '10x5', size = boxWidth .. 'x0'
						},
					bgcolor = gtblConstants.White, sunken = 'YES'
					},
				margin = '4x2', fontstyle = ''
				},
			title = 'Facts to be given citations', fontstyle = 'Bold'
			}

		-- Footer Buttons
		local bContinue

		local btnSave = iup.button{
			title = 'Save', image = 'IUP_FileSave', padding = '10x0',
			action = function(self)
				bContinue = true
				return iup.CLOSE
				end
			}
		local btnCancel = iup.button{
			title = 'Cancel', padding = '10x3',
			action = function(self)
				bContinue = false
				return iup.CLOSE
				end
			}
		local hboxFooter = iup.hbox{
			btnSave, btnCancel;
			normalizesize = 'BOTH', margin = '0x5', gap = 40
			}

		local dialogConfirm = iup.dialog{
			iup.vbox{
				lblHeading, frameActions, frameCitation, frameFacts, hboxFooter;
				margin = '15x5', gap = 5, alignment = 'ACENTER'
				};
			resize = 'No', minbox = 'No', maxbox = 'No',
			title = 'GRO Index ' .. Event .. ' Citation',
			defaultesc = btnCancel,
			PARENTDIALOG = dialogMain
			}

		-- Resize citation labels to fit if they need multiple lines
		dialogConfirm:map()
		local MaxLabelWidth = boxWidth - 87
		local bRefresh = false
		local Xwidth = dialogConfirm.Size

		local size = iup.GetAttribute(lblWhereWithin, 'SIZE')
		local isize = tonumber(size:match('^%d+'))
		if isize > MaxLabelWidth then
			if isize < MaxLabelWidth * 2 then
				lblWhereWithin.size = tostring(MaxLabelWidth) .. 'x16'		-- two lines
			else
				lblWhereWithin.size = tostring(MaxLabelWidth) .. 'x24'		-- three lines
			end
			bRefresh = true
		end

		size = iup.GetAttribute(lblTextFromSrc, 'SIZE')
		isize = tonumber(size:match('^%d+'))
		if isize > MaxLabelWidth then
			if isize < MaxLabelWidth * 2 then
				lblTextFromSrc.size = tostring(MaxLabelWidth) .. 'x16'		-- two lines
			else
				lblTextFromSrc.size = tostring(MaxLabelWidth) .. 'x24'		-- three lines
			end
			bRefresh = true
		end

		if bRefresh then
			iup.SetAttribute(dialogConfirm, 'SIZE', '')
			iup.Refresh(dialogConfirm)
		end

		local dialogMainPosX, dialogMainPosY = dialogMain.ScreenPosition:match('([^,]+),([^,]+)')
		dialogMainPosX = tonumber(dialogMainPosX)
		dialogMainPosY = tonumber(dialogMainPosY) + 32				-- Down relative to dialogMain by default caption height
		dialogConfirm:popup(dialogMainPosX, dialogMainPosY)
		dialogConfirm:destroy()
		if not bContinue then
			Exit = false
			return
		end

		-- User has confirmed to go ahead and make the changes
		Exit = true
 		return iup.CLOSE
	end				-- 	function btnContinue:action
------------------------------------------------------------------------------------------------------------------------

	dialogMain:map()
	WhereWithinUpdate()
	TextFromSrcUpdate()

	local ScreenPosX = gtblSettings.ScreenPosX
	local ScreenPosY = gtblSettings.ScreenPosY
	local bFirstMove = true

	function dialogMain:move_cb(x, y)
		if bFirstMove then
			bFirstMove = false				-- Ignore call back that occurs during popup of dialog
		else
			ScreenPosX = x
			ScreenPosY = y
		end
	end

	function dialogMain:show_cb(state)		-- Make sure dialog is not off the top of the screen
		if state == 0 then
			local x, y = dialogMain.screenposition:match('([^,]*),([^,]*)')
			if tonumber(y) < 0 then
				ScreenPosX = x
				ScreenPosY = 0
				dialogMain:showxy(ScreenPosX, ScreenPosY)
			end
		end
	end

	dialogMain:popup(ScreenPosX, ScreenPosY)
	dialogMain:destroy()

	if ScreenPosX ~= gtblSettings.ScreenPosX or	ScreenPosY ~= gtblSettings.ScreenPosY then
		gtblSettings.ScreenPosX = ScreenPosX
		gtblSettings.ScreenPosY = ScreenPosY
		SaveSettings()
	end

	if Exit then
		DoActions(arrActions)
	end

	return Exit

end			-- function MainDialog
--======================================================================================================================

-- Populate an iuplist with values from an array
function PopulateList(iuplist, tblVals)
	local is_indexed = (rawget( tblVals, 1 ) ~= nil)
	if not is_indexed then
	    local i=1
	    for k, _ in pairs(tblVals) do
	        iuplist[tostring(i)]=k
	        i=i+1
	    end
	else
		for i, v in ipairs(tblVals) do
			iuplist[tostring(i)]=v
		end
	end
end

------------------------------------------------------------------------------------------------------------------------

function ToggleValue(bValue)	-- Toggle on/off from setting true/false
	if bValue then
		return'ON'
	else
		return 'OFF'
	end
end			-- function SetToggleValue
--======================================================================================================================

-- Check for existing event and if there compare data; user decides what to update if existing data
function CompareEvent(ptrRecord, Event, strEvent, EventDate, District, dialogMain)
	-- Returns:	bContinue - false if user cancelled
	--			Event - true if event to be created, false if already exists
	--			Date - "C"reate, "U"pdate, "X" do nothing
	--			Place (and address) - "C"reate, "U"pdate, "X" do nothing
	local ptrEvent = fhNewItemPtr()
	local ptrField = fhNewItemPtr()
	ptrEvent:MoveTo(ptrRecord, '~.' .. strEvent)
	if ptrEvent:IsNull() then
		return true, true, 'C', 'C'
	end

	ptrField:MoveTo(ptrEvent,'~.DATE')
	local ExistingDate = fhGetValueAsDate(ptrField):GetDisplayText('COMPACT')
	local ExistingPlace = fhGetItemText(ptrEvent, '~.PLAC')
	local ExistingAddress = fhGetItemText(ptrEvent, '~.ADDR')

	if ExistingDate == '' and ExistingPlace == '' then
		return true, false, 'C', 'C'
	end

	-- Compare data with existing event data
	if EventDate == ExistingDate and District == ExistingPlace then
		return true, false, 'X', 'X'
	end
	-- Get user to choose whether to update event
	local hboxExistingDate = iup.hbox{iup.label{title = 'Date:', size = '50x0'}, iup.label{title = ExistingDate}}
	local hboxExistingPlace = iup.hbox{
		iup.label{title = 'Place:', size = '50x0'}, iup.label{title = ExistingPlace:gsub('&', '&&')}}
	local hboxExistingAddress = iup.hbox{
		iup.label{title = 'Address:', size = '50x0'}, iup.label{title = ExistingAddress:gsub('&', '&&')}}
	local frameExisting = iup.frame{
		iup.hbox{
			iup.frame{
				iup.hbox{
					iup.vbox{hboxExistingDate, hboxExistingPlace,hboxExistingAddress; gap = 0, margin = '2x2' },
					iup.fill{};
					padding = '0x0', gap = 15, margin = '10x5'
					},
				bgcolor = gtblConstants.White, sunken = 'YES'
				},
			margin = '4x2', fontstyle = ''
			},
		title = 'Existing ' .. Event .. ' Details', fontstyle = 'Bold'
		}
	local hboxEventDate = iup.hbox{iup.label{title = 'Date:', size = '50x0'}, iup.label{title = EventDate}}
	local hboxEventPlace = iup.hbox{iup.label{title = 'Place:', size = '50x0'}, iup.label{title = District:gsub('&', '&&')}}
	local hboxEventAddress = iup.hbox{iup.label{title = 'Address:', size = '50x0'}, iup.label{title = 'Registration District' }}
	local frameEvent = iup.frame{
		iup.hbox{
			iup.frame{
				iup.hbox{
					iup.vbox{hboxEventDate, hboxEventPlace,hboxEventAddress; gap = 0, margin = '2x2' },
					iup.fill{};
					padding = '0x0', gap = 15, margin = '10x5'
					},
				bgcolor = gtblConstants.White, sunken = 'YES'
				},
			margin = '4x2', fontstyle = ''
			},
		title = 'Suggested by this Citation', fontstyle = 'Bold'
		}

	local tglPlaceOnly = iup.toggle{name = 'Place', title = 'Replace just ' .. Event .. ' place* with suggested place'}
	local tglDateOnly = iup.toggle{name = 'Date', title = 'Replace just ' .. Event .. ' date with suggested date'}
	local tglBoth = iup.toggle{name = 'Both', title = 'Replace ' .. Event .. ' date and place* with suggested date and place'}
	local tglNeither = iup.toggle{name = 'Neither', title = 'Don\'t change '.. Event .. ' event'}
	local radioAction = iup.radio{
		iup.vbox{
			tglPlaceOnly, tglDateOnly, tglBoth, tglNeither,
			iup.label{title = '* Address will be changed when replacing Place'};
			gap = 1, margin = '10x5',
			}
		}
	if EventDate == ExistingDate then
		tglDateOnly.active = 'NO'
		tglBoth.active = 'NO'
	elseif District == ExistingPlace then
		tglPlaceOnly.active = 'NO'
		tglBoth.active = 'NO'
	end

	radioAction.value = tglNeither

	local bContinue

	local btnContinue = iup.button{
		title = 'Continue', padding = '10x0',
		action = function(self)
			bContinue = true
			return iup.CLOSE
			end
		}
	local btnCancel = iup.button{
		title = 'Cancel', padding = '10x3',
		action = function(self)
			bContinue = false
			return iup.CLOSE
			end
		}

	local hboxFooter = iup.hbox{
		iup.fill{}, btnContinue, btnCancel, iup.fill{};
		normalizesize = 'BOTH', margin = '0x5', gap = 40
		}

	local dialogEventData = iup.dialog{
		iup.vbox{frameExisting, frameEvent, radioAction, hboxFooter; margin = '15x5', gap = 5};
		resize = 'No', minbox = 'No', maxbox = 'No',
		title = 'GRO Index ' .. Event .. ' Citation',
		defaultesc = btnCancel,
		PARENTDIALOG = dialogMain
		}

	dialogEventData:popup(iup.CENTERPARENT, iup.CENTERPARENT)

	local DateAction = 'X'
	local PlaceAction = 'X'
	if bContinue then
		if tglDateOnly.value == 'ON' or tglBoth.value == 'ON' then
			if ExistingDate == '' then
				DateAction = 'C'
			else
				DateAction = 'U'
			end
		end
		if tglPlaceOnly.value == 'ON' or tglBoth.value == 'ON' then
			if ExistingPlace == '' then
				PlaceAction = 'C'
			else
				PlaceAction = 'U'
			end
		end
	end

	dialogEventData:destroy()
	return bContinue, false, DateAction, PlaceAction
	end				-- function CompareEvent
--======================================================================================================================
-- Death: Decide if age in the death event needs adding/updating
function DeathAge(ptrRecord, Age, dialogMain)
		-- Returns:	bContinue - false if user cancelled
	--				Action - "A" Add DOB to birth event, "U"pdate, "X" do nothing
	--				Death age object
	if Age == '' then
		return true, 'X', nil
	end
	local ageEntered = fhNewAge()
	ageEntered:SetValue('=', tonumber(Age))

	local ageExisting
	local ptrEvent = fhNewItemPtr()
	local ptrField = fhNewItemPtr()
	ptrEvent:MoveTo(ptrRecord, '~.DEAT')
	if ptrEvent:IsNull() then
		return true, 'A', ageEntered
	else
		ptrField:MoveTo(ptrEvent, '~.AGE')
		ageExisting = fhGetValueAsAge(ptrField)
	end

	if ageExisting:IsNull() then
		return true, 'A', ageEntered
	end
	if ageEntered:GetYears() == ageExisting:GetYears() then
		return true, 'X', ''
	end
	local strMessage = 'Age does not match existing death age\n\n'
						.. 'Existing: ' .. ageExisting:GetDisplayText() .. '\n'
						.. 'Entered: ' .. ageEntered:GetDisplayText() .. '\n\n'
						.. 'Overwrite death Age?'
	local Response = MessageBox('GRO Index Death Citation', strMessage, 'YESNOCANCEL', 'QUESTION', 3, dialogMain)
	if Response == 3 then
		return false, ''
	end
	if Response == 1 then
		return true, 'U', ageEntered
	else
		return true, 'X', nil
	end
end
--======================================================================================================================
-- Death: decide whether to change individuals date of birth
function DeathDOB(ptrRecord, Age, DOB, dteEventDate, dialogMain)
	-- Returns:	bContinue - false if user cancelled
	--			Action - "A" Add DOB to birth event, "U"pdate, "C"itation only, "X" do nothing
	--			Date object containing date of birth
	if Age == '' and DOB == '' then
		return true, 'X', fhNewDatePt()							-- Neither entered so nothing to do
	end
	local ptrEvent = fhNewItemPtr()
	local ptrField = fhNewItemPtr()
	local dteExistingDOB
	ptrEvent:MoveTo(ptrRecord, '~.BIRT')
	if ptrEvent:IsNull() then
		dteExistingDOB = fhNewDate()
	else
		ptrField:MoveTo(ptrEvent, '~.DATE')
		dteExistingDOB = fhGetValueAsDate(ptrField)
	end

	local dteNewDOB = fhNewDate()
	if Age ~= '' then
		dteNewDOB = fhu.calcBirth(dteEventDate, Age)
	else
		dteNewDOB:SetValueAsText(DOB)
	end

	local hboxExisting = iup.hbox{iup.label{title = 'Existing Date:', size = '120x0'}, iup.label{title = dteExistingDOB:GetDisplayText('COMPACT')}}
	local hboxSuggested = iup.hbox{iup.label{title = 'Date from GRO Death:', size = '120x0'}, iup.label{title = dteNewDOB:GetDisplayText('COMPACT')}}
	local frameDOB = iup.frame{
		iup.hbox{
			iup.frame{
				iup.hbox{
					iup.vbox{hboxExisting, hboxSuggested; gap = 0, margin = '2x2' },
					iup.fill{};
					padding = '0x0', gap = 15, margin = '10x5'
					},
				bgcolor = gtblConstants.White, sunken = 'YES'
				},
			margin = '4x2', fontstyle = ''
			},
		title = 'Birth Date', fontstyle = 'Bold'
		}
	local AddUpdate
	if dteExistingDOB:IsNull() then
		AddUpdate = 'Add'
	else
		AddUpdate = 'Update'
	end
	local tglAddUpdate = iup.toggle{title = AddUpdate .. ' Birth Date'}
	local tglCitation = iup.toggle{title = 'Only add Citation to Birth Date'}
	local tglNothing = iup.toggle{title = 'Don\'t ' .. AddUpdate .. ' Birth Date or add citation'}
	local radioAction = iup.radio{
		iup.vbox{
			tglAddUpdate, tglCitation, tglNothing;
			gap = 1, margin = '10x5'
			}
		}
	if not dteExistingDOB:IsNull() then
		if dteNewDOB:GetValueAsText() == dteExistingDOB:GetValueAsText() then
			tglAddUpdate.active = 'NO'
			tglNothing .title = 'Don\'t add citation'
			radioAction.value = tglCitation
		else
			radioAction.value = tglNothing
		end
	else
		tglCitation.active = 'NO'			-- Disable citation only if no existing date
	end

	local bContinue

	local btnContinue = iup.button{
		title = 'Continue', padding = '10x0',
		action = function(self)
			bContinue = true
			return iup.CLOSE
			end
		}
	local btnCancel = iup.button{
		title = 'Cancel', padding = '10x3',
		action = function(self)
			bContinue = false
			return iup.CLOSE
			end
		}

	local hboxFooter = iup.hbox{
		iup.fill{}, btnContinue, btnCancel, iup.fill{};
		normalizesize = 'BOTH', margin = '0x5', gap = 40
		}

	local dialogDeathDOB = iup.dialog{
		iup.vbox{frameDOB, radioAction, hboxFooter; margin = '15x5', gap = 5};
		resize = 'No', minbox = 'No', maxbox = 'No',
		title = 'GRO Index Death Citation',
		defaultesc = btnCancel,
		PARENTDIALOG = dialogMain
		}

	dialogDeathDOB:popup(iup.CENTERPARENT, iup.CENTERPARENT)
	local Action = 'X'
	if bContinue then
		if tglAddUpdate.value == 'ON' then
			if dteExistingDOB:IsNull() then
				Action = 'A'
			else
				Action = 'U'
			end
		elseif tglCitation.value == 'ON' then
			Action = 'C'
		end
	end

	dialogDeathDOB:destroy()
	return bContinue, Action, dteNewDOB

end				-- function DeathDOB
--======================================================================================================================
-- Citations being added to name(s) - check for multiple and if so pick which one
function MultNameCheck(ptrRecord, Event, Role, dialogMain)

	local ptrField = fhNewItemPtr()
	ptrField:MoveTo(ptrRecord, '~.NAME')
	if ptrField:IsNull() then
		OkOnlyMessage(gPluginName, Role .. ' has no name', 'ERROR', dialogMain)
		return false, 0
	end
	local arrNames = {}
	while ptrField:IsNotNull() do
		table.insert(arrNames, fhGetItemText(ptrField, '~:STORED'))
		ptrField:MoveNext('SAME_TAG')
	end
	if #arrNames == 1 then
		return true, 1, arrNames[1]
	end

	lblHeading = iup.label{title= Role .. ' has Multiple names\n\nSelect name to be given the citation', expand = 'HORIZONTAL'}
	listNames = iup.list{visiblecolumns = 25, visiblelines = 3}
	PopulateList(listNames, arrNames)

	local frameNames = iup.frame{
		iup.hbox{
			iup.frame{
				iup.hbox{
					iup.vbox{lblHeading, listNames; gap = 2, margin = '2x2' },
					iup.fill{};
					padding = '0x0', gap = 15, margin = '10x5'
					},
				bgcolor = gtblConstants.White, sunken = 'YES'
				},
			margin = '4x2', fontstyle = ''
			},
		title = 'Multiple Names', fontstyle = 'Bold'
		}

	local bContinue

	local btnContinue = iup.button{
		title = 'Continue', padding = '10x0'
		}

	function btnContinue:action()
		if listNames.value == "0" then
			OkOnlyMessage(gPluginName, "Name not selected", 'WARNING', dialogMain)
			return
		end
		bContinue = true
		return iup.CLOSE
	end

	local btnCancel = iup.button{
		title = 'Cancel', padding = '10x3',
		action = function(self)
			bContinue = false
			return iup.CLOSE
			end
		}

	local hboxFooter = iup.hbox{
		iup.fill{}, btnContinue, btnCancel, iup.fill{};
		normalizesize = 'BOTH', margin = '0x5', gap = 40
		}

	local dialogMultName = iup.dialog{
		iup.vbox{frameNames, hboxFooter; margin = '15x5', gap = 5};
		resize = 'No', minbox = 'No', maxbox = 'No',
		title = 'GRO Index ' .. Event .. ' Citation',
		defaultesc = btnCancel,
		PARENTDIALOG = dialogMain
		}

	dialogMultName:popup(iup.CENTERPARENT, iup.CENTERPARENT)

	local SelName = listNames.value
	local SelNameDisp = arrNames[tonumber(SelName)]

	dialogMultName:destroy()
	return bContinue, tonumber(SelName), SelNameDisp

end				-- function MultNameCheck
--======================================================================================================================
function DoActions(arrActions)

--[[All the actions to be taken and data needed is in arrActions
	  arrActions.ptrRecord			ptr		Individual or family record to have event added
				.strEvent			str		Event item (BIRT, MARR or DEAT)
				.Event				boo		true=Create event, false= event exists
				.EventDate			str		'C'reate, 'U'pdate, 'X' do nothing
				.dteEventDate		dte		Event date object
				.EventPlace			str		'C'reate, 'U'pdate, 'X' do nothing
				.txtEventPlace		str		Full place of district
				.DeathAge			str		'C'reate, 'U'pdate, 'X' do nothing
				.ageDeathAge		age		Age at death
				.DeathDOB			str		'A'dd DOB to birth event, 'U'pdate DOB, 'C'itation only, 'X' do nothing
				.dteDOB				dte		Date of birth date object
				.UpdateName			boo		Individiduals name to be updated (birth Only)
				.NewName			str		New name for update
				.Ind1NameNo			str		Individual 1 Number of name for citation (0 if no citation to be added)
				.ptrInd1			ptr		Individual 1 record for name citation (marriage only)
				.Ind2NameNo			str		Individual 2 Number of name for citation (0 if no citation to be added)
				.ptrInd2			ptr		Individual 2 record for name citation (marriage only)
				.CitationMother		boo		True = Add citation to mother's name
				.MotherNameNo		str		Mother - Number of name for citation and, if required, updating
				.Mother				str		'U'pdate name, 'F'amily and individual to be created,
											'I' Create individual and add to existing family as mother,
											'A'dd name to existing unnamed person, 'X' do nothing
				.MotherName			str		Full Name of Mother
				.CitationDate		boo		True=Add date to citation
				.dteCitationDate	dte		Citation date object
				.CitationAssess		boo		true=Add assessment to citation
				.txtCitationAssess	str		Assessment
				.UnsetLiving		boo		true=Delete living flag
				.WhereWithin		str		Citation's Where Within
				.TextFromSrc		str		Citation's Text From Source - may be blank
]]

	if DEBUG_MODE and debug then
		io.write(dumpstr(arrActions, '==Actions=='))
	end

	local ptrEvent = fhNewItemPtr()
	local ptrField = fhNewItemPtr()
	local bOK

	if arrActions.UpdateName then
		ptrField:MoveTo(arrActions.ptrRecord, '~.NAME[' .. 	arrActions.Ind1NameNo .. ']')
		bOK = fhSetValueAsText(ptrField, arrActions.NewName)
		if not bOK then error('Updating Name') end
	end

	ptrEvent:MoveTo(arrActions.ptrRecord, '~.' .. arrActions.strEvent)
	if arrActions.Event then			-- Event to be created
		if ptrEvent:IsNotNull() then
			error('Event found when expecting to create event')
		end
		ptrEvent = fhCreateItem(arrActions.strEvent, arrActions.ptrRecord)
	else
		if ptrEvent:IsNull() then
			error('Event not found when expecting to update event')
		end
	end

	if arrActions.EventDate ~= 'X' then
		ptrField:MoveTo(ptrEvent, '~.DATE')
		if arrActions.EventDate == 'C' then
			if ptrField:IsNotNull() then
				if fhGetDisplayText(ptrField, '', 'min') ~= '' then			-- Field there - should be empty
					error('Field found when expecting to create date')
				end
			else
				ptrField = fhCreateItem("DATE",ptrEvent)
			end
		else
			if ptrField:IsNull() then
				error('Field not found when expecting to update date')
			end
		end
		bOK = fhSetValueAsDate(ptrField, arrActions.dteEventDate)
		if not bOK then error('Setting event date') end
		-- Check date doesn't give fh warnings
		local i = 0
		local strWarning
		local strMessage = ''
		repeat
			i = i + 1
			strWarning = fhCallBuiltInFunction("GetDataWarning", ptrField, i)
			if strWarning ~= '' then
				strMessage = strMessage .. '\n' .. strWarning
			end
		until strWarning == ''
		if strMessage ~= '' then
			strMessage = strMessage .. '\n\nDo you wish to undo the entry?'
			local Response = MessageBox('Event date warning', strMessage, 'YESNO', 'WARNING' , 1)
			if Response == 1 then
				error('\nPlugin aborting - select \'Yes\' to undo this entry')
			end
		end
	end

	if arrActions.EventPlace ~= 'X' then
		ptrField:MoveTo(ptrEvent, '~.PLAC')
		if arrActions.EventPlace == 'C' then
			if ptrField:IsNotNull() then
				if fhGetItemText(ptrField, '~') ~= '' then					-- Field there - should be empty
					error('Field found when expecting to create place')
				end
			else
				ptrField = fhCreateItem("PLAC",ptrEvent)
			end
		else
			if ptrField:IsNull() then
				error('Field not found when expecting to update place')
				if not bOK then error('Setting age') end
			end
		end
		bOK = fhSetValueAsText(ptrField,arrActions.txtEventPlace)
		if not bOK then error('Setting Event Place') end

		ptrField:MoveTo(ptrEvent, '~.ADDR')
		if ptrField:IsNull() then
			ptrField = fhCreateItem('ADDR', ptrEvent)
		end
		fhSetValueAsText(ptrField, 'Registration District')
	end

	if arrActions.strEvent == 'DEAT' then
		if arrActions.DeathAge ~= 'X' then
			ptrField:MoveTo(ptrEvent,"~.AGE")
			if ptrField:IsNull() then
				ptrField = fhCreateItem("AGE",ptrEvent)
			end
			bOK = fhSetValueAsAge(ptrField, arrActions.ageDeathAge)
			if not bOK then error('Setting age') end
		end

		if arrActions.DeathDOB == 'A' or arrActions.DeathDOB == 'U' then
			ptrEvent:MoveTo(arrActions.ptrRecord, '~.BIRT')
			if ptrEvent:IsNull() then
				ptrEvent = fhCreateItem('BIRT', arrActions.ptrRecord)
			end
			ptrField:MoveTo(ptrEvent,'~.DATE')
			if ptrField:IsNull() then
				ptrField = fhCreateItem('DATE',ptrEvent)
			end
			bOK = fhSetValueAsDate(ptrField, arrActions.dteDOB)
			if not bOK then error('Setting DOB') end
			-- Check date doesn't give fh warnings
			local i = 0
			local strWarning
			local strMessage = ''
			repeat
				i = i + 1
				strWarning = fhCallBuiltInFunction("GetDataWarning", ptrField, i)
				if strWarning ~= '' then
					strMessage = strMessage .. '\n' .. strWarning
				end
			until strWarning == ''
			if strMessage ~= '' then
				strMessage = strMessage .. '\n\nDo you wish to undo the entry?'
				local Response = MessageBox('Date of birth warning', strMessage, 'YESNO', 'WARNING' , 1)
				if Response == 1 then
					error('\nPlugin aborting - select \'Yes\' to undo this entry')
				end
			end
		end

		if arrActions.UnsetLiving then
			ptrField:MoveTo(arrActions.ptrRecord, '~._FLGS.__LIVING')
			bOK = fhDeleteItem(ptrField)
			if not bOK then error('Unsetting Living Flag') end
		end
	end

	local ptrMother = fhNewItemPtr()
	local ptrFamily = fhNewItemPtr()
	ptrMother = fhGetItemPtr(arrActions.ptrRecord,'~.FAMC>WIFE>')	-- May be null but will be set below if Mother is added

	if arrActions.CitationMother and arrActions.Mother ~= 'X' then		-- Data changes needed for Mother
		if arrActions.Mother == 'U' then								-- Change Mother's name
			ptrField:MoveTo(ptrMother, '~.NAME[' .. arrActions.MotherNameNo .. ']')
			bOK = fhSetValueAsText(ptrField, arrActions.MotherName)
			if not bOK then error('Updating Mother\'s name') end
		elseif arrActions.Mother == 'A' then							-- Add Mother's name to currently unnamed Mother
			ptrField = fhGetItemPtr(ptrMother, '~.NAME')				-- Name field could be present but empty
			if ptrField:IsNull() then
				ptrField = fhCreateItem('NAME', ptrMother)
			end
			bOK = fhSetValueAsText(ptrField, arrActions.MotherName)
			if not bOK then error('Adding Mother\'s name') end
		else															-- Must be 'F' or 'I'
			if arrActions.Mother == 'F' then								-- Family (and Mother) to be created
				ptrFamily = fhCreateItem('FAM')								-- Create family
				ptrField = fhCreateItem('CHIL', ptrFamily)
				bOK = fhSetValueAsLink(ptrField, arrActions.ptrRecord)					-- Link individual as child in new family
				if not bOK then error ('Creating new family and linking child') end
			else
				ptrFamily = fhGetItemPtr(arrActions.ptrRecord,'~.FAMC>')
				if ptrFamily:IsNull() then error('Expected family not found') end
			end
			-- Create mother and add to existing family or newly created family
			ptrMother = fhCreateItem('INDI')
			ptrField = fhCreateItem('NAME', ptrMother)
			bOK = fhSetValueAsText(ptrField, arrActions.MotherName)
			if not bOK then error('Setting new Mother\'s name') end
			ptrField = fhCreateItem('SEX', ptrMother)
			bOK = fhSetValueAsText(ptrField, 'F')
			if not bOK then error('Setting new Mother\'s sex') end
			ptrField =fhGetItemPtr(ptrFamily, '~.WIFE')						-- There could be an empty WIFE
			if ptrField:IsNull() then
				ptrField = fhCreateItem('WIFE', ptrFamily)
			end
			bOK = fhSetValueAsLink(ptrField, ptrMother)
			if not bOK then error('Adding new Mother to family') end
		end
	end

	--------------------------------------------------------------------------------------------------------------------
	function AddCitation(ptrRecord, fact)
		local bOK
		local ptrFact = fhNewItemPtr()
		local ptrField = fhNewItemPtr()
		local ptrData = fhNewItemPtr()
		ptrFact:MoveTo(ptrRecord, '~.' .. fact)
		if ptrFact:IsNull() then
			error('Fact ' .. fact .. ' not found on ' .. fhGetDisplayText(ptrRecord))
		end
		local ptrCitation = fhCreateItem('SOUR', ptrFact)
		bOK = fhSetValueAsLink(ptrCitation, gtblData.ptr)
		if not bOK then
			error('Setting citation link on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact)
		end

		ptrField = fhCreateItem('PAGE', ptrCitation)
		if ptrField:IsNull() then
			error('Create PAGE failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR')
		end
		bOK = fhSetValueAsText(ptrField, arrActions.WhereWithin)
		if not bOK then
			error('Setting Where Within on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.PAGE')
		end

		ptrData = fhCreateItem('DATA', ptrCitation)
		if ptrData:IsNull() then
			error('Create DATA failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR')
		end

		if arrActions.CitationDate then
			ptrField = fhCreateItem('DATE', ptrData)
			if ptrField:IsNull() then
				error('Create DATE failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA')
			end
			bOK = fhSetValueAsDate(ptrField, arrActions.dteCitationDate)
			if not bOK then
				error('Setting date on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA.DATE')
			end
		end

		if arrActions.TextFromSrc ~= '' then
			ptrField = fhCreateItem('TEXT', ptrData)
			if ptrField:IsNull() then
				error('Create TEXT failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA')
			end
			bOK = fhSetValueAsText(ptrField, arrActions.TextFromSrc)
			if not bOK then
				error('Setting Text from Source on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA.TEXT')
			end
		end

		if arrActions.CitationAssess then
			ptrField = fhCreateItem('QUAY', ptrCitation)
			if ptrField:IsNull() then
				error('Create QUAY failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR')
			end
			bOK = fhSetValueAsText(ptrField, arrActions.txtCitationAssess)
			if not bOK then
				error('Setting Assessment on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.QUAY')
			end
		end

		if arrActions.CitationNote then
			ptrField = fhCreateItem('NOTE2', ptrCitation)
			if ptrField:IsNull() then
				error('Create NOTE2 failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR')
			end
			bOK = fhSetValueAsText(ptrField, arrActions.txtCitationNote)
			if not bOK then
				error('Setting Assessment on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.NOTE2')
			end
		end
	end				--function AddCitation
	--------------------------------------------------------------------------------------------------------------------

	AddCitation(arrActions.ptrRecord, arrActions.strEvent)			-- Add citation to event
	if arrActions.Ind1NameNo ~= 0 then
		AddCitation(arrActions.ptrInd1, 'NAME[' .. arrActions.Ind1NameNo .. ']')	-- Add citation to name (or 1st name in a marriage)
	end
	if arrActions.strEvent == 'MARR' and arrActions.Ind2NameNo ~= 0 then
		AddCitation(arrActions.ptrInd2, 'NAME[' .. arrActions.Ind2NameNo .. ']')	-- Add citation to 2nd name in a marriage
	end
	if arrActions.strEvent == 'DEAT' and arrActions.DeathDOB ~= "X" then
		AddCitation(arrActions.ptrRecord, 'BIRT')					-- Add citation to birth (Death event)
	end
	if arrActions.strEvent == 'BIRT' and arrActions.CitationMother then
		AddCitation(ptrMother, 'NAME[' .. arrActions.MotherNameNo .. ']')
	end

end				-- function DoActions

--======================================================================================================================
function GRODistricts(ParentDialog)			--[[Maintain the list of GRO districts
												Called from main BMD entry screen or from settings
												]]

	local dialogGRODistricts, listPlaces, listGROPlaces, btnSaveYears		-- Forward declares
	local tblYears = {}

	function Populate_listPlaces()
		listPlaces['1'] = nil			-- Empty list
		for i,v in pairs(tblPlaces) do
		if not IntblGROPlaces(tblGROPlaces, v[1]) then
				listPlaces[tostring(i)] = v[1]
			else
				listPlaces[tostring(i)] = v[1] ..' *'
			end
		end
	end

	function IntblGROPlaces(Table, PlaceName)
		for _, v in pairs(tblGROPlaces) do
			if PlaceName == v[1] then
				return true
			end
			if PlaceName < v[1] then	-- List is alphbetic order and we've gone past where it should be
				break
			end
		end
		return false
	end

	-- Create list box with all places, existing tblGROPlaces marked * plus edit box to allow entry of totally new
	listPlaces = iup.list{dropdown = 'YES', editbox = 'YES', visibleitems = 10, expand = 'HORIZONTAL'}
	Populate_listPlaces()

	local btnUKBMD = iup.button{
		title = 'UKBMD District Information',
		image =iup.image{
			width = 12, height = 12, pixels = {
				1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,	0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
				0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,	0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0,
				0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,	0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
				0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1,	0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
				},
			colors = {"0 0 0", "255 255 255"}
			},
		imageposition = 'RIGHT', spacing = '8', padding = '8x0',
		action = function() fhShellExecute('https://www.ukbmd.org.uk/reg/districts/index.html') end
		}

	local btnAdd = iup.button{title = 'Add Place to GRO Districts', padding = '8x2'}

	function listPlaces:valuechanged_cb()
		if not IntblGROPlaces(tblGROPlaces, listPlaces.value:gsub('%s%*?$', '')) then
			if listPlaces.value:find('%*$') then
				local SavePos = listPlaces.caretpos
				listPlaces.value = listPlaces.value:sub(1,-3)
				listPlaces.caretpos = SavePos
			end
			btnAdd.visible = 'YES'
		else
			if not listPlaces.value:find('%*$') then
				local SavePos = listPlaces.caretpos
				listPlaces.value = listPlaces.value .. ' *'
				listPlaces.caretpos = SavePos
			end
			btnAdd.visible= 'NO'
		end
	end

	local frameAddDistrict = iup.frame{
		iup.vbox{
			iup.frame{
				iup.vbox{
					iup.label{title = 'Select or enter place to add to GRO Districts (* Existing GRO district)'},
					listPlaces,
					iup.hbox{btnUKBMD, iup.fill{}, btnAdd; normalizesize = 'VERTICAL', margin = '0x0'};
					gap = 2, margin = '4x4', fontstyle= ''
					},
				sunken = 'YES', margin = '4x0'
				},
			margin = '4x0'
			};
		title = 'Add GRO district', fontstyle = 'Bold'
		}

	function btnAdd:action()
		if listPlaces.value ~= '' then
			local ptrPlace = fhNewItemPtr()
			for i, v in pairs(tblPlaces) do
				if listPlaces.value == v[1] then
					ptrPlace:MoveToRecordById('_PLAC', v[2])
					break
				end
				if listPlaces.value < v[1] then		-- List is in alpha order and we've gone past where it should be
					break
				end
			end
			if ptrPlace:IsNull() then
				ptrPlace = fhCreateItem('_PLAC')
				if ptrPlace:IsNull() then
					error('Creating place record')
				end
				local ptrText = fhCreateItem('TEXT', ptrPlace, true)		-- ReuseEmpties flag set
				if not fhSetValueAsText(ptrText, listPlaces.value) then
					error ('Setting Place text')
				end
				table.insert(tblPlaces,{fhGetDisplayText(ptrPlace), fhGetRecordId(ptrPlace)})
				table.sort(tblPlaces, function(k1, k2) return k1[1] < k2[1] end)
			end
			local ptrNote = fhCreateItem('NOTE2', ptrPlace)
			if not fhSetValueAsText(ptrNote, 'GRO District') then
				error ('Adding GRO District Note')
			end
			table.insert(tblGROPlaces,{fhGetDisplayText(ptrPlace), '', fhGetRecordId(ptrPlace)})
			table.sort(tblGROPlaces, function(k1, k2) return k1[1] < k2[1] end)
			Populate_listPlaces()
			PopulateListFromtblGROPlaces(listGROPlaces)
			ClearYears()
			-- Position lists on newly added place
			listPlaces.value = fhGetDisplayText(ptrPlace) .. ' *'
			for i, v in pairs(tblGROPlaces) do
				if v[1] == fhGetDisplayText(ptrPlace) then
					listGROPlaces.value = i
					break
				end
			end
			for _, v in pairs(tblYears) do
				v.active = 'YES'
			end
			btnSaveYears.active = 'NO'
			fhUpdateDisplay()
			OkOnlyMessage(dialogGRODistricts.title, fhGetDisplayText(ptrPlace) .. '\n\nadded to GRO Districts',
							'INFORMATION', dialogGRODistricts)
			iup.SetFocus(tblYears[1])
		end
	end

	listGROPlaces = iup.list{dropdown = 'NO', visiblelines = 5, expand = 'HORIZONTAL', editbox = 'NO'}
	PopulateListFromtblGROPlaces(listGROPlaces)

	local btnRemove = iup.button{title = 'Remove from GRO Districts', padding = '8x2'}

	function btnRemove:action()
		if listGROPlaces.value == '0' then
			return
		end
		local Ans = MessageBox(dialogGRODistricts.title,
					'Remove ' ..tblGROPlaces[tonumber(listGROPlaces.value)][1] .. ' from GRO Districts?\n\n'
						.. '(The Place record will not be deleted)',
					'OKCANCEL', 'QUESTION', 2, dialogGRODistricts)
		if Ans == 1 then
			local ptrPlace = fhNewItemPtr()
			ptrPlace:MoveToRecordById('_PLAC', tblGROPlaces[tonumber(listGROPlaces.value)][3])
			local ptrNote = fhGetItemPtr(ptrPlace, '~.NOTE2')
			while ptrNote:IsNotNull() do
				local strNote = fhGetItemText(ptrNote,'~')
				if strNote:match('GRO District') then
					break
				end
				ptrNote:MoveNext('SAME_TAG')
			end
			if ptrNote:IsNull() then
				error('GRO District note found')
			end
			if not fhDeleteItem(ptrNote) then
				error('Delete GRO District note failed')
			end
			table.remove(tblGROPlaces, tonumber(listGROPlaces.value))
			Populate_listPlaces()
			PopulateListFromtblGROPlaces(listGROPlaces)
			ClearYears()
			fhUpdateDisplay()
			OkOnlyMessage(dialogGRODistricts.title, fhGetDisplayText(ptrPlace) .. ' removed from GRO Districts',
							'INFORMATION', dialogGRODistricts)
		end
	end

	function TextBoxYear(i)
		local txtYear = iup.text{
			nc = '4', filter = 'NUMERIC', alignment = 'ACENTER', visiblecolumns = 3,
			active = 'NO'
			}

		function txtYear:getfocus_cb()
			if tblYears[i].value ~= '' then
				tblYears[i].selection = 'ALL'
			end
		end

		function txtYear:action()
			tblYears[i].bgcolor = gtblConstants.White
			btnSaveYears.active = 'YES'
		end

		function txtYear:killfocus_cb()
			tblYears[i].selection = 'NONE'
			if tblYears[i].value ~= '' then
				if tonumber(tblYears[i].value) < 1837 or tonumber(tblYears[i].value) > gtblConstants.CurrYear then
					tblYears[i].bgcolor = gtblConstants.Red
					tblYears[i].selection = 'ALL'
					iup.SetFocus(tblYears[i])
					return iup.IGNORE
				else
					tblYears[i].bgcolor = gtblConstants.White
				end
			end
		end

		function txtYear:k_any(keycode)
			if keycode == iup.K_TAB then
				if tblYears[i].value == '' then
					iup.SetFocus(btnSaveYears)
					return iup.IGNORE
				end
			end
		end

		return txtYear
	end
	for i = 1, 8 do
		tblYears[i] = TextBoxYear(i)
	end
	local tblYearsFromTo = {}
	for i = 1, 4 do
		tblYearsFromTo[i] = {tblYears[(i * 2) - 1], tblYears[(i * 2)]}
	end

	function ClearYears()
		for _, v in pairs(tblYears) do
			v.value = ''
			v.bgcolor = gtblConstants.White
			v.active = 'NO'
		end
	end

	function PopulateYears(ItemNo)
		local strYears = tblGROPlaces[tonumber(ItemNo)][2]
		local FromToPattern = '(%d*%-?%d*)'
		local tblFromTo = {strYears:match(FromToPattern .. ',?' .. FromToPattern .. ',?' .. FromToPattern .. ',?' .. FromToPattern)}
		for i,v in pairs(tblFromTo) do
			From, To = v:match('(%d*)%-?(%d*)')
			tblYearsFromTo[i][1].value = From or ''
			tblYearsFromTo[i][2].value = To or ''
		end
		-- Make all years active
		for _, v in pairs(tblYears) do
			v.active = 'YES'
		end
	end

	function listGROPlaces:action(text, item, state)
		if state == 1 then
			PopulateYears(item)
		else
			ClearYears()
		end
		btnSaveYears.active = 'NO'
	end

	function hboxFromTo(i)
		return iup.hbox{
			iup.label{title = tostring(i) .. ' From'}, tblYearsFromTo[i][1],
			iup.label{title = 'To'}, tblYearsFromTo[i][2],
			cgap = '6', alignment = 'ACENTER'
			}
	end

	local gridYears = iup.gridbox{
		hboxFromTo(1), hboxFromTo(2),
		iup.fill{size = '12'},iup.fill{size = '12'},
		hboxFromTo(3), hboxFromTo(4),
		numdiv = '2', cgaplin = '2', sizecol = '-1', orientation = 'VERTICAL'}

	btnSaveYears = iup.button{title = 'Save Active Years', padding = '8x2', active = 'NO'}

	function ValidateYears()
		for i,v in pairs(tblYearsFromTo) do
			if v[1].bgcolor == gtblConstants.Red or  v[2].bgcolor == gtblConstants.Red then
				return false
			end
		end
		-- Values are all 4 digit numeric or they would be red so they can be compared as strings
		local bBlank = false
		for i, v in pairs(tblYearsFromTo) do		-- Check all pairs are valid, nothing after first blank
			if bBlank and v[1].value ~= '' then
				tblYearsFromTo[i-1][2].bgcolor = gtblConstants.Red
				return false
			end
			if v[2].value ~= '' then
				if v[1].value == '' or v[1].value > v[2].value then
					v[2].bgcolor = gtblConstants.Red
					return false
				end
			else
				bBlank = true
			end
			if v[1].value ~= '' and i > 1 and tonumber(v[1].value) <= tonumber(tblYearsFromTo[i-1][2].value) + 1 then
				v[1].bgcolor = gtblConstants.Red		-- Ranges must be in order with a gap (contiguous ranges must be merged)
				return false
			end
		end

		return true
	end

	function btnSaveYears:action()
		if listGROPlaces.value == '0' then
			return
		end

		if not ValidateYears() then
			OkOnlyMessage(dialogGRODistricts.title, 'Years District Active are invalid',
							'ERROR', dialogGRODistricts)
			iup.SetFocus(tblYears[1])
			return
		end

		local SaveGROSelection = listGROPlaces.value
		local strYears = ''
		for _, v in pairs(tblYearsFromTo) do
			if v[1].value ~= '' then
				strYears = strYears .. v[1].value .. '-' .. v[2].value .. ','
			end
		end
		strYears = strYears:gsub(',$', '')
		local ptrPlace = fhNewItemPtr()
		ptrPlace:MoveToRecordById('_PLAC', tblGROPlaces[tonumber(listGROPlaces.value)][3])
		local ptrNote = fhGetItemPtr(ptrPlace, '~.NOTE2')
		while ptrNote:IsNotNull() do
			local strNote = fhGetItemText(ptrNote,'~')
			if strNote:match('GRO District') then
				break
			end
			ptrNote:MoveNext('SAME_TAG')
		end
		if ptrNote:IsNull() then
			error('GRO District note note found')
		end
		if not fhSetValueAsText(ptrNote, 'GRO District\nYears: ' .. strYears) then
			error ('Adding GRO District Note')
		end
		tblGROPlaces[tonumber(listGROPlaces.value)][2] = strYears
		PopulateListFromtblGROPlaces(listGROPlaces)
		listGROPlaces.value = SaveGROSelection
		btnSaveYears.active = 'NO'
		OkOnlyMessage(dialogGRODistricts.title, fhGetDisplayText(ptrPlace) .. '\n\nYears Active saved',
							'INFORMATION', dialogGRODistricts)
	end

	local frameEditDistrict = iup.frame{
		iup.vbox{
			iup.frame{
				iup.vbox{
					iup.hbox{iup.label{title = 'Select GRO District'}, iup.fill{}; margin = '0x0'},
					listGROPlaces,
					iup.hbox{iup.fill{}, btnRemove; margin = '0x4'},
					iup.frame{
						iup.vbox{
							gridYears,
							iup.hbox{
								iup.fill{},
								iup.label{title = 'Leave final "To" blank \nif District still active', fontstyle = 'Italic'},
								iup.fill{},
								btnSaveYears;
								cmargin = '8x0', expand = 'YES'
								};
							margin = '6x2', expand = 'NO', cgap = '4'
							},
						title = 'Years District Active (optional)'
						};
					alignment = 'ACENTER', gap = '2'
					},
				margin = '10x2', sunken = 'YES'
				};
			margin = '4x2', fontstyle = ''
			};
		title = 'Edit GRO District', fontstyle = 'Bold'
		}

	local btnClose = iup.button{
		title = 'Close', padding = '10x3',
		action = function() return iup.CLOSE end
		}
	local hboxFooter = iup.hbox{
		iup.fill{}, btnClose, iup.fill{};
		normalizesize = 'BOTH', margin = '0x5', gap = 40
		}
	dialogGRODistricts = iup.dialog{
		iup.vbox{frameAddDistrict, frameEditDistrict, hboxFooter; margin = '15x5', gap = 5};
		resize = 'No', minbox = 'No', maxbox = 'No',
		title = 'GRO Districts',
		defaultesc = btnClose,
		PARENTDIALOG = ParentDialog
		}
	dialogGRODistricts:popup(iup.CENTERPARENT, iup.CENTERPARENT)
	local SaveGROListSelection = listGROPlaces.value
	dialogGRODistricts:destroy()
	return SaveGROListSelection
end

--======================================================================================================================
function PopulateListFromtblGROPlaces(List)		-- Populate an iuplist from GROPlaces
	List['1'] = nil					-- Empty the list
	for i,v in pairs(tblGROPlaces) do
		if v[2] ~= '' then
			List[tostring(i)] = v[1] .. ' [' .. v[2] .. ']'
		else
			List[tostring(i)] = v[1]
		end
	end
end
--======================================================================================================================

function Initialise()			-- Create global variables and fetch/set default settings
	gPluginName = fhGetContextInfo('CI_PLUGIN_NAME'):gsub(' %(.*', '')		-- Without a dev/test version in brackets

	-- Set parameter types for processing configuration file
	gtblSettingsTypeBoolean = {
		['QtrMonthNames'] = true, ['UCRefs'] = true, ['ShortPlaces'] = true, --['IncNameInCitation'] = true,
		['CitationNameBirth'] = true, ['CitationNameMarriage'] = true, ['CitationNameDeath'] = true,
		['CitationToMotherDefault'] = true, ['UseGRODistricts'] = true
		}

	GetSettings()

	if gtblSettings.VersionCheckInterval ~= 0 and gtblSettings.VersionNextCheck <= os.date('%Y-%m-%d') then
		local bOK, strMsg = VersionCheck()
		if not bOK then
			strMsg = 'Check for plugin update failed\n\n' .. strMsg
					.. '\n\nPostpone checking for a day?'
			if MessageBox (gPluginName, strMsg, 'YESNO', 'WARNING') == 1 then
				gtblSettings.VersionNextCheck = DateAdd(os.date('%Y-%m-%d'), 1)
				SaveSettings()
			end
		else
			while gtblSettings.VersionNextCheck <= os.date('%Y-%m-%d') do
				gtblSettings.VersionNextCheck = DateAdd(gtblSettings.VersionNextCheck, gtblSettings.VersionCheckInterval)
			end
			gtblSettings.VersionCheckedDate = os.date('%Y-%m-%d')
			SaveSettings()
		end
	end

	gtblConstants = {}
	gtblConstants.Periods = {'Q1';'Q2';'Q3';'Q4';'Jan';'Feb';'Mar';'Apr';'May';'Jun';'Jul';'Aug';'Sep';'Oct';'Nov';'Dec';}
	gtblConstants.PeriodsLong = {'Q1 Jan-Feb-Mar';'Q2 Apr-May-Jun';'Q3 Jul-Aug-Sep';'Q4 Oct-Nov-Dec';
								'January';'February';'March';'April';'May';'June';
								'July';'August';'September';'October';'November';'December';}
	gtblConstants.CurrYear = fhCallBuiltInFunction('today'):GetYear()
	gtblConstants.CitationAssess = {'None', 'Unreliable', 'Questionable', 'Secondary Evidence', 'Primary Evidence'}
	gtblConstants.White = '255 255 255'
	gtblConstants.Red = '255 31 31'
	gtblConstants.Blue = '0 0 255'
	gtblConstants.Yellow = '255 255 0'
	gtblConstants.PaleYellow = '255 255 127'
	gtblConstants.VersionCheckFrequency = {['0']= 0, ['1']=1, ['2']=7, ['3']=14, ['4']=28 }

	tblPlaces = {}
	tblGROPlaces = {}
	local ptrPlace = fhNewItemPtr()
	local ptrNote = fhNewItemPtr()
	ptrPlace:MoveToFirstRecord('_PLAC')
	while ptrPlace:IsNotNull() do
		table.insert(tblPlaces,{fhGetDisplayText(ptrPlace), fhGetRecordId(ptrPlace)})
		local ptrNote = fhGetItemPtr(ptrPlace, '~.NOTE2')
		while ptrNote:IsNotNull() do
			local strNote = fhGetItemText(ptrNote,'~')
			if strNote:match('GRO District') then
				table.insert(tblGROPlaces,{fhGetDisplayText(ptrPlace),
											strNote:match('Years:%s*(.*)$') or '',
											fhGetRecordId(ptrPlace)})
				break
			end
			ptrNote:MoveNext('SAME_TAG')
		end
		ptrPlace:MoveNext()
	end
	table.sort(tblPlaces, function(k1, k2) return k1[1] < k2[1] end)
	if gtblSettings.UseGRODistricts then
		table.sort(tblGROPlaces, function(k1, k2) return k1[1] < k2[1] end)
	end

	gtblData = {}		-- Data for the main dialog - source being cited, the event and the records selected
	gtblData.ptr = fhNewItemPtr()

 end -- function Initialise

------------------------------------------------------------------------------------------------------------------------

function GetSettings()			-- Read settings from file in Plugin Data - create default if it doesn't exist

	gtblSettings = {}

	local SettingsFile = fhGetContextInfo('CI_PROJECT_DATA_FOLDER') .. '\\Plugin Data\\'
						.. gPluginName .. '.ini'
	if not fhfu.fileExists(SettingsFile) then
		DefaultSettings()
		SaveSettings()
		return
	end
	local strData = fhLoadTextFile(SettingsFile)
	for strLine in strData:gmatch('[^\r\n]+') do
		local Parameter, Value = strLine:match('^(%w+)=([%g%s]*)$')
		if gtblSettingsTypeBoolean[Parameter] then
			if Value == 'N' then
				gtblSettings[Parameter] = false
			else
				gtblSettings[Parameter] = true
			end
		else
			if not Value:find('^-?%d+$') or Value == '' then		-- Not integer starting with optional - sign?
				gtblSettings[Parameter] = Value
			else
				gtblSettings[Parameter] = tonumber(Value)
			end
		end
	end
	-- Check for new settings not in current ini file
	if DefaultSettings() then
		SaveSettings()
	end
	if gtblSettings.Version ~= Version then
		-- Place to do any conversions for new version
		gtblSettings.Version = Version
		SaveSettings()
	end

end -- function GetSettings
------------------------------------------------------------------------------------------------------------------------

function SaveSettings()			-- Save settings to file in Plugin Data

	local SettingsFolder = fhGetContextInfo('CI_PROJECT_DATA_FOLDER') .. '\\Plugin Data'
	if not fhfu.folderExists(SettingsFolder) then
		fhfu.createFolder(SettingsFolder)
	end
	local SettingsFile = SettingsFolder .. '\\' .. gPluginName .. '.ini'

	-- Build table of lines to go in the settings file
	local tblLines = {}
	for Parameter, Value in pairs(gtblSettings) do
		if gtblSettingsTypeBoolean[Parameter] then
			local str = 'N'
			if Value then
				str = 'Y'
			end
			table.insert(tblLines, Parameter .. '=' .. str)
		elseif type(Value) ~= 'number' then					-- Any non-numeric characters?
			table.insert(tblLines, Parameter .. '=' .. Value)
		else		-- must be a number
			table.insert(tblLines, Parameter .. '=' .. tostring(Value))
		end
	end

	-- Prepare ini file with lines sorted alphabetically
	table.sort(tblLines)
	local str = ''
	for _, line in ipairs(tblLines) do
		str = str .. line .. '\n'
	end

	fhSaveTextFile(SettingsFile, str)

end -- function SaveSettings
------------------------------------------------------------------------------------------------------------------------

function DefaultSettings()			-- Default settings for first run or if new settings parameter(s) introduced
	local Changes = false
	-- Sources
	if gtblSettings.SourceIdBirth == nil then
		gtblSettings.SourceIdBirth = 0
		Changes = true
	end
	if gtblSettings.SourceIdMarriage == nil then
		gtblSettings.SourceIdMarriage = 0
		Changes = true
	end
	if gtblSettings.SourceIdDeath == nil then
		gtblSettings.SourceIdDeath = 0
		Changes = true
	end

	-- Citation text labels
	if gtblSettings.LabelPrefixBirth == nil then
		gtblSettings.LabelPrefixBirth = 'Birth^'
		Changes = true
	end
	if gtblSettings.LabelPrefixMarriage == nil then
		gtblSettings.LabelPrefixMarriage = 'Marriage^'
		Changes = true
	end
	if gtblSettings.LabelPrefixDeath == nil then
		gtblSettings.LabelPrefixDeath = 'Death^'
		Changes = true
	end

	-- GRO Reference Labels
	if gtblSettings.LabelDistrictName == nil then
		gtblSettings.LabelDistrictName = ''
		Changes = true
	end
	if gtblSettings.LabelVolume == nil then
		gtblSettings.LabelVolume = 'Vol:^'
		Changes = true
	end
	if gtblSettings.LabelPage == nil then
		gtblSettings.LabelPage = 'Page:^'
		Changes = true
	end
	if gtblSettings.LabelDistrictNo == nil then
		gtblSettings.LabelDistrictNo = 'District:^'
		Changes = true
	end
	if gtblSettings.LabelRegister == nil then
		gtblSettings.LabelRegister = 'Reg:^'
		Changes = true
	end
	if gtblSettings.LabelEntry == nil then
		gtblSettings.LabelEntry = 'Entry:^'
		Changes = true
	end

	-- Event Specific Labels
	if gtblSettings.LabelMother == nil then
		gtblSettings.LabelMother = 'Mother\'s Maiden Surname:^'
		Changes = true
	end
	if gtblSettings.LabelAge == nil then
		gtblSettings.LabelAge = 'Age:^'
		Changes = true
	end
	if gtblSettings.LabelDOB == nil then
		gtblSettings.LabelDOB = 'DOB:^'
		Changes = true
	end

	-- Options
	if gtblSettings.QtrMonthNames == nil then			-- Full or abrieviated month names in citation text
		gtblSettings.QtrMonthNames = false
		Changes = true
	end
	if gtblSettings.UCRefs == nil then					-- Make alpha characters in reference upper case
		gtblSettings.UCRefs = false
		Changes = true
	end
	if gtblSettings.ShortPlaces == nil then				-- District name only in citation text
		gtblSettings.ShortPlaces = true
		Changes = true
	end

	if gtblSettings.IncNameInCitation == nil then		-- Name(s) to be included in citation text
		gtblSettings.IncNameInCitation = 'ON'
		Changes = true
	end

	if gtblSettings.NameFormat == nil then				-- Format of Names
		gtblSettings.NameFormat = 3						-- Given Surname (as entered in FH)
		Changes = true
	end

	if gtblSettings.CitationNameBirth == nil then		-- Add citation to Name for Birth
		gtblSettings.CitationNameBirth = true
		Changes = true
	end
	if gtblSettings.CitationNameMarriage == nil then	-- Add citation to Name for Marriage
		gtblSettings.CitationNameMarriage = false
		Changes = true
	end
	if gtblSettings.CitationNameDeath == nil then		-- Add citation to Name for Death
		gtblSettings.CitationNameDeath = true
		Changes = true
	end
	if gtblSettings.CitationToMotherDefault == nil then	-- Add birth citation to Mother's name
		gtblSettings.CitationToMotherDefault = false
		Changes = true
	end
	if gtblSettings.NewMotherGivenName == nil then		-- Given name to use for new mother's when recording a birth
		gtblSettings.NewMotherGivenName = ''
		Changes = true
	end
	if gtblSettings.CitationEntryDate == nil then		-- Date to record in Citation Specific Date
		gtblSettings.CitationEntryDate = 1				-- "No Date"
		Changes = true
	end
	if gtblSettings.CitationAssess == nil then			-- Default citation assessment
		gtblSettings.CitationAssess = 1					-- "None"
		Changes = true
	end
	if gtblSettings.UseGRODistricts == nil then			-- Use places marked as GRO districts
		gtblSettings.UseGRODistricts = false
		Changes = true
	end

	-- Main dialog screen position
	if gtblSettings.ScreenPosX == nil then
		gtblSettings.ScreenPosX = iup.CENTERPARENT
		Changes = true
	end
	if gtblSettings.ScreenPosY == nil then
		gtblSettings.ScreenPosY = iup.CENTERPARENT
		Changes = true
	end

	-- Version Control
	if gtblSettings.Version == nil then
		gtblSettings.Version = Version
		gtblSettings.VersionCheckedDate  = '2000-01-01'
		gtblSettings.VersionNextCheck = os.date('%Y-%m-%d')		-- Force check on first run
		gtblSettings.VersionCheckInterval = 7
		Changes = true
	end

	return Changes
 end -- function DefaultSettings

--======================================================================================================================
function VersionCheck(dialogParent)
	local dialogChecking = iup.dialog{
		iup.label{title = 'Checking for update...', padding = '40x16'},
		title = gPluginName,
		dialogframe = 'Yes', menubox = 'No', parentdialog = dialogParent
		}
	dialogChecking:showxy(iup.CENTERPARENT, iup.CENTERPARENT)
	fhUpdateDisplay()
	local function httpRequest(strRequest)
		local http = luacom.CreateObject("winhttp.winhttprequest.5.1")
		http:Open("GET",strRequest,false)
		http:Send()
		return http.Responsebody
	end -- local function httpRequest
	local strRequest ='http://www.family-historian.co.uk/lnk/checkpluginversion.php?name=' .. gPluginName
	local isOK, strReturn = pcall(httpRequest,strRequest)
	local StoreVersion = ''
	dialogChecking:destroy()
	if not isOK then
		return false, 'Call to plugin store to check latest version failed'
		-- Do nothing - will check again after today
	else
		if strReturn ~= nil then
			StoreVersion = strReturn:match('([%d%.]*),') -- Version digits & dots then comma
		else
			return false, 'nil return from plugin store'
		end
	end
	local Latest
	if StoreVersion > Version then
		local strMsg = 'A later version of this plugin is available\nfrom the Family Historian Plugin Store\n\n'
						.. 'Current version: ' .. Version ..'\n'
						.. 'Store version: ' .. StoreVersion ..'\n\n'
						.. 'Go to ' .. gPluginName .. '\non the Plugin Store?'
		if MessageBox('Plugin update available', strMsg, 'YESNO', 'INFORMATION', 1, dialogParent) == 1 then
			fhShellExecute('https://pluginstore.family-historian.co.uk/page/plugin/gro-bmd-index-citations')
		end
		Latest = 'N'
	else
		Latest = 'Y'
	end

	return true, Latest
end
------------------------------------------------------------------------------------------------------------------------
function DateAdd(Date, Days)
	local tblDate =YMDtoTable(Date)
	SecondsToAdd = Days * 24 * 60 * 60
	return os.date('%Y-%m-%d', os.time(tblDate) + SecondsToAdd)
end

function YMDtoTable(Date)
	local tblDate = {}
	tblDate.year = tonumber(Date:sub(1,4))
	tblDate.month = tonumber(Date:sub(6,7))
	tblDate.day = tonumber(Date:sub(9,10))
	return tblDate
end
-- ============================================ General purpose routines -- ============================================

function Capitalise(str)		-- Make string title capitalised (1st letter each word capital, rest lower)
	str = str:gsub('(%a)([%w_]*)', UpperFirst)
	return str
end

function UpperFirst(first, rest)
	return first:upper() .. rest:lower()
end
------------------------------------------------------------------------------------------------------------------------
function Replace(strTxt,strOld,strNew,intNum)		-- replace is plain text version of gsub() - function courtesy of Mike Tate
	local strMagic = "([%^%$%(%)%%%.%[%]%*%+%-%?])"	-- UTF-8 replacement for "(%W)"
	strOld = tostring(strOld or ""):gsub(strMagic,"%%%1")	-- Hide magic pattern symbols
	return tostring(strTxt or ""):gsub(strOld,function() return strNew end,tonumber(intNum))
end -- function Replace
------------------------------------------------------------------------------------------------------------------------
function OkOnlyMessage(strTitle, strMessage, strDialogType, ParentDialog)		-- Display a message with only an OK button
	-- strDialogType: "MESSAGE" (No Icon), "ERROR" (Stop-sign), "WARNING" (Exclamation-point), "QUESTION" (Question-mark)  or "INFORMATION" (Letter "i").
	MessageBox(strTitle, strMessage, 'OK', strDialogType, 1, ParentDialog)
end		-- function OkOnlyMessage

--[[====================================================================================================================
	Replacement for iup.messagedlg
	Call:	MessageBox(strTitle, strMessage, strButtons, strDialogType, strDefault, ParentDialog)
			strButtons 	'OK', 'OKCANCEL', 'RETRYCANCEL', 'YESNO', or 'YESNOCANCEL'
			strDialogType:	'MESSAGE' (No Icon) (default)
							'ERROR' (Stop-sign)
							'WARNING' (Exclamation-point)
							'QUESTION' (Question-mark)
							'INFORMATION' (Letter 'i')
			strDefault;		Number of the default button, if omitted defaults to 1
			ParentDialog:	If called from within an iup dialog this should be the handle of the dialog. If not given
							the message box will not centre on the parent dialog
							Omit if not called from another dialog

	Returns: Number of the button pressed
			 If Esc keyed or close button (X) is keyed it returns number of the last button (same as iup.messagedlg)

			Example:
				dialogExample = iup.dialog{...}
				...
				Response = MessageBox('Demo Title', 'Hello World', 'OKCANCEL', 'QUESTION', dialogExample)
----------------------------------------------------------------------------------------------------------------------]]

function MessageBox(strTitle, strMessage, strButtons, strDialogType, strDefault, ParentDialog)

	local MaxLineLength = 80			-- Maximum characters per line used for word wrapping

	function create_image_Information()
		local Information = iup.image{
			width = 32,
			height = 32,
			pixels = {
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0,
				0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0,
				0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0,
				0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0,
				0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0,
				0,0,1,6,9,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,9,6,1,0,0,
				0,0,3,13,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,3,0,0,
				0,1,6,14,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,14,6,1,0,
				0,2,7,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,7,2,0,
				0,3,8,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,8,3,0,
				0,4,9,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,9,4,0,
				0,5,10,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,10,5,0,
				0,5,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,11,5,0,
				0,5,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,11,5,0,
				0,5,10,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,10,5,0,
				0,4,9,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,9,4,0,
				0,3,8,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,8,3,0,
				0,2,7,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,7,2,0,
				0,1,6,14,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,14,6,1,0,
				0,0,3,13,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,13,3,0,0,
				0,0,1,6,9,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,9,6,1,0,0,
				0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0,
				0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0,
				0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0,
				0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0,
				0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
				},
			colors = {
				'255 255 255',	'239 243 247',	'159 188 211',	'96 144 183',	'64 122 168',
				'0 78 140',		'16 89 147',	'0 86 154',		'0 99 178',		'0 110 196',
				'0 117 210',	'0 120 215',	'143 177 204',	'0 94 168',		'0 115 206',
				'0 83 149',		'48 111 161'
				}
			}
		return Information
	end

	function create_image_Warning()

		local Warning = iup.image{width = 32, height = 32,
			pixels = {
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,6,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,7,7,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,7,7,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,1,3,6,6,6,6,6,7,7,6,6,6,6,6,3,1,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,2,4,6,6,6,6,6,7,7,6,6,6,6,6,4,2,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,1,3,6,6,6,6,6,6,7,7,6,6,6,6,6,6,3,1,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,2,4,6,6,6,6,6,6,7,7,6,6,6,6,6,6,4,2,0,0,0,0,0,0,0,
				0,0,0,0,0,0,1,3,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,3,1,0,0,0,0,0,0,
				0,0,0,0,0,0,2,4,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,4,2,0,0,0,0,0,0,
				0,0,0,0,0,1,3,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,3,1,0,0,0,0,0,
				0,0,0,0,0,2,4,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,4,2,0,0,0,0,0,
				0,0,0,0,1,3,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,3,1,0,0,0,0,
				0,0,0,0,2,4,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,4,2,0,0,0,0,
				0,0,0,1,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,1,0,0,0,
				0,0,0,2,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,2,0,0,0,
				0,0,1,3,6,6,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,6,6,3,1,0,0,
				0,0,2,4,6,6,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,6,6,4,2,0,0,
				0,1,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,1,0,
				0,2,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,2,0,
				1,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,3,1,
				2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2
				},
			colors = {
				'255 255 255',	'255 228 159',	'255 193 32',	'255 188 0',	'254 205 0',
				'253 213 0',	'252 225 0',	'0 0 0'
				}
			}
		return Warning
	end

	function create_image_Error()
		local Error = iup.image{
			width = 32,
			height = 32,
			pixels = {
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0,
				0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0,
				0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0,
				0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0,
				0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0,
				0,0,1,6,9,11,11,11,17,18,19,11,11,11,11,11,11,11,11,11,11,19,18,17,11,11,11,9,6,1,0,0,
				0,0,3,13,11,11,11,11,18,0,20,19,11,11,11,11,11,11,11,11,19,20,0,18,11,11,11,11,13,3,0,0,
				0,1,6,14,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,14,6,1,0,
				0,2,7,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,7,2,0,
				0,3,8,11,11,11,11,11,11,11,19,20,0,20,19,11,11,19,20,0,20,19,11,11,11,11,11,11,11,8,3,0,
				0,4,9,11,11,11,11,11,11,11,11,19,20,0,20,19,19,20,0,20,19,11,11,11,11,11,11,11,11,9,4,0,
				0,5,10,11,11,11,11,11,11,11,11,11,19,20,0,20,20,0,20,19,11,11,11,11,11,11,11,11,11,10,5,0,
				0,5,11,11,11,11,11,11,11,11,11,11,11,19,20,0,0,20,19,11,11,11,11,11,11,11,11,11,11,11,5,0,
				0,5,11,11,11,11,11,11,11,11,11,11,11,19,20,0,0,20,19,11,11,11,11,11,11,11,11,11,11,11,5,0,
				0,5,10,11,11,11,11,11,11,11,11,11,19,20,0,20,20,0,20,19,11,11,11,11,11,11,11,11,11,10,5,0,
				0,4,9,11,11,11,11,11,11,11,11,19,20,0,20,19,19,20,0,20,19,11,11,11,11,11,11,11,11,9,4,0,
				0,3,8,11,11,11,11,11,11,11,19,20,0,20,19,11,11,19,20,0,20,19,11,11,11,11,11,11,11,8,3,0,
				0,2,7,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,7,2,0,
				0,1,6,14,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,14,6,1,0,
				0,0,3,13,11,11,11,11,18,0,20,19,11,11,11,11,11,11,11,11,19,20,0,18,11,11,11,11,13,3,0,0,
				0,0,1,6,9,11,11,11,17,18,19,11,11,11,11,11,11,11,11,11,11,19,18,17,11,11,11,9,6,1,0,0,
				0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0,
				0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0,
				0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0,
				0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0,
				0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
				},
			colors = {
				'255 255 255',	'249 241 240',	'221 173 166',	'198 119 107',	'187 92 78',
				'165 39 20',	'170 51 34',	'179 42 20',	'203 48 20',	'221 53 22',
				'235 57 22',	'240 58 22',	'215 159 151',	'193 47 20',	'231 55 22',
				'174 40 20',	'181 78 63',	'241 70 39',	'251 206 197',	'243 95 67',
				'254 243 241'
				}
			}
	return Error
end

	function create_image_Question()
		local Question = iup.image{
			width = 32,
			height = 32,
			pixels = {
				3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
				3,3,3,3,3,3,3,3,3,3,7,14,6,13,12,12,12,12,13,6,14,7,3,3,3,3,3,3,3,3,3,3,
				3,3,3,3,3,3,3,3,7,6,9,4,10,5,1,0,0,1,5,10,4,9,6,7,3,3,3,3,3,3,3,3,
				3,3,3,3,3,3,3,11,9,8,2,0,0,0,0,0,0,0,0,0,0,2,8,9,11,3,3,3,3,3,3,3,
				3,3,3,3,3,7,6,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,4,6,7,3,3,3,3,3,
				3,3,3,3,7,13,4,1,0,0,0,0,0,0,0,2,5,5,2,0,0,0,0,0,2,4,13,7,3,3,3,3,
				3,3,3,3,6,4,1,0,0,0,0,0,2,8,9,6,11,11,6,4,5,0,0,1,1,2,4,6,3,3,3,3,
				3,3,3,11,4,1,0,0,0,0,0,5,13,15,3,3,3,3,3,7,13,5,1,1,1,1,2,4,11,3,3,3,
				3,3,7,9,5,0,0,0,0,0,0,5,11,3,7,14,11,15,3,3,7,9,1,1,1,1,1,5,9,7,3,3,
				3,3,6,8,0,0,0,0,0,0,0,5,11,14,9,10,5,10,6,3,3,6,5,1,1,1,1,1,8,6,3,3,
				3,7,9,2,0,0,0,0,0,0,0,5,9,8,0,0,0,1,10,15,3,15,10,1,1,1,1,1,5,9,7,3,
				3,14,4,0,0,0,0,0,0,0,0,5,8,0,0,0,0,0,10,15,3,15,10,1,1,1,1,1,1,4,14,3,
				3,6,10,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,4,7,3,11,5,1,1,1,1,1,2,10,6,3,
				3,13,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,6,3,3,9,2,1,1,1,1,1,2,5,13,3,
				3,12,1,0,0,0,0,0,0,0,0,1,0,1,0,0,10,6,3,3,11,10,1,1,1,1,1,2,2,2,12,3,
				3,12,0,0,0,0,0,0,0,0,0,0,0,0,1,10,6,3,3,14,8,1,1,1,1,1,2,2,2,2,12,3,
				3,12,0,0,0,0,0,0,0,0,1,0,0,1,5,6,3,3,14,8,2,1,1,1,1,2,2,2,2,2,12,3,
				3,12,1,0,0,0,0,0,0,1,0,0,1,1,8,7,3,14,8,1,1,1,1,1,2,2,2,2,2,2,12,3,
				3,13,5,0,0,0,0,0,0,0,0,1,1,0,4,3,3,13,2,1,1,1,1,2,2,2,2,2,2,5,13,3,
				3,6,10,0,0,0,0,0,1,0,1,1,1,1,4,3,3,4,1,1,1,1,2,2,2,2,2,2,2,10,6,3,
				3,14,4,0,1,0,1,0,0,1,1,1,1,1,8,3,3,4,1,1,1,2,2,2,2,2,2,2,2,4,14,3,
				3,7,9,2,0,0,0,0,1,0,1,1,1,1,4,4,4,8,1,1,2,2,2,2,2,2,2,2,5,9,7,3,
				3,3,6,8,0,0,0,1,1,1,1,1,1,1,10,4,4,8,1,2,2,2,2,2,2,2,2,2,8,6,3,3,
				3,3,7,9,5,0,1,0,1,1,1,1,1,1,4,3,3,4,2,2,2,2,2,2,2,2,2,5,9,7,3,3,
				3,3,3,11,4,2,1,1,1,1,1,1,1,1,4,3,3,4,2,2,2,2,2,2,2,2,5,4,11,3,3,3,
				3,3,3,3,6,4,2,1,1,1,1,1,1,1,8,4,4,8,2,2,2,2,2,2,2,2,4,6,3,3,3,3,
				3,3,3,3,7,13,4,2,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,5,4,13,7,3,3,3,3,
				3,3,3,3,3,7,6,4,5,1,1,1,1,1,2,2,2,2,2,2,2,2,2,5,4,6,7,3,3,3,3,3,
				3,3,3,3,3,3,3,11,9,8,5,1,1,2,2,2,2,2,2,2,2,5,8,9,11,3,3,3,3,3,3,3,
				3,3,3,3,3,3,3,3,7,6,9,4,10,5,2,2,2,2,5,10,4,9,6,7,3,3,3,3,3,3,3,3,
				3,3,3,3,3,3,3,3,3,3,7,14,6,13,12,12,12,12,13,6,14,7,3,3,3,3,3,3,3,3,3,3,
				3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
				},
			colors = {
				'0 110 202',	'0 106 199',	'0 103 197',	'255 255 255',	'9 75 132',
				'0 97 181',		'102 138 167',	'238 242 246',	'6 83 147',		'26 82 126',
				'6 88 159',		'142 168 190',	'9 69 115',		'63 110 147',	'166 187 205',
				'199 212 223'
				}
			}
		return Question
	end

--	function textwrap takes a string of text which can incude  line breaks and returns a string with long lines split if needed
	function textwrap(strText, maxchars)
		-- Split string in to an array
		local arrLines = {}
		strText = strText .. '\n'
		strText:gsub('(.-)\n', function(strLine) arrLines[#arrLines+1] = strLine end)

		-- Split long lines
		local i = 1
		repeat
			if #arrLines[i] > maxchars + 1 then		-- Each line has \n at the end so new line after maxlen is OK
				--splitpos = maxchars
				local splitpos = arrLines[i]:sub(1,maxchars):find('[%s][^%s]*$') or maxchars
				local strRest = arrLines[i]:sub(splitpos +1):gsub('^(%s*)','')
				table.insert(arrLines, i + 1, strRest)
				arrLines[i] = arrLines[i]:sub(1,splitpos)
			end
			i = i + 1
		until i > #arrLines

		-- Make string from the array
		local str = ''
		for _, line in ipairs(arrLines) do
			str = str .. line .. '\n'
		end
		return str:sub(1,-2)
	end

	strMessage = textwrap(strMessage, MaxLineLength)

	strMessage = strMessage:gsub('&', '&&')

	strButtons = tostring(strButtons):upper()
	strDialogType = tostring(strDialogType):upper()
	if type(strDefault) == 'nil' then
		intDefault = 1
	elseif type(strDefault) == 'number' then
		intDefault = strDefault
	elseif type(strDefault) == 'string' then
		if strDefault:match("%D+") then
			error('Invalid strDefault: "' .. strDefault .. '"\n')
		else
			intDefault = tonumber(strDefault)
		end
	else
		error('Invalid strDefault: "' .. type(strDefault) .. '"\n')
	end

	local selection

	local lblMessage = iup.flatlabel{title = strMessage, padding = '4x0', textwrap = 'NO',
							expand = 'VERTICAL'}

	local hboxMessage
	if strDialogType == 'MESSAGE' then
		hboxMessage = iup.hbox{}
	else
		if strDialogType == 'ERROR' then
			hboxMessage = iup.hbox{iup.label{image = create_image_Error()}}
		elseif strDialogType == 'WARNING' then
			hboxMessage = iup.hbox{iup.label{image = create_image_Warning()}}
		elseif strDialogType == 'QUESTION' then
			hboxMessage = iup.hbox{iup.label{image = create_image_Question()}}
		elseif strDialogType == 'INFORMATION' then
			hboxMessage = iup.hbox{iup.label{image = create_image_Information()}}
		else
			error('Invalid strDialogType Setting\n')
		end
		hboxMessage:append(iup.space{size = '8x0'})
	end
	hboxMessage:append(lblMessage)
	hboxMessage:append(iup.fill{})			-- Needed to pad box if short message
	hboxMessage.padding = '0x2'
	hboxMessage.expand = 'VERTICAL'
	hboxMessage.cmargin = '16x8'

	function Button(Value)
		return iup.button{title = ' ', size = '48x0',
				action = function(self) selection = Value return iup.CLOSE end }
	end

	local Button1 = Button(1)
	local Button2 = Button(2)
	local Button3 = Button(3)

	local hboxButtons
	if strButtons == 'OK' then
		Button1.title = 'OK'
		hboxButtons = iup.hbox{Button1;}
	elseif strButtons == 'OKCANCEL' then
		Button1.title = 'OK'
		Button2.title = 'Cancel'
		hboxButtons = iup.hbox{Button1, Button2;}
	elseif strButtons == 'RETRYCANCEL' then
		Button1.title = 'Retry'
		Button2.title = 'Cancel'
		hboxButtons = iup.hbox{Button1, Button2;}
	elseif strButtons == 'YESNO' then
		Button1.title = 'Yes'
		Button2.title = 'No'
		hboxButtons = iup.hbox{Button1, Button2;}
	elseif strButtons == 'YESNOCANCEL' then
		Button1.title = 'Yes'
		Button2.title = 'No'
		Button3.title = 'Cancel'
		hboxButtons = iup.hbox{Button1, Button2, Button3;}
	else
		error('Invalid strButtons setting: "' ..strButtons .. '"\n')
	end

	local ButtonCount =  iup.GetChildCount(hboxButtons)
	if intDefault > ButtonCount then
		error ('Invalid default button: "'.. intDefault .. '\n')
	end

	hboxButtons:insert(nil, iup.fill{})
--	hboxButtons:append(iup.fill{})				-- Activate this line to makes buttons centered rather than right aligned
	hboxButtons.gap = 6
	hboxButtons.margin = '12x10'

	local dialogMessage = iup.dialog{
		iup.vbox{iup.flatframe{hboxMessage; framecolor = '255 255 255', bgcolor = '255 255 255'}, hboxButtons};
		title = strTitle, dialogframe = 'Yes'
		}
	if intDefault == 2 then
		dialogMessage.startfocus = Button2
	elseif intDefault == 3 then
		dialogMessage.startfocus = Button3
	end

	if ButtonCount == 1 then
		dialogMessage.defaultesc = Button1
	elseif ButtonCount == 2 then
		dialogMessage.defaultesc = Button2
	else
		dialogMessage.defaultesc = Button3
	end

	if ParentDialog ~= nil then
		dialogMessage.PARENTDIALOG = ParentDialog
	end
	selection = ButtonCount		-- Default if dialog closed without clicking a button


	dialogMessage:popup(IUP_CENTERPARENT, IUP_CENTERPARENT)
	dialogMessage:destroy()

	return tonumber(selection)

end				-- function MessageBox
--======================================================================================================================

--[[ =============== Debugging utilities ===============]]

function dumpstr (tt, label,indent, done, str)
	local t = {}
	if label == nil then
		label = 'Dump'
	end
	done = done or {}
	indent = indent or 0
	if type(tt) == 'table' then
		if indent == 0 then
			table.insert(t,string.rep (' ', indent))
			table.insert(t,label..'\n')
		end
		for key, value in pairs (tt) do
			table.insert(t,string.rep (' ', indent)) -- indent it
			if type (value) == 'table' and not done [value] then
				done [value] = true
				table.insert(t,string.format('[%s] => table\n', tostring (key)));
				table.insert(t,string.rep (' ', indent+4)) -- indent it
				table.insert(t,'(\n');
				table.insert(t,dumpstr (value, tostring(key),indent + 7, done, str))
				table.insert(t,string.rep (' ', indent+4)) -- indent it
				table.insert(t,')\n');
			else
				table.insert(t,string.format('[%s] => %s\n',
				tostring (key), tostring(value)))
			end
		end
	else
		table.insert(t,tostring(label) .. ':' .. tostring(tt))
	end
	if indent == 0 then
		table.insert(t,'\n')
	end
	if str == nil then
		return table.concat(t)
	else
		return table.concat(t)
	end

end			-- function dumpstr

--======================================================================================================================

debug = false

main()

debug = false

if DEBUG_MODE and debug then
	io.write(dumpstr(gtblSettings, '==Settings=='))
	io.write(dumpstr(gtblConstants,'==gtblConstants=='))
	iup.Message('Debug','End of plugin')
end

-- End of plugin
--======================================================================================================================

Source:GRO-BMD-Index-Citations-2.fh_lua