GRO Source Reference.fh_lua

--[[
@Title:			GRO Source Reference
@Type:			Standard
@Author:		Chris Read
@Contributors:	Mike Tate, Jane Taubman
@Version:		1.5
@Keywords:		GRO
@LastUpdated:	04/04/2024
@Licence:		This plugin is copyright (c) Chris Read and 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 Event and optionally create the event and or set the fields.
				This version is derived from 'Add GRO Index Source Citation' V1.0 by Mike Tate which in turn was derived from 'Add GRO Source' V1.3 by Jane Taubman.
				The primary difference is the dynamic behaviour of the UI and awarness of the changes in the GRO index information over time. Also, the addition of
			    user definable formats for the GRO Index citation. As of v1.2 this includes the ability to specify a date range the format applies to, so they are
				auto-filtered in the selection list for convenience.
@V1.0			It's my first foray in to LUA and FH Plugin development, and I have much to learn. I will say, IUP is drastically short on good examples beyond
				the booming obvious, but hey-ho, and onward we go. 40 years of software development should count for something.
				This implementation adds:
					- Enable/disable of parameter fields relevant to the selected BMD type and the citation reference type selected.
					- Selectable citation reference string formats for each register type. These use a named field style i.e. {YEAR} internally
						  for the built-in formats.
				Future enhancements:
					- Allow user creation of citation formats using the internal named field implementation, once I figure out how best to save/restore
					  them and implement a creation dialog.

@V1.1			This implementation adds:
					- Handling for Age at Death starting 1866, and changing to Date of Birth from Q2 1969
					- Changed auto-text {BIRTH} to {DOB} and separated {AGE} & {DOB} to only contain a value when applicable
					- Removed extraneous 'c' in Oct in {MONTH} field
@V1.2			This implementation adds:
					- Specification of a date period for formats so that the list of formats will be filtered to those applicable to the GRO Index Date and quarter/month.
					  I found that in practical use, as I had defined several formats for the different GRO Index information (particularly Deaths), I was constantly having
					  to locate and select different ones. In theory you only need one or maybe two per period; one for single line version (for 'Where in') and one
					  for multi-line (Text from Source/Event Note); that is, if you like to put the extra info (Mothers Maiden name, Death Age/DoB) on the next line.
					  In general a given user will probably only use one option for where the citation gets put, so only needs one format the way they like it. This means
					  the format selection essentially become automatic.
					- Added the selected register type to the saved settings, and bumped the data version to V2. For previous V1 settings, it will default to births when loaded.
@V1.3			This implementation adds:
					- Tidy ups and corrections as suggested by Visual Studio Code LUA Extension, and selected formatting using the StyLua Extension
					- Separated {VOL}/{REG} and {PAG}/{ENT} to only contain a value when applicable as previoulsuy done for the {AGE} & {DOB} fields.
@V1.4			This implementation adds:
					- Removal of the pre-defined BIRTH format Q3 1837 to Q3 1911 with no mothers maiden name, as although the official date of the information being included
					  is Q3 1911, the GRO has been retrospectivly adding the information where it is able to determine it. Date range for subsequent format has been updated to
					  cover from Q3 1837.
					- Corrected unintentional error in the removal of formats due to missunderstanding of LUA tables which resulted in a subsequent plugin crash.
					- Removed unecessary error information when the plugin aborts due to missing information or other issue when attempting to apply a citation. This makes
					  it looks less like an uncontrolled crash.
@V1.5			This implementation adds:
					- Input field for selected persons name as recorded in the GRO Index. This may be different from their actual name due to miss-transcription and use of initials
					  rather than full given names. The name will be available for formats definitions as {RNAME}.
					- Input field for selected persons spouse as recorded in the GRO Index for marriages. This may be different from their actual name due to miss-transcription. The
					  name will be available for format definitions as {RSPOUSE}.
					- Resolved problem restoring the previously saved register type.
					- Corrected error in formatted citation generation for the actual citation.
]]

Version = "1.5"

require("iuplua")
require("iupluacontrols")

if fhGetAppVersion() > 5 then
	fhSetStringEncoding("UTF-8")
	iup.SetGlobal("UTF8MODE","YES")
	iup.SetGlobal("UTF8MODE_FILE","NO")
	iup.SetGlobal("CUSTOMQUITMESSAGE","YES")					-- Needed for IUP 3.28
end

-- Define the named fields for Citation format strings
namedFields = {	"RNAME",
				"YEAR",
				"QTR",
				"MONTH",
				"DIST",
				"DISTNO",
				"VOL",
				"REG",
				"PAGE",
				"ENT",
				"RSPOUSE",
				"MAID",
				"AGE",
				"DOB"}

--[[
	Definitions of the pre-canned citation formats for each register type BIRTH, MARRIAGE, DEATH
	Each register entry is itself a table of formats, where each entry comprises
		a PERIOD of FROM, TO during which the format should be applicable.
		a DISPLAY string to display that could be a name or an example of the format
		a FORMAT string using the named fields that will be used to create the GRO string.
]]
--[[
	Rules for GRO Registers
	Q3 1837	• Civil Registration in England and Wales begins
	1866	• GRO indexes include age at death
	Q3 1911	• GRO Birth Indexes include Mothers Maiden Name, but GRO has been adding this to earlier index entries.
	Q1 1912	• GRO Marriage Indexes include Spouses Surname
	Q2 1969	• GRO Death Indexes show date of Birth instead of age at Death
	Q2 1974	• GRO Volume Numbers re-organised (e.g. 9c became 32).
	Q1 1993	• Grouping of Districts into Volumes ceased for Births & Deaths, District/Register/Entry Numbers started
	Q1 1994	• Grouping of Districts into Volumes ceased for Marriages, District/Register/Entry Numbers started
]]

citationFormats = 	{	BIRTH =		{	{PERIOD = {FROM =  "Q3 1837", TO = "Q4 1992"},
										 DISPLAY = "GRO: Qn yyyy, District, Vol, Page|Mother:",
										 FORMAT = "GRO: {QTR} {YEAR}, {DIST}, {VOL}, {PAGE}\nMother: {MAID}"},
										{PERIOD = {FROM = "Q1 1993"},
										 DISPLAY = "GRO: Qn yyyy, District(No.), Reg, Entry|Mother:",
										 FORMAT = "GRO: {QTR} {YEAR} {MONTH}, {DIST}({DISTNO}), {REG}, {ENT}\nMother: {MAID}"}
											},
						MARRIAGE =	{	{PERIOD = {FROM =  "Q3 1837", TO = "Q4 1993"},
										 DISPLAY = "GRO: Qn yyyy, District, Vol, Page",
										 FORMAT = "GRO: {QTR} {YEAR}, {DIST}, {VOL}, {PAGE}"},
										{PERIOD = {FROM =  "Q1 1994"},
										 DISPLAY = "GRO: Qn yyyy, District(No.), Reg, Entry",
										 FORMAT = "GRO: {QTR} {YEAR} {MONTH}, {DIST}({DISTNO}), {REG}, {ENT}"}
									},
						DEATH =		{	{PERIOD = {FROM = "Q3 1837", TO = "1865"},
										 DISPLAY = "GRO: Qn yyyy, District, Vol, Page",
										 FORMAT = "GRO: {QTR} {YEAR}, {DIST}, {VOL}, {PAGE}"},
										{PERIOD = {FROM = "1866", TO = "Q1 1969"},
										 DISPLAY = "GRO: Qn yyyy, District, Vol, Page|Age: nnn",
										 FORMAT = "GRO: {QTR} {YEAR}, {DIST}, {VOL}, {PAGE}\nAge: {AGE}"},
										{PERIOD = {FROM = "Q2 1969", TO="1992"},
										 DISPLAY = "GRO: Qn yyyy, District, Vol, Page|DoB: dd mmm yyyy",
										 FORMAT = "GRO: {QTR} {YEAR}, {DIST}, {VOL}, {PAGE}\nDoB: {DOB}"},
										{PERIOD = {FROM = "1993"},
										 DISPLAY = "GRO: Qn yyyy, District(No.), Reg, Entry|DoB: dd mmm yyyy",
										 FORMAT = "GRO: {QTR} {YEAR} {MONTH}, {DIST}({DISTNO}), {REG}, {ENT}\nDoB: {DOB}"}
									}
						}

