ToolBook: Going to the Next Page on Ctrl + Page Down

I had this question from one of our Learning & Mastering ToolBook CD customers this morning and thought it would be a good post.

I am trying to get a learner to click Ctrl + Page Down to get to the next screen. Where can I find instructions on how to do that? 

My response: I would try adding an Action at the page level and handling the keyDown event. The first task is to figure out the keyCode for Page Down. Put in an alert on the keyCode parameter as shown below.

Go to reader mode and press the Page Down key. You’ll see an alert with 34 as shown below:

Next, you need to figure out how to go to the next page. Easiest is to trigger the next page button on the background. However, if you are using SmartPages, the Actions Editor can only “see” the group and not the buttons. But there is a LiveXtension for this as shown below:

You can now Trigger the next page button as shown. This has the advantage of NOT working if that button is disabled, such as when you disable it while they are watching a video.

Note that you probably want to put this on the background so that it works on all pages using that background. Be sure to publish to HTML and then test there as sometimes things work differently in the browser.

Adding a Paint Object Programmatically

For the ToolBook 11 version of Learning & Mastering ToolBook 11, we wanted to add the “Configuring LiveXtensions” button and associated graphic to a training page. However, the page was programmed to show and hide paint objects. Starting with ToolBook 10.5, however, the option to import paint objects was taken out. Instead, inserting a graphic now creates a bitmap resource and image object. We COULD have converted the other paint objects to resources and image objects, but why do that when we can still get a paint object via OpenScript using the code to below. The only slight snag is that the graphic was originally a .png. That wouldn’t import but the .bmp version worked fine.

importgraphic "C:\Users\Jeff\ToolBookProducts\ICBT110\training\resources\icbt2\update\LiveXtensionsConfiguration.bmp"

Enabling or Disabling Voice Recordings

How would you write a tool like the “Voice Recording Toggle” that is available via LiveXtensions in ToolBook? The first task is to figure out how the enabling happens. If you check the “Enable voice recording playback for this page” box on the Voice Recordings tab of the Properties for Page sheet and then check the User Properties of the page using the Property Browser, you’ll see that it adds a property called TBK_VoiceRecordings. It is true if recordings are enabled and false otherwise. That leads to an implementation from the Command Window like the code below. Notice how we check whether the property of the page is NULL. That keeps us from setting the property where there is no voice recording in the first place.

isEnabled = TRUE
step num from 1 to pageCount of this book
	pageId = page num
	if TBK_VoiceRecording of pageId <> NULL
		TBK_VoiceRecording of pageId = isEnabled
	end if
end step

International Date Formats

Question: I need to get out of my provincial ways and format dates appropriate to the local preference (i.e., US, European, etc). Are there any functions I could take advantage of in ToolBook to make my path easier for localization of date formats?

The function below from our TBK Tracker product should be helpful. In early versions, we ran into problems with dates stored in an Access database on the user’s machine being in a different format than what we expected in ToolBook. This was particularly true in regions that put the day first (25/2/2014 as opposed to 2/25/2014).

to handle tbk_setSysDateFormat
	-- if not ASP mode, converts registry date 
	-- format to ToolBook equivalent
	-- else gets ToolBook date format from ASP 
	-- page

	system string s_originalSysDateFormat, 
		s_fileDir, s_tbk_iniFile, s_tbk_dbType

	local string newFormat, blkSus, errorString, aspPage
	local word offsetNum

	if s_originalSysDateFormat <> NULL 
		-- already set
		break
	end if

	if s_tbk_dbType = "ASP" -- get date format 
		-- from server-side ASP page
		aspPage = tbk_getASPPage("formatData")
		get platte_SetHTTPPostParameter("formatType", "date")
		s_originalSysDateFormat = platte_HTTPPost(aspPage)
		if s_originalSysDateFormat = null
			s_originalSysDateFormat = "mm/dd/y"
		end if
	else -- use date format of client machine
		blkSus = ASYM_blockSuspend()
		newFormat = RegistryGetKey("HKEY_CURRENT_USER\ControlPanel\International", \
		    "sShortDate")
		errorString = ASYM_restoreSuspend(blkSus)
		if (errorString <> null) OR (newFormat = null) 
		    -- unable to read date format 
			-- from registry; try win.ini
			newFormat = getWinIniVar("intl", "sShortDate")
			if (newFormat = null) OR (ASYM_isNumber(newFormat) = TRUE) 
				-- unable to read from ini file
				s_originalSysDateFormat = sysDateFormat -- default
				break
			end if
		end if

		-- year
		offsetNum = offset("yyyy", newFormat)
		if offsetNum <> 0
			chars offsetNum to (offsetNum + 3) of newFormat = "y"
		end if

		-- month
		if offset("MMM", newFormat) = 0 -- leave 
			-- alone if contains MMM; else make 
			-- lower case
			newFormat = lowerCase(newFormat)
		end if

		-- day
		-- no conversion req'd

		s_originalSysDateFormat = newFormat
	end if
end tbk_setSysDateFormat

You can skip the “ASP” part. The rest reads the registry and uses that to set the s_originalSysDateFormat system variable. When it comes time to display a date, we temporarily set sysDateFormat to this variable. The rest of the time, we set sysDateFormat to “seconds” since we use the sysDate for time tracking. You likely could just set sysDateFormat once.

Avoiding “Save Current Changes” Message

Question: Does anyone know how to eliminate the “Save current changes?” message when the user hyperlinks from one book to another?

Answer: You can set the saveOnClose property of the book to No. Easiest way to do this is to go to Properties for Book – General Tab. Set the “Save on Close” ComboBox to Never. Or you could go to the Command Window and use this script:

saveOnClose of this book = "No"

Be careful though, as doing this will keep you from being prompted to save changes in author level as well. If you want to be prompted at author level, you could put this in your book script:

to handle enterApplication
	forward
	if sysRuntime = TRUE -- runtime ToolBook
		saveOnClose of this book = "No"
	else -- author level ToolBook
		saveOnClose of this book = "Ask"
	end if
end enterApplication

Aligning Objects within Groups

I was doing a demo of the Plug-In Pro awhile back and we got into a discussion of how it is a hassle if objects within a group (such as a multiple choice question) have gotten moved around slightly. I came up with the script to the left to deal with that. You figure out the desired left coordinate (4403 page units in this example), select the group, and then press Enter in the Command Window. The getObjectListfunction grabs all the objects within the selected object (group). We then pop each item from the list into a separate variable and set the x coordinate (item 1 of position) to the desired value. Note that passing FALSE to getObjectList means that all matching objects are included, regardless of whether they have a script.

leftVal = 4403

objList = getObjectList(selection, "", FALSE)

while objList <> null
	pop objList into objId
	item 1 of position of objId = leftVal
end while

Using Resources of Another Book

Question: I want to use a system book as a source of my images. Is this possible?

Answer: This is definitely possible. I have included some code from our Plug-In Pro below that should help. The main idea is that you want to check to see if the resource is already there. If so, either use it or call replace resource. If not, you want to use import bitmap resource. In the code below, callingBookId would refer to your system book. You could get your hands on that by using sysBooks. For example:

callingBookId = book (item 1 of sysBooks)

Here is the code I mentioned.

shortName = chars max(1, maxChars - 31) to maxChars of fullImageFile 
tempRef = "bitmap" && quote & shortName & quote && "of" && callingBookId 
oldImageRef = normalGraphic of placeholderId 
if isObject (tempRef)
        replace resource tempRef with imageFile
        normalGraphic of placeholderId = tempRef
        errString = ASYM_RestoreSuspend(blkSus) 
else
        import bitmap resource imageFile as shortName
        normalGraphic of placeholderId = bitmap shortName
        errString = ASYM_RestoreSuspend(blkSus) 
end if

Determining if an Object is Within a Group

I’m putting some recent (2010 and later) segments from The EnterPage Newsletter into my blog. That is what is behind this flurry of activity:).

Question: I want to find out through OpenScript whether an object is part of a certain group.

Answer: Since objects and groups can be within other groups, I think the easiest way is to loop through the parents of the object as in the code below. If you know that there is only a single group, though, a quicker solution would be to use the objectContainer function like this:

groupId = objectContainer(button "button 1", "group") 
if groupId = group "group 2"
	request "IsInGroup = true"
else
	request "IsInGroup = false"
end if

Here is a more general solution that will work with groups inside groups.

to handle buttonClick
	local logical isInGroup

	isInGroup = findIfInGroup(button "button 1", group "group 2")
	request "isInGroup = " & isInGroup
end buttonClick

to get findIfInGroup object startingObject, object groupObject
	local logical inGroup
	local object parentId
	local string parentType

	parentId = startingObject
	parentType = object of startingObject

	inGroup = FALSE
	while (inGroup = FALSE AND (parentType <> "page" AND parentType <> "background"))
		parentId = parent of parentId
		if parentId is groupObject
			inGroup = TRUE
		else
			parentType = object of parentId
		end if
	end while

	return inGroup
end findIfInGroup

OpenScript to Get an Alphabetized List of Objects on a Page

I’m working on a new version of the Storyboarder utility and wanted to get a list of all the object names on a page so that I could read the values of the various fields, checkboxes, and comboboxes. Since ToolBook OpenScript doesn’t have any type of IntelliSense where the editor helps you with the object names, you must come up with them on your own. It is a pain to keep closing the editor (which is modal in ToolBook) to go find the object name, so I used the script below to make a list of names. I can then put this in TextPad and grab the object names as I need them. I thought someone else may find it useful. Just paste it into the Command Window and press Enter. There are lots of scripts like this in The ToolBook Companion, but using OpenScript and the Command Window is something of a lost art:).

objList = getObjectList(this page, "", FALSE) 
nameList = "" 
while objList <> null  
     pop objList into objId  
     if name of objId <> null   
          push name of objId onto nameList  
     end if 
end while 
nameList = sortList(nameList) 
put listToTextline(nameList)

Here’s a partial list of the result:

includePageIdsBox
includePageNamesBox
includePageNumbersBox
includeQuestionsBox
includeRecordfieldsBox
includeScreenCapturesBox
includeSimulationsBox
pageRangeGroup
powerboardDirectory
powerboardDirectoryBtn

Another Method to Eliminate Suspend Data: SCORM 2004

As a follow-up to last post, the customer noticed that the user was not getting a bookmark back to the scoring page during the next section. I had used SCORM Watch for testing and saw the bookmark (cmi.location) being sent by ToolBook. But then I tested in Tracker.Net and saw that the customer was right. This was because the SCORM 2004 standard says that once a lesson (SCO) has been marked as complete, the user basically has to start over if they return to the lesson. That means that no bookmark, status, or suspend data is sent the next session. So a simpler solution to preventing the extra suspend data is to stick with SCORM 2004 (this doesn’t work in SCORM 1.2) and mark the book as completed if the user passes and otherwise discard results. This is shown below.

This is somewhat less efficient than the last post in that the huge suspend data is sent to the LMS and stored. But it is not sent BACK to the content and, most importantly, ToolBook doesn’t need to spend the time and processing power to reset all the questions to their original state.