Utility AppleScript Subroutines and Methods

This page lists some utility subroutines that may be used by more than one tutorial:

These utilities are written to be as general as possible so then can be reused in different scripts. You can copy and paste the script code in the boxes on this page into your own scripts.

CheckAvailable()
(* Activate GEDitCOM II (if needed) and verify acceptable
     version is running and a document is open. Return true
     or false if script can run.
*)
on CheckAvailable(sName,vNeed)
  tell application "GEDitCOM II"
    activate
    if versionNumber < vNeed then
      user option title "The script '" & sName & ¬
        "' requires GEDitCOM II, Version " & vNeed & " or newer"¬
        message "Please upgrade and try again." buttons {"OK"}
      return false
    end if
    if number of documents is 0 then
      user option title "The script '" & sName & ¬
        "' requires a document to be open" ¬
        message "Please open a document and try again." ¬
        buttons {"OK"}
      return false
    end if
  end tell
  return true
end CheckAvailable

CheckAvailable(sName,vNeed) Subroutine

Nearly all GEDitCOM II scripts will start by calling this subroutine to verify the script can run.

First, this subroutine checks the version number of GEDitCOM II by using the versionNumber property of the application. As GEDitCOM II develops, it will add new AppleScripting features, but new features cannot be used unless the version currently running supports them. The AppleScripting dictionary for GEDitCOM II will mention which features are only available in some versions (e.g., it will say "since 1.1" for a particular command or property). If you use such a feature, this subroutine can verify that feature is in the currently running GEDitCOM II. The version number you need is passed in the vNeed subroutine argument.

Note that the MacOS has to compile the script before it can run. A script written for a newer version of GEDitCOM II may not even be able to compile and thus an error will occur before the script runs and therefore before this method can check the verison. If errors occur during compiliation, either the script has bugs or you need to update GEDitCOM II to the latest version.

Second, it almost never makes sense to run a script unless a document is open. The second check verifies that at least one document is open.

Finally, this subroutine returns true if it passes all tests or returns false if any test fails. Note that the script name is passed in the variable sName. If needed, that name is displayed in a message window that explains the reason the script will not run.


notify progress logic
  -- set progress variables
  set {fractionStepSize, nextFraction} to {0.01, 0.01}
  
  -- main script loop
  tell application "GEDitCOM II"
    repeat with i from 1 to numTasks
      -- the script code
      
      -- time for progress
      set fractionDone to i / numFams
      if fractionDone > nextFraction then
        notify progress fraction fractionDone
        set nextFraction to nextFraction + fractionStepSize
      end if
    end repeat
  end tell

notify progress Command

AppleScripting is a great tool, but it can get slow for large calculations in large files. For this reason, all good GEDitCOM II scripts should inform the user of the progress of the calculations. The code on the right gives the logic

At the beginning of the script you define two variables. The fractionStepSize defines how often to inform the user of progress. There is no point in sending too many notifications since the progress is rounded to the nearest present. A good choice is usually to notify the user after each percent is completed and thus fractionStepSize is set to 0.01. The nextFraction variable defines the next time the notification should be sent to GEDitCOM II. It is initialized to the same value as fractionStepSize to define the point for the first notification.

Periodically during the script, you should then calculate the fraction of the script that has finished. This sample code shows a script with a repeat loop that will execute numTasks times. At the end the each pass through the loop, the fractionDone is easily calculated to be i/numTasks. If this fraction has passed a predetermined notification point (stored in nextFraction), a notification is sent to GEDitCOM II using the notify progress command and nextFraction is advanced by the fractionStepSize defined at the start of the script.

For other scripts, it may be harder to determine the fraction completed (such as a script searching for ancestors when you do not know how many ancestors will be found). For these scripts, you have two options. First, you can periodically guess the fraction done and send a notify progress to GEDitCOM II. Second, you can post a message to the user with a second message argument to the notify progress command; it can be any string. Third, you can do nothing. The scripting palette in GEDitCOM II will still show a spinning progress wheel to indicate the script is still running.


GetAgeSpan()
(* Convert two date SDNs to years from first date to second date *)
on GetAgeSpan(beginDate, endDate)
	return (endDate - beginDate) / 365.25
end GetAgeSpan

GetAgeSpan(beginDate, endDate) Subroutine