function main()
	local dlgResult
	local deathInfo = "AGE"
	local volregInfo = "VOLUME"
	local arrShortPeriod = {"Q1";"Q2";"Q3";"Q4";"Jan";"Feb";"Mar";"Apr";"May";"Jun";"Jul";"Aug";"Sep";"Oct";"Nov";"Dec";}
	local formatMap = {} -- Used to map the selection list entries to the formats after filtering for the drop-down list

	-- Create the active objects we need
	-- Buttons for completion and opening customisation dialog
	btnOk = iup.button({
		name = "OK",
		title = "Apply",
		size = "40x20",
		tip = "Apply the GRO Source Citation according to your inputs and option settings",
		action = function(self)
			dlgResult = self.name
			return iup.CLOSE
		end,
	})
	btnCancel = iup.button({
		name = "CANCEL",
		title = "Close",
		size = "40x20",
		tip = "Exit without making any changes to the project",
		action = function(self)
			dlgResult = self.name
			return iup.CLOSE
		end,
	})
	btnCustomise = iup.button({
		name = "EDIT",
		title = "Customise...",
		size = "x10",
		action = function()
			showCustomiseDialog()
		end,
		tip = "Manage and customise the available GRO formats",
	})

	-- Toggles that we need for selection of the register
	radBirth = iup.toggle({
		name = "BIRTH",
		title = "Birth",
		value = "OFF",
		tip = "Select Birth Registrations",
		action = function(self)
			selectRegister(self)
			return iup.DEFAULT
		end,
	})
	radMarriage = iup.toggle({
		name = "MARRIAGE",
		title = "Marriage",
		value = "OFF",
		tip = "Select Marriage Registrations",
		action = function(self)
			selectRegister(self)
			return iup.DEFAULT
		end,
	})
	radDeath = iup.toggle({
		name = "DEATH",
		title = "Death",
		value = "OFF",
		tip = "Select Death Registrations",
		action = function(self)
			selectRegister(self)
			return iup.DEFAULT
		end,
	})
	radBMD = iup.radio({ iup.hbox({ radBirth, radMarriage, radDeath }) })

	-- List to select the GRO Quarter or Month as applicable
	listQuarter = iup.list({
		"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",
		value = 1,
		dropdown = "YES",
		visible_items = 5,
		tip = "Select registration quarter or month",
	})

	-- List to select the option for what to do with the GRO date information if anything
	listIndexInDate = iup.list({
		"No Event Date",
		"Year Only Event Date",
		"Inexact Event Date",
		value = 1,
		dropdown = "YES",
		size = "100",
		tip = "When should the event date be updated",
	})

	-- List to select the option for how the citation should be recorded
	listCitRef = iup.list({
		"Where within",
		"Text From Source",
		"Event Note Only",
		value = 1,
		dropdown = "YES",
		size = "100",
		tip = "Where should the citation reference be put",
	})

	-- Dynamic list of the GRO citation formats that can be selected
	listRefFormat = iup.list({
		value = 0,
		size = "200",
		dropdown = "YES",
		action = function()
			updateCitation()
		end,
		tip = "Select citation reference format",
	})

	-- List to select the option for how the citation date should be recorded
	listCitDate = iup.list({
		"No Date",
		"Today's Date",
		"GRO Index Reg Date",
		value = 1,
		dropdown = "YES",
		size = "100",
		tip = "What date should be used for the citation",
	})

	-- List to select the option for what assesment to give the citation
	listCitAss = iup.list({
		"None",
		"Unreliable",
		"Questionable",
		"Secondary Evidence",
		"Primary Evidence",
		value = 1,
		dropdown = "YES",
		size = "100",
		tip = "Select the assessement to give the citation",
	})

	-- Table of values used to set the assesement when option is to apply one
	local tblAssess = { "Unreliable", "Questionable", "Secondary Evidence", "Primary Evidence", "" }

	-- Checkbox to select to add the citation to individual/parents names
	chkAddToName = iup.toggle({ value = "OFF", tip = "Should the citation be added to the individual/parents name" })

	-- Text input fields
	textRecordedName = iup.text{size="200", value="", tip = "Name as recorded in GRO Index", active = "YES"}
	textRecordedSpouse = iup.text{size="200", value="", tip = "Spouse name as recorded in GRO Index", active = "NO"}
	textDistrict = iup.text{size="200", value="", tip = "Name of the registration district"}
	textDistrictNo = iup.text({ size = "100", value = "", tip = "Registration district number after 1992." })
	textRefVolReg = iup.text({ size = "100", value = "", tip = "Volume pre-1993, Register after 1992." })
	textYear = iup.text({ SPIN = "YES", SPINMIN = 1837, SPINMAX = 2005, value = "1837" })
	textRefPagEnt = iup.text({ size = "100", value = "", tip = "Page number pre-1993, Entry number after 1992." })
	textMother = iup.text({ active = "YES", value = "", size = "200", tip = "Maiden name of mother for birth registrations" })
	textCitation = iup.text({
		active = "YES",
		multiline = "YES",
		size = "200x30",
		tip = "What the citation will look like based on your selections",
	})
	textDeath = iup.text({ size = "100", active = "NO", value = "", tip = "Age at Death or Birth date:" })
	textBirthId = iup.text({ tip = "Family Historian source record ID for GRO Birth Index source" })
	textMarriageId = iup.text({ tip = "Family Historian source record ID for GRO Marriage Index source" })
	textDeathId = iup.text({ tip = "Family Historian source record ID for GRO Death Index source" })

	-- Named labels where we want to be able to change active state of
	lblRecordedSpouse = iup.label{ title = "Spouse as Recorded:", active = "NO" }
	lblDistrict = iup.label{ title = "Registration District:" }
	lblDistrictNo = iup.label{ title = "Registration District Number:", active = "NO" }
	lblRefVolReg = iup.label{ title = "Volume or Register number:" }
	lblRefPagEnt = iup.label{ title = "Volume Page or Register Entry:" }
	lblMother = iup.label{ title = "Mothers Maiden name:", active = "YES" }
	lblDeath = iup.label{ title = "Age at Death or Birth date:", active = "NO" }
	lblBirthSrcId = iup.label{ title = "Birth Source Record ID:" }
	lblMarriageSrcId = iup.label{ title = "Marriage Source Record ID:" }
	lblDeathSrcId = iup.label{ title = "Death Source Record ID:" }
	lblCitDate = iup.label{ title = "Set Citation Entry Date:" }
	lblCitAss = iup.label{ title = "Set Citation Assessment:" }
	lblAddToName = iup.label{ title = "Add Citation to Name:" }

	-- Create display grid
	grid = iup.gridbox{	iup.label{title = "GRO Index Type:"}, 						radBMD,
							iup.label{ title = "Name as Recorded:" },				textRecordedName,
							iup.label{title = "Registration Year:"},				textYear,
							iup.label{title = "Registration Quarter or Month:"},	listQuarter,
							lblDistrict,											textDistrict,
							lblDistrictNo,											textDistrictNo,
							lblRefVolReg,											textRefVolReg,
							lblRefPagEnt,											textRefPagEnt,
							lblRecordedSpouse,										textRecordedSpouse,
							lblMother,												textMother,
							lblDeath,												textDeath,
							iup.label{title = "Put GRO Index date in event Date when:", size="150"}, listIndexInDate,
							iup.label{title = "Select Citation Reference Field:"},	listCitRef,
							iup.hbox{iup.label{title = "GRO Reference Format:"}, btnCustomise; alignment="ACENTER"}, listRefFormat,
							iup.label{title = "GRO Reference Preview:"},			textCitation,
							iup.label{separator = "HORIZONTAL"}, iup.label{separator = "HORIZONTAL"},
							lblBirthSrcId,											textBirthId,
							lblMarriageSrcId,										textMarriageId,
							lblDeathSrcId,											textDeathId,
							lblCitDate,												listCitDate,
							lblCitAss,												listCitAss,
							lblAddToName,											chkAddToName;
							orientation = "HORIZONTAL", numdiv = 2, sizecol=-1, sizelin=-1, padding="2x5", fontstyle=""}

	-- Set Parameter Default Values
	local intPYear = 1900			-- Registration Year
	local intPQuarter = 1			-- Registration Quarter or Month
	local intPIndexInDate = 1		-- Overwrite Event Date Criteria
	local intPCitRef = 1			-- Select Citation Field for Ref
	local intPBirthId = nil			-- Source Record Id Birth
	local intPMarriageId = nil		-- Source Record Id Marriage
	local intPDeathId = nil			-- Source Record Id Death
	local intPCitDate = 1			-- Select Citation Entry Date
	local intPCitAss = 1			-- Select Citation Assessment
	local strPAddToName = ""		-- Add Citation to Name?
	local tblCitationFormats = citationFormats	-- Assume the pre-defined if nothing saved (first run)

	function textYear:valuechanged_cb()
		-- Check it is a valid year
		if ValidYear(textYear.value) then
			-- Enable/Disable fields for year and register
			setForYear(radBMD.value.name, tonumber(textYear.value))

			-- Update citation
			updateCitation()
		else
			iup.Message("Registration Year", "Year value not recognised as a year, please correct before continuing.")
		end
	end

	function textYear:spin_cb(pos)
		-- NOTE: pos is the value it will be after the spin, not current value
		-- Check it is a valid year
		if ValidYear(pos) then
			-- Enable/Disable fields for year and register
			setForYear(radBMD.value.name, tonumber(pos))

			-- Update citation
			updateCitation()
		else
			iup.Message("Registration Year", "Year value not recognised as a year, please correct before continuing.")
		end
	end

	function listQuarter:action(t, i, v)
		if v == 1 then
			-- Enable/Disable fields for year and register
			setForYear(radBMD.value.name, tonumber(textYear.value))

			-- Update citation
			updateCitation()
		end
		return iup.DEFAULT
	end

	--[[
	Rules for GRO Registers
	Q3 1837	• Civil Registration in England and Wales begins
	1852	• GRO References change from Roman Numerals (XVI) to Arabic numbers and letters (6d)
	1866	• GRO indexes include age at death
	Q3 1911	• GRO Birth Indexes include Mothers Maiden Name, but GRO has been adding this to earlier index entries.
	Q1 1912	• GRO Marriage Indexes include Spouses Surname
	Q2 1969	• GRO Death Indexes show date of Birth instead of age at Death
	Q2 1974	• GRO Volume Numbers re-organised (e.g. 9c became 32).
	Q1 1993	• Grouping of Districts into Volumes ceased for Births & Deaths, District/Register/Entry Numbers started
	Q1 1994	• Grouping of Districts into Volumes ceased for Marriages, District/Register/Entry Numbers started
]]
	function setForYear(reg, year)
		theQuarter, _ = getQuarterAndMonth()
		if (year < 1837) or (year == 1837 and (theQuarter == "Q1" or theQuarter == "Q2")) then
			-- No GRO Indexes prior to Q3 1837, all GRO input fields disabled
			lblDistrict.active = "NO"
			textDistrict.active = "NO"
			lblDistrictNo.active = "NO"
			textDistrictNo.active = "NO"
			lblRefVolReg.active = "NO"
			textRefVolReg.active = "NO"
			lblRefPagEnt.active = "NO"
			textRefPagEnt.active = "NO"
		elseif
			((reg == "BIRTH" or reg == "DEATH") and (year >= 1837 and year <= 1992))
			or (reg == "MARRIAGE" and (year >= 1837 and year <= 1993))
		then
			-- No District number, Volume and Page
			volregInfo = "VOLUME"
			lblDistrict.active = "YES"
			textDistrict.active = "YES"
			lblDistrictNo.active = "NO"
			textDistrictNo.active = "NO"
			lblRefVolReg.title = "Volume Number:"
			lblRefVolReg.active = "YES"
			textRefVolReg.active = "YES"
			lblRefPagEnt.title = "Page Number:"
			lblRefPagEnt.active = "YES"
			textRefPagEnt.active = "YES"
		else
			-- District number, Register and Entry
			volregInfo = "REGISTER"
			lblDistrict.active = "YES"
			textDistrict.active = "YES"
			lblDistrictNo.active = "YES"
			textDistrictNo.active = "YES"
			lblRefVolReg.title = "Register Number:"
			lblRefVolReg.active = "YES"
			textRefVolReg.active = "YES"
			lblRefPagEnt.title = "Register Entry:"
			lblRefPagEnt.active = "YES"
			textRefPagEnt.active = "YES"
		end

		setForBMDType(reg, year, theQuarter)
	end -- setForYear

	-- Essentially called as the common BMD toggle action
	function selectRegister(self)
		if self.value == "ON" then
			setForYear(radBMD.value.name, tonumber(textYear.value))
			-- Load the relevant formats for the selected register type
			loadFormats(radBMD.value.name)

			updateCitation()
		end
	end -- selectRegister

	-- Set the availability of the Mother's maiden name and Death Age based on register type selection
	function setForBMDType(reg, year, qtr)
		if (year < 1837) or (year == 1837 and (theQuarter == "Q1" or theQuarter == "Q2")) then
			-- No GRO Indexes prior to Q3 1837, all GRO input fields disabled
			textRecordedSpouse.active = "NO"
			textMother.active = "NO"
			textDeath.active = "NO"
			lblRecordedSpouse.active = "NO"
			lblMother.active = "NO"
			lblDeath.active = "NO"
		else
			-- Set the Death age/DoB label as appropriate to the year etc.
			if ((year >= 1866) and (year <= 1968)) or ((year == 1969) and (qtr == "Q1")) then
				deathInfo = "AGE"
				lblDeath.title = "Age at Death:"
			else
				deathInfo = "DOB"
				lblDeath.title = "Date of Birth:"
			end

			-- Enable/Disable GRO fields relevant to the register type
			if reg == "BIRTH" then
				textRecordedSpouse.active = "NO"
				textMother.active = "YES"
				textDeath.active = "NO"
				lblRecordedSpouse.active = "NO"
				lblMother.active = "YES"
				lblDeath.active = "NO"
			elseif reg == "MARRIAGE" then
				textRecordedSpouse.active = "YES"
				textMother.active = "NO"
				textDeath.active = "NO"
				lblRecordedSpouse.active = "YES"
				lblMother.active = "NO"
				lblDeath.active = "NO"
			elseif reg == "DEATH" then
				textRecordedSpouse.active = "NO"
				textMother.active = "NO"
				textDeath.active = "YES"
				lblRecordedSpouse.active = "NO"
				lblMother.active = "NO"
				-- No age recorded prior to 1866
				if year < 1866 then
					lblDeath.active = "NO"
					textDeath.active = "NO"
				else
					lblDeath.active = "YES"
					textDeath.active = "YES"
				end
			else
				-- Should never get here unless we did a bad coding LOL
				iup.Message(
					"function setForBMDType()",
					'Called with invalid register type: "'
						.. reg
						.. '".\nPlease debug and correct the coding error.\n\nPlugin aborted!'
				)
				return iup.CLOSE
			end
		end
		-- Load the relevant formats for the selected register type
		loadFormats(reg)
	end --setForBMDType

	-- Populate the format selection list for the specified register
	function loadFormats(reg)
		-- Convert the GRO Index year to a date object (should be valid already)
		local groDate = fhNewDate()
		local added = 0
		local quarter
		local month
		quarter, month = getQuarterAndMonth()

		if string.len(month) > 3 then
			-- It's a quarter date selection
			groDate:SetValueAsText(quarter .. " " .. textYear.value)
		else
			-- It's a specific month selection
			groDate:SetValueAsText(month .. " " .. textYear.value)
		end
		-- Clear the list
		listRefFormat[1] = nil
		formatMap[1] = nil

		for f = 1, #tblCitationFormats[reg] do
			local fromDate = nil
			local toDate = nil
			if tblCitationFormats[reg][f].PERIOD then
				-- PERIOD is present get limits if present
				if tblCitationFormats[reg][f].PERIOD.FROM then
					fromDate = fhNewDate()
					res = fromDate:SetValueAsText(tblCitationFormats[reg][f].PERIOD.FROM)
				end
				if tblCitationFormats[reg][f].PERIOD.TO then
					toDate = fhNewDate()
					res = toDate:SetValueAsText(tblCitationFormats[reg][f].PERIOD.TO)
				end
			end
			-- Add the format if within the period specified
			if InPeriod(groDate, fromDate, toDate) then
				added = added + 1
				formatMap[added] = f
				listRefFormat[added] = tblCitationFormats[reg][f]["DISPLAY"]
			end
		end
		if tonumber(listRefFormat.count) > 0 then
			listRefFormat.value = 1
		end
	end -- loadFormats

	-- Enable/Disable the citation fields depending on citation type selection
	function listCitRef:action(t, i, v)
		if v == 1 then
			setForCitRefField(i)
		end
		return iup.DEFAULT
	end

	-- Enable/Disable the fields that only apply to a source citation
	function setForCitRefField(i)
		if i == 3 then
			-- Event Note only so disable all fields
			textBirthId.active = "NO"
			textMarriageId.active = "NO"
			textDeathId.active = "NO"
			listCitDate.active = "NO"
			listCitAss.active = "NO"
			chkAddToName.active = "NO"

			lblBirthSrcId.active = "NO"
			lblBirthSrcId.active = "NO"
			lblMarriageSrcId.active = "NO"
			lblDeathSrcId.active = "NO"
			lblCitDate.active = "NO"
			lblCitAss.active = "NO"
			lblAddToName.active = "NO"
		else
			-- Other selections enable all fields
			textBirthId.active = "YES"
			textMarriageId.active = "YES"
			textDeathId.active = "YES"
			listCitDate.active = "YES"
			listCitAss.active = "YES"
			chkAddToName.active = "YES"

			lblBirthSrcId.active = "YES"
			lblBirthSrcId.active = "YES"
			lblMarriageSrcId.active = "YES"
			lblDeathSrcId.active = "YES"
			lblCitDate.active = "YES"
			lblCitAss.active = "YES"
			lblAddToName.active = "YES"
		end
	end -- setForCitRefField

	function textDistrict:valuechanged_cb()
		-- Update citation
		updateCitation()
	end

	function textRecordedName:valuechanged_cb()
		-- Update citation
		updateCitation()
	end

	function textDistrictNo:valuechanged_cb()
		-- Update citation
		updateCitation()
	end

	function textRefVolReg:valuechanged_cb()
		-- Update citation
		updateCitation()
	end

	function textRefPagEnt:valuechanged_cb()
		-- Update citation
		updateCitation()
	end

	function textRecordedSpouse:valuechanged_cb()
		-- Update citation
		updateCitation()
	end
	function textMother:valuechanged_cb()
		-- Update citation
		updateCitation()
	end

	function textDeath:valuechanged_cb()
		-- Update citation
		updateCitation()
	end

	function chkAddToName:action()
		if chkAddToName.value == "ON" then
			chkAddToName.title = "On"
		else
			chkAddToName.title = "Off"
		end
	end

	function updateCitation()
		-- Declare the shared values for separation purposes
		local deathAge = ""
		local deathDoB = ""
		local volName = ""
		local regName = ""
		local pageNo = ""
		local entNo = ""

		-- Split the Volume/Register and Page/Entry
		if volregInfo == "VOLUME" then
			volName = textRefVolReg.value
			pageNo = textRefPagEnt.value
		else
			regName = textRefVolReg.value
			entNo = textRefPagEnt.value
		end

		-- Split the Age/Dob for death
		if deathInfo == "AGE" then
			deathAge = textDeath.value
		else
			deathDoB = textDeath.value
		end
		-- Convert the Quarter or Month selection to a Quarter and a Month
		theQuarter, theMonth = getQuarterAndMonth()

		if tonumber(listRefFormat.value) > 0 then
			-- Create the Citation string from the input parameters and selected format
			textCitation.value = formatCitation(tblCitationFormats[radBMD.value.name][formatMap[tonumber(listRefFormat.value)]]["FORMAT"],
											{	RNAME = 	textRecordedName.value,
												YEAR =		textYear.value,
												QTR =		theQuarter,
												MONTH =		theMonth,
												DIST =		textDistrict.value,
												DISTNO =	textDistrictNo.value,
												VOL =		volName,
												REG =		regName,
												PAGE =		pageNo,
												ENT =		entNo,
												RSPOUSE =	textRecordedSpouse.value,
												MAID =		textMother.value,
												AGE =		deathAge,
												DOB =		deathDoB})
		else
			textCitation.value = ""
		end
	end

