1 line
32 KiB
C
Executable File
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 |