Moving from Microsoft Office VBA to AppleScript:
MacTech's Guide to Making the Transition
Introduction
|
Table of Contents
Page Prev and Page Next buttons at bottom of the page.
|
April, 2007
Page 38
end if
end repeat
set screen updating to true
end tell
AppleScript does not have anything like VBA's .Next(wdRow) to go on to the next row no matter where you are in the loop or if you just deleted a row or not. Any sort of repeat loop, even one iterating through a previously-formed list such as
set allRows to every row of theTable
is still composed of objects that are references. And in this case, as in many cases the references are by index {row 1 of table 1, row 2 of table 1, row 3 of table 1, … etc.} So even if you
set allRows to every row of theTable
repeat with i from 1 to (count allRows)
set theRow to item i of allRows
hoping that it will merely go fetch the item i of your list as it was when you made the list, that is not the case. That item was a reference to row i of theTable, so now it goes and re-evaluates row i of the table, which has a different content than row i had previously (if you altered the table during the loop). The result is that it will skip an item. This is not the case in an application like Entourage where almost every application reference is to a "hard-coded" object with a unique ID in its database. That is a sort of "luxury" a database-based application can have.
(In Entourage, all references to objects, however you make them – by name, by index, whatever – all resolve to the "canonic" reference by ID. So a list of allMessages in a folder, for example, would consist of {message id 12345, message id 12346, message id 12347, message id 12348}. Even if you deleted the second item of that list, it does not disappear from the list, so getting item 3 of allMessages still gets you message id 12347, not id 12348.)
In Word, as in the Finder and most other applications, the index of the reference will be re-evaluated, and you'll end up with an item skipped and later an error when the final indices are found not to exist any longer. That's why you have to iterate backwards, since indices lower than items you have deleted are not affected.
There are some situations where you can work around Word's propensity to re-evaluate every reference: for example, application references such as active document. If you set a variable like theDoc to active document (meaning the document in the front, of course), then minimize (collapse) window 1 of the front document to the Dock, and then call theDoc again (perhaps to activate it again), your original reference is lost, since Word re-evaluates theDoc to the new document that is now in the front!
The way to get around this is to find something that uniquely identifies the current active document and refer to it by this identifier when setting your variable. The ideal unique identifier of any document is its name: there can only be one document with the same name:
tell application "Microsoft Word"
set theName to name of active document
set theDoc to document theName
end tell
Here the variable theDoc remains "hard-coded" to the document currently active. So even when you collapse its window to the Dock and another document becomes active window, the variable theDoc remains pointing to the same document that it was originally set to, and you can re-activate or do anything to it without it pointing to a different document.
But in the case of this list of allRows, where each individual list item is a reference to {row 1 of table 1, row 2 of table 1… etc.} – a list of rows by index – and gets re-evaluated when called, resulting in skipped items and an error. So you must iterate backwards. There is no unique identifying feature for any row, at least not when getting 'every row' as a list. (Although the Dictionary definition for table claims that you can get a row element "by name" that really means "by index" since rows do not have a name property). It‘s the index for each row that constantly gets re-evaluated as you delete rows, so iterating backwards is the only way to do it.
Because of the repeat with i from numRows to 1 by -1 format, where i is a counter that does not need to be explicitly incremented by you, there is also no need for an initializing statement equivalent to VBA's
Set oRow = oTable.Rows(1).Range
That all gets taken care of inside the repeat loop by
set rowText to text object of row 1 of theTable
Similarly there is no need for a check for textInRow being true, since there's nothing to do if it is: the i counter will increment by itself.
The screen updating and status bar features work just the same in AppleScript as in VBA, only with the adaptations above you will now see the status bar counting down to 1 (which is neat in itself since you'll know how far there is to go before it finishes).
Remove all empty paragraphs from a document
Here's an example that shouldn't require a repeat loop, therefore no backwards iterations either. That's because it uses Find/Replace to do the removing in one go. It comes from a macro by Dave Rado at <http://www.word.mvps.org/FAQs/MacrosVBA/DeleteEmptyParas.htm>, which has extra code to remove empty lines from within and around tables in the document. That code is quite similar to the example just above here, extended to all the tables in a document, and does require converting to backwards repeat loop iterations "by -1". You should not have any trouble adding those portions if you've been following the examples here so far.
< Previous Page Next Page>
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine