Simple Birth Certificate.fh_lua

--[[
@Title: Simple Birth Certificate 
@Type: Standard
@Author: Norman Martin
@Version: 1.1 
@Keywords: 
@LastUpdated: 26 April 2024
@Licence: This plugin is copyright (c) Norman Martin 2024 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: Designed to work with 'Civil Registration Certificate' Source Template from the Essentials collection,
it will record the details from a birth certificate, for the current individual, based on the format of the 
England and Wales Birth Certificate since 1837.

Where the information is provided, it will record the occupation of the father and details of the informant.

For further support please post in the appropriate Family Historian User Group forums (FHUG) topic or a reply at the end of this download page, 
where the plugin author will answer your questions which can be supplemented with screenshots if necessary.

@changes:
1.1 April 2024 Amended Help and Support Options
		Added Vertical Scroll bar for certificate details to cater for different screen resolutions.
		Auto resize dialog to fit display resolution (avoid need to resize form)
1.0 June 2023 Version used for personal use
]]

local help=[[
This plugin records a Birth Certificate based on the format of the England and Wales Birth Certificate since 1837.
It also creates of birth and father's occupation facts. To ensure consistency, it uses the Civil Registration Certificate Template
for the source.

Prerequisites

Before it can be used, the "Civil Registration Certificate" template from the Essentials collection
must have already been added to the project. This can be done by selecting Tools>Source Template Definitions 
and then highlight the template(s) and add to project.
This plugin does NOT create any individuals. Before it is called, It requires that a person is first selected.

To avoid replication of input, the following fields are saved to be displayed next time the plugin is used:
Region, Repository, Assessment, Image Directory and Media Directory.
The Region is a dropdown list of Regions declared in the source template; Repository is a dropdown list of Repositories you have set up;
and Assessment is a dropdown list of valid assessments for the source.
Image Directory is the windows directory where the birth certificate image can be found. It can be populated by typing the directory
name in manually, or found by pressing the 'Image Directory' button to the left of the field and showing a Windows Folder Dialog screen.
Media Directory is where the image of the Birth Certificate will be saved. It can be populated in the same method for the
Image Directory. Note you can create the appropriate media directory in the Windows Folder Dialog screen.

Form Fields

Region (Required):- this is a list of Regions declared in the source template
Repository	: - this is a list of Repositories you have already set up
Registration District (Required)	:   Place where birth registered - either select from drop down for existing place or type new place
which will be added as a new place
Entry Number: - Entry number of certificate (if known)
Date of Birth (Required): - Date of birth
Address: - Address where born (if known)
Where Born (Required): - Place  where born  
Name Recorded (Required): -Pre selected on loading form - can be amended
Sex (Required): - Pre selected on loading form - can be amended
Name and Surname of Father: -Pre selected on loading form - can be amended
Name and Maiden Name of Mother (Required):  -Pre selected on loading form - can be amended
Rank or Profession of Father: Occupation of father (if known)
Name of Informant (Required): - Name of person registering death
Qualification of Informant: - qualification of person registering death
Residence of Informant: - address of informant  - free text
When Registered (Required): - date birth registered
Registrar (Required): - Name of registrar
Assessment : - Dropdown list of valid assessments
Image Directory : - Directory to initiate search for image of certificate 
Media Directory (Required): - Directory where image will be saved
As Source : - Dropdown list to determine name given to file when saved in the media directory
              Options are As Source - filename same as source title
                          As Filename - filename same as original filename
                         My Choice - type your own filename in Image File field
Media : - Button to invoke windows file dialog to identify certificate image to be copied
Image File (Required): - name of media file to be created
Status :	 - status of form - e.g. errors or certificate saved

When you press the save button, the form is validated and any errors reported in the status bar.
If the action is successful, then the appropriate message is shown in that status bar.

When the certificate is saved, the ini file is updated to retain the Region, Repository, Assessment, Image Directory
and Media Directory fields.

Saving a Certificate will create a source record for the birth certificate and create a formatted 'text from source'
and a link to a newly created media record. The source record is linked to the Birth Fact. 
The text written to the 'text from source' contains the values derived from the form as you have entered them. 

This text can later be amended in the Citation record in Family Historian to correct any textual errors.

If the information has been provided, then the father's occupation fact is created and linked to the source record. 


You will need to press the Exit button, after saving, to exit the form and return back to Family Historian.
]]


-- << Comment in the next 2 lines if you need to use IUP >>
require("iuplua");
iup.SetGlobal("CUSTOMQUITMESSAGE","YES");
fh = require("fhUtils");
fhfu =require("fhFileUtils")

fhInitialise(7);  -- implies users will need version 7 as a minimum; change if that is not the case

local plugIn="Simple Birth Certificate" -- plugin name
local hasUsed="No"							-- variable to decide to show welcome screen
local dlgHelp
local iniFile=""
local templateID=-1
local currPtr = fhNewItemPtr()
local fatherPtr = fhNewItemPtr()
local motherPtr = fhNewItemPtr()
local curPerson=""
local curDates=""
local ptrTemplate=fhNewItemPtr()
local ptrRepos=fhNewItemPtr()
local region=""		
local type="Birth"
local assessment=""
local address=""	
local source=-1
local haveCitation=false
local Places={}
local Families={}
local selFile=""

-------------------------------------------------------
-- Define Dialog Fields
-------------------------------------------------------
local color = {error = '255 200 200',red = '255 0 0',green = '0 128 0'}

local lst_Region  = iup.list{name='Region',expand='HORIZONTAL', dropdown="YES"}
local lst_Repos = iup.list{name='Repository',expand='HORIZONTAL', dropdown="YES"}
local txt_Name = iup.text{name='Name',expand='HORIZONTAL' }
local dte_Birthdate = iup.datepick{name='BirthDate',expand='HORIZONTAL'}
local dte_Regdate = iup.datepick{name='RegistrationDate',expand='HORIZONTAL' }
local txt_Sex = iup.text{name='Sex',expand='HORIZONTAL' }
local txt_Father = iup.text{name='Father',expand='HORIZONTAL' }
local txt_Mother = iup.text{name='Mother',expand='HORIZONTAL' }
local txt_Occupation = iup.text{name='Occupation',expand='HORIZONTAL' }
local lst_RegDistrict  = iup.list{name='RegDistrict',expand='HORIZONTAL',editbox='YES', dropdown="NO",visiblelines=2}
local lst_BirthPlace  = iup.list{name='BirthPlace',expand='HORIZONTAL',editbox='YES', dropdown="NO",visiblelines=2}
local txt_Informant  = iup.text{name='Informant',expand='HORIZONTAL'}
local lst_InformID=iup.list{name='InformID',"name only",value=1; dropdown="YES"}
local txt_Qualification = iup.text{name='Qualification',expand='HORIZONTAL' }
local txt_InformAddress = iup.text{name='InformAddress',expand='HORIZONTAL' }
local txt_Address = iup.text{name='Address',expand='HORIZONTAL' }
local lst_InformPlace  = iup.list{name='InformPlace',expand='HORIZONTAL',editbox='YES', dropdown="NO",visiblelines=2}
local txt_Reference = iup.text{name='Reference',expand='HORIZONTAL' }
local lst_Assessment  = iup.list{name='Assessment',expand='HORIZONTAL', dropdown="YES"}
local txt_Registrar = iup.text{name='Notes',expand='HORIZONTAL'}
local lab_message = iup.label{title=' ',fgcolor=color.red, expand='HORIZONTAL'}

local inp_img_dir = iup.text{name='ImageDir',expand='HORIZONTAL' }
local inp_Media_dir  = iup.text{name='MediaDir',  expand='HORIZONTAL', }
local inp_name  = iup.text{Name='Filter',  expand='HORIZONTAL',readonly="YES"  }
local inp_file_list  = iup.label{name='FileList',expand="HORIZONTAL", }
local lst_name  = iup.list{name='Filename',"As Source","As Filename","My Choice",value=1;  dropdown="YES"}
local lab_Mess = iup.label{title='Please select a file to select the image to copy ',fgcolor=color.blue, expand='HORIZONTAL'}

local bt_Save = iup.button{name='save', title='Save'}
local btn_Cancel = iup.button{name='cancel', title='Cancel'}
local btn_Media = iup.button{name='source', title='Media'}
local btn_help = iup.button {name='help', title='Help'}
local btn_mediaDir=iup.button{name='MediaDir', title='Media Directory>>'} 
local btn_imageDir=iup.button{name='ImageDir', title='Image Directory>>'} 
local info_Mess = iup.label{title='---------------Birth Certificate Details-------------------',fgcolor=color.blue, expand='HORIZONTAL'}
local image_Mess = iup.label{title='----------------Birth Certificate File--------------------',fgcolor=color.blue, expand='HORIZONTAL'}



function main()
local ptrFamily=fhNewItemPtr();
local iniDir=fhGetContextInfo("CI_PROJECT_DATA_FOLDER").."\\Plugin Data\\"
	-- check that plugin data folder exists
	if not fhfu.folderExists(iniDir) then
		fhfu.createFolder (iniDir)
	end

 	iniFile=fhGetContextInfo("CI_PROJECT_DATA_FOLDER").."\\Plugin Data\\"..fhGetContextInfo("CI_PLUGIN_NAME")..".dat"
	
	hasUsed=fhGetIniFileValue(iniFile, "Defaults", "Welcome", "text") 

	-- do I need to display welcome screen
	if hasUsed~="Yes" then
			Welcome()
	end

	templateID=FindTemplate('Civil Registration Certificate')

	-- check that template exists
	if templateID~=-1 then
		--Try to get the Current Individual, Can't continue if not selected
		currPtr=fh.getCurrentIndividual ()
		if currPtr:IsNotNull() then
			curPerson=fhGetItemText(currPtr,'INDI.NAME')
			curDates=fhGetItemText(currPtr,'INDI.BIRT.DATE')..' - '..fhGetItemText(currPtr,'INDI.DEAT.DATE')
			-- get Parents

		ptrFamily:MoveTo(currPtr, "INDI.FAMC")
		if ptrFamily:IsNull() then
			fhMessageBox(curPerson.." has no parents")
		else
			ptrFamily = fhGetValueAsLink(ptrFamily)
			fatherPtr=fhGetValueAsLink(fhGetItemPtr(ptrFamily,'~.HUSB'))
			motherPtr=fhGetValueAsLink(fhGetItemPtr(ptrFamily,'~.WIFE'))
		end
			
			-- show dialog screen
			MainDialog()
		else
 			fhMessageBox("No person selected")
		end		
	else
 		fhMessageBox("Requires Civil Registration Index source template from the Essentials collection")
	end


end

function lst_name:action(t,i,v)

	if i==3 then
		inp_name.readonly="NO"
	else	
		inp_name.readonly="YES"
	end
  ShowFilename()
