Help for Help
Volume Number: | | 5
|
Issue Number: | | 6
|
Column Tag: | | Basic School
|
Help for HELP Software
By Dave Kelly, MacTutor Editorial Board
In spite of the fact that Apple has referred to the Macintosh as the computer for the rest of us, there are some people that still have trouble with computers of any kind, even the Macintosh. I think that Macintosh users sometimes look for complexity that is not there. So there are many training courses, classes, etc. which may be taken to console those that may not feel comfortable with the application or system environment they are working with.
Many commercial application programs have developed help systems which allow the user to access on-line documentation of the application. The About menu item (in the Apple menu) is typically the place to go to find some kind of help information. Users look for and expect consistency as they divide their time across several applications. This consistent atmosphere is why Macintosh users tend to use more different software applications than other computer users. On other systems users select a group of 3-4 applications and learn them well, but dont usually pick up other applications because of the time it takes to learn the new programs layout and how to get it to work. With the Macintosh, the consistent placement of the menus in the standard order (Apple Menu, File Menu, Edit Menu, etc.) helps to reduce the learning curve of new applications.
Even with this consistency, users sometimes dont have the manual handy or cant quite remember everything that the application does. This is the purpose of the on-line documentation. Help Software has recognized the need for a consistent help system. They have devised a system which developers may use to create on-line documentation quickly and easy for the user to use. Zedcor uses this help system to support on-line help for the ZBasic compiler. By typing HELP or accessing the About ZBasic dialog, the HELP DA is accessed and the ZBasic Help file is opened. Applications may access the HELP DA directly and request that specific messages be displayed in the HELP DA window. These are referred to as context sensitive or extended alerts by the HELP system.
Im sure you can see the benefits of having an on-line documentation system as part of your application. For example, the user can type cmd-? or hit the help button (on the extended keyboard) and then click or select some object or control in the current application and the help system will provide a help message specific to the object selected. This is the type of system that MS Word and some other Microsoft products have implemented. But with HELP Softwares DA, you dont have to write the on-line help system, its already written for you.
It is the purpose of this column to introduce you to the HELP Software Desktop Help system and demonstrate a way to access context sensitive help messages from within your ZBasic application. Typically this involves more than just opening up the Help DA. Under Multifinder, DAs load via the DA Handler unless the option key is held down when the DA is selected. For context sensitive help or extended alert messages, the application program needs to pass the message number to the Help DA so that the desired message can be displayed.
The LS Pascal code is divided into two projects. The Help project calls the Help DA with the option key pressed. The ContextHelp project calls the Help DA with the option key pressed and receives a integer message number which is passed from the calling application. Both projects are similar except for the message being passed. This code originated from the C example that Help Software provides with the Desktop Help system. The major difference is that the C code is a function instead of a procedure. I changed to a procedure because ZBasic doesnt provide an easy way to call a function. The CALL statement is meant for calling machine language or procedures from Pascal or C. Since it was tricky to get the return (error) value from the function, I decided to ignore the error and change to a procedure. The error flag would only be used to tell the user that the help system was not found or the help data file could not be loaded. An easier method may have been to rewrite the code to be executed directly in ZBasic. Due to time constraints I have not been able to try that yet; Ill leave that to you.
Since DAs dont load into the application space when Multifinder is active, the option key press is simulated. To do this the variable theKeyMap is used to pointed to the location $178 where the key map offset to the option key is located. Modifier keys themselves do not generate keyboard events (see Inside Macintosh, I-246). The Help Software programmer followed the GetKey procedure with TMON and found the master KeyMap (theKeyMap). He decided to simulate an Option key press by putting there he same thing that pressing and holding the key would. The location of the option key at $178 is not documented anywhere, but works on all ROMS from the 128K Mac thru the Plus, SE, II, IIx, and SE/30. If Apple doesnt change the key mapping then there shouldnt be any problem. One thing to remember is that if you press the option key this way, be sure to unpress the option key by changing theKeyMap back. The statement:
LongPtr(theKeyMap)^ :=BitOr(LongPtr(theKeyMap)^, 4);
presses the option key and:
LongPtr(theKeyMap)^ := 0;
releases the option key.
The help file is created with the Desktop Help system with the Help Editor. This file should be created after the application has been completely written, though may be updated and modified with the Help Editor throughout the entire development process. The file type of the help file should be set (with ResEdit) to the name of the creator of your application so that the help file which belongs to the application. DeskTop Help costs about $395 and allows you to edit your own on-line documentation files for any or all applications you desire (even commercial ones). To distribute the Help DA you pay a yearly license of $150 after the first year. The Help Editor should never be distributed, but can be purchased by anyone by buying the complete Desktop Help package.
The see how the context sensitive help works in the sample ZBasic program, press the cmd-? key to change the cursor to the help mode. The program will then respond to menu selections and call a specific message corresponding to the message set up in the help file. The help key or the About menu item will also select the help DA (with the option key pressed). If Multifinder is never used, the Help DA may be loaded directly without calling the Pascal procedure by using the OPENDESKACC function.
The ZBasic program loads the Pascal code into memory with the GETRESOURCE function. Next the code is locked in memory (so it wont move on us) and the ZBasic CALL statement executes the code. We can execute the code resource directly because LightSpeed Pascal puts a branch to the actual code at the beginning of the code header. After executing the code, the code is unlocked and the handle is released to free up memory.
These routines may be used with Pascal programs and BASIC without a lot of time and effort. The whole idea is to save time and provide a consistent way for the user to access on-line documentation or help.
Desktop Help is available from Help Software, 10659A Maplewood Road, Cupertino, Calif. 95014, tel. 408-257-3815. NOT COPY PROTECTED!
unit getHelp;
{Procedure for Calling the Help desk accessory }
{for Context Sensitive Help and Extended Alert Messages}
{{Copyright ©1988 , Help Software , Inc . }
{ Modified by Dave Kelly for MacTutor, April 1989 }
{ Source code in Lightspeed Pascal 2.0 }
{ This procedure will open the Help DA }
{ Under MultiFinder , this procedure will install the Help DA in * the
}
{ application heap . }
interface
procedure main;
implementation
procedure main;
type
LongPtr = ^LONGINT;
var
error, Help: integer; { Help is the da refnum }
myhandle: Handle;
name: string;
theKeyMap: longint;
theKeyMapPtr: LongPtr;
begin
theKeyMap := $178;
Help := 0;
name := CONCAT(CHR(0), Help);
SetResLoad(FALSE); { Dont load it,}
myhandle := GetNamedResource(DRVR, name);
{ ...just get the handle }
error := ResError; { -192 = Help not available }
SetResLoad(TRUE); { Reset SetResLoad }
if (error = noErr) then
begin { Help is available}
EmptyHandle(myhandle); { Try to purge the Help DA }
if myhandle = nil then
{ If handle=NIL, its not loaded, }
begin
ResrvMem(SizeResource(myhandle));
{ ...reserve memory for it }
error := MemError; { -108 = Not enough room in heap }
end;
if error = noErr then { all go .. . }
begin
LongPtr(theKeyMap)^ := BitOr(LongPtr(theKeyMap)^, 4);
{ Required to work properly with MultiFinder }
{ Press the Option key }
Help := OpenDeskAcc(name); { Open the Help DA }
if (Help < 0) and (Help = (WindowPeek (FrontWindow)^.windowKind)) then
{ If the Help DA open }
begin
end { end if Is the Help DA open }
else
begin
error := 1;
{ Let the caller know that the Help DA was not opened }
LongPtr(theKeyMap)^ := 0;
{ Release the Option key }
end;
end
else
begin
{ Display an Alert : Not enough memory to open Help DA }
end
end {Help is Available }
else
begin
{ Display an Alert : Help DA not available }
end; { End No Help Available}
end; {of Help procedure }
end.
unit getContextHelp;
{Procedure for Calling the Help desk accessory }
{for Context Sensitive Help and Extended Alert Messages}
{{Copyright ©1988 , Help Software , Inc . }
{ Modified by Dave Kelly for MacTutor, April 1989 }
{ Source code in Lightspeed Pascal 2.0 }
{ This procedure will open the Help DA and pass it the number}
{of the Help message to be displayed . }
{ Under MultiFinder , this procedure will install the Help DA in * the
application heap . }
interface
procedure main (message: integer);
{ message: number of message to display }
implementation
procedure main (message: integer);
type
LongPtr = ^LONGINT;
var
error, Help: integer; {Help is desk accessory refnum }
param: ParamBlockRec; {parameter blck for control call }
myhandle: Handle;
name: string;
theKeyMap: longint;
theKeyMapPtr: LongPtr;
begin
theKeyMap := $178;
Help := 0;
name := CONCAT(CHR(0), Help);
SetResLoad(FALSE); { Dont load it,}
myhandle := GetNamedResource(DRVR, name);
error := ResError; { -192 = Help not available }
SetResLoad(TRUE); { Reset SetResLoad }
if (error = noErr) then
begin { Help is available}
EmptyHandle(myhandle); { Try to purge the Help DA }
if myhandle = nil then
{ If handle=NIL, its not loaded, }
begin
ResrvMem(SizeResource(myhandle));
{ ...reserve memory for it }
error := MemError;
{ -108 = Not enough room in heap }
end;
if error = noErr then { all go .. . }
begin
LongPtr(theKeyMap)^ := BitOr(LongPtr(theKeyMap)^, 4);
{ Required to work properly with MultiFinder }
{ Press the Option key }
Help := OpenDeskAcc(name); { Open the Help DA }
if (Help < 0) and (Help = (WindowPeek(FrontWindow)^.windowKind)) then
{ If the Help DA open }
begin
param.ioCompletion := nil;
param.ioRefNum := Help;
{ Help is the value returned}
{ by the OpenDeskAcc call }
param.csCode := 5000;
{ 5000 tells the Help DA: This is}
{a context sensitive help call }
param.csParam[0] := message;
{ Help message to display }
error := PBControl(@param, TRUE);
{ asynchronous control call tells Help DA what to show ! }
end { end if Is the Help DA open }
else
begin
error := 1;
{ Let the caller know that the Help DA was not opened }
LongPtr(theKeyMap)^ := 0;
{ Release the Option key }
end;
end
else
begin
{ Display an Alert : Not enough memory to open Help DA }
end
end {Help is Available }
else
begin
{ Display an Alert : Help DA not available }
end; { End No Help Available}
{ main:=error; for function}
end; {of GetContextHelp ( message ) procedure }
end.
This program demonstrates the use of the
ZBasic 5.0 CALL statement when calling
a (LS) PASCAL CODE resource.
©1989 MacTutor
By Dave Kelly
WINDOW OFF
COORDINATE WINDOW
DEF MOUSE=-1
MOUSE ON
BREAK ON
DEFSTR LONG:T&=CVI(CODE)
DEF LEN=255
DIM B%(7)
GOSUB Define Help Cursor
APPLE MENU About Call Help
MENU 1,0,1,File
MENU 1,1,1,Open
MENU 1,2,1,Close
MENU 1,3,0,-
MENU 1,4,1,Save
MENU 1,5,1,Save As
MENU 1,6,0,-
MENU 1,7,1,Page Setup
MENU 1,8,1,Print
MENU 1,9,0,-
MENU 1,10,1,Quit
EDIT MENU 2
Message%=0 Set up string parameters
HelpCursor%=0
Find out screen size.
CALL GETWMGRPORT(WMgrPort&)
PortTop=PEEK WORD(WMgrPort&+8)
PortLeft=PEEK WORD(WMgrPort&+10)
PortBottom=PEEK WORD(WMgrPort&+12)
PortRight=PEEK WORD(WMgrPort&+14)
WINDOW 1,Main Window,(10,44)-(PortRight-4,PortBottom-4),5
GET WINDOW #1, WindowPtr&
ON MENU GOSUB Do MenuEvent
ON DIALOG GOSUB Do DialogEvent
MENU ON:DIALOG ON:BREAK ON
Loop
CALL GETKEYS(B%(0))
LONG IF B%(2)=16 AND B%(3)=-32767
GOSUB ChangeCursor
END IF
GOTO Loop
DIALOG OFF:MENU OFF
Do MenuEvent
Menunumber=MENU(0)
Menuitem=MENU(1)
MENU
SELECT Menunumber
CASE 255
Message%=0
GOSUB Do Call
CASE 1
GOSUB Do File Menu
END SELECT
HelpCursor%=1
Message%=0
GOSUB ChangeCursor
RETURN
Do File Menu
LONG IF HelpCursor%=0
SELECT Menuitem
CASE 1 Open
CASE 2 Close
CASE 4 Save
CASE 5 Save As
CASE 7 Page Setup
DEF PAGE
CASE 8 Print
DEF LPRINT
CASE 10 Quit
GOTO Quit
END SELECT
XELSE
SELECT Menuitem
CASE 1 Open
Message%=1
GOSUB Do Call
CASE 2 Close
Message%=2
GOSUB Do Call
CASE 4 Save
Message%=4
GOSUB Do Call
CASE 5 Save As
Message%=5
GOSUB Do Call
CASE 7 Page Setup
Message%=7
GOSUB Do Call
CASE 8 Print
Message%=8
GOSUB Do Call
CASE 10 Quit
Message%=10
GOSUB Do Call
END SELECT
END IF
RETURN
Quit
END
Do DialogEvent
D=DIALOG(0):DI=DIALOG(D)
SELECT D
CASE 1 Button
CASE 2 Edit Field
CASE 3 Inactive Window
CASE 4 Close Box
GOTO Quit
CASE 5 Window Update
CASE 6 Return Key
CASE 7 Tab Key
CASE 8 Zoom-in
CASE 9 Zoom-out
CASE 10 Shift-Tab
CASE 11 Clear Key
CASE 12 Left-Arrow
CASE 13 Right-Arrow
CASE 14 Up-Arrow
CASE 15 Down-Arrow
CASE 16 Event inkey$
IF DI=5 THEN GOSUB Do Call
CASE 17 Disk Insert
END SELECT
RETURN
Do Call
LONG IF Message%=0
Id=500 Help DA
XELSE
Id=501 Context Sensitive Help
END IF
Result&=0:Y%=0
SCode&= FN GETRESOURCE(T&,Id) load the call into memory
IF SCode&=0 THEN PRINT Opps:END End if there is no call to call
Y%=FN HLOCK(SCode&) Lock a Relocatable Memory Block
ActualCode&=PEEK LONG (SCode&)
LONG IF Message%=0
CALL ActualCode&Call the Help procedure
XELSE
CALL ActualCode&(Message%) Call the Context Sensitive Help function
END IF
CALL RELEASERESOURCE(SCode&)
Y%=FN HUNLOCK(SCode&) Unlock a Locked Handle
Y%=FN DISPOSHANDLE(SCode&) Dispose of Memory block pointed to by a
Handle
Message%=0 Set up string parameters
RETURN
Define Help Cursor
DIM A%(40)
A%(0)=&H0000:A%(1)=&H3F80:A%(2)=&H7FC0:A%(3)=&H60C0
A%(4)=&H60C0:A%(5)=&H00C0:A%(6)=&H01C0:A%(7)=&H0380
A%(8)=&H0700:A%(9)=&H0E00:A%(10)=&H0C00:A%(11)=&H0C00
A%(12)=&H0000:A%(13)=&H0C00:A%(14)=&H0C00:A%(15)=&H0000
A%(16)=&H3F80:A%(17)=&H7FC0:A%(18)=&HFFE0:A%(19)=&HFFE0
A%(20)=&HF1E0:A%(21)=&HF1E0:A%(22)=&H03E0:A%(23)=&H07C0
A%(24)=&H0F80:A%(25)=&H1F00:A%(26)=&H1E00:A%(27)=&H1E00
A%(28)=&H1E00:A%(29)=&H1E00:A%(30)=&H1E00:A%(31)=&H1E00
A%(32)=&H0004:A%(33)=&H0002
HelpCursor%=0
RETURN
ChangeCursor
LONG IF HelpCursor%=0
CALL SETCURSOR(A%(0))
CALL SHOWCURSOR
HelpCursor%=1
XELSE
CURSOR=0
HelpCursor%=0
END IF
WHILE B%(2)=16 AND B%(3)=-32767
CALL GETKEYS(B%(0))
WEND
RETURN