-- vvvvvvvvvvvvvvvvvvvvv Customise GRO Format Dialog vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
--[[
	This function displays and handles the format customisation.
]]
	function showCustomiseDialog()
	formatHelp =
[[
The format can make use of most standard ASCII characters. Special auto-text fields
may also be used to substitute in values you have entered in the main dialog.
These are as follows:
	{RNAME}, the name of the individual as recorded
	{YEAR}, the year of registration
	{QTR}, the quarter of registration
	{MONTH}, the month of registration where applicable
	{DIST}, the registration district name
	{DISTNO}, the registration district number where applicable
	{VOL}, the volume number as applicable
	{REG}, the register number as applicable
	{PAGE}, the page number as applicable
	{ENT}, the entry number as applicable
	{RSPOUSE}, the name of the spouse as recorded
	{MAID}, the mother's maiden name for births
	{AGE}, Age at Death
	{DOB}}, Date of Birth (for deaths)
]]

		local textDisplay = iup.text({ size = "250", value = "", tip = "Display name for the format" })
		local textFormat = iup.text({ multiline = "YES", size = "300x30", value = "", tip = formatHelp })
		local textFrom = iup.text({ size = "50", value = "", tip = "Date from which the format is applicable" })
		local textTo = iup.text({ size = "50", value = "", tip = "Date to which the format is applicable" })
		-- for some reaason this has to go before the use in listExisting action
		function updateFields()
			-- Copy selected format information to edit fields
			if listExisting.value ~= 0 then
				local format = tblCitationFormats[radBMD.value.name][tonumber(listExisting.value)]
				if format.PERIOD ~= nil then
					if format.PERIOD.FROM ~= nil then
						textFrom.value = format.PERIOD.FROM
					else
						textFrom.value = ""
					end
					if format.PERIOD.TO ~= nil then
						textTo.value = format.PERIOD.TO
					else
						textTo.value = ""
					end
				end
				textDisplay.value = format["DISPLAY"]
				textFormat.value = format["FORMAT"]
			end
		end -- updateFields

		function doAdd()
			if ValidDate(textFrom.value) then
				if ValidDate(textTo.value) then
					-- Simply take whatever is in the fields and add to the format table
					-- Create the PERIOD table
					local tblPeriod = {}
					if textFrom.value ~= "" then
						tblPeriod.FROM = textFrom.value
					end
					if textTo.value ~= "" then
						tblPeriod.TO = textTo.value
					end

					-- Does not care if it is a duplicate as that is not an issue
					table.insert(
						tblCitationFormats[radBMD.value.name],
						{ PERIOD = tblPeriod, DISPLAY = textDisplay.value, FORMAT = textFormat.value }
					)
					loadExisting()
				else
					iup.Message("Add Format", "The To date is invalid.\nFormat not added.")
				end
			else
				iup.Message("Add Format", "The From date is invalid.\nFormat not added.")
			end
		end -- doAdd

		function doRemove()
			-- Remove the format table entry for the selected one, by corresponding index position
			table.remove(tblCitationFormats[radBMD.value.name], tonumber(listExisting.value))
			loadExisting()
		end -- doRemove

		function doUpdate()
			if ValidDate(textFrom.value) then
				if ValidDate(textTo.value) then
					-- Create the PERIOD table
					local tblPeriod = {}
					if textFrom.value ~= "" then
						tblPeriod.FROM = textFrom.value
					end
					if textTo.value ~= "" then
						tblPeriod.TO = textTo.value
					end

					-- replace the selected entry data with the edit fields content.
					tblCitationFormats[radBMD.value.name][tonumber(listExisting.value)] =
						{ PERIOD = tblPeriod, DISPLAY = textDisplay.value, FORMAT = textFormat.value }

					loadExisting()
				else
					iup.Message("Add Format", "The To date is invalid.\nFormat not updated.")
				end
			else
				iup.Message("Add Format", "The From date is invalid.\nFormat not updated.")
			end
		end -- doUpdate

		-- Populate the format selection list
		function loadExisting()
			listExisting[1] = nil
			for f = 1, #tblCitationFormats[radBMD.value.name] do
				listExisting[f] = tblCitationFormats[radBMD.value.name][f]["DISPLAY"]
			end
			listExisting.value = 1
			updateFields()
		end -- loadExisting

		listExisting = iup.list({
			value = 0,
			dropdown = "YES",
			tip = "Select existing format",
			size = "250",
			action = function()
				updateFields()
			end,
		})
		local btnAdd = iup.button({
			name = "ADD",
			title = "Add",
			size = "40x",
			action = function()
				doAdd()
				return iup.DEFAULT
			end,
			tip = "Add a new GRO format",
		})
		local btnUpdate = iup.button({
			name = "UPDATE",
			title = "Update",
			size = "40x",
			action = function()
				doUpdate()
				return iup.DEAFULT
			end,
			tip = "Update the selected GRO format",
		})
		local btnRemove = iup.button({
			name = "REMOVE",
			title = "Remove",
			size = "40x",
			action = function()
				doRemove()
				return iup.DEFAULT
			end,
			tip = "Remove the selected GRO format",
		})
		local btnSave = iup.button({
			name = "SAVE",
			title = "Save",
			size = "40x",
			action = function()
				SaveSettings()
				return iup.DEFAULT
			end,
			tip = "Save plugin settings with GRO formats",
		})
		local btnClose = iup.button({
			name = "CLOSE",
			title = "Close",
			size = "40x20",
			action = function()
				return iup.CLOSE
			end,
		})
		local content = iup.vbox{
								iup.frame	{iup.vbox{
												iup.vbox {iup.hbox{iup.fill{},iup.label{title = "Select an Existing GRO Format:"}, iup.fill{}},iup.hbox{iup.fill{}, listExisting, iup.fill{}}},
												iup.hbox {iup.label{separator = "HORIZONTAL", size="300"}, margin ="0x5"},
												iup.vbox {iup.hbox{iup.fill{}, iup.label{title = "Period for which the Format is Applicable:"}, iup.fill{}},iup.hbox{iup.fill{}, iup.label{title="From:"}, textFrom, iup.label{title="To:"}, textTo, iup.fill{}}},
												iup.vbox {iup.hbox{iup.fill{}, iup.label{title = "Friendly Name to Show for Selection:"}, iup.fill{}},iup.hbox{iup.fill{}, textDisplay, iup.fill{}}},
												iup.vbox {iup.hbox{iup.fill{}, iup.label{title = "Define the Format using Text and Field Names:"}, iup.fill{}},iup.hbox{iup.fill{}, textFormat, iup.fill{}}},
												iup.hbox {iup.fill{}, btnAdd, btnUpdate, btnRemove, btnSave, iup.fill{}; margin="5x10", normalizesize="YES", gap="20", alignment="ACENTER"},
												padding="2x5"}
											},
								iup.hbox {iup.fill{}, btnClose, iup.fill{}};
										}
		local dlgCustomise = iup.dialog{iup.frame{content}, title="Customise GRO Formats for "..radBMD.value.name; resize="NO", minbox="NO", maxbox="NO"}

		-- Initialise the edit fields with default selection
		loadExisting()

		dlgCustomise:popup(IUP_CENTERPARENT, IUP_CENTERPARENT)

		loadFormats(radBMD.value.name)
	end -- showCustomiseDialog
