(No) Future of VBA in Office for Mac?

K

koengeter

I thought maybe here I could get an answer to a particular thing that I
read yesterday. According to a report on macobserver.com Virtual Basic
scripting support will be discontinued in the next version of Office
for Mac (http://www.macobserver.com/article/2006/08/07.17.shtml).

I always felt that VBA and Virtual Basic were amongst the important
features of Office:Mac in terms of cross-plattform compatibility. So,
with support for these discontinued, it seems to me that one of the
main arguments to use (and buy) future versions of Office:Mac will just
cease to exist.

I have no genuine technical understanding of these things. But doesn't
this decision actually mean that I can't use the VBA-based
Word-templates I receive from several publishers to get formatting of a
text in accordance with their guidelines? And to what extend will the
announced support of AppleScript and Automator be a substitute for the
loss of VBA-support?

What do you think?

Matthias
 
P

Paul Berkowitz

First go read two extensive discussions on this. Two MacBU developers have
posted very interesting comments on their blogs:

http://www.schwieb.com/blog/2006/08/08/saying-goodbye-to-visual-basic/

http://blogs.msdn.com/rick_schaut/archive/2006/08/09/693499.aspx

--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
P

Paul Berkowitz


True, but it doesn't really address the cross-platform issues.

--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
J

Jim Gordon

I thought maybe here I could get an answer to a particular thing that I
read yesterday. According to a report on macobserver.com Virtual Basic
scripting support will be discontinued in the next version of Office
for Mac (http://www.macobserver.com/article/2006/08/07.17.shtml).

I always felt that VBA and Virtual Basic were amongst the important
features of Office:Mac in terms of cross-plattform compatibility. So,
with support for these discontinued, it seems to me that one of the
main arguments to use (and buy) future versions of Office:Mac will just
cease to exist.

I have no genuine technical understanding of these things. But doesn't
this decision actually mean that I can't use the VBA-based
Word-templates I receive from several publishers to get formatting of a
text in accordance with their guidelines? And to what extend will the
announced support of AppleScript and Automator be a substitute for the
loss of VBA-support?

What do you think?

Matthias

I think you will be happy to keep using Office 2004. The only other
possible cross-platform alternative is OpenOffice, which is not
compatible in significant ways making it not viable.

-Jim Gordon
Mac MVP
 
P

Phillip Jones

I for one have never ever used VBA or Macros in Word, and will look
forward to the day when Mac Office is based entirely on XML that can be
opened by anything that can use XML.

Jim said:
I think you will be happy to keep using Office 2004. The only other
possible cross-platform alternative is OpenOffice, which is not
compatible in significant ways making it not viable.

-Jim Gordon
Mac MVP

--
------------------------------------------------------------------------
Phillip M. Jones, CET |LIFE MEMBER: VPEA ETA-I, NESDA, ISCET, Sterling
616 Liberty Street |Who's Who. PHONE:276-632-5045, FAX:276-632-0868
Martinsville Va 24112 |[email protected], ICQ11269732, AIM pjonescet
------------------------------------------------------------------------

If it's "fixed", don't "break it"!

mailto:p[email protected]

<http://www.kimbanet.com/~pjones/default.htm>
<http://www.kimbanet.com/~pjones/90th_Birthday/index.htm>
<http://www.kimbanet.com/~pjones/Fulcher/default.html>
<http://www.kimbanet.com/~pjones/Harris/default.htm>
<http://www.kimbanet.com/~pjones/Jones/default.htm>

<http://www.vpea.org>
 
J

Jim Gordon

Hi Phillip,

There are some specific things for various kinds of XML formats. The new
Microsoft Office formats will be able to be opened by any application
that understands the word processing XML standard, but not every single
application that knows XML will necessarily open them as word processing
documents. You will be able to read any document's HTML/XML/CSS
information in any text editor or word processor.

There is a lot of information available about the new file formats.
Here's a walk-through:
http://msdn2.microsoft.com/en-us/library/ms771890.aspx

-Jim Gordon
Mac MVP
 
J

John McGhie [MVP - Word and Word Macintosh]

Hi Phillip:

I, for another, use VBA extensively and in complex ways. Most of what I
write is intended to be cross-platform.

Like you Phillip, I am looking forward to the XML file formats, which mean
that we will have a lot more control over what goes on, and can reach into
Office documents from anywhere.

A very great deal of what I use VBA for, can be done with XML and XSLT
transforms. This is a very important part of this debate, and it's great
that you have bought it to the fore.

The "old" automation solutions will not be cross-platform. But most of the
"new" ones will be. Anything that's developed in XML only for the PC should
"just work" in the XML version of Mac Office.

A lot of the "workflow"-style solutions currently constructed in VBA will be
easily able to translate into AppleScript. Such solutions are often not
cross-platform in any case.

Hopefully, that leaves us with a small fraction of a small fraction of jobs
you can't do in Mac Office.

The complete loss of cross-platform VBA automation is a huge hit. Let's not
minimise that: this is going to hurt. But we're still a way from being able
to define just who will get hurt and how much.

Cheers


I for one have never ever used VBA or Macros in Word, and will look
forward to the day when Mac Office is based entirely on XML that can be
opened by anything that can use XML.

--

Please reply to the newsgroup to maintain the thread. Please do not email
me unless I ask you to.

John McGhie <[email protected]>
Microsoft MVP, Word and Word for Macintosh. Consultant Technical Writer
Sydney, Australia +61 (0) 4 1209 1410
 
D

David-Artur Daix

Last year, I've programmed an application using Visual Basic for Word 2004 to
help Hellenists convert their documents that make use of older and obsolete
encodings to Unicode. It's called GreekTranscoder
(http://www.greektranscoder.org) and works quite well, on Macs as well as PCs.

I've just learned that VB will not be part of the next version of Office for
Macs. Which is quite distressing. It means I'll have to rewrite at least
portions of the code using AppleScript and that the resulting solution will
not be cross-platform anymore (it will probably become an AppleScript Studio
project from now on).

I've looked at the Word 2004 AppleScript Reference made available on
Mactopia and tried to understand how I could translate my VB code into AS.
Unfortunately, I find the AS syntax (as used by Word at least) extremely
confusing, since commands, arguments, parameters, keywords etc. often seem to
run into each other. Moreover, the documentation offers snippets of code that
are not formatted in any way to help differentiate between commands and
arguments. For instance, one can read on p.25 that example:

execute find find object of myRange find text "blue" with match forward

Without any way -- short of pasting this line into ScriptEditor and
compiling it -- to understand what's what, reading the documentation becomes
a difficult task.

Here is a VB function I've written, which is quite simple (especially in the
summed-up form I offer below), but exercises many options of a find & replace
operation in Word. I hope some of you can help me better understand how to
translate the VB "structures" into AppleScript ones.

Public Function CharConverter(findChar As String, replaceChar As String, _
findFont, replaceFont) As Boolean

For Each StoryRange In ActiveDocument.StoryRanges

With StoryRange.Find
.Font.Name = findFont
.Text = findChar
With .Replacement
.Font.Name = replaceFont
Selection.NoProofing = True
With Options
.AutoFormatAsYouTypeReplaceQuotes = False
End With
.Text = replaceChar
End With
.Execute Format:=True, Replace:=wdReplaceAll
' Free up some memory.
ActiveDocument.UndoClear
End With

Next StoryRange

' Clean up.
StoryRange.Find.ClearFormatting
StoryRange.Find.Replacement.ClearFormatting
Set StoryRange = Nothing

End Function

I know how to create and call AppleScript subroutines, so that's not a
problem.

I don't know if one needs to go through each 'story range' to make sure the
operation is performed on the whole document, or if the following command
will suffice when using AS:

'set StoryRange to text object of active document'

I think that may only affect the main story. But unfortunately there does
not seem to be a 'list' (a collection) of the available story ranges one
could go through with a 'repeat with...' loop and a 'get story ranges'
command ('get story range' exists but does not work like that). Does one have
to 'try' to 'get story range' for each story type in turn? There must be a
better way to do this, but I haven't been able to find it in the Word AS
Reference.

Also how would one translate in an elegant manner the 'With... End With'
block? As well as the other nested blocks within?

What I've come up with looks like this (though I haven't tried yet to see if
the code works):

set StoryRangeFind to find object of text object of active document
tell StoryRangeFind
clear formatting
set content to findChar
set font object to findFont
clear formatting replacement
set content of replacement to replaceChar
set font object of replacement to replacefont
set language id of replacement to language no proofing
execute find wrap find find continue find format true _
replace replace all with match forward
end tell

However, the Autoformat options completely elude me when dealing with
AppleScript.

And the 'wrap find' parameter is probably not necessary in the 'execute
find' command, nor the 'with match' one, since I'm doing a 'replace replace
all', but I'm not sure.

ActiveDocument.UndoClear translates to 'undo clear active document' I think.

But all in all I'm not very happy with my attempts at translating the VB
code into AS. So I hope some of you can help me find elegant and efficient
solutions and better understand the AppleScript syntax Word expects..

Thanks in advance.

Best regards,
--
David-Artur Daix
Centre d'Études Anciennes
Département des Sciences de l'Antiquité
École Normale Supérieure
45 rue d'Ulm, 75230 Paris Cedex 05
<http://www.greektranscoder.org>
 
D

David-Artur Daix

Our resident AppleScript expert, Paul Berkowitz, will no doubt be able to
answer your questions when he gets here. Keep checking back.

I will.

I have put together a preliminary version of the script which looks like
this (it's meant to be called from inside my REALbasic prototype):

on run {findChar, replaceChar, findFont, replaceFont}
using terms from application "Microsoft Word"
tell application "Microsoft Word"

set StoryRange to text object of active document

set StoryRangeFind to find object of StoryRange

set auto format as you type replace quotes of settings to false
set auto format as you type replace symbols of settings to false
set auto format as you type replace ordinals of settings to false
set auto format replace quotes of settings to false

tell StoryRangeFind
clear formatting
set content to findChar
set name of font object to findFont
set match case to true
set match whole word to false
set match wildcards to false
set match sounds like to false
set match all word forms to false
clear formatting replacement of StoryRangeFind
set content of replacement of StoryRangeFind to replaceChar
set name of font object of replacement of StoryRangeFind to replaceFont
set language ID of replacement of StoryRangeFind to language no proofing
execute find replace replace all with find format
end tell

undo clear active document -- Free up some memory.

tell StoryRangeFind -- Clean up
clear formatting
clear formatting replacement of StoryRangeFind
end tell


end tell
end using terms from
end run

It does not take into account al the options I need, but I can add them
later now that I know where to look (mostly).

The good news is that by relying on that script instead of calling on
REALbasic's "Office Automation" features, the RB prototype I've built is now
able to perform its task about as fast as the VB version of the program.
Which will save me time, as I won't have to redo the whole application in
AppleScript Studio. I just need to make sure that the script I call upon
performs all the tasks the original VB function did.

What the script doesn't do yet is automatically go through each story range
in the active document: it only deals with the main story. Is there an easy
way using AppleScript to do that (comparable to the VB 'For each StoryRange
in ActiveDocument.StoryRanges' command)? I have considered trying to build a
list (using 'try... end try' statements) of the existing story ranges myself
and then 'repeat with' using the values in that list. But I'm hoping that a
simpler solution exists.

Also, can the 'set... settings' commands be rewritten like this to create a
more readable block?

tell settings
set auto format as you type replace quotes to false
set auto format as you type replace symbols to false
set auto format as you type replace ordinals to false
set auto format replace quotes to false
end tell

Thanks in advance.

Best regards,
--
David-Artur Daix
Centre d'Études Anciennes
Département des Sciences de l'Antiquité
École Normale Supérieure
45 rue d'Ulm, 75230 Paris Cedex 05
<http://www.greektranscoder.org>
 
P

Paul Berkowitz

Hello, David-Artur

I'm glad you found your way here. You have also now worked out for yourself
that a 'tell' block directed at any object is the equivalent of VBA's 'With'
block, so you've solved over half of your original issues. Similarly, a
'repeat' block can usually be devised equivalent to VBA's 'For Each' loops.
But you have asked about a special case (see below).

First of all, there's no need for a 'using terms' block. That's just for
scripts that are compiled with the (appropriate version of) Microsoft Word
but which may need to run on some other computer that has an unknown version
of Word which might have a different name. You'd then figure out the name of
the application on the new computer (from its creator code, usually) and set
a variable to it, and direct your code at that variable within a 'using
terms' block. There will never be any need for that. All future versions of
Word are guaranteed to be called "Microsoft Word", and, in any case, you've
gone ahead and written 'tell application "Microsoft Word"' anyway! So you
really don't need 'using terms from'. (It's also not going to help you avoid
errors if tried on Word X, if that's what you were hoping.) Did you have
some other reason for 'using terms from'?

What are the circumstances that you'd need an explicit 'on run' handler with
parameters? Will you be calling this script from outside (RealBasic?) in a
way that requires this? More usual would be to not have an explicit 'on run'
handler at all, but to have a regular handler which you can call from either
outside (having loaded the script) or inside, as needed:

on DoThis(findChar, replaceChar, findFont, replaceFont)
--your script here
end

That would let you save the script as a compiled script library, rather than
as an application, and it would run faster, without needing to launch an
application shell. But you may have a good reason - I don't know what RB
requires.

!

OK, yes, you can 'tell settings' instead of using all those repeated 'of'
references. Did you not try it? Does it not work for you? Sometimes it's
helpful to throw in the keyword 'its' in a tell block, and absolutely
necessary to do so if the thing you're referring to might be a class as well
as a property. But it's not necessary here. I just tried your 'tell
settings' block here, and it works perfectly just as you wrote it. (Set the
various AutoFormat settings ON manually in Tools menu/AutoCorrect, then run
the script and check back, and you'll see they've all been unchecked.)



I've never used stories other than the main story, so this is new territory
for me, and I may not be able to test adequately. I'll try. Whatever is
possible in VBA ought to be possible in AppleScript, unless there's a bug -
and if there is, it would be great to find and identify it. Normally, what I
would do would be to get each story, and in a repeat loop call the commands
you specified for only the main story. Sometimes that's more convenient
calling a separate handler (subroutine) to do so - sometimes it's not so
helpful. Either way works.

In this case, it _is_ necessary, since the separate stories are not
available as 'every story of active document'. 'story' is not a class,
stories are not elements (child objects) of document. They seem to live in a
weird counter-culture of their own. ;-) There is a an enumeration 'story
type' used as a parameter to the 'get story range' command. For each story
type you specify, you get a different text range as a result, and you can
then run your 'tell StoryRangeFind' procedure on each range. Because you
have to specify each story type by name, you cannot run a repeat loop on a
list (such as 'every story of theDocument', if story _were_ an element).
Instead having to re-write the whole business 10 times over, call it as a
separate subroutine 10 times:


tell application "Microsoft Word"

tell settings -- only need this once
set auto format as you type replace quotes to false
set auto format as you type replace symbols to false
set auto format as you type replace ordinals to false
set auto format replace quotes to false
end tell

set storyRange to get story range active document story type main text
story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type comments
story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type endnotes
story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type primary
footer story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type primary
header story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type even pages
footer story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type even pages
header story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type first page
footer story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type first page
header story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

set storyRange to get story range active document story type footnotes
story
my ProcessStoryRange(storyRange, findChar, replaceChar, findFont,
replaceFont)

end tell


on ProcessStoryRange(storyRange)

tell application "Microsoft Word"
set StoryRangeFind to find object of storyRange

tell StoryRangeFind
clear formatting
set content to findChar
set name of font object to findFont
set match case to true
set match whole word to false
set match wildcards to false
set match sounds like to false
set match all word forms to false
clear formatting replacement of StoryRangeFind
set content of replacement of StoryRangeFind to replaceChar
set name of font object of replacement of StoryRangeFind to
replaceFont
set language ID of replacement of StoryRangeFind to language no
proofing
execute find replace replace all with find format
end tell

undo clear active document -- Free up some memory.

tell StoryRangeFind -- Clean up
clear formatting
clear formatting replacement of StoryRangeFind
end tell

end tell

end ProcessStoryRange



It is a nuisance that 'story' is merely an enumerated parameter to a command
'get story range;' rather than part of the object model as StoryRange is in
VBA. My guess would be that because there is no such thing as a Collection
Object in AppleScript, there's nothing like VBA's StoryRanges collection
Object for each document. They would have had to create separate 'main text
story', 'comments story' etc etc classes, even there's only one per
document. Or else a 'story' class with 10 enumerated types. Since it's
actually just a text range when all is said and done, it made more sense to
do it as a command with an enumerated parameter.

Now, it turns out that when there are no comments, say, 'get story range
active document story type comments story' errors rather than returning
'missing value' or "". I'll report that as a bug. The workaround is to put
each command in a try block. You can test each one individually to see for
yourself, e.g.:

try
set storyRange to get story range active document story type
comments story
on error
display dialog "No comments story"
end try


Anyway, that's how it is: if you have a primary header, then 'primary header
story' type will have a result, otherwise not. So you need to do something
like this around each handler call:


set storyCheck to false
try
set storyRange to get story range active document story type main
text story
set storyCheck to true
end try
if storyCheck then my ProcessStoryRange(storyRange, findChar,
replaceChar, findFont, replaceFont)


You need to repeat that storyCheck 10 times in the script above.

One thing I have not checked out yet is that in a document with several
sections, each section has its own headers and footers of all the different
flavors - perhaps its own footnotes and endnotes too. Yet 'get story range'
takes only document - not section - as its direct object. I wonder if it
only gets the first section's results that way, or somehow gets the whole
document's. If you test this thoroughly, please let us know. I may check
this too. If you can't do it this way for multi-section documents, you'd
need to get separate headers and footers for each section : 'get header' and
'get footer' in the Text Suite do operate on 'section' and return a 'header
footer' object, then get the 'text object' of each header footer as the
ranges on which to process your Find/Replace routine.

I have not tested thoroughly because I don't have examples for findChar,
replaceChar, findFont, replaceFont as yet. Please let us know how it all
works.


--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
P

Paul Berkowitz

I have not tested thoroughly because I don't have examples for findChar,
replaceChar, findFont, replaceFont as yet.

OK - after all that, I've found out that nothing works!

There's a serious bug - any text range whatsoever returned from 'get story
range' command has no properties. Even if 'main text story'. If you do"

set storyRange to get story range active document story type main text story
get properties of storyRange
--> {class:text range, content:missing value, formatted text:missing value,
start of content:missing value, end of content:missing value, font
object:missing value, story type:missing value, footnote options:missing
value, endnote options:missing value, show Word comments by:missing value,
border options:missing value, shading:missing value, text retrieval
mode:missing value, paragraph format:missing value, list format:missing
value, show hidden bookmarks:missing value, bold:missing value,
italic:missing value, underline:missing value, emphasis mark:missing value,
disable character space grid:missing value, style:missing value, story
length:missing value, language ID:missing value, subdocuments
expanded:missing value, grammar checked:missing value, spelling
checked:missing value, highlight color index:missing value, column
options:missing value, row options:missing value, is end of row mark:missing
value, bookmark id:missing value, previous bookmark id:missing value, find
object:missing value, page setup:missing value, case:missing value,
orientation:missing value, next story range:missing value, language ID east
asian:missing value, supplemental language ID:missing value, fit text
width:missing value, no proofing:missing value}

The one and only property that is correct is its class, that's it. Every
other property is 'missing value', even 'content'.

So your script isn't going to work this way at all.

I will report the bug. Hopefully, they know about it already and have fixed
it. I will also ask about what to do (when it's fixed) for multi-section
documents with separate headers and footers for each section.

The error you get when a particular story doesn't exist is not, it sees, a
bug. It is documented on p. 272 of the Word AppelScript Reference:

"If you attempt to return a story that isn't available in the specified
document, an error occurs. The
following example determines whether or not a footnote story is available in
the active document.

try
set myRange to get story range active document story type footnotes
story
on error
display dialog "The footnotes story is not available."
end try
"

Same as my example. It's a shame, though, that no one tested out the next
bit:

"
Example

This example adds text to the primary header story and then displays the
text.

set content of text object of (get header section 1 of active document index
¬
header footer primary) to "Header text"
display dialog content of (get story range active document story type
primary ¬
header story) as string
"

--> "missing value"

is what you actually get!

(BTW, if you don't know the Reference, get it, and similar for Excel and
PowerPoint at the MacTopia website: http://www.microsoft.com/mac/ and go to
Resources/Developer/AppleScript . They are invaluable, even if errors like
the above appear.


So, until they fix the bug (next version, when you'll have to do without
VBA) you'd have to rewrite the script, forgetting a;together about stories,
and just getting the content of the text object of the document and of every
header, footer, footnote, etc. This will be very laborious. Maybe better
wait until the next version...

--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
P

Paul Berkowitz

I will report the bug. Hopefully, they know about it already and have fixed
it. I will also ask about what to do (when it's fixed) for multi-section
documents with separate headers and footers for each section.

Checking the VBA Help with some help from a Word MVP, I can see now that
'next story range' property of text range will get the header and footer
ranges of sections after the first, once the main bug is fixed. (At the
moment, that property too is always 'missing value' - bug.) When fixed,
you'd add a line:

set nextStoryRange to next story range of storyRange
if nextStoryRange is not missing value then my
ProcessStoryRange(nextStoryRange, findChar, replaceChar, findFont,
replaceFont)


(or if it errors instead of producing 'missing value' put it in a try
block).

But it's all moot at the moment... I'll try to make sure they get this bit
right when they fix the bug.


--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
D

David-Artur Daix

Hello Paul,

And many thanks for you help!

You have also now worked out for yourself that a 'tell' block directed at any
object is the equivalent of VBA's 'With' block, so you've solved over half of
your original issues.

There was a telling (pun intended ;-) example in the Word AS documentation,
fortunately.
Similarly, a 'repeat' block can usually be devised
equivalent to VBA's 'For Each' loops.

Yes: I hope that when the next version of Office is released without VBA
support, it will come with good documentation and offer many examples of VBA
code statements translated into AS to help people make the transition.
Did you have some other reason for 'using terms from'?

No. It was just part of an AppleScript Studio example I ran into and thought
it might be a good idea to include that statement. I didn't actually check
whether it was needed or not and simply assumed it was. Which I shouldn't
have. Thanks for clarification.
What are the circumstances that you'd need an explicit 'on run' handler with
parameters? Will you be calling this script from outside (RealBasic?) <snip> I
don't know what RB requires.

Yes, that's the way RB wants its compiled script written so that they can be
called from within the RB code just as though they were regular RB functions
using the name of the compiled scripts' _files_ one includes in the project
(in other word what would be the name of the subroutine becomes the name of
the script file, while the code starts with 'on run').

It's annoying, since as far as I can tell you must have one compiled script
per subroutine, even for something as simple as 'save as active document
file name docName'.

I suspect that with VBA gone from Office, all the REALbasic Office
Automation features will stop working as well, since they're just a way to
call upon VBA inside the Office Suite using OLE. So that every command,
object and constant I now use that rely on Word to do the work, even those
that are not unbearably slow (OLE is not as efficient as AS on the Mac, to
say the least), but just simple save or screen update operations, will have
to be rewritten as small snippets of "on run" AS code inside compiled
scripts.

I'll have a better idea of what I need to do when the next version of Office
is available. Right now I'm just trying to get familiar with AppleScript and
what's need to translate VBA code into AS (I generally find the Visual Basic
syntax much easier to use and understand, unfortunately). And whether or not
RB can be used to create the interface and main logic. Right now it appears
so, which would let me use AS only for the work done inside Word itself (the
find & replace "engine", a few saves and screen updates, checking Word's
version, that kind of things: all in all maybe half a dozen rather short
subroutines) and reuse the RB prototype I've created last year for the rest:
a great time saver.
In this case, it _is_ necessary, since the separate stories are not
available as 'every story of active document'. 'story' is not a class,
stories are not elements (child objects) of document. They seem to live in a
weird counter-culture of their own. ;-) There is a an enumeration 'story
type' used as a parameter to the 'get story range' command. For each story
type you specify, you get a different text range as a result, and you can
then run your 'tell StoryRangeFind' procedure on each range. Because you
have to specify each story type by name, you cannot run a repeat loop on a
list (such as 'every story of theDocument', if story _were_ an element).
Instead having to re-write the whole business 10 times over, call it as a
separate subroutine 10 times:

That's the conclusion I had reached. So I have tried to create a list of the
available story ranges myself, so that I could then use 'repeat with' on it,
like this:

-- List declaration.
set StoryRangeList to {}
-- List reference declaration.
set StoryRangeListRef to a reference to StoryRangeList
try
set MainTextStoryRange to get story range active document story type
main text story
copy MainTextStoryRange to the end of StoryRangeListRef
set FootnotesStoryRange to get story range active document story type
footnotes story
copy FootnotesStoryRange to the end of StoryRangeListRef
set EndnotesStoryRange to get story range active document story type
endnotes story
copy EndnotesStoryRange to the end of StoryRangeListRef
set CommentsStoryRange to get story range active document story type
comments story
copy CommentsStoryRange to the end of StoryRangeListRef
[etc.]
end try
repeat with StoryRange in StoryRangeListRef
end repeat

But it doesn't work, because 'get story range' doesn't produce the expected
result.

[Update: I've now read your following posts and see that you have run into
the same problems as I have with that command and its documentation: thank
you for testing it and letting me know I'm not crazy! I just couldn't
understand why my code wouldn't work, even though I ended up simply copying
and pasting the examples given in the documentation to replace my own code.
And of course being new to AS I thought it was my fault.]

[Update: there is a typo in the Word AS Reference, p. 271: in the story type
list for the 'get story range' command, one can read "even pages header
footer story" instead of the expected "even pages footer story" ("header"
there is a copy/paste mistake I assume).]
It is a nuisance that 'story' is merely an enumerated parameter to a command
'get story range;' rather than part of the object model as StoryRange is in
VBA. My guess would be that because there is no such thing as a Collection
Object in AppleScript, there's nothing like VBA's StoryRanges collection

Indeed! It's quite annoying. Yet can't one create one's own "collections" by
using AS lists, as I intended to do?

In fact, this code works:

tell application "Microsoft Word"

-- List declaration.
set StoryRangeList to {}

-- List reference declaration.
set StoryRangeListRef to a reference to StoryRangeList

try

-- Get the main text story range using a working command
set MainTextStoryRange to text object of active document
copy MainTextStoryRange to the end of StoryRangeListRef

-- Repeat to add another entry and test the loop below
set AltMainTextStoryRange to text object of active document
copy AltMainTextStoryRange to the end of StoryRangeListRef

on error

display dialog "error"

end try

repeat with StoryRange in StoryRangeListRef

-- Normally here I'd put my find & replace code
display dialog content of StoryRange as string

end repeat

end tell

Which means, I hope, that my idea of creating my own story range list is
sound and that when 'get story range' is fixed I will then be able to
iterate through my own "collection" of story ranges, so to speak.

It is my understanding that by omitting the 'on error' part of the 'try'
statement I will be able to simply ignore all the cases where the story
range does not in fact contain any data. That way I will end up with a list
of all the existing story ranges without actually encountering any errors.
Is that correct?
One thing I have not checked out yet is that in a document with several
sections, each section has its own headers and footers of all the different
flavors - perhaps its own footnotes and endnotes too. Yet 'get story range'
takes only document - not section - as its direct object. I wonder if it
only gets the first section's results that way, or somehow gets the whole
document's.

By reading your following posts, I see that you've looked into this matter
yourself: thanks again for your help! And for providing me with examples of
how to use the 'next story range' property, which was not clear to me when
reading the Word AS Reference (more examples are needed in it I'd say).
But it's all moot at the moment... I'll try to make sure they get this bit
right when they fix the bug.

Thank you. In fact maybe you could suggest they implement in any way they
choose an elegant AS solution (indeed at first I expected something like
'repeat with every story range of active document' or even 'repeat with
StoryRange in (get story ranges)' to work: you can imagine my
disappointment!) to obtain the same result as the simple VB instruction:

'For Each StoryRange In ActiveDocument.StoryRanges'

The idea of having not only to go through each story range of the first
section, but also of all the next subsequent sections instead of having Word
do the work for me makes me shudder.

If only Word all by itself could create a complete list of the available
story ranges for the user, that would be really a good improvement.

That way it would become simple to script "replace all" operations that
actually apply to the whole document, not just the main section.

One more question. In my original VB code, in the find & replace operation,
I take into account the font color, so that normally it doesn't change
(using the "wdColorAutomatic" constant). But sometimes I want to change it
and color the replacement characters in red (to note characters that can't
be transcoded for instance, because they do not exist in the target
font/encoding).

When calling the script from REALbasic, I must pass the font object's 'color
index' value that I want to use as a string (VBA constants are no longer an
option and REALbasic can't handle the equivalent AS values directly). But
when I need to set that property in the script, a string won't do. In other
word, if 'fontColor' is the passed parameter, this snippet does not work:

set color index of font object of replacement of StoryRangeFind to fontColor

What I've come up with is this:

if fontColor = "red" then
set color index of font object of replacement of StoryRangeFind to red
else
set color index of font object of replacement of StoryRangeFind to auto
end if

Is there a better way to handle that situation?

I'm looking forward to hearing from you.

Best regards,

--
David-Artur Daix
Centre d'Études Anciennes
Département des Sciences de l'Antiquité
École Normale Supérieure
45 rue d'Ulm, 75230 Paris Cedex 05
<mailto:daix _AT_ ens _DOT_ fr>
<http://www.antiquite.ens.fr/index.html>
<http://www.greektranscoder.org>
 
P

Paul Berkowitz

Yes, that's the way RB wants its compiled script written so that they can be
called from within the RB code just as though they were regular RB functions
using the name of the compiled scripts' _files_ one includes in the project
(in other word what would be the name of the subroutine becomes the name of
the script file, while the code starts with 'on run').

It's annoying, since as far as I can tell you must have one compiled script
per subroutine, even for something as simple as 'save as active document
file name docName'.

That sounds like a reason in itself for trying out AppleScript Studio. You
can put everything in one script file if you like, although it is
recommended that you use an MVC (Model-View-Controller) structure for long
scripts and bugger projects. You can put your UI event-driven handlers in
the main script file and call out to "action" handlers in another script
file, for example. Or not. It's up0 to you. The full panoply of AppleScript
script objects and handlers (subroutines) are available to you as you like.
I suspect that with VBA gone from Office, all the REALbasic Office
Automation features will stop working as well, since they're just a way to
call upon VBA inside the Office Suite using OLE. So that every command,
object and constant I now use that rely on Word to do the work, even those
that are not unbearably slow (OLE is not as efficient as AS on the Mac, to
say the least), but just simple save or screen update operations, will have
to be rewritten as small snippets of "on run" AS code inside compiled
scripts.

We don't know at this point whether RealBasic's hooks into OLE will just
work "as is" on Intel Macs, or if RB will have to do a conversion similar to
what MS has declined to do for VBA. The reason why AppleScript just works
"as is" is because Apple has built the low-level changes into AppleScript
itself (and built the application conversion into Xcode's "Universal Binary"
checkbox). But Apple knew about the Intel changes for years and could plan
ahead.
I'll have a better idea of what I need to do when the next version of Office
is available. Right now I'm just trying to get familiar with AppleScript and
what's need to translate VBA code into AS (I generally find the Visual Basic
syntax much easier to use and understand, unfortunately). And whether or not
RB can be used to create the interface and main logic. Right now it appears
so, which would let me use AS only for the work done inside Word itself (the
find & replace "engine", a few saves and screen updates, checking Word's
version, that kind of things: all in all maybe half a dozen rather short
subroutines) and reuse the RB prototype I've created last year for the rest:
a great time saver.


That's the conclusion I had reached. So I have tried to create a list of the
available story ranges myself, so that I could then use 'repeat with' on it,
like this:

-- List declaration.
set StoryRangeList to {}
-- List reference declaration.
set StoryRangeListRef to a reference to StoryRangeList

Not necessary.

try
set MainTextStoryRange to get story range active document story type
main text story
copy MainTextStoryRange to the end of StoryRangeListRef
set FootnotesStoryRange to get story range active document story type
footnotes story
copy FootnotesStoryRange to the end of StoryRangeListRef
set EndnotesStoryRange to get story range active document story type
endnotes story
copy EndnotesStoryRange to the end of StoryRangeListRef
set CommentsStoryRange to get story range active document story type
comments story
copy CommentsStoryRange to the end of StoryRangeListRef
[etc.]
end try
repeat with StoryRange in StoryRangeListRef
end repeat

Yes. When you read the Neuburg book, you'll learn that it's a lot more
efficient to write:

set end of StoryRangeList to MainTextStoryRange

rather than

copy MainTextStoryRange to the end of StoryRangeListRef

but otherwise, yes, that will be fine. ('set end' doesn't have to keep
copying the entire list over and over internally.) Then you can operate on
the list in a repeat loop.

If and when you have a very large list (say 100 items or more, but
especially for thousands) it then becomes faster to use 'a reference to' -
actually there are better techniques than that (use 'my listName' in
top-level scripts and run handlers, make a script object within regular
handlers - these are advanced techniques). But for a list of 11 items, this
is really not necessary nor helpful.
But it doesn't work, because 'get story range' doesn't produce the expected
result.

[Update: I've now read your following posts and see that you have run into
the same problems as I have with that command and its documentation: thank
you for testing it and letting me know I'm not crazy! I just couldn't
understand why my code wouldn't work, even though I ended up simply copying
and pasting the examples given in the documentation to replace my own code.
And of course being new to AS I thought it was my fault.]

It's a good idea to test out just getting a single example as I did, to see
what results. Yes, the reason it didn't work for you is due to the bad bug
that 'get story range' produces useless dummy ranges.
[Update: there is a typo in the Word AS Reference, p. 271: in the story type
list for the 'get story range' command, one can read "even pages header
footer story" instead of the expected "even pages footer story" ("header"
there is a copy/paste mistake I assume).]

