--[[ @Title: GRO BMD Index Citations @Type: Standard @Author: John Elvin @Contributors: Mike Tate @Version: 1.3.1 @Keywords: GRO @LastUpdated: 7 October 2026 @Licence: This plugin is copyright (c) 2024 John Elvin & contributors and is licensed under the MIT License which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence) @Description: Add a GRO Index Source Citation to an birth, marriage or death with GRO reference in Where Within and additional information in Text from Source; event is created or optionally updated with the date and/or place. Also has option to set birth date from Age, year of birth or date of birth from Death citation. It cannot be used outside of a project (i.e. with standalone GEDCOM). @V0.1: Loosely based on Add GRO Index Source Citation • reference is stored in Where Within Source, additional information in Text from Source • choice of which parts of event details are to be updated if they already exist • offers to set birth date from age at death or date of birth given in death index • configuration of the labels for the fields in the citation text • district name can be first part only in the citation but the whole place is used when creating/updating events • many other usability enhancements @V0.1.1 • Correct comparison of death age • Remove spurious leading space on text from source if text from source headers are blank @V0.2 • Copy existing name - surname. For a female picks up last spouse's surname (if present) • Allow year only birth date for death events @V0.2.1 • Correct creation of birth event when adding death citation • Correct substitution of ^ in text from source • Amend tag in settings for District name only to clarify function • Change way non-configured source records are handled - button(s) for associated events are disabled • Disable help button pending writing of help • Change label on Exit button to Close to match FH dialogs • Change default setting for Full month names to Off • Remove superfluous retry cancel after select individual/family dialogs - user can retry from main menu • Correct trucated labels in settings dialog on systems with different font • Clarify copying existing names by changing label and adding tips to boxes @v0.2.2 • Correct disabling of main menu buttons when source(s) not selected • Move Name(s) section of main dialog above event specific options • Allow death without Age or DOB (early FreeBMD (and others) had neither) • Move Event label (Birth, Marriage or Death) to Where Within Source, not Text from Source • Change Citation date text in settings to clarify functionality • Support for user defined fonts • Trailing space on end of italic tags to prevent last character being clipped • Move button to reset main dialog screen position from settings to main menu. Button only active if dialog moved @v0.2.3 • Change validation of source settings to make sure templated sources are always detected and highlight invalid ids without wiping them • Correct setting of Source to be used by each event • Add Source Id to Confirmation of changes dialog • Correct truncation of label in confirmation of changes dialog @0.3 • Add option to capitalise letters as keyed in Volume, District No. and Register No. • Enable keying Ctrl ' to insert Mother's surname in Mother's Maiden Name field • Death: If entered birth date (or implied birth date) is same as existing birth date disable option to update • Prevent error if using option Only first field of district and entered data begins with a comma • Correct error whereby for Death, a citation was always being added to the birth fact • Put Citation Labels and Options in Settings dialog on to tabs to reduce height of dialog and make room for more options • Move cursor to end of the name after inserting a name using CTRL ' (applies to given, surname and mother) • Add option to add a citation to the Mother's name, including adding new individual as the mother and creating the family as required. Settings has an option to set the Mother's Given name to be used when it is not known. • Allow alpha in the page number (have found entries with alpha page suffix) • Change layout of Name panel • Ctrl tab in any of the reference fields (Volume, Page etc.) will jump focus to the first field in the Name panel (Add Citation to Name) • Death - Date of Birth: Allow part date of mmm yyyy (as well as year only) • Death - Birth Date update options: change last option to 'Don't add citation' if the date matches • Some code rationalisation @0.4 • Replace iup.messagesdlg with custom function - error messages will display using the same user's font as in all the dialogs • Apply name format rule to mother's surname in the citation text from source @0.4.1 • Restructure to ensure all messages and dialogs are children of their parent dialog • Prevent a family consisting of only one individual being given a marriage citation • Give consistent titles to all message • Improve all tips displayed on mouseover • Add link to help and add button for settings help on settings dialog • Improve display of current record selection in main menu • GRO reference labels in Where Within: Don't add a space if label begins with / or - • Improve validation of DOB on death event • Ensure the main dialog is not off the top of the screen if the FH window is smaller than the main dialog and near the top of the screen • Change CTRL TAB on reference fields to jump to first Given name • Ensure & is displayed correctly in existing citations message and Places in the comparison of event details @1.0 Release to plugin store @1.1 • Add option for using a list of GRO District names: List can be maintained from settings, or from a button beside the District name on the main entry screen. Optionally, the years the district was valid can be added. These will be shown in the drop down list and the year selected will be checked against the range(s) • Add option to update name when adding Birth citation • Allow selection of quarter by keying 1, 2, 3, or 4 • Ensure & is correctly displayed in messages • Ensure & in an address is correctly displayed in existing event details dialog • Improve display of existing citations (particularly when there are multiple citations) @1.1.1 • Correct selection of quarter to prevent error if control key used @1.2 Add checking for updated version in plugin store (Check frequency configured in settings) @1.2.1 • Correct checking for existing Death citation • Append a space to all lines of the text of a message box to prevent truncation of the last character • Check for changed names when recording marriages and deaths and offer to amend • Ensure & is correctly displayed in the Data Updates pane of Confirmation of Changes dialog • Enable delete key on district to clear selection @1.3 Add option which sets the Living flag when recording a birth where the individual is less than the maximum living age defined in the settings @1.3.1 Disables use of fhGetAppVersion when run in Family Historian 8.0.1. This means the plugin will not use the user preferred font. ]] ------------------------------------------------------------------------------------------------------------------------ function GetAppVersion() -- Gets FH version as a string local iV1, iV2, iV3 = fhGetAppVersion() return iV1 .. '.' .. iV2 .. '.' .. iV3 end ------------------------------------------------------------------------------------------------------------------------ Version = '1.3.1' fhInitialise(7,0,0,'save_recommended') fhfu = require('fhFileUtils') fhu = require('fhutils') if GetAppVersion() ~= '8.0.1' then fhu.setIupDefaults() else -- Everything that setIupDefaults does except the custom font iup.SetGlobal("CUSTOMQUITMESSAGE", "YES") iup.SetGlobal("UTF8MODE", "YES") iup.SetGlobal("UTF8MODE_FILE", "YES") --use UTF8 file names fhSetStringEncoding("UTF-8") end require('iupluaimglib') require('luacom') iup.SetGlobal('IMAGESTOCKSIZE', 32) function main() if fhGetContextInfo('CI_APP_MODE') ~= 'Project Mode' then OkOnlyMessage(fhGetContextInfo('CI_PLUGIN_NAME'), 'This plugin does not support running in Gedcom Mode', 'ERROR') return end Initialise() function CheckSource(SourceId) -- Check source exists and is not templated if SourceId == 0 then return false end local ptrSource = fhNewItemPtr() ptrSource:MoveToRecordById('SOUR', SourceId) if ptrSource:IsNull() then return false end local ptrTemplate = fhGetItemPtr(ptrSource, '~._SRCT>') if ptrTemplate:IsNotNull() then return false end return true end -- function CheckSource function SetSource(SourceId) gtblData.id = SourceId gtblData.ptr:MoveToRecordById('SOUR', SourceId) gtblData.name = fhGetDisplayText(gtblData.ptr) end repeat local Exit = false local selMain = Menu() if selMain == 1 then Exit = MainDialog('Birth', gtblData.arrInd1Details, nil, nil, gtblData.FirstYear, gtblData.LastYear) end if selMain == 2 then Exit = MainDialog('Marriage', gtblData.arrInd1Details, gtblData.arrInd2Details, gtblData.arrFamilyDetails, gtblData.FirstYear, gtblData.LastYear) end if selMain == 3 then Exit = MainDialog('Death', gtblData.arrInd1Details, nil, nil, gtblData.FirstYear, gtblData.LastYear) end if selMain == 9 then config() end if Exit then -- Task complete ar Exit selected return end until not selMain or selMain == 0 end -- function Main --====================================================================================================================== function Menu() -- Main menu -- Get display name of current record (individual or family) local ptrList = fhGetCurrentRecordSel("INDI") local strCurrent if #ptrList == 1 then strCurrent = fhIndGetName(ptrList[1], false, false) elseif #ptrList > 1 then strCurrent = '' else local ptrList = fhGetCurrentRecordSel('FAM') if #ptrList ==1 then strCurrent = 'Family ' .. fhGetItemText(ptrList[1], '~'):sub(4) elseif #ptrList > 1 then strCurrent = '' else ptrList = fhGetCurrentRecordSel() if #ptrList == 0 then strCurrent = '' else if #ptrList ==1 then strCurrent = fhGetTypeInfo(ptrList[1], 'label') .. ': ' .. fhGetItemText(ptrList[1], '~') else strCurrent = '' end end end end local lblCurrentRecord = iup.flatlabel{title = strCurrent, textwrap = 'YES', size= '180x18', expand = 'VERTICAL'} lblCurrentRecord.fontstyle = 'Bold' local hboxCurrentRecord = iup.hbox{ iup.label{title = 'Current Record:'}, lblCurrentRecord; gap = 4, margin = '0x0', alignment = 'ATOP' } local selection -- Main actions buttons -- Birth local btnBirth = iup.button{ title = 'Birth Citation', padding = '10x3'} if not CheckSource(gtblSettings.SourceIdBirth) then btnBirth.active = 'NO' end -- Marriage local btnMarriage = iup.button{ title = 'Marriage Citation', padding = '10x3'} if not CheckSource(gtblSettings.SourceIdMarriage) then btnMarriage.active = 'NO' end -- Death local btnDeath = iup.button{ title = 'Death Citation', padding = '10x3' } if not CheckSource(gtblSettings.SourceIdDeath) then btnDeath.active = 'NO' end local btnScreenPosReset = iup.button{ title = 'Reset Dialog Position', padding = '16x0', tip = 'Reset position of the citation dialog to\ncentered on Family Historian Window', } if gtblSettings.ScreenPosX == iup.CENTERPARENT and gtblSettings.ScreenPosX == iup.CENTERPARENT then btnScreenPosReset.active = 'NO' end function btnScreenPosReset:action() gtblSettings.ScreenPosX = iup.CENTERPARENT gtblSettings.ScreenPosY = iup.CENTERPARENT SaveSettings() btnScreenPosReset.active = 'NO' -- Stop it being pressed twice end local btnConfig = iup.button{ title = 'Settings', padding = '10x3', action = function(self) selection = 9 return iup.CLOSE end } -- Footer buttons local btnHelp = iup.button{ title = 'Help',padding = '10x3', tip = 'Display plugin help page'} function btnHelp:action() fhShellExecute('https://pluginstore.family-historian.co.uk/page/help/gro-bmd-index-citations') end local btnExit = iup.button{ title = 'Close', padding = '10x3', tip = 'Exit plugin' } function btnExit:action() selection = 0 return iup.CLOSE end -- Assemble form local vboxMainForm = iup.vbox{ hboxCurrentRecord, iup.frame{ iup.vbox{ iup.hbox{ btnBirth, btnMarriage, btnDeath; normalizesize = 'BOTH', gap = 6, margin = '10x10' }, btnScreenPosReset; alignment = 'ACENTER' } }, iup.hbox{ iup.fill{}, btnConfig, btnHelp, btnExit, iup.fill{}; normalizesize = 'BOTH', margin = 'x10', gap = 50 }; gap = 10, margin = '10x10' } local dialogMenu = iup.dialog{ vboxMainForm; resize = 'No', minbox = 'No', maxbox = 'No', title = gPluginName .. ' (v' .. Version .. ')', tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1' } iup.SetAttribute(dialogMenu, 'NATIVEPARENT', fhGetContextInfo('CI_PARENT_HWND')) --******************************* Action functions for Birth, Marriage, Death buttons ******************************** function btnBirth:action() local ptrInd = GetIndividual() if ptrInd == nil then return end if ExistingCitation(ptrInd, '~.BIRT', gtblSettings.SourceIdBirth) then -- Check for existing citation to source return end local arrIndDetails = GetIndDetails(ptrInd) if arrIndDetails['LatestBirth'] < 1837 and arrIndDetails['LatestBirth'] ~= 0 then OkOnlyMessage('GRO Index Birth Citation', arrIndDetails['DispName'] .. ' was born before GRO index starts', 'WARNING', dialogMenu) return end -- Range to be used for the year drop down gtblData.FirstYear = math.max(arrIndDetails['EarliestBirth'], 1837) gtblData.LastYear = math.min(arrIndDetails['LatestBirth'], gtblConstants.CurrYear) SetSource(gtblSettings.SourceIdBirth) gtblData.arrInd1Details = arrIndDetails selection = 1 return iup.CLOSE end ------------------------------------------------------------------------------------------------------------------------ function btnMarriage:action() local ptrFamily = GetFamily() if ptrFamily == nil then return end if ExistingCitation(ptrFamily, '~.MARR', gtblSettings.SourceIdMarriage) then -- Check for existing citation to source return end -- Get details of the two individuals & predict date range for marriage local ptrInd1 = fhNewItemPtr() ptrInd1:MoveTo(ptrFamily,'~.~SPOU[1]>') if ptrInd1:IsNull() then local strMessage = 'Selected family has no parents' OkOnlyMessage('GRO Index Marriage Citation', strMessage, 'ERROR', dialogMenu) return end local arrIndDetails1 = GetIndDetails(ptrInd1) local ptrInd2 = fhNewItemPtr() local arrIndDetails2 ptrInd2:MoveTo(ptrFamily,'~.~SPOU[2]>') if ptrInd2:IsNotNull() then arrIndDetails2 = GetIndDetails(ptrInd2) else local strMessage = 'Selected family only has one parent' OkOnlyMessage('GRO Index Marriage Citation', strMessage, 'ERROR', dialogMenu) return end local arrFamDetails = {} arrFamDetails['Ptr'] = ptrFamily arrFamDetails['DispName'] = fhGetItemText(ptrFamily, '~') arrFamDetails['MarriageDatePtr'] = fhGetItemPtr(ptrFamily, '~.MARR.DATE') local EarliestYear, LatestYear if arrFamDetails['MarriageDatePtr']:IsNotNull() then local dtDate = fhGetValueAsDate(arrFamDetails['MarriageDatePtr']) local dpDate = dtDate:GetDatePt1() EarliestYear = dpDate:GetYear() dpDate = dtDate:GetDatePt2() if dpDate:IsNull() then LatestYear = EarliestYear else LatestYear = dpDate:GetYear() end elseif arrIndDetails2 ~= nil then EarliestYear = math.max(arrIndDetails1['EarliestBirth'], arrIndDetails2['EarliestBirth']) + 12 LatestYear = math.min(arrIndDetails1['LatestDeath'], arrIndDetails2['LatestDeath']) else EarliestYear = arrIndDetails1['EarliestBirth'] + 12 LatestYear = arrIndDetails1['LatestDeath'] end LatestYear = math.min(LatestYear, gtblConstants.CurrYear) if LatestYear < 1837 then OkOnlyMessage('GRO Index Marriage Citation', arrFamDetails['DispName']:sub(7) .. ' married before GRO index starts', 'WARNING', dialogMenu) return end gtblData.FirstYear = math.max(EarliestYear, 1837) gtblData.LastYear = LatestYear --return MainDialog('Marriage', arrIndDetails1, arrIndDetails2, arrFamDetails, EarliestYear, LatestYear) SetSource(gtblSettings.SourceIdMarriage) gtblData.arrInd1Details = arrIndDetails1 gtblData.arrInd2Details = arrIndDetails2 gtblData.arrFamilyDetails = arrFamDetails selection = 2 return iup.CLOSE end ------------------------------------------------------------------------------------------------------------------------ function btnDeath:action() local ptrInd = GetIndividual() if ptrInd == nil then return end if ExistingCitation(ptrInd, '~.DEAT', gtblSettings.SourceIdDeath) then -- Check for existing citation to source return end local arrIndDetails = GetIndDetails(ptrInd) if arrIndDetails['LatestDeath'] < 1837 then OkOnlyMessage('GRO Index Death Citation' , arrIndDetails['DispName'] .. ' died before GRO index starts', 'WARNING', dialogMenu) return end -- Range to be used for the year drop down gtblData.FirstYear = math.max(arrIndDetails['EarliestDeath'], 1837) gtblData.LastYear = math.min(arrIndDetails['LatestDeath'], gtblConstants.CurrYear) SetSource(gtblSettings.SourceIdDeath) gtblData.arrInd1Details = arrIndDetails selection = 3 return iup.CLOSE end -- **************************** Common routines used by functions Birth, Marriage, Death ***************************** function GetIndividual() -- Get current individual or prompt if no current local ptrList = fhGetCurrentRecordSel("INDI") while #ptrList ~= 1 do ptrList = fhPromptUserForRecordSel("INDI", 1, dialogMenu.HWND) if #ptrList ~= 1 then return nil end end return ptrList[1] end -- function GetIndividual ------------------------------------------------------------------------------------------------------------------------ function GetFamily() -- Get current family or family of current individual; if neither prompt local ptrListF = fhGetCurrentRecordSel('FAM') if #ptrListF == 1 then return ptrListF[1] -- Just one family selected end local ptrListI = {} -- Initialise arrays so test to prompt for family below doesn't fail local arrFams = {} if #ptrListF == 0 then -- No family selected ptrListI = fhGetCurrentRecordSel('INDI') if #ptrListI == 1 then -- Just one individual seleted - find their family(s) local ptrFams = fhNewItemPtr() ptrFams:MoveTo(ptrListI[1],'~.FAMS') while ptrFams:IsNotNull() do table.insert(arrFams,fhGetValueAsLink(ptrFams)) ptrFams:MoveNext('SAME_TAG') end if #arrFams == 1 then -- Individual has only one family return arrFams[1] end end end -- If multiple families selected OR not just one individual selected OR selected individual has no families prompt for selection -- Prompt for family if nothing possible seleted if #ptrListF > 1 or #ptrListI ~= 1 or #arrFams == 0 then while #ptrListF ~= 1 do ptrListF = fhPromptUserForRecordSel('FAM', 1) if #ptrListF ~= 1 then return nil end end return ptrListF[1] end -- No family selected, a single individual is selected and they have multiple families -- We already have array of families (arrFams) - get spouses for them local listFamilies = iup.list{} local ptrSpouse = fhNewItemPtr() for i = 1, #arrFams do ptrSpouse:MoveTo(ptrListI[1],'~.~SPOU[' .. i .. ']>') if ptrSpouse:IsNotNull() then listFamilies[i] = fhGetDisplayText(ptrListI[1]) .. ' and ' .. fhGetDisplayText(ptrSpouse) else listFamilies[i] = fhGetDisplayText(ptrListI[1]) .. ' and <>' end end local btnOK =iup.button{title = 'OK', padding = '10x3'} local Continue local btnCancel = iup.button{ title = 'Cancel', padding = '10x3', action = function(self) Continue = false return iup.CLOSE end } strTitle = 'GRO Index Marriage Citation' local dialogFamily = iup.dialog{ iup.vbox{ iup.label{title = fhGetDisplayText(ptrListI[1]) .. ' has multiple families\nSelect family for this marriage'; padding = '0x4', alignment = 'ACENTER:ATOP', expand = 'YES' }, listFamilies, iup.hbox{ iup.fill{}, btnOK, btnCancel, iup.fill{}; normalizesize = 'BOTH', margin = '0x0', gap = 40 }; gap = 10, margin = '10x10' }; resize = 'No', minbox = 'No', maxbox = 'No', helpbutton = 'Yes', title = strTitle, defaultesc = btnCancel, PARENTDIALOG = dialogMenu, tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1' } function btnOK:action() if listFamilies.value == "0" then OkOnlyMessage(strTitle, 'Selection of family required', 'ERROR', dialogFamily) return end Continue = true return iup.CLOSE end dialogFamily:popup(iup.CENTERPARENT, iup.CENTERPARENT) local Selection = nil if Continue then Selection = arrFams[tonumber(listFamilies.value)] end dialogFamily:destroy() return Selection end -- function GetFamily ------------------------------------------------------------------------------------------------------------------------ function GetIndDetails(ptrInd) local arrDetails = {} arrDetails['Ptr'] = ptrInd arrDetails['Id'] = fhGetRecordId(ptrInd) arrDetails['DispNameFull'] = fhIndGetName(ptrInd, true, false) arrDetails['DispName'] = fhIndGetName(ptrInd, false, false) local strNameStored =fhGetItemText(ptrInd,'INDI.NAME[1]:STORED') local strGiven if strNameStored:sub(-1) == '/' then strGiven = strNameStored:match('^[^/]*') arrDetails['Surname'] = strNameStored:match('%b//'):gsub('/','') arrDetails['NameOrder'] = 1 elseif strNameStored:sub(1, 1) == '/' then strGiven = strNameStored:match('[^/]*$') arrDetails['Surname'] = strNameStored:match('%b//'):gsub('/','') arrDetails['NameOrder'] = 2 else -- Indeterminate name structure, put in given name so user controls capitalisation strGiven = strNameStored arrDetails['Surname'] = '' arrDetails['NameOrder'] = 3 end arrDetails['GivenName'] = strGiven:match('^%s*(.-)%s*$') arrDetails['ptrMother'] = fhGetItemPtr(ptrInd,'~.FAMC>WIFE>') arrDetails['ptrFamily'] = fhGetItemPtr(ptrInd,'~.FAMC>') arrDetails['Mother'] = Capitalise(fhGetItemText(ptrInd, 'INDI.~MOTH[1]>NAME[1]:SURNAME')) arrDetails['EarliestBirth'] = fhCallBuiltInFunction('EstimatedBirthDate', ptrInd, 'EARLIEST',3):GetYear() arrDetails['LatestBirth'] = fhCallBuiltInFunction('EstimatedBirthDate', ptrInd, 'LATEST',3):GetYear() arrDetails['EarliestDeath'] = fhCallBuiltInFunction('EstimatedDeathDate', ptrInd, 'EARLIEST',3):GetYear() local LatestDeath = fhCallBuiltInFunction('EstimatedDeathDate', ptrInd, 'LATEST',3):GetYear() arrDetails['LatestDeath'] = math.max(LatestDeath, gtblConstants.CurrYear) -- Latest estimated death can be zero return arrDetails end -- function GetIndDetails ------------------------------------------------------------------------------------------------------------------------ function ExistingCitation (ptrRecord, EventRef, SourceId) -- Checks for existing citation to source -- Returns true if found and user cancels local ptrEventSource = fhNewItemPtr() local ptrEventSourceRecord = fhNewItemPtr() local ptrTemp = fhNewItemPtr() local bExisting = false local strCitations = '' ptrEventSource:MoveTo(ptrRecord, EventRef .. '.SOUR') while ptrEventSource:IsNotNull() do ptrEventSourceRecord = fhGetValueAsLink(ptrEventSource) if fhGetRecordId(ptrEventSourceRecord) == SourceId then bExisting = true strCitations = strCitations .. '\n' ptrTemp = fhGetItemPtr(ptrEventSource, '~.PAGE') if ptrTemp:IsNotNull() then strCitations = strCitations .. '\n' .. fhGetValueAsText(ptrTemp) end ptrTemp = fhGetItemPtr(ptrEventSource, '~.DATA.TEXT') if ptrTemp:IsNotNull() then strCitations = strCitations .. '\n' .. fhGetValueAsText(ptrTemp) end end ptrEventSource:MoveNext('SAME_TAG') end if bExisting then local strMessage = 'Existing ' .. GetSource(SourceId) .. ' citation(s) found:' .. strCitations .. '\n\nContinue?' if MessageBox(gPluginName, strMessage, 'OKCANCEL', 'QUESTION', 1, dialogMenu) == 2 then return true end end return false end -- *************************End of Common routines used by functions Birth, Marriage, Death *************************** dialogMenu:show() iup.MainLoop() dialogMenu:destroy() return selection end -- function Menu --====================================================================================================================== function config() -- Configuration dialog local dialogSettings -- 'forward' declaration -- Sources -- Birth local lblBirth = iup.flatlabel{title = 'Birth', size = '40x11', alignment = 'ALEFT:ACENTRE'} local txtBirthSourceId = iup.text{ visiblecolumns = 3, alignment = 'ACENTER', filter = 'NUMBER' } local lblBirthSource = iup.flatlabel{ title = ' ', border = 'YES', size = '140x11', alignment = 'ALEFT:ACENTRE', padding = '4,0' } if gtblSettings.SourceIdBirth ~= 0 then txtBirthSourceId.value = gtblSettings.SourceIdBirth local SourceName, Templated = GetSource(gtblSettings.SourceIdBirth) if SourceName ~= '' then if Templated then lblBirthSource.title = '' txtBirthSourceId.bgcolor = gtblConstants.Yellow else lblBirthSource.title = SourceName end else lblBirthSource.title = '' txtBirthSourceId.bgcolor = gtblConstants.Yellow end else lblBirthSource.title = '' end local btnBirthSource = iup.button{title = 'Select', padding = '2x0'} local hboxBirthSource = iup.hbox{ lblBirth, txtBirthSourceId, lblBirthSource, btnBirthSource, iup.fill{}; gap = 10, margin = '5x0' } -- Marriage local lblMarriage = iup.flatlabel{title = 'Marriage', size = '40x11', alignment = 'ALEFT:ACENTRE'} local txtMarriageSourceId = iup.text{ visiblecolumns = 3, alignment = 'ACENTER', filter = 'NUMBER' } local lblMarriageSource = iup.flatlabel{ title = ' ', border = 'YES', size = '140x11', alignment = 'ALEFT:ACENTRE', padding = '4,0' } if gtblSettings.SourceIdMarriage ~= 0 then txtMarriageSourceId.value = gtblSettings.SourceIdMarriage local SourceName, Templated = GetSource(gtblSettings.SourceIdMarriage) if SourceName ~= '' then if Templated then lblMarriageSource.title = 'Source ' .. txtMarriageSourceId.value .. ' is templated' txtMarriageSourceId.bgcolor = gtblConstants.Yellow else lblMarriageSource.title = SourceName end else lblMarriageSource.title = '' txtMarriageSourceId.bgcolor = gtblConstants.Yellow end else lblMarriageSource.title = '' end local btnMarriageSource = iup.button{title = 'Select', padding = '2x0'} local hboxMarriageSource = iup.hbox{ lblMarriage, txtMarriageSourceId, lblMarriageSource, btnMarriageSource, iup.fill{}; gap = 10, margin = '5x0' } -- Death local lblDeath = iup.flatlabel{title = 'Death', size = '40x11', alignment = 'ALEFT:ACENTRE'} local txtDeathSourceId = iup.text{ visiblecolumns = 3, alignment = 'ACENTER', filter = 'NUMBER' } local lblDeathSource = iup.flatlabel{ title = ' ', border = 'YES', size = '140x11', alignment = 'ALEFT:ACENTRE', padding = '4,0' } if gtblSettings.SourceIdDeath ~= 0 then txtDeathSourceId.value = gtblSettings.SourceIdDeath local SourceName, Templated = GetSource(gtblSettings.SourceIdDeath) if SourceName ~= '' then if Templated then lblDeathSource.title = 'Source ' .. txtDeathSourceId.value .. ' is templated' txtDeathSourceId.bgcolor = gtblConstants.Yellow else lblDeathSource.title = SourceName end else lblDeathSource.title = '' txtDeathSourceId.bgcolor = gtblConstants.Yellow end else lblDeathSource.title = '' end local btnDeathSource = iup.button{title = 'Select', padding = '2x0'} local hboxDeathSource = iup.hbox{ lblDeath, txtDeathSourceId, lblDeathSource, btnDeathSource, iup.fill{}; gap = 10, margin = '5x0' } local frameSources = iup.frame{ iup.vbox{ hboxBirthSource, hboxMarriageSource, hboxDeathSource; expandchildren = 'YES', gap = '2', margin = '6x3', fontstyle = '' }, title = 'Sources', fontstyle = 'Bold' } -- Citation labels -- Where Within Source Headers local txtLabelPrefixBirth = iup.text{value = gtblSettings.LabelPrefixBirth, visiblecolumns = '8'} local vboxLabelPrefixBirth = iup.vbox{iup.label{title = 'Birth'}, txtLabelPrefixBirth; gap = '2'} local txtLabelPrefixMarriage = iup.text{value = gtblSettings.LabelPrefixMarriage, visiblecolumns = '8'} local vboxLabelPrefixMarriage = iup.vbox{iup.label{title = 'Marriage'}, txtLabelPrefixMarriage; gap = '2'} local txtLabelPrefixDeath = iup.text{value = gtblSettings.LabelPrefixDeath, visiblecolumns = '8'} local vboxLabelPrefixDeath = iup.vbox{iup.label{title = 'Death'}, txtLabelPrefixDeath; gap = '2'} local lblTFSHeaders = iup.label{title = 'Where Within Source\nheaders', size = '0x0', multiline = 'yes', padding = '0x4', fgcolor = gtblConstants.Blue} local hboxCitatationLabels = iup.hbox{ iup.frame{ iup.hbox{ lblTFSHeaders, vboxLabelPrefixBirth, iup.fill{}, vboxLabelPrefixMarriage, iup.fill{}, vboxLabelPrefixDeath; margin = '5x2', alignment = 'ABOTTOM', gap = '10' }, sunken = 'YES' }, margin = '5x0' } -- GRO Reference Labels local txtLabelDistrictName = iup.text{value = gtblSettings.LabelDistrictName, visiblecolumns = '6'} local hboxLabelDistrictName = iup.hbox{iup.label{title = 'District'}, iup.fill{}, txtLabelDistrictName; alignment = 'ACENTER'} local txtLabelVolume = iup.text{value = gtblSettings.LabelVolume, visiblecolumns = '6'} local hboxLabelVolume = iup.hbox{iup.label{title = 'Volume'}, iup.fill{}, txtLabelVolume; alignment = 'ACENTER'} local txtLabelPage = iup.text{value = gtblSettings.LabelPage, visiblecolumns = '6'} local hboxLabelPage = iup.hbox{iup.label{title = 'Page'}, iup.fill{}, txtLabelPage; alignment = 'ACENTER'} local txtLabelDistrictNo = iup.text{value = gtblSettings.LabelDistrictNo, visiblecolumns = '6'} local hboxLabelDistrictNo = iup.hbox{iup.label{title = 'District No.'}, iup.fill{}, txtLabelDistrictNo; alignment = 'ACENTER'} local txtLabelRegister = iup.text{value = gtblSettings.LabelRegister, visiblecolumns = '6'} local hboxLabelRegister = iup.hbox{iup.label{title = 'Register No.'}, iup.fill{}, txtLabelRegister; alignment = 'ACENTER'} local txtLabelEntry = iup.text{value = gtblSettings.LabelEntry, visiblecolumns = '6'} local hboxLabelEntry = iup.hbox{iup.label{title = 'Entry No.'}, iup.fill{}, txtLabelEntry; alignment = 'ACENTER'} local vboxGRORef1 = iup.vbox{hboxLabelDistrictName, hboxLabelVolume, hboxLabelPage; gap = '0', margin = '2x2'} local vboxGRORef2 = iup.vbox{hboxLabelDistrictNo, hboxLabelRegister, hboxLabelEntry; gap = '0', margin = '2x2'} local lblWWSLabels = iup.flatlabel{title = 'Where within Source\nLabels', size = '0x0', multiline='yes', padding = '0x4', fgcolor = gtblConstants.Blue} local hboxGRORefLabels = iup.hbox{ iup.frame{ iup.hbox{ lblWWSLabels, vboxGRORef1, iup.fill{}, vboxGRORef2; margin = '5x2', alignment = 'ATOP', gap = '10' }, sunken = 'YES' }, margin = '5x0' } -- Event Specific Labels local txtLabelMother = iup.text{value = gtblSettings.LabelMother, expand = 'HORIZONTAL'} local hboxLabelMother = iup.hbox{ iup.label{title = 'Birth', size = '30x0'}, iup.label{title = 'Mother\'s Maiden Name'}, txtLabelMother; gap = '10', margin = '0x0', alignment = 'ACENTER' } local txtLabelAge = iup.text{value = gtblSettings.LabelAge, visiblecolumns = '6'} local hboxLabelAge = iup.hbox{iup.label{title = 'Death', size = '30x0'}, iup.label{title= 'Age'}, txtLabelAge; alignment = 'ACENTER'} local txtLabelDOB = iup.text{value = gtblSettings.LabelDOB, visiblecolumns = '6'} local hboxLabelDOB = iup.hbox{iup.label{title = 'Date of Birth'}, txtLabelDOB; alignment = 'ACENTER'} local hboxLabelsDeath = iup.hbox{hboxLabelAge, iup.fill{}, hboxLabelDOB; gap = '10', margin = '0x0'} local lblTFSEventLabels = iup.flatlabel{title = 'Text from Source\nEvent Specific Labels', size = '0x0', multiline='yes', padding = '0x4', fgcolor = gtblConstants.Blue} local hboxEventLabels = iup.hbox{ iup.frame{ iup.hbox{ lblTFSEventLabels, iup.vbox{ hboxLabelMother, iup.label{separator = 'HORIZONTAL'}, hboxLabelsDeath; gap = '4', margin = '3x0' }; margin = '6x2', alignment = 'ATOP', gap = '10' }, sunken = 'YES' }, margin = '5x0' } local vboxLabels = iup.vbox{ iup.hbox{ iup.fill{}, iup.label{ title = 'Use ^ to specify trailing spaces on labels; $ to embed data within label ', fontstyle = 'Italic', alignment= 'ACENTRE' }, iup.fill{} }, hboxCitatationLabels, hboxGRORefLabels, hboxEventLabels; expandchildren = 'YES', margin = '5x2', gap = '2', fontstyle = '', tabtitle = ' Citation Labels ' } -- Options settings function ItalicLabel (strTitle) local label = iup.label{title = strTitle} label.fontstyle = 'Italic' return label end -- function ItalicLabel local lblQtrMonthNames = iup.label{title = 'Full Month Names && Quarter Months in Citation Text'} local tglQtrMonthNames = iup.toggle{value = ToggleValue(gtblSettings.QtrMonthNames), tipballoontitle = 'Month and quarter names in citation', tip = 'OFF: Q1, Jan etc.\nON: Q1 Jan-Feb-Mar, January etc.'} local lblUCRefs = iup.label{title = 'Make alpha characters in reference upper case'} local tglUCRefs = iup.toggle{value = ToggleValue(gtblSettings.UCRefs), tipballoontitle = 'Where within source references', tip = 'Convert lower case characters in Volume,\nDistrict, and Register numbers to upper case'} local lblUseGRODistricts = iup.label{title = 'Use GRO District List'} local tglUseGRODistricts = iup.toggle{value = ToggleValue(gtblSettings.UseGRODistricts), tipballoontitle = 'Use GRO District List', tip ='Use list of GRO District Names instead of all Places in project'} local btnGRODistricts = iup.button{ title = 'Edit GRO District List', padding = '8x0', action = function(self) GRODistricts(dialogSettings) end } if not gtblSettings.UseGRODistricts then btnGRODistricts.visible = 'NO' else btnGRODistricts.visible = 'YES' end local hboxGRODistricts = iup.hbox{ tglUseGRODistricts, btnGRODistricts; gap = '8', alignment = 'ACENTER', margin = '0x0'} function tglUseGRODistricts:action(state) if state == 0 then btnGRODistricts.visible = 'NO' else btnGRODistricts.visible = 'YES' end end local lblShortPlaces = iup.label{title = 'Only first field of District Name in Citation Text'} local tglShortPlaces = iup.toggle{value = ToggleValue(gtblSettings.ShortPlaces), tipballoontitle = 'Where within source Registration District Names', tip ='The district name should be entered as a full place\n' .. 'Enable this option to use the first field only in Where Within Source'} local lblIncNameInCitation = iup.hbox{ iup.label{title ='Include Name(s) in Citation Text '}, ItalicLabel('(default) '); gap = 0, margin = '0x0' } local tglIncNameInCitation = iup.toggle{ ['3state'] = 'YES', value = gtblSettings.IncNameInCitation, tipballoontitle = 'Include name(s) in Citation Text', tip = 'This specifies the initial setting in the BMD dialogs\n' .. 'which can be changed for individual citations.\n' .. 'Options are: Always, Only if adding citation to name and Never' } local lblIncNameState = iup.label{title = ' ', size = '120x0'} local hboxIncNameInCitation = iup.hbox{tglIncNameInCitation, lblIncNameState, gap = 4, margin = '0x0'} local lblNameFormat = iup.label{title='Format of Names'} local listNameFormat = iup.list{ 'Surname, Given', 'SURNAME, Given', 'Given Surname', 'Given SURNAME', value = gtblSettings.NameFormat, dropdown = 'YES', size = '95x0', tipballoontitle = 'Format of names', tip = 'Select how names are to be capitalised in citations\n' .. 'Also applies to mother\'s maiden name' } local lblCitationForName = iup.hbox{ iup.label{title ='Add Citation to Name(s) '}, ItalicLabel('(default) '); gap = 0, margin = '0x0' } local tglCitationNameBirth = iup.toggle{value = ToggleValue(gtblSettings.CitationNameBirth), title = 'Births', tipballoontitle = 'Add Citation to Name', tip = 'This specifies the initial setting in the Birth dialog\n' .. 'which can be changed for individual citations.\n' } local tglCitationNameMarriage = iup.toggle{value = ToggleValue(gtblSettings.CitationNameMarriage), title = 'Marriages', tipballoontitle = 'Add Citation to Names', tip = 'This specifies the initial setting in the Marriage dialog\n' .. 'which can be changed for individual citations.\n' } local tglCitationNameDeath = iup.toggle{value = ToggleValue(gtblSettings.CitationNameDeath), title = 'Deaths', tipballoontitle = 'Add Citation to Name', tip = 'This specifies the initial setting in the Death dialog\n' .. 'which can be changed for individual citations.\n' } local hboxCitationNames = iup.hbox{tglCitationNameBirth,tglCitationNameMarriage,tglCitationNameDeath; margin = '0x0'} local function SetlblIncNameState() local strState if tglIncNameInCitation.value == 'ON' then strState = 'Always' elseif tglIncNameInCitation.value == 'OFF' then strState = 'Never' else strState = 'Only if adding citation to name' end lblIncNameState.title = strState end SetlblIncNameState() function tglCitationNameBirth:action() if not ValidateCitationToName() then tglCitationNameBirth.value = 'OFF' end end function tglCitationNameMarriage:action() if not ValidateCitationToName() then tglCitationNameMarriage.value = 'OFF' end end function tglCitationNameDeath:action() if not ValidateCitationToName() then tglCitationNameDeath.value = 'OFF' end end local lblCitationToMotherDefault = iup.hbox{ iup.label{title ='Add Birth Citation to Mother\'s Name '}, ItalicLabel('(default) '); gap = 0, margin = '0x0' } local tglCitationToMotherDefault = iup.toggle{value = ToggleValue(gtblSettings.CitationToMotherDefault), tipballoontitle= 'Add Birth Citation to Mother\'s Name', tip = 'This specifies the initial setting in the Birth dialog\n' .. 'which can be changed for individual citations.\n' } local lblNewMotherGivenName = iup.label{title = 'Given name for new Birth Mother records'} local txtNewMotherGivenName = iup.text{ value = gtblSettings.NewMotherGivenName, visiblecolumns = '8', tipballoontitle = 'Given name for new Birth Mother records', tip = 'If Add Birth Citation to Mother\'s Name is on and the Mother\'s record\n' .. 'is to be created (or existing record has no name)\n' .. 'this name will be combined with the entered Surname' } local lblAddLivingFlag = iup.label{title = 'Set Living for recent births'} local tglAddLivingFlag = iup.toggle{ value = ToggleValue(gtblSettings.AddLivingFlag), tipballoontitle = 'Set Living for recent births', tip = 'Set the Living flag when recording a birth if the individual\n' .. 'is less than the selected maximum age and no death or burial recorded' } local lblLivingMaxAge = iup.label{title = 'Maximum living age'} local txtLivingMaxAge = iup.text{ visiblecolumns = 3, alignment = 'ACENTER', filter = 'NUMBER', value = gtblSettings.LivingMaxAge } local hboxLivingFlg = iup.hbox{ tglAddLivingFlag, lblLivingMaxAge, txtLivingMaxAge; gap = '8', alignment = 'ACENTER', margin = '0x0'} if not gtblSettings.AddLivingFlag then lblLivingMaxAge.active = 'NO' txtLivingMaxAge.active = 'NO' end function tglAddLivingFlag:action(state) if state == 0 then lblLivingMaxAge.active = 'NO' txtLivingMaxAge.active = 'NO' else lblLivingMaxAge.active = 'YES' txtLivingMaxAge.active = 'YES' end end function txtLivingMaxAge:valuechanged_cb() txtLivingMaxAge.bgcolor = gtblConstants.White end function txtLivingMaxAge:killfocus_cb() if txtLivingMaxAge.value == '' or tonumber(txtLivingMaxAge.value) < 75 or tonumber(txtLivingMaxAge.value) > 125 then txtLivingMaxAge.bgcolor = gtblConstants.Yellow OkOnlyMessage(gPluginName, 'Maximum living age out of range (75-125)', 'ERROR', dialogSettings) end end local lblCitationEntryDate = iup.label{title = 'Citation Date'} local listCitationEntryDate = iup.list{ 'No Date', 'Current Date', 'GRO Index Reg. Date', value = gtblSettings.CitationEntryDate, dropdown = 'YES', size = '95x0', tipballoontitle = 'Citation Date', tip = 'Date to be recorded in the Citation Specific Date' } local lblCitationAssess = iup.hbox{ iup.label{title ='Citation Assessment '}, ItalicLabel('(default) '); gap = 0, margin = '0x0' } local listCitationAssess = iup.list{ dropdown = 'YES', size = '95x0', visibleitems = 6, tipballoontitle = 'Citation Assessment', tip = 'This specifies the initial setting in the BMD dialogs\n' .. 'which can be changed for individual citations.\n' } PopulateList(listCitationAssess, gtblConstants.CitationAssess) listCitationAssess.value = gtblSettings.CitationAssess local gridboxOptions = iup.gridbox{ lblQtrMonthNames, tglQtrMonthNames, lblUCRefs, tglUCRefs, lblUseGRODistricts, hboxGRODistricts, lblShortPlaces, tglShortPlaces, lblIncNameInCitation, hboxIncNameInCitation, lblNameFormat, listNameFormat, lblCitationForName, hboxCitationNames, lblCitationToMotherDefault, tglCitationToMotherDefault, lblNewMotherGivenName, txtNewMotherGivenName, lblAddLivingFlag, hboxLivingFlg, lblCitationEntryDate, listCitationEntryDate, lblCitationAssess, listCitationAssess; numdiv = 2, sizelin = -1, sizecol = -1, gapcol = 32, gaplin = 6, alignmentlin = 'ACENTER', padding = '0x0' -- Set for inheritance } local hboxOptions = iup.hbox{ iup.frame{ iup.hbox{ gridboxOptions; expandchildren = 'YES', margin = '3x2', fontstyle = '' }, sunken = 'YES', }; margin = '5x2', tabtitle = ' Options ' } -- Update Checks function FormatYMDdate(Date) -- Date yyyy-mm-dd return os.date('%d %b %Y',os.time(YMDtoTable(Date))) end local lblAutoCheck = iup.label{title = 'Auto Check for Plugin Updates'} local tglAutoCheck = iup.toggle{} if gtblSettings.VersionCheckInterval ~= 0 then tglAutoCheck.value = 'ON' end local lblCheckFrequency = iup.label{title = 'Auto Check Frequency'} local i for k, v in pairs(gtblConstants.VersionCheckFrequency) do if gtblSettings.VersionCheckInterval == v then i = k break end end local listCheckFrequency = iup.list{ 'Daily', 'Weekly', 'Fortnightly', 'Four Weekly', dropdown = 'YES', value = i } if gtblSettings.VersionCheckInterval == 0 then lblCheckFrequency.active = 'NO' listCheckFrequency.active = 'NO' end local lblLastCheck = iup.label{title = 'Last Auto Check Date'} local txtLastCheck = iup.text{ value = FormatYMDdate(gtblSettings.VersionCheckedDate), visiblecolumns = 8, readonly = 'YES', canfocus = 'NO' } local lblNextCheck = iup.label{title = 'Next Auto Check Date'} local txtNextCheck = iup.text{ visiblecolumns = 8, readonly = 'YES', canfocus = 'NO' } if gtblSettings.VersionCheckInterval ~= 0 then txtNextCheck.value = FormatYMDdate(gtblSettings.VersionNextCheck) end function NextCheck(Date, Interval) -- Date yyyy-mm-dd while Date <= os.date('%Y-%m-%d') do Date = DateAdd(Date, Interval) end return Date end function tglAutoCheck:action(state) if state == 1 then lblCheckFrequency.active = 'YES' listCheckFrequency.active = 'YES' if listCheckFrequency.value == '0' then listCheckFrequency.value = 2 local Interval = gtblConstants.VersionCheckFrequency[listCheckFrequency.value] txtNextCheck.value = FormatYMDdate(NextCheck(gtblSettings.VersionCheckedDate, Interval)) end else lblCheckFrequency.active = 'NO' listCheckFrequency.active = 'NO' txtNextCheck.value = '' end end function listCheckFrequency:valuechanged_cb() local Interval = gtblConstants.VersionCheckFrequency[listCheckFrequency.value] txtNextCheck.value = FormatYMDdate(NextCheck(gtblSettings.VersionCheckedDate, Interval)) end local gridboxUpdateCheck = iup.gridbox{ lblAutoCheck, tglAutoCheck, lblCheckFrequency, listCheckFrequency, lblLastCheck, txtLastCheck, lblNextCheck, txtNextCheck; numdiv = 2, sizelin = -1, sizecol = -1, gapcol = 32, gaplin = 6, alignmentlin = 'ACENTER', padding = '0x0' -- Set for inheritance } local btnUpdateCheck = iup.button{ title = 'Check for Update', padding = '10x3' } function btnUpdateCheck:action() local bOK, strMsg = VersionCheck(dialogSettings) if not bOK then strMsg = 'Check for plugin update failed\n\n' .. strMsg OkOnlyMessage(gPluginName, strMsg, 'ERROR, dialogSettings') else if strMsg == 'Y' then strMsg = 'You are running the latest version' OkOnlyMessage(gPluginName, strMsg, 'INFORMATION', dialogSettings) end end end local hboxUpdateCheck = iup.hbox{ iup.fill{}, iup.frame{ iup.vbox{ gridboxUpdateCheck, iup.label{separator='HORIZONTAL'}, btnUpdateCheck; expandchildren = 'NO', margin = '3x2', fontstyle = '', cgap = 8 }, sunken = 'YES', }, iup.fill{}; margin = '5x2', tabtitle = ' Update Check ' } ------------------------------------------------------------------------------------------------------------------------ -- Footer Buttons local btnSave = iup.button{ title = 'Save', image = 'IUP_FileSave', padding = '10x0' } function btnSave:action() if txtBirthSourceId.bgcolor == gtblConstants.Yellow or txtMarriageSourceId.bgcolor == gtblConstants.Yellow or txtDeathSourceId.bgcolor == gtblConstants.Yellow then OkOnlyMessage(gPluginName, 'Source(s) are invalid', 'ERROR', dialogSettings) return end if tglAddLivingFlag.value == 'ON' then if txtLivingMaxAge.value == '' or tonumber(txtLivingMaxAge.value) < 75 or tonumber(txtLivingMaxAge.value) > 125 then txtLivingMaxAge.bgcolor = gtblConstants.Yellow end if txtLivingMaxAge.bgcolor == gtblConstants.Yellow or txtLivingMaxAge.value == '' then OkOnlyMessage(gPluginName, 'Maximum living age out of range (75-125)', 'ERROR', dialogSettings) return end end local bAnyChanged = false -- Sources gtblSettings.SourceIdBirth, bAnyChanged = SettingUpdate(txtBirthSourceId.value, gtblSettings.SourceIdBirth, bAnyChanged) gtblSettings.SourceIdMarriage, bAnyChanged = SettingUpdate(txtMarriageSourceId.value, gtblSettings.SourceIdMarriage, bAnyChanged) gtblSettings.SourceIdDeath, bAnyChanged = SettingUpdate(txtDeathSourceId.value, gtblSettings.SourceIdDeath, bAnyChanged) -- Citation Prefixes gtblSettings.LabelPrefixBirth, bAnyChanged = SettingUpdate(txtLabelPrefixBirth.value, gtblSettings.LabelPrefixBirth, bAnyChanged) gtblSettings.LabelPrefixMarriage, bAnyChanged = SettingUpdate(txtLabelPrefixMarriage.value, gtblSettings.LabelPrefixMarriage, bAnyChanged) gtblSettings.LabelPrefixDeath, bAnyChanged = SettingUpdate(txtLabelPrefixDeath.value, gtblSettings.LabelPrefixDeath, bAnyChanged) -- GRO Reference Labels gtblSettings.LabelDistrictName, bAnyChanged = SettingUpdate(txtLabelDistrictName.value, gtblSettings.LabelDistrictName, bAnyChanged) gtblSettings.LabelVolume, bAnyChanged = SettingUpdate(txtLabelVolume.value, gtblSettings.LabelVolume, bAnyChanged) gtblSettings.LabelPage, bAnyChanged = SettingUpdate(txtLabelPage.value, gtblSettings.Page, bAnyChanged) gtblSettings.LabelDistrictNo, bAnyChanged = SettingUpdate(txtLabelDistrictNo.value, gtblSettings.LabelDistrictNo, bAnyChanged) gtblSettings.LabelRegister, bAnyChanged = SettingUpdate(txtLabelRegister.value, gtblSettings.LabelRegister, bAnyChanged) gtblSettings.LabelEntry, bAnyChanged = SettingUpdate(txtLabelEntry.value, gtblSettings.LabelEntry, bAnyChanged) -- Event Specific Labels gtblSettings.LabelMother, bAnyChanged = SettingUpdate(txtLabelMother.value, gtblSettings.LabelMother, bAnyChanged) gtblSettings.LabelAge, bAnyChanged = SettingUpdate(txtLabelAge.value, gtblSettings.LabelAge, bAnyChanged) gtblSettings.LabelDOB, bAnyChanged = SettingUpdate(txtLabelDOB.value, gtblSettings.LabelDOB, bAnyChanged) -- Other settings gtblSettings.QtrMonthNames, bAnyChanged = SettingUpdate(tglQtrMonthNames.value, gtblSettings.QtrMonthNames, bAnyChanged) gtblSettings.UCRefs, bAnyChanged = SettingUpdate(tglUCRefs.value, gtblSettings.UCRefs, bAnyChanged) gtblSettings.UseGRODistricts, bAnyChanged = SettingUpdate(tglUseGRODistricts.value, gtblSettings.UseGRODistricts, bAnyChanged) gtblSettings.ShortPlaces, bAnyChanged = SettingUpdate(tglShortPlaces.value, gtblSettings.ShortPlaces, bAnyChanged) gtblSettings.IncNameInCitation, bAnyChanged = SettingUpdate(tglIncNameInCitation.value, gtblSettings.IncNameInCitation, bAnyChanged) gtblSettings.NameFormat, bAnyChanged = SettingUpdate(listNameFormat.value, gtblSettings.NameFormat, bAnyChanged) gtblSettings.CitationNameBirth, bAnyChanged = SettingUpdate(tglCitationNameBirth.value, gtblSettings.CitationNameBirth, bAnyChanged) gtblSettings.CitationNameMarriage, bAnyChanged = SettingUpdate(tglCitationNameMarriage.value, gtblSettings.CitationNameMarriage, bAnyChanged) gtblSettings.CitationNameDeath, bAnyChanged = SettingUpdate(tglCitationNameDeath.value, gtblSettings.CitationNameDeath, bAnyChanged) gtblSettings.CitationToMotherDefault, bAnyChanged = SettingUpdate(tglCitationToMotherDefault.value, gtblSettings.CitationToMotherDefault, bAnyChanged) gtblSettings.NewMotherGivenName, bAnyChanged = SettingUpdate(txtNewMotherGivenName.value, gtblSettings.NewMotherGivenName, bAnyChanged) gtblSettings.AddLivingFlag, bAnyChanged = SettingUpdate(tglAddLivingFlag.value, gtblSettings.AddLivingFlag, bAnyChanged) gtblSettings.LivingMaxAge, bAnyChanged = SettingUpdate(txtLivingMaxAge.value, gtblSettings.LivingMaxAge, bAnyChanged) gtblSettings.CitationEntryDate, bAnyChanged = SettingUpdate(listCitationEntryDate.value, gtblSettings.CitationEntryDate, bAnyChanged) gtblSettings.CitationAssess, bAnyChanged = SettingUpdate(listCitationAssess.value, gtblSettings.CitationAssess, bAnyChanged) -- Update Check local Interval, NextCheckDate if tglAutoCheck.value == 'OFF' then Interval = 0 NextCheckDate = gtblSettings.VersionNextCheck else Interval = gtblConstants.VersionCheckFrequency[listCheckFrequency.value] NextCheckDate = NextCheck(gtblSettings.VersionCheckedDate, Interval) end gtblSettings.VersionCheckInterval, bAnyChanged = SettingUpdate(Interval, gtblSettings.VersionCheckInterval, bAnyChanged) gtblSettings.VersionNextCheck, bAnyChanged = SettingUpdate(NextCheckDate, gtblSettings.VersionNextCheck, bAnyChanged) if bAnyChanged then SaveSettings() end return iup.CLOSE end function SettingUpdate(NewValue, CurrSetting, bAnyChanged) -- Force new settings to same types as current settings local NewSetting if type(CurrSetting) == 'boolean' then NewSetting = false if NewValue == 'ON' then NewSetting = true end elseif type(CurrSetting) == 'number' then if NewValue ~= '' then NewSetting = tonumber(NewValue) else NewSetting = 0 end else NewSetting = NewValue end if NewSetting ~= CurrSetting then return NewSetting, true else return CurrSetting, bAnyChanged end end ------------------------------------------------------------------------------------------------------------------------ local btnCancel = iup.button{ title = 'Cancel', padding = '10x3', action = function(self) return iup.CLOSE end } local btnHelp = iup.button{ title = 'Help', padding = '10x3', tipballoontitle = 'Settings', tip = 'Display Settings help page' } function btnHelp:action() fhShellExecute('https://pluginstore.family-historian.co.uk/page/help/gro-bmd-index-citations-settings') end ------------------------------------------------------------------------------------------------------------------------ dialogSettings = iup.dialog{ iup.vbox{ frameSources, iup.tabs{vboxLabels, hboxOptions, hboxUpdateCheck; fontstyle = 'Bold'}, iup.hbox{ iup.fill{}, btnSave, btnHelp, btnCancel, iup.fill{}; normalizesize = 'BOTH', margin = '0x0', gap = 40 }; gap = 10, margin = '10x10' }; resize = 'No', minbox = 'No', maxbox = 'No', helpbutton = 'Yes', title = gPluginName .. ' - Settings', defaultesc = btnCancel, taskbarbutton = 'SHOW', tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1' } iup.SetAttribute(dialogSettings, 'NATIVEPARENT', fhGetContextInfo('CI_PARENT_HWND')); function SourceCheck(SourceId) local SourceName, Templated = GetSource(tonumber(SourceId)) if Templated then OkOnlyMessage(gPluginName, 'Templated sources are not currently supported', 'WARNING', dialogSettings) return false, '' end if SourceName ~= '' then return true, SourceName else OkOnlyMessage(gPluginName, 'Invalid Source Record Id', 'ERROR', dialogSettings) return false, '' end end ------------------ Birth functions and call backs ------------------------------------------------------------------ function txtBirthSourceId:valuechanged_cb() -- Clear title if ID changed txtBirthSourceId.bgcolor = gtblConstants.White lblBirthSource.title = '' end function txtBirthSourceId:killfocus_cb() if lblBirthSource.title == '' then SetBirthTitle() end end function txtBirthSourceId:k_any(c) if c == iup.K_CR then if lblBirthSource.title == '' then SetBirthTitle() end end end function btnBirthSource:action() local ptrlist = fhPromptUserForRecordSel('SOUR', 1, dialogSettings.HWND) if #ptrlist ~= 0 then txtBirthSourceId.value = fhGetRecordId(ptrlist[1]) txtBirthSourceId.bgcolor = gtblConstants.White SetBirthTitle() end end function SetBirthTitle() if txtBirthSourceId.value == 0 or txtBirthSourceId.value == '' then lblBirthSource.title = '' return end local SrcValid SrcValid, lblBirthSource.title = SourceCheck(txtBirthSourceId.value) if not SrcValid then txtBirthSourceId.bgcolor = gtblConstants.Yellow end end ------------------ Marriage functions and call backs --------------------------------------------------------------- function txtMarriageSourceId:valuechanged_cb() -- Clear title if ID changed txtMarriageSourceId.bgcolor = gtblConstants.White lblMarriageSource.title = '' end function txtMarriageSourceId:killfocus_cb() if lblMarriageSource.title == '' then SetMarriageTitle() end end function txtMarriageSourceId:k_any(c) if c == iup.K_CR then if lblMarriageSource.title == '' then SetMarriageTitle() end end end function btnMarriageSource:action() local ptrlist = fhPromptUserForRecordSel('SOUR', 1, dialogSettings.HWND) if #ptrlist ~= 0 then txtMarriageSourceId.value = fhGetRecordId(ptrlist[1]) txtMarriageSourceId.bgcolor = gtblConstants.White SetMarriageTitle() end end function SetMarriageTitle() if txtMarriageSourceId.value == 0 or txtMarriageSourceId.value == '' then lblMarriageSource.title = '' return end local SrcValid SrcValid, lblMarriageSource.title =SourceCheck(txtMarriageSourceId.value) if not SrcValid then txtMarriageSourceId.bgcolor = gtblConstants.Yellow end end ------------------ Death functions and call backs ------------------------------------------------------------------ function txtDeathSourceId:valuechanged_cb() -- Clear title if ID changed txtDeathSourceId.bgcolor = gtblConstants.White lblDeathSource.title = '' end function txtDeathSourceId:killfocus_cb() if lblDeathSource.title == '' then SetDeathTitle() end end function txtDeathSourceId:k_any(c) if c == iup.K_CR then if lblDeathSource.title == '' then SetDeathTitle() end end end function btnDeathSource:action() local ptrlist = fhPromptUserForRecordSel('SOUR', 1, dialogSettings.HWND) if #ptrlist ~= 0 then txtDeathSourceId.value = fhGetRecordId(ptrlist[1]) txtDeathSourceId.bgcolor = gtblConstants.White SetDeathTitle() end end function SetDeathTitle() if txtDeathSourceId.value == 0 or txtDeathSourceId.value == '' then lblDeathSource.title = '' return end local SrcValid SrcValid, lblDeathSource.title = SourceCheck(txtDeathSourceId.value) if not SrcValid then txtDeathSourceId.bgcolor = gtblConstants.Yellow end end -------------------------------------------------------------------------------------------------------------------- function tglIncNameInCitation:action() if tglIncNameInCitation.value == 'OFF' then if tglCitationNameBirth.value == 'ON' or tglCitationNameMarriage.value == 'ON' or tglCitationNameDeath.value == 'ON' then local strMsg = 'Include Name(s) in Citation Text must be set to\n\n' .. '"Always" or "Only if adding citation to name"\n\n' .. 'if any Add Citation to Name option is on' OkOnlyMessage(gPluginName, strMsg, 'WARNING',dialogSettings) tglIncNameInCitation.value = 'ON' end end SetlblIncNameState() end function ValidateCitationToName() if tglIncNameInCitation.value == 'OFF' then local strMsg = 'Not permitted if Include Name(s) in Citation Text\nis set to Never' OkOnlyMessage(gPluginName, strMsg, 'ERROR', dialogSettings) return false end return true end -- Equalise section label sizes in Citation Labels frame dialogSettings:map() local LabelSize = lblWWSLabels.size local LabelWidth = tonumber(LabelSize:match('^(%d*)')) LabelSize = lblTFSHeaders.size if tonumber(LabelSize:match('^(%d*)')) > LabelWidth then LabelWidth = tonumber(LabelSize:match('^(%d*)')) end LabelSize = lblTFSEventLabels.size if tonumber(LabelSize:match('^(%d*)')) > LabelWidth then LabelWidth = tonumber(LabelSize:match('^(%d*)')) end LabelSize = tostring(LabelWidth) .. 'x0' lblWWSLabels.size = LabelSize lblTFSHeaders.size = LabelSize lblTFSEventLabels.size = LabelSize dialogSettings:popup(IUP_CENTERPARENT, IUP_CENTERPARENT) dialogSettings:destroy() end -- function config function GetSource(Id) -- Check source exists, returns empty string if not found -- if found returns name and true/false if templated or not local ptrSource = fhNewItemPtr() local ptrTemplate = fhNewItemPtr() ptrSource:MoveToRecordById('SOUR',Id) if ptrSource:IsNull() then return '', false else ptrTemplate = fhGetItemPtr(ptrSource, '~._SRCT>') if ptrTemplate:IsNotNull() then return fhGetDisplayText(ptrSource), true else return fhGetDisplayText(ptrSource), false end end end -- fuction GetSource --====================================================================================================================== --==== Main Dialog used for Births, Marriages and Deaths ==== --====================================================================================================================== function MainDialog(Event, arrInd1, arrInd2, arrFam, intFirstYear, intLastYear) -- arrInd2 and arrFam are nil for birth and death -- arrInd2 will be blank if family only has one individual local arrActions = {} local strTitle = 'GRO Index ' .. Event .. ' Citation' local strHeader = 'Citation for ' .. Event .. ' of ' .. arrInd1['DispNameFull'] if Event == 'Marriage' then strHeader = strHeader .. '\nand ' if arrInd2 ~= nil then strHeader = strHeader .. arrInd2['DispNameFull'] else strHeader = strHeader .. '<>' end end local vboxFormHead = iup.vbox{ iup.label{title = strHeader, fontstyle = 'Bold', padding = '0x0', alignment = 'ACENTER', multiline = 'YES'}, alignment = 'ACENTER', normalizesize = 'horizontal', expandchildren = 'YES' } ------------------------ Registration details panel -------------------- -- Make array of years for listbox local addrange = 3 -- Number of years to extend range if very narrow local ExactYear = nil if intFirstYear == intLastYear then ExactYear = intFirstYear end if intLastYear - intFirstYear < addrange * 2 + 1 then intFirstYear = math.max(intFirstYear - addrange, 1837) intLastYear = math.min(intLastYear + addrange, gtblConstants.CurrYear) end local arrYears = {} for Yr = intFirstYear, intLastYear do table.insert(arrYears, Yr) end local listYear = iup.list{dropdown = 'YES'; editbox = 'YES', visibleitems = 10, visiblecolumns = 3} PopulateList(listYear, arrYears) if ExactYear ~= nil then listYear.value = ExactYear end local listQtrMonth = iup.list{ dropdown = 'YES', visibleitems = 10, visiblecolumns = 8, bgcolor = gtblConstants.White} PopulateList(listQtrMonth, gtblConstants.PeriodsLong) local hboxWhen = iup.hbox{ iup.label{title = 'Year:', size = '35x0', padding = '2x0'}, listYear, iup.fill{}, iup.label{title = 'Quarter or Month:'}, listQtrMonth, iup.fill{}, iup.fill{}; gap = 4, margin = '0x0', alignment = 'ACENTER' } local listDistrictName = iup.list{dropdown = 'YES', visibleitems = 15, expand = 'HORIZONTAL'} if not gtblSettings.UseGRODistricts then listDistrictName.editbox = 'YES' for i,v in pairs(tblPlaces) do listDistrictName[tostring(i)] = v[1] end else listDistrictName.editbox = 'NO' PopulateListFromtblGROPlaces(listDistrictName) end local strDistrictNameFilter = '' local hboxDistrictName = iup.hbox{ iup.label{title='District:', size = '35x0', padding = '2x0'}, listDistrictName; alignment = 'ACENTER' } local btnGRODistricts = iup.button{title = 'Add/Edit', padding = '4x1'} if gtblSettings.UseGRODistricts then hboxDistrictName:append(btnGRODistricts) end local txtVolume = iup.text {visiblecolumns = 4} local vboxVolume = iup.vbox{iup.label{title = 'Volume'}, txtVolume; alignment = 'ACENTER', gap = 2} local txtPage = iup.text {visiblecolumns = 4} local vboxPage = iup.vbox{iup.label{title = 'Page'}, txtPage; alignment = 'ACENTER', gap = 2} local txtDistNo = iup.text {visiblecolumns = 4} local vboxDistNo = iup.vbox{iup.label{title = 'District No.'}, txtDistNo; alignment = 'ACENTER', gap = 2} local txtRegister = iup.text {visiblecolumns = 4} local vboxRegister = iup.vbox{iup.label{title = 'Register No.'}, txtRegister; alignment = 'ACENTER', gap = 2} local txtEntry = iup.text {visiblecolumns = 6, filter = 'NUMBER'} local vboxEntry = iup.vbox{iup.label{title = 'Entry No.'}, txtEntry; alignment = 'ACENTER', gap = 2} local hboxRefs = iup.hbox{iup.label{size = '21x0'},vboxVolume, vboxPage, vboxDistNo, vboxRegister, vboxEntry; gap = 25, margin = '0x0', padding = '0x0'} local txtCitationNote = iup.text{expand = 'HORIZONTAL'} local hboxCitationNote = iup.hbox{ iup.label{title = 'Citation Specific Note: ', padding = '2x0'}, txtCitationNote; alignment = 'ACENTER' } local frameRegistration = iup.frame{ iup.hbox{ iup.frame{ iup.vbox{ hboxWhen, hboxDistrictName, hboxRefs, hboxCitationNote; gap = 2, margin = '2x4', fontstyle= '' }, sunken = 'YES' }, padding = '5x0' }, title='Registration Details', fontstyle= 'Bold' } --------- Event specific details - Birth (mother), Death (age/DOB) --------- local txtMother = iup.text{ size = '115x0', padding = '0x0', tipballoontitle = 'Mother\'s Maiden Name', tip = "Ctrl ' (single quote) to copy '" .. arrInd1['Mother'] .. "'"} local tglCitationToMother = iup.toggle{ title = 'Add Citation to Mother\'s Name:', value = ToggleValue(gtblSettings.CitationToMotherDefault), rightbutton = 'YES' } local txtAge = iup.text{visiblecolumns = 3, padding = '0x0', filter = 'NUMBER'} local txtDOB = iup.text{visiblecolumns = 10, padding = '0x0'} local boxEvent if Event == 'Birth' then boxEvent = iup.vbox{ iup.hbox{ iup.label{title = 'Mother\'s Maiden Name:'; padding = '0x0'}, txtMother, iup.label{title = '(Ctrl \' to copy Mother\'s name) ', fontstyle = 'Italic'}; iup.fill{}; gap = 4, margin = '0x0', alignment='ACENTER' }, iup.hbox{ tglCitationToMother, iup.fill{}; margin = '0x0' }; margin = '4x2' } elseif Event == 'Death' then boxEvent = iup.hbox{ iup.label{title = 'Age:', size = '35x0'}, txtAge, iup.fill{}, iup.label{title = 'Date of Birth or birth year:', size = '0x0'}, txtDOB, iup.fill{},iup.fill{}; gap = 4, margin = '0x4', alignment = 'ACENTER' } end local frameEvent if Event ~= 'Marriage' then frameEvent = iup.frame{ iup.hbox{ iup.frame{ boxEvent; sunken = 'YES' }, padding = '5x0', fontstyle = '' }, title = Event, fontstyle = 'Bold' } end function ValidateDate(strDate, PartDate) -- Returns true/false, date formated to FH preferred short date format -- and a date object if valid local dtDate = fhNewDate() if not dtDate:SetValueAsText(strDate, false) then return false end local strType = dtDate:GetType() if strType ~= 'Simple' then return false end local dpDatePt = dtDate:GetDatePt1() local Day = dpDatePt:GetDay() local Month = dpDatePt:GetMonth() local Year = dpDatePt:GetYear() if Year < 1730 then -- Earliest DOB of a death in 1837 is ~100 years before return false end local FmtDate = dtDate:GetDisplayText('COMPACT') if PartDate and Day == 0 then return true, FmtDate, dtDate end if Day == 0 or Month == 0 or Month > 12 then return false end local DaysInMonth = {31,29,31,30,31,30,31,31,30,31,30,31} if Day > DaysInMonth[Month] then return false end if Month ==2 and Day == 29 then if Year%4 ~= 0 then return false end if Year%100 == 0 and Year%400 ~=0 then return false end end return true, FmtDate, dtDate end ------------------------ Name(s) for citation panel ------------------------ local titleFrame = 'Name' local CitationToNameDefault if Event == 'Birth' then CitationToNameDefault = gtblSettings.CitationNameBirth elseif Event == 'Marriage' then CitationToNameDefault = gtblSettings.CitationNameMarriage else CitationToNameDefault = gtblSettings.CitationNameDeath end local tglCitationToNames = iup.toggle{ title = 'Add Citation to Name:', value = ToggleValue(CitationToNameDefault), rightbutton = 'YES' } local tglIncNameInCitation = iup.toggle{ title ='Include Name in Citation:', margin = '0x0', rightbutton = 'YES', ['3state'] = 'YES', value = gtblSettings.IncNameInCitation } local lblIncNameState = iup.label{title = ' ', expand = 'HORIZONTAL'} function SurnameToCopy() -- Get surname for first individual to be used if Ctrl ' keyed -- For death of a wife it will be her married name if Event ~= 'Death' then return arrInd1['Surname'] else local ptrField = fhNewItemPtr() ptrField:MoveTo(arrInd1.Ptr, '~.SEX') if fhGetValueAsText(ptrField) ~= 'Female' then return arrInd1['Surname'] else ptrField:MoveTo(arrInd1.Ptr, 'INDI.~SPOU[LAST]>') if ptrField:IsNull() then return arrInd1['Surname'] else return fhu.getSurname(fhGetItemText(ptrField, '~.NAME:STORED')) end end end end local txtGiven = iup.text{size = '150x0', tip = "Ctrl ' (single quote) to copy '" .. arrInd1['GivenName'] .. "'"} local txtSurname = iup.text{size = '115x0', tip = "Ctrl ' (single quote) to copy '" .. SurnameToCopy() .. "'"} local txtGiven2 = iup.text{size = '150x0'} local txtSurname2 = iup.text{size = '115x0'} local lblRow = 'Citation Name' local hboxNameLabels = iup.hbox{ iup.label{title = 'Given', size = '150x0', padding = '4x0'}, iup.label{title = 'Surname', size = '0x0', padding = '4x0'}; gap = 2, margin = '0x0' } local hboxCitationName1 = iup.hbox{txtGiven, txtSurname; gap = 2, margin = '0x1'} local hboxCitationName2 = iup.hbox{txtGiven2, txtSurname2; gap = 2, margin = '0x3'} local vboxCitationNames = iup.vbox{ hboxNameLabels, hboxCitationName1; gap = 0, padding = '2x0', tipballoontitle = 'Citation names' } if arrInd2 ~= nil then lblRow = lblRow .. '(s)' tglIncNameInCitation.title = tglIncNameInCitation.title:gsub('Name','Name(s)') tglCitationToNames.title = tglCitationToNames.title:gsub('Name','Name(s)') titleFrame = titleFrame .. '(s)' txtGiven2.tip = "Ctrl ' (single quote) to copy '" .. arrInd2['GivenName'] .. "'" txtSurname2.tip = "Ctrl ' (single quote) to copy '" .. arrInd1['Surname'] .. "'" iup.Append(vboxCitationNames, hboxCitationName2) end local function SetlblIncNameState() local strState if tglIncNameInCitation.value == 'ON' then strState = 'Yes' elseif tglIncNameInCitation.value == 'OFF' then strState = 'No' else strState = 'Only if adding citation to name' end lblIncNameState.title = strState end SetlblIncNameState() local frameNames = iup.frame{ iup.hbox{ iup.frame { iup.vbox{ iup.hbox{tglCitationToNames, iup.space{size = '12x0'}, tglIncNameInCitation, lblIncNameState; gap = 4, margin = '0x0'}, iup.hbox{ iup.vbox{ iup.flatlabel{title = lblRow}, iup.label{title = '(Ctrl \' to copy\nexisting name) ', fontstyle = 'Italic'}; gap = 0, margin = '0x0' }, iup.fill{}, vboxCitationNames; --vboxGiven, vboxSurname; gap = 4, alignment = 'ACENTER', margin = '0x0' }; --tglCitationToNames; gap = 4, alignment = 'ALEFT', margin = '4x2', padding = '0x0' -- Set for inheritance }, sunken = 'YES' }; padding = '5x2', fontstyle = '' }, title = titleFrame, fontstyle = 'Bold' } ----------------------- Citation text preview panel ------------------------ local lblWhereWithin = iup.flatlabel{ title = ' ', size = '350x18', fgcolor = gtblConstants.Blue, padding = '2x0', textwrap = 'YES', alignment = 'ALEFT:ATOP', border = 'YES' } local vboxWhereWithin = iup.vbox{ iup.label{title = 'Where Within', padding = '2x0'}, lblWhereWithin; gap = '0', margin = '0x0' } local lblTextFromSource = iup.flatlabel{ title = ' ', size = '350x18', fgcolor = gtblConstants.Blue, padding = '2x0', textwrap = 'YES', alignment = 'ALEFT:ATOP', border = 'YES' } local vboxTextFromSource = iup.vbox{ iup.label{title = 'Text from Source', padding = '2x0'}, lblTextFromSource; gap = '0', margin = '0x0' } local listCitationAssess = iup.list{ dropdown = 'YES', size = '95x0', visibleitems = 6} PopulateList(listCitationAssess, gtblConstants.CitationAssess) listCitationAssess.value = gtblSettings.CitationAssess local hboxCitationAssess = iup.hbox{ iup.label{title ='Citation Assessment: ', padding = '2x0'}, listCitationAssess; margin = '0x0', alignment = 'ACENTER'} local framePreview = iup.frame{ iup.hbox{ iup.frame{ iup.vbox{ vboxWhereWithin, vboxTextFromSource, hboxCitationAssess; gap = 6, margin = '4x2' }, sunken = 'YES' }, padding = '5x2', fontstyle = '' }, title = 'Citation', fontstyle = 'Bold' } ------------------------------ Footer buttons ------------------------------ local btnContinue = iup.button{ title = 'Continue', padding = '10x0' } local btnCancel = iup.button{ title = 'Cancel', padding = '10x3', action = function(self) return iup.CLOSE end } local hboxFooter = iup.hbox{ iup.fill{}, btnContinue, btnCancel, iup.fill{}; normalizesize = 'BOTH', margin = '0x5', gap = 40 }; ---------------------- Assemble and display the dialog --------------------- local vboxMain = iup.vbox{ vboxFormHead, frameRegistration, frameNames, frameEvent, framePreview, hboxFooter; gap = 5, margin = '10x5', alighment = 'ACENTER' }; local dialogMain = iup.dialog{ vboxMain; resize = 'No', minbox = 'No', maxbox = 'No', title = 'GRO Index ' .. Event .. ' Citation', defaultesc = btnCancel, taskbarbutton = 'SHOW', tipballoon = 'YES', tipdelay = 10000, tipballoontitleicon = '1' } iup.SetAttribute(dialogMain, 'NATIVEPARENT', fhGetContextInfo('CI_PARENT_HWND')) -------------------------------------------------------------------------------------------------------------------- function FormatName(Given, Surname) -- 1 = Surname, Given 3 = Given Surname if gtblSettings.NameFormat%2 == 0 then -- 2 = SURNAME, Given 4 = Given SURNAME Surname = Surname:upper() end if not Given then return Surname end if gtblSettings.NameFormat < 3 then return Surname .. ', ' .. Given else return Given .. ' ' .. Surname end end -------------------------------------------------------------------------------------------------------------------- function WhereWithin() local WhereWithin = '' if Event == 'Birth' then WhereWithin = gtblSettings.LabelPrefixBirth elseif Event == 'Marriage' then WhereWithin = gtblSettings.LabelPrefixMarriage else WhereWithin = gtblSettings.LabelPrefixDeath end WhereWithin = WhereWithin .. listYear.value local intSel = tonumber(listQtrMonth.value) if intSel ~= 0 then if gtblSettings.QtrMonthNames then WhereWithin = WhereWithin .. ' ' .. gtblConstants.PeriodsLong[intSel] else WhereWithin = WhereWithin .. ' ' .. gtblConstants.Periods[intSel] end end if listDistrictName.value ~= '' then local strDistrict if not gtblSettings.UseGRODistricts then strDistrict = listDistrictName.value else if listDistrictName.value ~= '0' then strDistrict = listDistrictName.valuestring:match('^(.*)%s%[') or listDistrictName.valuestring else strDistrict = '' end end if gtblSettings.ShortPlaces then local strValue = strDistrict strValue = strValue:match('^([^,]+)') if strValue ~= null then WhereWithin = WhereWithin .. ' ' .. LabelData(gtblSettings.LabelDistrictName, strValue) end else WhereWithin = WhereWithin .. ' ' .. LabelData(gtblSettings.LabelDistrictName, strDistrict) end end if txtVolume.value ~= '' then WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelVolume, txtVolume.value)) end if txtPage.value ~= '' then WhereWithin = WhereWithinRefs(WhereWithin , LabelData(gtblSettings.LabelPage, txtPage.value)) end if txtDistNo.value ~= '' then WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelDistrictNo, txtDistNo.value)) end if txtRegister.value ~= '' then WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelRegister, txtRegister.value)) end if txtEntry.value ~= '' then WhereWithin = WhereWithinRefs(WhereWithin, LabelData(gtblSettings.LabelEntry, txtEntry.value)) end WhereWithin = WhereWithin:gsub('%^',' ') return WhereWithin end -- function WhereWithin function WhereWithinRefs(strWW, strLabelledData) if strLabelledData:find('^[-/]') then return strWW .. strLabelledData -- No space before label beginning with - or / else return strWW .. ' ' .. strLabelledData end end function WhereWithinUpdate() lblWhereWithin.title = WhereWithin():gsub('&', '&&') end -------------------------------------------------------------------------------------------------------------------- function TextFromSrc() local TextFromSrc = '' if tglIncNameInCitation.value == 'ON' or tglCitationToNames.value == 'ON' then TextFromSrc = TextFromSrc .. ' ' .. FormatName(txtGiven.value, txtSurname.value) if Event == 'Marriage' then if arrInd2 ~= nil then TextFromSrc = TextFromSrc .. ', ' .. FormatName(txtGiven2.value, txtSurname2.value) else TextFromSrc = TextFromSrc .. ', ' end end end if Event == 'Birth' and txtMother.value ~= '' then TextFromSrc = TextFromSrc .. ' ' .. LabelData(gtblSettings.LabelMother, FormatName(nil, txtMother.value)) end if Event == 'Death' then if txtAge.value ~= '' then TextFromSrc = TextFromSrc .. ' ' .. LabelData(gtblSettings.LabelAge, txtAge.value) end if txtDOB.value ~= '' and txtDOB.bgcolor ~= gtblConstants.Red then -- Don't add if not valid TextFromSrc = TextFromSrc .. ' ' .. LabelData(gtblSettings.LabelDOB, txtDOB.value) end end TextFromSrc = TextFromSrc:sub(2) -- drop leading space TextFromSrc = TextFromSrc:gsub('%^',' ') return TextFromSrc end -- function TextFromSrc function TextFromSrcUpdate() lblTextFromSource.title = TextFromSrc():gsub('&','&&') end -------------------------------------------------------------------------------------------------------------------- function LabelData(Label, Data) -- Combine label and data if Label:find('%$') then return Label:gsub('%$', Data) else return Label .. Data end end -------------------------------------------------------------------------------------------------------------------- function listYear:valuechanged_cb() listYear.bgcolor = gtblConstants.White WhereWithinUpdate() end function listYear:killfocus_cb() if listYear.bgcolor == gtblConstants.Red then return end if listYear.value ~= '' then if listYear.value:find("%D") or tonumber(listYear.value) < 1837 or tonumber(listYear.value) > gtblConstants.CurrYear then OkOnlyMessage(strTitle, 'Invalid Year (must be between 1837 and ' .. gtblConstants.CurrYear .. ')', 'ERROR', dialogMain) listYear.bgcolor = gtblConstants.Red end if gtblSettings.UseGRODistricts and not GRODistrictYear() then OkOnlyMessage(strTitle, 'GRO District was not active in ' .. listYear.value, 'WARNING', dialogMain) end end end function listYear:k_any() listYear.bgcolor = gtblConstants.White end function listQtrMonth:valuechanged_cb() WhereWithinUpdate() end function listQtrMonth:k_any(v) if iup.isprint(v) then local k = string.char(v) if k >= '1' and k <= '4' then listQtrMonth.value = tonumber(k) end end end function listDistrictName:valuechanged_cb() WhereWithinUpdate() end function listDistrictName:killfocus_cb() if gtblSettings.UseGRODistricts and not GRODistrictYear() then OkOnlyMessage(strTitle, 'GRO District was not active in ' .. listYear.value, 'WARNING', dialogMain) end end function listDistrictName:k_any(k) if gtblSettings.UseGRODistricts then if iup.isprint(k) then -- List will be on first entry with that starting letter strDistrictNameFilter = strDistrictNameFilter .. string.char(k):upper() if #strDistrictNameFilter > 1 then local i = tonumber(listDistrictName.value) -- Must be forward from current line local InList = false while i <= tonumber(listDistrictName.count) do if listDistrictName[tostring(i)]:upper():find('^' .. strDistrictNameFilter) then iup.Flush() listDistrictName.value = i WhereWithinUpdate() InList = true break elseif listDistrictName[tostring(i)]:upper() > strDistrictNameFilter then break end i = i + 1 end if not InList then strDistrictNameFilter = string.char(k):upper() -- List will be on first entry with that starting letter end else iup.Flush() if listDistrictName.value == '0' then strDistrictNameFilter = '' -- No districts matching initial letter keyed end end end if k == iup.K_BS then iup.Flush() if #strDistrictNameFilter <= 1 then listDistrictName.value = 0 WhereWithinUpdate() strDistrictNameFilter = '' else strDistrictNameFilter = strDistrictNameFilter:sub(1, -2) local i = 1 while i <= tonumber(listDistrictName.count) do if listDistrictName[tostring(i)]:upper():find('^' .. strDistrictNameFilter) then listDistrictName.value = i InList = true break elseif listDistrictName[tostring(i)]:upper() > strDistrictNameFilter then break end i = i + 1 end if not InList then listDistrictName.value = 0 end WhereWithinUpdate() end end if k == iup.K_DEL then listDistrictName.value = 0 WhereWithinUpdate() strDistrictNameFilter = '' end end end function btnGRODistricts:action() SelectedGRODistrict = GRODistricts(dialogMain) PopulateListFromtblGROPlaces(listDistrictName) -- Refresh in case change made listDistrictName.value = SelectedGRODistrict end function RefToUpper(txtField) if gtblSettings.UCRefs then if txtField.value ~= txtField.value:upper() then local pos = txtField.caret txtField.value = txtField.value:upper() txtField.caret = pos end end end function txtVolume:valuechanged_cb() RefToUpper(txtVolume) WhereWithinUpdate() end function txtVolume:k_any(c) if c == iup.XkeyCtrl(iup.K_TAB) then iup.SetFocus(txtGiven) end end function txtPage:valuechanged_cb() RefToUpper(txtPage) WhereWithinUpdate() end function txtPage:k_any(c) if c == iup.XkeyCtrl(iup.K_TAB) then iup.SetFocus(txtGiven) end end function txtDistNo:valuechanged_cb() RefToUpper(txtDistNo) WhereWithinUpdate() end function txtDistNo:k_any(c) if c == iup.XkeyCtrl(iup.K_TAB) then iup.SetFocus(txtGiven) end end function txtRegister:valuechanged_cb() RefToUpper(txtRegister) WhereWithinUpdate() end function txtRegister:k_any(c) if c == iup.XkeyCtrl(iup.K_TAB) then iup.SetFocus(txtGiven) end end function txtEntry:valuechanged_cb() WhereWithinUpdate() end function txtEntry:k_any(c) if c == iup.XkeyCtrl(iup.K_TAB) then iup.SetFocus(txtGiven) end end function txtMother:k_any(c) if c == iup.XkeyCtrl(iup.K_apostrophe) then txtMother.value = arrInd1['Mother'] txtMother.bgcolor = gtblConstants.White txtMother.CaretPos = #arrInd1['Mother'] TextFromSrcUpdate() end end function txtMother:valuechanged_cb() txtMother.bgcolor = gtblConstants.White TextFromSrcUpdate() end function txtAge:valuechanged_cb() TextFromSrcUpdate() end function GRODistrictYear() -- If GRO has years active check if year is in range if listYear.value ~= '' and listDistrictName.value ~= '0' then local strYears = tblGROPlaces[tonumber(listDistrictName.value)][2] if strYears == '' then return true -- No years specified - anything goes end local P = '(%d*%-?%d*)' -- Pattern for matching yyyy-yyyy, second only or both can be blank local tblFromTo = {strYears:match(P .. ',?' .. P .. ',?' .. P .. ',?' .. P)} for i,v in pairs(tblFromTo) do From, To = v:match('(%d*)%-(%d*)') if From == nil or tonumber(listYear.value) < tonumber(From) then return false end if To == '' then return true -- Open ended range end if tonumber(listYear.value) <= tonumber(To) then return true end end return false end return true end function ValidateDOB() if txtDOB.bgcolor ~= gtblConstants.White then return end if txtDOB.value == '' then txtDOB.bgcolor = gtblConstants.White TextFromSrcUpdate() return end local DateValid, Formatted, dtDate = ValidateDate(txtDOB.value, true) -- true to allow year or month/year only if not DateValid then txtDOB.bgcolor = gtblConstants.Red OkOnlyMessage(strTitle, 'Invalid Date of Birth', 'ERROR', dialogMain) return end local DOBYear = dtDate:GetDatePt1():GetYear() if DOBYear < arrInd1.EarliestBirth or DOBYear > arrInd1.LatestBirth then txtDOB.bgcolor = gtblConstants.PaleYellow OkOnlyMessage(strTitle, 'Date outside of expected range', 'WARNING', dialogMain) end txtDOB.value = Formatted TextFromSrcUpdate() end function txtDOB:k_any(c) if c == iup.K_CR then ValidateDOB() txtDOB.caretpos = #txtDOB.value return IUP_CONTINUE end if c ~= iup.K_TAB then txtDOB.bgcolor = gtblConstants.White return IUP_CONTINUE else if txtDOB.bgcolor == gtblConstants.White then ValidateDOB() if txtDOB.bgcolor == gtblConstants.Red then iup.SetFocus(txtDOB) return end end return IUP_CONTINUE end end function txtDOB:killfocus_cb() if txtDOB.bgcolor ~= gtblConstants.White then return end ValidateDOB() end function txtDOB:getfocus_cb() txtDOB.caretpos = #txtDOB.value end function tglIncNameInCitation:action() if tglIncNameInCitation.value == 'OFF' and tglCitationToNames.value == 'ON' then local strMsg = 'Include Name(s) in Citation Text must be set to\n' .. '"Always" or "Only if adding citation to name"\n' .. 'if Add Citation to Name option is on' OkOnlyMessage(strTitle, strMsg, 'ERROR', dialogMain) tglIncNameInCitation.value = 'ON' end SetlblIncNameState() TextFromSrcUpdate() end function tglCitationToNames:action() if tglCitationToNames.value == 'ON' and tglIncNameInCitation.value == 'OFF' then local strMsg = 'Requires Include Name(s) in Citation Text\nnot to be set to "No"' OkOnlyMessage(strTitle, strMsg, 'ERROR', dialogMain) tglCitationToNames.value = 'OFF' end TextFromSrcUpdate() end function txtGiven:k_any(c) if c == iup.XkeyCtrl(iup.K_apostrophe) then txtGiven.value = arrInd1['GivenName'] txtGiven.bgcolor = gtblConstants.White txtGiven.CaretPos = #arrInd1['GivenName'] TextFromSrcUpdate() end end function txtGiven:valuechanged_cb() txtGiven.bgcolor = gtblConstants.White TextFromSrcUpdate() end function txtSurname:k_any(c) if c == iup.XkeyCtrl(iup.K_apostrophe) then txtSurname.value = SurnameToCopy() txtSurname.bgcolor = gtblConstants.White txtSurname.CaretPos = #txtSurname.value TextFromSrcUpdate() end end function txtSurname:valuechanged_cb() txtSurname.bgcolor = gtblConstants.White TextFromSrcUpdate() end function txtGiven2:k_any(c) if c == iup.XkeyCtrl(iup.K_apostrophe) then txtGiven2.value = arrInd2['GivenName'] txtGiven2.bgcolor = gtblConstants.White txtGiven2.CaretPos = #arrInd2['GivenName'] TextFromSrcUpdate() end end function txtGiven2:valuechanged_cb() if arrInd2 ~= nil then txtGiven2.bgcolor = gtblConstants.White TextFromSrcUpdate() end end function txtSurname2:k_any(c) if c == iup.XkeyCtrl(iup.K_apostrophe) then txtSurname2.value = arrInd2['Surname'] txtSurname2.bgcolor = gtblConstants.White txtSurname2.CaretPos = #arrInd2['Surname'] TextFromSrcUpdate() end end function txtSurname2:valuechanged_cb() if arrInd2 ~= nil then txtSurname2.bgcolor = gtblConstants.White TextFromSrcUpdate() end end ----------------------------------- Validate data and if OK create citations etc. ---------------------------------- function btnContinue:action() -- Validate input if listYear.value == '' or listYear.bgcolor == gtblConstants.Red then OkOnlyMessage(strTitle, 'Year not selected', 'WARNING', dialogMain) return end if listQtrMonth.value == '0' then OkOnlyMessage(strTitle, 'Quarter/Month not selected', 'WARNING', dialogMain) return end if listDistrictName.value == '' or listDistrictName.value == '0' then OkOnlyMessage(strTitle, 'District not entered/selected', 'WARNING', dialogMain) return end if gtblSettings.UseGRODistricts and not GRODistrictYear() then OkOnlyMessage(strTitle, 'GRO District was not active in ' .. listYear.value, 'ERROR', dialogMain) return end -- Count completed reference fields - there should be at least two local intCount = 0 if txtVolume.value ~= '' then intCount = intCount + 1 end if txtPage.value ~= '' then intCount = intCount + 1 end if txtDistNo.value ~= '' then intCount = intCount + 1 end if txtRegister.value ~= '' then intCount = intCount + 1 end if txtEntry.value ~= '' then intCount = intCount + 1 end if intCount < 2 then OkOnlyMessage(strTitle, 'GRO Reference details incomplete', 'WARNING', dialogMain) return end -- Birth: if mothers name is to be given citation it must be entered if Event == 'Birth' then if tglCitationToMother.value == 'ON' and txtMother.value == '' then txtMother.bgcolor = gtblConstants.Yellow OkOnlyMessage(strTitle, 'Mother\'s name required if being given citation', 'WARNING', dialogMain) return end end -- Death requires age or date of birth but not both if Event == 'Death' then if txtAge.value == '' then if txtDOB.value == '' then local strMessage = 'Neither Age nor Date of Birth entered\n\nContinue?\n\n' .. '(Tip: Age or Date of Birth can usually be found at ' .. 'https://www.gro.gov.uk/gro/content/certificates/login.asp)' if MessageBox(gPluginName, strMessage, 'OKCANCEL', 'WARNING', 2, dialogMain) == 2 then return end elseif txtDOB.bgcolor == gtblConstants.Red then OkOnlyMessage(strTitle, 'Date of Birth invalid', 'WARNING', dialogMain) return end elseif txtDOB.value ~= '' then OkOnlyMessage(strTitle, 'Enter either Age or Date of Birth (not both)', 'WARNING', dialogMain) return end end if tglIncNameInCitation.value == 'ON' or tglCitationToNames.value == 'ON' then local BlankName = false if txtGiven.value == '' or txtSurname.value == '' then BlankName = true elseif arrInd2 ~= nil and txtGiven2.value == '' or txtSurname2.value =='' then BlankName = true end if BlankName then -- At least one missing - flag all empty if txtGiven.value == '' then txtGiven.bgcolor = gtblConstants.Yellow end if txtSurname.value == '' then txtSurname.bgcolor = gtblConstants.Yellow end if arrInd2 ~= nil then if txtGiven2.value == '' then txtGiven2.bgcolor = gtblConstants.Yellow end if txtSurname2.value == '' then txtSurname2.bgcolor = gtblConstants.Yellow end end OkOnlyMessage(strTitle, 'Names required for inclusion in citation', 'WARNING', dialogMain) return end end -- Determine actions to be taken local strEvent = '' local ptrRecord = fhNewItemPtr() if Event == 'Birth' then strEvent = 'BIRT' ptrRecord = arrInd1.Ptr elseif Event == 'Marriage' then strEvent = 'MARR' ptrRecord = arrFam.Ptr else strEvent = 'DEAT' ptrRecord = arrInd1.Ptr end local EventDate = gtblConstants.Periods[tonumber(listQtrMonth.value)] .. ' ' .. listYear.value local dteEventDate = fhNewDate() dteEventDate:SetValueAsText(EventDate) arrActions.ptrRecord = ptrRecord arrActions.strEvent = strEvent arrActions.dteEventDate = dteEventDate if not gtblSettings.UseGRODistricts then arrActions.txtEventPlace = listDistrictName.value else arrActions.txtEventPlace = listDistrictName.valuestring:match('^(.*)%s%[') or listDistrictName.valuestring end local bContinue bContinue, arrActions.Event, arrActions.EventDate, arrActions.EventPlace = CompareEvent(ptrRecord, Event, strEvent, EventDate, arrActions.txtEventPlace, dialogMain) if not bContinue then return -- User cancelled end function CompareName(ExistingName, EnteredGiven, EnteredSurname) -- Compares names and offers to update. Does not offer if entered is shorter because it has initials -- that match instead of given names local ExistingGiven, ExistingSurname, EnteredFullName if ExistingName:sub(-1) == '/' then ExistingGiven = ExistingName:match('^[^/]*') ExistingSurname = ExistingName:match('%b//'):gsub('/','') EnteredFullName = EnteredGiven .. ' /' .. EnteredSurname .. '/' elseif ExistingName:sub(1, 1) == '/' then ExistingGiven = ExistingName:match('[^/]*$') ExistingSurname = ExistingName:match('%b//'):gsub('/','') EnteredFullName = '/' .. EnteredSurname .. '/ ' .. EnteredGiven else -- Indeterminate name structure, can't do update return true, false -- Continue, don't update end ExistingGiven = ExistingGiven:match('^%s*(.-)%s*$') -- Remove leading/trailing spaces if ExistingName == EnteredFullName then return true, false -- Continue, don't update end local bOfferUpdate = false if EnteredSurname == ExistingSurname then local ExistingGivenNames = {} local EnteredGivenNames = {} for str in ExistingGiven:gmatch("([^ ]+)") do table.insert(ExistingGivenNames, str) end for str in EnteredGiven:gmatch("([^ ]+)") do table.insert(EnteredGivenNames, str) end if #ExistingGivenNames > #EnteredGivenNames then -- fewer given names entered so don't offer to update return true, false -- Continue, don't update else local i = 0 while i < #ExistingGivenNames do i = i + 1 if ExistingGivenNames[i] ~= EnteredGivenNames[i] and ExistingGivenNames[i]:sub(1,1) ~= EnteredGivenNames[i] then bOfferUpdate = true break end end if bOfferUpdate ~= true and #ExistingGivenNames < #EnteredGivenNames then -- More names/initials entered bOfferUpdate = true end end else bOfferUpdate = true -- Surname doesn't match end if bOfferUpdate then local strMsg = 'Name entered does not match current name\n\n' .. 'Current: ' .. ExistingName .. '\n' .. 'Entered: ' .. EnteredFullName .. '\n\n' .. 'Update name?' local Ans = MessageBox(strTitle, strMsg, 'YESNOCANCEL', 'QUESTION', 3, dialogMain) if Ans == 3 then return false -- Cancelled, don't continue elseif Ans == 1 then return true, true, EnteredFullName -- Continue, update, new name else return true, false -- Continue, don't update end end return true, false -- Continue, don't update end -- Adding citation to names - check for multiple names and if so select name number local Ind1Name, Ind2Name if tglCitationToNames.value == 'ON' then if Event ~= 'Marriage' then bContinue, arrActions.Ind1NameNo, Ind1Name = MultNameCheck(ptrRecord, Event, 'Individual', dialogMain) if not bContinue then return -- User cancelled end arrActions.ptrInd1 = ptrRecord -- Check if name is different to stored bContinue, arrActions.UpdateName, arrActions.NewName = CompareName(Ind1Name, txtGiven.value, txtSurname.value) if not bContinue then return -- User cancelled end else bContinue, arrActions.Ind1NameNo, Ind1Name = MultNameCheck(arrInd1.Ptr, Event, 'Husband', dialogMain) if not bContinue then return -- User cancelled end arrActions.ptrInd1 = arrInd1.Ptr -- Check if name is different to stored bContinue, arrActions.UpdateName, arrActions.NewName = CompareName(Ind1Name, txtGiven.value, txtSurname.value) if not bContinue then return -- User cancelled end if arrInd2 ~= nil then bContinue, arrActions.Ind2NameNo, Ind2Name = MultNameCheck(arrInd2.Ptr, Event, 'Wife', dialogMain) if not bContinue then return -- User cancelled end arrActions.ptrInd2 = arrInd2.Ptr -- Check if name2 is different to stored bContinue, arrActions.UpdateName2, arrActions.NewName2 = CompareName(Ind2Name, txtGiven2.value, txtSurname2.value) if not bContinue then return -- User cancelled end else arrActions.Ind2NameNo = 0 -- Family only has one parent (odd but possible!) end end else arrActions.Ind1NameNo = 0 arrActions.Ind2NameNo = 0 end -- Adding birth citation to mother's name - check for mother's record if Event == 'Birth' and tglCitationToMother.value == 'ON' then arrActions.CitationMother = true if arrInd1.ptrMother:IsNull() then arrActions.MotherName = gtblSettings.NewMotherGivenName .. ' /'.. txtMother.value .. '/' local strMessage if arrInd1.ptrFamily:IsNull() then arrActions.Mother = 'F' strMessage = 'Create new family and add Mother?' else arrActions.Mother = 'I' strMessage = 'Add Mother to family ' .. fhGetItemText(arrInd1.ptrFamily, '~'):sub(7) end strMessage = strMessage .. '?\n\nName: ' .. arrActions.MotherName if MessageBox(gPluginName, strMessage, 'OKCANCEL', 'QUESTION', 1, dialogMain) == 2 then return end arrActions.MotherNameNo = 1 elseif fhGetItemPtr(arrInd1.ptrMother, '~.NAME'):IsNull() then arrActions.Mother = 'A' arrActions.MotherName = gtblSettings.NewMotherGivenName .. ' /'.. txtMother.value .. '/' arrActions.MotherNameNo = 1 else local strCurrMother bContinue, arrActions.MotherNameNo, strCurrMother = MultNameCheck(arrInd1.ptrMother, Event, 'Mother', dialogMain) if not bContinue then return -- User cancelled end local MotherSurname = strCurrMother:match('/(.*)/') if txtMother.value:upper() ~= MotherSurname:upper() then local strNewMother = Replace(strCurrMother, '/' .. MotherSurname .. '/', '/' .. txtMother.value .. '/') -- Replace family name local strMessage = 'Mother\'s maiden name entered does not match existing name\n\n' .. 'Existing name: ' .. strCurrMother .. '\n' .. 'Maiden name entered: ' .. txtMother.value .. '\n\n' .. 'YES Change Mother\'s name to ' .. strNewMother .. '\n' .. 'NO Add citation without changing Mother\'s name' local response = MessageBox(gPluginName, strMessage, 'YESNOCANCEL', 'QUESTION', 1, dialogMain) if response == 1 then arrActions.Mother = 'U' arrActions.MotherName = strNewMother elseif response == 2 then arrActions.Mother = 'X' arrActions.MotherName = strCurrMother else return end else arrActions.Mother = 'X' arrActions.MotherName = strCurrMother end end end -- Death - decide if anything needs doing with age or birth date if Event == 'Death' then bContinue, arrActions.DeathAge, arrActions.ageDeathAge = DeathAge(ptrRecord, txtAge.value, dialogMain) if not bContinue then return -- User cancelled end bContinue, arrActions.DeathDOB, arrActions.dteDOB = DeathDOB(ptrRecord, txtAge.value, txtDOB.value, dteEventDate, dialogMain) if not bContinue then return -- User cancelled end end -- Have all the information - display what will be done & get confirmation to proceed local strHeader = 'Citation for ' .. Event .. ' of ' .. arrInd1['DispNameFull'] if Event == 'Marriage' then strHeader = strHeader .. '\nand ' if arrInd2 ~= nil then strHeader = strHeader .. arrInd2['DispNameFull'] else strHeader = strHeader .. '<>' end end strHeader = strHeader .. '\n\nConfirmation of changes' local lblHeading = iup.label{title = strHeader, fontstyle = 'Bold', alignment = 'ACENTER'} -- Data changes local strDataChanges local bDataChanges = false if Event ~= 'Marriage' then strDataChanges = arrInd1.DispName else strDataChanges = 'Family ' .. arrFam.DispName:sub(4) end local NewLine = '\n' .. string.rep(' ', 4) if Event ~= 'Marriage' and arrActions.UpdateName then local strNo = '' if arrActions.Ind1NameNo ~= 1 then strNo = '[' .. arrActions.Ind1NameNo .. ']' end strDataChanges = strDataChanges .. NewLine .. 'Change Name' .. strNo .. ' to: ' .. arrActions.NewName bDataChanges = true end if arrActions.Event then strDataChanges = strDataChanges .. NewLine .. 'Create ' .. Event .. ' Event' bDataChanges = true end if arrActions.EventDate == 'C' then strDataChanges = strDataChanges .. NewLine .. Event .. ' date: ' .. EventDate bDataChanges = true elseif arrActions.EventDate == 'U' then strDataChanges = strDataChanges .. NewLine .. 'Change ' .. Event .. ' date to: ' .. EventDate bDataChanges = true end if arrActions.EventPlace ~= 'X' then if arrActions.EventPlace == 'C' then strDataChanges = strDataChanges .. NewLine .. Event .. ' place: ' .. arrActions.txtEventPlace bDataChanges = true elseif arrActions.EventPlace == 'U' then strDataChanges = strDataChanges .. NewLine .. 'Change ' .. Event .. ' place to: ' .. arrActions.txtEventPlace bDataChanges = true end strDataChanges = strDataChanges .. NewLine .. Event .. ' address: Registration District' bDataChanges = true end if Event == 'Birth' and gtblSettings.AddLivingFlag -- Living to be set for 'recent' births and fhGetItemText(ptrRecord, '~._FLGS.__LIVING') ~= 'Y'then -- not needed if already set -- Calculate event date + maximum living age local dteTemp = fhNewDate() dteTemp:SetValueAsText(gtblConstants.Periods[tonumber(listQtrMonth.value)] .. ' ' .. tostring(tonumber(listYear.value) + gtblSettings.LivingMaxAge)) -- Compare to today local dteToday = fhNewDate() dteToday:SetSimpleDate(fhCallBuiltInFunction("today")) if dteTemp:Compare(dteToday) >= 0 then -- Could be alive check for death or burial if fhGetItemPtr(ptrRecord,'~.DEAT'):IsNull() and fhGetItemPtr(ptrRecord,'~.BURI'):IsNull()then arrActions.SetLiving = true strDataChanges = strDataChanges .. NewLine .. 'Set Living flag' bDataChanges = true else arrActions.SetLiving = false end end end if Event == 'Death' then if arrActions.DeathAge == 'A' then strDataChanges = strDataChanges .. NewLine .. 'Death Age: ' .. txtAge.value bDataChanges = true elseif arrActions.DeathAge == 'U' then strDataChanges = strDataChanges .. NewLine .. 'Change Death Age to: ' .. txtAge.value bDataChanges = true end if arrActions.DeathDOB == 'A' then strDataChanges = strDataChanges .. NewLine .. 'Birth Date: ' .. arrActions.dteDOB:GetDisplayText('COMPACT') bDataChanges = true elseif arrActions.DeathDOB == 'U' then strDataChanges = strDataChanges .. NewLine .. 'Change Birth Date to: ' .. arrActions.dteDOB:GetDisplayText('COMPACT') bDataChanges = true end -- Check for Living flag to be removed if fhGetItemText(ptrRecord, '~._FLGS.__LIVING') == 'Y' then arrActions.UnsetLiving = true strDataChanges = strDataChanges .. NewLine .. 'Unset Living flag' bDataChanges = true else arrActions.UnsetLiving = false end end if Event == 'Marriage' then if arrActions.UpdateName then local strNo = '' if arrActions.Ind1NameNo ~= 1 then strNo = '[' .. arrActions.Ind1NameNo .. ']' end strDataChanges = strDataChanges .. '\n' .. arrInd1.DispName .. NewLine .. 'Change Name' .. strNo .. ' to: ' .. arrActions.NewName bDataChanges = true end if arrActions.UpdateName2 then local strNo = '' if arrActions.Ind2NameNo ~= 1 then strNo = '[' .. arrActions.Ind2NameNo .. ']' end strDataChanges = strDataChanges .. '\n' .. arrInd2.DispName .. NewLine .. 'Change Name' .. strNo .. ' to: ' .. arrActions.NewName2 bDataChanges = true end end if not bDataChanges then strDataChanges = strDataChanges .. NewLine .. 'No changes' end if Event == 'Birth' and arrActions.CitationMother and arrActions.Mother ~= 'X' then if arrActions.Mother == 'A' then strDataChanges = strDataChanges .. '\n[unnamed person] (Mother)' .. NewLine .. 'Add Name: ' .. arrActions.MotherName bDataChanges = true elseif arrActions.Mother == 'U' then strDataChanges = strDataChanges .. '\n' .. fhGetItemText(ptrRecord, '~.~MOTH[1]>NAME[' .. arrActions.MotherNameNo .. ']:STORED') .. ' (Mother)' .. NewLine .. 'Change Name to: ' .. arrActions.MotherName bDataChanges = true else -- Mother to be added strDataChanges = strDataChanges .. '\nNew Individual (Mother)' .. NewLine .. 'Name: ' .. arrActions.MotherName .. NewLine .. 'Sex: Female' .. NewLine .. 'Family' if arrActions.Mother == 'F' then strDataChanges = strDataChanges .. ' (new): ' else -- arrActions.Mother == 'I' local ptrFather = fhGetItemPtr(ptrRecord,'~.FAMC>HUSB>') if not ptrFather:IsNull() then strDataChanges = strDataChanges .. ': Spouse ' .. fhGetItemText(ptrRecord, 'INDI.~FATH[1]>NAME[1]:STORED') else strDataChanges = strDataChanges .. ': ' end end end end local boxWidth = tostring(tonumber(dialogMain.Size:match('(%d+)')) - 43) -- Make this dialog the same width as dialogMain local frameActions = iup.frame{ iup.hbox{ iup.frame{ iup.hbox{ iup.label{title = strDataChanges:gsub('&', '&&')}; padding = '0x0', margin = '10x5', size = boxWidth .. 'x0' }, bgcolor = gtblConstants.White, sunken = 'YES' }, margin = '4x2', fontstyle = '' }, title = 'Data Updates', fontstyle = 'Bold' } -- Citation details local dteCitationDate = fhNewDate() if gtblSettings.CitationEntryDate > 1 then arrActions.CitationDate = true if gtblSettings.CitationEntryDate == 2 then dteCitationDate:SetSimpleDate(fhCallBuiltInFunction("today")) else dteCitationDate:SetValueAsText(EventDate) end arrActions.dteCitationDate = dteCitationDate else arrActions.CitationDate = false end arrActions.txtCitationAssess = gtblConstants.CitationAssess[tonumber(listCitationAssess.value)] if listCitationAssess.value ~= '1' then arrActions.CitationAssess = true else arrActions.CitationAssess = false end if txtCitationNote.value ~= '' then arrActions.CitationNote = true arrActions.txtCitationNote = txtCitationNote.value else arrActions.CitationNote = false end local strTmp = WhereWithin() arrActions.WhereWithin = strTmp local lblWhereWithin = iup.flatlabel{ -- This may be resized after dialog mapped if multi-line title = strTmp:gsub('&', '&&'), textwrap = 'YES', expand = 'VERTICAL' } strTmp = TextFromSrc() arrActions.TextFromSrc = strTmp local lblTextFromSrc = iup.flatlabel{ -- This may be resized after dialog mapped if multi-line title = strTmp:gsub('&','&&'), textwrap = 'YES', expand = 'VERTICAL' } local frameCitation = iup.frame{ iup.hbox{ iup.frame{ iup.gridbox{ iup.label{title = 'Source:'}, iup.label{title = gtblData.name .. '[' .. gtblData.id .. ']'}, iup.label{title = 'Where within:'}, lblWhereWithin, iup.label{title = 'Date:'}, iup.label{title = dteCitationDate:GetDisplayText('COMPACT')}, iup.label{title = 'Assessment:'}, iup.label{title = arrActions.txtCitationAssess}, iup.label{title = 'Note:'}, iup.label{title = arrActions.txtCitationNote}, iup.label{title = 'Text from Source:'}, lblTextFromSrc; orientation = 'horizontal', numdiv = 2, sizelin = -1, sizecol = -1, cgapcol = 4, size = boxWidth .. 'x0' }, bgcolor = gtblConstants.White, sunken = 'YES' }, margin = '4x2', fontstyle = '' }, title = 'Citation', fontstyle = 'Bold' } -- Facts to be given Citations local strFacts if Event ~= 'Marriage' then strFacts = Event:upper() .. ' : ' .. arrInd1.DispName else strFacts = 'MARRIAGE : ' .. arrFam.DispName:sub(7) end if tglCitationToNames.value == 'ON' then if arrActions.Ind1NameNo == 1 then strFacts = strFacts .. '\nNAME : ' .. Ind1Name else strFacts = strFacts .. '\nNAME[' .. arrActions.Ind1NameNo .. '] : ' .. Ind1Name end if Event == 'Marriage' then if arrActions.Ind2NameNo == 1 then strFacts = strFacts .. '\nNAME : ' .. Ind2Name elseif arrActions.Ind2NameNo > 1 then strFacts = strFacts .. '\nNAME[' .. arrActions.Ind2NameNo .. '] : ' .. Ind2Name end end end if Event == 'Birth' and arrActions.CitationMother then strFacts = strFacts .. '\nNAME' if arrActions.MotherNameNo ~= 1 then strFacts = strFacts .. '[' .. arrActions.MotherNameNo .. ']' end strFacts = strFacts .. ' : ' .. arrActions.MotherName .. ' (Mother)' end if Event == 'Death' and arrActions.DeathDOB ~= 'X' then strFacts = strFacts .. '\nBIRTH : ' .. arrInd1.DispName end local frameFacts = iup.frame{ iup.hbox{ iup.frame{ iup.hbox{ iup.label{title = strFacts}; padding = '0x0', margin = '10x5', size = boxWidth .. 'x0' }, bgcolor = gtblConstants.White, sunken = 'YES' }, margin = '4x2', fontstyle = '' }, title = 'Facts to be given citations', fontstyle = 'Bold' } -- Footer Buttons local bContinue local btnSave = iup.button{ title = 'Save', image = 'IUP_FileSave', padding = '10x0', action = function(self) bContinue = true return iup.CLOSE end } local btnCancel = iup.button{ title = 'Cancel', padding = '10x3', action = function(self) bContinue = false return iup.CLOSE end } local hboxFooter = iup.hbox{ btnSave, btnCancel; normalizesize = 'BOTH', margin = '0x5', gap = 40 } local dialogConfirm = iup.dialog{ iup.vbox{ lblHeading, frameActions, frameCitation, frameFacts, hboxFooter; margin = '15x5', gap = 5, alignment = 'ACENTER' }; resize = 'No', minbox = 'No', maxbox = 'No', title = 'GRO Index ' .. Event .. ' Citation', defaultesc = btnCancel, PARENTDIALOG = dialogMain } -- Resize citation labels to fit if they need multiple lines dialogConfirm:map() local MaxLabelWidth = boxWidth - 87 local bRefresh = false local Xwidth = dialogConfirm.Size local size = iup.GetAttribute(lblWhereWithin, 'SIZE') local isize = tonumber(size:match('^%d+')) if isize > MaxLabelWidth then if isize < MaxLabelWidth * 2 then lblWhereWithin.size = tostring(MaxLabelWidth) .. 'x16' -- two lines else lblWhereWithin.size = tostring(MaxLabelWidth) .. 'x24' -- three lines end bRefresh = true end size = iup.GetAttribute(lblTextFromSrc, 'SIZE') isize = tonumber(size:match('^%d+')) if isize > MaxLabelWidth then if isize < MaxLabelWidth * 2 then lblTextFromSrc.size = tostring(MaxLabelWidth) .. 'x16' -- two lines else lblTextFromSrc.size = tostring(MaxLabelWidth) .. 'x24' -- three lines end bRefresh = true end if bRefresh then iup.SetAttribute(dialogConfirm, 'SIZE', '') iup.Refresh(dialogConfirm) end local dialogMainPosX, dialogMainPosY = dialogMain.ScreenPosition:match('([^,]+),([^,]+)') dialogMainPosX = tonumber(dialogMainPosX) dialogMainPosY = tonumber(dialogMainPosY) + 32 -- Down relative to dialogMain by default caption height dialogConfirm:popup(dialogMainPosX, dialogMainPosY) dialogConfirm:destroy() if not bContinue then Exit = false return end -- User has confirmed to go ahead and make the changes Exit = true return iup.CLOSE end -- function btnContinue:action ------------------------------------------------------------------------------------------------------------------------ dialogMain:map() WhereWithinUpdate() TextFromSrcUpdate() local ScreenPosX = gtblSettings.ScreenPosX local ScreenPosY = gtblSettings.ScreenPosY local bFirstMove = true function dialogMain:move_cb(x, y) if bFirstMove then bFirstMove = false -- Ignore call back that occurs during popup of dialog else ScreenPosX = x ScreenPosY = y end end function dialogMain:show_cb(state) -- Make sure dialog is not off the top of the screen if state == 0 then local x, y = dialogMain.screenposition:match('([^,]*),([^,]*)') if tonumber(y) < 0 then ScreenPosX = x ScreenPosY = 0 dialogMain:showxy(ScreenPosX, ScreenPosY) end end end dialogMain:popup(ScreenPosX, ScreenPosY) dialogMain:destroy() if ScreenPosX ~= gtblSettings.ScreenPosX or ScreenPosY ~= gtblSettings.ScreenPosY then gtblSettings.ScreenPosX = ScreenPosX gtblSettings.ScreenPosY = ScreenPosY SaveSettings() end if Exit then DoActions(arrActions) end return Exit end -- function MainDialog --====================================================================================================================== -- Populate an iuplist with values from an array function PopulateList(iuplist, tblVals) local is_indexed = (rawget( tblVals, 1 ) ~= nil) if not is_indexed then local i=1 for k, _ in pairs(tblVals) do iuplist[tostring(i)]=k i=i+1 end else for i, v in ipairs(tblVals) do iuplist[tostring(i)]=v end end end ------------------------------------------------------------------------------------------------------------------------ function ToggleValue(bValue) -- Toggle on/off from setting true/false if bValue then return'ON' else return 'OFF' end end -- function SetToggleValue --====================================================================================================================== -- Check for existing event and if there compare data; user decides what to update if existing data function CompareEvent(ptrRecord, Event, strEvent, EventDate, District, dialogMain) -- Returns: bContinue - false if user cancelled -- Event - true if event to be created, false if already exists -- Date - "C"reate, "U"pdate, "X" do nothing -- Place (and address) - "C"reate, "U"pdate, "X" do nothing local ptrEvent = fhNewItemPtr() local ptrField = fhNewItemPtr() ptrEvent:MoveTo(ptrRecord, '~.' .. strEvent) if ptrEvent:IsNull() then return true, true, 'C', 'C' end ptrField:MoveTo(ptrEvent,'~.DATE') local ExistingDate = fhGetValueAsDate(ptrField):GetDisplayText('COMPACT') local ExistingPlace = fhGetItemText(ptrEvent, '~.PLAC') local ExistingAddress = fhGetItemText(ptrEvent, '~.ADDR') if ExistingDate == '' and ExistingPlace == '' then return true, false, 'C', 'C' end -- Compare data with existing event data if EventDate == ExistingDate and District == ExistingPlace then return true, false, 'X', 'X' end -- Get user to choose whether to update event local hboxExistingDate = iup.hbox{iup.label{title = 'Date:', size = '50x0'}, iup.label{title = ExistingDate}} local hboxExistingPlace = iup.hbox{ iup.label{title = 'Place:', size = '50x0'}, iup.label{title = ExistingPlace:gsub('&', '&&')}} local hboxExistingAddress = iup.hbox{ iup.label{title = 'Address:', size = '50x0'}, iup.label{title = ExistingAddress:gsub('&', '&&')}} local frameExisting = iup.frame{ iup.hbox{ iup.frame{ iup.hbox{ iup.vbox{hboxExistingDate, hboxExistingPlace,hboxExistingAddress; gap = 0, margin = '2x2' }, iup.fill{}; padding = '0x0', gap = 15, margin = '10x5' }, bgcolor = gtblConstants.White, sunken = 'YES' }, margin = '4x2', fontstyle = '' }, title = 'Existing ' .. Event .. ' Details', fontstyle = 'Bold' } local hboxEventDate = iup.hbox{iup.label{title = 'Date:', size = '50x0'}, iup.label{title = EventDate}} local hboxEventPlace = iup.hbox{iup.label{title = 'Place:', size = '50x0'}, iup.label{title = District:gsub('&', '&&')}} local hboxEventAddress = iup.hbox{iup.label{title = 'Address:', size = '50x0'}, iup.label{title = 'Registration District' }} local frameEvent = iup.frame{ iup.hbox{ iup.frame{ iup.hbox{ iup.vbox{hboxEventDate, hboxEventPlace,hboxEventAddress; gap = 0, margin = '2x2' }, iup.fill{}; padding = '0x0', gap = 15, margin = '10x5' }, bgcolor = gtblConstants.White, sunken = 'YES' }, margin = '4x2', fontstyle = '' }, title = 'Suggested by this Citation', fontstyle = 'Bold' } local tglPlaceOnly = iup.toggle{name = 'Place', title = 'Replace just ' .. Event .. ' place* with suggested place'} local tglDateOnly = iup.toggle{name = 'Date', title = 'Replace just ' .. Event .. ' date with suggested date'} local tglBoth = iup.toggle{name = 'Both', title = 'Replace ' .. Event .. ' date and place* with suggested date and place'} local tglNeither = iup.toggle{name = 'Neither', title = 'Don\'t change '.. Event .. ' event'} local radioAction = iup.radio{ iup.vbox{ tglPlaceOnly, tglDateOnly, tglBoth, tglNeither, iup.label{title = '* Address will be changed when replacing Place'}; gap = 1, margin = '10x5', } } if EventDate == ExistingDate then tglDateOnly.active = 'NO' tglBoth.active = 'NO' elseif District == ExistingPlace then tglPlaceOnly.active = 'NO' tglBoth.active = 'NO' end radioAction.value = tglNeither local bContinue local btnContinue = iup.button{ title = 'Continue', padding = '10x0', action = function(self) bContinue = true return iup.CLOSE end } local btnCancel = iup.button{ title = 'Cancel', padding = '10x3', action = function(self) bContinue = false return iup.CLOSE end } local hboxFooter = iup.hbox{ iup.fill{}, btnContinue, btnCancel, iup.fill{}; normalizesize = 'BOTH', margin = '0x5', gap = 40 } local dialogEventData = iup.dialog{ iup.vbox{frameExisting, frameEvent, radioAction, hboxFooter; margin = '15x5', gap = 5}; resize = 'No', minbox = 'No', maxbox = 'No', title = 'GRO Index ' .. Event .. ' Citation', defaultesc = btnCancel, PARENTDIALOG = dialogMain } dialogEventData:popup(iup.CENTERPARENT, iup.CENTERPARENT) local DateAction = 'X' local PlaceAction = 'X' if bContinue then if tglDateOnly.value == 'ON' or tglBoth.value == 'ON' then if ExistingDate == '' then DateAction = 'C' else DateAction = 'U' end end if tglPlaceOnly.value == 'ON' or tglBoth.value == 'ON' then if ExistingPlace == '' then PlaceAction = 'C' else PlaceAction = 'U' end end end dialogEventData:destroy() return bContinue, false, DateAction, PlaceAction end -- function CompareEvent --====================================================================================================================== -- Death: Decide if age in the death event needs adding/updating function DeathAge(ptrRecord, Age, dialogMain) -- Returns: bContinue - false if user cancelled -- Action - "A" Add DOB to birth event, "U"pdate, "X" do nothing -- Death age object if Age == '' then return true, 'X', nil end local ageEntered = fhNewAge() ageEntered:SetValue('=', tonumber(Age)) local ageExisting local ptrEvent = fhNewItemPtr() local ptrField = fhNewItemPtr() ptrEvent:MoveTo(ptrRecord, '~.DEAT') if ptrEvent:IsNull() then return true, 'A', ageEntered else ptrField:MoveTo(ptrEvent, '~.AGE') ageExisting = fhGetValueAsAge(ptrField) end if ageExisting:IsNull() then return true, 'A', ageEntered end if ageEntered:GetYears() == ageExisting:GetYears() then return true, 'X', '' end local strMessage = 'Age does not match existing death age\n\n' .. 'Existing: ' .. ageExisting:GetDisplayText() .. '\n' .. 'Entered: ' .. ageEntered:GetDisplayText() .. '\n\n' .. 'Overwrite death Age?' local Response = MessageBox('GRO Index Death Citation', strMessage, 'YESNOCANCEL', 'QUESTION', 3, dialogMain) if Response == 3 then return false, '' end if Response == 1 then return true, 'U', ageEntered else return true, 'X', nil end end --====================================================================================================================== -- Death: decide whether to change individuals date of birth function DeathDOB(ptrRecord, Age, DOB, dteEventDate, dialogMain) -- Returns: bContinue - false if user cancelled -- Action - "A" Add DOB to birth event, "U"pdate, "C"itation only, "X" do nothing -- Date object containing date of birth if Age == '' and DOB == '' then return true, 'X', fhNewDatePt() -- Neither entered so nothing to do end local ptrEvent = fhNewItemPtr() local ptrField = fhNewItemPtr() local dteExistingDOB ptrEvent:MoveTo(ptrRecord, '~.BIRT') if ptrEvent:IsNull() then dteExistingDOB = fhNewDate() else ptrField:MoveTo(ptrEvent, '~.DATE') dteExistingDOB = fhGetValueAsDate(ptrField) end local dteNewDOB = fhNewDate() if Age ~= '' then dteNewDOB = fhu.calcBirth(dteEventDate, Age) else dteNewDOB:SetValueAsText(DOB) end local hboxExisting = iup.hbox{iup.label{title = 'Existing Date:', size = '120x0'}, iup.label{title = dteExistingDOB:GetDisplayText('COMPACT')}} local hboxSuggested = iup.hbox{iup.label{title = 'Date from GRO Death:', size = '120x0'}, iup.label{title = dteNewDOB:GetDisplayText('COMPACT')}} local frameDOB = iup.frame{ iup.hbox{ iup.frame{ iup.hbox{ iup.vbox{hboxExisting, hboxSuggested; gap = 0, margin = '2x2' }, iup.fill{}; padding = '0x0', gap = 15, margin = '10x5' }, bgcolor = gtblConstants.White, sunken = 'YES' }, margin = '4x2', fontstyle = '' }, title = 'Birth Date', fontstyle = 'Bold' } local AddUpdate if dteExistingDOB:IsNull() then AddUpdate = 'Add' else AddUpdate = 'Update' end local tglAddUpdate = iup.toggle{title = AddUpdate .. ' Birth Date'} local tglCitation = iup.toggle{title = 'Only add Citation to Birth Date'} local tglNothing = iup.toggle{title = 'Don\'t ' .. AddUpdate .. ' Birth Date or add citation'} local radioAction = iup.radio{ iup.vbox{ tglAddUpdate, tglCitation, tglNothing; gap = 1, margin = '10x5' } } if not dteExistingDOB:IsNull() then if dteNewDOB:GetValueAsText() == dteExistingDOB:GetValueAsText() then tglAddUpdate.active = 'NO' tglNothing .title = 'Don\'t add citation' radioAction.value = tglCitation else radioAction.value = tglNothing end else tglCitation.active = 'NO' -- Disable citation only if no existing date end local bContinue local btnContinue = iup.button{ title = 'Continue', padding = '10x0', action = function(self) bContinue = true return iup.CLOSE end } local btnCancel = iup.button{ title = 'Cancel', padding = '10x3', action = function(self) bContinue = false return iup.CLOSE end } local hboxFooter = iup.hbox{ iup.fill{}, btnContinue, btnCancel, iup.fill{}; normalizesize = 'BOTH', margin = '0x5', gap = 40 } local dialogDeathDOB = iup.dialog{ iup.vbox{frameDOB, radioAction, hboxFooter; margin = '15x5', gap = 5}; resize = 'No', minbox = 'No', maxbox = 'No', title = 'GRO Index Death Citation', defaultesc = btnCancel, PARENTDIALOG = dialogMain } dialogDeathDOB:popup(iup.CENTERPARENT, iup.CENTERPARENT) local Action = 'X' if bContinue then if tglAddUpdate.value == 'ON' then if dteExistingDOB:IsNull() then Action = 'A' else Action = 'U' end elseif tglCitation.value == 'ON' then Action = 'C' end end dialogDeathDOB:destroy() return bContinue, Action, dteNewDOB end -- function DeathDOB --====================================================================================================================== -- Citations being added to name(s) - check for multiple and if so pick which one function MultNameCheck(ptrRecord, Event, Role, dialogMain) local ptrField = fhNewItemPtr() ptrField:MoveTo(ptrRecord, '~.NAME') if ptrField:IsNull() then OkOnlyMessage(gPluginName, Role .. ' has no name', 'ERROR', dialogMain) return false, 0 end local arrNames = {} while ptrField:IsNotNull() do table.insert(arrNames, fhGetItemText(ptrField, '~:STORED')) ptrField:MoveNext('SAME_TAG') end if #arrNames == 1 then return true, 1, arrNames[1] end lblHeading = iup.label{title= Role .. ' has Multiple names\n\nSelect name to be given the citation', expand = 'HORIZONTAL'} listNames = iup.list{visiblecolumns = 25, visiblelines = 3} PopulateList(listNames, arrNames) local frameNames = iup.frame{ iup.hbox{ iup.frame{ iup.hbox{ iup.vbox{lblHeading, listNames; gap = 2, margin = '2x2' }, iup.fill{}; padding = '0x0', gap = 15, margin = '10x5' }, bgcolor = gtblConstants.White, sunken = 'YES' }, margin = '4x2', fontstyle = '' }, title = 'Multiple Names', fontstyle = 'Bold' } local bContinue local btnContinue = iup.button{ title = 'Continue', padding = '10x0' } function btnContinue:action() if listNames.value == "0" then OkOnlyMessage(gPluginName, "Name not selected", 'WARNING', dialogMain) return end bContinue = true return iup.CLOSE end local btnCancel = iup.button{ title = 'Cancel', padding = '10x3', action = function(self) bContinue = false return iup.CLOSE end } local hboxFooter = iup.hbox{ iup.fill{}, btnContinue, btnCancel, iup.fill{}; normalizesize = 'BOTH', margin = '0x5', gap = 40 } local dialogMultName = iup.dialog{ iup.vbox{frameNames, hboxFooter; margin = '15x5', gap = 5}; resize = 'No', minbox = 'No', maxbox = 'No', title = 'GRO Index ' .. Event .. ' Citation', defaultesc = btnCancel, PARENTDIALOG = dialogMain } dialogMultName:popup(iup.CENTERPARENT, iup.CENTERPARENT) local SelName = listNames.value local SelNameDisp = arrNames[tonumber(SelName)] dialogMultName:destroy() return bContinue, tonumber(SelName), SelNameDisp end -- function MultNameCheck --====================================================================================================================== function DoActions(arrActions) --[[All the actions to be taken and data needed is in arrActions arrActions.ptrRecord ptr Individual or family record to have event added .strEvent str Event item (BIRT, MARR or DEAT) .Event boo true=Create event, false= event exists .EventDate str 'C'reate, 'U'pdate, 'X' do nothing .dteEventDate dte Event date object .EventPlace str 'C'reate, 'U'pdate, 'X' do nothing .txtEventPlace str Full place of district .DeathAge str 'C'reate, 'U'pdate, 'X' do nothing .ageDeathAge age Age at death .DeathDOB str 'A'dd DOB to birth event, 'U'pdate DOB, 'C'itation only, 'X' do nothing .dteDOB dte Date of birth date object .UpdateName boo Individidual's name to be updated (birth, death and 1st partner of marriage) .NewName str New name for update .UpdateName2 boo Name of 2nd partner of marriage to be updated .NewName2 str New name for 2nd partner .Ind1NameNo str Individual 1 Number of name for citation (0 if no citation to be added) .ptrInd1 ptr Individual 1 record for name citation (marriage only) .Ind2NameNo str Individual 2 Number of name for citation (0 if no citation to be added) .ptrInd2 ptr Individual 2 record for name citation (marriage only) .CitationMother boo True = Add citation to mother's name .MotherNameNo str Mother - Number of name for citation and, if required, updating .Mother str 'U'pdate name, 'F'amily and individual to be created, 'I' Create individual and add to existing family as mother, 'A'dd name to existing unnamed person, 'X' do nothing .MotherName str Full Name of Mother .CitationDate boo True=Add date to citation .dteCitationDate dte Citation date object .CitationAssess boo true=Add assessment to citation .txtCitationAssess str Assessment .SetLiving boo true=Set Living flag (birth only) .UnsetLiving boo true=Delete living flag (death only) .WhereWithin str Citation's Where Within .TextFromSrc str Citation's Text From Source - may be blank ]] if DEBUG_MODE and debug then io.write(dumpstr(arrActions, '==Actions==')) end local ptrEvent = fhNewItemPtr() local ptrField = fhNewItemPtr() local bOK if arrActions.strEvent ~= 'MARR' then if arrActions.UpdateName then ptrField:MoveTo(arrActions.ptrRecord, '~.NAME[' .. arrActions.Ind1NameNo .. ']') bOK = fhSetValueAsText(ptrField, arrActions.NewName) if not bOK then error('Updating Name') end end else if arrActions.UpdateName then ptrField:MoveTo(arrActions.ptrInd1, '~.NAME[' .. arrActions.Ind1NameNo .. ']') bOK = fhSetValueAsText(ptrField, arrActions.NewName) if not bOK then error('Updating Name') end end if arrActions.UpdateName2 then ptrField:MoveTo(arrActions.ptrInd2, '~.NAME[' .. arrActions.Ind2NameNo .. ']') bOK = fhSetValueAsText(ptrField, arrActions.NewName2) if not bOK then error('Updating Name 2') end end end ptrEvent:MoveTo(arrActions.ptrRecord, '~.' .. arrActions.strEvent) if arrActions.Event then -- Event to be created if ptrEvent:IsNotNull() then error('Event found when expecting to create event') end ptrEvent = fhCreateItem(arrActions.strEvent, arrActions.ptrRecord) else if ptrEvent:IsNull() then error('Event not found when expecting to update event') end end if arrActions.EventDate ~= 'X' then ptrField:MoveTo(ptrEvent, '~.DATE') if arrActions.EventDate == 'C' then if ptrField:IsNotNull() then if fhGetDisplayText(ptrField, '', 'min') ~= '' then -- Field there - should be empty error('Field found when expecting to create date') end else ptrField = fhCreateItem("DATE",ptrEvent) end else if ptrField:IsNull() then error('Field not found when expecting to update date') end end bOK = fhSetValueAsDate(ptrField, arrActions.dteEventDate) if not bOK then error('Setting event date') end -- Check date doesn't give fh warnings local i = 0 local strWarning local strMessage = '' repeat i = i + 1 strWarning = fhCallBuiltInFunction("GetDataWarning", ptrField, i) if strWarning ~= '' then strMessage = strMessage .. '\n' .. strWarning end until strWarning == '' if strMessage ~= '' then strMessage = strMessage .. '\n\nDo you wish to undo the entry?' local Response = MessageBox('Event date warning', strMessage, 'YESNO', 'WARNING' , 1) if Response == 1 then error('\nPlugin aborting - select \'Yes\' to undo this entry') end end end if arrActions.EventPlace ~= 'X' then ptrField:MoveTo(ptrEvent, '~.PLAC') if arrActions.EventPlace == 'C' then if ptrField:IsNotNull() then if fhGetItemText(ptrField, '~') ~= '' then -- Field there - should be empty error('Field found when expecting to create place') end else ptrField = fhCreateItem("PLAC",ptrEvent) end else if ptrField:IsNull() then error('Field not found when expecting to update place') if not bOK then error('Setting age') end end end bOK = fhSetValueAsText(ptrField,arrActions.txtEventPlace) if not bOK then error('Setting Event Place') end ptrField:MoveTo(ptrEvent, '~.ADDR') if ptrField:IsNull() then ptrField = fhCreateItem('ADDR', ptrEvent) end fhSetValueAsText(ptrField, 'Registration District') end if arrActions.strEvent == 'DEAT' then if arrActions.DeathAge ~= 'X' then ptrField:MoveTo(ptrEvent,"~.AGE") if ptrField:IsNull() then ptrField = fhCreateItem("AGE",ptrEvent) end bOK = fhSetValueAsAge(ptrField, arrActions.ageDeathAge) if not bOK then error('Setting age') end end if arrActions.DeathDOB == 'A' or arrActions.DeathDOB == 'U' then ptrEvent:MoveTo(arrActions.ptrRecord, '~.BIRT') if ptrEvent:IsNull() then ptrEvent = fhCreateItem('BIRT', arrActions.ptrRecord) end ptrField:MoveTo(ptrEvent,'~.DATE') if ptrField:IsNull() then ptrField = fhCreateItem('DATE',ptrEvent) end bOK = fhSetValueAsDate(ptrField, arrActions.dteDOB) if not bOK then error('Setting DOB') end -- Check date doesn't give fh warnings local i = 0 local strWarning local strMessage = '' repeat i = i + 1 strWarning = fhCallBuiltInFunction("GetDataWarning", ptrField, i) if strWarning ~= '' then strMessage = strMessage .. '\n' .. strWarning end until strWarning == '' if strMessage ~= '' then strMessage = strMessage .. '\n\nDo you wish to undo the entry?' local Response = MessageBox('Date of birth warning', strMessage, 'YESNO', 'WARNING' , 1) if Response == 1 then error('\nPlugin aborting - select \'Yes\' to undo this entry') end end end if arrActions.UnsetLiving then ptrField:MoveTo(arrActions.ptrRecord, '~._FLGS.__LIVING') bOK = fhDeleteItem(ptrField) if not bOK then error('Unsetting Living Flag') end end end if arrActions.strEvent == 'BIRT' then if arrActions.SetLiving then local ptrFlags = fhGetItemPtr(arrActions.ptrRecord, '~._FLGS') if ptrFlags:IsNull() then ptrFlags = fhCreateItem("_FLGS", arrActions.ptrRecord) -- Create if it doesn't exist already end local ptrFlag = fhCreateItem('__LIVING', ptrFlags) if ptrFlag:IsNull() then error ('Setting living flag') end end end local ptrMother = fhNewItemPtr() local ptrFamily = fhNewItemPtr() ptrMother = fhGetItemPtr(arrActions.ptrRecord,'~.FAMC>WIFE>') -- May be null but will be set below if Mother is added if arrActions.CitationMother and arrActions.Mother ~= 'X' then -- Data changes needed for Mother if arrActions.Mother == 'U' then -- Change Mother's name ptrField:MoveTo(ptrMother, '~.NAME[' .. arrActions.MotherNameNo .. ']') bOK = fhSetValueAsText(ptrField, arrActions.MotherName) if not bOK then error('Updating Mother\'s name') end elseif arrActions.Mother == 'A' then -- Add Mother's name to currently unnamed Mother ptrField = fhGetItemPtr(ptrMother, '~.NAME') -- Name field could be present but empty if ptrField:IsNull() then ptrField = fhCreateItem('NAME', ptrMother) end bOK = fhSetValueAsText(ptrField, arrActions.MotherName) if not bOK then error('Adding Mother\'s name') end else -- Must be 'F' or 'I' if arrActions.Mother == 'F' then -- Family (and Mother) to be created ptrFamily = fhCreateItem('FAM') -- Create family ptrField = fhCreateItem('CHIL', ptrFamily) bOK = fhSetValueAsLink(ptrField, arrActions.ptrRecord) -- Link individual as child in new family if not bOK then error ('Creating new family and linking child') end else ptrFamily = fhGetItemPtr(arrActions.ptrRecord,'~.FAMC>') if ptrFamily:IsNull() then error('Expected family not found') end end -- Create mother and add to existing family or newly created family ptrMother = fhCreateItem('INDI') ptrField = fhCreateItem('NAME', ptrMother) bOK = fhSetValueAsText(ptrField, arrActions.MotherName) if not bOK then error('Setting new Mother\'s name') end ptrField = fhCreateItem('SEX', ptrMother) bOK = fhSetValueAsText(ptrField, 'F') if not bOK then error('Setting new Mother\'s sex') end ptrField =fhGetItemPtr(ptrFamily, '~.WIFE') -- There could be an empty WIFE if ptrField:IsNull() then ptrField = fhCreateItem('WIFE', ptrFamily) end bOK = fhSetValueAsLink(ptrField, ptrMother) if not bOK then error('Adding new Mother to family') end end end -------------------------------------------------------------------------------------------------------------------- function AddCitation(ptrRecord, fact) local bOK local ptrFact = fhNewItemPtr() local ptrField = fhNewItemPtr() local ptrData = fhNewItemPtr() ptrFact:MoveTo(ptrRecord, '~.' .. fact) if ptrFact:IsNull() then error('Fact ' .. fact .. ' not found on ' .. fhGetDisplayText(ptrRecord)) end local ptrCitation = fhCreateItem('SOUR', ptrFact) bOK = fhSetValueAsLink(ptrCitation, gtblData.ptr) if not bOK then error('Setting citation link on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact) end ptrField = fhCreateItem('PAGE', ptrCitation) if ptrField:IsNull() then error('Create PAGE failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR') end bOK = fhSetValueAsText(ptrField, arrActions.WhereWithin) if not bOK then error('Setting Where Within on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.PAGE') end ptrData = fhCreateItem('DATA', ptrCitation) if ptrData:IsNull() then error('Create DATA failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR') end if arrActions.CitationDate then ptrField = fhCreateItem('DATE', ptrData) if ptrField:IsNull() then error('Create DATE failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA') end bOK = fhSetValueAsDate(ptrField, arrActions.dteCitationDate) if not bOK then error('Setting date on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA.DATE') end end if arrActions.TextFromSrc ~= '' then ptrField = fhCreateItem('TEXT', ptrData) if ptrField:IsNull() then error('Create TEXT failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA') end bOK = fhSetValueAsText(ptrField, arrActions.TextFromSrc) if not bOK then error('Setting Text from Source on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.DATA.TEXT') end end if arrActions.CitationAssess then ptrField = fhCreateItem('QUAY', ptrCitation) if ptrField:IsNull() then error('Create QUAY failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR') end bOK = fhSetValueAsText(ptrField, arrActions.txtCitationAssess) if not bOK then error('Setting Assessment on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.QUAY') end end if arrActions.CitationNote then ptrField = fhCreateItem('NOTE2', ptrCitation) if ptrField:IsNull() then error('Create NOTE2 failed on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR') end bOK = fhSetValueAsText(ptrField, arrActions.txtCitationNote) if not bOK then error('Setting Assessment on ' .. fhGetDisplayText(ptrRecord) .. ' ' .. fact .. '.SOUR.NOTE2') end end end --function AddCitation -------------------------------------------------------------------------------------------------------------------- AddCitation(arrActions.ptrRecord, arrActions.strEvent) -- Add citation to event if arrActions.Ind1NameNo ~= 0 then AddCitation(arrActions.ptrInd1, 'NAME[' .. arrActions.Ind1NameNo .. ']') -- Add citation to name (or 1st name in a marriage) end if arrActions.strEvent == 'MARR' and arrActions.Ind2NameNo ~= 0 then AddCitation(arrActions.ptrInd2, 'NAME[' .. arrActions.Ind2NameNo .. ']') -- Add citation to 2nd name in a marriage end if arrActions.strEvent == 'DEAT' and arrActions.DeathDOB ~= "X" then AddCitation(arrActions.ptrRecord, 'BIRT') -- Add citation to birth (Death event) end if arrActions.strEvent == 'BIRT' and arrActions.CitationMother then AddCitation(ptrMother, 'NAME[' .. arrActions.MotherNameNo .. ']') end end -- function DoActions --====================================================================================================================== function GRODistricts(ParentDialog) --[[Maintain the list of GRO districts Called from main BMD entry screen or from settings ]] local dialogGRODistricts, listPlaces, listGROPlaces, btnSaveYears -- Forward declares local tblYears = {} function Populate_listPlaces() listPlaces['1'] = nil -- Empty list for i,v in pairs(tblPlaces) do if not IntblGROPlaces(tblGROPlaces, v[1]) then listPlaces[tostring(i)] = v[1] else listPlaces[tostring(i)] = v[1] ..' *' end end end function IntblGROPlaces(Table, PlaceName) for _, v in pairs(tblGROPlaces) do if PlaceName == v[1] then return true end if PlaceName < v[1] then -- List is alphbetic order and we've gone past where it should be break end end return false end -- Create list box with all places, existing tblGROPlaces marked * plus edit box to allow entry of totally new listPlaces = iup.list{dropdown = 'YES', editbox = 'YES', visibleitems = 10, expand = 'HORIZONTAL'} Populate_listPlaces() local btnUKBMD = iup.button{ title = 'UKBMD District Information', image =iup.image{ width = 12, height = 12, pixels = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 }, colors = {"0 0 0", "255 255 255"} }, imageposition = 'RIGHT', spacing = '8', padding = '8x0', action = function() fhShellExecute('https://www.ukbmd.org.uk/reg/districts/index.html') end } local btnAdd = iup.button{title = 'Add Place to GRO Districts', padding = '8x2'} function listPlaces:valuechanged_cb() if not IntblGROPlaces(tblGROPlaces, listPlaces.value:gsub('%s%*?$', '')) then if listPlaces.value:find('%*$') then local SavePos = listPlaces.caretpos listPlaces.value = listPlaces.value:sub(1,-3) listPlaces.caretpos = SavePos end btnAdd.visible = 'YES' else if not listPlaces.value:find('%*$') then local SavePos = listPlaces.caretpos listPlaces.value = listPlaces.value .. ' *' listPlaces.caretpos = SavePos end btnAdd.visible= 'NO' end end local frameAddDistrict = iup.frame{ iup.vbox{ iup.frame{ iup.vbox{ iup.label{title = 'Select or enter place to add to GRO Districts (* Existing GRO district)'}, listPlaces, iup.hbox{btnUKBMD, iup.fill{}, btnAdd; normalizesize = 'VERTICAL', margin = '0x0'}; gap = 2, margin = '4x4', fontstyle= '' }, sunken = 'YES', margin = '4x0' }, margin = '4x0' }; title = 'Add GRO district', fontstyle = 'Bold' } function btnAdd:action() if listPlaces.value ~= '' then local ptrPlace = fhNewItemPtr() for i, v in pairs(tblPlaces) do if listPlaces.value == v[1] then ptrPlace:MoveToRecordById('_PLAC', v[2]) break end if listPlaces.value < v[1] then -- List is in alpha order and we've gone past where it should be break end end if ptrPlace:IsNull() then ptrPlace = fhCreateItem('_PLAC') if ptrPlace:IsNull() then error('Creating place record') end local ptrText = fhCreateItem('TEXT', ptrPlace, true) -- ReuseEmpties flag set if not fhSetValueAsText(ptrText, listPlaces.value) then error ('Setting Place text') end table.insert(tblPlaces,{fhGetDisplayText(ptrPlace), fhGetRecordId(ptrPlace)}) table.sort(tblPlaces, function(k1, k2) return k1[1] < k2[1] end) end local ptrNote = fhCreateItem('NOTE2', ptrPlace) if not fhSetValueAsText(ptrNote, 'GRO District') then error ('Adding GRO District Note') end table.insert(tblGROPlaces,{fhGetDisplayText(ptrPlace), '', fhGetRecordId(ptrPlace)}) table.sort(tblGROPlaces, function(k1, k2) return k1[1] < k2[1] end) Populate_listPlaces() PopulateListFromtblGROPlaces(listGROPlaces) ClearYears() -- Position lists on newly added place listPlaces.value = fhGetDisplayText(ptrPlace) .. ' *' for i, v in pairs(tblGROPlaces) do if v[1] == fhGetDisplayText(ptrPlace) then listGROPlaces.value = i break end end for _, v in pairs(tblYears) do v.active = 'YES' end btnSaveYears.active = 'NO' fhUpdateDisplay() OkOnlyMessage(dialogGRODistricts.title, fhGetDisplayText(ptrPlace) .. '\n\nadded to GRO Districts', 'INFORMATION', dialogGRODistricts) iup.SetFocus(tblYears[1]) end end listGROPlaces = iup.list{dropdown = 'NO', visiblelines = 5, expand = 'HORIZONTAL', editbox = 'NO'} PopulateListFromtblGROPlaces(listGROPlaces) local btnRemove = iup.button{title = 'Remove from GRO Districts', padding = '8x2'} function btnRemove:action() if listGROPlaces.value == '0' then return end local Ans = MessageBox(dialogGRODistricts.title, 'Remove ' ..tblGROPlaces[tonumber(listGROPlaces.value)][1] .. ' from GRO Districts?\n\n' .. '(The Place record will not be deleted)', 'OKCANCEL', 'QUESTION', 2, dialogGRODistricts) if Ans == 1 then local ptrPlace = fhNewItemPtr() ptrPlace:MoveToRecordById('_PLAC', tblGROPlaces[tonumber(listGROPlaces.value)][3]) local ptrNote = fhGetItemPtr(ptrPlace, '~.NOTE2') while ptrNote:IsNotNull() do local strNote = fhGetItemText(ptrNote,'~') if strNote:match('GRO District') then break end ptrNote:MoveNext('SAME_TAG') end if ptrNote:IsNull() then error('GRO District note found') end if not fhDeleteItem(ptrNote) then error('Delete GRO District note failed') end table.remove(tblGROPlaces, tonumber(listGROPlaces.value)) Populate_listPlaces() PopulateListFromtblGROPlaces(listGROPlaces) ClearYears() fhUpdateDisplay() OkOnlyMessage(dialogGRODistricts.title, fhGetDisplayText(ptrPlace) .. ' removed from GRO Districts', 'INFORMATION', dialogGRODistricts) end end function TextBoxYear(i) local txtYear = iup.text{ nc = '4', filter = 'NUMERIC', alignment = 'ACENTER', visiblecolumns = 3, active = 'NO' } function txtYear:getfocus_cb() if tblYears[i].value ~= '' then tblYears[i].selection = 'ALL' end end function txtYear:action() tblYears[i].bgcolor = gtblConstants.White btnSaveYears.active = 'YES' end function txtYear:killfocus_cb() tblYears[i].selection = 'NONE' if tblYears[i].value ~= '' then if tonumber(tblYears[i].value) < 1837 or tonumber(tblYears[i].value) > gtblConstants.CurrYear then tblYears[i].bgcolor = gtblConstants.Red tblYears[i].selection = 'ALL' iup.SetFocus(tblYears[i]) return iup.IGNORE else tblYears[i].bgcolor = gtblConstants.White end end end function txtYear:k_any(keycode) if keycode == iup.K_TAB then if tblYears[i].value == '' then iup.SetFocus(btnSaveYears) return iup.IGNORE end end end return txtYear end for i = 1, 8 do tblYears[i] = TextBoxYear(i) end local tblYearsFromTo = {} for i = 1, 4 do tblYearsFromTo[i] = {tblYears[(i * 2) - 1], tblYears[(i * 2)]} end function ClearYears() for _, v in pairs(tblYears) do v.value = '' v.bgcolor = gtblConstants.White v.active = 'NO' end end function PopulateYears(ItemNo) local strYears = tblGROPlaces[tonumber(ItemNo)][2] local FromToPattern = '(%d*%-?%d*)' local tblFromTo = {strYears:match(FromToPattern .. ',?' .. FromToPattern .. ',?' .. FromToPattern .. ',?' .. FromToPattern)} for i,v in pairs(tblFromTo) do From, To = v:match('(%d*)%-?(%d*)') tblYearsFromTo[i][1].value = From or '' tblYearsFromTo[i][2].value = To or '' end -- Make all years active for _, v in pairs(tblYears) do v.active = 'YES' end end function listGROPlaces:action(text, item, state) if state == 1 then PopulateYears(item) else ClearYears() end btnSaveYears.active = 'NO' end function hboxFromTo(i) return iup.hbox{ iup.label{title = tostring(i) .. ' From'}, tblYearsFromTo[i][1], iup.label{title = 'To'}, tblYearsFromTo[i][2], cgap = '6', alignment = 'ACENTER' } end local gridYears = iup.gridbox{ hboxFromTo(1), hboxFromTo(2), iup.fill{size = '12'},iup.fill{size = '12'}, hboxFromTo(3), hboxFromTo(4), numdiv = '2', cgaplin = '2', sizecol = '-1', orientation = 'VERTICAL'} btnSaveYears = iup.button{title = 'Save Active Years', padding = '8x2', active = 'NO'} function ValidateYears() for i,v in pairs(tblYearsFromTo) do if v[1].bgcolor == gtblConstants.Red or v[2].bgcolor == gtblConstants.Red then return false end end -- Values are all 4 digit numeric or they would be red so they can be compared as strings local bBlank = false for i, v in pairs(tblYearsFromTo) do -- Check all pairs are valid, nothing after first blank if bBlank and v[1].value ~= '' then tblYearsFromTo[i-1][2].bgcolor = gtblConstants.Red return false end if v[2].value ~= '' then if v[1].value == '' or v[1].value > v[2].value then v[2].bgcolor = gtblConstants.Red return false end else bBlank = true end if v[1].value ~= '' and i > 1 and tonumber(v[1].value) <= tonumber(tblYearsFromTo[i-1][2].value) + 1 then v[1].bgcolor = gtblConstants.Red -- Ranges must be in order with a gap (contiguous ranges must be merged) return false end end return true end function btnSaveYears:action() if listGROPlaces.value == '0' then return end if not ValidateYears() then OkOnlyMessage(dialogGRODistricts.title, 'Years District Active are invalid', 'ERROR', dialogGRODistricts) iup.SetFocus(tblYears[1]) return end local SaveGROSelection = listGROPlaces.value local strYears = '' for _, v in pairs(tblYearsFromTo) do if v[1].value ~= '' then strYears = strYears .. v[1].value .. '-' .. v[2].value .. ',' end end strYears = strYears:gsub(',$', '') local ptrPlace = fhNewItemPtr() ptrPlace:MoveToRecordById('_PLAC', tblGROPlaces[tonumber(listGROPlaces.value)][3]) local ptrNote = fhGetItemPtr(ptrPlace, '~.NOTE2') while ptrNote:IsNotNull() do local strNote = fhGetItemText(ptrNote,'~') if strNote:match('GRO District') then break end ptrNote:MoveNext('SAME_TAG') end if ptrNote:IsNull() then error('GRO District note note found') end if not fhSetValueAsText(ptrNote, 'GRO District\nYears: ' .. strYears) then error ('Adding GRO District Note') end tblGROPlaces[tonumber(listGROPlaces.value)][2] = strYears PopulateListFromtblGROPlaces(listGROPlaces) listGROPlaces.value = SaveGROSelection btnSaveYears.active = 'NO' OkOnlyMessage(dialogGRODistricts.title, fhGetDisplayText(ptrPlace) .. '\n\nYears Active saved', 'INFORMATION', dialogGRODistricts) end local frameEditDistrict = iup.frame{ iup.vbox{ iup.frame{ iup.vbox{ iup.hbox{iup.label{title = 'Select GRO District'}, iup.fill{}; margin = '0x0'}, listGROPlaces, iup.hbox{iup.fill{}, btnRemove; margin = '0x4'}, iup.frame{ iup.vbox{ gridYears, iup.hbox{ iup.fill{}, iup.label{title = 'Leave final "To" blank \nif District still active', fontstyle = 'Italic'}, iup.fill{}, btnSaveYears; cmargin = '8x0', expand = 'YES' }; margin = '6x2', expand = 'NO', cgap = '4' }, title = 'Years District Active (optional)' }; alignment = 'ACENTER', gap = '2' }, margin = '10x2', sunken = 'YES' }; margin = '4x2', fontstyle = '' }; title = 'Edit GRO District', fontstyle = 'Bold' } local btnClose = iup.button{ title = 'Close', padding = '10x3', action = function() return iup.CLOSE end } local hboxFooter = iup.hbox{ iup.fill{}, btnClose, iup.fill{}; normalizesize = 'BOTH', margin = '0x5', gap = 40 } dialogGRODistricts = iup.dialog{ iup.vbox{frameAddDistrict, frameEditDistrict, hboxFooter; margin = '15x5', gap = 5}; resize = 'No', minbox = 'No', maxbox = 'No', title = 'GRO Districts', defaultesc = btnClose, PARENTDIALOG = ParentDialog } dialogGRODistricts:popup(iup.CENTERPARENT, iup.CENTERPARENT) local SaveGROListSelection = listGROPlaces.value dialogGRODistricts:destroy() return SaveGROListSelection end --====================================================================================================================== function PopulateListFromtblGROPlaces(List) -- Populate an iuplist from GROPlaces List['1'] = nil -- Empty the list for i,v in pairs(tblGROPlaces) do if v[2] ~= '' then List[tostring(i)] = v[1] .. ' [' .. v[2] .. ']' else List[tostring(i)] = v[1] end end end --====================================================================================================================== function Initialise() -- Create global variables and fetch/set default settings gPluginName = fhGetContextInfo('CI_PLUGIN_NAME'):gsub(' %(.*', '') -- Without a dev/test version in brackets -- Set parameter types for processing configuration file gtblSettingsTypeBoolean = { ['QtrMonthNames'] = true, ['UCRefs'] = true, ['ShortPlaces'] = true, --['IncNameInCitation'] = true, ['CitationNameBirth'] = true, ['CitationNameMarriage'] = true, ['CitationNameDeath'] = true, ['CitationToMotherDefault'] = true, ['UseGRODistricts'] = true, ['AddLivingFlag'] = true } GetSettings() if gtblSettings.VersionCheckInterval ~= 0 and gtblSettings.VersionNextCheck <= os.date('%Y-%m-%d') then local bOK, strMsg = VersionCheck() if not bOK then strMsg = 'Check for plugin update failed\n\n' .. strMsg .. '\n\nPostpone checking for a day?' if MessageBox (gPluginName, strMsg, 'YESNO', 'WARNING') == 1 then gtblSettings.VersionNextCheck = DateAdd(os.date('%Y-%m-%d'), 1) SaveSettings() end else while gtblSettings.VersionNextCheck <= os.date('%Y-%m-%d') do gtblSettings.VersionNextCheck = DateAdd(gtblSettings.VersionNextCheck, gtblSettings.VersionCheckInterval) end gtblSettings.VersionCheckedDate = os.date('%Y-%m-%d') SaveSettings() end end gtblConstants = {} gtblConstants.Periods = {'Q1';'Q2';'Q3';'Q4';'Jan';'Feb';'Mar';'Apr';'May';'Jun';'Jul';'Aug';'Sep';'Oct';'Nov';'Dec';} gtblConstants.PeriodsLong = {'Q1 Jan-Feb-Mar';'Q2 Apr-May-Jun';'Q3 Jul-Aug-Sep';'Q4 Oct-Nov-Dec'; 'January';'February';'March';'April';'May';'June'; 'July';'August';'September';'October';'November';'December';} gtblConstants.CurrYear = fhCallBuiltInFunction('today'):GetYear() gtblConstants.CitationAssess = {'None', 'Unreliable', 'Questionable', 'Secondary Evidence', 'Primary Evidence'} gtblConstants.White = '255 255 255' gtblConstants.Red = '255 31 31' gtblConstants.Blue = '0 0 255' gtblConstants.Yellow = '255 255 0' gtblConstants.PaleYellow = '255 255 127' gtblConstants.VersionCheckFrequency = {['0']= 0, ['1']=1, ['2']=7, ['3']=14, ['4']=28 } tblPlaces = {} tblGROPlaces = {} local ptrPlace = fhNewItemPtr() local ptrNote = fhNewItemPtr() ptrPlace:MoveToFirstRecord('_PLAC') while ptrPlace:IsNotNull() do table.insert(tblPlaces,{fhGetDisplayText(ptrPlace), fhGetRecordId(ptrPlace)}) local ptrNote = fhGetItemPtr(ptrPlace, '~.NOTE2') while ptrNote:IsNotNull() do local strNote = fhGetItemText(ptrNote,'~') if strNote:match('GRO District') then table.insert(tblGROPlaces,{fhGetDisplayText(ptrPlace), strNote:match('Years:%s*(.*)$') or '', fhGetRecordId(ptrPlace)}) break end ptrNote:MoveNext('SAME_TAG') end ptrPlace:MoveNext() end table.sort(tblPlaces, function(k1, k2) return k1[1] < k2[1] end) if gtblSettings.UseGRODistricts then table.sort(tblGROPlaces, function(k1, k2) return k1[1] < k2[1] end) end gtblData = {} -- Data for the main dialog - source being cited, the event and the records selected gtblData.ptr = fhNewItemPtr() end -- function Initialise ------------------------------------------------------------------------------------------------------------------------ function GetSettings() -- Read settings from file in Plugin Data - create default if it doesn't exist gtblSettings = {} local SettingsFile = fhGetContextInfo('CI_PROJECT_DATA_FOLDER') .. '\\Plugin Data\\' .. gPluginName .. '.ini' if not fhfu.fileExists(SettingsFile) then DefaultSettings() SaveSettings() return end local strData = fhLoadTextFile(SettingsFile) for strLine in strData:gmatch('[^\r\n]+') do local Parameter, Value = strLine:match('^(%w+)=([%g%s]*)$') if gtblSettingsTypeBoolean[Parameter] then if Value == 'N' then gtblSettings[Parameter] = false else gtblSettings[Parameter] = true end else if not Value:find('^-?%d+$') or Value == '' then -- Not integer starting with optional - sign? gtblSettings[Parameter] = Value else gtblSettings[Parameter] = tonumber(Value) end end end -- Check for new settings not in current ini file if DefaultSettings() then SaveSettings() end if gtblSettings.Version ~= Version then -- Place to do any conversions for new version gtblSettings.Version = Version SaveSettings() end end -- function GetSettings ------------------------------------------------------------------------------------------------------------------------ function SaveSettings() -- Save settings to file in Plugin Data local SettingsFolder = fhGetContextInfo('CI_PROJECT_DATA_FOLDER') .. '\\Plugin Data' if not fhfu.folderExists(SettingsFolder) then fhfu.createFolder(SettingsFolder) end local SettingsFile = SettingsFolder .. '\\' .. gPluginName .. '.ini' -- Build table of lines to go in the settings file local tblLines = {} for Parameter, Value in pairs(gtblSettings) do if gtblSettingsTypeBoolean[Parameter] then local str = 'N' if Value then str = 'Y' end table.insert(tblLines, Parameter .. '=' .. str) elseif type(Value) ~= 'number' then -- Any non-numeric characters? table.insert(tblLines, Parameter .. '=' .. Value) else -- must be a number table.insert(tblLines, Parameter .. '=' .. tostring(Value)) end end -- Prepare ini file with lines sorted alphabetically table.sort(tblLines) local str = '' for _, line in ipairs(tblLines) do str = str .. line .. '\n' end fhSaveTextFile(SettingsFile, str) end -- function SaveSettings ------------------------------------------------------------------------------------------------------------------------ function DefaultSettings() -- Default settings for first run or if new settings parameter(s) introduced local Changes = false -- Sources if gtblSettings.SourceIdBirth == nil then gtblSettings.SourceIdBirth = 0 Changes = true end if gtblSettings.SourceIdMarriage == nil then gtblSettings.SourceIdMarriage = 0 Changes = true end if gtblSettings.SourceIdDeath == nil then gtblSettings.SourceIdDeath = 0 Changes = true end -- Citation text labels if gtblSettings.LabelPrefixBirth == nil then gtblSettings.LabelPrefixBirth = 'Birth^' Changes = true end if gtblSettings.LabelPrefixMarriage == nil then gtblSettings.LabelPrefixMarriage = 'Marriage^' Changes = true end if gtblSettings.LabelPrefixDeath == nil then gtblSettings.LabelPrefixDeath = 'Death^' Changes = true end -- GRO Reference Labels if gtblSettings.LabelDistrictName == nil then gtblSettings.LabelDistrictName = '' Changes = true end if gtblSettings.LabelVolume == nil then gtblSettings.LabelVolume = 'Vol:^' Changes = true end if gtblSettings.LabelPage == nil then gtblSettings.LabelPage = 'Page:^' Changes = true end if gtblSettings.LabelDistrictNo == nil then gtblSettings.LabelDistrictNo = 'District:^' Changes = true end if gtblSettings.LabelRegister == nil then gtblSettings.LabelRegister = 'Reg:^' Changes = true end if gtblSettings.LabelEntry == nil then gtblSettings.LabelEntry = 'Entry:^' Changes = true end -- Event Specific Labels if gtblSettings.LabelMother == nil then gtblSettings.LabelMother = 'Mother\'s Maiden Surname:^' Changes = true end if gtblSettings.LabelAge == nil then gtblSettings.LabelAge = 'Age:^' Changes = true end if gtblSettings.LabelDOB == nil then gtblSettings.LabelDOB = 'DOB:^' Changes = true end -- Options if gtblSettings.QtrMonthNames == nil then -- Full or abrieviated month names in citation text gtblSettings.QtrMonthNames = false Changes = true end if gtblSettings.UCRefs == nil then -- Make alpha characters in reference upper case gtblSettings.UCRefs = false Changes = true end if gtblSettings.ShortPlaces == nil then -- District name only in citation text gtblSettings.ShortPlaces = true Changes = true end if gtblSettings.IncNameInCitation == nil then -- Name(s) to be included in citation text gtblSettings.IncNameInCitation = 'ON' Changes = true end if gtblSettings.NameFormat == nil then -- Format of Names gtblSettings.NameFormat = 3 -- Given Surname (as entered in FH) Changes = true end if gtblSettings.CitationNameBirth == nil then -- Add citation to Name for Birth gtblSettings.CitationNameBirth = true Changes = true end if gtblSettings.CitationNameMarriage == nil then -- Add citation to Name for Marriage gtblSettings.CitationNameMarriage = false Changes = true end if gtblSettings.CitationNameDeath == nil then -- Add citation to Name for Death gtblSettings.CitationNameDeath = true Changes = true end if gtblSettings.CitationToMotherDefault == nil then -- Add birth citation to Mother's name gtblSettings.CitationToMotherDefault = false Changes = true end if gtblSettings.AddLivingFlag == nil then -- Set living for births less than set maximum age (if no death) gtblSettings.AddLivingFlag = false Changes = true end if gtblSettings.LivingMaxAge == nil then -- Maximum age considered potentially living gtblSettings.LivingMaxAge = 100 Changes = true end if gtblSettings.NewMotherGivenName == nil then -- Given name to use for new mother's when recording a birth gtblSettings.NewMotherGivenName = '' Changes = true end if gtblSettings.CitationEntryDate == nil then -- Date to record in Citation Specific Date gtblSettings.CitationEntryDate = 1 -- "No Date" Changes = true end if gtblSettings.CitationAssess == nil then -- Default citation assessment gtblSettings.CitationAssess = 1 -- "None" Changes = true end if gtblSettings.UseGRODistricts == nil then -- Use places marked as GRO districts gtblSettings.UseGRODistricts = false Changes = true end -- Main dialog screen position if gtblSettings.ScreenPosX == nil then gtblSettings.ScreenPosX = iup.CENTERPARENT Changes = true end if gtblSettings.ScreenPosY == nil then gtblSettings.ScreenPosY = iup.CENTERPARENT Changes = true end -- Version Control if gtblSettings.Version == nil then gtblSettings.Version = Version gtblSettings.VersionCheckedDate = '2000-01-01' gtblSettings.VersionNextCheck = os.date('%Y-%m-%d') -- Force check on first run gtblSettings.VersionCheckInterval = 7 Changes = true end return Changes end -- function DefaultSettings --====================================================================================================================== function VersionCheck(dialogParent) local dialogChecking = iup.dialog{ iup.label{title = 'Checking for update...', padding = '40x16'}, title = gPluginName, dialogframe = 'Yes', menubox = 'No', parentdialog = dialogParent } dialogChecking:showxy(iup.CENTERPARENT, iup.CENTERPARENT) fhUpdateDisplay() local function httpRequest(strRequest) local http = luacom.CreateObject("winhttp.winhttprequest.5.1") http:Open("GET",strRequest,false) http:Send() return http.Responsebody end -- local function httpRequest local strRequest ='http://www.family-historian.co.uk/lnk/checkpluginversion.php?name=' .. gPluginName local isOK, strReturn = pcall(httpRequest,strRequest) local StoreVersion = '' dialogChecking:destroy() if not isOK then return false, 'Call to plugin store to check latest version failed' -- Do nothing - will check again after today else if strReturn ~= nil then StoreVersion = strReturn:match('([%d%.]*),') -- Version digits & dots then comma else return false, 'nil return from plugin store' end end local Latest if StoreVersion > Version then local strMsg = 'A later version of this plugin is available\nfrom the Family Historian Plugin Store\n\n' .. 'Current version: ' .. Version ..'\n' .. 'Store version: ' .. StoreVersion ..'\n\n' .. 'Go to ' .. gPluginName .. '\non the Plugin Store?' if MessageBox('Plugin update available', strMsg, 'YESNO', 'INFORMATION', 1, dialogParent) == 1 then fhShellExecute('https://pluginstore.family-historian.co.uk/page/plugin/gro-bmd-index-citations') end Latest = 'N' else Latest = 'Y' end return true, Latest end ------------------------------------------------------------------------------------------------------------------------ function DateAdd(Date, Days) local tblDate =YMDtoTable(Date) SecondsToAdd = Days * 24 * 60 * 60 return os.date('%Y-%m-%d', os.time(tblDate) + SecondsToAdd) end function YMDtoTable(Date) local tblDate = {} tblDate.year = tonumber(Date:sub(1,4)) tblDate.month = tonumber(Date:sub(6,7)) tblDate.day = tonumber(Date:sub(9,10)) return tblDate end -- ============================================ General purpose routines -- ============================================ function Capitalise(str) -- Make string title capitalised (1st letter each word capital, rest lower) str = str:gsub('(%a)([%w_]*)', UpperFirst) return str end function UpperFirst(first, rest) return first:upper() .. rest:lower() end ------------------------------------------------------------------------------------------------------------------------ function Replace(strTxt,strOld,strNew,intNum) -- replace is plain text version of gsub() - function courtesy of Mike Tate local strMagic = "([%^%$%(%)%%%.%[%]%*%+%-%?])" -- UTF-8 replacement for "(%W)" strOld = tostring(strOld or ""):gsub(strMagic,"%%%1") -- Hide magic pattern symbols return tostring(strTxt or ""):gsub(strOld,function() return strNew end,tonumber(intNum)) end -- function Replace ------------------------------------------------------------------------------------------------------------------------ function OkOnlyMessage(strTitle, strMessage, strDialogType, ParentDialog) -- Display a message with only an OK button -- strDialogType: "MESSAGE" (No Icon), "ERROR" (Stop-sign), "WARNING" (Exclamation-point), "QUESTION" (Question-mark) or "INFORMATION" (Letter "i"). MessageBox(strTitle, strMessage, 'OK', strDialogType, 1, ParentDialog) end -- function OkOnlyMessage --[[==================================================================================================================== Replacement for iup.messagedlg Call: MessageBox(strTitle, strMessage, strButtons, strDialogType, strDefault, ParentDialog) strButtons 'OK', 'OKCANCEL', 'RETRYCANCEL', 'YESNO', or 'YESNOCANCEL' strDialogType: 'MESSAGE' (No Icon) (default) 'ERROR' (Stop-sign) 'WARNING' (Exclamation-point) 'QUESTION' (Question-mark) 'INFORMATION' (Letter 'i') strDefault; Number of the default button, if omitted defaults to 1 ParentDialog: If called from within an iup dialog this should be the handle of the dialog. If not given the message box will not centre on the parent dialog Omit if not called from another dialog Returns: Number of the button pressed If Esc keyed or close button (X) is keyed it returns number of the last button (same as iup.messagedlg) Example: dialogExample = iup.dialog{...} ... Response = MessageBox('Demo Title', 'Hello World', 'OKCANCEL', 'QUESTION', dialogExample) ----------------------------------------------------------------------------------------------------------------------]] function MessageBox(strTitle, strMessage, strButtons, strDialogType, strDefault, ParentDialog) local MaxLineLength = 80 -- Maximum characters per line used for word wrapping function create_image_Information() local Information = iup.image{ width = 32, height = 32, pixels = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0, 0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0, 0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0, 0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0, 0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0, 0,0,1,6,9,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,9,6,1,0,0, 0,0,3,13,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,13,3,0,0, 0,1,6,14,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,14,6,1,0, 0,2,7,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,7,2,0, 0,3,8,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,8,3,0, 0,4,9,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,9,4,0, 0,5,10,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,10,5,0, 0,5,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,11,5,0, 0,5,11,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,11,5,0, 0,5,10,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,10,5,0, 0,4,9,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,9,4,0, 0,3,8,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,8,3,0, 0,2,7,11,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,11,7,2,0, 0,1,6,14,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,14,6,1,0, 0,0,3,13,11,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,11,13,3,0,0, 0,0,1,6,9,11,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,11,9,6,1,0,0, 0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,0,0,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0, 0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0, 0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0, 0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0, 0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, colors = { '255 255 255', '239 243 247', '159 188 211', '96 144 183', '64 122 168', '0 78 140', '16 89 147', '0 86 154', '0 99 178', '0 110 196', '0 117 210', '0 120 215', '143 177 204', '0 94 168', '0 115 206', '0 83 149', '48 111 161' } } return Information end function create_image_Warning() local Warning = iup.image{width = 32, height = 32, pixels = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,6,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,3,6,6,6,6,7,7,6,6,6,6,3,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,2,4,6,6,6,6,7,7,6,6,6,6,4,2,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,3,6,6,6,6,6,7,7,6,6,6,6,6,3,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,2,4,6,6,6,6,6,7,7,6,6,6,6,6,4,2,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,1,3,6,6,6,6,6,6,7,7,6,6,6,6,6,6,3,1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,2,4,6,6,6,6,6,6,7,7,6,6,6,6,6,6,4,2,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,3,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,3,1,0,0,0,0,0,0, 0,0,0,0,0,0,2,4,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,4,2,0,0,0,0,0,0, 0,0,0,0,0,1,3,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,3,1,0,0,0,0,0, 0,0,0,0,0,2,4,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,4,2,0,0,0,0,0, 0,0,0,0,1,3,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,3,1,0,0,0,0, 0,0,0,0,2,4,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,4,2,0,0,0,0, 0,0,0,1,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,1,0,0,0, 0,0,0,2,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,2,0,0,0, 0,0,1,3,6,6,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,6,6,3,1,0,0, 0,0,2,4,6,6,6,6,6,6,6,6,6,6,6,7,7,6,6,6,6,6,6,6,6,6,6,6,4,2,0,0, 0,1,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,1,0, 0,2,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,2,0, 1,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,3,1, 2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2 }, colors = { '255 255 255', '255 228 159', '255 193 32', '255 188 0', '254 205 0', '253 213 0', '252 225 0', '0 0 0' } } return Warning end function create_image_Error() local Error = iup.image{ width = 32, height = 32, pixels = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0, 0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0, 0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0, 0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0, 0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0, 0,0,1,6,9,11,11,11,17,18,19,11,11,11,11,11,11,11,11,11,11,19,18,17,11,11,11,9,6,1,0,0, 0,0,3,13,11,11,11,11,18,0,20,19,11,11,11,11,11,11,11,11,19,20,0,18,11,11,11,11,13,3,0,0, 0,1,6,14,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,14,6,1,0, 0,2,7,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,7,2,0, 0,3,8,11,11,11,11,11,11,11,19,20,0,20,19,11,11,19,20,0,20,19,11,11,11,11,11,11,11,8,3,0, 0,4,9,11,11,11,11,11,11,11,11,19,20,0,20,19,19,20,0,20,19,11,11,11,11,11,11,11,11,9,4,0, 0,5,10,11,11,11,11,11,11,11,11,11,19,20,0,20,20,0,20,19,11,11,11,11,11,11,11,11,11,10,5,0, 0,5,11,11,11,11,11,11,11,11,11,11,11,19,20,0,0,20,19,11,11,11,11,11,11,11,11,11,11,11,5,0, 0,5,11,11,11,11,11,11,11,11,11,11,11,19,20,0,0,20,19,11,11,11,11,11,11,11,11,11,11,11,5,0, 0,5,10,11,11,11,11,11,11,11,11,11,19,20,0,20,20,0,20,19,11,11,11,11,11,11,11,11,11,10,5,0, 0,4,9,11,11,11,11,11,11,11,11,19,20,0,20,19,19,20,0,20,19,11,11,11,11,11,11,11,11,9,4,0, 0,3,8,11,11,11,11,11,11,11,19,20,0,20,19,11,11,19,20,0,20,19,11,11,11,11,11,11,11,8,3,0, 0,2,7,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,7,2,0, 0,1,6,14,11,11,11,11,19,20,0,20,19,11,11,11,11,11,11,19,20,0,20,19,11,11,11,11,14,6,1,0, 0,0,3,13,11,11,11,11,18,0,20,19,11,11,11,11,11,11,11,11,19,20,0,18,11,11,11,11,13,3,0,0, 0,0,1,6,9,11,11,11,17,18,19,11,11,11,11,11,11,11,11,11,11,19,18,17,11,11,11,9,6,1,0,0, 0,0,0,12,15,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,15,12,0,0,0, 0,0,0,0,3,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,3,0,0,0,0, 0,0,0,0,1,16,7,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10,7,16,1,0,0,0,0, 0,0,0,0,0,1,3,15,9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,9,15,3,1,0,0,0,0,0, 0,0,0,0,0,0,0,12,6,13,14,11,11,11,11,11,11,11,11,11,11,14,13,6,12,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,3,6,7,8,9,10,11,11,10,9,8,7,6,3,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,5,5,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, colors = { '255 255 255', '249 241 240', '221 173 166', '198 119 107', '187 92 78', '165 39 20', '170 51 34', '179 42 20', '203 48 20', '221 53 22', '235 57 22', '240 58 22', '215 159 151', '193 47 20', '231 55 22', '174 40 20', '181 78 63', '241 70 39', '251 206 197', '243 95 67', '254 243 241' } } return Error end function create_image_Question() local Question = iup.image{ width = 32, height = 32, pixels = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3,7,14,6,13,12,12,12,12,13,6,14,7,3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,7,6,9,4,10,5,1,0,0,1,5,10,4,9,6,7,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,11,9,8,2,0,0,0,0,0,0,0,0,0,0,2,8,9,11,3,3,3,3,3,3,3, 3,3,3,3,3,7,6,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,4,6,7,3,3,3,3,3, 3,3,3,3,7,13,4,1,0,0,0,0,0,0,0,2,5,5,2,0,0,0,0,0,2,4,13,7,3,3,3,3, 3,3,3,3,6,4,1,0,0,0,0,0,2,8,9,6,11,11,6,4,5,0,0,1,1,2,4,6,3,3,3,3, 3,3,3,11,4,1,0,0,0,0,0,5,13,15,3,3,3,3,3,7,13,5,1,1,1,1,2,4,11,3,3,3, 3,3,7,9,5,0,0,0,0,0,0,5,11,3,7,14,11,15,3,3,7,9,1,1,1,1,1,5,9,7,3,3, 3,3,6,8,0,0,0,0,0,0,0,5,11,14,9,10,5,10,6,3,3,6,5,1,1,1,1,1,8,6,3,3, 3,7,9,2,0,0,0,0,0,0,0,5,9,8,0,0,0,1,10,15,3,15,10,1,1,1,1,1,5,9,7,3, 3,14,4,0,0,0,0,0,0,0,0,5,8,0,0,0,0,0,10,15,3,15,10,1,1,1,1,1,1,4,14,3, 3,6,10,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,4,7,3,11,5,1,1,1,1,1,2,10,6,3, 3,13,5,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,6,3,3,9,2,1,1,1,1,1,2,5,13,3, 3,12,1,0,0,0,0,0,0,0,0,1,0,1,0,0,10,6,3,3,11,10,1,1,1,1,1,2,2,2,12,3, 3,12,0,0,0,0,0,0,0,0,0,0,0,0,1,10,6,3,3,14,8,1,1,1,1,1,2,2,2,2,12,3, 3,12,0,0,0,0,0,0,0,0,1,0,0,1,5,6,3,3,14,8,2,1,1,1,1,2,2,2,2,2,12,3, 3,12,1,0,0,0,0,0,0,1,0,0,1,1,8,7,3,14,8,1,1,1,1,1,2,2,2,2,2,2,12,3, 3,13,5,0,0,0,0,0,0,0,0,1,1,0,4,3,3,13,2,1,1,1,1,2,2,2,2,2,2,5,13,3, 3,6,10,0,0,0,0,0,1,0,1,1,1,1,4,3,3,4,1,1,1,1,2,2,2,2,2,2,2,10,6,3, 3,14,4,0,1,0,1,0,0,1,1,1,1,1,8,3,3,4,1,1,1,2,2,2,2,2,2,2,2,4,14,3, 3,7,9,2,0,0,0,0,1,0,1,1,1,1,4,4,4,8,1,1,2,2,2,2,2,2,2,2,5,9,7,3, 3,3,6,8,0,0,0,1,1,1,1,1,1,1,10,4,4,8,1,2,2,2,2,2,2,2,2,2,8,6,3,3, 3,3,7,9,5,0,1,0,1,1,1,1,1,1,4,3,3,4,2,2,2,2,2,2,2,2,2,5,9,7,3,3, 3,3,3,11,4,2,1,1,1,1,1,1,1,1,4,3,3,4,2,2,2,2,2,2,2,2,5,4,11,3,3,3, 3,3,3,3,6,4,2,1,1,1,1,1,1,1,8,4,4,8,2,2,2,2,2,2,2,2,4,6,3,3,3,3, 3,3,3,3,7,13,4,2,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,5,4,13,7,3,3,3,3, 3,3,3,3,3,7,6,4,5,1,1,1,1,1,2,2,2,2,2,2,2,2,2,5,4,6,7,3,3,3,3,3, 3,3,3,3,3,3,3,11,9,8,5,1,1,2,2,2,2,2,2,2,2,5,8,9,11,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,7,6,9,4,10,5,2,2,2,2,5,10,4,9,6,7,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3,7,14,6,13,12,12,12,12,13,6,14,7,3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 }, colors = { '0 110 202', '0 106 199', '0 103 197', '255 255 255', '9 75 132', '0 97 181', '102 138 167', '238 242 246', '6 83 147', '26 82 126', '6 88 159', '142 168 190', '9 69 115', '63 110 147', '166 187 205', '199 212 223' } } return Question end -- function textwrap takes a string of text which can incude line breaks and returns a string with long lines split if needed function textwrap(strText, maxchars) -- Split string in to an array local arrLines = {} strText = strText .. '\n' strText:gsub('(.-)\n', function(strLine) arrLines[#arrLines+1] = strLine end) -- Split long lines local i = 1 repeat if #arrLines[i] > maxchars + 1 then -- Each line has \n at the end so new line after maxlen is OK --splitpos = maxchars local splitpos = arrLines[i]:sub(1,maxchars):find('[%s][^%s]*$') or maxchars local strRest = arrLines[i]:sub(splitpos +1):gsub('^(%s*)','') table.insert(arrLines, i + 1, strRest) arrLines[i] = arrLines[i]:sub(1,splitpos) end i = i + 1 until i > #arrLines -- Make string from the array local str = '' for _, line in ipairs(arrLines) do str = str .. line .. ' \n' end return str:sub(1,-2) end strMessage = textwrap(strMessage, MaxLineLength) strMessage = strMessage:gsub('&', '&&') strButtons = tostring(strButtons):upper() strDialogType = tostring(strDialogType):upper() if type(strDefault) == 'nil' then intDefault = 1 elseif type(strDefault) == 'number' then intDefault = strDefault elseif type(strDefault) == 'string' then if strDefault:match("%D+") then error('Invalid strDefault: "' .. strDefault .. '"\n') else intDefault = tonumber(strDefault) end else error('Invalid strDefault: "' .. type(strDefault) .. '"\n') end local selection local lblMessage = iup.flatlabel{title = strMessage, padding = '4x0', textwrap = 'NO', expand = 'VERTICAL'} local hboxMessage if strDialogType == 'MESSAGE' then hboxMessage = iup.hbox{} else if strDialogType == 'ERROR' then hboxMessage = iup.hbox{iup.label{image = create_image_Error()}} elseif strDialogType == 'WARNING' then hboxMessage = iup.hbox{iup.label{image = create_image_Warning()}} elseif strDialogType == 'QUESTION' then hboxMessage = iup.hbox{iup.label{image = create_image_Question()}} elseif strDialogType == 'INFORMATION' then hboxMessage = iup.hbox{iup.label{image = create_image_Information()}} else error('Invalid strDialogType Setting\n') end hboxMessage:append(iup.space{size = '8x0'}) end hboxMessage:append(lblMessage) hboxMessage:append(iup.fill{}) -- Needed to pad box if short message hboxMessage.padding = '0x2' hboxMessage.expand = 'VERTICAL' hboxMessage.cmargin = '16x8' function Button(Value) return iup.button{title = ' ', size = '48x0', action = function(self) selection = Value return iup.CLOSE end } end local Button1 = Button(1) local Button2 = Button(2) local Button3 = Button(3) local hboxButtons if strButtons == 'OK' then Button1.title = 'OK' hboxButtons = iup.hbox{Button1;} elseif strButtons == 'OKCANCEL' then Button1.title = 'OK' Button2.title = 'Cancel' hboxButtons = iup.hbox{Button1, Button2;} elseif strButtons == 'RETRYCANCEL' then Button1.title = 'Retry' Button2.title = 'Cancel' hboxButtons = iup.hbox{Button1, Button2;} elseif strButtons == 'YESNO' then Button1.title = 'Yes' Button2.title = 'No' hboxButtons = iup.hbox{Button1, Button2;} elseif strButtons == 'YESNOCANCEL' then Button1.title = 'Yes' Button2.title = 'No' Button3.title = 'Cancel' hboxButtons = iup.hbox{Button1, Button2, Button3;} else error('Invalid strButtons setting: "' ..strButtons .. '"\n') end local ButtonCount = iup.GetChildCount(hboxButtons) if intDefault > ButtonCount then error ('Invalid default button: "'.. intDefault .. '\n') end hboxButtons:insert(nil, iup.fill{}) -- hboxButtons:append(iup.fill{}) -- Activate this line to makes buttons centered rather than right aligned hboxButtons.gap = 6 hboxButtons.margin = '12x10' local dialogMessage = iup.dialog{ iup.vbox{iup.flatframe{hboxMessage; framecolor = '255 255 255', bgcolor = '255 255 255'}, hboxButtons}; title = strTitle, dialogframe = 'Yes' } if intDefault == 2 then dialogMessage.startfocus = Button2 elseif intDefault == 3 then dialogMessage.startfocus = Button3 end if ButtonCount == 1 then dialogMessage.defaultesc = Button1 elseif ButtonCount == 2 then dialogMessage.defaultesc = Button2 else dialogMessage.defaultesc = Button3 end if ParentDialog ~= nil then dialogMessage.PARENTDIALOG = ParentDialog end selection = ButtonCount -- Default if dialog closed without clicking a button dialogMessage:popup(IUP_CENTERPARENT, IUP_CENTERPARENT) dialogMessage:destroy() return tonumber(selection) end -- function MessageBox --[[ =============== Debugging utilities ===============]] function dumpstr (tt, label,indent, done, str) local t = {} if label == nil then label = 'Dump' end done = done or {} indent = indent or 0 if type(tt) == 'table' then if indent == 0 then table.insert(t,string.rep (' ', indent)) table.insert(t,label..'\n') end for key, value in pairs (tt) do table.insert(t,string.rep (' ', indent)) -- indent it if type (value) == 'table' and not done [value] then done [value] = true table.insert(t,string.format('[%s] => table\n', tostring (key))); table.insert(t,string.rep (' ', indent+4)) -- indent it table.insert(t,'(\n'); table.insert(t,dumpstr (value, tostring(key),indent + 7, done, str)) table.insert(t,string.rep (' ', indent+4)) -- indent it table.insert(t,')\n'); else table.insert(t,string.format('[%s] => %s\n', tostring (key), tostring(value))) end end else table.insert(t,tostring(label) .. ':' .. tostring(tt)) end if indent == 0 then table.insert(t,'\n') end if str == nil then return table.concat(t) else return table.concat(t) end end -- function dumpstr --====================================================================================================================== debug = false main() debug = false if DEBUG_MODE and debug then io.write(dumpstr(gtblSettings, '==Settings==')) io.write(dumpstr(gtblConstants,'==gtblConstants==')) iup.Message('Debug','End of plugin') end -- End of plugin --======================================================================================================================