-- ^^^^^^^^^^^^^^^^^^^^^ Customise GRO Format Dialog ^^^^^^^^^^^^^^^^^^^^^^^^^^^

-- =========================== Start doing things ==============================
	local strParamFile = fhGetPluginDataFileName()

	function LoadSettings()
		local strChunk = fhLoadTextFile(strParamFile)
		if strChunk then
			local doChunk = load(strChunk)

			-- Get the saved data version by reading first value ignoring the rest
			local strDataVer = doChunk()

			if strDataVer == "V1" then
				-- Original V1 saved data
				strDataVer, intPYear, intPQuarter, intPIndexInDate, intPCitRef, intPBirthId, intPMarriageId, intPDeathId, intPCitDate, intPCitAss, strPAddToName, tblCitationFormats =
					doChunk()

				-- Add missing register type defaulted to births
				strPRegister = "BIRTH"
			elseif strDataVer == "V2" then
				-- Updated to include register type in V2 data
				strDataVer, strPRegister, intPYear, intPQuarter, intPIndexInDate, intPCitRef, intPBirthId, intPMarriageId, intPDeathId, intPCitDate, intPCitAss, strPAddToName, tblCitationFormats =
					doChunk()
			end
		end

		-- Poulate the dialog fields from restored values

		-- It seems that radBMD.value has to be updated with the desired toggle rather than setting the
		-- toggle value to "ON". This seems to set the toggle to "ON" as desired, so it does not need to
		-- be explicitly set.
		if strPRegister == "BIRTH" then
			radBMD.value = radBirth
		elseif strPRegister == "MARRIAGE" then
			radBMD.value = radMarriage
		else
			radBMD.value = radDeath
		end

		textYear.value = intPYear
		listQuarter.value = intPQuarter
		listIndexInDate.value = intPIndexInDate
		listCitRef.value = intPCitRef
		textBirthId.value = intPBirthId
		textMarriageId.value = intPMarriageId
		textDeathId.value = intPDeathId
		listCitDate.value = intPCitDate
		listCitAss.value = intPCitAss
		chkAddToName.value = strPAddToName
		if strPAddToName == "ON" then
			chkAddToName.title = "On"
		else
			chkAddToName.title = "Off"
		end
	end -- function LoadSettings

	function SaveSettings()
		-- Get the settings parameters to save for next time
		strRegister		= string.format('%q', radBMD.value.name)
		intPYear		= tonumber(textYear.value)
		intPQuarter		= tonumber(listQuarter.value)
		intPIndexInDate	= tonumber(listIndexInDate.value)
		intPCitRef		= tonumber(listCitRef.value)
		intPBirthId		= textBirthId.value
		intPMarriageId	= textMarriageId.value
		intPDeathId		= textDeathId.value
		intPCitDate		= tonumber(listCitDate.value)
		intPCitAss		= tonumber(listCitAss.value)
		strPAddToName	= chkAddToName.value
		strFormats		= FormatsAsString(tblCitationFormats)
		strDataVer		= string.format('%q', "V2") -- V2 includes the register type selection

		-- Save Sticky Parameters
		if fhGetContextInfo("CI_APP_MODE") == "Project Mode" then
			local strParams = "return "..strDataVer..","..strRegister..","..intPYear..","..intPQuarter..","..intPIndexInDate..","..intPCitRef..","..intPBirthId..","..intPMarriageId..","..intPDeathId..","..intPCitDate..","..intPCitAss..","..string.format('%q', strPAddToName)..","..strFormats.."\n"
			fhSaveTextFile(strParamFile, strParams, "UTF-8")
		end
	end -- function SaveSettings

	-- Get Parameters from Previous Sticky Data
	LoadSettings()
	setForYear(radBMD.value.name, tonumber(textYear.value))

	-- Select BMD Source Records if not restored from Sticky Data
	if intPBirthId == nil then
		local strAns = "Retry"
		while strAns == "Retry" do
			strAns = ""
			fhMessageBox(
				"\n Please select just one generic GRO Index source record. \n                        ALTERNATIVELY \n Select 3 source records for Birth, Marriage, and Death, \n ensuring they are in that order in the righthand pane. \n",
				"MB_OK",
				"MB_ICONINFORMATION"
			)
			-- Get Source Records to use.
			local ptrList = fhPromptUserForRecordSel("SOUR", 3)
			if #ptrList == 0 then
				strAns = fhMessageBox(
					"\n No source records have been selected ...\n\n   Click  'Abort'  to cancel the plugin\n   Click  'Retry'    to select the sources again\n   Click  'Ignore'  to continue without sources \n",
					"MB_ABORTRETRYIGNORE",
					"MB_ICONQUESTION"
				)
				if strAns == "Abort" then
					return
				end
				intPBirthId = 0
				intPMarriageId = 0
				intPDeathId = 0
			elseif #ptrList == 1 then
				intPBirthId = fhGetRecordId(ptrList[1])
				intPMarriageId = fhGetRecordId(ptrList[1])
				intPDeathId = fhGetRecordId(ptrList[1])
			elseif #ptrList == 2 then
				strAns = fhMessageBox(
					"\n Only two source records were selected ...\n\n   Click  'Retry'    to select the sources again \n   Click  'Cancel'  to cancel the plugin \n",
					"MB_RETRYCANCEL",
					"MB_ICONQUESTION"
				)
				if strAns == "Cancel" then
					return
				end
			else --#ptrList == 3 then
				intPBirthId = fhGetRecordId(ptrList[1])
				intPMarriageId = fhGetRecordId(ptrList[2])
				intPDeathId = fhGetRecordId(ptrList[3])
			end
		end
	end

	-- Get Current Individual Record if Available
	local ptrList = fhGetCurrentRecordSel("INDI")
	if #ptrList == 1 then
		strName = "Current Record: "..fhGetItemText(ptrList[1],"~.NAME"):gsub("%%","%%%%").." ("..fhCallBuiltInFunction("LifeDates",ptrList[1])..")"
	else
		strName = "No record selected"
	end

	-- Poulate the dialog fields from restored values
	textYear.value			= intPYear
	listQuarter.value		= intPQuarter
	listIndexInDate.value	= intPIndexInDate
	listCitRef.value		= intPCitRef
	textBirthId.value		= intPBirthId
	textMarriageId.value	= intPMarriageId
	textDeathId.value		= intPDeathId
	listCitDate.value		= intPCitDate
	listCitAss.value		= intPCitAss
	chkAddToName.value		= strPAddToName

	-- Set active fields based on default or restored values
	updateCitation()
	setForCitRefField(tonumber(listCitRef.value))

	-- Construct and Display the parameters dialog to the user
	frmParams = iup.frame({ grid, title = strName, fontstyle = "Bold" })
	boxbuttons = iup.hbox({ iup.fill({}), btnOk, btnCancel, iup.fill({}), gap = "10" })
	dlg = iup.dialog({
		iup.vbox({ frmParams, boxbuttons }),
		title = "GRO Source Reference V" .. Version,
		size = "370x355",
		childoffset = "5x5",
		resize = "NO",
		maxbox = "NO",
	})
	dlg:showxy(iup.CENTER, iup.CENTER)

	if (iup.MainLoopLevel()==0) then
		iup.MainLoop()
	end

	-- How did the dialog exit
	if dlgResult == "OK" then
		-- Save Sticky Parameters
		SaveSettings()

		-- Now do the actual citation stuff copied and modified from Mike's plug-in

		-- Select Source Record
		if radBMD.value.name == "BIRTH" then
			intSource = intPBirthId
		end
		if radBMD.value.name == "MARRIAGE" then
			intSource = intPMarriageId
		end
		if radBMD.value.name == "DEATH" then
			intSource = intPDeathId
		end
		local ptrSource = fhNewItemPtr()
		ptrSource:MoveToRecordById("SOUR", intSource)
		if ptrSource:IsNull() then
			doError("Source Record Id " .. intSource .. " Not Found")
		end

		-- Obtain Desired Record
		if radBMD.value.name == "MARRIAGE" then
			ptrList = fhGetCurrentRecordSel("FAM")
			if #ptrList == 0 then
				ptrList = fhGetCurrentRecordSel("INDI")
				if #ptrList == 1 then
					ptrList = getSpouseFamilies(ptrList[1])
				end
			end
			if #ptrList ~= 1 then
				ptrList = fhPromptUserForRecordSel("FAM", 1)
			end
		else
			ptrList = fhGetCurrentRecordSel("INDI")
			if #ptrList ~= 1 then
				ptrList = fhPromptUserForRecordSel("INDI", 1)
			end
		end

		if #ptrList == 0 then
			doError("No Principal Record Selected")
		end

		-- Check Registration Year
		if intPYear < 1837 then
			doError("Registration Year before 1837")
		end

		local ptrEvent = fhNewItemPtr()
		local ptrField = fhNewItemPtr()
		local evtType = string.sub(radBMD.value.name, 1, 4)
		ptrEvent:MoveTo(ptrList[1], "~." .. evtType)
		if ptrEvent:IsNull() then
			-- Create Event, using first four letters of register type
			ptrEvent = fhCreateItem(evtType, ptrList[1])
		end

		-- Set Event Date based on selection
		ptrField:MoveTo(ptrEvent, "~.DATE")
		if ptrField:IsNull() then
			ptrField = fhCreateItem("DATE", ptrEvent)
		end
		local dtDate = fhGetValueAsDate(ptrField)
		local dpOne = dtDate:GetDatePt1()
		local dpTwo = dtDate:GetDatePt2()
		local Month = dpOne:GetMonth()
		local DayNo = dpOne:GetDay()
		if
			intPIndexInDate == 1 and dpOne:IsNull() and dpTwo:IsNull() -- No Date
			or intPIndexInDate == 2 and dpOne:GetMonth() == 0 and dpTwo:GetMonth() == 0 -- Year Only Date
			or intPIndexInDate == 3 and dpOne:GetDay() == 0 and dpTwo:GetDay() == 0 -- Inexact Date
		then
			dtDate:SetValueAsText(arrShortPeriod[intPQuarter] .. " " .. intPYear)
			fhSetValueAsDate(ptrField, dtDate)
		end

		-- Set Event Place & Address
		strPdist = promptPlace(textDistrict.value)
		ptrField:MoveTo(ptrEvent, "~.PLAC")
		if ptrField:IsNull() then
			ptrField = fhCreateItem("PLAC", ptrEvent)
		end
		if fhGetValueAsText(ptrField) == "" then
			fhSetValueAsText(ptrField, strPdist)
			-- If Place was added, add "Registration District" as the Address field
			ptrField:MoveTo(ptrEvent, "~.ADDR")
			if ptrField:IsNull() then
				ptrField = fhCreateItem("ADDR", ptrEvent)
			end
			if fhGetValueAsText(ptrField) == "" then
				fhSetValueAsText(ptrField, "Registration District")
			end
		end

		-- For Death Add age if entered.
		if deathInfo == "AGE" and radBMD.value.name == "DEATH" and textDeath.value:len() > 0 then
			-- Check it looks like a viable age
			if tonumber(textDeath.value) < 130 then
				ptrField:MoveTo(ptrEvent, "~.AGE")
				if ptrField:IsNull() then
					ptrField = fhCreateItem("AGE", ptrEvent)
					fhSetValueAsText(ptrField, textDeath.value)
				end
			end
		end

		-- Declare the shared values for separation purposes
		local deathAge = ""
		local deathDoB = ""
		local volName = ""
		local regName = ""
		local pageNo = ""
		local entNo = ""

		-- Split the Volume/Register and Page/Entry
		if volregInfo == "VOLUME" then
			volName = textRefVolReg.value
			pageNo = textRefPagEnt.value
		else
			regName = textRefVolReg.value
			entNo = textRefPagEnt.value
		end

		-- Split the Age/Dob for death
		if deathInfo == "AGE" then
			deathAge = textDeath.value
		else
			deathDoB = textDeath.value
		end
		-- Convert the Quarter or Month selection to a Quarter and a Month
		theQuarter, theMonth = getQuarterAndMonth()

		-- Create the Citation string from the input parameters and selected format
		strCitation = formatCitation(tblCitationFormats[radBMD.value.name][formatMap[tonumber(listRefFormat.value)]]["FORMAT"],
								{	RNAME = 	textRecordedName.value,
									YEAR =		textYear.value,
									QTR =		theQuarter,
									MONTH =		theMonth,
									DIST =		textDistrict.value,
									DISTNO =	textDistrictNo.value,
									VOL =		volName,
									REG =		regName,
									PAGE =		pageNo,
									ENT =		entNo,
									RSPOUSE =	textRecordedSpouse.value,
									MAID =		textMother.value,
									AGE =		deathAge,
									DOB =		deathDoB})

		-- Add Source Citation
		local eDate = nil
		if intPCitDate ~= 1 then
			-- Record Entry Date
			eDate = fhNewDate()
			if intPCitDate == 2 then
				eDate:SetSimpleDate(fhCallBuiltInFunction("today"))
			else
				if tonumber(listQuarter.value) > 4 then
					eDate:SetValueAsText(theMonth .. " " .. textYear.value)
				else
					eDate:SetValueAsText(theQuarter .. " " .. textYear.value)
				end
			end
		end

		-- Add Citation to Event
		local strAssessment = nil
		if intPCitAss > 1 then
			strAssessment = tblAssess[intPCitAss - 1]
		end
		addSource(ptrEvent, ptrSource, strCitation, intPCitRef, eDate, strAssessment)

		-- Add Citation to Name(s)
		local tblTags = {
			BIRTH = { "~.NAME[1]" },
			MARRIAGE = { "~.HUSB[1]>NAME[1]", "~.WIFE[1]>NAME[1]", "~.HUSB[2]>NAME[1]", "~.WIFE[2]>NAME[1]" },
			DEATH = { "~.NAME[1]" },
		}
		if strPAddToName == "ON" then
			for _, strTag in ipairs(tblTags[radBMD.value.name]) do
				ptrField:MoveTo(ptrList[1], strTag)
				if ptrField:IsNotNull() then
					addSource(ptrField, ptrSource, strCitation, intPCitRef, eDate, tblAssess[intPCitAss - 1])
				end
			end
		end

		-- Check Data
		ptrField:MoveTo(ptrEvent, "~.DATE")
		local strResult = fhCallBuiltInFunction("GetDataWarning", ptrField, 1)
		if strResult ~= "" then
			local strButton =
				fhMessageBox(strResult .. "\nDo you wish to undo the entry?", "MB_YESNO", "MB_ICONEXCLAMATION")
			if strButton == "Yes" then
				doError("Date Validation Failed")
			end
		end
	end -- Successful exit of dialog

