/*
 * - Check that SaveFile puts correct initial name in.
 * - Make sure that filename gets initialized properly in all cases.
 */

/*
 * TransEdit.c version 3.05 - TransSkel plug-in module supporting an
 * arbitrary number of generic edit windows.  Each window may be
 * bound to a file.
 * 
 * *** Requires FakeAlert.c for proper linking! ***
 *
 * TransSkel and TransEdit are public domain.  For more information,
 * contact:
 *
 * 			Paul DuBois
 * 			Wisconsin Regional Primate Research Center
 * 			1220 Capitol Court
 * 			Madison, WI  53715-1299  USA
 *
 * Internet:	dubois@primate.wisc.edu
 *		
 *
 * This version of TransEdit is written for THINK C 6.0.
 * THINK C is a trademark of:
 *
 * 			Symantec Corporation
 * 			10201 Torre Avenue
 * 			Cupertino, CA 95014  USA
 *
 * History
 * 08/25/86	Genesis.  Beta version.
 * 09/15/86
 * - Changed to allow arbitrary number of windows.
 *
 * 11/04/86 Release 1.0
 * - Added conditional stuff to allow compilation in single- or
 * multiple-window mode.
 *
 * 01/17/87 Release 1.01
 * - The window type when a new window is created is documentProc+8
 * now, so that the window will have a zoom box on a machine with
 * 128K ROMS.  Default file name in SaveFile() is initialized after
 * SyncAllGlobals() - fixing bug found by Owen Hartnett.
 *
 * 01/29/89 Release 2.0
 * - Converted to work with TransSkel 2.0.  2-byte and
 * 4-byte integer types are typedef'ed to Integer and LongInt to
 * ease porting.  Added SystemEdit() check to Edit menu handler.
 *
 * 16 Jun 92 Release 3.01
 * - Ported for TransSkel 3.00. Basically this amounted to adding
 * function prototypes.
 * - Use real curly quotes in FakeAlert() messages.
 *
 * 05 Jun 93 Release 3.02
 * - Conversion for THINK C 6.0.
 *
 * 08 Jun 93 Release 3.03
 * - Took out all the stuff to allow compiling to handle only a single
 * edit window.  The savings in bytes of object code is no longer worth
 * the extra source code complexity.  It's also unnecessary because I no longer
 * maintain a window list, since I ...
 * - Reimplemented linked list holding edit window data using TransSkel's
 * window property functions (new in TS 3.00).  The property type is
 * skelWPropEditWind, and the data value is a handle to the window data
 * structure.
 * - Took out all the "register" declarations.  The compiler's smart enough
 * now that they don't make any difference, so they're just clutter.
 * 05 Jul 93
 * - Redid FakeAlert() calls to correspond better to new button positioning.
 * (See FakeAlert.c remarks.)
 * - Fixed Activate() and Update() so that when a window goes inactive,
 * the scroll bar is hidden (and only the frame drawn).  Previously, the
 * scroll bar was just made inactive, which doesn't quite conform to the user
 * interface guidelines.
 * 18 Dec 93
 * - Untitled windows have "untitled" rather than "Untitled" in title bar,
 * and first untitled window isn't given a number, in accordance with Apple
 * guidelines.
 * - Replaced SFGetFile()/SFPutFile() with SFPGetFil()/SFPPutFile() so can
 * use SkelDlogFilter().
 * 21 Dec 93
 * - Use color grafports when available.
 * 04 Jan 94
 * - Undid Integer/LongInt type stuff back to short/long.
 *
 * 18 Jan 94 Release 3.04
 * - FakeAlert() code revamped.  See FakeAlert.c.
 * 19 Jan 94
 * - Window creation routines do better checks for allocation failures.
 * - Much rewriting to eliminate many global variables.  No more SyncGlobals().
 * 20 Jan 94
 * - Fixed bug with missing SkelRmveDlogFilter() after SFPGetFile() call.
 * 23 Jan 94
 * - Fixed bug in EWindowEditOp(); edit window doesn't need to be marked dirty
 * for Copy operation.
 *
 * 21 Feb 94 Release 3.05
 * - Updated for TransSkel 3.11.
 * - Converted interface to be Pascal-compatible.
 */

# include	"TransSkel1.h"

# include	"TransEdit1.h"


# define	normalHilite	0
# define	dimHilite		255

/*
 * Maximum size of file we'll attempt to read.  The value leaves
 * a little cushion against the TextEdit limit of 32767.
 */

# define	maxFileSize		32000


# define	WindowIsActive(w)	((WindowPeek) (w))->hilited


/*
 * New(TypeName) returns handle to new object, for any TypeName.
 * If there is insufficient memory, the result is nil.
 */

# define	New(type)	(type **) NewHandle ((Size) sizeof (type))


# define	enter			3
# define	cr				13


typedef enum			/* Edit menu item numbers */
{
	undo = 1,
	/* --- */
	cut = 3,
	copy,
	paste,
	clear		/* (it's ok if the host doesn't have this item) */
};


/*
 * Default values for edit window text display characteristics
 * and event notification procedures.  Used when new edit windows
 * are created.
 */

static short	e_font = kFontIDMonaco;			/* default font                 */
static short	e_size = 9;						/* default pointsize            */
static short	e_style = normal;				/* default style -- L. Tierney  */
static short	e_wrap = 0;						/* default word wrap (on)       */
static short	e_just = teJustLeft;			/* default justification        */
static TEditKeyProcPtr		e_key = nil;		/* default key procedure        */
static TEditActivateProcPtr	e_activate = nil;	/* default activation procedure */
static TEditCloseProcPtr	e_close = nil;		/* default close procedure      */
static TEditCloseProcPtr	e_idle = nil;		/* default idle procedure -- L. Tierney */


/*
 * Edit window document record.  A handle to a window's document
 * record is stored in the window's property list.
 */

typedef struct DocRecord	DocRecord, *DocPtr, **DocHandle;

struct DocRecord
{
	WindowPtr		editWind;	/* the edit window                   */
	Boolean			bound;		/* whether window is bound to file   */
	SFReply			editFile;	/* file it's bound to, if bound true */
	TEHandle		editTE;		/* window text                       */
	Boolean			dirty;		/* whether text modified since save  */
	ControlHandle	eScroll;	/* scroll bar                        */
	short			visLines;	/* # lines visible in window, max    */
	TEditKeyProcPtr			eKey;		/* key click notifier        */
	TEditActivateProcPtr	eActivate;	/* activate event notifier   */
	TEditCloseProcPtr		eClose;		/* close notifier            */
	TEditIdleProcPtr		eIdle;		/* idle action -- L. Tierney */
};


/*
 * Macros for accessing parts of a document record, given a document
 * handle.
 */

# define	DocWind(doc)			((**doc).editWind)
# define	DocBound(doc)			((**doc).bound)
# define	DocFile(doc)			((**doc).editFile)
# define	DocTE(doc)				((**doc).editTE)
# define	DocDirty(doc)			((**doc).dirty)
# define	DocScroll(doc)			((**doc).eScroll)
# define	DocVisLines(doc)		((**doc).visLines)
# define	DocKeyProc(doc)			((**doc).eKey)
# define	DocActivateProc(doc)	((**doc).eActivate)
# define	DocCloseProc(doc)		((**doc).eClose)
# define	DocIdleProc(doc)		((**doc).eIdle) /* L. Tierney */


static Point	dlogWhere = { 70, 100 };	/* GetFile/PutFile location */
static OSType	creator = 'TEDT';			/* default file creator */

static RgnHandle	clipRgn = (RgnHandle) nil;


/* -------------------------------------------------------------------- */
/*				Miscellaneous Internal (private) Routines				*/
/* -------------------------------------------------------------------- */


static void
ErrMesg (StringPtr s)
{
	(void) FakeAlert (s, "\p", "\p", "\p", 1, 1, 0, "\pOK", "\p", "\p");
}


/*
 * Save and restore the current window's clip region
 */

static void
SaveClipRgn (void)
{
	clipRgn = NewRgn ();
	GetClip (clipRgn);
}


static void
RestoreClipRgn (void)
{
	SetClip (clipRgn);
	DisposeRgn (clipRgn);
}


/*
 * Draw grow box in lower right hand corner of window.
 */

static void
DrawGrowBox (WindowPtr w)
{
Rect	r;

	SaveClipRgn ();
	r = w->portRect;
	r.left = r.right - 15;		/* draw only in corner */
	r.top = r.bottom - 15;
	ClipRect (&r);
	DrawGrowIcon (w);
	RestoreClipRgn ();
}


/*
 * Generate title for new untitled document.  First one is
 * unnumbered, others are numbered.
 */

static void
Untitled (StringPtr	name)
{
static long	num = 0;
Str255		numBuf;

	BlockMove ("\puntitled", name, 9L);
	if (++num > 1)
	{
		name[++name[0]] = ' ';
		NumToString (num, numBuf);
		BlockMove (&numBuf[1], &name[name[0]+1], (long) numBuf[0]);
		name[0] += numBuf[0];
	}
}


/* -------------------------------------------------------------------- */
/*			Lowest-level Internal (Private) Edit Window Routines		*/
/* -------------------------------------------------------------------- */


/*
 * Get document handle associated with edit window.
 * Return nil if window isn't a known edit window.
 */

static DocHandle
WindDocHandle (WindowPtr w)
{
SkelWindPropHandle	ph;
DocHandle	doc = (DocHandle) nil;

	if (w != (WindowPtr) nil)
	{
		ph = SkelGetWindProp (w, skelWPropEditWind);
		if (ph != (SkelWindPropHandle) nil)
			doc = (DocHandle) (**ph).skelWPropData;
	}
	return (doc);
}


/* -------------------------------------------------------------------- */
/*					Internal (private) Display Routines					*/
/* -------------------------------------------------------------------- */


/*
 * Calculate the dimensions of the editing rectangle for a window
 * (which is assumed to be the current port).  (The viewRect and
 * destRect are the same size.)  Assumes the port, text font and
 * text size are all set properly.  The viewRect is sized so that
 * an integral number of lines can be displayed in it, i.e., so that
 * a partial line never shows at the bottom.  If that's not done,
 * funny things can happen to the caret.
 */

static void
CalcEditRect (WindowPtr w, Rect *r)
{
FontInfo	f;
short		lineHeight;

	GetFontInfo (&f);
	lineHeight = f.ascent + f.descent + f.leading;
	*r = w->portRect;
	r->left += 4;
	r->right -= 17;			/* leave room for scroll bar + 2 */
	r->top += 2;
	r->bottom = r->top + ((r->bottom - r->top - 2) / lineHeight) * lineHeight;
}


/*
 * Calculate the dimensions of the scroll bar rectangle for a
 * window.  Make sure that the edges overlap the window frame and
 * the grow box.
 */

static void
CalcScrollRect (WindowPtr w, Rect *r)
{
	*r = w->portRect;
	++r->right;
	--r->top;
	r->left = r->right - 16;
	r->bottom -= 14;
}


/*
 * Return true if the mouse is in the non-scrollbar part of an
 * edit window.
 */

static Boolean
PtInText (WindowPtr w, Point pt)
{
Rect	r;

	r = w->portRect;
	r.right -= 15;
	return (PtInRect (pt, &r));
}


/*
 * Set the cursor appropriately.  If theCursor == iBeamCursor, check
 * that it's really in the text area of an edit window (and if not
 * set the cursor to an arrow instead).  Otherwise, set the cursor
 * to the given type (usually a watch).
 *
 * Pass -1 for theCursor to set the cursor to the arrow.
 */

static void
FixCursor (short theCursor)
{
WindowPtr	w;
GrafPtr		savePort;
Point		pt;

	if (theCursor == iBeamCursor)			/* check whether there's an edit */
	{										/* window in front and if so,    */
		theCursor = -1;						/* whether the cursor's in its   */
		w = FrontWindow ();					/* text area                     */
		if (IsEWindow (w))
		{
			GetPort (&savePort);
			SetPort (w);
			GetMouse (&pt);
			if (PtInText (w, pt))
				theCursor = iBeamCursor;
			SetPort (savePort);
		}
	}
	SetCursor (theCursor == -1 ? &arrow : *GetCursor (theCursor));
}


/*
 * Calculate the number of lines currently scrolled off
 * the top of an edit record.
 */

static short
LinesOffTop (TEHandle hTE)
{
	return (((**hTE).viewRect.top - (**hTE).destRect.top) / (**hTE).lineHeight);
}


/*
 * Return the line number that the caret (or the beginning of
 * the currently selected text) is in.  Value returned is in
 * the range 0..(**editTE).nLines.  If = (**editTE).nLines, the
 * caret is past the last line.  The only special case to watch out
 * for is when the caret is at the very end of the text.  If the
 * last character is not a carriage return, then the caret is on
 * the (nLines-1)th line, not the (nLines)th line.
 *
 * (This really should do a binary search for speed.)
 */

