Customizing Pages ’09: System Events and modifier keys

Posted by Pierre Igot in: Pages
April 6th, 2010 • 11:15 am

After my post last week on using scripts and keyboard shortcuts to customize Pages ’09, I was in a “customizing” mood and decided to revisit an old issue for which I used to have a scripted solution back in the days when Microsoft Word was my main word processing tool.

The issue has to do with formatting lists. As readers familiar with word processing tools know, there are two ways to format lists of items in a word processor: with user-defined paragraph styles or with automatic list styles.

I first came across automatic list styles in Microsoft Word, when they introduced their “Bullets and Numbering…” feature. Needless to say, like everything that Microsoft produces, this feature was a half-baked solution that created about as many new problems as it pretended to solve existing ones.

Microsoft have supposedly fine-tuned the feature over the years, but, based on the documents produced by other people that I receive as part of my work, there are still far too many problems that users do not know how to solve and the end result is that most Word documents using automatic list formatting are buggy with alignment or numbering problems that are nearly impossible to solve and make you want to pull your hair out.

Based on my initial experience with this poorly implemented feature, I quickly decided that it was not worth the trouble and that, in the vast majority of situations, it is much preferable to continue to format lists using manually defined user styles, even if it involves typing the bullet symbols manually or, in the case of numbered lists, having to enter the numbers manually.

Yes, it’s a pain to have to re-enter the numbers when the number or the order of the items in the list changes, but it is a much smaller pain than having to deal with Microsoft Word’s crappy pseudo-automatic features.

So I never use Word’s automatic features myself, but of course I still have to deal with them on a daily basis in documents that I receive from other people.

What about Apple’s Pages? Well, it comes with its own automatic feature for formatting lists, in the form of a series of list styles that are included by default in all new documents and that can be customized.

Overall, these list styles work better than Microsoft Word’s equivalent “Bullets and Numbering…” feature. They are more reliable and do not break so easily.

That said, I still find them awkward to use as soon as I want something beyond the most basic stuff. In particular, they are not particularly convenient when you need numbered lists with multiple hierarchical levels. There are so many manual adjustments required that it is not really worth the trouble.

In addition, the user interface in Pages ’09 is heavily reliant on the mouse, because the list style options are only available through the “List” tab in the text inspector. When you are in the process of entering a list of stuff, it is a pain to have to switch constantly from the keyboard to the mouse and back.

All this is to say that, because lists often play an important role in the type of documents I have to work on, I really need my own custom solution in order to reach a higher level of productivity in my work.

I had designed such as solution back in the day in Microsoft Word using macros and keyboard shortcuts, but of course I lost all this when I abandoned Word and moved to Pages. Besides, Microsoft eliminated Visual Basic for Applications in Word 2008, so I would have had to rebuild my solution from scratch even if I had stayed with Microsoft Word.

After my initial experience with customizing Pages ’09 last week, I decided to try and recreate my solution in Pages ’09 using AppleScript.

Here is, in a nutshell, how my custom solution is designed to work.

In my documents, I have a style sheet that includes a series of user-defined paragraph styles (not to be confused with Pages ’09′s own automatic list styles) named “List 1,” “List 2,” List 3,” “List 4,” and so on.

These styles are pretty simple. They have a left indent and then a negative first line indent, as well as a tab aligned with the left indent. (The tab stop is redundant in Pages, but necessary when exporting the document in other file formats.) The “List 1” style is aligned with the left margin, because it’s intended for the first level:

List 1

Then all the other styles have a left indent of increasing value. This is the “List 2” style:

List 2

And it goes on like this. With such styles, I can easily create a hierarchical list, whether it is numbered or simply formatted with bullet symbols:

Hierarchical list

For each list style, I also have a corresponding “List n – Keep With Next” style. The “Keep With Next” style has the same formatting options as its corresponding “List n” style, as well as the “Keep with following paragraph” option. This is intended for use when a “List n” item introduces a “List n+1” list, as with 1b in the hierarchical example above. The reason is simple: I don’t want the “List n” item introducing the “List n+1” list to be separated from the list it introduces by an automatic page break.

I also have a “Body – Keep With Next” style that I use to format the paragraph of body text that comes immediately before the beginning of a level 1 list.

And finally, for more formatting flexibility, I also have, for each list style, a corresponding “List n – Last” list. This style is intended for the last item in the list. It has the same formatting options as other list items, except for a bit of extra space after.

So, to sum up, I have the following paragraph styles in my style sheet:


Body
Body – Keep With Next
List 1
List 1 – Keep With Next
List 1 – Last
List 2
List 2 – Keep With Next
List 2 – Last
List 3
List 3 – Keep With Next
List 3 – Last

and so on.

Now my idea is to create scripts that make it easy to move from one style to the next with keyboard shortcuts while entering the text for the items in the lists. The system I used to have in Word used the command-Return shortcut to start a list and the shift-command-Return shortcut to end a list.

command-Return would trigger a “Start List” script that would examine the current paragraph style and automatically change it to the corresponding “Keep With Next” style, then move to the next paragraph and change the style of that paragraph to the list style for the next level.

In other words, if the current paragraph style was “Body,” my “Start List” script would change it to “Body – Keep With Next” and then start a new paragraph in “List 1” style.

If the current paragraph style was “List 1,” that same “Start List” script would change it to “List 1 – Keep With Next” and then start a new paragraph in “List 2” style.

And so on.

The “End List” script triggered by the shift-command-Return shortcut, on the other hand, would examine the current paragraph style and automatically change it to the corresponding “Last” style, then move to the next paragraph and change the style of that paragraph to the list (or body) style for the previous level.

In other words, if the current paragraph style was “List 1,” my “End List” script would change it to “List 1 – Last” and then start a new paragraph in “Body” style.

If the current paragraph style was “List 2,” my “End List” script would change it to “List 2 – Last” and then start a new paragraph in “List 1” style.

And so on.

Now the question was: Could I achieve the same thing in Pages ’09 with AppleScript?

After my experimentation with custom scripts and keyboard shortcuts last week, I believed I had the required building blocks, i.e. the necessary bits of AppleScript code and a utility such as Red Sweater Software’s FastScript for assigning keyboard shortcuts to my scripts.

Unfortunately, as per usual with AppleScript (or so it seems), things turned out to be rather more complicated than I expected. I will spare you all the details of my experimentations. But the basic problems I encountered were, once again, that I couldn’t figure out how to do some of the simplest things in AppleScript (like moving the insertion point), and also that defining keyboard shortcuts comes with its own set of challenges.

I will start by giving the scripts that I ended up using, and then I’ll provide some explanation.

My “Start List” script ended up being the following:

tell application "Pages"
	activate
	set mySel to (get selection of document 1)
	set myStyle to paragraph style of mySel
	if myStyle is paragraph style "Body" of document 1 then
		set paragraph style of mySel to "Body - Keep With Next"
	else if myStyle is paragraph style "List 1" of document 1 then
		set paragraph style of mySel to "List 1 - Keep With Next"
	else if myStyle is paragraph style "List 2" of document 1 then
		set paragraph style of mySel to "List 2 - Keep With Next"
	else if myStyle is paragraph style "List 3" of document 1 then
		set paragraph style of mySel to "List 3 - Keep With Next"
	else if myStyle is paragraph style "List 4" of document 1 then
		set paragraph style of mySel to "List 4 - Keep With Next"
	end if
	delay 0.5
	tell application "System Events" to tell application process "Pages"
		key code 36
	end tell
	set mySel to (get selection of document 1)
	if myStyle is paragraph style "Body" of document 1 then
		set paragraph style of mySel to "List 1"
	else if myStyle is paragraph style "List 1" of document 1 then
		set paragraph style of mySel to "List 2"
	else if myStyle is paragraph style "List 2" of document 1 then
		set paragraph style of mySel to "List 3"
	else if myStyle is paragraph style "List 3" of document 1 then
		set paragraph style of mySel to "List 4"
	end if
end tell

And my “End List” script ended up being the following:

tell application "Pages"
	activate
	set mySel to (get selection of document 1)
	set myStyle to paragraph style of mySel
	if myStyle is paragraph style "List 1" of document 1 then
		set paragraph style of mySel to "List 1 - Last"
	else if myStyle is paragraph style "List 2" of document 1 then
		set paragraph style of mySel to "List 2 - Last"
	else if myStyle is paragraph style "List 3" of document 1 then
		set paragraph style of mySel to "List 3 - Last"
	else if myStyle is paragraph style "List 4" of document 1 then
		set paragraph style of mySel to "List 4 - Last"
	end if
	delay 0.5
	tell application "System Events" to tell application process "Pages"
		key code 36
	end tell
	set mySel to (get selection of document 1)
	if myStyle is paragraph style "List 1" of document 1 then
		set paragraph style of mySel to "Body"
	else if myStyle is paragraph style "List 2" of document 1 then
		set paragraph style of mySel to "List 1"
	else if myStyle is paragraph style "List 3" of document 1 then
		set paragraph style of mySel to "List 2"
	else if myStyle is paragraph style "List 4" of document 1 then
		set paragraph style of mySel to "List 3"
	end if