end -- function main

function addSource(ptrFact, ptrSource, strCit, intWhere, eDate, strAssess)
	if intWhere == 3 then
		-- Event Note Only
		local ptrNote = fhNewItemPtr()
		ptrNote:MoveTo(ptrFact, "~.NOTE2")
		if ptrNote:IsNull() then
			ptrNote = fhCreateItem("NOTE2", ptrFact) -- FACT.NOTE2
		end
		local strNote = fhGetValueAsText(ptrNote)
		if strNote ~= "" then
			-- Put it at the start of existing note
			strNote = strCit .. "\n" .. strNote
		else
			strNote = strCit
		end
		fhSetValueAsText(ptrNote, strNote)
	else -- Not the Event Note, so need to Add Citation
		local ptrCite = fhCreateItem("SOUR", ptrFact) -- FACT.SOUR
		fhSetValueAsLink(ptrCite, ptrSource)
		local ptrData = fhNewItemPtr()
		local ptrAge = fhNewItemPtr()
		if eDate or intWhere == 2 then
			-- Text From Source
			ptrData = fhCreateItem("DATA", ptrCite) -- FACT.SOUR.DATA
		end
		if eDate then
			local ptrDate = fhCreateItem("DATE", ptrData) -- FACT.SOUR.DATA.DATE
			fhSetValueAsDate(ptrDate, eDate)
		end
		if strAssess then
			ptrQuay = fhCreateItem("QUAY", ptrCite) -- FACT.SOUR.QUAY
			fhSetValueAsText(ptrQuay, strAssess)
		end
		if intWhere == 1 then
			-- Where within Source
			ptrAge = fhCreateItem("PAGE", ptrCite) -- FACT.SOUR.PAGE
		else
			ptrAge = fhCreateItem("TEXT", ptrData) -- FACT.SOUR.DATA.TEXT
		end
		fhSetValueAsText(ptrAge, strCit)
	end
