Pararena2/Sources/Show_help.c

1 line
32 KiB
C
Executable File

#define USE_PICTS 1 /* 1 if PICTs are to be displayed, 0 for text only */
#define ONE_RESOURCE 0 /* 1 for Get1Resource, 0 for GetResource, etc. */
#define COMPRESSION 1 /* compressed TEXT/styl */
#define STANDALONE 0
#define CHECKPOINTS 0
#define ASSERTIONS 0
#if STANDALONE
#define Show_help main
#else
void main( void );
#endif
#include "Show_help.h"
/*
Show_help by James W. Walker, June 1991
version 1.6, updated March 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 or
with an accompanying HyperCard stack.
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_dlog_id, StringPtr help_text_name,
short base_pict_id, StringPtr default_filename,
StringPtr default_menuname );
help_dlog_id is the resource ID of the DLOG resource. The dialog
should have an OK button as item 1 and a userItem, to
display the text, in item 2.
help_text_name is the suffix of the resource name of the TEXT and
styl resources. For instance if help_text_name is
"\pblah", and COMPRESSION is 1, then Show_help will
look for '4CMP' resources named "TEXTblah" and "stylblah".
If COMPRESSION is 0, Show_help looks for a 'TEXT'
resource named "blah" and a 'styl' resource named "blah".
base_pict_id is the resource ID of the first PICT resource.
default_filename is the filename initially presented in the Save dialog
when the user saves the help text as TeachText.
default_menuname is the menu item used for a line that consists of
a tab and nothing else.
TO DO: error recovery, support for modeless use.
*/
#if __option(a4_globals)
#define RESTORE_A4 asm { move.L (SP)+, A4 }
#if __option(pcrel_strings) // single-segment code resource
#define SETUP_A4 asm { move.L A4, -(SP) }\
asm { LEA main, A4 }
#else // multi-segment code resource
#include <SetUpA4.h>
#define SETUP_A4 SetUpA4()
#endif
#else
#define SETUP_A4
#define RESTORE_A4
#endif
#ifndef NIL
#define NIL 0L
#endif
#if ONE_RESOURCE
#define GetResource Get1Resource
#define CountResources Count1Resources
#define GetNamedResource Get1NamedResource
#endif ONE_RESOURCE
#if ASSERTIONS
#define ASSERT(x,y) if (!(x)) {DebugStr("\p" y);}
#else
#define ASSERT(x,y)
#endif ASSERTIONS
#if CHECKPOINTS
#define CKPT(x) DebugStr( "\p" x )
#else
#define CKPT(x)
#endif CHECKPOINTS
enum {
c_OK = 1, /* OK button */
c_help, /* userItem for our help display */
c_save, /* Button to save as TeachText */
c_menu /* userItem for popup menu */
};
#define SCROLLBAR_WIDTH 16
#define TEXT_INSET 4
typedef struct {
Rect bounds;
PicHandle pict;
} pict_info;
typedef struct {
short array_size;
short high_waiting;
Rect high_rect[];
} high_info;
#define INITIAL_HIGHLIGHTS 8
typedef struct { // piggyback some other info on the dialog record
DialogRecord dialog;
ControlHandle scrollbar;
#if USE_PICTS
high_info **high;
short pict_count; /* how many pictures */
pict_info *pict_data; /* pointer to an array */
Boolean high_defer_flag;
#endif
} help_record, *help_ptr;
#if COMPRESSION
typedef struct {
ResType srcType;
Handle srcHandle;
ResType dstType;
Handle dstHandle;
} ParmInfo;
/* We don't actually use the first parameter passed to the CNVT code,
but it is a pointer to a structure as below.
*/
typedef struct {
ProcPtr entryPoint;
short resID;
short parmCount;
Boolean useDefault; /* 2 bytes? */
} RoutineInfo;
typedef pascal OSErr (*CNVT_routine)(RoutineInfo *, ParmInfo *);
#endif COMPRESSION
/* private global variables */
static RgnHandle save_clip;
static CursHandle ibeam_cursor;
/* Prototypes of private routines
*/
static void Push_highlight( high_info **hh, Rect *rect );
static Boolean Pop_highlight( high_info **hh, Rect *rect );
static pascal Boolean Help_filter( DialogPtr dialog,
EventRecord *event, short *itemHit);
static pascal void Text_userItem_proc( WindowPtr the_window, short item_num );
static pascal void Menu_userItem_proc( WindowPtr the_window, short item_num );
static pascal void Scroll_text( ControlHandle the_bar, int part_code );
static pascal Auto_scroll( void );
static void Handle_scroll( DialogPtr dialog,
short the_part, Point local_point );
static void Adjust_text( DialogPtr dialog );
static void Save_text( TEHandle the_text, short base_pict_id,
StringPtr default_filename );
static void Topic_menu( DialogPtr dlog, MenuHandle help_popup );
static MenuHandle Build_popup( TEHandle the_text, StringPtr default_menuname );
static short Find_char(
Handle data_h, // handle to a block of characters
short offset, // initial offset within block
char what ); // the character we're looking for
#if COMPRESSION
static Handle Get_compressed_resource( ResType the_type, StringPtr the_name );
static void Release_compressed_resource( Handle rsrc_h );
#else
#define Get_compressed_resource GetNamedResource
#define Release_compressed_resource ReleaseResource
#endif
#if USE_PICTS
static void Find_pictures( DialogPtr dlog, short first_pict_id );
static void Draw_picts( WindowPtr the_window, Rect *update_rect );
static pascal void High_hook( Rect *high_rect );
static void High_hook_glue( void );
static void Do_deferred_hilites( help_ptr hptr, Rect *update_rect );
#endif
/* ------------------------- Show_help --------------------------------- */
pascal void Show_help( short help_dlog_id, StringPtr help_text_name,
short base_pict_id, StringPtr default_filename,
StringPtr default_menuname )
{
register DialogPtr dptr;
register TEHandle the_text;
short itype, ihit;
Handle item_h;
Rect help_item_box, box;
Handle help_TEXT;
StScrpHandle help_styl;
GrafPtr save_port;
Rect dest, view;
ControlHandle the_bar;
short max_scroll, nLines;
Point place;
MenuHandle help_popup;
CursHandle watch_cursor;
#if __option(a4_globals)
#if __option(pcrel_strings) // single-segment code resource
asm {
LEA main, A4
}
#else // multi-segment code resource
RememberA4();
#endif
#endif
watch_cursor = GetCursor( watchCursor );
SetCursor( *watch_cursor );
ibeam_cursor = GetCursor( iBeamCursor );
dptr = (DialogPtr) NewPtr( sizeof(help_record) );
dptr = GetNewDialog( help_dlog_id, (DialogPeek) dptr, (WindowPtr)-1L );
ASSERT( dptr != NIL, "Failed GetNewDialog" );
GetPort( &save_port );
SetPort( dptr );
((help_ptr) dptr)->high = (high_info **) NewHandle(
sizeof(high_info) + INITIAL_HIGHLIGHTS * sizeof(Rect) );
ASSERT( ((help_ptr) dptr)->high != NIL, "Failed to get highlight record");
(**((help_ptr) dptr)->high).array_size = 2;
(**((help_ptr) dptr)->high).high_waiting = 0;
help_TEXT = Get_compressed_resource( 'TEXT', help_text_name );
if (help_TEXT == NIL)
{
ASSERT(false, "Failed to find help TEXT resource" );
SysBeep(1);
goto getout;
}
help_styl = (StScrpHandle)
Get_compressed_resource( 'styl', help_text_name );
if (help_styl == NIL)
{
DisposHandle( help_TEXT );
ASSERT( false, "Failed to find styl resource" );
SysBeep(1);
goto getout;
}
HLock( help_TEXT );
GetDItem( dptr, c_help, &itype, &item_h, &help_item_box );
SetDItem( dptr, c_help, itype, (Handle) Text_userItem_proc, &help_item_box );
view = help_item_box;
InsetRect( &view, 1, 1 );
view.right -= SCROLLBAR_WIDTH;
dest = view;
InsetRect( &dest, TEXT_INSET, 0 );
the_text = TEStylNew( &dest, &view );
ASSERT( the_text != NIL, "Failed TEStylNew." );
TEStylInsert( *help_TEXT, GetHandleSize(help_TEXT),
help_styl, the_text );
TEActivate( the_text );
Release_compressed_resource( (Handle) help_styl );
Release_compressed_resource( help_TEXT );
nLines = (**the_text).nLines;
SetWRefCon( dptr, (long)the_text );
max_scroll = TEGetHeight( (long) nLines, 1L, the_text )
- (view.bottom - view.top);
help_item_box.left = help_item_box.right - SCROLLBAR_WIDTH;
the_bar = NewControl( dptr, &help_item_box, "\p", true,
0, 0, max_scroll, scrollBarProc, NIL );
ASSERT( the_bar != NIL, "Failed NewControl for scroll bar." );
((help_ptr) dptr)->scrollbar = the_bar;
#if USE_PICTS
Find_pictures( dptr, base_pict_id );
#endif
help_popup = Build_popup( the_text, default_menuname );
TEAutoView( TRUE, the_text ); /* Permit auto-scrolling */
CKPT( "Installing ClikLoop" );
(**the_text).clikLoop = (ProcPtr)Auto_scroll;
#if USE_PICTS
(**the_text).highHook = (ProcPtr) High_hook_glue;
((help_ptr)dptr)->high_defer_flag = false;
#endif
GetDItem( dptr, c_menu, &itype, &item_h, &box );
SetDItem( dptr, c_menu, itype, (Handle) Menu_userItem_proc, &box );
save_clip = NewRgn(); /* Used in Draw_picts */
ShowWindow( dptr );
InitCursor();
do {
ModalDialog( (ProcPtr) Help_filter, &ihit );
if (ihit == c_save)
Save_text( the_text, base_pict_id, default_filename );
else if (ihit == c_menu)
Topic_menu( dptr, help_popup );
} while (ihit != c_OK);
DisposeRgn( save_clip );
#if USE_PICTS
DisposPtr( (Ptr) ((help_ptr)dptr)->pict_data );
#endif
TEDispose( the_text );
getout:
DisposDialog( dptr );
DisposeMenu( help_popup );
SetPort( save_port );
}
/* --------------------------- Push_highlight -------------------------- */
static void Push_highlight( high_info **hh, Rect *rect )
{
if ( (**hh).high_waiting >= (**hh).array_size )
{
SetHandleSize( (Handle)hh, sizeof(high_info) +
(INITIAL_HIGHLIGHTS + (**hh).array_size) * sizeof(Rect) );
ASSERT( MemError() == noErr, "Can't expand highlight array" );
(**hh).array_size = ( GetHandleSize( (Handle)hh ) -
sizeof(high_info) ) / sizeof(Rect);
}
if ( (**hh).high_waiting < (**hh).array_size )
{
(**hh).high_rect[ (**hh).high_waiting ] = *rect;
(**hh).high_waiting++;
}
}
/* --------------------------- Pop_highlight -------------------------- */
static Boolean Pop_highlight( high_info **hh, Rect *rect )
{
if ( (**hh).high_waiting > 0 )
{
(**hh).high_waiting--;
*rect = (**hh).high_rect[ (**hh).high_waiting ];
return true;
}
else
return false;
}
/* --------------------------- Find_char -------------------------- */
/*
Find a character within a handle. In a previous version I did this
with Munger().
returns: the offset of the character, or -1 if not found.
*/
static short Find_char(
Handle data_h, // handle to a block of characters
short offset, // initial offset within block
char what ) // the character we're looking for
{
Ptr text;
short text_size, scan;
text_size = (short) GetHandleSize( data_h );
text = *data_h;
for (scan = offset; (text[scan] != what) && (scan < text_size); ++scan)
;
if (scan == text_size) // not found
scan = -1;
return scan;
}
/* --------------------------- Build_popup ------------------------- */
/*
Build a popup menu of the sections of the help text. We scan for
tab characters. The text between the tab character and the preceding
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 MenuHandle Build_popup( TEHandle the_text, StringPtr default_menuname )
{
MenuHandle popup;
short menu_id;
SignedByte text_state;
Handle text_h; /* handle to just the text */
Str255 menu_data;
char *text; /* pointer to the help text */
register short scan, line_start;
short text_size, title_length;
/* Find an unused menu ID */
menu_id = 1300; /* no particular reason */
while (GetMHandle(menu_id))
++menu_id;
popup = NewMenu( menu_id, "\p" );
text_h = (**the_text).hText;
text_state = HGetState( text_h );
HLock( text_h );
text = *text_h;
text_size = (short) GetHandleSize( text_h );
line_start = 0;
for (scan = 0; scan < text_size; scan++ )
{
if (text[scan] == '\r')
{
line_start = scan + 1;
}
else if (text[scan] == '\t')
{
title_length = scan - line_start;
if (title_length == 0)
BlockMove( default_menuname, menu_data, 256 );
else
{
menu_data[0] = title_length; // note: <= 255
BlockMove( &text[line_start], &menu_data[1], menu_data[0] );
}
/*
AppendMenu recognizes meta-characters like slash,
which is probably not what we want in this case. So
we use SetItem, which does not use meta-characters.
*/
AppendMenu( popup, "\p " );
SetItem( popup, CountMItems(popup), menu_data );
}
}
HSetState( text_h, text_state );
return popup;
}
/* ------------------------- Topic_menu ------------------------ */
/*
This routine is called when the menu title is clicked.
It pops up the menu and scrolls to the indicated tab character.
*/
static void Topic_menu( DialogPtr dptr, MenuHandle menu )
{
short menu_id;
Handle item_h;
short itype;
Rect box;
Point where;
long menu_return;
short menu_choice;
ControlHandle bar;
register short i;
register TEHandle the_text;
Handle text_h;
register short offset;
TextStyle what_style;
short line_height, font_ascent;
if (menu == NIL) return;
InsertMenu( menu, -1 );
GetDItem( dptr, c_menu, &itype, &item_h, &box );
where.h = box.left;
where.v = box.bottom;
LocalToGlobal( &where );
HiliteMode &= ~(1 << hiliteBit);
InvertRect( &box );
menu_return = PopUpMenuSelect( menu, where.v, where.h, 0 );
HiliteMode &= ~(1 << hiliteBit);
InvertRect( &box );
if (HiWord(menu_return)) /* Something selected */
{
menu_choice = LoWord( menu_return );
bar = ((help_ptr)dptr)->scrollbar;
the_text = (TEHandle) GetWRefCon( dptr );
text_h = (**the_text).hText;
/* Find tab character number menu_choice */
offset = -1L;
for (i = 1; i <= menu_choice; ++i)
{
++offset; /* so we don't find the same thing twice */
offset = Find_char( text_h, offset, '\t' );
}
where = TEGetPoint( (short)offset, the_text );
TEGetStyle( (short)offset, &what_style, &line_height,
&font_ascent, the_text );
where.v -= line_height; /* align to TOP of tab */
/*
Now where.v is in local coordinates.
*/
where.v -= (**the_text).destRect.top;
SetCtlValue( bar, where.v );
Adjust_text( dptr );
}
menu_id = (**menu).menuID;
DeleteMenu( menu_id );
}
/* ------------------------- Save_text ------------------------ */
/*
This is called when the user clicks on the "Save as TeachText"
button.
*/
static void Save_text( TEHandle the_text, short base_pict_id,
StringPtr default_filename )
{
Point where;
SFReply reply;
OSErr err;
short data_refnum, res_refnum, old_resfile;
Handle text_data;
SignedByte state;
long count;
#if USE_PICTS
register short num_picts, pict_id;
Handle old_pict, new_pict;
#endif
where.h = where.v = 100;
SFPutFile( where, "\pName of TeachText file:",
default_filename, NIL, &reply );
if (reply.good)
{
old_resfile = CurResFile();
#if USE_PICTS
num_picts = CountResources( 'PICT' );
#endif
(void) FSDelete( reply.fName, reply.vRefNum );
(void) Create( reply.fName, reply.vRefNum, 'ttxt', 'ttro' );
(void) FSOpen( reply.fName, reply.vRefNum, &data_refnum );
text_data = (**the_text).hText;
state = HGetState(text_data );
HLock( text_data );
count = GetHandleSize( text_data );
(void) FSWrite( data_refnum, &count, *text_data );
(void) FSClose( data_refnum );
HSetState( text_data, state );
#if USE_PICTS
if (num_picts > 0)
{
(void) SetVol( NIL, reply.vRefNum );
CreateResFile( reply.fName );
ASSERT( ResError() == noErr, "\pCreateResFile error" );
res_refnum = OpenResFile( reply.fName );
ASSERT( ResError() == noErr, "\pOpenResFile error" );
for (pict_id = base_pict_id;
pict_id < base_pict_id + num_picts; ++pict_id )
{
UseResFile( old_resfile );
old_pict = GetResource( 'PICT', pict_id );
if (old_pict == NIL)
break;
new_pict = old_pict;
(void) HandToHand( &new_pict );
UseResFile( res_refnum );
AddResource( new_pict, 'PICT',
pict_id - base_pict_id + 1000, "\p" );
ASSERT( ResError() == noErr, "\pAddResource error" );
}
CloseResFile( res_refnum );
(void) FlushVol( NIL, reply.vRefNum );
}
#endif
}
}
#if USE_PICTS
/* ------------------------- High_hook_glue ------------------------ */
static void High_hook_glue( void )
{
asm {
move.L (SP)+, A0 ; get address of rectangle
movem.L A2-A5/D3-D7, -(SP) ; save registers
move.L A0, -(SP)
}
CKPT( "High_hook_glue" );
asm {
JSR High_hook
movem.L (SP)+, A2-A5/D3-D7 ; restore registers
RTS
}
}
/* ------------------------- High_hook -------------------------- */
/*
This deferred highlighting scheme is used to ensure that highlighting
will be done after any pictures have been drawn, not before. To do
otherwise can cause pictures to be incorrectly highlighted during
auto-scrolling. This effect can be seen in TeachText.
*/
static pascal void High_hook( Rect *the_rect )
{
register help_ptr front;
CKPT( "High_hook");
front = (help_ptr) FrontWindow();
if ( (front != NIL) &&
(GetPtrSize((Ptr)front) == sizeof(help_record)) &&
!EmptyRect( the_rect ) )
{
if (!front->high_defer_flag)
{
HiliteMode &= ~(1 << hiliteBit);
InvertRect( the_rect );
}
else
{
Push_highlight( front->high, the_rect );
}
}
}
#endif /* USE_PICTS */
/* ------------------------- Auto_scroll ----------------------------- */
/*
This is a ClikLoop routine, called repeatedly by TEClick when the
mouse is down.
*/
static pascal Auto_scroll()
{
register WindowPtr the_display;
register ControlHandle the_bar;
Point mouse_point;
Rect view_rect;
register TEHandle the_text;
asm {
movem.l a1-a5/d1-d7, -(SP)
}
CKPT( "Auto_scroll");
the_display = FrontWindow();
if ( (the_display != NIL) &&
(GetPtrSize((Ptr)the_display) == sizeof(help_record)) )
{
the_text = (TEHandle) GetWRefCon( the_display );
the_bar = ((help_ptr) the_display)->scrollbar;
GetMouse( &mouse_point );
view_rect = (**the_text).viewRect;
if (mouse_point.v < view_rect.top)
Scroll_text( the_bar, inUpButton );
else if (mouse_point.v > view_rect.bottom)
Scroll_text( the_bar, inDownButton );
}
asm {
movem.L (SP)+, a1-a5/d1-d7
moveQ #1, D0
}
}
#if USE_PICTS
/* ------------------------- Draw_picts --------------------------------- */
/*
Called by Adjust_text and Text_userItem_proc to draw pictures.
*/
static void Draw_picts( WindowPtr the_window, Rect *update_rect )
{
register TEHandle the_text;
register short pict_count, pict_index;
PicHandle the_pict;
short v_offset;
Rect pict_loc, dummy;
CKPT( "Draw_picts");
the_text = (TEHandle) GetWRefCon( the_window );
v_offset = (**the_text).destRect.top - (**the_text).viewRect.top
- TEXT_INSET;
pict_count = ((help_ptr) the_window)->pict_count;
for (pict_index = 0; pict_index < pict_count; pict_index++)
{
pict_loc = ((help_ptr) the_window)->pict_data[pict_index].bounds;
OffsetRect( &pict_loc, 0, v_offset );
if (!SectRect( &pict_loc, update_rect, &dummy ))
continue;
the_pict = ((help_ptr) the_window)->pict_data[pict_index].pict;
LoadResource( (Handle) the_pict );
GetClip( save_clip );
ClipRect( update_rect );
DrawPicture( the_pict, &pict_loc );
SetClip( save_clip );
}
}
#define OPTION_SPACE_CHAR 0xCA
/* ---------------------- Find_pictures ---------------------------- */
static void Find_pictures( DialogPtr dlog, short first_pict_id )
{
register TEHandle the_text;
Handle text_h;
SignedByte text_state;
register short offset;
short num_picts;
register short which_pict;
pict_info *pict;
Point place;
short line_height, font_ascent;
TextStyle what_style;
CKPT( "Find_pictures");
the_text = (TEHandle) GetWRefCon( dlog );
text_h = (**the_text).hText;
text_state = HGetState( text_h );
HLock( text_h );
/* Count option-space characters in the text. */
offset = 0;
num_picts = 0;
offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
while ( offset >= 0 )
{
num_picts++;
offset++;
offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
}
/* Allocate storage for an array of picture bounds. */
pict = (pict_info *) NewPtr( sizeof(pict_info) * num_picts );
((help_ptr)dlog)->pict_data = pict;
/*
Initialize the picture info. For each picture we record the
picture handle and its rectangle, in unscrolled window
coordinates.
*/
offset = 0;
for (which_pict = 0; which_pict < num_picts; which_pict++)
{
pict[which_pict].pict = (PicHandle) GetResource( 'PICT',
first_pict_id + which_pict );
if ( pict[which_pict].pict == NIL )
break;
offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
place = TEGetPoint( offset, the_text );
TEGetStyle( offset, &what_style, &line_height,
&font_ascent, the_text );
place.v -= line_height; /* align picture with TOP of option-space */
offset++;
pict[which_pict].bounds = (**pict[which_pict].pict).picFrame;
OffsetRect( &pict[which_pict].bounds,
( ((**the_text).destRect.right + (**the_text).destRect.left) -
(pict[which_pict].bounds.right + pict[which_pict].bounds.left)
) / 2,
- pict[which_pict].bounds.top + place.v );
}
((help_ptr)dlog)->pict_count = which_pict;
getout:
HSetState( text_h, text_state );
}
#endif /* USE_PICTS */
/* ---------------------- Scroll_text ---------------------------- */
/*
This is used as a TrackControl actionProc for scrolling, and also
called by Auto_scroll for automatic scrolling.
*/
static pascal void Scroll_text( ControlHandle the_bar, int part_code )
{
register TEHandle the_text;
register short delta;
register WindowPtr the_display;
short old_value;
short offset, line;
Point place;
Rect view;
TextStyle style;
short line_height, font_ascent;
CKPT( "Scroll_text");
SETUP_A4; // for access to static global save_clip
if (part_code != 0)
{
the_display = (**the_bar).contrlOwner;
the_text = (TEHandle) GetWRefCon( the_display );
view = (**the_text).viewRect;
place.h = view.left + TEXT_INSET;
switch (part_code)
{
case inUpButton:
place.v = view.top - 4;
offset = TEGetOffset( place, the_text );
place = TEGetPoint( offset, the_text );
TEGetStyle( offset, &style, &line_height, &font_ascent,
the_text );
delta = place.v - line_height - view.top;
break;
case inDownButton:
place.v = view.bottom + 2;
offset = TEGetOffset( place, the_text );
place = TEGetPoint( offset, the_text );
/* Now place.v is at the baseline of the border line. */
delta = place.v - view.bottom;
break;
case inPageUp:
/*
I want top border line to remain visible, and
the top of a line should end up at view.top.
*/
place.v = view.top + 2;
offset = TEGetOffset( place, the_text );
place = TEGetPoint( offset, the_text );
/* place.v is at the baseline of the top border line. */
TEGetStyle( offset, &style, &line_height, &font_ascent,
the_text );
place.v += line_height - font_ascent;
place.v -= view.bottom - view.top;
offset = TEGetOffset( place, the_text );
place = TEGetPoint( offset, the_text );
TEGetStyle( offset, &style, &line_height, &font_ascent,
the_text );
delta = place.v - view.top;
if (offset == 0)
delta -= line_height;
break;
case inPageDown:
/*
I want bottom border line to remain visible, and
the bottom of a line should end up at view.bottom.
*/
place.v = view.bottom - 2;
offset = TEGetOffset( place, the_text );
place = TEGetPoint( offset, the_text );
/* place.v is at the baseline of the bottom border line. */
TEGetStyle( offset, &style, &line_height, &font_ascent,
the_text );
place.v -= font_ascent; /* Top edge of bottom border line */
place.v += view.bottom - view.top;
/* We're looking at the bottom border of the next page. */
offset = TEGetOffset( place, the_text );
place = TEGetPoint( offset, the_text );
TEGetStyle( offset, &style, &line_height, &font_ascent,
the_text );
delta = place.v - line_height - view.bottom;
if (offset == (**the_text).teLength)
delta += line_height;
break;
}
old_value = GetCtlValue( the_bar );
if ( ((delta < 0) && (old_value > 0)) ||
((delta > 0) && (old_value < GetCtlMax(the_bar))) )
{
/*
When this routine is called, TextEdit may have set the
clipping region to the view rectangle, so we reset it
here to make sure the scroll bar gets drawn.
*/
GetClip( save_clip );
ClipRect( &the_display->portRect );
SetCtlValue( the_bar, old_value + delta );
SetClip( save_clip );
}
Adjust_text( the_display );
}
RESTORE_A4;
}
/* ---------------------- Adjust_text ---------------------------- */
/*
Called by Handle_scroll and Scroll_text to scroll the text and
pictures into sync with the scroll bar's control value.
*/
static void Adjust_text( DialogPtr dialog )
{
register TEHandle the_text;
register short scroll_down;
short old_scroll;
Rect update_rect;
ControlHandle the_bar;
CKPT( "Adjust_text");
the_text = (TEHandle) GetWRefCon( dialog );
the_bar = ((help_ptr) dialog)->scrollbar;
old_scroll = (**the_text).viewRect.top - (**the_text).destRect.top;
scroll_down = old_scroll - GetCtlValue( the_bar );
if (scroll_down == 0)
return;
#if USE_PICTS
((help_ptr) dialog)->high_defer_flag = true;
//((help_ptr) dialog)->high_waiting = 0;
#endif
TEScroll( 0, scroll_down, the_text );
#if USE_PICTS
update_rect = (**the_text).viewRect;
if (scroll_down > 0)
{
if (scroll_down < (update_rect.bottom - update_rect.top))
update_rect.bottom = update_rect.top + scroll_down;
}
else
if (- scroll_down < (update_rect.bottom - update_rect.top))
update_rect.top = update_rect.bottom + scroll_down;
Draw_picts( dialog, &update_rect );
Do_deferred_hilites( (help_ptr) dialog, &update_rect );
#endif
}
#if USE_PICTS
/* ---------------------- Do_deferred_hilites ---------------------- */
static void Do_deferred_hilites( help_ptr hptr, Rect *update_rect )
{
Rect hilite;
while (Pop_highlight( hptr->high, &hilite ))
{
if (SectRect( &hilite, update_rect, &hilite ))
{
HiliteMode &= ~(1 << hiliteBit);
InvertRect( &hilite );
}
}
hptr->high_defer_flag = false;
}
#endif
/* ---------------------- Handle_scroll ---------------------------- */
/*
Called by Help_filter to handle mouseDown events in the scroll bar.
*/
static void Handle_scroll( DialogPtr dialog, short the_part, Point where )
{
register ControlHandle the_bar;
CKPT( "Handle_scroll"); SetPort( dialog );
the_bar = ((help_ptr) dialog)->scrollbar;
if (the_part == inThumb)
{
(void) TrackControl( the_bar, where, NIL );
Adjust_text( dialog );
}
else
(void) TrackControl( the_bar, where, (ProcPtr)Scroll_text );
}
/* ---------------------- Help_filter ------------------------- */
/*
This is the dialog event filter for our help window.
*/
#define RETURN_CHAR 0x0D
#define TILDE_CHAR 0x7E
#define ENTER_CHAR 0x03
#define ESCAPE_CHAR 0x1B
static pascal Boolean Help_filter( DialogPtr dialog,
EventRecord *event, short *itemHit)
{
Point local_point;
short the_part;
ControlHandle the_control;
short charcode;
register TEHandle the_text;
Rect item_box;
short cursor;
SETUP_A4; // for access to static global ibeam_cursor
the_text = (TEHandle) GetWRefCon( dialog );
GetMouse( &local_point );
if (PtInRect( local_point, &(**the_text).viewRect ))
SetCursor( *ibeam_cursor );
else
InitCursor();
TEIdle( the_text );
switch (event->what) {
case nullEvent:
break;
case mouseDown:
CKPT( "Help_filter mousedown");
local_point = event->where;
GlobalToLocal( &local_point );
the_part = FindControl( local_point, dialog, &the_control );
if (the_part && ((**the_control).contrlMax > 1) )
{
Handle_scroll( dialog, the_part, local_point );
*itemHit = 2;
break;
}
if (PtInRect( local_point, &(**the_text).viewRect ))
{
if (event->modifiers & shiftKey)
TEClick( local_point, true, the_text );
else
TEClick( local_point, false, the_text );
}
break;
case keyDown :
case autoKey :
charcode = event->message & charCodeMask;
/*
There's no Cancel button, so we treat the OK button
the same as a Cancel button.
*/
if ( (charcode == RETURN_CHAR) || (charcode == ENTER_CHAR) ||
(charcode == TILDE_CHAR) || (charcode == ESCAPE_CHAR) ||
((charcode == '.') && (event->modifiers & cmdKey)) )
{
*itemHit = c_OK; /* OK */
Flash_button( dialog, *itemHit );
RESTORE_A4;
return( TRUE );
}
if ( (charcode == 'c') && (event->modifiers & cmdKey) )
{
(void) ZeroScrap();
TECopy( the_text );
SystemEdit(3);
local_point = (**the_text).selPoint;
*(long *)&local_point = PinRect( &(**the_text).viewRect,
local_point );
cursor = TEGetOffset( local_point, the_text );
TESetSelect( (long)cursor, (long)cursor, the_text );
event->what = nullEvent;
}
break;
} /* end switch */
/* tell the Dialog Manager that the event has NOT been handled and that it should
** take further action on this event.
*/
RESTORE_A4;
return false;
}
/* ---------------------- Text_userItem_proc ------------------------- */
static pascal void Text_userItem_proc( WindowPtr the_window, short item_num )
{
Handle item_h;
Rect item_box;
short item_type;
TEHandle the_text;
SETUP_A4; // for access to static global save_clip
CKPT( "Text_userItem_proc");
the_text = (TEHandle) GetWRefCon( the_window );
item_box = (**the_text).viewRect;
#if USE_PICTS
((help_ptr) the_window)->high_defer_flag = true;
#endif
TEUpdate( &item_box, the_text );
#if USE_PICTS
Draw_picts( the_window, &item_box );
Do_deferred_hilites( (help_ptr) the_window, &item_box );
#endif
/*
Get the item's rectangle, and frame it.
*/
GetDItem( the_window, item_num, &item_type, &item_h, &item_box );
FrameRect( &item_box );
RESTORE_A4;
}
/* ---------------------- Menu_userItem_proc ------------------------- */
static pascal void Menu_userItem_proc( WindowPtr the_window, short item_num )
{
Handle item_h;
Rect item_box;
short item_type;
asm {
MOVEM.L a1-a5/d0-d7, -(SP)
}
CKPT( "Menu_UserItem_proc");
/*
Get the item's rectangle, and frame it.
*/
GetDItem( the_window, item_num, &item_type, &item_h, &item_box );
InsetRect(&item_box, -1, -1);
FrameRect( &item_box );
/* Draw the drop-shadow */
MoveTo( item_box.left + 3, item_box.bottom );
LineTo( item_box.right, item_box.bottom );
LineTo( item_box.right, item_box.top + 3 );
asm {
movem.l (SP)+, a1-a5/d0-d7 ; restore registers
}
}
/* ---------------------- Flash_button ----------------------------- */
pascal void Flash_button( DialogPtr the_dialog, short item_number )
{
ControlHandle item_h;
long time;
short itype;
Rect box;
GetDItem( the_dialog, item_number, &itype, (Handle *)&item_h, &box );
HiliteControl( item_h, inButton );
Delay( 9L, &time );
HiliteControl( item_h, 0 );
}
#if COMPRESSION
/* ---------------------- Get_compressed_resource ----------------------- */
static Handle Get_compressed_resource( ResType the_type, StringPtr the_name )
{
register Handle CNVT_h;
ParmInfo info;
Str255 name;
CNVT_routine Converter;
name[0] = the_name[0] + 4;
BlockMove( &the_type, &name[1], 4 );
BlockMove( &the_name[1], &name[5], the_name[0] );
info.srcHandle = GetNamedResource( '4CMP', name );
CNVT_h = GetNamedResource( 'CNVT', "\p4CMPUncompress 4CMP" );
if ( (info.srcHandle == NIL) || /* maybe there's an uncompressed one */
(CNVT_h == NIL) )
{
info.dstHandle = GetNamedResource( the_type, the_name );
}
else /* found a compressed resource */
{
info.srcType = '4CMP';
HLock(CNVT_h);
Converter = (CNVT_routine) StripAddress( *CNVT_h );
CKPT("\pAbout to call the CNVT");
(void) Converter( NIL, &info );
CKPT("\pAfter the CNVT");
HUnlock(CNVT_h);
ReleaseResource( info.srcHandle );
}
return info.dstHandle;
}
#define mem_resource 0x20
/* ------------------------- Release_compressed_resource ------------- */
/*
If it's a resource handle, release it, otherwise dispose of it.
*/
static void Release_compressed_resource( Handle rsrc_h )
{
if ( HGetState( rsrc_h ) & mem_resource )
ReleaseResource( rsrc_h );
else
DisposHandle( rsrc_h );
}
#endif COMPRESSION