home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-01-28 | 37.4 KB | 1,434 lines | [TEXT/CWIE] |
- /*
- HASHelp.c from Hsoi's App Shell. © 1995-1997 John C. Daub. All rights reserved.
-
- This file handles all the help related stuff.
-
- Some of it is my original code (100% mine) and some is or is based upon
- James Walker's Show_help 2.0 (and 3.2.8) code. I originally used to use
- Show_help "as is" in HAS, but then I decided that it'd be nice if Show_help
- used WASTE instead of TextEdit. Due to this fact, what I thought would be
- a simple "replace TE calls with WE calls and a few other modifications for
- compatability" turned into a big rewrite of Show_help. So, my code does
- have some similarities (as you can tell) to Show_help, but is quite different.
-
- Anyways, here's some usage and copyright info from Show_help 2.0
-
- Show_help by James W. Walker, June 1991
-
- version 2.0, updated July 1992
-
- This code is freely usable. If you want to show your gratitude,
- you could send me a free copy of whatever program you develop
- with it.
-
- e-mail:
- Internet 76367.2271@compuserve.com
- CIS 76367,2271
- America Online JWWalker
-
- This code displays scrolling text in a dialog box. The text comes
- from TEXT/styl resources, which can be created with ResEdit 2.1.
- The text cannot be edited, but one can select text and copy it to
- the clipboard using command-C, or save it as a TeachText file.
-
- Pictures can be included in the text using the same scheme as
- TeachText: Each option-space character indicates where the top
- edge of a picture should go, and pictures are centered horizontally.
- Pictures come from consecutively-numbered PICT resources.
-
- A popup menu can be used to jump to "bookmarks", which are indicated
- by tab characters at ends of lines.
-
- Prototype:
-
- pascal void Show_help( short help_info_id,
- pascal void (*Handle_update)( WindowPtr ) );
-
- TO DO: error recovery, support for modeless use.
-
- -----
-
- that's James's old info...with some updating to Show_help 3.2.8 and
- also my changes, there are somethings to note:
-
- to create the text, you'll have a 'TEXT', a 'styl', and a 'SOUP' resource.
- you can create these in a number of ways.
-
- First, if you're not using any SOUP (no embedded objects like pictures or
- sounds), make sure to #define USE_SOUP as zero (0). And if this is
- the case, you can just create you TEXT and styl in ResEdit (or any old
- resource editor). Also, make sure that whatever you set USE_SOUP to,
- that it is conformant with the macro, WASTE_OBJECTS (from HAS HsoiHeaders.pch
- and WASTEIntf.h). If WASTE_OBJECTS == 0, and you want to use SOUP
- (USE_SOUP == 1), then you could be in for trouble....i've tried to put
- some #ifdef's in to deal with this, but be careful what you do! and
- be sure that things are consistant.
-
- If you're using SOUP, here are a couple options:
-
- Open up any WASTE-based text editor (for example, the demo app for HAS, or
- the WASTE Demo App that comes with the WASTE distribution). Author your
- help files as you'd like them to be: the text, any style stuff (color,
- different fonts, sizes, etc), and insert any objects you'd like (PICTs,
- snd's, etc).
-
- Now, if your Mac supports drag-and-drop, you can just select all of
- the text you wrote and then drag that onto the desktop to create
- a clipping file. By creating the clipping file, the 'TEXT', 'styl',
- and 'SOUP' resources will be created. You then open up the resource
- file for your project in ResEdit and also open up the clipping file
- in ResEdit. Copy the 'TEXT', 'styl', and 'SOUP' from the clipping
- and paste them into your app/project res fork. it's not totally necessary,
- but to ease things, try to make sure all the resources have the same ID
- numbers. (if nothing else, make sure you put the proper ID numbers in
- the 'Hso?' resource!)
-
- If you don't support drag-and-drop (or just want to do it this way),
- save your help file as a regular HAS file (or whatever WASTE-editor you use).
- Select all and copy and paste the text into a new 'TEXT' resource. That
- should give you your TEXT and styl. Then, open up your saved file in
- ResEdit and copy and paste the SOUP into the res file. Again, make sure
- the res ID's are the same number.
-
- then, input these ID numbers into the 'Hso?' resource for the ShowHelpInfo
- struct to get.
-
- Incidentally, if you used any SOUP, in the TEXT resource you'll probably see
- little boxes in place of where your objects are supposed to be. don't delete
- these! sure they don't show up in thee TEXT resource, but those are the
- placeholders that WASTE will use to insert the SOUP.
-
- now, for other changes:
-
- text can be selected and copied to the clipboard...i've added a "Select All"
- (cmd-A) call in the hsoiHelpFilter() to help this...and the file is
- NOT saved as a TeachText file...it is saved as a read-only file with a
- creator of 'Hsoi', so it's a Hsoi's App Shell file (this is to ensure
- that SOUP is handled cause TeachText/SimpleText can't do that). But in
- the future, it might be nice to allow saving as a SimpleText file and
- parse the output so all the "SOUP" stuff shows up in a format
- SimpleText can understand.
-
- Pictures are no longer supported like James did it...opt-space characters
- in the TEXT are just ignored by this rewrite. If you want to have
- a picture inline (or any sort of object, like a sound, or an HFS or
- any other sort of object that you want to write the WASTE handler for)
- just insert it into your text as you write the help file (remember, in
- a WASTE-based editor). As for how they're displayed, all the text
- stuff in the help dialog is WASTE, so it's dealt with that way.
-
- someday, not only here in the help stuff but also all throughout Hsoi's App
- Shell, i would like to have the file routines work to be able to read in
- TeachText/SimpleText files and WASTE files and be able to work them either
- and any way. For example, read in a SimpleText file (that has PICTs and
- snds in it as well as styl and text) and then be able to save as a WASTE
- file (i.e. convert all the PICT and snd stuff to SOUP). and of course,
- vice versa and all that. but those sorts of parsing/conversion in the
- read/write routines will again be some future work (or a fun exercise
- for you!).
-
- the popup menu still works the same...a tab character is looked for..then
- anything between that tab and the preceeding carriage return turns into
- a bookmark for the popup menu. eg:
-
- <return>
- This is a title<tab>
-
- that'll be a title....but do be careful using tabs in the body of your
- text...like i said, that'll turn into a bookmark.
-
- and the function prototype has changed:
-
-
- pascal void HsoiShow_help( short infoID, HsoiHelpUpdateUPP handleUpdate,
- HsoiBeginHelpUPP initCallback );
-
- I've attemped to write Universal ProcPtr's (UPP's) for the callback
- routines...but being as how this is my first attempt at writing a
- UPP (and considering I have little idea how to do it...no instructions,
- no books, no references, just looking at WASTE.h and "copying" what
- Marco did), i can only hope they work right :-\ So far from my testing
- on my IIvx (68k) at home at some PowerMacs I have access to, all things
- work ok...
-
- Otherwise, that's that....as for legal rights info, what's James's is
- his and what's mine is mine.
-
- */
-
- #pragma mark ••• #includes •••
-
- // include our other header files
-
- #include "HASHelp.h"
- #include "HASGlobals.h"
- #ifndef __HSOIS_APP_SHELL__
- #include "HASMain.h"
- #endif
- #include "HASUtilDialogs.h"
- #include "HASUtilCursors.h"
- #include "HASMenus.h"
- #include "HASFiles.h"
- #include "HASMiscEvents.h"
- #include "HASLongControls.h"
- #include "HASUtilities.h"
- #include "HASMiscEvents.h"
-
- #include "WASTE_Objects.h"
-
- #pragma mark -
- #pragma mark ••• Static Function Prototypes •••
-
- // these are static functions that will only be called by our help routines.
- // since they're static, we'll declare the function prototyps here instead
- // of in HASHelp.h
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- static pascal Boolean HsoiHelpHandleUpdate( WindowRef );
- static pascal void HsoiHelpTextUserItemProc( WindowRef, short );
- static pascal void HsoiHelpMenuUserItemProc( WindowRef, short );
- static MenuRef HsoiBuildHelpPopup( WEReference, StringPtr );
- static void HsoiHelpTopicMenu( DialogRef, MenuRef );
- static void HsoiHelpSaveText( WEReference, StringPtr );
- static pascal Boolean hsoiHelpFilter( DialogRef, EventRecord *, short * );
- static pascal void hsoiHelpTextScrolled( WEReference );
- static void HsoiHelpAdjustBar( DialogRef );
- static void HsoiHelpScrollBarChanged( DialogRef );
- static void HsoiDoHelpScrollBar( Point, EventModifiers, DialogRef );
- static pascal void hsoiHelpScrollProc( ControlRef, ControlPartCode );
- static void HsoiDoHelpScrollKey( SignedByte, DialogRef );
-
- // a couple of local-global variables (global, yet static!)
-
- #pragma mark -
- #pragma mark ••• Globals •••
-
- static long sHelpScrollStep = 0;
- static ControlActionUPP sHelpScrollProc = nil;
-
-
- #ifdef __cplusplus
- }
- #endif
-
- #pragma mark -
- #pragma mark ••• Do Help •••
-
- /*
- * When the user either hits the "Help" button on the extended keyboard, or calls up the
- * help via the Help menu, this is what gets called to actually do the help routines.
- */
-
- void HsoiDoHelpStuff( void )
- {
- SndListHandle sndHandle;
- OSErr err;
- HsoiHelpUpdateUPP updateUPP = nil;
-
- // if there is a sound playing, stop it
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // just to be cute, play a "Help me" sound
-
- // if the user (via the prefs settings) wants to hear the sound..
-
- if ( gMyPrefs.playHelpSound )
- {
- //... get the sound resource
-
- sndHandle = (SndListHandle)GetResource( TYPE_SOUND , rSoundHelp );
-
- // no handle, let's complain about it
-
- if ( sndHandle == nil )
- HsoiDoError( rErrorStrings, errCantLoadSound, ResError(), kErrNote );
-
- // if we got the thing, let's play the sound.
-
- err = SndPlay( nil, sndHandle, true );
-
- // if there's a problem, let's handle it
-
- if ( err )
- {
- HsoiDoError( rErrorStrings, errCantLoadSound, err, kErrNote );
- }
-
- // we're done, so let's free up the memory that the sound takes up
-
- HsoiForgetResource( (Handle *)&sndHandle );
-
- }
-
- // since we're handling a lot of the dialog activities, one thing we must do is make
- // sure that any other windows are deactivated
-
- HsoiDoActivate( false, FrontWindow() );
-
- // set this as such (cause we will be in a modal state) for proper menu adjustment
- // and handling of menu commands and other events
-
- gInModalState = true;
-
- // adjust our menus properly
-
- HsoiAdjustMenus();
-
- // here's the actual call to our help routine/dialog
- //Pass nil as the 3rd argument cause we don't have a callback routine...
-
- updateUPP = NewHsoiHelpUpdateProc(HsoiHelpHandleUpdate);
-
- HsoiShowHelp( rShowHelpInfo, updateUPP, nil );
-
- DisposeRoutineDescriptor( updateUPP );
-
- // we're done...no longer in a modal state, so set globals as necessary
-
- gInModalState = false;
-
- // adjust our menus to fit our situation
-
- HsoiAdjustMenus();
-
- // reactivate the front window
-
- HsoiDoActivate( false, FrontWindow() );
-
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• Help Dialog •••
-
- // now, let's put in all the stuff that does our help movable modal dialog
-
- #define MODAL_MASK 0x017F // don't take disk, OS, or high-level events
-
- /* ---------------------------------------------------------------------
- Help_ModalDialog ModalDialog cannot be used to handle movable
- modal dialogs (without skanky hacks) because
- it prevents one from switching to another
- application, even if the window is not of the dBoxProc type. Therefore
- I use this routine, which simulates much of what ModalDialog does.
- ---------------------------------------------------------------------
- */
-
-
- pascal void HsoiHelpModalDialog( ModalFilterUPP filterProc,short *itemHit )
- {
- EventRecord event;
- Boolean handled = false;
- DialogRef front, whichDialog;
- short whatHit;
- GrafPtr savePort;
-
- *itemHit = 0;
- front = (DialogRef)FrontWindow();
-
- if (!front) // no window
- return;
-
- GetPort( &savePort );
- SetGrafPortOfDialog( front );
-
- do {
- WaitNextEvent( MODAL_MASK, &event, 0, nil );
-
- if ( filterProc )
- handled = CallModalFilterProc( filterProc, front, &event, itemHit );
-
- if (!handled)
- {
- if (IsDialogEvent(&event))
- {
- if (DialogSelect( &event, &whichDialog, &whatHit ))
- {
- if (whichDialog == front)
- {
- *itemHit = whatHit;
- }
- }
- if (whichDialog == front)
- handled = true;
- }
- /*
- Beep if there is a mouse click on another window
- owned by this application.
- */
- if ( !handled && (event.what == mouseDown) )
- {
- SysBeep(1);
- }
- }
-
-
- } while (*itemHit == 0);
-
- SetPort( savePort );
-
- return;
- }
-
-
- // This is the updateFilterProc called by Help_ModalDialog() to make sure
- // that your applications windows and various other stuff get redrawn
- // if the user moves the help dialog box around the screen.
- //
- // You'll have to add in your own code (or use the code from Hsoi's App Shell
- // if you're using this with Hsoi's App Shell) to do the actual updating
- // (redrawing, etc) of things. In Hsoi's App Shell, all that DoUpdate() is
- // is the "regular" update handler for the shell (same thing called in the
- // MainEventLoop() when an updateEvt is received). Makes life easier than
- // writing duplicate code...plus, having one update handler can make sure
- // that you handle all things properly.
- //
- // I probably ought to use WaitNextEvent() instead of GetNextEvent() and
- // SystemTask(), but due to the nature of the filter/handler, I think
- // GetNextEvent() is more appropriate (correct me if I'm wrong). :)
-
- static pascal Boolean HsoiHelpHandleUpdate( WindowRef whichWindow )
- {
- #pragma unused ( whichWindow )
-
- EventRecord theEvent;
-
- GetNextEvent( everyEvent, &theEvent );
-
- if ( theEvent.what == updateEvt )
- HsoiDoUpdate( (WindowRef)(theEvent.message) );
-
- SystemTask();
-
- return true;
- }
-
- static pascal Boolean hsoiHelpFilter( DialogRef dialog, EventRecord *event, short *itemHit )
- {
- Point localPoint;
- LongRect longViewRect;
- Rect viewRect;
- short thePart;
- ControlRef theControl, theBar;
- short charcode, keycode;
- WEReference we;
- long selStart, selEnd;
- WindowRef window;
- Boolean retval = false;
- ModalFilterUPP stdFilter;
- Rect grayRect;
- HelpHandle help;
- MenuRef editMenu;
- short i;
-
-
- GetStdFilterProc( &stdFilter );
-
- help = (HelpHandle)GetWRefCon( GetDialogWindow(dialog));
- we = (*help)->we;
-
- GetMouse( &localPoint );
- WEGetViewRect( &longViewRect, we );
- WELongRectToRect( &longViewRect, &viewRect );
- if ( PtInRect( localPoint, &viewRect ) )
- // SetCursor( *(GetCursor(iBeamCursor)));
- SetCursor( *gEditCursor );
- else
- InitCursor();
-
- WEIdle( &gSleepTime, we );
-
- editMenu = GetMenuHandle( mEdit );
-
- for ( i = 0; i <= CountMenuItems( editMenu ); i++ )
- DisableItem( editMenu, i );
-
- EnableItem( editMenu, 0 );
- EnableItem( editMenu, iSelectAll );
-
- WEGetSelection( &selStart, &selEnd, we );
- if ( selStart != selEnd )
- EnableItem( editMenu, iCopy );
-
-
- switch( event->what )
- {
- case nullEvent:
- break;
-
- case updateEvt:
- window = (WindowRef)event->message;
-
- if ( (window != GetDialogWindow(dialog)) && ((*help)->handleUpdate != nil ) )
- {
- CallHsoiHelpUpdateProc( (*help)->handleUpdate, window );
- retval = true;
- }
- break;
-
- case mouseDown:
- CKPT( "HelpFilter mousedown" );
-
- thePart = FindWindow( event->where, &window );
-
- if ( thePart == inMenuBar )
- {
- MenuSelect( event->where );
- retval = true;
- }
- else if ( (window == GetDialogWindow(dialog)) && (thePart == inContent) )
- {
- localPoint = event->where;
- GlobalToLocal( &localPoint );
-
- thePart = FindControl( localPoint, dialog, &theControl );
- if ( thePart && (GetControlMaximum(theControl) > 1 ) )
- {
- HsoiDoHelpScrollBar( localPoint, event->modifiers, dialog );
-
- *itemHit = 2;
- retval = true;
- break;
- }
- else if ( PtInRect( localPoint, &viewRect ) )
- {
- WEClick( localPoint, event->modifiers, event->when, we );
- retval = true;
- }
- }
- else if ( (window == GetDialogWindow(dialog)) && (thePart == inDrag ) )
- {
- if ( gHasColorQD )
- grayRect = ( *GetGrayRgn())->rgnBBox;
- else
- grayRect = qd.screenBits.bounds;
-
- DragWindow( dialog, event->where, &grayRect );
- retval = true;
- }
- break;
-
- case keyDown:
-
- charcode = event->message & charCodeMask;
- keycode = (event->message & keyCodeMask ) >> 8;
- theBar = (*help)->scrollbar;
-
- if ( (charcode == kReturnKey) || (charcode == kEnterKey) ||
- (charcode == kEscKey) || ((charcode == '.') && (event->modifiers & cmdKey)) )
- {
- *itemHit = cOkButton;
- HsoiFlashButton(dialog, cOkButton);
- retval = true;
- }
- else if ( ((charcode == 'c') && (event->modifiers & cmdKey)) ||
- (keycode == keyF3) ) // the Copy FKey
- {
- ZeroScrap();
- HsoiStartVBLSpinning();
- WECopy( we );
- HsoiStopVBLSpinning();
- // SystemEdit( 3 );
- WEGetSelection( &selStart, &selEnd, we );
- WESetSelection( selStart, selStart, we );
- event->what = nullEvent;
- retval = true;
- }
- else if ( (charcode == 'a') && (event->modifiers & cmdKey) )
- {
- // select all!
- WESetSelection( 0, MAXLONG, we );
- }
- else if ( keycode == keyPgUp )
- {
- HsoiDoHelpScrollKey( keyPgUp, dialog );
- }
- else if ( keycode == keyPgDn )
- {
- HsoiDoHelpScrollKey( keyPgDn, dialog );
- }
- else if ( keycode == keyHome )
- {
- HsoiDoHelpScrollKey( keyHome, dialog );
- }
- else if ( keycode == keyEnd )
- {
- HsoiDoHelpScrollKey( keyEnd, dialog );
- }
- break;
-
- }// end switch (event->what)
-
- if ( !retval )
- retval = CallModalFilterProc( stdFilter, dialog, event, itemHit );
-
- return retval;
- }
-
- #pragma mark -
- #pragma mark ••• Show Help •••
-
-
- // and now all the fun stuff that actually does the help!
-
- pascal void HsoiShowHelp( short infoID, HsoiHelpUpdateUPP handleUpdate, HsoiBeginHelpUPP initCallback )
- {
- // DialogRef dialog;
- HelpHandle hHelp;
- WEReference we;
- short iHit;
- Handle iHandle;
- Rect helpItemBox;
- Handle helpTEXT = nil;
- StScrpHandle helpStyl = nil;
- WESoupHandle helpSOUP = nil;
- GrafPtr savePort;
- Rect dest, view;
- LongRect longDest, longView;
- ControlRef theBar;
- MenuRef helpPopup;
- ShowHelpInfo **theInfo;
- StringPtr defaultMenuname, defaultFilename;
- UserItemUPP textUPP, menuUPP;
- ModalFilterUPP filterUPP;
- WEScrollUPP weScroller = nil;
- Boolean stoppedSpinning = false;
-
- // let's make sure there's a reasonable amount of memory before starting
-
- iHandle = NewHandle( PREFLIGHT_MEMORY );
- if ( iHandle == nil )
- {
- SysBeep(1);
- return;
- }
- else
- HsoiForgetHandle( &iHandle );
-
- // set the cursor to the watch cursor cause this might take a bit...
-
- // SetCursor( *gWaitCursor );
- HsoiStartVBLSpinning();
-
- // make sure we're using our application's resource fork
-
- UseResFile( gAppResourceFork );
-
- // get the necessary info (what DLOG/TEXT/styl/SOUP/etc resource ID #'s)
- // from the app's resource fork (from the 'Hso?' resource)
-
- theInfo = (ShowHelpInfo **)Get1Resource( TYPE_HELP, infoID );
- ASSERT( theInfo != nil, "Hso? resource missing" );
- HLock( (Handle)theInfo );
-
- // separate out the default filename and the default menu name
- // from the ShowHelpInfo struct
-
- defaultFilename = (**theInfo).strings;
- defaultMenuname = defaultFilename + defaultFilename[0] + 1;
-
- // acclocate a relocateable block to hold a HelpRecord
-
- hHelp = (HelpHandle)NewHandleClear( sizeof( HelpRecord ) );
- ASSERT( MemError() == noErr, "Failed NewHandleClear" );
-
- // get the dialog
-
- gHelpDialog = GetNewDialog( (**theInfo).dialogID, nil, MOVE_TO_FRONT );
- ASSERT ( gHelpDialog != nil, "Failed GetNewDialog" );
-
- // link the HelpRecord to the dialog, and the dialog to the HelpRecord
-
- SetWRefCon( GetDialogWindow(gHelpDialog), (long)hHelp );
- (*hHelp)->dialog = gHelpDialog;
-
- // set the dialog's windowKind for menu adjustment.
-
- // actually do NOT do this...i did this as an experiment some time ago, and unless
- // the windowKind of a dialog is dialogKind, the Dialog Manager won't work with it.
-
- // so, don't mess with dialog's windowKind field...this is just left in as a
- // learning example :)
-
-
- // SetWindowKind( GetDialogWindow( gHelpDialog ), kHelpDialogKind );
-
- // set the port
-
- GetPort( &savePort );
- SetGrafPortOfDialog( gHelpDialog );
-
- // put a handle to our update handler in the HelpRecord
-
- (*hHelp)->handleUpdate = handleUpdate;
-
- // store a handle to the I-Beam cursor in our HelpRecord
-
- (*hHelp)->ibeamCursor = GetCursor( iBeamCursor );
- HLock( (Handle)(*hHelp)->ibeamCursor );
-
- // get our TEXT resource
-
- helpTEXT = Get1Resource( TYPE_TEXT, (**theInfo).textID );
- if ( helpTEXT == nil )
- {
- stoppedSpinning = true;
- HsoiStopVBLSpinning();
- ASSERT( false, "Failed to find help TEXT resource" );
- goto getout;
- }
-
- // get our styl resource
-
- helpStyl = (StScrpHandle)Get1Resource( TYPE_STYLE, (**theInfo).stylID );
- if ( helpStyl == nil )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiForgetHandle( &helpTEXT );
- ASSERT_SET_NIL( helpTEXT );
- ASSERT( false, "Failed to find styl resource" );
- goto getout;
- }
-
- #if USE_SOUP
- // get our SOUP resource
-
- helpSOUP = (WESoupHandle)Get1Resource( TYPE_SOUP, (**theInfo).soupID );
- if ( helpSOUP == nil )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiForgetHandle( &helpTEXT );
- ASSERT_SET_NIL( helpTEXT );
- HsoiForgetHandle( (Handle *)&helpStyl );
- ASSERT_SET_NIL( helpStyl );
- ASSERT( false, "Failed to find SOUP resource" );
- goto getout;
- }
- #endif
-
- // set up our text updating procedure for the DITL's user item
-
- textUPP = NewUserItemProc( HsoiHelpTextUserItemProc );
- HsoiSetDialogItemProc( gHelpDialog, cHelpRect, textUPP );
-
- // set up things with the user rect to have the text area and the scroll bar area
-
- HsoiGetDialogItemRect( gHelpDialog, cHelpRect, &view );
- InsetRect( &view, 1, 1 );
- view.right -= kScrollBarWidth;
- dest = view;
- InsetRect( &dest, TEXT_INSET, 0 );
-
- // do a little conversion of things and get our WASTE instance
-
- WERectToLongRect( &dest, &longDest );
- WERectToLongRect( &view, &longView );
- WENew( &longDest, &longView, weDoAutoScroll +
- weDoOutlineHilite +
- weDoUseTempMem +
- weDoDrawOffscreen, &we );
- ASSERT( we != nil, "Failed WENew" );
-
- // set the alignment to weFlushLeft to "slop recalc" is disabled
-
- WESetAlignment( weFlushLeft, we );
-
- // save a reference to the dialog in the WASTE instance
- // and also save the WE handle in the help record
- // and stick a reference to our help record in the dialog
-
- WESetInfo( weRefCon, (WindowRef)&gHelpDialog, we );
- (*hHelp)->we = we;
- SetWRefCon( (WindowRef)gHelpDialog, (long)hHelp );
-
- // lock the handle to the TEXT
-
- HLock( helpTEXT );
-
- // insert the TEXT, styl, and SOUP into the WASTE instance
-
- WEInsert( *helpTEXT, GetHandleSize(helpTEXT), helpStyl, helpSOUP, we );
-
- // unlock the TEXT
-
- HUnlock( helpTEXT );
-
- // just for kicks, stick the insertion point at the beginning of the text
-
- WESetSelection( 0, 0, we );
-
- // make sure the text is activated
-
- WEActivate( we );
-
-
- // recalc the line breaks
-
- WECalText( we );
-
- // get and set up our scrollbar
-
- HsoiGetDialogItemRect( gHelpDialog, cHelpRect, &helpItemBox );
- helpItemBox.left = helpItemBox.right - kScrollBarWidth;
- theBar = NewControl( gHelpDialog, &helpItemBox, NIL_STRING, true, 0, 0, 0, scrollBarProc, nil );
- ASSERT( theBar != nil, "Failed NewControl for scroll bar" );
-
-
- // attach a LongControl record the the scroll bar: this allows us to use long
- // settings and thus scroll text larger than 32,767 pixels
-
- HsoiLCAttach( theBar );
-
- // store a reference to our scrollbar in the help record
-
- (*hHelp)->scrollbar = theBar;
-
- // set up and install our scroll callback procedure
-
- if ( weScroller == nil )
- {
- weScroller = NewWEScrollProc( hsoiHelpTextScrolled );
- }
-
- WESetInfo( weScrollProc, &weScroller, we );
-
- // make our popup menu
-
- helpPopup = HsoiBuildHelpPopup( we, defaultMenuname );
- InsertMenu( helpPopup, hierMenu );
-
- // set the menu procedure for our popup menu
-
- menuUPP = NewUserItemProc( HsoiHelpMenuUserItemProc );
- HsoiSetDialogItemProc( gHelpDialog, cPopupMenu, menuUPP );
-
- // call our BeginHelpProc, if it exists
-
- if ( initCallback != nil )
- CallHsoiBeginHelpProc( initCallback, GetDialogWindow(gHelpDialog) );
-
- // set up the dialog's default and cancel items
-
- SetDialogDefaultItem( gHelpDialog, cOkButton );
- SetDialogCancelItem( gHelpDialog, cOkButton );
-
- // make sure the WASTE instance is read-only
- // NOTE: we *could* have passed the "weFReadOnly" flag to WENew, but then
- // the call to WEInsert() would fail. therefore, we do it here
-
- WEFeatureFlag( weFReadOnly, weBitSet, we );
-
- // adjust the scroll bar settings based on the total text height
-
- HsoiHelpAdjustBar( gHelpDialog );
-
- // and finally show the dialog!!
-
- ShowWindow( GetDialogWindow(gHelpDialog) );
-
- // make the cursor appear and be an arrow
-
- // InitCursor();
- if ( !stoppedSpinning )
- HsoiStopVBLSpinning();
-
- // get our dialog filter
-
- filterUPP = NewModalFilterProc( hsoiHelpFilter );
-
- // and do the dialog (our own little movable modal dialog thing)
-
- do {
- HsoiHelpModalDialog( filterUPP, &iHit );
-
- if ( iHit == cSaveButton )
- HsoiHelpSaveText( we, defaultFilename );
- else if ( iHit == cPopupMenu )
- HsoiHelpTopicMenu( gHelpDialog, helpPopup );
- } while ( iHit != cOkButton );
-
- // dump the dialog filter
-
- DisposeRoutineDescriptor( filterUPP );
-
- // dump our ShowHelpInfo stuff
-
- HsoiForgetResource( (Handle *)&theInfo );
-
- // dump all the popup menu stuff
-
- DeleteMenu( (**helpPopup).menuID );
- DisposeMenu( helpPopup );
- DisposeRoutineDescriptor( menuUPP );
- Scrollbar:
-
- // dump our handles to the TEXT, styl, and SOUP
-
- #if USE_SOUP
- HsoiForgetResource( (Handle *)&helpSOUP );
- ASSERT_SET_NIL( helpSOUP );
- #endif
- HsoiForgetResource( (Handle *)&helpStyl );
- ASSERT_SET_NIL( helpStyl );
- HsoiForgetResource( &helpTEXT );
- ASSERT_SET_NIL( helpTEXT );
-
- DisposeRoutineDescriptor( sHelpScrollProc );
-
- // dump our WASTE instance
-
- WEDispose( we );
- DisposeRoutineDescriptor( textUPP );
- DisposeRoutineDescriptor( weScroller );
- ASSERT_SET_NIL( we );
- getout:
-
- // dump the dialog
- DisposeDialog( gHelpDialog );
- gHelpDialog = nil;
-
- // restore the original port
-
- SetPort( savePort );
-
- // bye!
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• Procs and Callbacks •••
-
- static pascal void HsoiHelpTextUserItemProc( WindowRef window, short item )
- {
- WEReference we;
- HelpHandle help;
- RgnHandle iRgn = nil;
- Rect userRect;
- LongRect viewRect;
-
- CKPT( "HsoiHelpTextUserItemProc" );
-
- help = (HelpHandle)GetWRefCon(window);
- we = (*help)->we;
-
- // in TextEdit, TEUpdate() takes a Rect (for the viewRect) whereas WEUpdate() takes
- // a RgnHandle. So, we need to get the viewRect and turn that into a RgnHandle.
- // here's how we do it!
-
- // get the viewRect
-
- WEGetViewRect( &viewRect, we );
-
- // convert the viewRect from a LongRect to a Rect
-
- WELongRectToRect( &viewRect, &userRect );
-
- // initialize a new Region (you MUST do this before you use a region)
- // using an uninitialized Rgn can cause you problems!
-
- iRgn = NewRgn();
-
- // a nice little toolbox routine that converts a Rect to a Rgn (look at just
- // what makes up a Region (check Inside Macintosh or THINK Reference) and
- // you'll see this is pretty simple...also, it'll show you how a Rect is
- // important and how a Region is important...cause not everything is a simple
- // rectangle)
-
- RectRgn( iRgn, &userRect );
-
- // and now call WEUpdate
-
- WEUpdate( iRgn, we );
-
- // and make sure to dispose of this region we allocated since we don't need
- // it anymore, and if it was left to hang around, we'd have all sorts of
- // memory usage problems (like leaks)
-
- if ( iRgn != nil )
- DisposeRgn( iRgn );
-
- // and throw a little frame around our viewRect
-
- HsoiGetDialogItemRect( (DialogRef)window, item, &userRect );
- FrameRect( &userRect );
-
- return;
- }
-
-
- static pascal void HsoiHelpMenuUserItemProc( WindowRef window, short item )
- {
- Rect itemRect;
-
- CKPT( "HsoiHelpMenuUserItemProc" );
-
- // get the item's rectangle and frame it
-
- HsoiGetDialogItemRect( (DialogRef)window, item, &itemRect );
- InsetRect( &itemRect, -1, -1 );
- FrameRect( &itemRect );
-
- // draw the drop-shadow
-
- MoveTo( itemRect.left + 2, itemRect.bottom );
- LineTo( itemRect.right, itemRect.bottom );
- LineTo( itemRect.right, itemRect.top + 2 );
-
- return;
- }
-
-
- static pascal void hsoiHelpScrollProc( ControlRef bar, ControlPartCode partCode )
- {
- long value, step;
-
- if ( partCode == kControlNoPart )
- return;
-
- value = HsoiLCGetValue( bar );
- step = sHelpScrollStep;
-
- if ( (( value < HsoiLCGetMax( bar )) && (step > 0 )) || (( value > 0 ) && ( step < 0 )) )
- {
- HsoiLCSetValue( bar, value + step );
- HsoiHelpScrollBarChanged( (DialogRef)FrontWindow() );
- }
-
- return;
- }
-
- // this is a callback rountine called whenever the text is scrolled automatically.
- // since auto-scrolling is enabled, WEScroll may be invoked internally by WASTE
- // in many different circumstances, and we want to be notified when this happens
- // so we can adjust the scroll bar
-
- static pascal void hsoiHelpTextScrolled( WEReference we )
- {
- DialogRef dialog = nil;
-
- // retrieve the window/dialog pointer stored in the WE instance as a refCon
-
- if ( WEGetInfo( weRefCon, (WindowRef)&dialog, we ) != noErr )
- return;
-
- // make sure the scroll bar is in synch with the destination rectangle
-
- HsoiHelpAdjustBar( dialog );
-
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• Popup Menu •••
-
- /*
- Build a popup menu of the sections of the help text. We scan for tab
- characters. The text between the tab character and the preceeding line
- break will be a menu item, unless it is the null string; then we use the
- default menu name that was passed to Show_help
- */
-
- static MenuRef HsoiBuildHelpPopup( WEReference we, StringPtr defaultMenuname )
- {
- MenuRef popup;
- short menuID;
- SignedByte textState;
- Handle hText; // handle to just the text
- Str255 menuData;
- char *pText; // pointer to the help text
- long scan, lineStart;
- short titleLength;
- long textSize;
-
-
- // find an unused menu ID
- menuID = 1300; // just an arbitrary number
- while ( GetMenuHandle( menuID ) )
- ++menuID;
-
- popup = NewMenu( menuID, NIL_STRING );
- ASSERT( popup != nil, "NewMenu failed" );
-
- hText = WEGetText( we );
- textState = HGetState( hText );
- HLock( hText );
- pText = *hText;
-
- textSize = GetHandleSize( hText );
- lineStart = 0;
-
- for ( scan = 0; scan < textSize; scan++ )
- {
- if ( pText[scan] == '\r' )
- {
- lineStart = scan + 1;
- }
- else if ( pText[scan] == '\t' )
- {
- titleLength = scan - lineStart;
- if ( titleLength == 0 )
- BlockMoveData( defaultMenuname, menuData, 256 );
- else
- {
- menuData[0] = titleLength; // note: <= 255
- BlockMoveData( &pText[lineStart], &menuData[1], menuData[0] );
- }
-
- AppendMenu( popup, "\p " );
- SetMenuItemText( popup, CountMenuItems(popup), menuData );
- }
- }
-
- HSetState( hText, textState );
-
- return popup;
- }
-
-
- /*
- This routine is called when the menu title is clicked.
- It pops up the menu and scrolls to the indicated tab character
- */
-
- static void HsoiHelpTopicMenu( DialogRef dialog, MenuRef menu )
- {
- Rect box;
- LongRect longRect;
- Point where;
- LongPt longPoint;
- long menuReturn;
- short menuChoice;
- ControlRef bar;
- short i;
- WEReference we;
- Handle hText;
- short offset;
- short lineHeight;
- HelpHandle help;
-
- if ( menu == nil )
- return;
-
- HsoiGetDialogItemRect( dialog, cPopupMenu, &box );
- where.h = box.left;
- where.v = box.bottom;
- LocalToGlobal( &where );
-
- InvertRect( &box );
- menuReturn = PopUpMenuSelect( menu, where.v, where.h, 0 );
- InvertRect( &box );
-
- if ( HiWrd( menuReturn ) ) // something was selected
- {
- menuChoice = LoWrd(menuReturn);
- help = (HelpHandle)GetWRefCon( GetDialogWindow(dialog) );
- bar = (*help)->scrollbar;
- we = (*help)->we;
- hText = WEGetText( we );
-
- // find tab character number menuChoice
-
- offset = -1L;
- for ( i = 1; i <= menuChoice; ++i )
- {
- ++offset; // so we don't find the same thing twice
- offset = HsoiFindChar( hText, offset, '\t' );
- }
-
- WEGetPoint( offset, hilite, &longPoint, &lineHeight, we );
- WELongPointToPoint( &longPoint, &where );
-
- where.v -= lineHeight; // align to TOP of tab
-
- // now where.v is in local coordinates
-
- WEGetDestRect( &longRect, we );
- WELongRectToRect( &longRect, &box );
- where.v -= box.top;
- HsoiLCSetValue( bar, where.v );
-
- HsoiHelpScrollBarChanged( dialog );
- }
-
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• Scroll Bar •••
-
-
- static void HsoiHelpAdjustBar( DialogRef dialog )
- {
- HelpHandle help;
- WEReference we;
- GrafPtr savePort;
- LongRect viewRect, destRect;
- long value, max;
- ControlRef bar;
-
- GetPort( &savePort );
- SetGrafPortOfDialog( dialog );
-
- // get the view and dest rects
-
- help = (HelpHandle)GetWRefCon( GetDialogWindow( dialog ) );
- we = (*help)->we;
-
- WEGetViewRect( &viewRect, we );
- WEGetDestRect( &destRect, we );
-
- // get the scroll bar
-
- bar = (*help)->scrollbar;
-
- // calc the new scroll bar settings
-
- value = viewRect.top - destRect.top;
-
- max = value + (destRect.bottom - viewRect.bottom );
-
- if ( max <= 0 )
- max = 0;
-
- // reset the scroll bar
-
- HsoiLCSetMax( bar, max );
- HsoiLCSetValue( bar, value );
-
- // if value exceeds max then the bottom of the destRect is above the bottom of the
- // view rectangle: we need to scrol the text downward
-
- if ( value > max )
- HsoiHelpScrollBarChanged( dialog );
-
- SetPort( savePort );
-
- return;
- }
-
-
- static void HsoiHelpScrollBarChanged( DialogRef dialog )
- {
- WEReference we;
- HelpHandle help;
- LongRect viewRect, destRect;
-
- help = (HelpHandle)GetWRefCon( GetDialogWindow( dialog ) );
- we = (*help)->we;
- WEGetViewRect( &viewRect, we );
- WEGetDestRect( &destRect, we );
-
- WEScroll( 0, viewRect.top - destRect.top - HsoiLCGetValue( (*help)->scrollbar ), we );
-
- return;
- }
-
-
- static void HsoiDoHelpScrollBar( Point hitPt, EventModifiers modifiers, DialogRef dialog )
- {
- HelpHandle help;
- ControlRef bar;
- LongRect viewRect;
- ControlPartCode partCode;
- short step;
-
- help = (HelpHandle)GetWRefCon( GetDialogWindow( dialog ) );
- WEGetViewRect( &viewRect, (*help)->we );
-
- // find out if the scroll bar was hit and if so, in which part
-
- partCode = FindControl( hitPt, (WindowRef)dialog, &bar );
-
- if ( bar != nil )
- {
- // dispatch on partCode
-
- if ( partCode == kControlIndicatorPart )
- {
- // click in thumb: call TrackControl with no actionProc and adjust text
-
- partCode = TrackControl( bar, hitPt, nil );
- HsoiLCSynch( bar );
- HsoiHelpScrollBarChanged( dialog );
- }
- else
- {
- // dispatch on our partCode
-
- switch ( partCode )
- {
- case kControlUpButtonPart:
- if ( (modifiers & optionKey) == 0 )
- step = -kScrollDelta;
- else
- step = -1;
- break;
-
- case kControlDownButtonPart:
- if ( (modifiers & optionKey) == 0 )
- step = +kScrollDelta;
- else
- step = 1;
- break;
-
- case kControlPageUpPart:
- step = -(viewRect.bottom - viewRect.top) + kScrollDelta;
- break;
-
- case kControlPageDownPart:
- step = (viewRect.bottom - viewRect.top) - kScrollDelta;
- break;
-
- default:
- step = 0;
- }
-
- sHelpScrollStep = step;
-
- if ( sHelpScrollProc == nil )
- sHelpScrollProc = NewControlActionProc( hsoiHelpScrollProc );
- partCode = TrackControl( bar, hitPt, sHelpScrollProc );
- }
- }
- return;
- }
-
-
- static void HsoiDoHelpScrollKey( SignedByte keyCode, DialogRef dialog )
- {
- HelpHandle help;
- ControlRef bar;
- long v;
- LongRect viewRect;
-
- help = (HelpHandle)GetWRefCon( GetDialogWindow( dialog ) );
- bar = (*help)->scrollbar;
-
- v = HsoiLCGetValue( bar );
-
- WEGetViewRect( &viewRect, (*help)->we );
-
- switch ( keyCode )
- {
- case keyPgUp:
- v = v - (viewRect.bottom - viewRect.top ) + kScrollDelta;
- break;
-
- case keyPgDn:
- v = v + (viewRect.bottom - viewRect.top ) - kScrollDelta;
- break;
-
- case keyHome:
- v = 0;
- break;
-
- case keyEnd:
- v = MAXLONG;
- break;
-
- default:
- break;
- }
-
- HsoiLCSetValue( bar, v );
- HsoiHelpScrollBarChanged( dialog );
-
- return;
- }
-
- #pragma mark -
- #pragma mark ••• Misc Utils •••
-
-
- pascal void HsoiFlashButton( DialogRef dialog, short item )
- {
- Handle hItem;
- long time;
-
- hItem = HsoiGetDialogItemHandle( dialog, item );
- HiliteControl( (ControlRef)hItem, kControlButtonPart );
- Delay( 8L, &time );
- HiliteControl( (ControlRef)hItem, 0 );
-
- return;
- }
-
-
- static void HsoiHelpSaveText( WEReference we, StringPtr defaultFilename )
- {
- StandardFileReply reply;
- OSErr err = noErr;
- GrafPtr savePort;
-
- // tho it might seem unnecessary to stop a sound here, we just might have to
- // if there is a sound embedded in the help dialog and it's playing.
-
- // we'll use these preprocessor macros. if we're saving the dialog's text,
- // we know the only sounds that could be playing would be sounds within
- // the dialog (no way while this dialog is up that any sounds in other documents
- // could get played). And if we're not using SOUP, we might run into problems
- // trying to stop a sound that can't exist.
-
- #if USE_SOUP
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
- #endif
-
- WEDeactivate( we );
- GetPort( &savePort );
-
- StandardPutFile( "\pName of Hsoi's App Shell file:", defaultFilename, &reply );
- SetPort( savePort );
-
- if ( reply.sfGood )
- {
- err = HsoiWriteTextFile( &reply.sfFile, true, we );
- }
-
- if ( err )
- SysBeep( 1 ); // lame error handling
-
- WEActivate( we );
-
- return;
- }
-
-
-
- /*
- This function finds a given character within a Handle.
-
- returns: the offset to the character, or -1 if not found
- */
-
- static short HsoiFindChar( Handle hData, // handle to a block of characters
- short offset, // initial offset within block
- char what ) // the character we're looking for
- {
- Ptr text;
- long textSize, scan;
-
- textSize = GetHandleSize( hData );
- text = *hData;
-
- for ( scan = offset; (text[scan] != what) && (scan < textSize); ++scan )
- ;
-
- if ( scan == textSize ) // not found
- scan = -1;
-
- return scan;
- }
-