end -- function addSource

function getSpouseFamilies(ptrIndi)
	local arrFams = {}
	local ptrFams = fhNewItemPtr()
	ptrFams:MoveTo(ptrIndi, "~.FAMS")
	while ptrFams:IsNotNull() do
		table.insert(arrFams, fhGetValueAsLink(ptrFams))
		ptrFams:MoveNext("SAME_TAG")
	end
	return arrFams
end -- function getSpouseFamilies

function addParam(arrParams, strText, strType, strTip)
	table.insert(arrParams, strText .. strType .. "{" .. (strTip or "") .. "}")
end -- function addParam

function promptPlace(strDistrict)
	local arrPlaces = placeList(strDistrict) -- List of Place names matching Registration District entered
	local arrPrompt = {}
	addParam(arrPrompt, "Please select a Place from the list or ", "%t")
	addParam(arrPrompt, "use the entry box to enter a Place name ", "%t")
	addParam(arrPrompt, "  Enter the District Place name: ", "%s")
	addParam(
		arrPrompt,
		"  Select from District Place list: ",
		"%l|Use Place Above|" .. table.concat(arrPlaces, "|") .. "|"
	)
	local strPrompt = table.concat(arrPrompt, "\n") .. "\n"
	local isOK, strPlace, intPlace = iup.GetParam("Select District Place Name", param_action, strPrompt, strDistrict, 0)
	-- Check for Cancel
	if not isOK then
		return strDistrict
	end
	if intPlace > 0 then
		strPlace = arrPlaces[intPlace]
	end
	return strPlace
