[mod.mac.sources] TransEdit.pit.Hqx Human Readable Version

dubois@uwmacc.UUCP (Paul DuBois) (11/26/86)

#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	TransEdit/TransEdit.h
#	TransEdit/TransEdit.c
#	TransEdit/FakeAlert.c
#
#	MODERATOR'S NOTE:  Tab-width is 4
#
sed 's/^X//' << 'SHAR_EOF' > TransEdit/TransEdit.h
X/*
X	TransEdit.h - TransEdit header file
X*/
X
X# ifndef	_WindowMgr_
X# include	"WindowMgr.h"
X# endif
X
X# ifndef	_TextEdit_
X# include	"TextEdit.h"
X# endif
X
X
X# ifndef	nil
X# define	nil		(0L)
X# endif
X
X
XWindowPtr	NewEWindow ();
XWindowPtr	GetNewEWindow ();
XTEHandle	GetEWindowTE ();
XBoolean		GetEWindowFile ();
XBoolean		IsEWindow ();
XBoolean		IsEWindowDirty ();
XBoolean		EWindowSave ();
XBoolean		EWindowSaveAs ();
XBoolean		EWindowSaveCopy ();
XBoolean		EWindowClose ();
XBoolean		EWindowRevert ();
XBoolean		ClobberEWindows ();
Xint			EWindowEditOp ();SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > TransEdit/TransEdit.c
X/*
X	TransEdit.c version 1.0 - TransSkel plug-in module supporting an
X	arbitrary number of generic edit windows.  Each window may be
X	bound to a file.
X
X	*** Requires FakeAlert.c for proper linking! ***
X
X	Shortcomings:
X		Doesn't check for the obvious out of memory conditions.
X	
X	TransSkel and TransEdit are public domain, and are written by:
X
X			Paul DuBois
X			Wisconsin Regional Primate Research Center
X			1220 Capital Court
X			Madison WI  53706  USA
X
X	UUCP:	{allegra,ihnp4,seismo}!uwvax!uwmacc!dubois
X	ARPA:	dubois@unix.macc.wisc.edu
X			dubois@rhesus.primate.wisc.edu
X
X	This version of TransEdit written for LightspeedC.  LightspeedC
X	is a trademark of:
X			THINK Technologies, Inc
X			420 Bedford Street  Suite 350
X			Lexington, MA  02173  USA
X
X  History
X  08/25/86	Genesis.  Beta version.
X  09/15/86	Changed to allow arbitrary number of windows.
X  11/04/86	Added conditional stuff to allow compilation in
X 			single- or multiple-window mode.  Changed version to 1.0.
X*/
X
X
X/*
X	The following symbol controls the compile mode.  If it is #define'd,
X	TransEdit allows only a single edit window, and generates less code.
X	If it is #undef'ed, TransEdit allows an arbitrary number of edit
X	windows, but generates more code.
X*/
X
X# undef	singleEdit
X
X
X# include	<ControlMgr.h>	/* includes WindowMgr.h, QuickDraw.h, MacTypes.h */
X# include	<StdFilePkg.h>
X# include	<FileMgr.h>
X# include	<ToolBoxUtil.h>
X# include	"TransEdit.h"
X
X
X/*
X	Edit window types, constants, variables.
X*/
X
X
X# define	enter			3
X# define	cr				13
X# define	monaco			4
X# define	shiftKey		0x0200
X
X
Xtypedef enum			/* Edit menu item numbers */
X{
X	undo = 1,
X	/* --- */
X	cut = 3,
X	copy,
X	paste,
X	clear		/* (it's ok if the host doesn't have this item) */
X};
X
X
X/*
X	Default values for edit window text display characteristics
X	and event notification procedures
X*/
X
Xstatic int		e_font = monaco;	/* default font                 */
Xstatic int		e_size = 9;			/* default pointsize            */
Xstatic int		e_wrap = 0;			/* default word wrap (on)       */
Xstatic int		e_just = teJustLeft;/* default justification        */
Xstatic ProcPtr	e_key = nil;		/* default key procedure        */
Xstatic ProcPtr	e_activate = nil;	/* default activation procedure */
Xstatic ProcPtr	e_close = nil;		/* default close procedure      */
X
X
X# ifndef	singleEdit
X
X/*
X	New(TypeName) returns handle to new object, for any TypeName.
X	If there is insufficient memory, the result is nil.
X*/
X
X# define	New(x)	(x **) NewHandle ((Size) sizeof (x))
X
X
X/*
X	ewList points to a list of structures describing the known edit
X	windows.
X*/
X
X
Xtypedef struct EditInfo
X{
X	WindowPtr		editWind;	/* the edit window                   */
X	Boolean			bound;		/* whether window is bound to file   */
X	SFReply			editFile;	/* file it's bound to, if bound true */
X	TEHandle		editTE;		/* window text                       */
X	Boolean			dirty;		/* whether text modified since save  */
X	ControlHandle	scroll;		/* scroll bar                        */
X	int				visLines;	/* # lines visible in window, max    */
X	ProcPtr			eKey;		/* key click notifier                */
X	ProcPtr			eActivate;	/* activate event notifier           */
X	ProcPtr			eClose;		/* close notifier                    */
X	struct EditInfo	**eNext;	/* next information structure        */
X} EditInfo, *EIPtr, **EIHandle;
X
X
Xstatic EIHandle		ewList = nil;
X
X# endif
X
X
X/*
X	Global variables - most of these are always synced to
X	the current window.  Note that not all these are set by
X	SyncGlobals, since some are not often needed.  When they
X	are all needed, use SyncAllGlobals.
X*/
X
X# ifndef	singleEdit
Xstatic EIHandle			editInfo;		/* window's info structure      */
X# endif
X
Xstatic WindowPtr		editWind = nil;	/* the window                   */
Xstatic TEHandle			editTE;			/* window text                  */
Xstatic ControlHandle	editScroll;		/* the scroll bar               */
Xstatic SFReply			editFile;		/* file information             */
Xstatic int				visLines;		/* number of lines in window    */
Xstatic Boolean			bound;			/* true if window bound to file */
Xstatic Boolean			dirty;			/* whether window is dirty      */
Xstatic ProcPtr			eKey;			/* key click notifier           */
Xstatic ProcPtr			eActivate;		/* activate event notifier      */
Xstatic ProcPtr			eClose;			/* close notifier               */
X
X
Xstatic int		windID = 0;
Xstatic Point	dlogWhere = { 70, 100 };	/* GetFile/PutFile location */
Xstatic OSType	creator = 'TEDT';			/* default file creator */
X
Xstatic RgnHandle	clipRgn;
X
X
X/* -------------------------------------------------------------------- */
X/*				Miscellaneous Internal (private) Routines				*/
X/* -------------------------------------------------------------------- */
X
X
X/*
X	Save and restore the current window's clip region
X*/
X
Xstatic SaveClipRgn ()
X{
X	clipRgn = NewRgn ();
X	GetClip (clipRgn);
X}
X
X
Xstatic RestoreClipRgn ()
X{
X	SetClip (clipRgn);
X	DisposeRgn (clipRgn);
X}
X
X
X/*
X	Draw grow box in lower right hand corner of window.
X*/
X
X
Xstatic DrawGrowBox ()
X{
XRect		r;
X
X	SaveClipRgn ();
X	r = editWind->portRect;
X	r.left = r.right - 15;		/* draw only in corner */
X	r.top = r.bottom - 15;
X	ClipRect (&r);
X	DrawGrowIcon (editWind);
X	RestoreClipRgn ();
X}
X
X
X/* -------------------------------------------------------------------- */
X/*			Lowest-level Internal (Private) Edit Window Routines		*/
X/* -------------------------------------------------------------------- */
X
X
X# ifndef	singleEdit
X
X/*
X	Get edit window info associated with window.
X	Return nil if window isn't a known edit window.
X*/
X
Xstatic EIHandle GetEInfo (theWind)
XWindowPtr	theWind;
X{
Xregister EIHandle	h;
X
X	for (h = ewList; h != nil; h = (**h).eNext)
X	{
X		if ((**h).editWind == theWind)
X			return (h);
X	}
X	return (nil);
X}
X
X# endif
X
X
X# ifdef	singleEdit
X# define	SyncAllGlobals	SyncGlobals
X# endif
X
X
X/*
X	Synchronize globals to an edit window and make it the
X	current port.  theWind must be a legal edit window, with one
X	exception:  if theWind is nil, the variables are synced to the
X	port that's already current.  That is safe (and correct) because:
X	(i)	 nil is only passed by edit window handler procedures,
X		 which are only attached to edit windows
X	(ii) TransSkel always sets the port to the window before
X		 calling the handler proc.
X	Hence, using the current port under these circumstances always
X	produces a legal edit window.
X*/
X
Xstatic SyncGlobals (theWind)
XWindowPtr	theWind;
X{
X
X	if (theWind == nil)					/* use current window */
X		GetPort (&theWind);
X
X	SetPort (theWind);
X
X# ifndef	singleEdit
X
X	editWind = theWind;
X	editInfo = GetEInfo (editWind);
X	editTE = (**editInfo).editTE;
X	editScroll = (**editInfo).scroll;
X	visLines = (**editInfo).visLines;
X
X# endif
X
X}
X
X
X# ifndef	singleEdit
X
Xstatic SyncAllGlobals (theWind)
XWindowPtr	theWind;
X{
X
X	SyncGlobals (theWind);				/* sync display globals */
X	editFile = (**editInfo).editFile;	/* sync file, state, and */
X	bound = (**editInfo).bound;			/* procedure globals */
X	dirty = (**editInfo).dirty;
X	eKey = (**editInfo).eKey;
X	eActivate = (**editInfo).eActivate;
X	eClose = (**editInfo).eClose;
X}
X
X# endif
X
X
X/*
X	Set dirty flag for current window
X*/
X
Xstatic SetDirty (boolVal)
XBoolean	boolVal;
X{
X
X# ifdef	singleEdit
X	dirty = boolVal;
X# else
X	(**editInfo).dirty = boolVal;
X# endif
X}
X
X
X/* -------------------------------------------------------------------- */
X/*					Internal (private) Display Routines					*/
X/* -------------------------------------------------------------------- */
X
X
X/*
X	Calculate the dimensions of the editing rectangle for
X	editWind (which must be set properly and is assumed to be
X	the current port).  (The viewRect and destRect are the
X	same size.)  Assumes the port, text font and text size are all
X	set properly.  The viewRect is sized so that an integral
X	number of lines can be displayed in it, i.e., so that a
X	partial line never shows at the bottom.  If that's not
X	done, funny things can happen to the caret.
X*/
X
Xstatic GetEditRect (r)
XRect	*r;
X{
XFontInfo		f;
Xregister int	lineHeight;
X
X	GetFontInfo (&f);
X	lineHeight = f.ascent + f.descent + f.leading;
X	*r = editWind->portRect;
X	r->left += 4;
X	r->right -= 17;		/* leave room for scroll bar */
X	r->top += 2;
X	r->bottom = r->top + ((r->bottom - r->top - 2) / lineHeight) * lineHeight;
X}
X
X
X/*
X	Set the edit rect properly.
X*/
X
Xstatic SetEditRect ()
X{
XRect	r;
X
X	GetEditRect (&r);
X	(**editTE).destRect.right = r.right;
X	(**editTE).viewRect = r;
X}
X
X
X
X/*
X	Calculate the dimensions of the scroll bar rectangle for
X	editWind (which must be set properly).  Make sure that
X	the edges overlap the window frame and the grow box.
X*/
X
Xstatic CalcScrollRect (r)
XRect		*r;
X{
X	*r = editWind->portRect;
X	++r->right;
X	--r->top;
X	r->left = r->right - 16;
X	r->bottom -= 14;
X}
X
X
X/*
X	Return true if the mouse is in the non-scrollbar part of the
X	edit window.
X*/
X
Xstatic Boolean PtInText (pt)
XPoint	pt;
X{
XRect	r;
X
X	r = editWind->portRect;
X	r.right -= 15;
X	return (PtInRect (pt, &r));
X}
X
X
X/*
X	Set the cursor appropriately.  If theCursor == iBeamCursor, check
X	that it's really in the text area of an edit window (and if not
X	set the cursor to an arrow instead).  Otherwise, set the cursor
X	to the given type (usually a watch).
X
X	If the cursor is supposed to be set to an i-beam, it is assumed
X	that the globals are synced, because DoCursor changes them and
X	syncs them back.
X
X	Pass -1 for theCursor to set the cursor to the arrow.
X*/
X
Xstatic DoCursor (theCursor)
Xint		theCursor;
X{
XPoint	pt;
XGrafPtr	savePort;
X
X	if (theCursor == iBeamCursor)			/* check whether there's an edit */
X	{										/* window in front and if so,    */
X		theCursor = -1;						/* whether the cursor's in its   */
X		if (IsEWindow (FrontWindow ()))		/* text area                     */
X		{
X			GetPort (&savePort);
X			SyncGlobals (FrontWindow ());
X			GetMouse (&pt);
X			if (PtInText (pt))
X				theCursor = iBeamCursor;
X			SyncGlobals (savePort);
X		}
X	}
X	SetCursor (theCursor == -1 ? &arrow : *GetCursor (theCursor));
X}
X
X
X/*
X	Calculate the number of lines currently scrolled off
X	the top.
X*/
X
Xstatic LinesOffTop ()
X{
Xregister TEPtr	ePtr;
X
X	ePtr = *editTE;
X	return (((*ePtr).viewRect.top - (*ePtr).destRect.top)
X				/ (*ePtr).lineHeight);
X}
X
X
X/*
X	Return the line number that the caret (or the beginning of
X	the currently selected text) is in.  Value returned is in
X	the range 0..(**editTE).nLines.  If = (**editTE).nLines, the
X	caret is past the last line.  The only special case to watch out
X	for is when the caret is at the very end of the text.  If the
X	last character is not a carriage return, then the caret is on
X	the (nLines-1)th line, not the (nLines)th line.
X
X	(This really should do a binary search for speed.)
X*/
X
Xstatic LineWithCaret ()
X{
Xregister int	i;
Xregister int	nLines;
Xregister int	teLength;
Xregister int	selStart;
Xregister int	lineStart;
X
X	selStart = (**editTE).selStart;
X	nLines = (**editTE).nLines;
X	teLength = (**editTE).teLength;
X
X	if (selStart == teLength)
X	{
X		if (teLength != 0 && (*((**editTE).hText))[teLength-1] != cr)
X			return (nLines - 1);
X		return (nLines);
X	}
X
X	for (i = 0; /* empty */; ++i)
X	{
X		if ((lineStart = (**editTE).lineStarts[i]) >= selStart)
X		{
X			if (lineStart != selStart)
X				--i;
X			return (i);
X		}
X	}
X}
X
X
X/*
X	Return the number of the last displayable line.  That's one
X	more than nLines if the text is empty or the last character
X	is a carriage return.
X*/
X
Xstatic LastLine ()
X{
Xregister int	nLines;
Xregister int	teLength;
X
X	nLines = (**editTE).nLines;
X	teLength = (**editTE).teLength;
X
X	if (teLength == 0 || (*((**editTE).hText))[teLength-1] == cr)
X		nLines++;
X	return (nLines);
X}
X
X
X/*
X	Set the maximum value of the scroll bar.  It's set so that if
X	there's more text than fits in the window, the bottom line can
X	be scrolled up at least a little below the bottom of the window.
X
X	The shenanigans with topLines and scrollableLines have to do with
X	the case where there may be less text than fills the window, but
X	most of it's scrolled off the top.  This can happen when you
X	scroll a bunch of stuff up, then delete everything visible in
X	the window.
X*/
X
Xstatic SetScrollMax ()
X{
Xregister int	topLines;
Xregister int	scrollableLines;
Xregister int	max;
X
X	topLines = LinesOffTop ();
X	scrollableLines = LastLine () - visLines;
X	max = (topLines > scrollableLines ? topLines : scrollableLines);
X
X	if (max < 0)
X		max = 0;
X
X	if (max != GetCtlMax (editScroll))
X	{
X		SetCtlMax (editScroll, max);
X		HiliteControl (editScroll, max > 0 ? 0 : 255);
X	}
X}
X
X
X/*
X	Set scroll bar current value (but only if it's different than
X	the current value, to avoid needless flashing).
X*/
X
Xstatic SetScrollValue (value)
Xint		value;
X{
X	if (GetCtlValue (editScroll) != value)
X		SetCtlValue (editScroll, value);
X}
X
X
X/*
X    Scroll to the correct position.  lDelta is the
X    amount to CHANGE the current scroll setting by.
X*/
X
Xstatic ScrollText (lDelta)
Xint		lDelta;
X{
Xregister int	topVisLine;
Xregister int	newTopVisLine;
X
X	topVisLine = LinesOffTop ();
X    newTopVisLine = topVisLine + lDelta;
X    if (newTopVisLine < 0)					/* clip to range */
X    	newTopVisLine = 0;
X    if (newTopVisLine > GetCtlMax (editScroll))
X		newTopVisLine = GetCtlMax (editScroll);
X    SetScrollValue (newTopVisLine);
X    TEScroll (0, (topVisLine-newTopVisLine )*(**editTE).lineHeight, editTE);
X}
X
X
X/*
X	Scroll to home position without redrawing.
X*/
X
Xstatic ScrollToHome ()
X{
XRect				r;
X
X	r = (**editTE).destRect;
X	OffsetRect (&r, 0, 2 - r.top);
X	(**editTE).destRect = r;
X}
X
X/*
X	ClikLoop proc for autoscrolling text when the mouse is dragged out
X	of the text view rectangle.
X
X	The clipping region has to be set to include the scroll bar,
X	because whenever this proc is called, TE has the region set down
X	to the view rectangle - if it's not reset, changes to the scroll
X	bar will not show up!
X*/
X
Xstatic pascal Boolean AutoScroll ()
X{
XPoint	p;
XRect	r;
X
X	SaveClipRgn ();
X	ClipRect (&editWind->portRect);
X	GetMouse (&p);
X	r = (**editTE).viewRect;
X	if (p.v < r.top)
X		ScrollText (-1);
X	else if (p.v > r.bottom)
X		ScrollText (1);
X	RestoreClipRgn ();
X	return (true);			/* true = 'keep tracking mouse' */
X}
X
X
X/*
X	Filter proc for tracking mousedown in scroll bar.  The code for
X	the part originally hit is shoved into the control's reference
X	value by Mouse() before this is called.
X
X	I suspect odd scrolling may occur for hits in paging regions if
X	the window is allowed to size such that less than two lines show.
X*/
X
Xstatic pascal void TrackScroll (theScroll, partCode)
XControlHandle	theScroll;
Xint				partCode;
X{
Xregister int	lDelta;
X
X    if (partCode == GetCRefCon (theScroll))	/* still in same part? */
X    {
X        switch (partCode)
X        {
X            case inUpButton: lDelta = -1; break;
X            case inDownButton: lDelta = 1; break;
X            case inPageUp: lDelta = -(visLines - 1); break;
X            case inPageDown: lDelta = visLines - 1; break;
X        }
X        ScrollText (lDelta);
X    }
X}
X
X
X/*
X	Set the scroll bar properly and adjust the text in the
X	window so that the line containing the caret is visible.
X	If the line with the caret if more than a line outside of
X	the viewRect, try to place it in the middle of the window.
X
X	Yes, it is necessary to SetScrollMax at the end.
X*/
X
Xstatic AdjustDisplay ()
X{
Xregister int	caretLine;
Xregister int	topVisLine;
Xregister int	d;
X
X	SetScrollMax ();
X	caretLine = LineWithCaret ();
X	topVisLine = LinesOffTop ();
X	if ((d = caretLine - topVisLine) < 0)
X		ScrollText (d == -1 ? -1 : d - visLines / 2);
X	else if (( d = caretLine - (topVisLine + visLines - 1)) > 0)
X		ScrollText (d == 1 ? 1 : d + visLines / 2);
X	else
X		SetScrollValue (topVisLine);
X	SetScrollMax ();	/* might have changed from scrolling */
X}
X
X
X/*
X	Overhaul the entire display.  This is called for major
X	catastrophes, such as resizing the window, or changes to
X	the word wrap style.  It makes sure the view and
X	destination rectangles are sized properly, and that the bottom
X	line of text never scrolls up past the bottom line of the
X	window, if there's enough to fill the window, and that the
X	scroll bar max and current values are set properly.
X
X	Resizing the dest rect just means resetting the right edge
X	(the top is NOT reset), since text might be scrolled off the
X	top (i.e., destRect.top != 0).
X
X	Doesn't redraw the control, though!
X*/
X
Xstatic OverhaulDisplay (showCaret, recalc)
XBoolean	showCaret;
XBoolean	recalc;
X{
XRect			r;
X
X	r = (**editTE).viewRect;	/* erase current viewRect */
X	EraseRect (&r);
X	SetEditRect ();				/* recalculate editing rects */
X	if (recalc)
X		TECalText (editTE);			/* recalculate line starts */
X	visLines = ((**editTE).viewRect.bottom - (**editTE).viewRect.top)
X					/ (**editTE).lineHeight;
X
X# ifndef	singleEdit
X	(**editInfo).visLines = visLines;
X# endif
X
X/*
X	If there is text, but none of it is visible in the window
X	(it's all scrolled off the top), pull some down.
X*/
X
X	if (showCaret)
X		AdjustDisplay ();
X	else
X		SetScrollMax ();
X	r = (**editTE).viewRect;
X	TEUpdate (&r, editTE);
X}
X
X
X/* ---------------------------------------------------------------- */
X/*						Window Handler Routines						*/
X/* ---------------------------------------------------------------- */
X
X
X/*
X	Handle mouse clicks in window.  The viewRect is never tested
X	directly, because if it were, clicks along the top, left and
X	bottom edges of the window wouldn't register.
X*/
X
Xstatic Mouse (thePt, t, mods)
XPoint	thePt;
Xlong	t;
Xint		mods;
X{
Xregister int	thePart;
Xregister int	oldCtlValue;
X
X	SyncGlobals (nil);		/* sync to current port */
X
X	if ((thePart = TestControl (editScroll, thePt)) == inThumb)
X	{
X		oldCtlValue = GetCtlValue (editScroll);
X		if (TrackControl (editScroll, thePt, nil) == inThumb)
X			ScrollText (GetCtlValue (editScroll) - oldCtlValue);
X	}
X	else if (thePart != 0)
X	{
X		SetCRefCon (editScroll, (long) thePart);
X		(void) TrackControl (editScroll, thePt, &TrackScroll);
X	}
X	else if (PtInText (thePt))
X	{
X		TEClick (thePt, (mods & shiftKey) != 0, editTE);
X	}
X
X	SetScrollMax ();
X}
X
X
X/*
X    Handle key clicks in window
X*/
X
Xstatic Key (c, mods)
Xchar	c;
Xint		mods;
X{
X	SyncAllGlobals (nil);		/* sync to current port */
X
X	if (c != enter)
X		TEKey (c, editTE);
X	AdjustDisplay ();
X	SetDirty (true);
X	if (eKey != nil)	/* report event to the host */
X		(*eKey) ();
X}
X
X
X/*
X	When the window comes active, highlight the scroll bar appropriately.
X	When the window is deactivated, un-highlight the scroll bar.
X	Redraw the grow box in any case.  Set the cursor (DoCursor avoids
X	changing it from an ibeam to an arrow back to an ibeam, in the case
X	where one edit window is going inactive and another is coming
X	active).
X
X	Report the event to the host.
X*/
X
Xstatic Activate (active)
XBoolean	active;
X{
X	SyncAllGlobals (nil);		/* sync to current port */
X
X	DrawGrowBox ();
X	if (active)
X	{
X		TEActivate (editTE);
X		HiliteControl (editScroll, GetCtlMax (editScroll) > 0 ? 0 : 255);
X	}
X	else
X	{
X		TEDeactivate (editTE);
X		HiliteControl (editScroll, 255);
X	}
X	DoCursor (iBeamCursor);
X	if (eActivate != nil)	/* report event to the host */
X		(*eActivate) (active);
X}
X
X
X/*
X	Close box was clicked.  If user specified notify proc, call it.
X	Otherwise do default close operation (ask about saving if dirty,
X	etc.).
X*/
X
Xstatic Close ()
X{
X	SyncAllGlobals (nil);		/* sync to current port */
X
X	if (eClose != nil)
X		(*eClose) ();
X	else
X		(void) EWindowClose (editWind);
X}
X
X
X/*
X	Update window.  The update event might be in response to a
X	window resizing.  If so, move and resize the scroll bar.
X	The ValidRect call is done because the HideControl adds the
X	control bounds box to the update region - which would generate
X	another update event!  Since everything gets redrawn below,
X	the ValidRect is used to cancel the update.
X*/
X
Xstatic Update (resized)
XBoolean	resized;
X{
XRect			r;
X
X	SyncGlobals (nil);		/* sync to current port */
X
X	r = editWind->portRect;
X	EraseRect (&r);
X	if (resized)
X	{
X		HideControl (editScroll);
X		r = (**editScroll).contrlRect;
X		ValidRect (&r);
X		CalcScrollRect (&r);
X		SizeControl (editScroll, 16, r.bottom - r.top);
X		MoveControl (editScroll, r.left, r.top);
X		OverhaulDisplay (false, (**editTE).crOnly >= 0);
X		ShowControl (editScroll);
X	}
X	else
X	{
X		OverhaulDisplay (false, false);
X		DrawControls (editWind);	/* redraw scroll bar */
X	}
X
X	DrawGrowBox ();
X}
X
X
X/*
X	Remove the edit window from the list, and dispose of it.
X	This is called by SkelRmveWind, not directly by user program.
X
X	At this point it's too late to back out if any changes have been
X	made to the text.
X
X	Since the clobber procedure is never called except for real edit
X	windows, and since the list must therefore be non-empty, it is
X	not necessary to check the legality of the window or that the
X	window's in the list.
X*/
X
Xstatic Clobber ()
X{
X# ifndef	singleEdit
Xregister EIHandle	h, h2;
X# endif
X
X	SyncGlobals (nil);					/* sync to current port */
X
X# ifndef	singleEdit
X
X	if ((**ewList).editWind == editWind)	/* is it the first window in list? */
X	{
X		h2 = ewList;
X		ewList = (**ewList).eNext;
X	}
X	else
X	{
X		for (h = ewList; h != nil; h = h2)
X		{
X			h2 = (**h).eNext;
X			if ((**h2).editWind == editWind)	/* found it */
X			{
X				(**h).eNext = (**h2).eNext;
X				break;
X			}
X		}
X	}
X	DisposHandle (h2);			/* get rid of information structure */
X
X# endif
X
X	TEDispose (editTE);			/* toss text record */
X	DisposeWindow (editWind);	/* disposes of scroll bar, too */
X	editWind = nil;
X	DoCursor (iBeamCursor);
X}
X
X
X/*
X	Blink the caret and make sure the cursor's an i-beam when it's
X	in the non-scrollbar part of the window.
X*/
X
Xstatic Idle ()
X{
X
X	SyncGlobals (nil);
X	TEIdle (editTE);			/* blink that caret! */
X	DoCursor (iBeamCursor);
X}
X
X
X/* ---------------------------------------------------------------- */
X/*						Internal File Routines						*/
X/* ---------------------------------------------------------------- */
X
X
Xstatic ErrMesg (s)
XStringPtr	s;
X{
X	(void) FakeAlert (s, "\p", "\p", "\p", 1, 1, "\pOK", "\p", "\p");
X}
X
X
X/*
X	Save the contents of the edit window.  If there is no file bound
X	to the window, ask for a file name.  If askForFile is true, ask
X	for a name even if the window is currently bound to a file.  If
X	bindToFile is true, bind the window to the file written to (if
X	that's different than the currently bound file), and clear the
X	window's dirty flag.
X
X	Return true if the file was written without error.  Return false
X	if (a) user was asked for name and clicked Cancel (b) there was
X	some error writing the file.  In the latter case, the window is
X	not bound to any new name given by user.
X
X	Always returns false if the window isn't an edit window.  This
X	simplifies EWindowSave, EWindowSaveAs, EWindowSaveCopy.  (They
X	don't do the test.)
X*/
X
Xstatic Boolean SaveFile (theWind, askForFile, bindToFile)
XWindowPtr	theWind;
XBoolean		askForFile;
XBoolean		bindToFile;
X{
Xint		f;
XFInfo	fndrInfo;	/* finder info */
XSFReply	tmpFile;
XHandle	hText;
Xlong	count;
XOSErr	result;
XBoolean	haveNewFile = false;
X
X	if (!IsEWindow (theWind))
X		return (false);
X
X	SyncAllGlobals (theWind);
X	if (bound == false || askForFile)
X	{
X		SFPutFile (dlogWhere, "\pSave file as:", editFile.fName,
X						nil, &tmpFile);
X		if (!tmpFile.good)
X			return (false);
X		else
X		{
X			haveNewFile = true;
X			if (GetFInfo (tmpFile.fName, tmpFile.vRefNum, &fndrInfo)
X					== noErr) /* exists */
X			{
X				if (fndrInfo.fdType != 'TEXT')
X				{
X					ErrMesg ("\pNot a TEXT File");
X					return (false);
X				}
X			}
X			else	/* doesn't exist.  create it. */
X			{
X				if (Create (tmpFile.fName, tmpFile.vRefNum,
X							creator, 'TEXT') != noErr)
X				{
X					ErrMesg ("\pCan't Create");
X					return (false);
X				}
X			}
X		}
X	}
X	
X	if (FSOpen (tmpFile.fName, tmpFile.vRefNum, &f) != noErr)
X		ErrMesg ("\pCan't Open");
X	else
X	{
X		DoCursor (watchCursor);
X		(void) SetFPos (f, fsFromStart, 0L);
X		hText = (**editTE).hText;
X		HLock (hText);
X		count = (**editTE).teLength;
X		result = FSWrite (f, &count, *hText);
X		(void) GetFPos (f, &count);
X		(void) SetEOF (f, count);
X		(void) FSClose (f);
X		(void) FlushVol (nil, tmpFile.vRefNum);
X		HUnlock (hText);
X		DoCursor (iBeamCursor);
X		if (result == noErr)
X		{
X			if (bindToFile)
X			{
X				SetDirty (false);
X				if (haveNewFile)	/* name different than current */
X				{
X					SetWTitle (editWind, tmpFile.fName);
X
X# ifdef	singleEdit
X					bound = true;
X					editFile = tmpFile;
X# else
X					(**editInfo).bound = true;
X					(**editInfo).editFile = tmpFile;
X# endif
X
X				}
X			}
X			return (true);
X		}
X		ErrMesg ("\pWrite error!");
X	}
X	return (false);
X}
X
X
X/*
X	Revert to version of file saved on disk.  Doesn't check whether
X	the window's really bound to a file or not, doesn't ask whether
X	to really revert if the window's dirty, does no redrawing, etc.
X	Just reports whether the file was read in successfully.
X*/
X
Xstatic Boolean Revert ()
X{
XBoolean	result = false;
Xint		f;
Xlong	len;
XHandle	h;
X
X	DoCursor (watchCursor);
X	if (FSOpen (editFile.fName, editFile.vRefNum, &f) != noErr)
X		ErrMesg ("\pCouldn't open file");
X	else
X	{
X		(void) GetEOF (f, &len);
X		if (len >= 32000)
X			ErrMesg ("\pFile is too big");
X		else
X		{
X			h = TEGetText (editTE);
X			SetHandleSize (h, len);
X			HLock (h);
X			(void) FSRead (f, &len, *h);
X			HUnlock (h);
X			(**editTE).teLength = len;
X			TESetSelect (0L, 0L, editTE);	/* set caret at start */
X			result = true;
X			SetDirty (false);
X		}
X		(void) FSClose (f);
X	}
X	DoCursor (iBeamCursor);
X	return (result);
X}
X
X
X/* ------------------------------------------------------------ */
X/*			Lowest-level Interface (Public) Routines			*/
X/* ------------------------------------------------------------ */
X
X
X/*
X	Return true/false to indicate whether the window is really an
X	edit window.
X*/
X
XBoolean IsEWindow (theWind)
XWindowPtr	theWind;
X{
X# ifdef	singleEdit
X	return (theWind == editWind && editWind != nil);
X# else
X	return (GetEInfo (theWind) != nil);
X# endif
X}
X
X
X/*
X	Return true/false to indicate whether the text associated with
X	the window has been changed since the last save/revert (or since
X	created, if not bound to file).
X*/
X
XBoolean IsEWindowDirty (theWind)
XWindowPtr	theWind;
X{
X# ifndef	singleEdit
Xregister EIHandle	eInfo;
X	if ((eInfo = GetEInfo (theWind)) != nil)
X		return ((**eInfo).dirty);
X# else
X	if (IsEWindow (theWind))
X		return (dirty);
X# endif
X	return (false);
X}
X
X
X/*
X	Return a handle to the TextEdit record associated with the edit
X	window, or nil if it's not an edit window
X*/
X
XTEHandle GetEWindowTE (theWind)
XWindowPtr	theWind;
X{
X# ifndef	singleEdit
Xregister EIHandle	eInfo;
X	if ((eInfo = GetEInfo (theWind)) != nil)
X		return ((**eInfo).editTE);
X# else
X	if (IsEWindow (theWind))
X		return (editTE);
X# endif
X	return (nil);
X}
X
X
X/*
X	Return true/false depending on whether the editor is bound to
X	a file or not, and a copy of the file info in the second
X	argument.  Pass nil for fileInfo if only want the return status.
X	Returns false if it's not an edit window.
X*/
X
XBoolean GetEWindowFile (theWind, fileInfo)
XWindowPtr	theWind;
XSFReply		*fileInfo;
X{
X# ifndef	singleEdit
Xregister EIHandle	eInfo;
X	if ((eInfo = GetEInfo (theWind)) != nil)
X	{
X		if (fileInfo != nil)
X			*fileInfo = (**eInfo).editFile;
X		return ((**eInfo).bound);
X	}
X# else
X	if (IsEWindow (theWind))
X	{
X		if (fileInfo != nil)
X			*fileInfo = editFile;
X		return (bound);
X	}
X# endif
X	return (false);
X}
X
X
X/* ---------------------------------------------------------------- */
X/*					Interface Display Routines						*/
X/* ---------------------------------------------------------------- */
X
X
X/*
X	Install event notification procedures for an edit window.
X*/
X
XSetEWindowProcs (theWind, pKey, pActivate, pClose)
XWindowPtr	theWind;
XProcPtr		pKey;
XProcPtr		pActivate;
XProcPtr		pClose;
X{
X# ifndef	singleEdit
Xregister EIHandle	eInfo;
X# endif
X
X	if (theWind == nil)			/* reset window creation defaults */
X	{
X		e_key = pKey;
X		e_activate = pActivate;
X		e_close = pClose;
X		return;
X	}
X
X# ifndef	singleEdit
X
X	if ((eInfo = GetEInfo (theWind)) != nil)
X	{
X		(**eInfo).eKey = pKey;
X		(**eInfo).eActivate = pActivate;
X		(**eInfo).eClose = pClose;
X	}
X
X# else
X
X	if (IsEWindow (theWind))
X	{
X		eKey = pKey;
X		eActivate = pActivate;
X		eClose = pClose;
X	}
X
X# endif
X
X}
X
X
X/*
X	Change the text display characteristics of an edit window
X	and redisplay it.
X
X	Scroll to home position before overhauling, because although
X	the overhaul sets the viewRect to display an integral number
X	of lines, there's no guarantee that the destRect offset will
X	also be integral except at home position.  Clipping is set to
X	an empty rect so the scroll doesn't show.
X*/
X
XSetEWindowStyle (theWind, font, size, wrap, just)
XWindowPtr	theWind;
Xint			font;
Xint			size;
Xint			wrap;
Xint			just;
X{
XGrafPtr				savePort;
XFontInfo			f;
Xregister TEHandle	te;
XRect				r;
Xint					oldWrap;
X
X	if (theWind == nil)			/* reset window creation defaults */
X	{
X		e_font = font;
X		e_size = size;
X		e_wrap = wrap;
X		e_just = just;
X		return;
X	}
X
X	if (IsEWindow (theWind))
X	{
X		GetPort (&savePort);
X		SyncGlobals (theWind);	/* sync and set port */
X		te = editTE;
X		ScrollToHome ();
X
X		oldWrap = (**te).crOnly;
X		(**te).crOnly = wrap;	/* set word wrap */
X		TESetJust (just, te);	/* set justification */
X
X		TextFont (font);	 	/* set the font and point size */
X		TextSize (size);		/* of text record */
X		GetFontInfo (&f);
X		(**te).lineHeight = f.ascent + f.descent + f.leading;
X		(**te).fontAscent = f.ascent;
X		(**te).txFont = font;
X		(**te).txSize = size;
X
X		OverhaulDisplay (true, (oldWrap >= 0 || wrap >= 0));
X
X		SetPort (savePort);
X	}
X}
X
X
X/*
X	Redo display.  Does not save current port.  This is used by hosts
X	that mess with the text externally to TransEdit.  The arguments
X	determine whether the text is scrolled to show the line with the
X	caret, whether the lineStarts are recalculated, and whether the
X	text should be marked dirty or not.
X*/
X
XEWindowOverhaul (theWind, showCaret, recalc, dirty)
XWindowPtr	theWind;
XBoolean		showCaret;
XBoolean		recalc;
XBoolean		dirty;
X{
X	if (IsEWindow (theWind))
X	{
X		SyncGlobals (theWind);
X		OverhaulDisplay (showCaret, recalc);
X		DrawControls (editWind);
X		SetDirty (dirty);
X	}
X}
X
X
X/* ---------------------------------------------------------------- */
X/*						Menu Interface Routine						*/
X/* ---------------------------------------------------------------- */
X
X
X/*
X	Do Edit menu selection.  This is only valid if an edit
X	window is frontmost.
X*/
X
XEWindowEditOp (item)
Xint		item;
X{
X
X	if (!IsEWindow (FrontWindow ()))
X		return;				/* host messed up */
X
X	SyncGlobals (FrontWindow ());
X
X	switch (item)
X	{
X
X/*
X	cut selection, put in TE Scrap, clear clipboard and put
X	TE scrap in it
X*/
X		case cut:
X		{
X			TECut (editTE);
X			(void) ZeroScrap ();
X			(void) TEToScrap ();
X			break;
X		}
X/*
X	copy selection to TE Scrap, clear clipboard and put
X	TE scrap in it
X*/
X		case copy:
X		{
X			TECopy (editTE);
X			(void) ZeroScrap ();
X			(void) TEToScrap ();
X			break;
X		}
X/*
X	get clipboard into TE scrap, put TE scrap into edit record
X*/
X		case paste:
X		{
X			(void) TEFromScrap ();
X			TEPaste (editTE);
X			break;
X		}
X/*
X	delete selection without putting into TE scrap or clipboard
X*/
X		case clear:
X		{
X			(void) TEDelete (editTE);
X			break;
X		}
X
X	}
X	AdjustDisplay ();
X	SetDirty (true);
X}
X
X
X/* ---------------------------------------------------------------- */
X/*						Interface File Routines						*/
X/* ---------------------------------------------------------------- */
X
X
X/*
X	Set file creator for any files created by TransEdit
X*/
X
XSetEWindowCreator (creat)
XOSType	creat;
X{
X	creator = creat;
X}
X
X
X
X/*
X	Save the contents of the given window
X*/
X
XBoolean EWindowSave (theWind)
XWindowPtr	theWind;
X{
X	return (SaveFile (theWind,	/* window to save */
X					  false,	/* don't ask for file if have one */
X					  true));	/* bind to new file if one given */
X}
X
X
X/*
X	Save the contents of the given window under a new name
X	and bind to that name.
X*/
X
XBoolean EWindowSaveAs (theWind)
XWindowPtr	theWind;
X{
X	return (SaveFile (theWind,	/* window to save */
X					  true,		/* ask for file even if have one */
X					  true));	/* bind to new file if one given */
X}
X
X
X/*
X	Save the contents of the given window under a new name, but
X	don't bind to the name.
X*/
X
XBoolean EWindowSaveCopy (theWind)
XWindowPtr	theWind;
X{
X	return (SaveFile (theWind,	/* window to save */
X					  true,		/* ask for file even if have one */
X					  false));	/* don't bind to file */
X}
X
X
X/*
X	Close the window.  If it's dirty and is either bound to a file
X	or (if not bound) has some text in it, ask about saving it first,
X	giving user option of saving changes, tossing them, or
X	cancelling altogether.
X
X	Return true if the file was saved and the window closed, false if
X	user cancelled or there was an error.
X*/
X
XBoolean EWindowClose (theWind)
XWindowPtr	theWind;
X{
X	if (IsEWindow (theWind) == false)
X		return (false);
X
X	SyncAllGlobals (theWind);
X	if ( (bound || (**editTE).teLength > 0) && dirty)
X	{
X		switch (FakeAlert ("\pSave changes to \"", editFile.fName,
X				"\p\"?", "\p", 3, 3,
X				"\pCancel", "\pDiscard", "\pSave"))	/* ask whether to save */
X		{
X
X			case 1:			/* cancel Close */
X				return (false);
X
X			case 2:			/* toss changes */
X				break;
X
X			case 3:
X				if (SaveFile (editWind,	/* window to save */
X							  false,	/* don't ask for name */
X							  false)	/* don't bind to name */
X						== false)
X					return (false);	/* cancelled or error - cancel Close */
X				break;
X		}
X	}
X	SkelRmveWind (editWind);
X	return (true);
X}
X
X
X/*
X	Revert to saved version of file on disk.  theWind must be an edit
X	window, and must be bound to a file.  Returns false if one of these
X	conditions is not met, or if they are met but there was an error
X	reading the file.
X
X	The window need not be dirty, but if it is, the user is asked
X	whether to really revert.
X*/
X
XBoolean EWindowRevert (theWind)
XWindowPtr	theWind;
X{
X	if (!IsEWindow (theWind))
X		return (false);
X	SyncAllGlobals (theWind);
X	if (!bound)
X		return (false);		/* no file to revert to */
X	if (dirty)
X	{
X		if (FakeAlert ("\p\"", editFile.fName,
X				"\p\" has been changed.  Really revert?",
X				"\p", 2, 1, "\pCancel", "\pRevert", "\p") == 1)
X			return (false);
X	}
X	if (Revert () == false)
X		return (false);
X	ScrollToHome ();
X	OverhaulDisplay (true, true);
X	DrawControls (editWind);
X	ValidRect (&editWind->portRect);
X	return (true);
X}
X
X
X/* ---------------------------------------------------------------- */
X/*			Interface Initialization/Termination Routines			*/
X/* ---------------------------------------------------------------- */
X
X
X/*
X	Initialize the window and associated data structures.
X	Return window pointer or nil if some sort of error.
X
X	Preserves the current port.
X*/
X
XWindowPtr NewEWindow (bounds, title, visible, behind,
X							goAway, refNum, bindToFile)
XRect		*bounds;
XStringPtr	title;
XBoolean		visible;
XWindowPtr	behind;
XBoolean		goAway;
Xlong		refNum;
XBoolean		bindToFile;
X{
XGrafPtr		savePort;
XRect		r;
XOSType		type = 'TEXT';
XStr255		s, s2;
XStringPtr	tPtr;
X
X# ifndef	singleEdit
Xregister EIHandle	eInfo;
X# endif
X
X# ifdef	singleEdit
X
X	if (editWind != nil)	/* allow only one window at a time */
X		return (nil);
X
X# endif
X
X/*
X	If supposed to bind to file, ask for name.  Return without doing
X	anything if Cancel button clicked.
X*/
X
X	if (bindToFile)
X	{
X		SFGetFile (dlogWhere, "\p", nil, 1, &type, nil, &editFile);
X		if (!editFile.good)
X			return (nil);
X	}
X	bound = bindToFile;
X
X/*
X	Create window and install handler.  Set window title:  If window is
X	to be bound to file, use name of file.  Otherwise use any title that
X	was passed in.  If nil was passed, use a default name ("Untitled nnn").
X	Also copy the name into the file info structure even if the window is
X	unbound, because the Save operations expect to find it there as the
X	most likely name to use if the window is untitled.
X
X	Save and restore port, because it gets reset by the rest of the
X	initialization code.
X*/
X
X	if (bound)
X		tPtr = editFile.fName;
X	else
X	{
X		if (title != nil)
X			tPtr = title;
X		else
X		{
X
X# ifndef	singleEdit
X			BlockMove ("\pUntitled ", s, 10L);
X			NumToString ((long) ++windID, s2);
X			BlockMove (&s2[1], &s[10], (long) s2[0]);
X			s[0] += s2[0];
X			tPtr = s;
X# else
X			tPtr = (StringPtr) "\pUntitled";
X# endif
X
X		}
X		BlockMove (tPtr, editFile.fName, (long) (tPtr[0] + 1));
X	}
X
X	editWind = NewWindow (nil, bounds, tPtr, false, documentProc,
X								behind, goAway, refNum);
X
X	GetPort (&savePort);
X	SkelWindow (editWind,	/* the window */
X				Mouse,		/* mouse click handler */
X				Key,		/* key click handler */
X				Update,		/* window updating procedure */
X				Activate,	/* window activate/deactivate procedure */
X				Close,		/* window close procedure */
X				Clobber,	/* window disposal procedure */
X				Idle,		/* idle proc */
X				true);		/* idle only when frontmost */
X
X
X/*
X	Build the scroll bar.
X*/
X
X	CalcScrollRect (&r);
X	editScroll = NewControl (editWind, &r, "\p", true, 0, 0, 0,
X								scrollBarProc, 0L);
X
X
X
X/*
X	Create the TE record used for text display.  Use default
X	characteristics.
X*/
X
X	GetEditRect (&r);
X	editTE = TENew (&r, &r);
X	SetClikLoop (AutoScroll, editTE);			/* set autoscroll proc */
X
X
X# ifndef	singleEdit
X
X/*
X	Get new information structure, attach to list of known edit
X	windows.
X*/
X
X	eInfo = New (EditInfo);
X	editInfo = eInfo;
X	(**eInfo).eNext = ewList;
X	ewList = eInfo;
X	(**eInfo).editWind = editWind;
X	(**eInfo).scroll = editScroll;
X	(**eInfo).editTE = editTE;
X	(**eInfo).bound = bound;
X	(**eInfo).editFile = editFile;
X
X# endif
X
X/*
X	Install default event notification procedures, font characteristics.
X*/
X
X	SetEWindowProcs (editWind, e_key, e_activate, e_close);
X	SetEWindowStyle (editWind, e_font, e_size, e_wrap, e_just);
X	SetDirty (false);
X
X/*
X	If supposed to read file, do so.  Check the return value of
X	Revert and toss the window if there was an error.
X*/
X
X	if (bindToFile && Revert () == false)
X	{
X		SkelRmveWind (editWind);
X		SetPort (savePort);
X		return (nil);
X	}
X
X/*
X	Show window if specified as visible, and return a pointer to it.
X*/
X
X	SyncGlobals (editWind);
X	OverhaulDisplay (true, true);
X	if (visible)
X		ShowWindow (editWind);
X	SetPort (savePort);
X	return (editWind);
X}
X
X
X/*
X	Look through the list of windows, shutting down all the edit
X	windows.  If any window is dirty, ask user about saving it first.
X	If the user cancels on any such request, ClobberEWindows returns
X	false.  If all edit windows are shut down, return true.  It is
X	then safe for the host to exit.
X
X	When a window *is* shut down, have to start looking through the
X	window list again, since theWind no longer points anywhere
X	meaningful.
X*/
X
XBoolean ClobberEWindows ()
X{
XWindowPtr	theWind;
X
X	for (;;)
X	{
X		for (theWind = FrontWindow ();
X				theWind != nil;
X					theWind = (WindowPtr) ((WindowPeek) theWind)->nextWindow)
X		{
X			if (IsEWindow (theWind))
X				break;
X		}
X		if (theWind == nil)
X			return (true);		/* all edit windows are shut down */
X
X		if (theWind != FrontWindow ())
X		{
X			SelectWindow (theWind);
X			ShowWindow (theWind);
X			EWindowOverhaul (theWind, false, false, IsEWindowDirty (theWind));
X			SetPort (theWind);
X			ValidRect (&theWind->portRect);
X		}
X
X		if (EWindowClose (theWind) == false)
X			return (false);		/* cancel or error */
X	}
X}
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > TransEdit/FakeAlert.c
X# include	<DialogMgr.h>
X
X
X# define	nil		(0L)
X
X
X/*
X	In-memory item list for dialog with four items:
X
X	1	"^0^1^2^3" (static text)
X	2	Button 1
X	3	Button 2
X	4	Button 3
X
X	The caller of FakeAlert passes the four strings that are to be
X	substituted into the first item, the number of buttons that
X	should be used, and the titles to put into each button.
X	A copy of the item list is hacked to use the right number of
X	buttons.
X
X	Thanks to Erik Kilk and Jason Haines.  Some of the stuff to do
X	this is modified from code they wrote.
X*/
X
X
Xstatic int	itemList [] =
X{
X	3,					/* max number of items - 1 */
X
X/*
X	statText item
X*/
X	0, 0,				/* reserve a long for item handle */
X	10, 27, 61, 225,	/* display rectangle */
X	((8+128) << 8) | 8,	/* 8 + 128 = statText (disabled), title 8 bytes long */
X	'^0', '^1',		/* ^0^1^2^3 */
X	'^2', '^3',
X
X/*
X	first button
X*/
X
X	0, 0,				/* reserve a long for item handle */
X	104, 140, 124, 210,	/* display rectangle */
X	(4 << 8) | 0,		/* 4 = pushButton, title is 0 bytes long*/
X
X/*
X	second button
X*/
X
X	0, 0,				/* reserve a long for item handle */
X	104, 30, 124, 100,	/* display rectangle */
X	(4 << 8) | 0,		/* 4 = pushButton, title is 0 bytes long */
X
X/*
X	third button
X*/
X
X	0, 0,				/* reserve a long for item handle */
X	72, 30, 92, 100,	/* display rectangle */
X	(4 << 8) | 0		/* 4 = pushButton, title is 0 bytes long */
X};
X
X
X/*
X	Set dialog button title and draw bold outline if makeBold true.
X	This must be done after the window is shown or else the bold
X	outline won't show up (which is probably the wrong way to do it).
X*/
X
Xstatic SetDControl (theDialog, itemNo, title, makeBold)
XDialogPtr	theDialog;
Xint			itemNo;
XStringPtr	title;
XBoolean		makeBold;
X{
XHandle		itemHandle;
Xint			itemType;
XRect		itemRect;
XPenState	pState;
X
X	GetDItem (theDialog, itemNo, &itemType, &itemHandle, &itemRect);
X	SetCTitle (itemHandle, title);
X	if (makeBold)
X	{
X		GetPenState (&pState);
X		PenNormal ();
X		PenSize (3, 3);
X		InsetRect (&itemRect, -4, -4);
X		FrameRoundRect (&itemRect, 16, 16);
X		SetPenState (&pState);
X	}
X}
X
X
X/*
X	Fake an alert, using an in-memory window and item list.
X	The message to be presented is constructed from the first
X	four arguments.  nButtons is the number of buttons to use,
X	defButton is the default button, the next three args are
X	the titles to put into the buttons.  The return value is
X	the button number (1..nButtons).  This must be interpreted
X	by the caller, since the buttons may be given arbitrary
X	titles.
X
X	nButtons should be between 1 and 3, inclusive.
X	defButton should be between 1 and nButtons, inclusive.
X*/
X
X
XFakeAlert (s1, s2, s3, s4, nButtons, defButton, t1, t2, t3)
XStringPtr	s1, s2, s3, s4;
Xint			nButtons;
Xint			defButton;
XStringPtr	t1, t2, t3;
X{
XGrafPtr		savePort;
Xregister DialogPtr	theDialog;
Xregister Handle		iListHandle;
XRect		bounds;
Xint			itemHit;
X
X	InitCursor ();
X	GetPort (&savePort);
X	iListHandle = NewHandle (512L);
X	HLock (iListHandle);
X	BlockMove (&itemList, *iListHandle, 512L);
X	((int *) *iListHandle)[0] = nButtons;	/* = number items - 1 */
X	SetRect (&bounds, 115, 80, 355, 220);
X	theDialog = NewDialog (nil, &bounds, "\p", false, dBoxProc, -1L,
X							false, 0L, iListHandle);
X
X	ParamText (s1, s2, s3, s4);		/* construct message */
X
X	SetPort (theDialog);
X	ShowWindow (theDialog);
X
X	switch (nButtons)				/* set button titles */
X	{
X		case 3:
X			SetDControl (theDialog, 4, t3, defButton == 3);
X			/* fall through... */
X		case 2:
X			SetDControl (theDialog, 3, t2, defButton == 2);
X			/* fall through... */
X		case 1:
X			SetDControl (theDialog, 2, t1, defButton == 1);
X	}
X
X/*
X	ModalDialog returns 1 if return/enter hit, which, since
X	the statText item is first, can be unambiguously
X	interpreted as "choose default".
X*/
X	ModalDialog (nil, &itemHit);
X	itemHit = (itemHit == 1 ? defButton : itemHit - 1);
X	HUnlock (iListHandle);
X	/*HPurge (iListHandle);*/
X	DisposDialog (theDialog);
X	SetPort (savePort);
X	return (itemHit);
X}
SHAR_EOF
exit