static short
LineWithCaret (TEHandle hTE)
{
short	i;
short	nLines;
short	teLength;
short	selStart;
short	lineStart;

	selStart = (**hTE).selStart;
	nLines = (**hTE).nLines;
	teLength = (**hTE).teLength;

	if (selStart == teLength)
	{
		if (teLength != 0 && (*((**hTE).hText))[teLength-1] != cr)
			return (nLines - 1);
		return (nLines);
	}

	for (i = 0; /* empty */; ++i)
	{
		if ((lineStart = (**hTE).lineStarts[i]) >= selStart)
		{
			if (lineStart != selStart)
				--i;
			return (i);
		}
	}
}


/*
 * Return the number of the last displayable line.  That's one
 * more than nLines if the text is empty or the last character
 * is a carriage return.
 */

static short
LastLine (TEHandle hTE)
{
short	nLines;
short	teLength;

	nLines = (**hTE).nLines;
	teLength = (**hTE).teLength;

	if (teLength == 0 || (*((**hTE).hText))[teLength-1] == cr)
		nLines++;
	return (nLines);
}


/*
 * Highlight the scroll bar properly.  This means that it's not
 * made active if the window itself isn't active, even if
 * there's enough text to fill the window.
 */

static void
HiliteScroll (DocHandle doc)
{
WindowPtr	w;
ControlHandle	scroll;
short	hilite;

	w = DocWind (doc);
	scroll = DocScroll (doc);
	hilite = (WindowIsActive (w) && GetControlMaximum (scroll) > 0
				? normalHilite : dimHilite);
	HiliteControl (scroll, hilite);
}


/*
 * Set scroll bar current value (but only if it's different than
 * the current value, to avoid needless flashing).
 */

static void
SetScrollValue (ControlHandle scroll, short value)
{
	if (GetControlValue (scroll) != value)
		SetControlValue (scroll, value);
}


/*
 * Set the maximum value of the scroll bar.  It's set so that if
 * there's more text than fits in the window, the bottom line can
 * be scrolled up at least a little below the bottom of the window.
 *
 * The shenanigans with topLines and scrollableLines have to do with
 * the case where there may be less text than fills the window, but
 * most of it's scrolled off the top.  This can happen when you
 * scroll a bunch of stuff up, then delete everything visible in
 * the window.
 */

static void
SetScrollMax (DocHandle doc)
{
ControlHandle	scroll;
TEHandle		hTE;
short	topLines;
short	scrollableLines;
short	max;

	scroll = DocScroll (doc);
	hTE = DocTE (doc);

	topLines = LinesOffTop (hTE);
	scrollableLines = LastLine (hTE) - DocVisLines (doc);;
	max = (topLines > scrollableLines ? topLines : scrollableLines);

	if (max < 0)
		max = 0;

	if (max != GetControlMaximum (scroll))
	{
		SetControlMaximum (scroll, max);
		HiliteScroll (doc);
	}
}


/*
 * Scroll to the correct position.  lDelta is the
 * amount to CHANGE the current scroll setting by.
 * Positive scrolls the text up, negative down.
 */

static void
ScrollText (DocHandle doc, short lDelta)
{
ControlHandle	scroll;
TEHandle	hTE;
short	topVisLine;
short	newTopVisLine;

	scroll = DocScroll (doc);
	hTE = DocTE (doc);

	topVisLine = LinesOffTop (hTE);
    newTopVisLine = topVisLine + lDelta;
    if (newTopVisLine < 0)					/* clip to range */
    	newTopVisLine = 0;
    if (newTopVisLine > GetControlMaximum (scroll))
		newTopVisLine = GetControlMaximum (scroll);
    SetScrollValue (scroll, newTopVisLine);
    TEScroll (0, (topVisLine - newTopVisLine ) * (**hTE).lineHeight, hTE);
}


/*
 * Scroll to home position without redrawing.
 */

static void
ScrollToHome (TEHandle hTE)
{
Rect	r;

	r = (**hTE).destRect;
	OffsetRect (&r, 0, 2 - r.top);
	(**hTE).destRect = r;
}


/*
 * ClikLoop proc for autoscrolling text when the mouse is dragged out
 * of the text view rectangle.
 *
 * The clipping region has to be set to include the scroll bar,
 * because whenever this proc is called, TextEdit has the region set
 * to the view rectangle - if it's not reset, changes to the scroll
 * bar won't show up!
 *
 * AutoScroll() uses scrollDoc, which must be set in Mouse() before
 * calling TEClick(), which calls AutoScroll().
 */


static DocHandle	scrollDoc;
static short		scrollPart;

static pascal Boolean
AutoScroll (void)
{
WindowPtr	w;
TEHandle	hTE;
Point	p;
Rect	r;

	w = DocWind (scrollDoc);
	hTE = DocTE (scrollDoc);

	SaveClipRgn ();
	ClipRect (&w->portRect);
	GetMouse (&p);
	r = (**hTE).viewRect;
	if (p.v < r.top)
		ScrollText (scrollDoc, -1);	/* back one line */
	else if (p.v > r.bottom)
		ScrollText (scrollDoc, 1);	/* forward one line */
	RestoreClipRgn ();
	return (true);			/* true = 'keep tracking mouse' */
}


/*
 * Filter proc for tracking mousedown in scroll bar.
 *
 * I suspect odd scrolling may occur for hits in paging regions if
 * the window is allowed to size such that less than two lines show.
 *
 * TrackScroll() uses scrollDoc and scrollPart, which must be set in
 * Mouse() before calling TrackControl(), which calls TrackScroll().
 * scrollPart is the original part code in which the mousedown occurred.
 */

static pascal void
TrackScroll (ControlHandle theScroll, short partCode)
{
short	lDelta;
short	visLines = DocVisLines (scrollDoc);

    if (partCode == scrollPart)	/* still in same part? */
    {
        switch (partCode)
        {
            case kControlUpButtonPart: lDelta = -1; break;
            case kControlDownButtonPart: lDelta = 1; break;
            case kControlPageUpPart: lDelta = -(visLines - 1); break;
            case kControlPageDownPart: lDelta = visLines - 1; break;
        }
        ScrollText (scrollDoc, lDelta);
    }
}


/*
 * Set the scroll bar properly and adjust the text in the
 * window so that the line containing the caret is visible.
 * If the line with the caret is more than a line outside of
 * the viewRect, try to place it in the middle of the window.
 *
 * Yes, it is necessary to call SetScrollMax() at the end.
 */

static void
AdjustDisplay (DocHandle doc)
{
ControlHandle	scroll;
TEHandle	hTE;
short		visLines;
short	caretLine;
short	topVisLine;
short	d;

	scroll = DocScroll (doc);
	hTE = DocTE (doc);
	visLines = DocVisLines (doc);

	SetScrollMax (doc);
	caretLine = LineWithCaret (hTE);
	topVisLine = LinesOffTop (hTE);
	if ((d = caretLine - topVisLine) < 0)
		ScrollText (doc, d == -1 ? -1 : d - visLines / 2);
	else if (( d = caretLine - (topVisLine + visLines - 1)) > 0)
		ScrollText (doc, d == 1 ? 1 : d + visLines / 2);
	else
		SetScrollValue (scroll, topVisLine);
	SetScrollMax (doc);	/* might have changed from scrolling */
}


/*
 * Overhaul the entire display.  This is called after catastrophic
 * events, such as resizing the window, or changes to the word
 * wrap style.  It makes sure the view and destination rectangles
 * are sized properly, and that the bottom line of text never
 * scrolls up past the bottom line of the window (if there's
 * enough to fill the window), and that the scroll bar max and
 * current values are set properly.
 *
 * Resizing the dest rect just means resetting the right edge
 * (the top is NOT reset), since text might be scrolled off the
 * top (i.e., destRect.top != 0).
 *
 * Doesn't redraw the control, though!
 */

static void
OverhaulDisplay (DocHandle doc, Boolean showCaret, Boolean recalc)
{
TEHandle	hTE;
Rect		r;

	hTE = DocTE (doc);
	r = (**hTE).viewRect;		/* erase current viewRect */
	EraseRect (&r);
	CalcEditRect (DocWind (doc), &r);		/* calculate new viewRect */
	(**hTE).destRect.right = r.right;
	(**hTE).viewRect = r;
	if (recalc)
		TECalText (hTE);			/* recalculate line starts */
	DocVisLines (doc) = (r.bottom - r.top) / (**hTE).lineHeight;

	/*
	 * If there is text, but none of it is visible in the window
	 * (it's all scrolled off the top), pull some down.
	 */

	if (showCaret)
		AdjustDisplay (doc);
	else
		SetScrollMax (doc);

	TEUpdate (&r, hTE);
}


/* ---------------------------------------------------------------- */
/*						Window Handler Routines						*/
/* ---------------------------------------------------------------- */


/*
 * Handle mouse clicks in window.  The viewRect is never tested
 * directly, because if it were, clicks along the top, left and
 * bottom edges of the window wouldn't register.
 */

static ControlActionUPP TrackScrollUPP = NULL;

static pascal void
Mouse (Point thePt, long t, short mods)
{
WindowPtr	w;
DocHandle	doc;
ControlHandle	scroll;
short	thePart;
short	oldCtlValue;

	GetPort (&w);
	doc = WindDocHandle (w);
	scroll = DocScroll (doc);

	if ((thePart = TestControl (scroll, thePt)) == kControlIndicatorPart)
	{
		oldCtlValue = GetControlValue (scroll);
		if (TrackControl (scroll, thePt, nil) == kControlIndicatorPart)
			ScrollText (doc, GetControlValue (scroll) - oldCtlValue);
	}
	else if (thePart != 0)
	{
		scrollDoc = doc;		/* set globals for TrackScroll */
		scrollPart = thePart;
		if (! TrackScrollUPP)
			TrackScrollUPP = NewControlActionProc((ProcPtr) TrackScroll);
		(void) TrackControl (scroll, thePt, TrackScrollUPP);
	}
	else if (PtInText (w, thePt))
	{
		scrollDoc = doc;		/* set global for AutoScroll */
		TEClick (thePt, (mods & shiftKey) != 0, DocTE (doc));
	}

	SetScrollMax (doc);
}


/*
 * Handle key clicks in window
 */

static pascal void
Key (short c, short code, short mods)
{
WindowPtr	w;
DocHandle	doc;

	w = FrontWindow(); /* changed from GetPort (&w) -- L. Tierney */
	doc = WindDocHandle (w);

	if (c != enter)
		TEKey (c, DocTE (doc));
	AdjustDisplay (doc);
	DocDirty (doc) = true;
	if (DocKeyProc (doc) != nil)	/* report event to the host */
		(*DocKeyProc (doc)) ();
}


/*
 * Update window.  The update event might be in response to a
 * window resizing.  If so, move and resize the scroll bar,
 * and recalculate the text display.
 *
 * The ValidRect call is done because the HideControl adds the
 * control bounds box to the update region - which would generate
 * another update event!  Since everything is redrawn below anyway,
 * the ValidRect is used to cancel the update.
 */

static pascal void
Update (Boolean resized)
{
WindowPtr	w;
DocHandle	doc;
ControlHandle	scroll;
TEHandle	hTE;
Rect	r;

	GetPort (&w);
	doc = WindDocHandle (w);
	scroll = DocScroll (doc);
	hTE = DocTE (doc);

	if (resized)
	{
		r = w->portRect;
		EraseRect (&r);
		HideControl (scroll);
		r = (**scroll).contrlRect;
		ValidRect (&r);
		CalcScrollRect (w, &r);
		SizeControl (scroll, 16, r.bottom - r.top);
		MoveControl (scroll, r.left, r.top);
		OverhaulDisplay (doc, false, (**hTE).crOnly >= 0);
		ShowControl (scroll);
	}
	else
	{
		OverhaulDisplay (doc, false, false);
		if (WindowIsActive (w))
			DrawControls (w);	/* redraw scroll bar */
		else
		{
			/* draw outline of scroll, erase interior */
			r = (**scroll).contrlRect;
			FrameRect (&r);
			InsetRect (&r, 1, 1);
			EraseRect (&r);
		}
	}

	DrawGrowBox (w);
}


/*
 * When the window comes active, highlight the scroll bar appropriately.
 * When the window is deactivated, hide the scroll bar (this is drawn
 * immediately rather than invalidating the rectangle and waiting for
 * Update(), because that just seems too slow).
 *
 * Redraw the grow box in any case.  Set the cursor (FixCursor avoids
 * changing it from an ibeam to an arrow back to an ibeam, in the case
 * where one edit window is going inactive and another is coming
 * active).
 *
 * Report the event to the host.
 */