end -- function promptPlace

function placeList(strFind)
	strFind = strFind:lower()
	local arrPlac = {}
	local ptrPlac = fhNewItemPtr()
	ptrPlac:MoveToFirstRecord("_PLAC")
	while ptrPlac:IsNotNull() do
		local strPlac = fhGetDisplayText(ptrPlac)
		if strPlac:lower():find(strFind) then
			table.insert(arrPlac, strPlac)
		end
		ptrPlac:MoveNext()
	end
	table.sort(arrPlac)
	return arrPlac
end -- function placeList

--[[
	function formatCitation
	This function takes a GRO format string and table of named values, then substitutes the
	fields in the format with the corresponding named value. This implementation is better than
	the initial version that relied on value position correspondance, which is not a guarantee
	in LUA (although in practice worked).
]]
function formatCitation(strFormat, values)
	-- Starting with the format string, substitute fields by iterating their names and corresponding values
	local strOutput = strFormat

	-- iterate the named fields
	for key, value in pairs(namedFields) do
		-- Need to check for missed input fields due to programming error rather than user error, treat as empty string
		local subst = values[value]
		if subst == nil then
			subst = ""
		end

		-- Do a substitution for each named field with the corresponding named input value
		strOutput = string.gsub(strOutput, "{" .. value .. "}", subst)
	end
	return strOutput