end


-------------------------------------------------------
-- identify media record
-------------------------------------------------------
function btn_Media:action() 
local i=lst_name.value
    res=self.name
		-- validate form
    selFile=ListFiles(inp_img_dir.value)
		inp_file_list.title = selFile
  		ShowFilename()
end

-------------------------------------------------------
-- Select media directory from window dialog
-------------------------------------------------------
function btn_mediaDir:action()
local dirName=""

    res=self.name
		-- get directory name
		dirName=ListDir(true)
		if dirName~="" then
			inp_Media_dir.value=dirName
		end
end

-------------------------------------------------------
-- Select image directory from window dialog
-------------------------------------------------------
function btn_imageDir:action()
local dirName=""

    res=self.name
		-- get directory name
		dirName=ListDir(false)
		if dirName~="" then
			inp_img_dir.value=dirName
		end
end

function btn_Cancel:action() 
local exit=true
	if btn_Cancel.title=="Cancel" then
		res = fhMessageBox("Do you wish to cancel saving this certificate?", "MB_YESNO", "MB_ICONQUESTION")
		if res == "No" then 
    		exit=false
			dlg:show()
		end 
	end

	if exit then 
		return iup.CLOSE  
	end

end

-------------------------------------------------------
-- show help screen
-------------------------------------------------------
function btn_help:action()
	Help()
end



-------------------------------------------------------
-- validate dialog and close screen if valid 
-------------------------------------------------------
function bt_Save:action() 
	-- validate
	lab_message.title = ''
		-- validate form
		if lst_Region.valuestring=='' then
			lab_message.title = 'No region selected'
			lst_Region.bgcolor = color.error
		elseif lst_RegDistrict.valuestring=='' then
			lab_message.title = 'No registration district selected'
			lst_RegDistrict.bgcolor = color.error
		elseif dte_Birthdate.value=='' then
			lab_message.title = 'No no birth date stated'
			dte_Birthdate.bgcolor = color.error
		elseif txt_Name.title=='' then
			lab_message.title = 'No Name selected'
			txt_Name.bgcolor = color.error
		elseif txt_Sex.value=='' then
			lab_message.title = 'No sex stated'
			txt_Sex.bgcolor = color.error
		elseif txt_Mother.value=='' then
			lab_message.title = 'No mother selected'
			txt_Mother.bgcolor = color.error
		elseif txt_Informant.vale=='' then
			lab_message.title = 'No informant selected'
			txt_Informant.bgcolor = color.error
		elseif dte_Regdate.vale=='' then
			lab_message.title = 'No registration Date'
			dte_Regdate.bgcolor = color.error
		elseif txt_Registrar.value=='' then
			lab_message.title = 'No registrar selected'
			txt_Registrar.bgcolor = color.error
		elseif inp_Media_dir.value=='' then
			lab_message.title = 'Please provide a media directory to copy file to'
			inp_Media_dir.bgcolor = color.error
		elseif inp_file_list.title=='' then
			lab_message.title = 'No media file selected'
			inp_file_list.bgcolor = color.error
		elseif inp_name.value=='' and inp_name.readonly=="NO" then
			lab_message.title = 'No filename for image file'
		elseif selFile=='' and selLink==0 then
			lab_message.title = 'No image selected'
		else
			SaveEntry()	
		end

end


-- << insert additional functions here >>
----------------------------------------------------
-- Function: MainDialog - display dialog
--           This is the Main Dialog Screen
---------------------------------------------------
function MainDialog()
-------------------------------------------------------
-- Dialog field actions 
-------------------------------------------------------

		dlg=CreateDialog()
		InitialiseDialog()

		dlg:show()      -- Show the dialog
		-- size the dialog screen appropriately
		dlg.size=ResizeDialog(dlg)
		iup.MainLoop()  -- Process the dialog

		dlg:destroy()   -- Clean up the dialog, note all dialog controls are destroyed as well

end

----------------------------------------------------
-- Function: CreateDialog - 
--           Creates the dialog form
---------------------------------------------------
function CreateDialog()

			dlg = iup.dialog{ 
      			iup.vbox{ 
          			iup.hbox{iup.label{title='Region',size='140'},lst_Region},
           		iup.hbox{iup.label{title='Repository',size='140'},lst_Repos},
              	iup.hbox{iup.label{title='',size='140'},info_Mess},
							iup.scrollbox{ 
								iup.vbox{ 										
                     iup.hbox{iup.label{title='Registration District',size='140'}, lst_RegDistrict},
 										iup.hbox{iup.label{title='Entry Number',size='140'},txt_Reference},
                     iup.hbox{iup.label{title='Date of Birth',size='140'},dte_Birthdate},
 										iup.hbox{iup.label{title='Address',size='140'},txt_Address},
                     iup.hbox{iup.label{title='Where Born',size='140'}, lst_BirthPlace},
                     iup.hbox{iup.label{title='Name Recorded',size='140'},txt_Name},
 										iup.hbox{iup.label{title='Sex',size='140'},txt_Sex},
 										iup.hbox{iup.label{title='Name and Surname of Father',size='140'},txt_Father},
 										iup.hbox{iup.label{title='Name and Maiden Name of Mother',size='140'},txt_Mother},
 										iup.hbox{iup.label{title='Rank or Profession of Father',size='140'},txt_Occupation},
                     iup.hbox{iup.label{title='Name of Informant',size='90'},lst_InformID, txt_Informant},
                     iup.hbox{iup.label{title='Qualification of Informant',size='140'}, txt_Qualification},
                     iup.hbox{iup.label{title='Residence of Informant',size='140'}, txt_InformAddress},
                     iup.hbox{iup.label{title='Place',size='140'}, lst_InformPlace},
                     iup.hbox{iup.label{title='When Registered',size='140'},dte_Regdate},
 										iup.hbox{iup.label{title='Registrar',size='140'},txt_Registrar},
                     iup.hbox{iup.label{title='Assessment',size='140'}, lst_Assessment},
        						},
      						},
               iup.hbox{iup.label{title='',size='140'},image_Mess},
               iup.hbox{btn_imageDir,iup.label{title='',size='1'},inp_img_dir},
               iup.hbox{btn_mediaDir,iup.label{title='',size='1'},inp_Media_dir},
							iup.hbox{iup.label{title=''},lst_name,size='70',inp_name,gap='25'},
							iup.hbox{btn_Media,lab_Mess},
               iup.hbox{iup.label{title='Image File',size='70'}, inp_file_list},
               iup.label{title='Status'},lab_message,
               iup.hbox{bt_Save,iup.label{title='',size='120'},btn_Cancel,iup.label{title='',size='120'},btn_help;padding='10',gap='5'}
                     ;padding='5',gap='5',nmargin='20x20'
                     },
             title='Add Birth Certificate for '..curPerson..' '..curDates,padding='10',gap='5', size='500',
             close_cb=function() res='closed' return iup.CLOSE end}

	return dlg