Right. Thanks for spotting that. I'll pass that on.
Indeed! It's quite annoying. Yet can't one create one's own "collections" by
using AS lists, as I intended to do?
Yes, see above. But don't call it a 'collection'. Lists in AppleScript are
like arrays in VB, not like those odd 'Collection Objects' they have there.
It is my understanding that by omitting the 'on error' part of the 'try'
statement I will be able to simply ignore all the cases where the story
range does not in fact contain any data. That way I will end up with a list
of all the existing story ranges without actually encountering any errors.
Is that correct?

Yes. The reason I suggested using that 'checkStory' variable (set to true or
false) is because that way you can trap _just_ the 'get story range' command
in a try/error block. You don't want to include the call to the
ProcessStoryRange() handler in the same try/error block, of course, because
that would prevent you from identifying other errors. using your own
structure of setting the end of StoryRangeList to 'get story range' means
you can put that command in the try/error block without needing to bother
with the checkStory variable, and will cut out extra lines.
By reading your following posts, I see that you've looked into this matter
yourself: thanks again for your help! And for providing me with examples of
how to use the 'next story range' property, which was not clear to me when
reading the Word AS Reference (more examples are needed in it I'd say).


Thank you. In fact maybe you could suggest they implement in any way they
choose an elegant AS solution (indeed at first I expected something like
'repeat with every story range of active document' or even 'repeat with
StoryRange in (get story ranges)' to work: you can imagine my
disappointment!) to obtain the same result as the simple VB instruction:

'For Each StoryRange In ActiveDocument.StoryRanges'

No, that cannot be done, for the reasons I explained earlier. 'story range'
is not a type of object - they're just ranges. Even in VBA, the StoryRange
is not a regular type of Object, nor is StoryRanges even a regular sort of
Collection Object - you cannot use the Add Method on it. Well, VBA can make
up ad hoc rules like that - they own the language. In AppleScript, an
application cannot legislate such that you can't 'make new story range at
active document'. It can't be done. Furthermore the restricted set of story
range types (main text, primary footer, etc. etc.) can only be specified as
an enumeration. and tat means it has to be a parameter to a command ('get
story range [story type]'. So it has to be the way it is.
The idea of having not only to go through each story range of the first
section, but also of all the next subsequent sections instead of having Word
do the work for me makes me shudder.

Once it's working properly doing

repeat while next story range of storyRange is not missing value
my ProcessStoryRange(storyRange)
end repeat


will work just fine.
If only Word all by itself could create a complete list of the available
story ranges for the user, that would be really a good improvement.

"all by itself"?
That way it would become simple to script "replace all" operations that
actually apply to the whole document, not just the main section.

One more question. In my original VB code, in the find & replace operation,
I take into account the font color, so that normally it doesn't change
(using the "wdColorAutomatic" constant). But sometimes I want to change it
and color the replacement characters in red (to note characters that can't
be transcoded for instance, because they do not exist in the target
font/encoding).

When calling the script from REALbasic, I must pass the font object's 'color
index' value that I want to use as a string (VBA constants are no longer an
option and REALbasic can't handle the equivalent AS values directly). But
when I need to set that property in the script, a string won't do. In other
word, if 'fontColor' is the passed parameter, this snippet does not work:

set color index of font object of replacement of StoryRangeFind to fontColor

What I've come up with is this:

if fontColor = "red" then
set color index of font object of replacement of StoryRangeFind to red
else
set color index of font object of replacement of StoryRangeFind to auto
end if

Is there a better way to handle that situation?

Once again, this is an enumeration (of '[background pattern] color index'
type). Naturally you can't the color index of a font to a string. Yes,
that's as good a way as any, but you'd need a subroutine that iterated
(if/else if) through all 18 color indices. Here's another way:

set fontColor to "red"
set color index of font object of replacement of StoryRangeFind to (run
script fontColor)

'run script' scripting addition (in Standard Additions, in Script
Editor/File/Open Dictionary - Standard Additions is built into every Mac and
always available) takes a string (or a compiled script's file path). So if
you give it a string inside an application's tell block and it can find the
string as a keyword in that app's dictionary it will compile it on the spot.
If you know that you will always be feeding it a valid enumeration, you can
use a variable like fontColor here to represent the string and do it in one
line instead of 18 if/else iterations. do note, however, that although the
18 if/else iterations take up more space in your script, it will run MUCH
faster than 'run script'. That won't matter if you're just calling it a few
times. But if it were running in a repeat loop on 1000 repeats. say. you'd
be much MUCH faster the "long" way round. 'run script', like every scripting
addition, carries a certain overhead.
Best,

--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
E

Elliott Roper

Paul Berkowitz said:
On 8/31/06 1:40 AM, in article C11C6D36.197DE%[email protected], "David-Artur
<snip>

Stop it! You're scarin' me!

Actually, I'm trying to follow along, rather slack-jawed. Thanks to you
both or providing more questions than answers in what looks like a
minefield.

;-)

Elliott

PS Paul. Was that an intentional typo, that adjective for project size?
 
P

Paul Berkowitz

PS Paul. Was that an intentional typo, that adjective for project size?

Yes. ; -) I just checked. Read "bigger".

--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
D

David-Artur Daix

That sounds like a reason in itself for trying out AppleScript Studio. You
can put everything in one script file if you like, although it is
recommended that you use an MVC (Model-View-Controller) structure for long
scripts and bugger projects. You can put your UI event-driven handlers in
the main script file and call out to "action" handlers in another script
file, for example. Or not. It's up0 to you. The full panoply of AppleScript
script objects and handlers (subroutines) are available to you as you like.

That's true, but that would mean rewriting _everything_ in AS or Obj-C.
Right now, I already have working code written in REAlbasic and the
FileMaker Pro database that I have created to produce the VBA code used in
the "transcoders" (huge arrays storing the characters to find and their
replacements) can be easily reconfigured to produce RB code instead. It
would be a much bigger effort to rewrite everything in AS or Obj-C. I may
have to if there are big performance issues when using RB, but if the only
drawback is having to create a few 'on run' scripts instead of one large
script, so be it. Right now I can limit the use of AS to just 5 or 6
subroutines, from tiny to small (my find & replace subroutine is the largest
of the bunch), when only Word itself can do the work.

BTW, about having to use individual files for each subroutine, that's just
par for the course when dealing with RB. In that environment _every_
method/function stands on its own. Each one of my VB transcoder module will
typically contain between ten and fifteen functions depending on the
different "translations" it has to deal with: when moved to RB, that module
becomes fifteen different methods and is all broken up.

The truth is, despite the difficulty of working with VBA on the Mac, I
actually really enjoyed programming using that language. REALbasic is quite
similar, but not quite as elegant IMO.

And I think I prefer both to AS.

But as always there is no accounting for taste. And in the end I'll use
whatever works.
We don't know at this point whether RealBasic's hooks into OLE will just
work "as is" on Intel Macs, or if RB will have to do a conversion similar to
what MS has declined to do for VBA. The reason why AppleScript just works
"as is" is because Apple has built the low-level changes into AppleScript
itself (and built the application conversion into Xcode's "Universal Binary"
checkbox). But Apple knew about the Intel changes for years and could plan
ahead.

I've posted messages on the REALbasic forums asking whether the Office
Automation features will still work in the next version of Office when VBA
is gone: I haven't received any answer. But I really doubt it.

In fact, with VBA gone, will OLE still be used in Office? Maybe AppleScript
will become the only model used in the next version for interapp
communication: wouldn't that make sense?
Yes. When you read the Neuburg book, you'll learn that it's a lot more
efficient to write:

set end of StoryRangeList to MainTextStoryRange

rather than

copy MainTextStoryRange to the end of StoryRangeListRef

but otherwise, yes, that will be fine. ('set end' doesn't have to keep
copying the entire list over and over internally.) Then you can operate on
the list in a repeat loop.

Thanks for the tip. I've ordered Neuburg's book and I'll read it carefully
as I prepare to make the move from VBA to AS.
Once it's working properly doing

repeat while next story range of storyRange is not missing value
my ProcessStoryRange(storyRange)
end repeat


will work just fine.

Great: I'll keep those lines in store for next year.
Once again, this is an enumeration (of '[background pattern] color index'
type). Naturally you can't the color index of a font to a string. Yes,
that's as good a way as any, but you'd need a subroutine that iterated
(if/else if) through all 18 color indices. Here's another way:

Actually I just need two cases: default is "auto" and then, once all the
replacements stored in the arrays are done, I call the CharConverter
function/method/script _once_ with the parameter "red" to color in red all
the characters that couldn't be transcoded at all (because they simply do
not exist in the target encoding). So a simple 'if... else' should do fine
and work fast.