end

--[[	function getQuarterAndMonth()
			This function returns the quarter and month based on the Quarter selection
]]
function getQuarterAndMonth()
	if listQuarter.value == "1" then
		return "Q1", "Jan-Feb-Mar"
	elseif listQuarter.value == "2" then
		return "Q2", "Apr-May-Jun"
	elseif listQuarter.value == "3" then
		return "Q3", "Jul-Aug-Sep"
	elseif listQuarter.value == "4" then
		return "Q4", "Oct-Nov-Dec"
	elseif listQuarter.value == "5" then
		return "Q1", "Jan"
	elseif listQuarter.value == "6" then
		return "Q1", "Feb"
	elseif listQuarter.value == "7" then
		return "Q1", "Mar"
	elseif listQuarter.value == "8" then
		return "Q2", "Apr"
	elseif listQuarter.value == "9" then
		return "Q2", "May"
	elseif listQuarter.value == "10" then
		return "Q2", "Jun"
	elseif listQuarter.value == "11" then
		return "Q3", "Jul"
	elseif listQuarter.value == "12" then
		return "Q3", "Aug"
	elseif listQuarter.value == "13" then
		return "Q3", "Sep"
	elseif listQuarter.value == "14" then
		return "Q4", "Oct"
	elseif listQuarter.value == "15" then
		return "Q4", "Nov"
	elseif listQuarter.value == "16" then
		return "Q4", "Dec"
	else
		return "", ""
	end
end -- getQuarterAndMonth

--[[
	fuction TableToString
	A function to convert format definition table to a safe string for output
]]
function FormatsAsString(formatTable)
	strTable = "{"
	-- iterate the outermost table BIRTH/MARRIAGE/DEATH
	for keyBMD, valBMD in pairs(formatTable) do
		strTable = strTable .. keyBMD .. " = {"
		-- Iterate the arbitrary list of formats defined for each BMD option
		for defn = 1, #valBMD do
			-- Iterate the DISPLAY/FUNCTION/PERIOD for each format
			strTable = strTable .. "{"
			for keyFmt, valFmt in pairs(valBMD[defn]) do
				if keyFmt == "PERIOD" then
					strTable = strTable .. keyFmt .. " = {"
					for periodKey, periodVal in pairs(valBMD[defn].PERIOD) do
						strTable = strTable .. periodKey .. " = " .. string.format("%q", periodVal) .. ", "
					end
					strTable = strTable .. "}, "
				else
					-- Ensure the string for Display/Format is correctly escaped and quoted
					strTable = strTable .. keyFmt .. " = " .. string.format("%q", valFmt) .. ", "
				end
			end
			strTable = strTable .. "}, "
		end
		strTable = strTable .. "}, "
	end

	strTable = strTable .. "}"

	return strTable
end -- TableToString

-- Function to validate that the input represents a Year
function ValidYear(year)
	local aDate = fhNewDate()
	return aDate:SetValueAsText(year)
end -- ValidYear

function ValidDate(date)
	local aDate = fhNewDate()
	return aDate:SetValueAsText(date)
end -- ValidateDate

function InPeriod(date, from, to)
	local cmpLower = -1 -- Assume before lower bound
	local cmpUpper = 1 -- Assume after upper bound

	-- Compare date to lower bound
	if from ~= nil then
		cmpLower = date:Compare(from)
	else
		-- Treat as equal to lower
		cmpLower = 0
	end
	-- Compare date to upper bound
	if to ~= nil then
		cmpUpper = date:Compare(to)
	else
		-- Treat as equal to upper
		cmpUpper = 0
	end

	return ((cmpLower == 0) or (cmpLower == 1)) and ((cmpUpper == 0) or (cmpUpper == -1))
end -- inPeriod

function doError(strMessage)
	error("\n\n" .. strMessage .. " - Plugin Aborted.\n\n", 0)
end -- function doError

fhInitialise(7,0,0,"save_recommended")

main()

Source:GRO-Source-Reference-2.fh_lua