end

----------------------------------------------------
-- Function: InitialiseDialog - 
--           sets field values to initial settings
---------------------------------------------------
function InitialiseDialog()

						-- populate list with defaults
						PopulateList(lst_Region,GetRegions(templateID))
						PopulateList(lst_Repos,GetRepositories())
						PopulateList(lst_RegDistrict,fh.createPlaceList())
						PopulateList(lst_BirthPlace,fh.createPlaceList())
						PopulateList(lst_InformPlace,fh.createPlaceList())
						ListInformants(lst_InformID)
						lst_InformID.value=1
						ListAssessments(lst_Assessment)
						inp_file_list.title=''
						-- populate name 
						txt_Name.value=fhGetItemText(currPtr,'INDI.NAME:GIVEN_ALL')
						if fhGetItemText(currPtr,'INDI.SEX')=='Male' then
							txt_Sex.value="Boy"
						else
							txt_Sex.value="Girl"
						end
						if fatherPtr:IsNotNull() then 
							txt_Father.value=fhGetItemText(fatherPtr,'INDI.NAME')
						end
						if motherPtr:IsNotNull() then 
							txt_Mother.value=fhGetItemText(motherPtr,'INDI.NAME:GIVEN_ALL').." "..fhGetItemText(fatherPtr,'INDI.NAME:SURNAME').." formerly "..fhGetItemText(motherPtr,'INDI.NAME:SURNAME')
						end

						-- read ini file and populate accordingly
						inp_img_dir.value=fhGetIniFileValue(iniFile, "Directories", "Images", "text") 
						inp_Media_dir.value=fhGetIniFileValue(iniFile, "Directories", "Birth", "text") 
						region=fhGetIniFileValue(iniFile, "Defaults", "Region", "text","") 		
						lst_Region.valuestring=region
						repos=fhGetIniFileValue(iniFile, region, "Repository", "text",'') 			
						lst_Repos.valuestring=repos
						assessment=fhGetIniFileValue(iniFile, region, "Assessment", "text",'') 			
						lst_Assessment.valuestring=assessment


end