Thanks again for your help.

Best wishes,

--
David-Artur Daix
Centre d'Études Anciennes
Département des Sciences de l'Antiquité
École Normale Supérieure
45 rue d'Ulm, 75230 Paris Cedex 05
<mailto:daix _AT_ ens _DOT_ fr>
<http://www.antiquite.ens.fr/index.html>
<http://www.greektranscoder.org>
 
P

Paul Berkowitz

The truth is, despite the difficulty of working with VBA on the Mac, I
actually really enjoyed programming using that language. REALbasic is quite
similar, but not quite as elegant IMO.

And I think I prefer both to AS.

But as always there is no accounting for taste. And in the end I'll use
whatever works.

As I mentioned to you also privately. there's one more alternative:

"has" (= Hamish Sanderson) has developed a Python framework called
"appscript" that lets you do application scripting - i.e. using the same
commands, classes, etc as AppleScript - using Python instead. Since Python
is a .dot language with resemblances to VBA but more logical - you might
prefer it to AppleScript. Perhaps you already know Python, which would make
it even easier. appscript has received very wide praise in the Python
community.

http://appscript.sourceforge.net

It has excellent documentation too.

In fact, with VBA gone, will OLE still be used in Office? Maybe AppleScript
will become the only model used in the next version for interapp
communication: wouldn't that make sense?