end tell

(There are probably more elegant ways of doing some of the things here. But I only have so much time to devote to my scripting activities.)

Now for the explanation. Retrieving the current style of the selection turns out not to be difficult at all. And changing it to something else is not hard either. What is hard, however, is to figure out how to change the selection from one paragraph to the next. Inserting a new paragraph is not particularly difficult. You can use something like “set the selection to contents of the selection & return.”

The hard part, however, as far as I can tell, is to control what is currently selected, in order to make sure that the change of paragraph styles applies to the right paragraph. There might be ways to achieve this using AppleScript commands for Pages ’09 exclusively, but they were not obvious to me, and so I ended up resorting to the “System Events” approach, which mimics what the user actually does with his keyboard.

The “key code 36” step in the scripts above is the equivalent of pressing the Return key once in the foreground document in Pages ’09. So essentially what my script does is that it changes the style of the current paragraph, then presses Return to start a new paragraph, and then changes the style of the new paragraph.

But when I started using this method, I quickly found that there was a new problem. My scripts worked fine when executed using the mouse (by clicking on the “Run” button in AppleScript Editor, for example), but when I tried to trigger them with my keyboard shortcuts of choice (command-Return and shift-command-Return), I had all kinds of problems with recursiveness (i.e. with the script triggering itself during its execution).

After lots of head scratching and research, I was able to track down the source of the problem, which was that, if I didn’t release the Command modifier key in my command-Return shortcut quickly enough, Mac OS X started executing the AppleScript script while the Command key was still down, and so when it reached the “key code 36” step, it would execute it as if it read “key code 36 using command down” step.

Since of course that was precisely the equivalent of pressing command-Return another time, it would trigger a new execution of the script within the script itself, and so on until my finger had actually released the Command key!

Excessive speed is a nice problem to have, but in this case it meant that my script did not work properly when executed using my keyboard shortcut, and I had to find a solution.

Obviously, I could choose another keyboard shortcut that did not involve the Command modifier key, for example using the F13 key by itself. But that wasn’t really an option for me, because I really liked my keyboard shortcuts of choice (command-Return and shift-command-Return), and I also knew that, if I had to rely on function keys for all my scripts, I would soon run out of options for shortcuts.

I also thought that I could add a pause in the script before the “key code 36” step, in order to force Mac OS X to wait until my finger had released the Command key before executing that step. But that didn’t strike me as particularly elegant and was not consistent with my primary purpose, which was precisely to speed up my work by automating these repetitive steps.

My thought was that, surely, there had to be a way to tell System Events to ignore the modifier keys. I naively tried “key code 36 not using command down” and “key code 36 using command up,” but neither worked.

I then went to the Apple Discussions forum and, after a quick fruitless search, started my own discussion topic on this.

I soon received a couple of replies that indicated that this was impossible to achieve with AppleScript by itself, but could be achieved by installing a scripting addition called “Extra Suites.”

As you can see by looking at the scripts above, the pause is the solution that I ended up adopting. I didn’t really want to spend $10 just to get the extra capability, and didn’t want to add to the complexity of my scripting setup at this early point in my scripting adventures.

It looks like 0.5 seconds is indeed enough to cause Mac OS X to wait until I have released the Command key before executing the key code command, and it is not really long enough that it interferes with the flow of my typing.

As I said, it is not a particularly elegant solution and the purist in me does not really like it. But if I really listened to the purist in me, I would also spend many more hours trying to figure out a way to build my solution without resorting to System Events at all. That might still happen later on, but right now I am satisfied with this, and it works reasonably well.

I should also note that FastScript is not the only solution for assigning keyboard shortcuts to AppleScript scripts in Pages. Following a reader’s suggestion, I also looked at Keyboard Maestro and decided that I liked it enough to actually purchase it. It provides many other tools for automating tasks beyond assigning shortcuts to AppleScript scripts.

I am now also using it as a replacement for Mac OS X’s own “Keyboard Shortcuts” feature in System Preferences, which is too limited and not reliable enough for my tastes. And I plan to continue working on automating tasks in my workflow, with a combination of AppleScript and Keyboard Maestro.

(There are other similar third-party tools beyond Keyboard Maestro.)


Comments are closed.