----------------------------------------------------
-- Function: SaveEntry - Saves  ini file settings
--           and creates citation
--           then updates events and saves citation
---------------------------------------------------
function SaveEntry()
local txt=""
local region=lst_Region.valuestring
local ptrLink =fhNewItemPtr();
local ptrBirth=fhNewItemPtr();
local ptrInfo =fhNewItemPtr();
local ptrResi =fhNewItemPtr();
local ptrTextFromSource=fhNewItemPtr();
local dtMyDate,yr

	-- save ini file settings
	fhSetIniFileValue(iniFile, "Defaults", "Region", "text",region)
	fhSetIniFileValue(iniFile,  region, "Assessment", "text",lst_Assessment.valuestring)
	fhSetIniFileValue(iniFile,  region, "Repository", "text",lst_Repos.valuestring)
	if CheckDirectory(inp_img_dir.value) then
		fhSetIniFileValue(iniFile, "Directories", "Images", "text",inp_img_dir.value)
	end
	if CheckDirectory(inp_Media_dir.value) then
		fhSetIniFileValue(iniFile, "Directories", "Birth", "text",inp_Media_dir.value)
	end

		lab_message.title = ''
		-- create a templated source record
		sourcePtr= fhCreateItem("SOUR")
		ptrLink = fhCreateItem("_SRCT", sourcePtr) 
		fhSetValueAsLink(ptrLink, ptrTemplate)
		ptrLink = fhCreateItem("TITL", sourcePtr)
		-- add fields
		AddInfo(sourcePtr,"PUBL",ref)
		AddInfo(sourcePtr, '~NM-PRINCIPAL', curPerson)
		AddInfo(sourcePtr, '~EN-TYPE', 'Birth')
		AddInfo(sourcePtr, '~EN-REGION', region)
		dtMyDate,yr=CalenderToDate(dte_Birthdate.value)  
		AddInfoAsDate(sourcePtr, '~DT-DATE', dtMyDate)
		AddInfo(sourcePtr, '~PL-LOCATION', lst_BirthPlace.value)
		AddInfo(sourcePtr, '~AD-ADDRESS', txt_Address.value)
		AddInfo(sourcePtr, '~TX-REFERENCE', txt_Reference.value)
		if lst_Repos.valuestring~=nil then
			ptrLink = fhCreateItem("~RP-REPOSITORY", sourcePtr) 
			fhSetValueAsLink(ptrLink, FindRepository(lst_Repos.valuestring))
		end
		AddInfo(sourcePtr, 'TITL', 'Birth certificate of '..fhGetItemText(sourcePtr,'~.~NM-PRINCIPAL')..', '..fhGetItemText(sourcePtr,'~.~DT-DATE'))

		-- Create text from Source
		AddTextFromSource(sourcePtr)

		-- Add Media Record
		if inp_name.value=='' then
			inp_name.value=fhGetItemText(sourcePtr,'~.TITL')
		end
		filename= SaveImageFile(inp_file_list.title,inp_Media_dir.value, inp_name.value) 
		if filename~="" then
			-- copied file , now create object
			if CreateObject(sourcePtr,filename) ==false then
				lab_message.title = 'Failed to link image to source'
			end
		else
			lab_message.title = 'Failed to copy file'
		end
		-- create / update birth event
		ptrBirth=fhGetItemPtr(currPtr,'~.BIRT')
		if ptrBirth:IsNull() then
			-- create Birth Event
			ptrBirth = fhCreateItem("BIRT", currPtr)
		end
		AddInfo(ptrBirth, 'DATE', fhGetItemText(sourcePtr,'~.~DT-DATE'))
		AddInfo(ptrBirth, "PLAC", lst_BirthPlace.value) 
		AddInfo(ptrBirth, "ADDR", txt_Address.value)
    	ptrLink = fhCreateItem("SOUR", ptrBirth) 
		AddInfo(ptrLink, 'QUAY', lst_Assessment.valuestring)
    	fhSetValueAsLink(ptrLink, sourcePtr)

		-- add informant
			if lst_InformID.valuestring=='Name Only' then
				fh.addWitness(ptrBirth, txt_Informant.value, 'Informant')
			elseif lst_InformID.valuestring=='Mother' then
				fh.addWitness(ptrBirth, motherPtr, 'Informant')
				ptrResi= fhCreateItem("RESI", motherPtr)
				AddInfo(ptrResi, 'DATE', fhGetItemText(sourcePtr,'~.~DT-DATE'))
				AddInfo(ptrResi, "PLAC", lst_InformPlace.value) 
				AddInfo(ptrResi, "ADDR", txt_InformAddress.value)
    			ptrLink = fhCreateItem("SOUR", ptrResi) 
    			fhSetValueAsLink(ptrLink, sourcePtr)
			elseif lst_InformID.valuestring=='Father' then
				fh.addWitness(ptrBirth, fatherPtr, 'Informant')
				ptrResi= fhCreateItem("RESI", fatherPtr)
				AddInfo(ptrResi, 'DATE', fhGetItemText(sourcePtr,'~.~DT-DATE'))
				AddInfo(ptrResi, "PLAC", lst_InformPlace.value) 
				AddInfo(ptrResi, "ADDR", txt_InformAddress.value)
    			ptrLink = fhCreateItem("SOUR", ptrResi) 
    			fhSetValueAsLink(ptrLink, sourcePtr)
			end

		-- add father's occupation
		if txt_Occupation.value ~="" and fatherPtr:IsNotNull() then
			ptrOcc=fhCreateItem("OCCU", fatherPtr)
			fhSetValueAsText(ptrOcc, txt_Occupation.value)
			AddInfoAsDate(ptrOcc, "DATE", dtMyDate)
    		ptrLink = fhCreateItem("SOUR", ptrOcc) 
			AddInfo(ptrLink, 'QUAY', lst_Assessment.valuestring)
    		fhSetValueAsLink(ptrLink, sourcePtr)
		end
		if lab_message.title == '' then
			lab_message.fgcolor=color.green
			lab_message.title = 'Birth Certificate Saved'
			btn_Cancel.title="Exit"
		end
end

---------------------------------------
-- Function: CreateObject - creates a new object 
--           and links to source record
---------------------------------------
function CreateObject(SourcePtr,Filename)
local ptrObj= fhNewItemPtr()
local ptrFile= fhNewItemPtr()
local ptrLink= fhNewItemPtr()
local newFile=""

	if SourcePtr:IsNotNull() then
		-- now create object

		ptrObj= fhCreateItem("OBJE")
		ptrFile= fhCreateItem("FILE", ptrObj,true)
		if ptrFile:IsNotNull() then
			fhSetValueAsText(ptrFile,Filename)
			AddInfo(ptrFile,"FORM",PictureFormat(Filename))
			AddInfo(ptrFile,"TITL",fhGetItemText(SourcePtr,"SOUR.TITL"))
			AddInfo(ptrObj,"_DATE",fhGetItemText(SourcePtr,"SOUR.~DT-DATE"))
			AddInfo(ptrObj,"_KEYS","Picture")
			-- now link object to source
			ptrLink = fhCreateItem("OBJE", SourcePtr )             -- create a OBJE field 
			fhSetValueAsLink(ptrLink, ptrObj) 
		return true 
		end   	
	end
	return false
end