static pascal void
Activate (Boolean active)
{
WindowPtr	w;
DocHandle	doc;
ControlHandle	scroll;
TEHandle	hTE;
RgnHandle	oldClip;
Rect		r;

	GetPort (&w);
	doc = WindDocHandle (w);
	scroll = DocScroll (doc);
	hTE = DocTE (doc);

	DrawGrowBox (w);
	if (active)
	{
   		TEActivate (hTE);
		HiliteScroll (doc);
		ShowControl (scroll);
	}
	else
	{
		TEDeactivate (hTE);
		/* hide scroll but don't show it being hidden */
		oldClip = NewRgn ();
		GetClip (oldClip);
		SetRect (&r, 0, 0, 0, 0);
		ClipRect (&r);
		HideControl (scroll);
		SetClip (oldClip);
		DisposeRgn (oldClip);
		/* now erase inside of scroll (but not outline, to avoid flicker) */
		r = (**scroll).contrlRect;		/* erase scroll */
		InsetRect (&r, 1, 1);			/* but not outline */
		EraseRect (&r);
	}
	FixCursor (iBeamCursor);
	if (DocActivateProc (doc) != nil)	/* report event to the host */
		(*DocActivateProc (doc)) (active);
}


/*
 * Close box was clicked.  If user specified notify proc, call it.
 * Otherwise do default close operation (ask about saving if dirty,
 * etc.).
 */

static pascal void
Close (void)
{
WindowPtr	w;
DocHandle	doc;

	GetPort (&w);
	doc = WindDocHandle (w);

	if (DocCloseProc (doc) != nil)
		(*DocCloseProc (doc)) ();
	else
		(void) EWindowClose (w);
}


/*
 * Clobber an edit window.  This routine is written defensively on the
 * assumption that not all pieces of a complete edit window are present.
 * This allows it to be called by SkelRmveWind() during window creation
 * attempts if allocations fail.
 *
 * The window's skelWPropEditWind property structure will be disposed
 * of by TransSkel, but the data associated with it (returned by
 * WindDocHandle()) must be disposed of here.
 *
 * At this point it's too late to back out if any changes have been
 * made to the text.
 */

static pascal void
Clobber (void)
{
WindowPtr	w;
DocHandle	doc;
TEHandle	hTE;

	GetPort (&w);
	doc = WindDocHandle (w);

	/*
	 * Toss document record and any pieces that exist
	 */
	if (doc != (DocHandle) nil)
	{
		if ((hTE = DocTE (doc)) != (TEHandle) nil)
			TEDispose (hTE);						/* toss text record */
		DisposeHandle ((Handle) doc);
	}
	DisposeWindow (w);			/* toss window (scroll bar, too) */
	FixCursor (iBeamCursor);
}


/*
 * Blink the caret and make sure the cursor's an i-beam when it's
 * in the non-scrollbar part of the window.
 */

static pascal void
Idle (void)
{
WindowPtr	w;
DocHandle	doc;

	GetPort (&w);
	doc = WindDocHandle (w);

	if (DocIdleProc (doc) != nil)
		(*DocIdleProc (doc)) ();    /* L. Tierney */
	TEIdle (DocTE (doc));			/* blink that caret! */
	FixCursor (iBeamCursor);
}


/* ---------------------------------------------------------------- */
/*						Internal File Routines						*/
/* ---------------------------------------------------------------- */


/*
 * Save the contents of the edit window.  If there is no file bound
 * to the window, ask for a file name.  If askForFile is true, ask
 * for a name even if the window is currently bound to a file.  If
 * bindToFile is true, bind the window to the file written to (if
 * that's different than the currently bound file), and clear the
 * window's dirty flag.
 *
 * Return true if the file was written without error.  Return false
 * if (a) user was asked for name and clicked Cancel (b) there was
 * some error writing the file.  In the latter case, the window is
 * not bound to any new name given by user.
 *
 * Always returns false if the window isn't an edit window.  This
 * simplifies EWindowSave, EWindowSaveAs, EWindowSaveCopy.  (They
 * don't do the test.)
 */

static Boolean
SaveFile (WindowPtr w, Boolean askForFile, Boolean bindToFile)
{
DocHandle	doc;
TEHandle	hTE;
short	f;
FInfo	fndrInfo;	/* finder info */
SFReply	tmpFile;
Handle	hText;
long	count;
OSErr	result;
Boolean	haveNewFile = false;

	if (!IsEWindow (w))
		return (false);

	doc = WindDocHandle (w);
	hTE = DocTE (doc);

	tmpFile = DocFile (doc);					/* initialize default name */
	if (DocBound (doc) == false || askForFile)
	{
		/*SFPPutFile (dlogWhere, "\pSave file as:", editFile.fName, nil, &tmpFile,
				putDlgID, SkelDlogFilter (nil, false));*/
		SFPPutFile (dlogWhere, "\pSave file as:", tmpFile.fName, nil, &tmpFile,
				putDlgID, SkelDlogFilter (nil, false));
		SkelRmveDlogFilter ();
		SkelDoUpdates ();
		if (!tmpFile.good)
			return (false);
		else
		{
			haveNewFile = true;
			if (GetFInfo (tmpFile.fName, tmpFile.vRefNum, &fndrInfo)
													== noErr) /* exists */
			{
				if (fndrInfo.fdType != 'TEXT')
				{
					ErrMesg ("\pNot a TEXT File");
					return (false);
				}
			}
			else	/* doesn't exist.  create it. */
			{
				if (Create (tmpFile.fName, tmpFile.vRefNum,
							creator, 'TEXT') != noErr)
				{
					ErrMesg ("\pCan't Create");
					return (false);
				}
			}
		}
	}
	
	if (FSOpen (tmpFile.fName, tmpFile.vRefNum, &f) != noErr)
		ErrMesg ("\pCan't Open");
	else
	{
		FixCursor (watchCursor);
		(void) SetFPos (f, fsFromStart, 0L);
		hText = (**hTE).hText;
		HLock (hText);
		count = (**hTE).teLength;
		result = FSWrite (f, &count, *hText);
		(void) GetFPos (f, &count);
		(void) SetEOF (f, count);
		(void) FSClose (f);
		(void) FlushVol (nil, tmpFile.vRefNum);
		HUnlock (hText);
		FixCursor (iBeamCursor);
		if (result == noErr)
		{
			if (bindToFile)
			{
				DocDirty (doc) = false;
				if (haveNewFile)	/* name different than current */
				{
					SetWTitle (w, tmpFile.fName);
					DocBound (doc) = true;
					DocFile (doc) = tmpFile;
				}
			}
			return (true);
		}
		ErrMesg ("\pWrite error!");
	}
	return (false);
}


/*
 * Read file from disk.  Return value indicates whether or not the
 * file was read in successfully.
 */

