MacTech Network:   MacTech Forums  |  MacForge.net  |  Computer Memory  |  Register Domains  |  Cables  |  iPod Deals  |  Mac Deals  |  Mac Book Shelf


  MacTech Magazine

The journal of Macintosh technology

 
 

Magazine In Print
  About MacTech  
  Home Page  
  Subscribe  
  Archives DVD  
  Submit News  
  MacTech Forums  
  Get a copy of MacTech RISK FREE  
Google
Entire Web
mactech.com
Mac Community
More...
MacTech Central
  by Category  
  by Company  
  by Product  
MacTech News
  MacTech News  
  Previous News  
  MacTech RSS  
Article Archives
  Show Indices  
  by Volume  
  by Author  
  Source Code FTP  
Inside MacTech
  Writer's Kit  
  Editorial Staff  
  Editorial Calendar  
  Back Issues  
  Advertising  
Contact Us
  Customer Service  
  MacTech Store  
  Legal/Disclaimers  
  Webmaster Feedback  

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.

Would you like a hard copy
or PDF of this Guide?

You can get a hard copy sent to you
AND download a PDF now ($19.95)
, or

... just download a PDF ($9.95).

Either way, you get a complimentary
MacTech Magazine Subscription

courtesy of the
Microsoft Mac Business Unit


 

  Magazine Reg. Price:  $50.00 value  
  Guide Reg. Price:  $40.00 value  
  You Save:  over $80.00!  

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.



 


Click here to find out more about our best subscription bundle deal ever!
2 years of the magazine, and the all new MacTech DVD ... at 70% off!



Click on the cover to
see this month's issue!

TRIAL SUBSCRIPTION
Get a RISK-FREE subscription to the only technical Mac magazine!
 
Nokia Qt Beta
 


MacTech Magazine. www.mactech.com
Toll Free 877-MACTECH, Outside US/Canada: 805-494-9797

Register Low Cost (ok dirt cheap!) Domain Names in the MacTech Domain Store. As low as $1.99!
Save on brand compatible and name brank ink jet and laser supplies.
Save on long distance * Upgrade your Computer
Movies with No Late Fees!

See local info about Westlake Village
SJ * BRJ * BJ * OJ * NITS
Staff Site Links



All contents are Copyright 1984-2008 by Xplain Corporation. All rights reserved.

MacTech is a registered trademark of Xplain Corporation. Xplain, Video Depot, Movie Depot, Palm OS Depot, Explain It, MacDev, MacDev-1, THINK Reference, NetProfessional, NetProLive, JavaTech, WebTech, BeTech, LinuxTech, Apple Expo, MacTech Central and the MacTutorMan are trademarks or service marks of Xplain Corporation. Sprocket is a registered trademark of eSprocket Corporation. Other trademarks and copyrights appearing in this printing or software remain the property of their respective holders.