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

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 display
    • label — 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 title
    • sTopMessage — text displayed at the top of the prompt, can be nil
    • fields — table of field definitions (see below)
    • tButtons — table of button labels (or iup objects) shown along the bottom; defaults to { "OK" }
    • shortcuts — table of string-array autofill shortcuts, keyed by field tag
    • hParent — parent dialog handle
  • Returns: fields table, extended with .results (values keyed by tag), .ok, .button_pressed and .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 display
    • sTitle — message box title
    • hParent — parent window handle; defaults to the Family Historian main window
  • Returns: booleantrue if 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 — a getParam results table (used to pre-fill name search fields)
    • iYear — year, used to estimate ages when searching
    • ptrHead — pointer to an individual the result may be related to (enables the relationship tab)
  • Returns:
    • table — { tab, ptr, name, relation, fam } where tab is -1 (cancelled), 1 (selected record), 2 (create record) or 3 (name only); ptr is the selected record (tab 1); name is the entered name (tab 2); relation is 1=Spouse, 2=Child, 3=Other, 4=Parent; fam is the family pointer to add to
    • errboolean, true if the dialog was cancelled
    • titlestring describing the selection/creation made
  • 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 in
    • string2 — string to search for
    • bUseSoundex — if true, compare Soundex codes instead of literal text
  • Returns: booleantrue if matched (or if string2 is nil)

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 via compareString)
    • year — year for which age should be computed
    • minage, 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 pointer
    • sType'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 a getParam fields table
    • bFixAmp — if true, escapes & as && for use in iup.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 } rows
    • pCite — 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 column
    • tblResults:newRow() — advance to the next row
    • tblResults:rowCount() — current row count
    • tblResults:outputResults() — sends all defined columns to fhOutputResultSetColumn

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: pCite object — { result, error, ptr, sourcelink, source, fields } (result = true and error set if not found), plus methods:
    • pCite:checkRequired(...)boolean, true if all named citation fields are present
    • pCite:getValue(fieldname) — raw field value (fh object or string), or nil
    • pCite:getDisplayValue(fieldname) — field value as display text, or nil
    • pCite: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 load
    • templateDefault — default template content, used if the file doesn't exist yet
    • pCite — prepared citation object from loadPreparedCitation()
    • 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 from loadPreparedCitation()
    • sText — rich text (RT) to store
    • sType"source" attaches to the source; anything else attaches to the citation DATA
  • Returns: fhItemPointer — the created or updated TEXT item

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 to
    • witness — individual record pointer, or a string for a name-only witness
    • role — 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 (INDI or FAM)
    • sTag — fact tag
    • sPlace, dtDate, sAddress, sValue — optional field values (sValue for 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 pointer
    • sTag — tag of the fact to prompt for
    • sFactLabel — description of the tag being created
    • sPlace, 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 s is 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 text if ptr isn'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 by compareString
  • table.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.