static Boolean
ReadFile (DocHandle doc)
{
TEHandle	hTE;
SFReply	sfReply;
Boolean	result = false;
short	f;
long	len;
Handle	h;

	hTE = DocTE (doc);
	sfReply = DocFile (doc);
	FixCursor (watchCursor);
	if (FSOpen (sfReply.fName, sfReply.vRefNum, &f) != noErr)
		ErrMesg ("\pCouldn't open file");
	else
	{
		(void) GetEOF (f, &len);
		if (len >= maxFileSize)
			ErrMesg ("\pFile is too big");
		else
		{
			h = (Handle) TEGetText (hTE);
			SetHandleSize (h, len);
			HLock (h);
			(void) FSRead (f, &len, *h);
			HUnlock (h);
			(**hTE).teLength = len;
			TESetSelect (0L, 0L, hTE);	/* set caret at start */
			DocDirty (doc) = false;
			result = true;
		}
		(void) FSClose (f);
	}
	FixCursor (iBeamCursor);
	return (result);
}


/* ------------------------------------------------------------ */
/*			Interface (Public) Lowest-level Routines			*/
/* ------------------------------------------------------------ */


/*
 * Return true/false to indicate whether the window is really an
 * edit window.
 */

pascal Boolean
IsEWindow (WindowPtr w)
{
	return ((Boolean) (WindDocHandle (w) != nil));
}


/*
 * Return true/false to indicate whether the text associated with
 * the window has been changed since the last save/revert (or since
 * created, if not bound to file).
 */

pascal Boolean
IsEWindowDirty (WindowPtr w)
{
DocHandle	doc;

	if ((doc = WindDocHandle (w)) != nil)
		return (DocDirty (doc));
	return (false);
}


/*
 * Return a handle to the TextEdit record associated with the edit
 * window, or nil if it's not an edit window
 */

pascal TEHandle
GetEWindowTE (WindowPtr w)
{
DocHandle	doc;

	return ((doc = WindDocHandle (w)) == nil ? nil : DocTE (doc));
}


/*
 * Return true/false depending on whether or not the editor is
 * bound to a file, and a copy of the file info in the second
 * argument.  Pass nil for fileInfo if only want the return status.
 * Returns false if it's not an edit window.
 */

pascal Boolean
GetEWindowFile (WindowPtr w, SFReply *fileInfo)
{
DocHandle	doc;

	if ((doc = WindDocHandle (w)) != nil)
	{
		if (fileInfo != nil)
			*fileInfo = DocFile (doc);
		return (DocBound (doc));
	}
	return (false);
}


/* ---------------------------------------------------------------- */
/*					Interface Display Routines						*/
/* ---------------------------------------------------------------- */


/*
 * Install event notification procedures for an edit window.
 */

pascal void
SetEWindowProcs (WindowPtr w, TEditKeyProcPtr pKey,
					TEditActivateProcPtr pActivate, TEditCloseProcPtr pClose,
					TEditIdleProcPtr pIdle)
{
DocHandle	doc;

	if (w == nil)			/* reset window creation defaults */
	{
		e_key = pKey;
		e_activate = pActivate;
		e_close = pClose;
		e_idle = pIdle; /* L. Tierney */
	}
	else if ((doc = WindDocHandle (w)) != (DocHandle) nil)
	{
		DocKeyProc (doc) = pKey;
		DocActivateProc (doc) = pActivate;
		DocCloseProc (doc) = pClose;
		DocIdleProc (doc) = pIdle; /* L. Tierney */
	}
}


/*
 * Change the text display characteristics of an edit window
 * and redisplay it.
 *
 * Scroll to home position before overhauling, because although
 * the overhaul sets the viewRect to display an integral number
 * of lines, there's no guarantee that the destRect offset will
 * also be integral except at home position.  Clipping is set to
 * an empty rect so the scroll doesn't show.
 */

pascal void
SetEWindowStyle (WindowPtr w, short font,
				short size, Style style, short wrap, short just)
				/* added style parameter -- L. Tierney */
{
DocHandle	doc;
GrafPtr		savePort;
FontInfo	f;
TEHandle	hTE;
short		oldWrap;

	if (w == nil)			/* reset window creation defaults */
	{
		e_font = font;
		e_size = size;
		e_style = style;   /* L. Tierney */
		e_wrap = wrap;
		e_just = just;
		return;
	}

	if ((doc = WindDocHandle (w)) != (DocHandle) nil)
	{
		GetPort (&savePort);
		SetPort (w);
		hTE = DocTE (doc);
		GetPort (&savePort);
		ScrollToHome (hTE);

		oldWrap = (**hTE).crOnly;
		(**hTE).crOnly = wrap;	/* set word wrap */
		TESetAlignment (just, hTE);	/* set justification */

		TextFont (font);	 	/* set the font and point size */
		TextSize (size);		/* of text record */
		TextFace (style);		/* text style -- L. Tierney */
		GetFontInfo (&f);
		(**hTE).lineHeight = f.ascent + f.descent + f.leading;
		(**hTE).fontAscent = f.ascent;
		(**hTE).txFont = font;
		(**hTE).txSize = size;
		(**hTE).txFace = style;/* text style -- L. Tierney */

		OverhaulDisplay (doc, true, (oldWrap >= 0 || wrap >= 0));

		SetPort (savePort);
	}
}


/*
 * Redo display.  Does not save current port.  This is used by hosts
 * that mess with the text externally to TransEdit.  The arguments
 * determine whether the text is scrolled to show the line with the
 * caret, whether the lineStarts are recalculated, and whether or not
 * the text should be marked dirty.
 */

pascal void
EWindowOverhaul (WindowPtr w, Boolean showCaret,
						Boolean recalc, Boolean dirty)
{
DocHandle	doc;

	if ((doc = WindDocHandle (w)) != (DocHandle) nil)
	{
		OverhaulDisplay (doc, showCaret, recalc);
		DrawControls (w);
		DocDirty (doc) = dirty;
	}
}


/* ---------------------------------------------------------------- */
/*						Interface Menu Routine						*/
/* ---------------------------------------------------------------- */


/*
 * Do Edit menu selection.  This is only valid if an edit
 * window is frontmost.  Cut, Paste, and Clear cause the window
 * to be marked dirty.  Copy does not.  Undo is unimplemented.
 */

