Add Buttons to Multi State Object

In our previous blog we demonstrated how one could create a mult-istate object using an AppleScript. To keep it simple, we left the process of adding buttons to the object up to the reader.

As long as you have gone to the trouble of creating a script to create a multi state object, it makes sense to let the script add the buttons automatically. For demonstration, we will create the buttons to a selected multi-state object. For creating interactive buttons, you have two options:

  • Use a button from InDesign’s button library (“ButtonLibrary.indl”
  • Create your own button

For this sample script we will use a button from InDesign’s button library.

AddButtons Script

The first task for the script will be to get a reference to the multi-state object to which the buttons will be associated. For now, we will assume the object has been created and is selected. For the purpose of calculation, make sure the measurement units for the document are in points or pixels. When the following is run, a reference to the selected object will be placed in the variable MSObject:

   set MSObject to checkSelection()
   --=========
   --HANDLERS
   --=========
   on checkSelection()
	tell application "Adobe InDesign CC 2014"
		set selList to selection
		if length of selList is 0 then
			error "Requires multi state object selection"
		end if
		set selItem to item 1 of selList
		if class of selItem = multi state object then
			return selItem
		else
			error "Requires multi state object selection"
		end if
	end tell
   end checkSelection

Notice that the checkSelection handler will throw an error if there is no selection or if the class of the selected item is not the required class.

Button Library

InDesign’s button library has a number of pre-defined buttons in a variety of colors. To allow the user to choose the buttons from a color list, we can use the following:

   --at top of script
   global buttonColors
   global buttonNumbers
   set buttonColors to {"Blue", "Gray", "Green", "Red", "Black"}
   set buttonNumbers to {143, 145, 147, 149, 151}
   --after call to checkSelection
   set buttonNumber to getButtonChoice()
   --in HANDLERS section of script
   on getButtonChoice()
	set buttonChoice to choose from list buttonColors
	if class of buttonChoice is list then
		set buttonColor to item 1 of buttonChoice
	else
		error "User Cancelled"
	end if
	repeat with i from 1 to length of buttonColors
		if buttonColor = item i of buttonColors then
			set buttonNumber to item i of buttonNumbers
			exit repeat
		end if
	end repeat
	return buttonNumber
   end getButtonChoice

This returns the number of the back button that corresponds to the chosen color. For the forward button, the script will add 1 to this number. The following opens the button library and gets a reference to the assets for both buttons.

   --add to top of script
   set libName to "ButtonLibrary.indl"
   --after call to getButtonChoice
   set {buttonLeft, buttonRight} to getButtonRefs (libName, buttonNumber, MSObject)
   --place in HANDLERS section for script
   on getButtonRefs (libName, buttonNumber, MSObject)
      tell application "Adobe InDesign CC 2014"
	set docRef to document 1
	set pageRef to parent page of MSObject
	set appPath to file path as string
	set libraryPath to appPath & "Presets:Button Library"
	set filePath to libraryPath & libName
	if not (exists library libName) then
		set fileAlias to (libraryPath & ":" & libName) as alias
		set libReference to open fileAlias
	else
		set libReference to library libName
	end if
	set buttonLeft to asset ("" & buttonNumber) of libReference
	set rightNumber to buttonNumber + 1
	set buttonRight to asset ("" & rightNumber) of libReference
	return {buttonLeft, buttonRight}
      end tell
   end getButtonRefs

Placing Buttons

All that is left at this point is to calculate the page position at which the buttons will be placed, place them, and set their behaviors. Notice for calculating place point the script does the math using the center point of the buttons as reference.

   --at top of script
   set buttonHalfSize to 10
   --after call to getButtonRefs
   addButtons(MSObject, buttonLeft, buttonRight, buttonHalfSize)
   --place in HANDLERS section for script
   on addButtons(MSObject, buttonLeft, buttonRight, buttonHalfSize)
      tell application "Adobe InDesign CC 2014"
	   set docRef to document 1
	   set pageRef to parent page of MSObject
	   set itemBounds to geometric bounds of MSObject
           --calculate the vertical center of MSObject
	   set cy to (((item 3 of itemBounds) - (item 1 of itemBounds)) div 2) + (item 1 of itemBounds)
	   set cx to (item 2 of itemBounds) - (buttonHalfSize * 2)
	   set backButton to my makeButton(docRef, pageRef, buttonLeft, buttonHalfSize, cx, cy)
	   tell backButton
		make goto previous state behavior with properties {associated multi state object:MSObject, event:mouse up, enable behavior:true, loops to next or previous:true}
		set x to behaviors
	   end tell
	   set cx to (item 4 of itemBounds) + (buttonHalfSize * 2)
	   set nextButton to my makeButton(docRef, pageRef, buttonRight, buttonHalfSize, cx, cy)
	   tell nextButton
		make goto next state behavior with properties {associated multi state object:MSObject, event:mouse up, enable behavior:true, loops to next or previous:true}
	   end tell
      end tell
   end addButtons

   on makeButton(docRef, pageRef, assetRef, bSize, cx, cy)
	set placePt to {cx - bSize, cy - bSize}
	tell application "Adobe InDesign CC 2014"
		set placedObj to place asset assetRef on docRef
		move placedObj to pageRef
		move placedObj to placePt
	end tell
	return item 1 of placedObj
   end makeButton

Interesting to note in the makeButton handler, the value for placedObj is a list with the button reference as its first and only item. For this reason the handler returns item 1 of placedObj.

Putting It Together

To handle errors and call the handlers, the top portion of the script should look similar to the following:

   global buttonColors
   global buttonNumbers
   set buttonColors to {"Blue", "Gray", "Green", "Red", "Black"}
   set buttonNumbers to {143, 145, 147, 149, 151}
   set buttonHalfSize to 10
   try
      set MSObject to checkSelection()
      set buttonNumber to getButtonChoice()
      set libName to "ButtonLibrary.indl"
      set {buttonLeft, buttonRight} to getButtonRefs(libName, buttonNumber)
      addButtons(MSObject, buttonLeft, buttonRight, buttonHalfSize)
   on error errStr
   activate
      display alert "Error: " & errStr
      return
   end try

MSOWithButtons

Now that you have the button portion of the script working, you can add it to the script from the previous blog to create a script that creates the multistate object and buttons. For this the top portion of the script will read as follows:

   global buttonColors
   global buttonNumbers
   set buttonColors to {"Blue", "Gray", "Green", "Red", "Black"}
   set buttonNumbers to {143, 145, 147, 149, 151}
   set buttonHalfSize to 10
   set libName to "ButtonLibrary.indl"
   try
	set selBounds to getSelectionBounds()
	set buttonNumber to getButtonChoice()
	set imagePath to getImagePath("Images")
	set fileList to getFileList(imagePath)
	set MSObject to makeMSO(selBounds, imagePath, fileList, "MultiImages")
	set {buttonLeft, buttonRight} to getButtonRefs(libName, buttonNumber, MSObject)
	addButtons(MSObject, buttonLeft, buttonRight, buttonHalfSize)
   on error errStr
	activate
	display alert errStr
   end try

You will also need to add the following handlers from our AddButton script above:

  • addButtons
  • getButtonRefs
  • makeButton
  • getButtonChoice

Now open or create a document for the multi-state object. Make sure measurements are in points or pixels. Create a rectangle the desired size of the object and at the required position on the page. With the rectangle selected, test out your MSOWithButtons script.

 Our multi-state object with buttons

Onward and Upward

You should now be well on your way to being able to to refine this script by using a custom dialog for the user interface. In addition to choosing the color, you may want to give the user the option of positioning the buttons on the top, bottom, or sides (as above).