AppleScript hooks into OLE. Yes, OLE will continue to work, but MS has not
(so far) published an API for it publicly. However, it does appear that they
exposed the API to RealBasic back in 2000 or so, when they were getting
Office v. X ready, they gave some support to RB (notice the "RealBasic
Editor" menu item in Tools and the Lite version of RB that they shipped with
Office) in the hope that developers would switch to RB and RB would shoulder
the programmability issues. It didn't work out that way since cross-platform
developers preferred to continue using VBA and Mac-only developers prefer
AppleScript, which they provided for in 2004. But now that VBA will be going
away, it's possible that RB might be able to fill the gap. It's just that
nobody knows if their compiler is independent of the CPU or not. It probably
has to be since it can already work on both PPC Macs and Windows computers.
But some adjustment will undoubtedly have to made for Intel Macs, only
nobody knows yet if that's a small adjustment or a huge one, nor whether
it's being done or not.

Eventually (but no one knows how long) in some future version of Office Mac,
some version of .NET will be implemented for Office Mac, when there's a
Microsoft-supported CLR for the Mac (they're working on that now). When
Office Windows also switches to that, Mac and Windows programming will be
compatible again. (Yes, there's VB.NET already, but apparently there are
some flaws. Anyway, no one knows yet what the final setup will be.) All
these issues have been discussed here:

http://www.schwieb.com/blog/

(i.e. http://www.schwieb.com/blog/2006/08/08/saying-goodbye-to-visual-basic/
and
<http://www.schwieb.com/blog/2006/08/10/so-tell-us-what-you-want-what-you-re
ally-really-want/> )

and here: http://blogs.msdn.com/rick_schaut/archive/2006/08/09/693499.aspx



--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top