---------------------------------------
-- Function: AddTextFromSource 
--           add source text in rich text format
---------------------------------------
function AddTextFromSource(SourcePtr)
local txt='Certified Copy of an Entry of Birth\n'
local txtPtr=fhNewItemPtr();
local dtMyDate,yr=CalenderToDate(dte_Birthdate.value)  
local rt = fhNewRichText()			  -- create a new rich text object

	txt=txt..yr..' Birth in '
	txt=txt..lst_RegDistrict.value..'\n\n'
	txt=txt..'\n'

	txt=txt..' No | When and where born | Name, if any | Sex | Name, and surname of father | Name, surname and maiden name of mother | Occupation of father | Signature, description and residence of informant | When registered | Signature o'
	txt=txt..'f registrar | Name entered after registration \n'

	txt=txt..'  '
	txt=txt..txt_Reference.value..' | '
	txt=txt..dtMyDate:GetDisplayText()..'\n'..txt_Address.value..'\n'..lst_BirthPlace.value..' | '
	txt=txt..txt_Name.value..' | '
	txt=txt..txt_Sex.value..' | '
	txt=txt..txt_Father.value..' | '
	txt=txt..txt_Mother.value..' | '
	txt=txt..txt_Occupation.value..' | '
	txt=txt..txt_Informant.value..'\n'..txt_Qualification.value..'\n'..txt_InformAddress.value..'\n' ..lst_InformPlace.value..' | '
	dtMyDate,yr=CalenderToDate(dte_Regdate.value) 
	txt=txt..dtMyDate:GetDisplayText()..' | '
	txt=txt..txt_Registrar.value..' | \n'
	txt=txt..''
	txtPtr= fhCreateItem("TEXT", sourcePtr)

	rt:SetText(txt)
	fhSetValueAsRichText(txtPtr , rt)  -- set value of the note using passed in parameter

end

---------------------------------------
-- Function: AddInfo - add info to fact, updating if fact exists
---------------------------------------
function AddInfo(ptr, Type, Value)
	
	if Type and Value then
		local infoPtr=fhGetItemPtr(ptr,'~.'..Type)
		if infoPtr:IsNull() then
			infoPtr= fhCreateItem(Type, ptr)
		end
		fhSetValueAsText(infoPtr, Value)
	end
end

---------------------------------------
-- Function: AddInfoAsDate - add info to fact, updating if fact exists
---------------------------------------
function AddInfoAsDate(ptr, Type, Value)
	
	if Type and Value then
		local infoPtr=fhGetItemPtr(ptr,'~.'..Type)
		if infoPtr:IsNull() then
			infoPtr= fhCreateItem(Type, ptr)
		end
		fhSetValueAsDate(infoPtr, Value)
	end
end




---------------------------------------------------
--function FindRepository(Repository name)
-------------------------------------------------------
function FindRepository(tName)
local ptr=fhNewItemPtr();


	ptr:MoveToFirstRecord('REPO')
	while ptr:IsNotNull() do
		if fhGetItemText(ptr,'~.NAME')==tName then
			ptrRepos=ptr
			return ptr
		end
		ptr:MoveNext()
	end
	return -1
end

---------------------------------------
-- Function: GetRepositories - returns table of repositories
---------------------------------------
function GetRepositories()

local array={}
local ptrRepos = fhNewItemPtr()  -- declare pointer
local strRepos=""

	ptrRepos:MoveToFirstRecord("REPO")    -- point to the first record
	while not ptrRepos:IsNull() do
   	-- For each Repository Add the Name to the list
  	 	strRepos =fhGetItemText(ptrRepos,'REPO.NAME')
		table.insert(array,strRepos )
   	ptrRepos:MoveNext()
	end

	return array
end

---------------------------------------
-- Function: ListAssessment - 
---------------------------------------
function ListAssessments(l)
local array ={}
	
	table.insert(array,'Unreliable')
	table.insert(array,'Questionable')
	table.insert(array,'Secondary information')
	table.insert(array,'Primary information')
	table.insert(array,'')
	PopulateList(l,array)

end

---------------------------------------
-- Function: ListInformants - 
---------------------------------------
function ListInformants(l)
local array ={}
	
	table.insert(array,'Name Only')
	table.insert(array,'Mother')
	if fatherPtr:IsNotNull() then
		table.insert(array,'Father')
	end
	PopulateList(l,array)

end

---------------------------------------------------
--function FindTemplate(template name)
-------------------------------------------------------
function FindTemplate(tName)
local ptr=fhNewItemPtr();


	ptr:MoveToFirstRecord('_SRCT')
	while ptr:IsNotNull() do
		if fhGetItemText(ptr,'~.NAME')==tName then
			ptrTemplate=ptr
			return fhGetRecordId(ptr)
		end
		ptr:MoveNext()
	end
	return -1
end

---------------------------------------
-- Function: GetRegions - returns table of regions for CRI
---------------------------------------
function GetRegions(ID)

local array={}
local ptrTemplate = fhNewItemPtr()  -- declare pointer
local ptrFDef=fhNewItemPtr()
local strRegions=""

	ptrTemplate:MoveToFirstRecord("_SRCT")    -- point to the first record
	while not ptrTemplate:IsNull() do
  	 	if fhGetRecordId(ptrTemplate)==ID then
			-- loop through regions
			ptrFDef=	fhGetItemPtr(ptrTemplate,'~.FDEF')
			while not ptrFDef:IsNull() do
				if fhGetItemText(ptrFDef,'~.NAME')=='Region' then
					-- found field definiton
					strRegions =fhGetItemText(ptrFDef,'~.PROM')
					return Split(strRegions,'|')
				end
				ptrFDef:MoveNext('SAME_TAG')
			end
		end
   	ptrTemplate:MoveNext()
	end

	return array
