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