pascal void
EWindowEditOp (short item)
{
DocHandle	doc;
TEHandle	hTE;
Boolean		modified = false;

	if (SystemEdit (item - 1))
		return;

	if ((doc = WindDocHandle (FrontWindow ())) == (DocHandle) nil)
		return;				/* not an edit window */

	hTE = DocTE (doc);

	switch (item)
	{
	/*
	 * cut selection, put in TE Scrap, clear clipboard and put
	 * TE scrap in it
	 */
	case cut:
		TECut (hTE);
		(void) ZeroScrap ();
		(void) TEToScrap ();
		modified = true;
		break;

	/*
	 * copy selection to TE Scrap, clear clipboard and put
	 * TE scrap in it
	 */
	case copy:
		TECopy (hTE);
		(void) ZeroScrap ();
		(void) TEToScrap ();
		break;

	/*
	 * get clipboard into TE scrap, put TE scrap into edit record
	 */
	case paste:
		(void) TEFromScrap ();
		TEPaste (hTE);
		modified = true;
		break;

	/*
	 * delete selection without putting into TE scrap or clipboard
	 */
	case clear:
		TEDelete (hTE);
		modified = true;
		break;
	}

	AdjustDisplay (doc);
	if (modified)
		DocDirty (doc) = true;
}


/* ---------------------------------------------------------------- */
/*						Interface File Routines						*/
/* ---------------------------------------------------------------- */


/*
 * Set file creator for any files created by TransEdit
 */

pascal void
SetEWindowCreator (OSType creat)
{
	creator = creat;
}



/*
 * Save the contents of the given window
 */

pascal Boolean
EWindowSave (WindowPtr w)
{
	return (SaveFile (w,		/* window to save */
					  false,	/* don't ask for file if have one */
					  true));	/* bind to new file if one given */
}


/*
 * Save the contents of the given window under a new name
 * and bind to that name.
 */

pascal Boolean
EWindowSaveAs (WindowPtr w)
{
	return (SaveFile (w,		/* window to save */
					  true,		/* ask for file even if have one */
					  true));	/* bind to new file if one given */
}


/*
 * Save the contents of the given window under a new name, but
 * don't bind to the name.
 */

pascal Boolean
EWindowSaveCopy (WindowPtr w)
{
	return (SaveFile (w,		/* window to save */
					  true,		/* ask for file even if have one */
					  false));	/* don't bind to file */
}


/*
 * Close the window.  If it's dirty and is either bound to a file
 * or (if not bound) has some text in it, ask about saving it first,
 * giving user option of saving changes, tossing them, or
 * cancelling altogether.
 *
 * Return true if the file was saved and the window closed, false if
 * user cancelled or there was an error.
 */

pascal Boolean
EWindowClose (WindowPtr w)
{
DocHandle	doc;
TEHandle	hTE;
SFReply		sfReply;

	if ((doc = WindDocHandle (w)) == (DocHandle) nil)
		return (false);
	hTE = DocTE (doc);
	sfReply = DocFile (doc);

	if ( (DocBound (doc) || (**hTE).teLength > 0) && DocDirty (doc))
	{
		switch (FakeAlert ("\pSave changes to \322", sfReply.fName,
				"\p\323?", "\p", 3, 1, 2,
				"\pSave", "\pCancel", "\pDon\325t Save"))	/* ask whether to save */
		{

		case 1:
			if (SaveFile (w,	/* window to save */
						  false,	/* don't ask for name */
						  false)	/* don't bind to name */
					== false)
				return (false);	/* canceled or error - cancel Close */
			break;

		case 2:			/* cancel Close */
			return (false);

		case 3:			/* toss changes */
			break;
		}
	}
	SkelRmveWind (w);
	return (true);
}


/*
 * Revert to saved version of file on disk.  The window must be an edit
 * window, and must be bound to a file.  Returns false if one of these
 * conditions is not met, or if they are met but there was an error
 * reading the file.
 *
 * The window need not be dirty; if it is, the user is asked
 * whether to really revert.
 */

pascal Boolean
EWindowRevert (WindowPtr w)
{
DocHandle	doc;
SFReply		sfReply;

	if ((doc = WindDocHandle (w)) == (DocHandle) nil)
		return (false);
	if (!DocBound (doc))
		return (false);		/* no file to revert to */
	if (DocDirty (doc))
	{
		sfReply = DocFile (doc);
		if (FakeAlert ("\p\322", sfReply.fName,
				"\p\323 has been changed.  Really revert?",
				"\p", 2, 1, 1, "\pCancel", "\pRevert", "\p") == 1)
			return (false);
	}
	if (ReadFile (doc) == false)
		return (false);
	ScrollToHome (DocTE (doc));
	OverhaulDisplay (doc, true, true);
	DrawControls (w);
	ValidRect (&w->portRect);
	return (true);
}


/* ---------------------------------------------------------------- */
/*			Interface Initialization/Termination Routines			*/
/* ---------------------------------------------------------------- */


/*
 * Set up the SFReply record for a document.  Ask for a file and use
 * the file selected if bindToFile is true.  Otherwise use title for
 * the name if it's non-nil, or make the document untitled if title is
 * nil or the empty string.
 *
 * Copy the name into the file info structure even if the window is
 * unbound, because the Save operations expect to find it there as the
 * most likely name to use.
 */

static Boolean
SetupFile (StringPtr title, Boolean bindToFile, SFReply *sfReply)
{
Str255		s;
OSType		type = 'TEXT';

	sfReply->vRefNum = 0;
	if (bindToFile)
	{
		SFPGetFile (dlogWhere, "\p", nil, 1, &type, nil, sfReply,
				getDlgID, SkelDlogFilter (nil, false));
		SkelRmveDlogFilter ();
		return (sfReply->good);
	}
	if (title == nil || title[0] == 0)
	{
		Untitled (s);
		title = s;
	}
	BlockMove (title, sfReply->fName, (long) (title[0] + 1));
	return (true);
}


/*
 * Create and initialize an edit window and the associated data
 * structures.  If the window and data cannot be allocated, destroy
 * the window and return nil.  Otherwise return the window.
 *
 * The caller should set and restore the port before and after calling
 * SetupDocWind().
 */

static TEClickLoopUPP AutoScrollUPP = NULL;