end

-------------------------------------------------------
-- PopulateList populates a dialog list with table values
-------------------------------------------------------

function PopulateList(l, tblVals)
   local is_indexed = (rawget( tblVals, 1 ) ~= nil)
   l.REMOVEITEM = "ALL"
   if not is_indexed then
      local i=1
      for k, _ in pairs(tblVals) do
         l[tostring(i)]=k
         i=i+1
       end
   else
       for i, v in ipairs(tblVals) do
          l[tostring(i)]=v
       end	
   end
end

-------------------------------------------------------
-- ListFiles - display windows file dialog screen to select image file
--						 - passes default directory to windows dialog screen
-------------------------------------------------------

function ListFiles(defDir)
	f, err = iup.GetFile(defDir.."/".."*.jpg;*.png")
	if err == 1 then
  		iup.Message("New file", f)
	elseif err == 0 then
  	return f
	end
end

function ListDir(projectDir)
	local dataDir=""
	if projectDir then dataDir=fhGetContextInfo("CI_PROJECT_DATA_FOLDER") end
	filedlg = iup.filedlg{directory=dataDir,dialogtype = "DIR", title = "Select Directory"}
	iup.Popup(filedlg)

	-- Gets file dialog status
	status = filedlg.status

	if status == "0" or status == "1" then 
  		return filedlg.value
	end
	return ""

end

----------------------------------------------------
-- Function: ShowFilename - display new filename to copy to
---------------------------------------------------
function ShowFilename()
local path=""
local file=""
local extension=""

		if lst_name.value=="1" then
			-- display source name
			inp_name.value=selTitle
		elseif lst_name.value=="2" then
			path,file,extension = SplitFilename(selFile)
			inp_name.value=string.gsub(file,"."..extension, "")
		else
			-- allow to overwrite
		end

end

---------------------------------------
-- Function: SaveImageFile - copies source image file to media directory
--														renaming to source title if requested
--           returns filename to be written as object file
---------------------------------------
function SaveImageFile(fromFile,ToDirectory, Title) 
local path=""
local file=""
local extension=""
local toFile=""


	path,file,extension = SplitFilename(fromFile)
	if fhfu.fileExists (fromFile) then
		toFile=ToDirectory.."\\"..Title.."."..extension
		if fhfu.fileExists (toFile) then
			fhMessageBox("Source already has image added")
		else
			-- does directory exist
			if CheckDirectory(ToDirectory) then
				-- copy file to new directory changing its name
				if fhfu.copyFile (fromFile, toFile, false) then 
					return string.gsub(toFile, fhGetContextInfo("CI_PROJECT_DATA_FOLDER").."\\", "")
				end
			end
		end
	end
	return ""
end


---------------------------------------
-- Function: PictureFormat - returns format of image file
---------------------------------------
function PictureFormat(fromFile)
local path=""
local file=""
local extension=""


	path,file,extension = SplitFilename(fromFile)  
	if string.upper(extension)=='JPG' then
		return 'jpeg'
	elseif string.upper(extension)=='TIF' then
		return 'tiff'
  	end
	return extension
end

---------------------------------------
-- Function: SplitFilename - Path, Filename, and Extension as 3 values
---------------------------------------
function SplitFilename(strFilename)
  -- Returns the Path, Filename, and Extension as 3 values
  if lfs.attributes(strFilename,"mode") == "directory" then
    local strPath = strFilename:gsub("[\\/]$","")
    return strPath.."\\","",""
  end
  strFilename = strFilename.."."
  return strFilename:match("^(.-)([^\\/]-%.([^\\/%.]-))%.?$")
end

---------------------------------------
-- Function: CheckDirectory - checks that directory exits
---------------------------------------
function CheckDirectory(Directory)
	if fhfu.folderExists(Directory) then 
		return true
	else
		-- create folder
		if fhfu.createFolder(Directory) then
			return true
		end
	end
	return false
end

---------------------------------------
-- Function: Split - splits a string by delimter
--										and returns a table of strings
---------------------------------------
function Split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, trim(match));
    end
    return result;
end

---------------------------------------
-- Function: trims a sting; 
--           ensures that files do not have leading or trailing spaces
---------------------------------------
function trim(s)
  return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
end


---------------------------------------
-- Function: CalenderToDate returns a FH date from Calender date 
--           yr is year from Calender date
---------------------------------------
function CalenderToDate(dteCal)
	local dt=Split(dteCal,'/')
	return fhNewDate(dt[1],dt[2],dt[3]),tostring(dt[1])  
end

---------------------------------------
-- Function: IsToday checks Calender date for today
---------------------------------------
function IsToday(dteCal)
	return dte_Today.value==dteCal.value
end

