Re: Adobe's lousy AppleScript implementations
Re: Adobe's lousy AppleScript implementations
- Subject: Re: Adobe's lousy AppleScript implementations
- From: has <email@hidden>
- Date: Sat, 28 Sep 2002 23:28:36 +0100
David Crowe wrote:
>
Thanks for all your hints. One of the problems that I have is the use
>
of QuicKeys so that I can connect the scripts to a hot-key.
>
>
If I put the AppleScripts in files to make it modular, then it has to
>
open all the files which makes it slow. From within QuicKeys you can
>
call multiple AppleScripts but not pass data between them. So,
>
writing monolithic applescripts are about the only way to do it.
You can present a single script to QK, and be still modular in its internal
construction. This is not a problem.
>
That's not so bad, but with 12 similar scripts, every time I discover
>
a non-compliant application, I have to make 12 changes!
Then I think you'll prefer the approach I'm talking about.
Okay, first up is the heart of the beast. The applicationObjectsModule
script contains a collection of script objects, one for each of your
awkward apps. In addition, it contains a constructor function that'll
create a "default" object suitable for use with those apps that do play
nicely. Each script object contains no more than is required to abstract
away the application-specific stuff; e.g. you wouldn't include any jogging
code in this, because that is application-independent.
It's not really in a shippable form as it currently stands: apart from
anything else, it's assuming that the host system has all the
"non-standard" apps installed, otherwise it's probably going to complain
that it can't find said apps. And you'll probably want to streamline the
interface a bit. But it'll demonstrate the principles (and I can always
explain how to loosen its grip on your apps some other time).
======================================================================
-- applicationObjectsModule
-------
--PRIVATE
--objects for "non-standard" applications
(* Each object MUST present the following public interface:
getWindowBounds(windowID)
windowID : integer -- window identifier, e.g. 1
Result : list -- a list containing four integers in order:
[NO-BREAK]wLeft, wTop, wRight, wBottom
setWindowBounds(windowID, wLeft, wTop, wRight, wBottom)
windowID : integer -- window identifier, e.g. 1
wLeft, wTop, wRight, wBottom : integer -- window bounds
Result : none -- window's bounds are changed
*)
script _Acrobat_5_0
property appName : "Acrobat 5.0"
property parent : application "Acrobat 5.0"
on getWindowBounds(windowID)
set {wLeft, wTop, wRight, wBottom} to bounds of pdf window
[NO-BREAK]windowID
return {wLeft, wTop, wRight, wBottom}
end getWindowBounds
on setWindowBounds(windowID, wLeft, wTop, wRight, wBottom)
set bounds of pdf window windowID to {wLeft, wTop, wRight,
[NO-BREAK]wBottom}
return
end setWindowBounds
end script
--
script _FrameMaker_6_0
property appName : "FrameMaker 6.0"
property parent : application "FrameMaker 6.0"
on getWindowBounds(windowID)
set {wTop, wLeft, wBottom, wRight} to bounds of document
[NO-BREAK]windowID
return {wLeft, wTop, wRight, wBottom}
end getWindowBounds
on setWindowBounds(windowID, wLeft, wTop, wRight, wBottom)
set bounds of document windowID to {wTop, wLeft, wBottom,
[NO-BREAK]wRight}
return
end setWindowBounds
end script
--
property _knownApplicationList : {_Acrobat_5_0, _FrameMaker_6_0}
-------
--PUBLIC
on newDefaultApplicationObj(applicationObj)
script
property parent : applicationObj
on getWindowPosition(windowID)
set {wLeft, wTop, wRight, wBottom} to bounds of document
[NO-BREAK]windowID
return {wLeft, wTop, wRight, wBottom}
end getWindowPosition
on setWindowPosition(windowID, wLeft, wTop, wRight, wBottom)
set bounds of document windowID to {wLeft, wTop, wRight,
[NO-BREAK]wBottom}
return
end setWindowPosition
end script
end newDefaultApplicationObj
on getApplicationObj(appName)
repeat with eachApp in _knownApplicationList
if eachApp's appName is appName then return eachApp's contents
end repeat
return missing value -- (no special object by this name found)
end getApplicationObj
======================================================================
Okay, so the above module gets stuck in its own file and saved to disk. QK
will never see it itself, however; all it'll see is the scripts that
perform the various whizzy operations such as jogging windows all over the
place.
The script you actually hand to QK will contain the
applicationObjectsModule, loaded at compile-time into a suitable property.
======================================================================
prop applicationObjectsModule : load script (alias "path to
[NO-BREAK]applicationObjectsModule file here") -- [compile-time load]
--
property currentTargetApp : missing value
on setTargetApp(appName, appObj)
set ourAppObj to applicationObjectsModule's
[NO-BREAK]getApplicationObj(appName)
if ourAppObj is missing value then
set currentTargetApp to applicationObjectsModule's
[NO-BREAK]newDefaultApplicationObj(appObj)
else
set currentTargetApp to ourAppObj
end if
return
end setTargetApp
--
property JogAmount : 50
on jogWindowLeft()
set windowID to 1
tell currentTargetApp
set {wLeft, wTop, wRight, wBottom} to getWindowBounds(windowID)
if wLeft > JogAmount then setWindowBounds(windowID, (wLeft -
[NO-BREAK]JogAmount), wTop, (wRight - JogAmount), wBottom)
end tell
return
end jogWindowLeft
======================================================================
This script contains no application-specific code, so you'll never have to
make any modifications to it should another "non-standard" app come along.
You should also be able to see how easy it'll be to write other scripts for
jogging right, up and down as well, not to mention scripts for
tiling/stacking windows, etc, etc, etc.
The connections between your app-specific stuff and your app-independent
stuff are nice and loose and flexible [loose coupling], and the niggling
differences that exist between different apps' object models are all hidden
away [abstraction] so that you're presented with an identical interface no
matter what app object you're talking to [polymorphism]. (And if you can
successfully grok how to do this, you'll have acquired a an advanced
problem-solving technique that most folks wouldn't be aware of outside a
higher CompSci education.)
Now when you need to add support for a new "non-standard" application, you
just need to add an extra script object to applicationObjectsModule, then
recompile your client scripts so that they pick up the changes.
Anyway, I suggest you try getting the above up and working. There's a few
bits I've not done, obviously, such as actually making the setTargetApp()
that'll select an object for your jog handler to talk to. Once you're happy
with that, I can show you how to tidy up the early-draft design of
applicationObjectsModule to ensure it'll be portable even to machines that
don't have all the "problem" apps installed.
HTH
has
p.s. I cheated a bit in using parent properties, if anyone's being really
picky. By definition, I should be using a wrapper that hides the original
app object's own properties and commands and shows only my own, not
subclassing [extending] the app object [which leaves the original
properties/commands exposed, and might encourage someone to use them from
outside, even though the whole point of the exercise is not to]. If I post
a followup showing a cleaned-up, shippable version of
applicationObjectsModule I'll sort this out while I'm at it.
--
http://www.barple.pwp.blueyonder.co.uk -- The Little Page of AppleScripts
_______________________________________________
applescript-users mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/applescript-users
Do not post admin requests to the list. They will be ignored.