home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 300-399 / ff386.lzh / XLispStat / src3.lzh / Mac / TransEdit1.c < prev    next >
C/C++ Source or Header  |  1990-10-11  |  41KB  |  1,828 lines

  1. /*
  2.     TransEdit.c version 1.0 - TransSkel plug-in module supporting an
  3.     arbitrary number of generic edit windows.  Each window may be
  4.     bound to a file.
  5.  
  6.     *** Requires FakeAlert.c for proper linking! ***
  7.  
  8.     Shortcomings:
  9.         Doesn't check for the obvious out of memory conditions.
  10.     
  11.     TransSkel and TransEdit are public domain, and are written by:
  12.  
  13.             Paul DuBois
  14.             Wisconsin Regional Primate Research Center
  15.             1220 Capital Court
  16.             Madison WI  53706  USA
  17.  
  18.     UUCP:    {allegra,ihnp4,seismo}!uwvax!uwmacc!dubois
  19.     ARPA:    dubois@unix.macc.wisc.edu
  20.             dubois@rhesus.primate.wisc.edu
  21.  
  22.     This version of TransEdit written for LightspeedC.  LightspeedC
  23.     is a trademark of:
  24.             THINK Technologies, Inc
  25.             420 Bedford Street  Suite 350
  26.             Lexington, MA  02173  USA
  27.  
  28.   History
  29.   08/25/86    Genesis.  Beta version.
  30.   09/15/86    Changed to allow arbitrary number of windows.
  31.   11/04/86    Added conditional stuff to allow compilation in
  32.              single- or multiple-window mode.  Changed version to 1.0.
  33. */
  34.  
  35.  
  36. /*
  37.     The following symbol controls the compile mode.  If it is #define'd,
  38.     TransEdit allows only a single edit window, and generates less code.
  39.     If it is #undef'ed, TransEdit allows an arbitrary number of edit
  40.     windows, but generates more code.
  41. */
  42.  
  43. # undef    singleEdit
  44.  
  45.  
  46. #ifdef MPWC
  47. # include    <Memory.h>
  48. # include    <Controls.h>    /* includes WindowMgr.h, QuickDraw.h, MacTypes.h */
  49. # include    <Packages.h>
  50. # include    <Files.h>
  51. # include    <ToolUtils.h>
  52. # include    <Scrap.h>
  53. # define arrow qd.arrow
  54. #else
  55. # include    <ControlMgr.h>    /* includes WindowMgr.h, QuickDraw.h, MacTypes.h */
  56. # include    <StdFilePkg.h>
  57. # include    <FileMgr.h>
  58. # include    <ToolBoxUtil.h>
  59. #endif MPWC
  60. # include    "TransEdit.h"
  61.  
  62. /*
  63.     Edit window types, constants, variables.
  64. */
  65.  
  66.  
  67. # define    enter            3
  68. # define    cr                13
  69. # define    monaco            4
  70. # ifndef    shiftKey
  71. # define    shiftKey        0x0200
  72. # endif        shiftKey
  73.  
  74. typedef enum            /* Edit menu item numbers */
  75. {
  76.     undo = 1,
  77.     /* --- */
  78.     cut = 3,
  79.     copy,
  80.     paste,
  81.     clear        /* (it's ok if the host doesn't have this item) */
  82. };
  83.  
  84.  
  85. /*
  86.     Default values for edit window text display characteristics
  87.     and event notification procedures
  88. */
  89.  
  90. static int        e_font = monaco;    /* default font                 */
  91. static int        e_size = 9;            /* default pointsize            */
  92. static int        e_wrap = 0;            /* default word wrap (on)       */
  93. static int        e_just = teJustLeft;/* default justification        */
  94. static ProcPtr    e_key = nil;        /* default key procedure        */
  95. static ProcPtr    e_activate = nil;    /* default activation procedure */
  96. static ProcPtr    e_close = nil;        /* default close procedure      */
  97. static ProcPtr    e_key_filter = nil;    /* default key filter procedure - L. Tierney */
  98. static ProcPtr    e_idle = nil;        /* default idle procedure - L. Tierney */
  99.  
  100.  
  101. # ifndef    singleEdit
  102.  
  103. /*
  104.     New(TypeName) returns handle to new object, for any TypeName.
  105.     If there is insufficient memory, the result is nil.
  106. */
  107.  
  108. # define    New(x)    (x **) NewHandle ((Size) sizeof (x))
  109.  
  110.  
  111. /*
  112.     ewList points to a list of structures describing the known edit
  113.     windows.
  114. */
  115.  
  116.  
  117. typedef struct EditInfo
  118. {
  119.     WindowPtr        editWind;    /* the edit window                   */
  120.     Boolean            bound;        /* whether window is bound to file   */
  121.     SFReply            editFile;    /* file it's bound to, if bound true */
  122.     TEHandle        editTE;        /* window text                       */
  123.     Boolean            dirty;        /* whether text modified since save  */
  124.     ControlHandle    scroll;        /* scroll bar                        */
  125.     int                visLines;    /* # lines visible in window, max    */
  126.     ProcPtr            eKey;        /* key click notifier                */
  127.     ProcPtr            eActivate;    /* activate event notifier           */
  128.     ProcPtr            eClose;        /* close notifier                    */
  129.     ProcPtr         eKeyFilter; /* key filter - L. Tierney           */
  130.     ProcPtr         eIdle;         /* idle procedure - L. Tierney       */
  131.     struct EditInfo    **eNext;    /* next information structure        */
  132. } EditInfo, *EIPtr, **EIHandle;
  133.  
  134.  
  135. static EIHandle        ewList = nil;
  136.  
  137. # endif
  138.  
  139.  
  140. /*
  141.     Global variables - most of these are always synced to
  142.     the current window.  Note that not all these are set by
  143.     SyncGlobals, since some are not often needed.  When they
  144.     are all needed, use SyncAllGlobals.
  145. */
  146.  
  147. # ifndef    singleEdit
  148. static EIHandle            editInfo;        /* window's info structure      */
  149. # endif
  150.  
  151. static WindowPtr        editWind = nil;    /* the window                   */
  152. static TEHandle            editTE;            /* window text                  */
  153. static ControlHandle    editScroll;        /* the scroll bar               */
  154. static SFReply            editFile;        /* file information             */
  155. static int                visLines;        /* number of lines in window    */
  156. static Boolean            bound;            /* true if window bound to file */
  157. static Boolean            dirty;            /* whether window is dirty      */
  158. static ProcPtr            eKey;            /* key click notifier           */
  159. static ProcPtr            eActivate;        /* activate event notifier      */
  160. static ProcPtr            eClose;            /* close notifier               */
  161. static ProcPtr            eKeyFilter;        /* key filter - L. Tierney      */
  162. static ProcPtr            eIdle;            /* idle procedure - L. Tierney  */
  163.  
  164.  
  165. static int        windID = 0;
  166. static Point    dlogWhere = { 70, 100 };    /* GetFile/PutFile location */
  167. static OSType    creator = 'TEDT';            /* default file creator */
  168.  
  169. static RgnHandle    clipRgn;
  170.  
  171.  
  172. /* -------------------------------------------------------------------- */
  173. /*                Miscellaneous Internal (private) Routines                */
  174. /* -------------------------------------------------------------------- */
  175.  
  176.  
  177. /*
  178.     Save and restore the current window's clip region
  179. */
  180.  
  181. static SaveClipRgn ()
  182. {
  183.     clipRgn = NewRgn ();
  184.     GetClip (clipRgn);
  185. }
  186.  
  187.  
  188. static RestoreClipRgn ()
  189. {
  190.     SetClip (clipRgn);
  191.     DisposeRgn (clipRgn);
  192. }
  193.  
  194.  
  195. /*
  196.     Draw grow box in lower right hand corner of window.
  197. */
  198.  
  199.  
  200. static DrawGrowBox ()
  201. {
  202. Rect        r;
  203.  
  204.     SaveClipRgn ();
  205.     r = editWind->portRect;
  206.     r.left = r.right - 15;        /* draw only in corner */
  207.     r.top = r.bottom - 15;
  208.     ClipRect (&r);
  209.     DrawGrowIcon (editWind);
  210.     RestoreClipRgn ();
  211. }
  212.  
  213.  
  214. /* -------------------------------------------------------------------- */
  215. /*            Lowest-level Internal (Private) Edit Window Routines        */
  216. /* -------------------------------------------------------------------- */
  217.  
  218.  
  219. # ifndef    singleEdit
  220.  
  221. /*
  222.     Get edit window info associated with window.
  223.     Return nil if window isn't a known edit window.
  224. */
  225.  
  226. static EIHandle GetEInfo (theWind)
  227. WindowPtr    theWind;
  228. {
  229. register EIHandle    h;
  230.  
  231.     for (h = ewList; h != nil; h = (**h).eNext)
  232.     {
  233.         if ((**h).editWind == theWind)
  234.             return (h);
  235.     }
  236.     return (nil);
  237. }
  238.  
  239. # endif
  240.  
  241.  
  242. # ifdef    singleEdit
  243. # define    SyncAllGlobals    SyncGlobals
  244. # endif
  245.  
  246.  
  247. /*
  248.     Synchronize globals to an edit window and make it the
  249.     current port.  theWind must be a legal edit window, with one
  250.     exception:  if theWind is nil, the variables are synced to the
  251.     port that's already current.  That is safe (and correct) because:
  252.     (i)     nil is only passed by edit window handler procedures,
  253.          which are only attached to edit windows
  254.     (ii) TransSkel always sets the port to the window before
  255.          calling the handler proc.
  256.     Hence, using the current port under these circumstances always
  257.     produces a legal edit window.
  258. */
  259.  
  260. static SyncGlobals (theWind)
  261. WindowPtr    theWind;
  262. {
  263.  
  264.     if (theWind == nil)                    /* use current window */
  265.         GetPort (&theWind);
  266.  
  267.     SetPort (theWind);
  268.  
  269. # ifndef    singleEdit
  270.  
  271.     editWind = theWind;
  272.     editInfo = GetEInfo (editWind);
  273.     editTE = (**editInfo).editTE;
  274.     editScroll = (**editInfo).scroll;
  275.     visLines = (**editInfo).visLines;
  276.  
  277. # endif
  278.  
  279. }
  280.  
  281.  
  282. # ifndef    singleEdit
  283.  
  284. static SyncAllGlobals (theWind)
  285. WindowPtr    theWind;
  286. {
  287.  
  288.     SyncGlobals (theWind);                /* sync display globals */
  289.     editFile = (**editInfo).editFile;    /* sync file, state, and */
  290.     bound = (**editInfo).bound;            /* procedure globals */
  291.     dirty = (**editInfo).dirty;
  292.     eKey = (**editInfo).eKey;
  293.     eActivate = (**editInfo).eActivate;
  294.     eClose = (**editInfo).eClose;
  295.     eKeyFilter = (**editInfo).eKeyFilter; /* L. Tierney */
  296.     eIdle = (**editInfo).eIdle; /* L. Tierney */
  297. }
  298.  
  299. # endif
  300.  
  301.  
  302. /*
  303.     Set dirty flag for current window
  304. */
  305.  
  306. static SetDirty (boolVal)
  307. Boolean    boolVal;
  308. {
  309.  
  310. # ifdef    singleEdit
  311.     dirty = boolVal;
  312. # else
  313.     (**editInfo).dirty = boolVal;
  314. # endif
  315. }
  316.  
  317.  
  318. /* -------------------------------------------------------------------- */
  319. /*                    Internal (private) Display Routines                    */
  320. /* -------------------------------------------------------------------- */
  321.  
  322.  
  323. /*
  324.     Calculate the dimensions of the editing rectangle for
  325.     editWind (which must be set properly and is assumed to be
  326.     the current port).  (The viewRect and destRect are the
  327.     same size.)  Assumes the port, text font and text size are all
  328.     set properly.  The viewRect is sized so that an integral
  329.     number of lines can be displayed in it, i.e., so that a
  330.     partial line never shows at the bottom.  If that's not
  331.     done, funny things can happen to the caret.
  332. */
  333.  
  334. static GetEditRect (r)
  335. Rect    *r;
  336. {
  337. FontInfo        f;
  338. register int    lineHeight;
  339.  
  340.     GetFontInfo (&f);
  341.     lineHeight = f.ascent + f.descent + f.leading;
  342.     *r = editWind->portRect;
  343.     r->left += 4;
  344.     r->right -= 17;        /* leave room for scroll bar */
  345.     r->top += 2;
  346.     r->bottom = r->top + ((r->bottom - r->top - 2) / lineHeight) * lineHeight;
  347. }
  348.  
  349.  
  350. /*
  351.     Set the edit rect properly.
  352. */
  353.  
  354. static SetEditRect ()
  355. {
  356. Rect    r;
  357.  
  358.     GetEditRect (&r);
  359.     (**editTE).destRect.right = r.right;
  360.     (**editTE).viewRect = r;
  361. }
  362.  
  363.  
  364.  
  365. /*
  366.     Calculate the dimensions of the scroll bar rectangle for
  367.     editWind (which must be set properly).  Make sure that
  368.     the edges overlap the window frame and the grow box.
  369. */
  370.  
  371. static CalcScrollRect (r)
  372. Rect        *r;
  373. {
  374.     *r = editWind->portRect;
  375.     ++r->right;
  376.     --r->top;
  377.     r->left = r->right - 16;
  378.     r->bottom -= 14;
  379. }
  380.  
  381.  
  382. /*
  383.     Return true if the mouse is in the non-scrollbar part of the
  384.     edit window.
  385. */
  386.  
  387. static Boolean PtInText (pt)
  388. Point    pt;
  389. {
  390. Rect    r;
  391.  
  392.     r = editWind->portRect;
  393.     r.right -= 15;
  394.     return (PtInRect (pt, &r));
  395. }
  396.  
  397.  
  398. /*
  399.     Set the cursor appropriately.  If theCursor == iBeamCursor, check
  400.     that it's really in the text area of an edit window (and if not
  401.     set the cursor to an arrow instead).  Otherwise, set the cursor
  402.     to the given type (usually a watch).
  403.  
  404.     If the cursor is supposed to be set to an i-beam, it is assumed
  405.     that the globals are synced, because DoCursor changes them and
  406.     syncs them back.
  407.  
  408.     Pass -1 for theCursor to set the cursor to the arrow.
  409. */
  410.  
  411. static DoCursor (theCursor)
  412. int        theCursor;
  413. {
  414. Point    pt;
  415. GrafPtr    savePort;
  416.  
  417.     if (theCursor == iBeamCursor)            /* check whether there's an edit */
  418.     {                                        /* window in front and if so,    */
  419.         theCursor = -1;                        /* whether the cursor's in its   */
  420.         if (IsEWindow (FrontWindow ()))        /* text area                     */
  421.         {
  422.             GetPort (&savePort);
  423.             SyncGlobals (FrontWindow ());
  424.             GetMouse (&pt);
  425.             if (PtInText (pt))
  426.                 theCursor = iBeamCursor;
  427.             SyncGlobals (savePort);
  428.         }
  429.     }
  430.     SetCursor (theCursor == -1 ? &arrow : *GetCursor (theCursor));
  431. }
  432.  
  433.  
  434. /*
  435.     Calculate the number of lines currently scrolled off
  436.     the top.
  437. */
  438.  
  439. static LinesOffTop ()
  440. {
  441. register TEPtr    ePtr;
  442.  
  443.     ePtr = *editTE;
  444.     return (((*ePtr).viewRect.top - (*ePtr).destRect.top)
  445.                 / (*ePtr).lineHeight);
  446. }
  447.  
  448.  
  449. /*
  450.     Return the line number that the caret (or the beginning of
  451.     the currently selected text) is in.  Value returned is in
  452.     the range 0..(**editTE).nLines.  If = (**editTE).nLines, the
  453.     caret is past the last line.  The only special case to watch out
  454.     for is when the caret is at the very end of the text.  If the
  455.     last character is not a carriage return, then the caret is on
  456.     the (nLines-1)th line, not the (nLines)th line.
  457.  
  458.     (This really should do a binary search for speed.)
  459. */
  460.  
  461. static LineWithCaret ()
  462. {
  463. register int    i;
  464. register int    nLines;
  465. register int    teLength;
  466. register int    selStart;
  467. register int    lineStart;
  468.  
  469.     selStart = (**editTE).selStart;
  470.     nLines = (**editTE).nLines;
  471.     teLength = (**editTE).teLength;
  472.  
  473.     if (selStart == teLength)
  474.     {
  475.         if (teLength != 0 && (*((**editTE).hText))[teLength-1] != cr)
  476.             return (nLines - 1);
  477.         return (nLines);
  478.     }
  479.  
  480.     for (i = 0; /* empty */; ++i)
  481.     {
  482.         if ((lineStart = (**editTE).lineStarts[i]) >= selStart)
  483.         {
  484.             if (lineStart != selStart)
  485.                 --i;
  486.             return (i);
  487.         }
  488.     }
  489. }
  490.  
  491.  
  492. /*
  493.     Return the number of the last displayable line.  That's one
  494.     more than nLines if the text is empty or the last character
  495.     is a carriage return.
  496. */
  497.  
  498. static LastLine ()
  499. {
  500. register int    nLines;
  501. register int    teLength;
  502.  
  503.     nLines = (**editTE).nLines;
  504.     teLength = (**editTE).teLength;
  505.  
  506.     if (teLength == 0 || (*((**editTE).hText))[teLength-1] == cr)
  507.         nLines++;
  508.     return (nLines);
  509. }
  510.  
  511.  
  512. /*
  513.     Set the maximum value of the scroll bar.  It's set so that if
  514.     there's more text than fits in the window, the bottom line can
  515.     be scrolled up at least a little below the bottom of the window.
  516.  
  517.     The shenanigans with topLines and scrollableLines have to do with
  518.     the case where there may be less text than fills the window, but
  519.     most of it's scrolled off the top.  This can happen when you
  520.     scroll a bunch of stuff up, then delete everything visible in
  521.     the window.
  522. */
  523.  
  524. static SetScrollMax ()
  525. {
  526. register int    topLines;
  527. register int    scrollableLines;
  528. register int    max;
  529.  
  530.     topLines = LinesOffTop ();
  531.     scrollableLines = LastLine () - visLines;
  532.     max = (topLines > scrollableLines ? topLines : scrollableLines);
  533.  
  534.     if (max < 0)
  535.         max = 0;
  536.  
  537.     if (max != GetCtlMax (editScroll))
  538.     {
  539.         SetCtlMax (editScroll, max);
  540.         HiliteControl (editScroll, max > 0 ? 0 : 255);
  541.     }
  542. }
  543.  
  544.  
  545. /*
  546.     Set scroll bar current value (but only if it's different than
  547.     the current value, to avoid needless flashing).
  548. */
  549.  
  550. static SetScrollValue (value)
  551. int        value;
  552. {
  553.     if (GetCtlValue (editScroll) != value)
  554.         SetCtlValue (editScroll, value);
  555. }
  556.  
  557.  
  558. /*
  559.     Scroll to the correct position.  lDelta is the
  560.     amount to CHANGE the current scroll setting by.
  561. */
  562.  
  563. static ScrollText (lDelta)
  564. int        lDelta;
  565. {
  566. register int    topVisLine;
  567. register int    newTopVisLine;
  568.  
  569.     topVisLine = LinesOffTop ();
  570.     newTopVisLine = topVisLine + lDelta;
  571.     if (newTopVisLine < 0)                    /* clip to range */
  572.         newTopVisLine = 0;
  573.     if (newTopVisLine > GetCtlMax (editScroll))
  574.         newTopVisLine = GetCtlMax (editScroll);
  575.     SetScrollValue (newTopVisLine);
  576.     TEScroll (0, (topVisLine-newTopVisLine )*(**editTE).lineHeight, editTE);
  577. }
  578.  
  579.  
  580. /*
  581.     Scroll to home position without redrawing.
  582. */
  583.  
  584. static ScrollToHome ()
  585. {
  586. Rect                r;
  587.  
  588.     r = (**editTE).destRect;
  589.     OffsetRect (&r, 0, 2 - r.top);
  590.     (**editTE).destRect = r;
  591. }
  592.  
  593. /*
  594.     ClikLoop proc for autoscrolling text when the mouse is dragged out
  595.     of the text view rectangle.
  596.  
  597.     The clipping region has to be set to include the scroll bar,
  598.     because whenever this proc is called, TE has the region set down
  599.     to the view rectangle - if it's not reset, changes to the scroll
  600.     bar will not show up!
  601. */
  602.  
  603. static pascal Boolean AutoScroll ()
  604. {
  605. Point    p;
  606. Rect    r;
  607.  
  608.     SaveClipRgn ();
  609.     ClipRect (&editWind->portRect);
  610.     GetMouse (&p);
  611.     r = (**editTE).viewRect;
  612.     if (p.v < r.top)
  613.         ScrollText (-1);
  614.     else if (p.v > r.bottom)
  615.         ScrollText (1);
  616.     RestoreClipRgn ();
  617.     return (true);            /* true = 'keep tracking mouse' */
  618. }
  619.  
  620.  
  621. /*
  622.     Filter proc for tracking mousedown in scroll bar.  The code for
  623.     the part originally hit is shoved into the control's reference
  624.     value by Mouse() before this is called.
  625.  
  626.     I suspect odd scrolling may occur for hits in paging regions if
  627.     the window is allowed to size such that less than two lines show.
  628. */
  629.  
  630. static pascal void TrackScroll (theScroll, partCode)
  631. ControlHandle    theScroll;
  632. short                partCode;
  633. {
  634. register int    lDelta;
  635.  
  636.     if (partCode == GetCRefCon (theScroll))    /* still in same part? */
  637.     {
  638.         switch (partCode)
  639.         {
  640.             case inUpButton: lDelta = -1; break;
  641.             case inDownButton: lDelta = 1; break;
  642.             case inPageUp: lDelta = -(visLines - 1); break;
  643.             case inPageDown: lDelta = visLines - 1; break;
  644.         }
  645.         ScrollText (lDelta);
  646.     }
  647. }
  648.  
  649.  
  650. /*
  651.     Set the scroll bar properly and adjust the text in the
  652.     window so that the line containing the caret is visible.
  653.     If the line with the caret if more than a line outside of
  654.     the viewRect, try to place it in the middle of the window.
  655.  
  656.     Yes, it is necessary to SetScrollMax at the end.
  657. */
  658.  
  659. static AdjustDisplay ()
  660. {
  661. register int    caretLine;
  662. register int    topVisLine;
  663. register int    d;
  664.  
  665.     SetScrollMax ();
  666.     caretLine = LineWithCaret ();
  667.     topVisLine = LinesOffTop ();
  668.     if ((d = caretLine - topVisLine) < 0)
  669.         ScrollText (d == -1 ? -1 : d - visLines / 2);
  670.     else if (( d = caretLine - (topVisLine + visLines - 1)) > 0)
  671.         ScrollText (d == 1 ? 1 : d + visLines / 2);
  672.     else
  673.         SetScrollValue (topVisLine);
  674.     SetScrollMax ();    /* might have changed from scrolling */
  675. }
  676.  
  677.  
  678. /*
  679.     Overhaul the entire display.  This is called for major
  680.     catastrophes, such as resizing the window, or changes to
  681.     the word wrap style.  It makes sure the view and
  682.     destination rectangles are sized properly, and that the bottom
  683.     line of text never scrolls up past the bottom line of the
  684.     window, if there's enough to fill the window, and that the
  685.     scroll bar max and current values are set properly.
  686.  
  687.     Resizing the dest rect just means resetting the right edge
  688.     (the top is NOT reset), since text might be scrolled off the
  689.     top (i.e., destRect.top != 0).
  690.  
  691.     Doesn't redraw the control, though!
  692. */
  693.  
  694. static OverhaulDisplay (showCaret, recalc)
  695. Boolean    showCaret;
  696. Boolean    recalc;
  697. {
  698. Rect            r;
  699.  
  700.     r = (**editTE).viewRect;    /* erase current viewRect */
  701.     EraseRect (&r);
  702.     SetEditRect ();                /* recalculate editing rects */
  703.     if (recalc)
  704.         TECalText (editTE);            /* recalculate line starts */
  705.     visLines = ((**editTE).viewRect.bottom - (**editTE).viewRect.top)
  706.                     / (**editTE).lineHeight;
  707.  
  708. # ifndef    singleEdit
  709.     (**editInfo).visLines = visLines;
  710. # endif
  711.  
  712. /*
  713.     If there is text, but none of it is visible in the window
  714.     (it's all scrolled off the top), pull some down.
  715. */
  716.  
  717.     if (showCaret)
  718.         AdjustDisplay ();
  719.     else
  720.         SetScrollMax ();
  721.     r = (**editTE).viewRect;
  722.     TEUpdate (&r, editTE);
  723. }
  724.  
  725.  
  726. /* ---------------------------------------------------------------- */
  727. /*                        Window Handler Routines                        */
  728. /* ---------------------------------------------------------------- */
  729.  
  730.  
  731. /*
  732.     Handle mouse clicks in window.  The viewRect is never tested
  733.     directly, because if it were, clicks along the top, left and
  734.     bottom edges of the window wouldn't register.
  735. */
  736.  
  737. static Mouse (thePt, t, mods)
  738. Point    thePt;
  739. long    t;
  740. int        mods;
  741. {
  742. register int    thePart;
  743. register int    oldCtlValue;
  744.  
  745.     SyncGlobals (nil);        /* sync to current port */
  746.  
  747.     if ((thePart = TestControl (editScroll, thePt)) == inThumb)
  748.     {
  749.         oldCtlValue = GetCtlValue (editScroll);
  750.         if (TrackControl (editScroll, thePt, nil) == inThumb)
  751.             ScrollText (GetCtlValue (editScroll) - oldCtlValue);
  752.     }
  753.     else if (thePart != 0)
  754.     {
  755.         SetCRefCon (editScroll, (long) thePart);
  756.         (void) TrackControl (editScroll, thePt, (ProcPtr) TrackScroll);
  757.     }
  758.     else if (PtInText (thePt))
  759.     {
  760.         TEClick (thePt, (mods & shiftKey) != 0, editTE);
  761.     }
  762.  
  763.     SetScrollMax ();
  764. }
  765.  
  766.  
  767. /*
  768.     Handle key clicks in window
  769. */
  770.  
  771. static Key (c, mods)
  772. char    c;
  773. int        mods;
  774. {
  775.     SyncAllGlobals (nil);        /* sync to current port */
  776.  
  777.     if (eKeyFilter != nil)
  778.       c = (*eKeyFilter) (c);  /* Filter the character - L. Tierney */
  779.     if (c != enter && c != '\0') /* '\0' used to signal a character to be ignored */
  780.         TEKey (c, editTE);
  781.     AdjustDisplay ();
  782.     SetDirty (true);
  783.     if (eKey != nil)    /* report event to the host */
  784.         (*eKey) ();
  785. }
  786.  
  787.  
  788. /*
  789.     When the window comes active, highlight the scroll bar appropriately.
  790.     When the window is deactivated, un-highlight the scroll bar.
  791.     Redraw the grow box in any case.  Set the cursor (DoCursor avoids
  792.     changing it from an ibeam to an arrow back to an ibeam, in the case
  793.     where one edit window is going inactive and another is coming
  794.     active).
  795.  
  796.     Report the event to the host.
  797. */
  798.  
  799. static Activate (active)
  800. Boolean    active;
  801. {
  802.     SyncAllGlobals (nil);        /* sync to current port */
  803.  
  804.     DrawGrowBox ();
  805.     if (active)
  806.     {
  807.         TEActivate (editTE);
  808.         HiliteControl (editScroll, GetCtlMax (editScroll) > 0 ? 0 : 255);
  809.     }
  810.     else
  811.     {
  812.         TEDeactivate (editTE);
  813.         HiliteControl (editScroll, 255);
  814.     }
  815.     DoCursor (iBeamCursor);
  816.     if (eActivate != nil)    /* report event to the host */
  817.         (*eActivate) (active);
  818. }
  819.  
  820.  
  821. /*
  822.     Close box was clicked.  If user specified notify proc, call it.
  823.     Otherwise do default close operation (ask about saving if dirty,
  824.     etc.).
  825. */
  826.  
  827. static Close ()
  828. {
  829.     SyncAllGlobals (nil);        /* sync to current port */
  830.  
  831.     if (eClose != nil)
  832.         (*eClose) ();
  833.     else
  834.         (void) EWindowClose (editWind);
  835. }
  836.  
  837.  
  838. /*
  839.     Update window.  The update event might be in response to a
  840.     window resizing.  If so, move and resize the scroll bar.
  841.     The ValidRect call is done because the HideControl adds the
  842.     control bounds box to the update region - which would generate
  843.     another update event!  Since everything gets redrawn below,
  844.     the ValidRect is used to cancel the update.
  845. */
  846.  
  847. static Update (resized)
  848. Boolean    resized;
  849. {
  850. Rect            r;
  851.  
  852.     SyncGlobals (nil);        /* sync to current port */
  853.  
  854.     r = editWind->portRect;
  855.     EraseRect (&r);
  856.     if (resized)
  857.     {
  858.         HideControl (editScroll);
  859.         r = (**editScroll).contrlRect;
  860.         ValidRect (&r);
  861.         CalcScrollRect (&r);
  862.         SizeControl (editScroll, 16, r.bottom - r.top);
  863.         MoveControl (editScroll, r.left, r.top);
  864.         OverhaulDisplay (false, (**editTE).crOnly >= 0);
  865.         ShowControl (editScroll);
  866.     }
  867.     else
  868.     {
  869.         OverhaulDisplay (false, false);
  870.         DrawControls (editWind);    /* redraw scroll bar */
  871.     }
  872.  
  873.     DrawGrowBox ();
  874. }
  875.  
  876.  
  877. /*
  878.     Remove the edit window from the list, and dispose of it.
  879.     This is called by SkelRmveWind, not directly by user program.
  880.  
  881.     At this point it's too late to back out if any changes have been
  882.     made to the text.
  883.  
  884.     Since the clobber procedure is never called except for real edit
  885.     windows, and since the list must therefore be non-empty, it is
  886.     not necessary to check the legality of the window or that the
  887.     window's in the list.
  888. */
  889.  
  890. static Clobber ()
  891. {
  892. # ifndef    singleEdit
  893. register EIHandle    h, h2;
  894. # endif
  895.  
  896.     SyncGlobals (nil);                    /* sync to current port */
  897.  
  898. # ifndef    singleEdit
  899.  
  900.     if ((**ewList).editWind == editWind)    /* is it the first window in list? */
  901.     {
  902.         h2 = ewList;
  903.         ewList = (**ewList).eNext;
  904.     }
  905.     else
  906.     {
  907.         for (h = ewList; h != nil; h = h2)
  908.         {
  909.             h2 = (**h).eNext;
  910.             if ((**h2).editWind == editWind)    /* found it */
  911.             {
  912.                 (**h).eNext = (**h2).eNext;
  913.                 break;
  914.             }
  915.         }
  916.     }
  917.     DisposHandle ((Handle) h2);            /* get rid of information structure */
  918.  
  919. # endif
  920.     TEDispose (editTE);            /* toss text record */
  921.     DisposeWindow (editWind);    /* disposes of scroll bar, too */
  922.     editWind = nil;
  923. /*    DoCursor (iBeamCursor);*//* Taken out because of SetPort problems after window kill - L. Tierney */
  924. }
  925.  
  926.  
  927. /*
  928.     Blink the caret and make sure the cursor's an i-beam when it's
  929.     in the non-scrollbar part of the window.
  930. */
  931.  
  932. static Idle ()
  933. {
  934.     SyncGlobals (nil);
  935.     if (eIdle != nil) (*eIdle)(); /* L. Tierney */
  936.     TEIdle (editTE);            /* blink that caret! */
  937.     DoCursor (iBeamCursor);
  938. }
  939.  
  940.  
  941. /* ---------------------------------------------------------------- */
  942. /*                        Internal File Routines                        */
  943. /* ---------------------------------------------------------------- */
  944.  
  945.  
  946. static ErrMesg (s)
  947. StringPtr    s;
  948. {
  949.     (void) FakeAlert (s, "\p", "\p", "\p", 1, 1, "\pOK", "\p", "\p");
  950. }
  951.  
  952.  
  953. /*
  954.     Save the contents of the edit window.  If there is no file bound
  955.     to the window, ask for a file name.  If askForFile is true, ask
  956.     for a name even if the window is currently bound to a file.  If
  957.     bindToFile is true, bind the window to the file written to (if
  958.     that's different than the currently bound file), and clear the
  959.     window's dirty flag.
  960.  
  961.     Return true if the file was written without error.  Return false
  962.     if (a) user was asked for name and clicked Cancel (b) there was
  963.     some error writing the file.  In the latter case, the window is
  964.     not bound to any new name given by user.
  965.  
  966.     Always returns false if the window isn't an edit window.  This
  967.     simplifies EWindowSave, EWindowSaveAs, EWindowSaveCopy.  (They
  968.     don't do the test.)
  969. */
  970.  
  971. static Boolean SaveFile (theWind, askForFile, bindToFile)
  972. WindowPtr    theWind;
  973. Boolean        askForFile;
  974. Boolean        bindToFile;
  975. {
  976. short        f;
  977. FInfo    fndrInfo;    /* finder info */
  978. SFReply    tmpFile;
  979. Handle    hText;
  980. long    count;
  981. OSErr    result;
  982. Boolean    haveNewFile = false;
  983.  
  984.     if (!IsEWindow (theWind))
  985.         return (false);
  986.  
  987.     SyncAllGlobals (theWind);
  988.     if (bound == false || askForFile)
  989.     {
  990.         SFPutFile (dlogWhere, "\pSave file as:", editFile.fName,
  991.                         nil, &tmpFile);
  992.         if (!tmpFile.good)
  993.             return (false);
  994.         else
  995.         {
  996.             haveNewFile = true;
  997.             if (GetFInfo (tmpFile.fName, tmpFile.vRefNum, &fndrInfo)
  998.                     == noErr) /* exists */
  999.             {
  1000.                 if (fndrInfo.fdType != 'TEXT')
  1001.                 {
  1002.                     ErrMesg ("\pNot a TEXT File");
  1003.                     return (false);
  1004.                 }
  1005.             }
  1006.             else    /* doesn't exist.  create it. */
  1007.             {
  1008.                 if (Create (tmpFile.fName, tmpFile.vRefNum,
  1009.                             creator, 'TEXT') != noErr)
  1010.                 {
  1011.                     ErrMesg ("\pCan't Create");
  1012.                     return (false);
  1013.                 } 
  1014.             }
  1015.         }
  1016.     }
  1017.     
  1018. /*************************************************************************/
  1019. /*             Added to make saving to existing file work - LjT             */
  1020. /*************************************************************************/
  1021.     else {
  1022. # ifdef    singleEdit
  1023.         tmpFile = editFile;
  1024. # else
  1025.         tmpFile = (**editInfo).editFile;
  1026. # endif
  1027.     }
  1028. /*************************************************************************/
  1029. /*             Added to make saving to existing file work - LjT             */
  1030. /*************************************************************************/
  1031.     
  1032.     if ( FSOpen (tmpFile.fName, tmpFile.vRefNum, &f) != noErr)
  1033.         ErrMesg ("\pCan't Open");
  1034.     else
  1035.     {
  1036.         DoCursor (watchCursor);
  1037.         (void) SetFPos (f, fsFromStart, 0L);
  1038.         hText = (**editTE).hText;
  1039.         HLock (hText);
  1040.         count = (**editTE).teLength;
  1041.         result = FSWrite (f, &count, *hText);
  1042.         (void) GetFPos (f, &count);
  1043.         (void) SetEOF (f, count);
  1044.         (void) FSClose (f);
  1045.         (void) FlushVol (nil, tmpFile.vRefNum);
  1046.         HUnlock (hText);
  1047.         DoCursor (iBeamCursor);
  1048.         if (result == noErr)
  1049.         {
  1050.             if (bindToFile)
  1051.             {
  1052.                 SetDirty (false);
  1053.                 if (haveNewFile)    /* name different than current */
  1054.                 {
  1055.                     SetWTitle (editWind, tmpFile.fName);
  1056.  
  1057. # ifdef    singleEdit
  1058.                     bound = true;
  1059.                     editFile = tmpFile;
  1060. # else
  1061.                     (**editInfo).bound = true;
  1062.                     (**editInfo).editFile = tmpFile;
  1063. # endif
  1064.  
  1065.                 }
  1066.             }
  1067.             return (true);
  1068.         }
  1069.         ErrMesg ("\pWrite error!");
  1070.     }
  1071.     return (false);
  1072. }
  1073.  
  1074.  
  1075. /*
  1076.     Revert to version of file saved on disk.  Doesn't check whether
  1077.     the window's really bound to a file or not, doesn't ask whether
  1078.     to really revert if the window's dirty, does no redrawing, etc.
  1079.     Just reports whether the file was read in successfully.
  1080. */
  1081.  
  1082. static Boolean Revert ()
  1083. {
  1084. Boolean    result = false;
  1085. short        f;
  1086. long    len;
  1087. Handle    h;
  1088.  
  1089.     DoCursor (watchCursor);
  1090.     if (FSOpen (editFile.fName, editFile.vRefNum, &f) != noErr)
  1091.         ErrMesg ("\pCouldn't open file");
  1092.     else
  1093.     {
  1094.         (void) GetEOF (f, &len);
  1095.         if (len >= 32000)
  1096.             ErrMesg ("\pFile is too big");
  1097.         else
  1098.         {
  1099.             h = (Handle) TEGetText (editTE);
  1100.             SetHandleSize (h, len);
  1101.             HLock (h);
  1102.             (void) FSRead (f, &len, *h);
  1103.             HUnlock (h);
  1104.             (**editTE).teLength = len;
  1105.             TESetSelect (0L, 0L, editTE);    /* set caret at start */
  1106.             result = true;
  1107.             SetDirty (false);
  1108.         }
  1109.         (void) FSClose (f);
  1110.     }
  1111.     DoCursor (iBeamCursor);
  1112.     return (result);
  1113. }
  1114.  
  1115.  
  1116. /* ------------------------------------------------------------ */
  1117. /*            Lowest-level Interface (Public) Routines            */
  1118. /* ------------------------------------------------------------ */
  1119.  
  1120.  
  1121. /*
  1122.     Return true/false to indicate whether the window is really an
  1123.     edit window.
  1124. */
  1125.  
  1126. Boolean IsEWindow (theWind)
  1127. WindowPtr    theWind;
  1128. {
  1129. # ifdef    singleEdit
  1130.     return (theWind == editWind && editWind != nil);
  1131. # else
  1132.     return (GetEInfo (theWind) != nil);
  1133. # endif
  1134. }
  1135.  
  1136.  
  1137. /*
  1138.     Return true/false to indicate whether the text associated with
  1139.     the window has been changed since the last save/revert (or since
  1140.     created, if not bound to file).
  1141. */
  1142.  
  1143. Boolean IsEWindowDirty (theWind)
  1144. WindowPtr    theWind;
  1145. {
  1146. # ifndef    singleEdit
  1147. register EIHandle    eInfo;
  1148.     if ((eInfo = GetEInfo (theWind)) != nil)
  1149.         return ((**eInfo).dirty);
  1150. # else
  1151.     if (IsEWindow (theWind))
  1152.         return (dirty);
  1153. # endif
  1154.     return (false);
  1155. }
  1156.  
  1157. /* change setting of dirty - L. Tierney */
  1158. SetEWindowDirty (theWind, ndirty)
  1159. WindowPtr    theWind;
  1160. int ndirty;
  1161. {
  1162. # ifndef    singleEdit
  1163. register EIHandle    eInfo;
  1164.     if ((eInfo = GetEInfo (theWind)) != nil)
  1165.         (**eInfo).dirty = ndirty;
  1166. # else
  1167.     if (IsEWindow (theWind))
  1168.         dirty = ndirty;
  1169. # endif
  1170.     return (false);
  1171. }
  1172.  
  1173. /*
  1174.     Return a handle to the TextEdit record associated with the edit
  1175.     window, or nil if it's not an edit window
  1176. */
  1177.  
  1178. TEHandle GetEWindowTE (theWind)
  1179. WindowPtr    theWind;
  1180. {
  1181. # ifndef    singleEdit
  1182. register EIHandle    eInfo;
  1183.     if ((eInfo = GetEInfo (theWind)) != nil)
  1184.         return ((**eInfo).editTE);
  1185. # else
  1186.     if (IsEWindow (theWind))
  1187.         return (editTE);
  1188. # endif
  1189.     return (nil);
  1190. }
  1191.  
  1192.  
  1193. /*
  1194.     Return true/false depending on whether the editor is bound to
  1195.     a file or not, and a copy of the file info in the second
  1196.     argument.  Pass nil for fileInfo if only want the return status.
  1197.     Returns false if it's not an edit window.
  1198. */
  1199.  
  1200. Boolean GetEWindowFile (theWind, fileInfo)
  1201. WindowPtr    theWind;
  1202. SFReply        *fileInfo;
  1203. {
  1204. # ifndef    singleEdit
  1205. register EIHandle    eInfo;
  1206.     if ((eInfo = GetEInfo (theWind)) != nil)
  1207.     {
  1208.         if (fileInfo != nil)
  1209.             *fileInfo = (**eInfo).editFile;
  1210.         return ((**eInfo).bound);
  1211.     }
  1212. # else
  1213.     if (IsEWindow (theWind))
  1214.     {
  1215.         if (fileInfo != nil)
  1216.             *fileInfo = editFile;
  1217.         return (bound);
  1218.     }
  1219. # endif
  1220.     return (false);
  1221. }
  1222.  
  1223.  
  1224. /* ---------------------------------------------------------------- */
  1225. /*                    Interface Display Routines                        */
  1226. /* ---------------------------------------------------------------- */
  1227.  
  1228.  
  1229. /*
  1230.     Install event notification procedures for an edit window.
  1231. */
  1232.  
  1233. SetEWindowProcs (theWind, pKey, pActivate, pClose)
  1234. WindowPtr    theWind;
  1235. ProcPtr        pKey;
  1236. ProcPtr        pActivate;
  1237. ProcPtr        pClose;
  1238. {
  1239. # ifndef    singleEdit
  1240. register EIHandle    eInfo;
  1241. # endif
  1242.  
  1243.     if (theWind == nil)            /* reset window creation defaults */
  1244.     {
  1245.         e_key = pKey;
  1246.         e_activate = pActivate;
  1247.         e_close = pClose;
  1248.         return;
  1249.     }
  1250.  
  1251. # ifndef    singleEdit
  1252.  
  1253.     if ((eInfo = GetEInfo (theWind)) != nil)
  1254.     {
  1255.         (**eInfo).eKey = pKey;
  1256.         (**eInfo).eActivate = pActivate;
  1257.         (**eInfo).eClose = pClose;
  1258.     }
  1259.  
  1260. # else
  1261.  
  1262.     if (IsEWindow (theWind))
  1263.     {
  1264.         eKey = pKey;
  1265.         eActivate = pActivate;
  1266.         eClose = pClose;
  1267.     }
  1268.  
  1269. # endif
  1270.  
  1271. }
  1272.  
  1273. /* add filter for key clicks - L. Tieryen */
  1274. SetEWindowKeyFilter (theWind, pKeyFilter)
  1275. WindowPtr    theWind;
  1276. ProcPtr        pKeyFilter;
  1277. {
  1278. # ifndef    singleEdit
  1279. register EIHandle    eInfo;
  1280. # endif
  1281.  
  1282. # ifndef    singleEdit
  1283.  
  1284.     if ((eInfo = GetEInfo (theWind)) != nil)
  1285.         (**eInfo).eKeyFilter = pKeyFilter;
  1286. # else
  1287.  
  1288.     if (IsEWindow (theWind))
  1289.         eKeyFilter = pKeyFilter;
  1290. # endif
  1291. }
  1292.  
  1293. /* add idle procedure - L. Tieryen */
  1294. SetEWindowIdle (theWind, pIdle)
  1295. WindowPtr    theWind;
  1296. ProcPtr        pIdle;
  1297. {
  1298. # ifndef    singleEdit
  1299. register EIHandle    eInfo;
  1300. # endif
  1301.  
  1302. # ifndef    singleEdit
  1303.  
  1304.     if ((eInfo = GetEInfo (theWind)) != nil)
  1305.         (**eInfo).eIdle = pIdle;
  1306. # else
  1307.  
  1308.     if (IsEWindow (theWind))
  1309.         eIdle = pIdle;
  1310. # endif
  1311. }
  1312.  
  1313. /*
  1314.     Change the text display characteristics of an edit window
  1315.     and redisplay it.
  1316.  
  1317.     Scroll to home position before overhauling, because although
  1318.     the overhaul sets the viewRect to display an integral number
  1319.     of lines, there's no guarantee that the destRect offset will
  1320.     also be integral except at home position.  Clipping is set to
  1321.     an empty rect so the scroll doesn't show.
  1322. */
  1323.  
  1324. SetEWindowStyle (theWind, font, size, wrap, just)
  1325. WindowPtr    theWind;
  1326. int            font;
  1327. int            size;
  1328. int            wrap;
  1329. int            just;
  1330. {
  1331. GrafPtr                savePort;
  1332. FontInfo            f;
  1333. register TEHandle    te;
  1334. int                    oldWrap;
  1335.  
  1336.     if (theWind == nil)            /* reset window creation defaults */
  1337.     {
  1338.         e_font = font;
  1339.         e_size = size;
  1340.         e_wrap = wrap;
  1341.         e_just = just;
  1342.         return;
  1343.     }
  1344.  
  1345.     if (IsEWindow (theWind))
  1346.     {
  1347.         GetPort (&savePort);
  1348.         SyncGlobals (theWind);    /* sync and set port */
  1349.         te = editTE;
  1350.         ScrollToHome ();
  1351.  
  1352.         oldWrap = (**te).crOnly;
  1353.         (**te).crOnly = wrap;    /* set word wrap */
  1354.         TESetJust (just, te);    /* set justification */
  1355.  
  1356.         TextFont (font);         /* set the font and point size */
  1357.         TextSize (size);        /* of text record */
  1358.         GetFontInfo (&f);
  1359.         (**te).lineHeight = f.ascent + f.descent + f.leading;
  1360.         (**te).fontAscent = f.ascent;
  1361.         (**te).txFont = font;
  1362.         (**te).txSize = size;
  1363.  
  1364.         OverhaulDisplay (true, (oldWrap >= 0 || wrap >= 0));
  1365.  
  1366.         SetPort (savePort);
  1367.     }
  1368. }
  1369.  
  1370.  
  1371. /*
  1372.     Redo display.  Does not save current port.  This is used by hosts
  1373.     that mess with the text externally to TransEdit.  The arguments
  1374.     determine whether the text is scrolled to show the line with the
  1375.     caret, whether the lineStarts are recalculated, and whether the
  1376.     text should be marked dirty or not.
  1377. */
  1378.  
  1379. EWindowOverhaul (theWind, showCaret, recalc, dirty)
  1380. WindowPtr    theWind;
  1381. Boolean        showCaret;
  1382. Boolean        recalc;
  1383. Boolean        dirty;
  1384. {
  1385.     if (IsEWindow (theWind))
  1386.     {
  1387.         SyncGlobals (theWind);
  1388.         OverhaulDisplay (showCaret, recalc);
  1389.         DrawControls (editWind);
  1390.         SetDirty (dirty);
  1391.     }
  1392. }
  1393.  
  1394. /* show caret - L. Tierney */
  1395. EWindowAdjustDisplay(theWind)
  1396. WindowPtr    theWind;
  1397. {
  1398.     if (IsEWindow (theWind))
  1399.     {
  1400.         SyncGlobals (theWind);
  1401.         AdjustDisplay();
  1402.     }
  1403. }
  1404.  
  1405. /* ---------------------------------------------------------------- */
  1406. /*                        Menu Interface Routine                        */
  1407. /* ---------------------------------------------------------------- */
  1408.  
  1409.  
  1410. /*
  1411.     Do Edit menu selection.  This is only valid if an edit
  1412.     window is frontmost.
  1413. */
  1414.  
  1415. EWindowEditOp (item)
  1416. int        item;
  1417. {
  1418.  
  1419.     if (!IsEWindow (FrontWindow ()))
  1420.         return;                /* host messed up */
  1421.  
  1422.     SyncGlobals (FrontWindow ());
  1423.  
  1424.     switch (item)
  1425.     {
  1426.  
  1427. /*
  1428.     cut selection, put in TE Scrap, clear clipboard and put
  1429.     TE scrap in it
  1430. */
  1431.         case cut:
  1432.         {
  1433.             TECut (editTE);
  1434.             (void) ZeroScrap ();
  1435.             (void) TEToScrap ();
  1436.             break;
  1437.         }
  1438. /*
  1439.     copy selection to TE Scrap, clear clipboard and put
  1440.     TE scrap in it
  1441. */
  1442.         case copy:
  1443.         {
  1444.             TECopy (editTE);
  1445.             (void) ZeroScrap ();
  1446.             (void) TEToScrap ();
  1447.             break;
  1448.         }
  1449. /*
  1450.     get clipboard into TE scrap, put TE scrap into edit record
  1451. */
  1452.         case paste:
  1453.         {
  1454.             (void) TEFromScrap ();
  1455.             TEPaste (editTE);
  1456.             break;
  1457.         }
  1458. /*
  1459.     delete selection without putting into TE scrap or clipboard
  1460. */
  1461.         case clear:
  1462.         {
  1463.             TEDelete (editTE);
  1464.             break;
  1465.         }
  1466.  
  1467.     }
  1468.     AdjustDisplay ();
  1469.     if (item != copy)    /* L. Tierney */
  1470.       SetDirty (true);
  1471. }
  1472.  
  1473.  
  1474. /* ---------------------------------------------------------------- */
  1475. /*                        Interface File Routines                        */
  1476. /* ---------------------------------------------------------------- */
  1477.  
  1478.  
  1479. /*
  1480.     Set file creator for any files created by TransEdit
  1481. */
  1482.  
  1483. SetEWindowCreator (creat)
  1484. OSType    creat;
  1485. {
  1486.     creator = creat;
  1487. }
  1488.  
  1489.  
  1490.  
  1491. /*
  1492.     Save the contents of the given window
  1493. */
  1494.  
  1495. Boolean EWindowSave (theWind)
  1496. WindowPtr    theWind;
  1497. {
  1498.     return (SaveFile (theWind,    /* window to save */
  1499.                       false,    /* don't ask for file if have one */
  1500.                       true));    /* bind to new file if one given */
  1501. }
  1502.  
  1503.  
  1504. /*
  1505.     Save the contents of the given window under a new name
  1506.     and bind to that name.
  1507. */
  1508.  
  1509. Boolean EWindowSaveAs (theWind)
  1510. WindowPtr    theWind;
  1511. {
  1512.     return (SaveFile (theWind,    /* window to save */
  1513.                       true,        /* ask for file even if have one */
  1514.                       true));    /* bind to new file if one given */
  1515. }
  1516.  
  1517.  
  1518. /*
  1519.     Save the contents of the given window under a new name, but
  1520.     don't bind to the name.
  1521. */
  1522.  
  1523. Boolean EWindowSaveCopy (theWind)
  1524. WindowPtr    theWind;
  1525. {
  1526.     return (SaveFile (theWind,    /* window to save */
  1527.                       true,        /* ask for file even if have one */
  1528.                       false));    /* don't bind to file */
  1529. }
  1530.  
  1531.  
  1532. /*
  1533.     Close the window.  If it's dirty and is either bound to a file
  1534.     or (if not bound) has some text in it, ask about saving it first,
  1535.     giving user option of saving changes, tossing them, or
  1536.     cancelling altogether.
  1537.  
  1538.     Return true if the file was saved and the window closed, false if
  1539.     user cancelled or there was an error.
  1540. */
  1541.  
  1542. Boolean EWindowClose (theWind)
  1543. WindowPtr    theWind;
  1544. {
  1545.     if (IsEWindow (theWind) == false)
  1546.         return (false);
  1547.  
  1548.     SyncAllGlobals (theWind);
  1549.     if ( (bound || (**editTE).teLength > 0) && dirty)
  1550.     {
  1551.         switch (FakeAlert ("\pSave changes to \"", editFile.fName,
  1552.                 "\p\"?", "\p", 3, 3,
  1553.                 "\pCancel", "\pDiscard", "\pSave"))    /* ask whether to save */
  1554.         {
  1555.  
  1556.             case 1:            /* cancel Close */
  1557.                 return (false);
  1558.  
  1559.             case 2:            /* toss changes */
  1560.                 break;
  1561.  
  1562.             case 3:
  1563.                 if (SaveFile (editWind,    /* window to save */
  1564.                               false,    /* don't ask for name */
  1565.                               false)    /* don't bind to name */
  1566.                         == false)
  1567.                     return (false);    /* cancelled or error - cancel Close */
  1568.                 break;
  1569.         }
  1570.     }
  1571.     SkelRmveWind (editWind);
  1572.     return (true);
  1573. }
  1574.  
  1575.  
  1576. /*
  1577.     Revert to saved version of file on disk.  theWind must be an edit
  1578.     window, and must be bound to a file.  Returns false if one of these
  1579.     conditions is not met, or if they are met but there was an error
  1580.     reading the file.
  1581.  
  1582.     The window need not be dirty, but if it is, the user is asked
  1583.     whether to really revert.
  1584. */
  1585.  
  1586. Boolean EWindowRevert (theWind)
  1587. WindowPtr    theWind;
  1588. {
  1589.     if (!IsEWindow (theWind))
  1590.         return (false);
  1591.     SyncAllGlobals (theWind);
  1592.     if (!bound)
  1593.         return (false);        /* no file to revert to */
  1594.     if (dirty)
  1595.     {
  1596.         if (FakeAlert ("\p\"", editFile.fName,
  1597.                 "\p\" has been changed.  Really revert?",
  1598.                 "\p", 2, 1, "\pCancel", "\pRevert", "\p") == 1)
  1599.             return (false);
  1600.     }
  1601.     if (Revert () == false)
  1602.         return (false);
  1603.     ScrollToHome ();
  1604.     OverhaulDisplay (true, true);
  1605.     DrawControls (editWind);
  1606.     ValidRect (&editWind->portRect);
  1607.     return (true);
  1608. }
  1609.  
  1610.  
  1611. /* ---------------------------------------------------------------- */
  1612. /*            Interface Initialization/Termination Routines            */
  1613. /* ---------------------------------------------------------------- */
  1614.  
  1615.  
  1616. /*
  1617.     Initialize the window and associated data structures.
  1618.     Return window pointer or nil if some sort of error.
  1619.  
  1620.     Preserves the current port.
  1621. */
  1622.  
  1623. WindowPtr NewEWindow (bounds, title, visible, behind,
  1624.                             goAway, refNum, bindToFile)
  1625. Rect        *bounds;
  1626. StringPtr    title;
  1627. Boolean        visible;
  1628. WindowPtr    behind;
  1629. Boolean        goAway;
  1630. long        refNum;
  1631. Boolean        bindToFile;
  1632. {
  1633. GrafPtr        savePort;
  1634. Rect        r;
  1635. OSType        type = 'TEXT';
  1636. Str255        s, s2;
  1637. StringPtr    tPtr;
  1638.  
  1639. # ifndef    singleEdit
  1640. register EIHandle    eInfo;
  1641. # endif
  1642.  
  1643. # ifdef    singleEdit
  1644.  
  1645.     if (editWind != nil)    /* allow only one window at a time */
  1646.         return (nil);
  1647.  
  1648. # endif
  1649.  
  1650. /*
  1651.     If supposed to bind to file, ask for name.  Return without doing
  1652.     anything if Cancel button clicked.
  1653. */
  1654.  
  1655.     if (bindToFile)
  1656.     {
  1657.         SFGetFile (dlogWhere, "\p", nil, 1, &type, nil, &editFile);
  1658.         if (!editFile.good)
  1659.             return (nil);
  1660.     }
  1661.     bound = bindToFile;
  1662.  
  1663. /*
  1664.     Create window and install handler.  Set window title:  If window is
  1665.     to be bound to file, use name of file.  Otherwise use any title that
  1666.     was passed in.  If nil was passed, use a default name ("Untitled nnn").
  1667.     Also copy the name into the file info structure even if the window is
  1668.     unbound, because the Save operations expect to find it there as the
  1669.     most likely name to use if the window is untitled.
  1670.  
  1671.     Save and restore port, because it gets reset by the rest of the
  1672.     initialization code.
  1673. */
  1674.  
  1675.     if (bound)
  1676.         tPtr = editFile.fName;
  1677.     else
  1678.     {
  1679.         if (title != nil)
  1680.             tPtr = title;
  1681.         else
  1682.         {
  1683.  
  1684. # ifndef    singleEdit
  1685.             BlockMove ("\pUntitled ", s, 10L);
  1686.             NumToString ((long) ++windID, s2);
  1687.             BlockMove (&s2[1], &s[10], (long) s2[0]);
  1688.             s[0] += s2[0];
  1689.             tPtr = s;
  1690. # else
  1691.             tPtr = (StringPtr) "\pUntitled";
  1692. # endif
  1693.  
  1694.         }
  1695.         BlockMove (tPtr, editFile.fName, (long) (tPtr[0] + 1));
  1696.     }
  1697.  
  1698.     editWind = NewWindow (nil, bounds, tPtr, false, 8 /* documentProc */,
  1699.                                 behind, goAway, refNum);
  1700.  
  1701.     GetPort (&savePort);
  1702.     SkelWindow (editWind,    /* the window */
  1703.                 Mouse,        /* mouse click handler */
  1704.                 Key,        /* key click handler */
  1705.                 Update,        /* window updating procedure */
  1706.                 Activate,    /* window activate/deactivate procedure */
  1707.                 Close,        /* window close procedure */
  1708.                 Clobber,    /* window disposal procedure */
  1709.                 Idle,        /* idle proc */
  1710.                 true);        /* idle only when frontmost */
  1711.  
  1712.  
  1713. /*
  1714.     Build the scroll bar.
  1715. */
  1716.  
  1717.     CalcScrollRect (&r);
  1718.     editScroll = NewControl (editWind, &r, "\p", true, 0, 0, 0,
  1719.                                 scrollBarProc, 0L);
  1720.  
  1721.  
  1722.  
  1723. /*
  1724.     Create the TE record used for text display.  Use default
  1725.     characteristics.
  1726. */
  1727.  
  1728.     GetEditRect (&r);
  1729.     editTE = TENew (&r, &r);
  1730.     SetClikLoop (AutoScroll, editTE);            /* set autoscroll proc */
  1731.  
  1732.  
  1733. # ifndef    singleEdit
  1734.  
  1735. /*
  1736.     Get new information structure, attach to list of known edit
  1737.     windows.
  1738. */
  1739.  
  1740.     eInfo = New (EditInfo);
  1741.     editInfo = eInfo;
  1742.     (**eInfo).eNext = ewList;
  1743.     ewList = eInfo;
  1744.     (**eInfo).editWind = editWind;
  1745.     (**eInfo).scroll = editScroll;
  1746.     (**eInfo).editTE = editTE;
  1747.     (**eInfo).bound = bound;
  1748.     (**eInfo).editFile = editFile;
  1749.  
  1750. # endif
  1751.  
  1752. /*
  1753.     Install default event notification procedures, font characteristics.
  1754. */
  1755.  
  1756.     SetEWindowProcs (editWind, e_key, e_activate, e_close);
  1757.     SetEWindowStyle (editWind, e_font, e_size, e_wrap, e_just);
  1758.     SetEWindowKeyFilter (editWind, e_key_filter); /* L. Tierney */
  1759.     SetEWindowIdle (editWind, e_idle); /* L. Tierney */
  1760.     SetDirty (false);
  1761.  
  1762. /*
  1763.     If supposed to read file, do so.  Check the return value of
  1764.     Revert and toss the window if there was an error.
  1765. */
  1766.  
  1767.     if (bindToFile && Revert () == false)
  1768.     {
  1769.         SkelRmveWind (editWind);
  1770.         SetPort (savePort);
  1771.         return (nil);
  1772.     }
  1773.  
  1774. /*
  1775.     Show window if specified as visible, and return a pointer to it.
  1776. */
  1777.  
  1778.     SyncGlobals (editWind);
  1779.     OverhaulDisplay (true, true);
  1780.     if (visible)
  1781.         ShowWindow (editWind);
  1782.     SetPort (savePort);
  1783.     return (editWind);
  1784. }
  1785.  
  1786.  
  1787. /*
  1788.     Look through the list of windows, shutting down all the edit
  1789.     windows.  If any window is dirty, ask user about saving it first.
  1790.     If the user cancels on any such request, ClobberEWindows returns
  1791.     false.  If all edit windows are shut down, return true.  It is
  1792.     then safe for the host to exit.
  1793.  
  1794.     When a window *is* shut down, have to start looking through the
  1795.     window list again, since theWind no longer points anywhere
  1796.     meaningful.
  1797. */
  1798.  
  1799. Boolean ClobberEWindows ()
  1800. {
  1801. WindowPtr    theWind;
  1802.  
  1803.     for (;;)
  1804.     {
  1805.         for (theWind = FrontWindow ();
  1806.                 theWind != nil;
  1807.                     theWind = (WindowPtr) ((WindowPeek) theWind)->nextWindow)
  1808.         {
  1809.             if (IsEWindow (theWind))
  1810.                 break;
  1811.         }
  1812.         if (theWind == nil)
  1813.             return (true);        /* all edit windows are shut down */
  1814.  
  1815.         if (theWind != FrontWindow ())
  1816.         {
  1817.             SelectWindow (theWind);
  1818.             ShowWindow (theWind);
  1819.             EWindowOverhaul (theWind, false, false, IsEWindowDirty (theWind));
  1820.             SetPort (theWind);
  1821.             ValidRect (&theWind->portRect);
  1822.         }
  1823.  
  1824.         if (EWindowClose (theWind) == false)
  1825.             return (false);        /* cancel or error */
  1826.     }
  1827. }
  1828.