fhUtils
Reference for the public functions exposed by fhUtils.fh_lua, a utility module for building
Family Historian plugins.
- Version: 1.16
- Author: Calico Pie
- Licence: MIT (see plugin licence)
- Dependencies:
iuplua,luacom,pl.init,lfs
All functions below are accessed via the fhUtils table, e.g. fhUtils.getParam(...), unless
noted otherwise.
Contents
- Environment / setup
- Dialogs & prompts
- String & comparison utilities
- Record & field helpers
- Result sets
- Citations
- Creating & updating records
- Rich text
- Options persistence
- Misc
Environment / setup
fhUtils.isWine()
Checks if Family Historian is running under Wine/Crossover.
- Returns:
boolean
fhUtils.setIupDefaults()
Turns on the CUSTOMQUITMESSAGE, sets the default font to match the Property Box font (skipped
under Wine), and enables UTF-8 support (fhSetStringEncoding('UTF-8')).
- Returns: none
fhUtils.helpButton(page, label)
Creates an iup.button which, when pressed, opens the plugin's help page on the Plugin Store.
- Parameters:
page— page title in the Plugin Store Help system to displaylabel— button label, defaults to'Help'
- Returns:
iup.button
Dialogs & prompts
fhUtils.getParam(sTitle, sTopMessage, fields, tButtons, shortcuts, hParent)
Builds a dynamic prompt dialog from a table of field definitions, supporting 6 field types with
validation, ranges, and cross-field updates. This is the core building block most of the other
prompt-related functions (yes, createUpdateFact, pickIndividualPrompt, …) are built on.
- Parameters:
sTitle— window titlesTopMessage— text displayed at the top of the prompt, can benilfields— table of field definitions (see below)tButtons— table of button labels (oriupobjects) shown along the bottom; defaults to{ "OK" }shortcuts— table of string-array autofill shortcuts, keyed by field taghParent— parent dialog handle
- Returns:
fieldstable, extended with.results(values keyed by tag),.ok,.button_pressedand.button_no
Field definition options (each entry in fields):
type |
Purpose | Notable options |
|---|---|---|
STRING |
free text | minlength, maxlength, mask, lines (multiline), tip |
NUMBER |
numeric text | range = {min, max} |
LIST |
dropdown | values, prompts (display text, defaults to values) |
DATE |
date entry with picker button | range = {minDate, maxDate} |
RECORD |
record-selection button | recordtype, minlength, prompt (custom selector function), buttontitle |
BOOLEAN |
toggle | — |
Common to all types: tag, label, value, length (width), protect (read-only),
child/childUpdate (cascading updates to a dependent field).
fhUtils.yes(sQuestion, sTitle, hParent)
Simple Yes/No confirmation prompt, built on getParam.
- Parameters:
sQuestion— message to displaysTitle— message box titlehParent— parent window handle; defaults to the Family Historian main window
- Returns:
boolean—trueif Yes was pressed
fhUtils.pickIndividualPrompt(values, iYear, ptrHead)
Helper for getParam: prompts the user to select an existing individual, create a new one, or
enter a name-only reference, with an optional relationship to a head-of-household record.
- Parameters:
values— agetParamresults table (used to pre-fill name search fields)iYear— year, used to estimate ages when searchingptrHead— pointer to an individual the result may be related to (enables the relationship tab)
- Returns:
- table —
{ tab, ptr, name, relation, fam }wheretabis-1(cancelled),1(selected record),2(create record) or3(name only);ptris the selected record (tab 1);nameis the entered name (tab 2);relationis1=Spouse,2=Child,3=Other,4=Parent;famis the family pointer to add to err—boolean,trueif the dialog was cancelledtitle—stringdescribing the selection/creation made
- table —
- See also:
indiList
String & comparison utilities
fhUtils.stripCommas(s)
Removes leading/trailing commas, collapses consecutive commas and surrounding whitespace, and
normalizes to a single ", " separator.
- Parameters:
s— string to clean up - Returns: cleaned string
fhUtils.compareString(string1, string2, bUseSoundex)
Compares two strings, optionally using Soundex matching.
- Parameters:
string1— string to search instring2— string to search forbUseSoundex— iftrue, compare Soundex codes instead of literal text
- Returns:
boolean—trueif matched (or ifstring2isnil)
fhUtils.editableName(sName)
Removes surname slashes from a name where the surname is a single word at the end of the string, so the name is safe to present in an editable text field.
- Parameters:
sName— individual's name - Returns:
string
fhUtils.getSurname(sName)
Extracts a surname from a name string: tries within / / first, then falls back to the last word.
- Parameters:
sName— individual's name - Returns:
string
Record & field helpers
fhUtils.getType(object)
Like Lua's type(), but returns the Family Historian metatable name (e.g. fh.PITEM, fh.DATE)
for userdata objects.
- Parameters:
object - Returns:
string
fhUtils.isSet(object)
Checks whether a variable exists and is meaningfully populated: non-null for Date/DatePoint/Pointer
types, non-empty for strings and rich text, and not false for booleans.
- Parameters:
object - Returns:
boolean
fhUtils.notSet(object)
Inverse of isSet.
- Parameters:
object - Returns:
boolean
fhUtils.records(type)
Iterator over all records of a given type. Use in a for loop:
for pi in fhUtils.records('INDI') do
print(fhGetDisplayText(pi))
end
- Parameters:
type— record type tag - Returns: iterator function yielding one record pointer per call
fhUtils.allItems(...)
Iterator over every item for all records of one or more given types (or all record types if none
given). Use in a for loop, similar to records.
- Parameters:
...— zero or more record type tags - Returns: iterator function yielding one item pointer per call
fhUtils.createPlaceList()
Returns the list of known place names, with an added :findFirst(sStart) method for matching a
starting string (used for autofill).
- Returns: table (list of place names, plus
findFirst)
fhUtils.createAddressList(place)
Returns the list of known addresses, optionally filtered by place name, with an added
:findFirst(sStart) method.
- Parameters:
place— place name to filter by (optional) - Returns: table (list of addresses, plus
findFirst)
fhUtils.indiList(surname, forename, year, minage, maxage)
Searches all individuals for name matches (checking all name variants: main name, given name,
nickname, _USED, alternate surname, and — for females — married surnames), applying an age
filter based on a target year.
- Parameters:
surname,forename— search strings (Soundex-aware viacompareString)year— year for which age should be computedminage,maxage— age range to include
- Returns: table of matches, one entry per hit:
{ label, ptr, age, match }
fhUtils.familyList(ptr, sType)
Helper for getParam: lists families for an individual, with an "Add new Family" entry appended.
- Parameters:
ptr— individual pointersType—'Parent'/'Child'/'FamilyAsChild'for families as child, or'Spouse'/'FamilyAsSpouse'for families as spouse
- Returns: table of
{ ptr, label }entries
fhUtils.getCurrentIndividual()
Returns the first individual currently selected — from the Property Box if it holds an individual, otherwise from the current record selection.
- Returns:
fhItemPointer(null if none found)
fhUtils.getParamValueForDisplay(field, bFixAmp)
Renders a getParam field's current value as a display string, handling each field type
appropriately (record display text, date text, list prompt, boolean label, etc.).
- Parameters:
field— a field table from agetParamfieldstablebFixAmp— iftrue, escapes&as&&for use iniup.label
- Returns:
string
fhUtils.outputUpdatedFields(tUpdatedFields, pCite)
Builds and outputs a result set summarising items that were added/updated/cited, typically after
a batch of createUpdateFact/addWitness-style operations.
- Parameters:
tUpdatedFields— indexed table of{ ptr, action }rowspCite— citation object (used for the result set title)
- Returns: none
Result sets
fhUtils.createResultTable()
Creates a result-set object providing a simplified way to build Family Historian result sets column-by-column.
- Returns: result set object with:
tblResults.<colname> = { title, type, width, align, sort, sortAscending, sortType, visibility }— define a column (assigning to any key creates it)tblResults.<colname>:set(value)— set the current row's value for that columntblResults:newRow()— advance to the next rowtblResults:rowCount()— current row counttblResults:outputResults()— sends all defined columns tofhOutputResultSetColumn
Citations
fhUtils.loadPreparedCitation()
Loads the current Prepared Citation (~._PCIT) and its source, gathering all populated
_FIELDs from both, plus PAGE, ENTRY-DATE and QUAY.
- Returns:
pCiteobject —{ result, error, ptr, sourcelink, source, fields }(result = trueanderrorset if not found), plus methods:pCite:checkRequired(...)—boolean,trueif all named citation fields are presentpCite:getValue(fieldname)— raw field value (fh object or string), ornilpCite:getDisplayValue(fieldname)— field value as display text, ornilpCite:appendCitation(ptr)— copies the prepared citation onto the given item pointer
fhUtils.formatTextFromSource(templatename, templateDefault, pCite, tOtherValues)
Formats an AutoText template by replacing citation and additional data field placeholders. Loads
the template from the plugin's AutoText folder (creating it from templateDefault if missing).
- Parameters:
templatename— template file name to loadtemplateDefault— default template content, used if the file doesn't exist yetpCite— prepared citation object fromloadPreparedCitation()tOtherValues— table of other placeholder values to substitute
- Returns:
string— formatted rich text
fhUtils.createTextFromSource(pCite, sText, sType)
Creates or updates a TEXT item from rich text and attaches it to a source or citation DATA.
- Parameters:
pCite— prepared citation object fromloadPreparedCitation()sText— rich text (RT) to storesType—"source"attaches to the source; anything else attaches to the citationDATA
- Returns:
fhItemPointer— the created or updatedTEXTitem
Creating & updating records
fhUtils.createIndi(sName, sSex)
Creates a new Individual record.
- Parameters:
sName— name;sSex—'Male'or'Female' - Returns: new Individual record pointer
fhUtils.createFamilyAsChild(ptrIndi)
Creates a new Family record and adds the individual to it as a child.
- Parameters:
ptrIndi— individual pointer - Returns: new Family record pointer
fhUtils.createFamilyAsSpouse(ptrIndi)
Creates a new Family record and adds the individual to it as a spouse.
- Parameters:
ptrIndi— individual pointer - Returns: new Family record pointer
fhUtils.addFamilyAsChild(ptrIndi, ptrFam)
Adds an individual to an existing family as a child.
- Parameters:
ptrIndi— individual pointer;ptrFam— family pointer - Returns: family record pointer
fhUtils.addFamilyAsSpouse(ptrIndi, ptrFam)
Adds an individual to an existing family as a spouse.
- Parameters:
ptrIndi— individual pointer;ptrFam— family pointer - Returns: family record pointer
fhUtils.addWitness(ptrFact, witness, role)
Adds a witness to an existing fact.
- Parameters:
ptrFact— fact pointer to add the witness towitness— individual record pointer, or a string for a name-only witnessrole— witness's role
- Returns: pointer to the new witness field
fhUtils.createFact(ptrRecord, sTag, sPlace, dtDate, sAddress, sValue, sAge)
Creates a new fact and populates its subfields, skipping any that are nil.
- Parameters:
ptrRecord— record to add the fact to (INDIorFAM)sTag— fact tagsPlace,dtDate,sAddress,sValue— optional field values (sValuefor attributes only)sAge— optional age (do not use for family facts)
- Returns: new fact record pointer
fhUtils.createUpdateItem(ptr, sTag, value)
Sets a child item's value, creating it if it doesn't exist, auto-detecting the item's data class (date/link/blob/richtext/integer/text) to call the correct setter.
- Parameters:
ptr— parent pointer;sTag— item tag;value— value to set - Returns: the item pointer
fhUtils.createUpdateFact(ptrRecord, sTag, sFactLabel, sPlace, dtNewDate, sAddress, sValue)
Prompts the user to add a new fact, update an existing one of the same tag, or (for an existing
fact) request a new citation without modifying it — the citation itself is not added by this
function. For "once only" facts (BIRT, DEAT, BAPM, MARR, BURI, CREM, CHR, CHRA) an
existing fact is offered for update; other tags always offer "add new".
- Parameters:
ptrRecord— record pointersTag— tag of the fact to prompt forsFactLabel— description of the tag being createdsPlace,dtNewDate,sAddress,sValue— new field values
- Returns:
- fact pointer (unless "Skip" was pressed, in which case nothing is returned)
- action string:
"Added","Updated", or"Cited"
Rich text
fhUtils.richTextReplace(s, old, new)
Replaces occurrences of old with new in s, escaping new for Family Historian rich text.
- Parameters:
s— string to replace in;old,new— text to find/replace with - Returns: resulting string (or nothing if
sis falsy)
fhUtils.richTextRecordLink(ptr, text)
Builds a rich-text record link (<rec=...>) for a record pointer.
- Parameters:
ptr— item pointer;text— link title text - Returns: rich-text-formatted link string (or the plain
textifptrisn't a record)
Options persistence
fhUtils.saveOptions(options, scope)
Saves a plugin options table as a serialized string via fhGetPluginDataFileName.
- Parameters:
options— table of options;scope— defaults to'CURRENT_PROJECT' - Returns: none
fhUtils.loadOptions(defaults, scope)
Loads a previously saved plugin options table, falling back to defaults if none is found.
- Parameters:
defaults— table used if no options file exists;scope— defaults to'CURRENT_PROJECT' - Returns: options table
fhUtils.resetOptions(defaults, scope)
Overwrites the saved options file with defaults.
- Parameters:
defaults— table of options to write;scope— defaults to'CURRENT_PROJECT' - Returns: none
Misc
fhUtils.version()
Returns the module's version string.
- Returns:
string, e.g."1.16 (2 Aug 2026)"
string.inList(self, ...)
Not part of fhUtils — this extends Lua's built-in string type globally. Checks whether the
string equals any of the given values.
if sTag:inList("BIRT", "DEAT", "BAPM") then ... end
- Returns:
boolean
Internal helpers (not exported)
A few functions are used internally by the module but aren't part of the fhUtils public table:
soundex(str)/soundexall(str)— Soundex encoding, used bycompareStringtable.getn— polyfilled globally if missing (pre-Lua-5.1 compatibility)
Functions using IUP constructs
These functions build or manipulate IUP UI elements
(iup.dialog, iup.button, iup.text, etc.) and therefore display a window and block on user
input when called.
Directly build/show IUP dialogs or controls
| Function | IUP usage |
|---|---|
fhUtils.setIupDefaults() |
Sets global IUP attributes (iup.SetGlobal) for quit behaviour, font, and UTF-8 mode |
fhUtils.helpButton(page, label) |
Returns an iup.button |
fhUtils.getParam(...) |
Builds and pops up a full iup.dialog, dynamically constructing iup.text, iup.list, iup.toggle, iup.button, iup.hbox/iup.vbox, and iup.scrollbox controls per field type |
fhUtils.pickIndividualPrompt(values, iYear, ptrHead) |
Builds and pops up its own iup.dialog with iup.tabs, iup.text, iup.list, and iup.button controls |
Indirectly show a dialog (via getParam)
| Function | Path |
|---|---|
fhUtils.yes(sQuestion, sTitle, hParent) |
Calls getParam to show a Yes/No iup.dialog |
fhUtils.createUpdateFact(...) |
Calls getParam to prompt for fact values |
All other fhUtils functions are headless — they operate on data (records, strings, citations,
options) without touching IUP, so they're safe to call without a UI thread or from batch/report
contexts.