Menu
Sharing Toolkit 3.0.3
UserLand Software, Inc.
©
copyright 1992-1994, UserLand Software, Inc.
UserLand
Software is located at 555 Bryant #237, Palo Alto, CA 94301. 415-326-7791,
415-326-7793 (fax). UserLand, Frontier and Frontier Runtime are trademarks of
UserLand Software, Inc. Other product names may be trademarks or registered
trademarks of their owners.
Email:
userland.dts@applelink.apple.com. If youÕre an AppleLink user, check out the
UserLand Discussion Board under the Third Parties icon. CompuServe users enter
GO USERLAND at any ! prompt. On America On-Line, enter the keyword USERLAND.
Comments,
questions and suggestions are welcome!
Background
WhatÕs New?
If you are already familiar with menu sharing, skip
to ÒMenu Sharing Toolkit 3.0 Change NotesÓ which explains the improvements in
version 3.0 of the Menu Sharing Toolkit.
FrontierÕs
Editable Menu Bar
One of FrontierÕs many capabilities is that it gives
the script writer an easy way to edit the contents of its own menu bar. When
the user selects an item from the menu, Frontier runs the script thatÕs linked
into the menu item.
Menu sharing extends that capability so that script
writers can add commands to the menu bars of compatible applications. If you
follow the instructions in this file, your application will support menu
sharing and will work with all versions of Frontier.
Menus sharing adds a set of standard Macintosh menus
at the end of your applicationÕs menu bar. Script writers can add commands to
these menus using Frontier. Any changes to your menus are automatically visible
as soon as the script writer switches back to your application.
In order to be menu sharing-aware, an application
includes the Menu Sharing Toolkit and inserts calls to Toolkit routines in
several places: on initialization; in the main event loop; before processing a
menu selection and where the user presses cmd-period.
Menu sharing does not in any way alter the
performance of your menu commands, it just adds new commands to your menu bar.
Applications-as-Development-Platforms
Menu sharing allows script writers to view your
application as a customizable development platform. They can add commands that
automate your application just as if it had an integrated scripting language.
Even if your application already has an integrated scripter, it makes sense to
support the menu sharing protocol because scripts can easily launch other
applications and integrate their capabilities with your program and the
Macintosh operating system.
Script users, usually less technical people than
script writers, will see simpler commands in these menus. Examples include:
prepare for a meeting, send a message to everyone working on a specific
project; write a press release; or hire a new employee. There are as many
potential custom scripts as there are Macintosh users. They will only be aware
that these commands were Òput intoÓ your application by a friend they work
with, or their organizationÕs network manager. The details of this technology
are completely open to the script writer, but neatly hidden from the end-user.
By opening your product to menu sharing you offer
more technical users an easy way to simplify your product for less technical
users. This means your technology is more useful to a much larger number of
people, and can only contribute to making your product more competitive and
more profitable.
No Royalty or
License Fee
There is no royalty or license fee for use of the
Menu Sharing Toolkit. ItÕs provided in source form so that developers know
exactly what the Toolkit is doing on their behalf, and so it can easily be
adapted to different development environments.
You may include this code in any Macintosh
application, but you must maintain our copyright notice at the head of each
UserLand-supplied source file.
Menu Sharing is
Open
WeÕre publishing source code for the client side of
the menu sharing protocol, and plan to support menu sharing in all future
versions of UserLand Frontier and all other software products developed by or
published by UserLand Software.
We believe menu sharing should be an open
specification, with freely distributed source code. Everyone wins if itÕs
widely supported on both sides of the equation, by clients (the vast majority
of Òsharing-awareÓ programs) and by servers (Frontier, and competitive
products).
Icing on the Cake
It makes little or no sense to add menu sharing if
your application is only minimally Apple Event-aware. Scripts launched from
your menu bar must be able to call back into your program to open or close a
window, get a value, add a record, dial the phone -- basically to do the types
of things your program was designed to do.
Menu sharing, therefore, is like icing on a cake.
Once youÕve added a reasonably complete set of scriptable IAC messages, the
next step is to open up your menu bar to allow script writers to insert new
commands that are implemented in scripts.
Who supports menu
sharing?
Examples of applications that support menu sharing:
Quark XPress 3.2, Think C and Symantec C++ 6.0, Aladdin Systems StuffIt 3.0 and
SITcomm 1.0, Apple ComputerÕs PhotoFlash.
Thru the FinderMenu extension, shipped with Frontier,
the System 7 Finder supports menu sharing.
Many more menu sharing-compatible applications are in
development and will be released in the coming months.
If an application already supports Apple Events, it
should also support menu sharing. ItÕs a much smaller investment in time than
Apple Event support, and it delivers an important real benefit to script
writers and their users.
Cookbook
Introduction
In this section weÕll step through the source code
for ÒMenuSharer,Ó a very minimal Macintosh program, whose main reason for
existing is this tutorial. It pays to browse through the MenuSharer source code
before installing menu sharing in your program. MenuSharer is written using
Think C 6.0. To view the source code, open menusharer.¹ with Think C.
The Toolkit is implemented in menusharing.c. Its
header file is menusharing.h. To see how these routines are called, page
through main.c. YouÕll see calls to Toolkit routines in five places:
1. In
handlemenu -- we call SharedMenuHit to run scripts selected from a shared menu.
2. In
handlekeystroke -- we call SharedScriptRunning and CancelSharedScript if the
user had pressed cmd-period to cancel a running script.
3. In
handleevent -- we call CheckSharedMenus to reload the shared menus if they have
been changed by a script writer.
4. In
main -- we call InitSharedMenus to set up globals and install handlers for
messages that may be sent by the menu server.
Following is a step by step ÒcookbookÓ for adding
menu sharing to any Macintosh application.
Step #1 -- Initialize the Toolkit
After initializing the Macintosh using InitGraf,
InitFonts, etc., call the Toolkit InitSharedMenus routine:
if
(!InitSharedMenus (&errordialog, &eventfilter))
goto error;
InitSharedMenus initializes menu sharing globals, and
installs Apple Event handlers for the two messages it must be prepared to
receive from the server.
It returns false if the initialization failed for
some reason. Your application can continue, or as MenuSharer does, simply
display an alert and exit to the operating system.
InitSharedMenus takes a two parameters: the address
of a routine that reports an error in a modal dialog and the address of a
routine that handles events. MenuSharer provides a two simple routines:
static
pascal void errordialog (Str255 s) {
ParamText (s, "\p", "\p",
"\p");
Alert (263, nil);
} /*errordialog*/
static
pascal void eventfilter (EventRecord *ev) {
handleevent (ev);
} /*eventfilter*/
The error dialog routine is called when a script
terminates in an error.
While a shared script is running, Frontier calls back
to the event filter routine when an update, activate, OS or null event is
available. This allows you to keep your windows pretty, and most important,
makes it possible for the user to bring another application to the front while
a script is running.
Step #2 -- Add a
Call in Your Main Event Loop
In your main event loop, when you receive a null
event and are not in the background, insert a call to CheckSharedMenus. You can
keep track of whether or not you are in the background by setting a global flag
each time you receive a suspend/resume event. ItÕs important that you only call
CheckSharedMenus when youÕre active to ensure that the menubar can be updated
properly.
If your menus need updating, CheckSharedMenus
automatically disposes your out-of-date shared menus, and conducts a short
conversation with the menu sharing server to get your updated menus.
CheckSharedMenus takes one parameter -- the resource
id for the first shared menu. If CheckSharedMenus loads new menus, they are
assigned resource ids starting with this number. It must be less than 255 to
allow for hiearchic menus, and must be small enough so that no menu has an id
of greater than 255. If there are n menus, and the parameter to
CheckSharedMenus is x, then the menu ids will range from x to x + n - 1.
MenuSharer calls CheckSharedMenus in its handleevent
routine in main.c.
Step #3 -- Add a Call in Your Menu Handler
In your menu handling routine, insert a call to
SharedMenuHit:
if
(SharedMenuHit (idmenu, iditem)) /*it was a shared menu item*/
goto exit;
SharedMenuHit checks to see if the indicated menu is
a shared menu. If it returns false, the menu is not a shared menu, and you should process the command the way
you normally would.
If it is a shared menu, the toolkit sends a message
instructing the server to run the script. If the server is Frontier 1.0 or 2.0,
this call returns before the script runs. The shared menus are disabled. They
will be re-enabled when the script completes. Your program should immediately
return to its main event loop.
If the server supports the Òcomponent menu sharingÓ
protocol, as Frontier 3.0 does, SharedMenuHit doesnÕt return until the script
has completed execution. Your program doesnÕt have to do anything special here.
MenuSharer calls SharedMenuHit in its handlemenu
routine in main.c.
Step #4 -- Add code to Your Keystroke Handler
Now that weÕve given the user a way to launch a
script from your applicationÕs menu bar, we have to provide a way to terminate
or cancel a script. Following the standard Macintosh user interface
conventions, this should be accomplished by pressing cmd-period while a script
is running.
Use SharedScriptRunning to determine if a script is
currently running. If it returns true, and the user has pressed cmd-period,
call CancelSharedScript to terminate the script.
HereÕs the MenuSharer code, in main.c, that
implements script termination:
static
void handlekeystroke (EventRecord *ev) {
register char ch = (*ev).message & charCodeMask;
if (SharedScriptRunning ()) /*cmd-period terminates the
script*/
if (((*ev).modifiers & cmdKey) && (ch == '.')) {
CancelSharedScript (); /*cancel the shared menu script*/
return;
}
handlemenu (MenuKey (ch)); /*process the normal way*/
} /*handlekeystroke*/
[Note: This is example code. If youÕre producing an
international product, you may use conventions other than cmd-period for
cancelling operations, and you should integrate this functionality with your
Òcancel-operation-from-keyboardÓ code.]
Step #5 --
Include "menusharing.h"
Add a #include at the top of every file that calls
Menu Sharing Toolkit routines:
#include
<menusharing.h>
Reference
Information
Introduction
The Menu Sharing Toolkit is implemented in a single C
source file, menusharing.c. To use the Toolkit, include the menusharing.h
header file at the beginning of every source file that calls a Toolkit routine
or references a menu sharing global.
There are five menu sharing routines that your
program is likely to call. Those routines are documented here. You can browse
the source code to see how these routines are implemented. You may need or want
to build other routines out of menusharing.c routines if you want shared menus
to appear as popup menus, or if youÕre implementing shared menus in a Control
Panel, XCMD or Finder extension.
In this section we try to avoid referring to Frontier
by name, rather we talk about the
Òmenu sharing serverÓ or Òmenu serverÓ application. As we mentioned
earlier, the menu sharing protocol is an open protocol, so itÕs possible to
imagine other software playing the role of menu server.
Much of the information presented here is provided in
more detail in the Cookbook Section, above. This reference section is provided
primarily as a summary of the information presented in that section.
Menu Sharing Globals
All the globals needed by the Menu Sharing Toolkit
are stored in a single C structure named MSglobals. Toolkit routines are
provided to initialize and operate on the fields of this structure, but we
provide the details and document it so you can monitor the performance of the
Toolkit using a source-level debugger.
HereÕs what the declaration of MSglobals looks like:
typedef struct tyMSglobals {
OSType
serverid;
OSType
clientid;
hdlmenuarray
hsharedmenus;
Boolean
fldirtysharedmenus;
Boolean
flscriptcancelled;
Boolean
flscriptrunning;
Boolean
flinitialized;
long
idscript;
ComponentInstance
menuserver;
tyMSerrordialog
scripterrorcallback;
tyMSeventfilter
eventfiltercallback;
}
tyMSglobals;
extern tyMSglobals MSglobals;
MSglobals.serverid is the
4-character identifier of the menu sharing server. ItÕs initialized by
InitSharedMenus to 'LAND' -- the 4-character creator id of UserLand Frontier.
MSglobals.clientid is the
4-character identifier of your program. ItÕs set automatically by
InitSharedMenus using the Macintosh Process Manager (see IM-VI, Chapter 29).
This information is passed to menu server in an IAC message to determine the
set of shared menus being requested.
MSglobals.hsharedmenus is the
data structure that holds the shared menus. ItÕs set to nil by InitSharedMenus.
See the declaration of hdlmenuarray in menusharing.h for further information.
MSglobals.fldirtysharedmenus is a boolean. If true, the shared menus
are re-loaded the next time your application becomes the frontmost process.
ItÕs set true by InitSharedMenus to force an update the first time thru the
main event loop, and when the 'updm' IAC message is received from the menu
sharing server when the script writer makes an editing change to your shared
menu.
MSglobals.flscriptcancelled is a boolean. If true, the currently
running script has been terminated by a call to CancelSharedScript, probably in
response to the user pressing cmd-period.
MSglobals.flscriptrunning is a boolean. If true, a script is
running and the shared menus are disabled. ItÕs set true by SharedMenuHit and
cleared when the menu server sends a 'done' IAC message.
MSglobals.flinitialized is a boolean. If true, InitSharedMenus
worked, if false it failed. Some toolkit routines return immediately if this
boolean is false.
MSglobals.idscript is the
menu sharing serverÕs identifier for the currently running script. When the
Toolkit sends a message to the server of the script it identifies the script by
passing this value to the server.
MSglobals.menuserver is the
Component Manager identifier for the connection between your application and
the menu sharing server. If the toolkit is using an Apple Event-based protocol
to communicate with the server, this field will be 0.
MSglobals.scripterrorcallback is the
address of a routine that displays error messages. See ÒMenu Sharing Toolkit
3.0 Change Notes,Ó below, for details.
MSglobals.eventfiltercallback is the
address of a routine that handles events while scripts are running. See ÒMenu
Sharing Toolkit 3.0 Change Notes,Ó below, for details.
Menu Sharing Routines
Boolean InitSharedMenus (tyMSerrordialog,
tyMSeventfilter);
Initializes the Menu Sharing Toolkit.
It takes two parameters: the first is the address of a
routine that displays a Str255 in a dialog and waits for the user to click on
OK. tyMSerrordialog is defined as follows:
typedef pascal void (*tyMSerrordialog) (Str255);
The second parameter is the address of a routine that
handles incoming update, activate, OS and null events. The server calls this
routine while it is running a shared script. tyMSeventfilter is defined as:
typedef pascal void (*tyMSeventfilter) (EventRecord *);
InitSharedMenus establishes initial values for the fields
of MSglobals and installs two Apple Event message handlers -- one to catch the
Òmenu needs updateÓ message, and another to handle the Òscript has completedÓ
message.
Returns true if the program is running on a version of the
Macintosh OS that supports Apple Events and if it was able to install handlers
for the two Apple Events, false otherwise.
Boolean CheckSharedMenus (short);
Call CheckSharedMenus from your main event loop when you
receive a null event and are not suspended via suspend/resume events. If the
shared menus need updating, the Toolkit sends an Apple Event message to the
server asking for your shared menus.
The MenuHandles are assigned resource ids starting with the
id indicated by the single parameter. This number must be less than 255 to
allow for hierarchic menus, and must be small enough so that no menu has an id
of greater than 255.
Returns false if there was an error loading the shared
menus.
Boolean SharedMenuHit (short, short);
Call this routine when youÕve received a menu selection.
The first parameter is the id of the selected menu, the second parameter is the
id of the selected menu item.
SharedMenuHit returns false if the menu isnÕt a shared
menu. Your program should process the menu selection as it normally would.
If it is a shared menu, SharedMenuHit sends an Apple Event
message to the menu sharing server, requesting that the script linked into that
menu item be run. Shared menus are disabled while the script is running.
Boolean SharedScriptRunning (void);
Returns true if a shared script is currently running, false
otherwise.
Use this routine to determine if a cmd-period should be
applied to terminating the current script using CancelSharedScript.
Boolean CancelSharedScript (void);
Call CancelSharedScript when the user presses cmd-period or
otherwise indicates that he or she wants the currently running script to be
terminated.
Messages that the
server sends
The server sends two Apple Event messages as its part
of the menu sharing protocol. Both messages are handled automatically by Apple
Event handlers installed by InitSharedMenus. The class for both events is the
same as the 4-character creator identifier of your application.
1. When your menu has been edited and requires
updating, the menu server sends an 'updm' message. The handler for this message
sets an MSglobals flag true. When your program becomes the frontmost
application, CheckSharedMenus sends a series of Apple Event messages to the
server requesting the new menus.
2. When a shared script has completed, the server
sends a 'done' message. The handler for this message simply records the fact in
MSglobals and re-enables the shared menus.
Where your shared
menu lives
Suppose your applicationÕs creator id is 'WXYZ'. If
your application has a shared menu, itÕs stored in FrontierÕs object database
at system.menubars.WXYZ.
Since MenuSharerÕs id is 'MENS', its menus are
located at system.menubars.MENS.
If you implement menu sharing, you should also
develop a sample shared menu for distribution with your product as part of your
Frontier install file. See the ÒFrontier Install File CreatorÓ sub-folder of
the Extras folder for details on creating an install file for your application.
Menu Sharing
Toolkit 3.0.3 -- 7/27/94 dmb
Universal
Headers, PowerPC compatibility
The Menu Sharing Toolkit can now be built using AppleÕs
Universal Headers under Symantec C/C++ 7.0 or Metrowerks C/C++ 1.0 68K or PPC.
Native or Òfat binaryÓ applications can be generated in the Code Warrior
environment. ANSI-conformant function prototypes are used thoughout the code,
so strict error checking can be enforced for Menu Sharing Toolkit projects.
MenuSharingLib
Instead of compiling the MSTK source into your native
application, you can link to MenuSharingLib, a PowerPC shared library. This
keeps the menusharing code separate from your applicationÕs code, and allows it
to be revised independently. Note that you will need to have the library
installed in your userÕs systems for your application to launch, unless you do
a ÒweakÓ link and check for the ToolkitÕs presence at run time. (See you the
CodeWarrior documentation for details.)
Menu Sharing
Toolkit 3.0
Component menu
sharing
Frontier 3.0 implements a more efficient menu sharing
protocol using the System 7.1 Component Manager.
The Menu Sharing Toolkit has been updated to use the
component protocol if it is available. Otherwise, it is prepared to use the
Frontier 2.0-compatible protocol, or even the Frontier 1.0 protocol.
In order for component menu sharing to be the
protocol it uses, the Macintosh Component Manager must be present, and the
toolkit must be able to create an instance of a menu sharing server component
(type 'SHMN').
When the component connection is used, scripts that
call back to your program run much faster because they are running within the
same process as your program. You can see how dramatic the results are by
running some of the scripts in MenuSharerÕs shared menus.
Additionally, dialogs displayed by scripts now show
up without a layer switch. This makes the whole effect much better for script
writers. The illusion that itÕs all happening inside your program is more solid
when dialogs show up without a layer switch.
Another advantage of using the Component Manager is
that, with this release, it is now possible for another program or code
resource to play the role of menu sharing server. Support for Frontier is only
hard-coded for earlier menu sharing protocols.
Frontier 3.0
supports all earlier protocols
If you support Menu Sharing 2.0 in a shipping
application, Frontier 3.0 is
compatible with it.
The net effect of installing Menu Sharing 3.0 is a
substantial increase in performance for script writers and users on systems
that have the Macintosh Component Manager installed.
How to Upgrade
1. Replace
the old versions of menusharing.c and menusharing.h with the new versions in
this package.
2. Recompile
your program. The compiler will catch two changes in the menu sharing API. The
next two sections detail the changes.
Two parameters
for InitSharedMenus
InitSharedMenus now takes two parameters: the address
of a callback routine that displays an error dialog and one that handles
events.
Check out the MenuSharer program for an example:
static
pascal void errordialog (Str255 s) {
ParamText (s, "\p", "\p",
"\p");
Alert (263, nil);
} /*errordialog*/
static
pascal void eventfilter (EventRecord *ev) {
handleevent (ev);
} /*eventfilter*/
if
(!InitSharedMenus (&errordialog, &eventfilter))
goto error;
The error dialog routine is called when a script
terminates in an error.
While a shared script is running, Frontier calls back
to the event filter routine when an update, activate, OS or null event is
available. This allows the client to keep its windows pretty, and most
important, makes it possible for the user to bring another application to the
front while a script is running.
ThereÕs a new command to MenuSharerÕs ÒHairyÓ menu,
called Divide By Zero. When you choose it, MenuSharerÕs errordialog routine is
called. To test the event filter routine, set a breakpoint on the handleevent
call and choose any command from MenuSharerÕs shared menus.
See the ÒCookbookÓ and ÒReference InformationÓ
sections, above for more info.
MSglobals.scriptcompletedcallback
is gone
In earlier versions of the Menu Sharing Toolkit, this
undocumented callback routine played a role very similar to the error dialog
described above -- it was used to report errors in scripts.
You should delete all references to
MSglobals.scriptcompletedcallback in your program, if you have any.
SharedScriptCancelled
calls can be deleted
We recommend that you delete all calls to
SharedScriptCancelled.
These calls are not needed for versions of Frontier
after version 2.0, which was shipped over a year ago, and was a free upgrade
for the entire installed base.
WeÕve deleted the documentation for
CancelSharedScript. It is no longer mentioned in the Cookbook section of the
menu sharing documentation.
Does not require
IAC Tools
Previous versions of the Menu Sharing Toolkit used
the IAC Tools library to manage Apple Events. In version 3.0, we removed this
dependency. All the Apple Event code needed to support menu sharing is included
in the menusharing.c file.
New fields in
MSglobals
There are are four new fields in the MSglobals
structure: menuserver,
scripterrorcallback, eventfiltercallback, and flinitialized. Like other
fields of MSglobals, these should be viewed as read-only. They are provided
primarily to support debugging.
See ÒReference InformationÓ above for details on
these new fields.
We deleted the scriptcompletedcallback field of
MSglobals, since its function has been completely been replaced by
scripterrorcallback.
Builds in MPW
menusharing.c compiles without error or warning in
MPW C. Thanks to Leonard Rosenthol of Aladdin Systems for his help here.
Added #ifdefs
There are two #defines at the head of menusharing.,c
that allow you to turn off support for component menu sharing, or to turn off
support for Frontier 2.0 and 1.0.
We strongly recommend that you leave both #defines as
they are. But if thereÕs a need to turn one or the other off, and you know what
youÕre doing, now it is possible.
Menu Sharing
Toolkit 2.0
No impact on the
API
WeÕve made some major improvements in the efficiency
of the Toolkit in version 2.0. However, these changes have no impact on the
API, if you built on the 1.0 API you can simply replace the 1.0 library with
the 2.0 library and rebuild your application.
Further, if you installed Menu Sharing 1.0 in a
shipping application, Frontier 2.0 is
compatible with it. The net effect of installing Menu Sharing 2.0 is an
increase in performance for script writers and users.
Where to call
CheckSharedMenus
WeÕve changed our recommendation on where to call
CheckSharedMenus in your program. In SDK 1.0 we recommended calling it in your
main event loop, at the same time you adjust your cursor and enable/disable
menu items. In 2.0, we go more conservative, and ask that you only call
CheckSharedMenus when you are not suspended and you receive a null event
(WaitNextEvent returns false). See the MenuSharer program for an example.
ItÕs much faster
We use the new support for system event handlers in
the IAC Tools library. If the server app has installed a set of system event
handlers to implement the server side of the menu sharing protocol, we use them
instead of sending Apple Events thru the operating system. The net result is virtually
instantaneous loading and updating of shared menus. This performance increase
is completely transparent to your program.
Compatibility
Frontier Runtime 1.0 supports the faster form of menu
sharing. Frontier 1.0 does not. A version of Frontier supporting the faster
protocol is in development. This Toolkit is fully compatible with present and
future versions of Frontier.
UserLand will continue to support the 1.0 form of
menu sharing in all future versions of Frontier and other products. If youÕve shipped
a product with the 1.0 Menu Sharing Toolkit, UserLand will remain compatible
with it.
Sends 'kill'
message when script is cancelled
We've enhanced the protocol so that someday it may
not be necessary to call SharedScriptCancelled in each of your Apple Event
handlers. Instead of just setting a flag, CancelSharedScript now sends a 'kill'
message to the menu sharing server (Frontier) to kill the script that was
launched by calling RunSharedMenuItem, if the server has such a handler
installed. If not (Frontier 1.0 does not), it depends on the
SharedScriptCancelled method.
However, this protocol is not understood by Frontier
1.0, so we had to leave in the old method. The Menu Sharing Toolkit knows which
version of Frontier it's talking to. If it's 1.0, it simply sets the flag and
returns. If it's 2.0 or greater, it sends the 'kill' message. The net: you have
to leave in your CancelSharedScript calls in your AE handlers for now. Once we
have most of the 1.0 base upgraded to 2.0, it will be possible to remove those
calls.
In
RunSharedMenuItem
We check to see if the server is still running, if
not, we remove the shared menus and return false.