This simple subrouting converts the time between two day numbers into the number of years. The time span is from the beginDate to the endDate.


RoundNum()
(* Round number n to numDecimal digits *)
on RoundNum(n, numDecimals)
  if numDecimals = 0 then
    return round n as string
  end if
  set x to 10 ^ numDecimals
  set nstr to ((((n * x) + 0.5) div 1) / x) as string
  set decPt to offset of "." in nstr
  if decPt = 0 then
    set nstr to nstr & "."
    set extra to numDecimals
  else
    set extra to numDecimals - (number of characters in nstr) + decPt
  end if
  repeat with i from 1 to extra
    set nstr to nstr ∓ "0"
  end repeat
  return nstr
end RoundNum

RoundNum(n, numDecimals) Subroutine

When outputting decimal numbers to a report or to a text file, it is often useful to control the format of the output number. I don't know of a AppleScript method for the task, but this subroutine does some formatting. Given any number in n, it will return a string representation of the number with numDecimals digits after the decimal point. If fewer digits are needed, extra "0" digits will added to reach the desired number of digits.

For example:

     RoundNum(43.81534,2)   ->   43.82
     RoundNum(72,3)         ->   72.000
     RoundNum(22.8144,1)    ->   22.8
     RoundNum(52.8144,0)    ->   53

TextToPages()
(* Change current selection to theText using paragraph properties in pstyle
      When done, move the selection point to the end of the inserted text.
*)
on TextToPages(theText, textStyle)
  tell application "Pages"
    tell front document
      set nc to count of characters
      set selection to theText & " "
      set endnc to count of characters
      if textStyle is not "" then
        set nc to nc + 1
        tell characters nc thru endnc
          set properties to textStyle
        end tell
      end if
      select character endnc
      set selection to ""
    end tell
  end tell
end TextToPages

TextToPages(theText,textStyle) Subroutine

Figuring out how to add style text to a Pages document is challenging. This utility subroutine solves the problem and may be all you need to write scripts to create Pages documents. The subroutine write the text in theText to the current selection and then moves the insertion point to after the entered text. A series of calls to this subroutine can build any report by adding blocks of text to the document.

The textStyle parameter lets you control the font and paragraph style for the inserted text. This parameter is an AppleScript record with any property allowed in the character element defined in Pages' scripting dictionary. When inserting a series of text blocks, the textStyle only needs to include properties that have changed from the previous text. If there are no changes, it can be an empty string ("")

The subroutine sets the current selection to the input text and adds a single blank character. If textStyle is provided, it is used to set the properties of the recently inserted text. Finally, the last character of the inserted text is selected and deleted. This deleted character is the blank character inserted above. When done the selection point will be at the end of the document ready to insert the next text. The extra blank character was a trick that solved the problem of getting the selection to the end of the inserted text in all cases; there may be other solutions, but this one works.


TextToWord()
(* Insert the text using changes in font in fontStyle and changes in
     paragraph style in paraStyle. Either can be "" to ignore. The new
	 text replaces the current selection.
*)
on TextToWord(theText, fontStyle, paraStyle)
  tell application "Microsoft Word"
    tell selection
      if fontStyle is not "" then
        set properties of font object to fontStyle
      end if
      if paraStyle is not "" then
        set properties of paragraph format of text object to paraStyle
      end if
      type text text theText
    end tell
  end tell
end TextToWord

TextToWord(theText,fontStyle,paraStyle) Subroutine

The MS Word scripting dictionary is complex and verbose. It took some work to come up with a reliable method for inserting formatted text, but this method does the trick and turned out rather simple.

This subroutine will replace the current selection with theText and format the new text using the font style in fontStyle and the paragraph style in paraStyle. These two style parameters should be an AppleScript record. They can contain any property allowed for the font object or the paragraph format elements, both of which are defined in MS Word's scripting dictionary. When inserting a series of text blocks, the style parameters only need to include properties that have changed from the previous text. If there are no changes, they can be empty strings ("")

When the method is done, the insertion point will be moved to the end of the new text. A series of calls to this method can build a report by inserting blocks of text with any style desired. The key AppleScript command was the type text command. The philosophy of the MS Word scripting interface is more like "Macros" than background program. The scripting options are meant to duplicate all the ways a user can interact with MS Word. Here the type text command is literally typing the text you provide into the document after setting the new font and paragraph styles.