---------------------------------------------------
--Resize Dialog
-------------------------------------------------------
function ResizeDialog(dlg)
local resize=false  -- do I need to resize
local dlgSize=dlg.naturalsize 					-- size of dialog in pixels
local dlgArea=dlg.size									-- size of dialog in size units
local screenPixels=Split(iup.GetGlobal("SCREENSIZE"),'x') 	-- width and height of screen in pixels
local dialogPixels=Split(dlgSize,'x')			-- width and height of screen in size units
local dialogSize=Split(dlgArea,'x')		 	-- width and height of dialog in pixels
local width=tonumber(dialogSize[1])			-- new width of dialog in size units - default no change
local height=tonumber(dialogSize[2])		-- new height of dialog in size units - default no change

	-- get smaller of width and height
	if tonumber(dialogPixels[1])>tonumber(screenPixels[1]) then
		-- recalculate width in size units
		width=(tonumber(screenPixels[1]) * tonumber(dialogSize[1]) / tonumber(dialogPixels[1]))
		width=math.floor(width)
		resize=true
	end

	if tonumber(dialogPixels[2])>tonumber(screenPixels[2]) then
		-- recalculate height in size units
		height=(tonumber(screenPixels[2]) * tonumber(dialogSize[2]) / tonumber(dialogPixels[2]))
		height=math.floor(height)
		resize=true
	end


	if resize then
 		-- resize (i.e. shrink) dialog, centered on the screen
		dlg.size=tostring(width).."x"..tostring(height)
		dlg:showxy(iup.CENTER, iup.CENTER)
		iup.Map(dlg)
	end
end

-------------------------------------------------------
-- Welcome Dialog
-------------------------------------------------------
local chk_hasUsed=iup.toggle{title = "Show this screen next time", value="OFF"}
local btn_OKW = iup.button{name='ok', title='OK'}

function chk_hasUsed:action(t,i,v)
		if t==1 then 
			hasUsed="No"
		else
			hasUsed="Yes"
		end
end

function btn_OKW:action() 
	res=self.name 
	return iup.CLOSE 
end

---------------------------------------------------
--Welcome Dialog
-------------------------------------------------------
function Welcome()
		hasUsed="Yes"
		txt="Thank you for using the "..plugIn .."\n\n"

		txt=txt.."To cater for different screen resolutions, the size of the form may be automatically adjusted\n"
		txt=txt.."so that the form is not larger than the screen itself. If it is resized a scroll bar\n"
		txt=txt.."will be added for the certificate details fields (showing as many fields as possible).\n\n"

		txt=txt.."Help for this plugin can be shown by pressing the help key on the main screen.\n\n"
		txt=txt.."For further support please post in the relevant Family Historian User Group forum\n"
		txt=txt.."or on the page for this plugin in the Family Historian store where the plugin author\n"
		txt=txt.."will answer your questions which can be supplemented with screenshots if necessary.\n\n"
		txt=txt.."Links to these sites are available on the help page for this plugin\n\n"


		dlgWel = iup.dialog{ 
      	iup.vbox{ 
      iup.hbox{iup.label{title=txt}},
 			iup.hbox{iup.label{title=''},chk_hasUsed},
      iup.hbox{btn_OKW;padding='10',gap='5'}
         ;padding='5',gap='5',nmargin='20x20'
 			},
      title='Welcome to '..plugIn,padding='10',gap='5', size='350',
      close_cb=function() res='closed' return iup.CLOSE end}
		dlgWel:show()      -- Show the dialog
		iup.MainLoop()  -- Process the dialog

		dlgWel:destroy()   -- Clean up the dialog, note all dialog controls are destroyed as well
		fhSetIniFileValue(iniFile, "Defaults", "Welcome", "text",hasUsed)

end

---------------------------------------------------
--Help Dialog
-------------------------------------------------------

local btn_OKH = iup.button{name='ok', title='OK',visible='YES'}

function btn_OKH:action() 
	res=self.name 
	return iup.CLOSE 
end

function CreateHelp()
	local txt_Help = iup.text{name='Help',expand='HORIZONTAL',multiline='YES',visiblelines=20}
	txt_Help.value=	help
	local txt="For further support please post in the relevant Family Historian User Group forum or on the page for this plugin in the Family Historian store\n"
		txt=txt.."where the plugin author will answer your questions which can be supplemented with screenshots if necessary."
		txt=txt.."Links to these sites are below\n\n"

	local resHelp="The plugin will adapt its size to compensate for different screen resolutions. As a result, \n"
		resHelp=resHelp.."the plugin may show a scrollbar for the Certificate details; as such not all fields will be visible.\n"

		dlgHelp = iup.dialog{ 

		iup.vbox{ 
 			iup.hbox{iup.label{title='',size='10'},txt_Help},
      iup.hbox{iup.label{title=''}},
			iup.hbox{iup.label{title=resHelp}},
			iup.hbox{iup.link{url="https://pluginstore.family-historian.co.uk/page/plugin/Simple-Birth-Certificate",title="A User Guide may be available in Family Historian Plugin store"}},
			iup.hbox{iup.label{title=txt}},
			iup.hbox{iup.link{url="https://pluginstore.family-historian.co.uk/page/plugin/Simple-Birth-Certificate",title="Family Historian Plugin store"}},
			iup.hbox{iup.link{url="https://www.fhug.org.uk/forum/viewtopic.php?t=23005",title="Family Historian User Group forums"}},
      iup.hbox{btn_OKH;padding='10',gap='5'}
         ;padding='5',gap='5',nmargin='20x20'      
 			},
      title=plugIn..' help ',padding='10',gap='5', size='500',
      close_cb=function() res='closed' return iup.CLOSE end}
			return dlgHelp
end

function Help()
-- create dialog only once
	if dlgHelp==nil then
	 	dlgHelp=CreateHelp()
	end
-- popup dialog	
	iup.Popup(dlgHelp)
end

main();



Source:Simple-Birth-Certificate-2.fh_lua