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