static WindowPtr
SetupDocWind (WindowPtr w, Boolean visible, Boolean bindToFile, SFReply *sfReply)
{
DocHandle	doc;
SkelWindPropHandle	prop;
ControlHandle	scroll;
TEHandle	hTE;
Rect		r;

	if (!SkelWindow (w,		/* the window */
				Mouse,		/* mouse click handler */
				Key,		/* key click handler */
				Update,		/* window updating procedure */
				Activate,	/* window activate/deactivate procedure */
				Close,		/* window close procedure */
				Clobber,	/* window disposal procedure */
				Idle,		/* idle proc */
				true))		/* idle only when frontmost */
	{
		DisposeWindow (w);
		return (nil);
	}

	/*
	 * After this point SkelRmveWind() can be called to remove the window
	 * if any allocations fail.
	 */

	/*
	 * Get new document record, attach to window property list.
	 * Also make document record point to window.
	 */

	if (!SkelAddWindProp (w, skelWPropEditWind, (long) 0L))
	{
		SkelRmveWind (w);
		return (nil);
	}
	doc = New (DocRecord);
	if (doc == (DocHandle) nil)
	{
		SkelRmveWind (w);
		return (nil);
	}
	prop = SkelGetWindProp (w, skelWPropEditWind);
	(**prop).skelWPropData = (long) doc;
	DocWind (doc) = w;

	/*
	 * Build the scroll bar.  Make sure the borders overlap the
	 * window frame and the frame of the grow box.
	 */

	CalcScrollRect (w, &r);
	scroll = NewControl (w, &r, "\p", true, 0, 0, 0, scrollBarProc, 0L);
	DocScroll (doc) = scroll;

	/*
	 * Create the TE record used for text display.  Use default
	 * characteristics.
	 */

	CalcEditRect (w, &r);
	hTE = TENew (&r, &r);
	DocTE (doc) = hTE;
	if (! AutoScrollUPP)
		AutoScrollUPP = NewTEClickLoopProc((ProcPtr) AutoScroll);
	TESetClickLoop ((TEClickLoopUPP) AutoScrollUPP, hTE);			/* set autoscroll proc */

	if (scroll == (ControlHandle) nil || hTE == (TEHandle) nil)
	{
		SkelRmveWind (w);
		return (nil);
	}

	/*
	 * Install default event notification procedures, font characteristics.
	 */

	SetEWindowProcs (w, e_key, e_activate, e_close, e_idle); /* idle added -- L. Tierney */
	SetEWindowStyle (w, e_font, e_size, e_style, e_wrap, e_just);

	DocFile (doc) = *sfReply;
	DocBound (doc) = bindToFile;
	if (bindToFile && !ReadFile (doc))
	{
		SkelRmveWind (w);
		return (nil);
	}
	DocDirty (doc) = false;

	/*
	 * Show window if specified as visible, and return a pointer to it.
	 */

	OverhaulDisplay (doc, true, true);
	if (visible)
		ShowWindow (w);

	return (w);
}


/*
 * Initialize the window and associated data structures.
 * Return window pointer or nil if some sort of error.
 *
 * Preserves the current port.  If the window is visible,
 * an activate event will follow, at which point the port
 * will be set to the window.
 *
 * If a file is to be solicited, ask for it before creating the
 * window to avoid the following problem:  If you create a window
 * to be frontmost but invisible, putting up the getfile dialog
 * after creating the window causes the window to go behind the
 * first visible window when the file dialog goes away.
 */

pascal WindowPtr
NewEWindow (Rect *bounds, StringPtr title, Boolean visible,
			WindowPtr behind, Boolean goAway,
			long refNum, Boolean bindToFile)
{
WindowPtr	w;
GrafPtr		savePort;
SFReply		sfReply;

	if (!SetupFile (title, bindToFile, &sfReply))	/* user cancelled */
		return (nil);

	if (SkelQuery (skelQHasColorQD))
	{
		w = NewCWindow (nil, bounds, sfReply.fName, false, documentProc + 8,
								behind, goAway, refNum);
	}
	else
	{
		w = NewWindow (nil, bounds, sfReply.fName, false, documentProc + 8,
								behind, goAway, refNum);
	}

	if (w != (WindowPtr) nil)
	{
		GetPort (&savePort);
		w = SetupDocWind (w, visible, bindToFile, &sfReply);	/* nil if allocation failed */
		SetPort (savePort);
	}

  	return (w);
}


/*
 * GetNewEWindow() is the resource equivalent of NewEWindow().  It reads in the
 * 'WIND' template and yanks window creation values out of it rather than using
 * GetWindow() since the latter would create the window visible right away.
 * NewEWindow() doesn't show the window until after the file has been read in, if
 * a file is to be read.  procID value from resource is ignored.
 */

/* 'WIND' resource structure */

typedef struct WTmpl WTmpl, **WTHandle;

struct WTmpl
{
	Rect	bounds;
	short	procId;
	short	visible;
	short	goAway;
	long	refCon;
	Str255	title;
};


pascal WindowPtr
GetNewEWindow (short resourceNum, WindowPtr behind, Boolean bindToFile)
{
WTHandle	h;
WindowPtr	w;

	h = (WTHandle) GetResource ('WIND', resourceNum);
	if (h == (WTHandle) nil)
		return ((WindowPtr) nil);
	MoveHHi ((Handle) h);
	HLock ((Handle) h);
	w = NewEWindow (&((**h).bounds), (**h).title, (Boolean) ((**h).visible != 0),
					behind, (Boolean) ((**h).goAway != 0), (**h).refCon, bindToFile);
	HUnlock ((Handle) h);
	return (w);
}


/*
 * Look through the list of windows, shutting down all the edit
 * windows.  If any window is dirty, ask user about saving it first.
 * If the user cancels on any such request, ClobberEWindows returns
 * false.  If all edit windows are shut down, return true.  It is
 * then safe for the host to exit.
 *
 * When a window *is* shut down, have to start looking through the
 * window list again, since w no longer points anywhere
 * meaningful.
 */

pascal Boolean
ClobberEWindows (void)
{
WindowPtr	w;

	for (;;)
	{
		for (w = FrontWindow ();
				w != nil;
					w = (WindowPtr) ((WindowPeek) w)->nextWindow)
		{
			if (IsEWindow (w))
				break;
		}
		if (w == nil)
			return (true);		/* all edit windows are shut down */

		if (w != FrontWindow ())
		{
			SelectWindow (w);
			ShowWindow (w);
			EWindowOverhaul (w, false, false, IsEWindowDirty (w));
			SetPort (w);
			ValidRect (&w->portRect);
		}

		if (EWindowClose (w) == false)
			return (false);		/* cancel or error */
	}
}

/**** Xlisp-Stat Additions -- L. Tierney */

/* change the dirty status */
pascal void
SetEWindowDirty (WindowPtr w, Boolean dirty)
{
DocHandle	doc;

	if ((doc = WindDocHandle (w)) != nil)
		DocDirty (doc) = dirty;
}

/* show caret */
pascal void EWindowAdjustDisplay(WindowPtr w)
{
DocHandle	doc;

	if ((doc = WindDocHandle (w)) != nil)
		AdjustDisplay(doc);
}


syntax highlighted by Code2HTML, v. 0.9.1