home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 August: Technology Seed / ADC Seed CD - August 1999.toast / Carbon SDK 1.0d10c3 / Sample Code / SimpleText / TextFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-05-04  |  101.3 KB  |  3,636 lines  |  [TEXT/CWIE]

  1. #include <stdio.h>
  2. /*
  3.     File:        TextFile.c
  4.  
  5.     Contains:    Text file support for simple text application.
  6.  
  7.     Version:    SimpleText 1.4 or later
  8.  
  9.     Copyright:    © 1993-1997 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     File Ownership:
  12.  
  13.         DRI:                Tom Dowdy
  14.  
  15.         Other Contact:        Jim Negrette
  16.  
  17.         Technology:            Macintosh Graphics Group
  18.  
  19.     Writers:
  20.  
  21.         (DH)    Dave Hersey
  22.         (dmp)    Dave Polaschek
  23.         (ecs)    Eric Schlegel
  24.         (ted)    Tom Dowdy
  25.         (Gr)    Greg Robbins
  26.         (TD)    Tom Dowdy
  27.         (DAL)    Dave Lyons
  28.  
  29.     Change History (most recent first):
  30.  
  31.     $Log: TextFile.c,v $
  32.     Revision 1.22  1999/05/04 20:03:35  jiarocci
  33.     Fix for #2331923 - Expletive in SimpleText sample code.
  34.     
  35.     Revision 1.21  1999/05/01 01:52:25  jiarocci
  36.     Comment out grafprocs so we build against latest CarbonLib interfaces on 8.x.
  37.  
  38.     Revision 1.20  1999/04/01 22:54:30  devans
  39.     Add code to compile and execute selections as applescripts.
  40.  
  41.     Revision 1.19  1999/02/19 22:11:50  danp
  42.     Minor changes for opaque regions.
  43.     
  44.     Revision 1.18  1999/02/16 00:41:41  christ
  45.     Integrate document proxy icon support
  46.     
  47.     Revision 1.17  1999/02/10 18:25:52  danp
  48.     Adding Andy Carol's Changes
  49.     
  50.     Revision 1.16  1999/01/08 04:42:02  christ
  51.     Text dragging support is back.
  52.     
  53.     Revision 1.15  1998/12/18 00:42:30  wilkes
  54.     Removed annoying DebugStr's.
  55.     
  56.     Revision 1.14  1998/11/25 21:01:22  wilkes
  57.     Removed all GX references.
  58.  
  59.     Revision 1.13  1998/11/11 22:28:59  wilkes
  60.     Fixed various problems caused by the interface changes made by Nitin earlier,
  61.     mostly involving static RoutineDescriptors...
  62.  
  63.     Revision 1.12  1998/10/14 18:52:47  voas
  64.     Eliminate all warnings. Get working with top of tree.
  65.  
  66.     Revision 1.11  1998/10/12 18:59:04  danp
  67.     Modifications to allow cross-compiling and for CarbonLib support. Removed lots of GX stuff
  68.     
  69.     Revision 1.10  1998/09/30 02:19:55  jiarocci
  70.     Fix timeout calculation to use GetCarretTime().
  71.     
  72.     Revision 1.9  1998/09/15 18:59:50  jiarocci
  73.     SimpleText now builds with -DTARGET_CARBON=1. Still needs further cleanup.
  74.  
  75.     Revision 1.8  1998/09/02 20:45:55  danp
  76.     Changes so it works with blue mwerks
  77.  
  78.  
  79.     Revision 1.7  1998/09/02 00:18:57  voas
  80.     Fix silly bug in RecalcTE
  81.  
  82.     Revision 1.6  1998/05/10 08:44:56  devans
  83.     Default open files to font size 18 point for the demos...
  84.     
  85.     Revision 1.5  1998/05/10 07:28:37  devans
  86.     Hack for TextEdit font size bug.  Set and change the font size when
  87.     we first fill in a TextEdit handle.  This causes the right calcs to
  88.     occur.  Also return NULL from OpenDoc instead of whatever was on the
  89.     stack.
  90.     
  91.     Revision 1.4  1998/05/06 22:21:27  devans
  92.     Replace usage of qd.arrow with GetQDarrow().
  93.     
  94.     Revision 1.3  1998/03/30 22:12:32  mkellner
  95.     Update to use new GetQDxxxx macros for qd.globals
  96.     
  97.     Revision 1.2  1998/03/20 03:20:08  mkellner
  98.     change qd.thePort to FrontWindow()
  99.     add SysEnvirons
  100.     
  101.     Revision 1.1.1.1  1998/03/18 22:56:12  ivory
  102.     Initial checkin of SimpleText.
  103.     
  104.         
  105.         8     11/5/97 7:45 AM Tom Dowdy
  106.         Saving a stationary could sometimes reset original type, dont do that
  107.         any longer.
  108.         
  109.         7     8/20/97 4:23 PM Tom Dowdy
  110.         1674136: adjust cursor after select all
  111.         
  112.         6     8/11/97 3:35 PM Tom Dowdy
  113.         Nav services now working!
  114.         
  115.         5     8/11/97 3:04 PM Tom Dowdy
  116.         rolling in nav services
  117.         
  118.         4     8/7/97 1:34 PM Tom Dowdy
  119.         picture header leak, 1671661
  120.         
  121.         3     7/29/97 2:05 PM Tom Dowdy
  122.         Removed all of the old and boring refs
  123.         
  124.         2     7/29/97 1:51 PM Tom Dowdy
  125.         Various new interface fixes
  126.         
  127.         1     7/28/97 11:24 AM Duane Byram
  128.         first added to Source Safe project
  129.  
  130.         <43>     6/18/97    ted        Use Get1Resource for pictures
  131.         <42>     6/17/97    ted        [1662824]  Fixing how style undo is handled
  132.         <41>     5/29/97    ted        [1659294]  undo reformat bug
  133.         <40>     5/19/97    ted        [1655162]  PICTs not printing
  134.         <39>     2/12/97    DH        TextReadDataFork wasn't initializing "anErr" if no data fork was
  135.                                     open.
  136.         <38>     1/13/97    ted        [1616537]  replace all "forever"
  137.         <37>      1/2/97    ecs        split TextMakeWindow into separate functions to make profiling
  138.                                     possible; added a thread to build the Voices menu to improve
  139.                                     boot speed
  140.         <36>    12/19/96    ted        [1395082]  Forward delete w/ new TE
  141.         <35>     12/4/96    ted        [1157761]  stationary does not need a special file type (thanks, Finder!)
  142.         <34>     12/2/96    ted        [1608152]  Fixing shift arrow selection for double bytes
  143.         <33>    11/26/96    ecs        support GXGraphics extension; smarter cursor adjustment
  144.         <32>    11/13/96    ted        [1603950]  Fixing undo of style
  145.         <31>    10/23/96    ted        [1289037]  fixing 32k arrow key problem
  146.         <30>    10/10/96    ted        changing implementforwarddelete to TE6 version
  147.         <29>     10/9/96    ted        [1395082]  Fixing forward delete on new double byte systems
  148.         <28>      9/9/96    dmp        staticfy local functions to eliminate warnings in MWC
  149.         <27>     8/21/96    ecs        ReadPartialResource cache; only draw pictures if not clipped
  150.         <26>      8/2/96    ted        fixing shift arrows for japanese
  151.         <25>      8/1/96    ted        fixing forward delete for japanese
  152.         <24>     7/25/96    ted        adding call to flushvol
  153.         <23>     7/24/96    ted        fixing stupid ttro draw hook bug
  154.         <22>     7/24/96    ted        fixing sub menu checking
  155.         <21>     7/19/96    ted        fixing menu nil dereference
  156.         <20>     6/10/96    ted        GENERATINGCFM should really be GENERATINGPOWERPC
  157.         <19>     5/31/96    ted        fixing clik loop for PPC
  158.         <18>     5/31/96    ted        adding PPC, FAT, and NuKernel builds
  159.         <17>     2/19/96    ted        fixing file type always on save
  160.         <16>      2/8/96    ted        CountMenuItems->CountMItems (??)
  161.         <15>      1/4/96    ecs        needed a (GrafPtr) cast
  162.         <14>     12/6/95    ted        adding file type open support
  163.         <13>     11/1/95    ted        adding partial picts
  164.         <12>    10/31/95    ted        increasing print margins
  165.         <11>    10/17/95    ted        optimizing page break code to speed up "boring" prints
  166.         <10>    10/12/95    ted        
  167.         <9*>    10/11/95    ted        adding page breaking support
  168.          <9>     10/2/95    TD        adding support for SC compiler
  169.          <8>     9/19/95    TD        fixing up undo with selection behavior
  170.          <7>     9/15/95    Gr        For contents menu selections, explicitly scroll before
  171.                                     TESetSelect
  172.          <6>     9/15/95    TD        modifying selection and undo behavior
  173.          <5>     9/15/95    Gr        Add contents menu support; change BlockMove calls to
  174.                                     BlockMoveData
  175.          <4>     9/13/95    TD        changing gcurrent voice check
  176.          <3>     8/31/95    TD        fixing a bug related to scroll bars on windows with no <cr>
  177.                                     after last line. bug #113167
  178.          <2>     8/22/95    TD        adding support for shift key w/ arrows
  179.          <1>     8/21/95    TD        First checked in.
  180.         <46>      1/4/95    DAL        Move the SetWTitle for "untitled" into SimpleText.c (to fix an
  181.                                     Apple Menu Options problem, Radar #s 1207631 and 1207614).
  182.         <45>    12/21/94    DAL        Radar #1199009. Fixed bug from <39> where
  183.                                     CalculateTextEditHeight was introduced, but it needs to return a
  184.                                     long, like the expression it replaced in ApplySize.
  185.         <44>     12/7/94    DAL        Radar #110489, 110490. Removed TEToScrap in Cut and Copy
  186.                                     operations, because it was putting a redundant copy of the
  187.                                     'TEXT' data into the scrap. With a styled TE record, TECopy does
  188.                                     all the work.
  189.         <39>      10/4/94    TED        (#1129365) use the script manager to figure out legal
  190.                                     text styles.  For example, Japanese doesn't support
  191.                                     underline.  (#1161797) system font/size/app font
  192.                                     are now mapped to real IDs and sizes so that 
  193.                                     folks don't get surprised by size=0 problems.
  194.         <38>      9/8/94    DAL        First untitled window uses FIRSTNEWDOCUMENTTITLE (#1148458).
  195.                                     UserCanceled error now propogates from Save (fixes #1183495). We
  196.                                     now handle update events behind Save dialog.
  197.  
  198. */
  199. #include "MacIncludes.h"
  200. #include <Threads.h>
  201.  
  202. #include <OSA.h>        // for AppleScript
  203. #include <AEDataModel.h>    // for AppleScript
  204.  
  205. #include "TextFile.h"
  206.  
  207. #pragma segment Text
  208.  
  209.  
  210.  
  211. // --------------------------------------------------------------------------------------------------------------
  212. // INTERNAL DEFINES
  213. // --------------------------------------------------------------------------------------------------------------
  214. #define kOnePageWidth         600            // preferred width of a window
  215. #define kMargins            4            // margins in window
  216. #define kPrintMargins        8            // margins in printing window
  217.  
  218. #define kPictureBase        1000        // resource base ID for PICTs in documents
  219. #define kSoundBase            10000        // resource base ID for 'snd 's in documents
  220.  
  221. // resources for controling printing
  222. #define kFormResource        'form'
  223.     #define kFormFeed            0x00000001    // form feed after this line
  224.  
  225. #define    kContentsListID        10000        // resource ID for STR# contents menu         
  226.  
  227. // some memory requirements
  228. #define kPrefBufferSize        90*1024
  229. #define kMinBufferSize        5*1024
  230.  
  231. #define kDeleteKey            8
  232. #define kForwardDeleteKey    0x75
  233.  
  234.  
  235. extern pascal unsigned char * AsmClikLoop(TEPtr pTE);
  236.  
  237. #define ABS(n)                (((n) < 0) ? -(n) : (n))
  238.  
  239. // --------------------------------------------------------------------------------------------------------------
  240. // STRUCTURES USED BY THIS FILE
  241. // --------------------------------------------------------------------------------------------------------------
  242.  
  243. typedef struct
  244. {
  245.     int            voiceCount;
  246.     MenuHandle    voiceMenu;
  247. }
  248. VoiceThreadParams;
  249.  
  250. // --------------------------------------------------------------------------------------------------------------
  251. // GLOBALS USED ONLY BY THESE ROUTINES
  252. // --------------------------------------------------------------------------------------------------------------
  253.  
  254. // These variables are used for speech, notice that on purpose we only
  255. // support a single channel at a time.
  256. #if 0
  257. static SpeechChannel    gSpeechChannel = nil;        
  258. static VoiceSpec        gCurrentVoice;
  259. static Ptr                gSpeakPtr = nil;
  260. static Boolean            gAddedVoices = false;
  261. static ThreadID            gVoicesThread;
  262. static void*            gThreadResults;
  263. static Boolean            gDontYield;
  264. #endif
  265.  
  266. static Str31            gPictMarker1, gPictMarker2;
  267.  
  268. // Other globals local to this file (and TextDrag.c)
  269. Boolean                    gTE6Version = false;
  270.  
  271. // External globals for AppleScript
  272. extern    ComponentInstance gOSAComponent;
  273.  
  274. // --------------------------------------------------------------------------------------------------------------
  275. // EXTERNAL FUNCTIONS
  276. // --------------------------------------------------------------------------------------------------------------
  277.  
  278. extern OSErr    TextDragTracking(WindowPtr pWindow, void *pData, DragReference theDragRef, short message);
  279. extern OSErr    TextDragReceive(WindowPtr pWindow, void *refCon, DragReference theDragRef);
  280. extern Boolean    DragText(WindowPtr pWindow, void *pData, EventRecord *pEvent, RgnHandle hilightRgn);
  281.  
  282. // --------------------------------------------------------------------------------------------------------------
  283. // INTERNAL ROUTINES
  284. // --------------------------------------------------------------------------------------------------------------
  285.  
  286. static void TextAddContentsMenu(WindowDataPtr pData);
  287. static void TextRemoveContentsMenu(WindowDataPtr pData);
  288. static OSErr TextGetContentsListItem(WindowDataPtr pData, short itemNum, StringPtr menuStr, StringPtr searchStr, short *totalItems);
  289. static OSErr TextAdjustContentsMenu(WindowDataPtr pData);
  290.  
  291. // --------------------------------------------------------------------------------------------------------------
  292. // UNDO UTILITY FUNCTIONS
  293. // --------------------------------------------------------------------------------------------------------------
  294. OSErr SaveCurrentUndoState(WindowDataPtr pData, short newCommandID)
  295. {
  296.     OSErr        anErr = noErr;
  297.     TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  298.     
  299.     // this is only a new command if:
  300.     //    the command ID is different
  301.     // OR
  302.     //    the selection is a range, and not equal to the old one
  303.     if     (
  304.             ( ((TextDataPtr) pData)->prevCommandID != newCommandID )
  305.         ||
  306.             (
  307.                 ( (**hTE).selStart != (**hTE).selEnd )
  308.             ||
  309.                 ( ABS((**hTE).selStart - ((TextDataPtr) pData)->prevSelStart) > 1 )
  310.             )
  311.         )
  312.         {
  313.         Handle        tempHandle;
  314.         Size        tempSize;
  315.         
  316.         // if we don't have save handles, make em!
  317.         if (!((TextDataPtr) pData)->prevText)
  318.             {
  319.             ((TextDataPtr) pData)->prevText = NewHandle(0);
  320.             anErr = MemError();
  321.             nrequire(anErr, MakeTextSave);
  322.             }
  323.         if  (!((TextDataPtr) pData)->prevStyle) 
  324.             {
  325.             ((TextDataPtr) pData)->prevStyle = NewHandle(0);
  326.             anErr = MemError();
  327.             nrequire(anErr, MakeStyleSave);
  328.             }
  329.             
  330.         // grow the save handles and block move into them
  331.         tempHandle = (**hTE).hText;
  332.         tempSize = GetHandleSize(tempHandle);
  333.         SetHandleSize( ((TextDataPtr) pData)->prevText, tempSize);
  334.         anErr = MemError();
  335.         nrequire(anErr, GrowTextSave);
  336.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevText, tempSize );
  337.         
  338.         tempHandle = (Handle) TEGetStyleHandle(hTE);
  339.         tempSize = GetHandleSize(tempHandle);
  340.         SetHandleSize( ((TextDataPtr) pData)->prevStyle, tempSize);
  341.         anErr = MemError();
  342.         nrequire(anErr, GrowTextSave);
  343.         BlockMoveData(*tempHandle, * ((TextDataPtr) pData)->prevStyle, tempSize );
  344.  
  345.         // save length
  346.         ((TextDataPtr) pData)->prevLength = (**hTE).teLength;
  347.         ((TextDataPtr) pData)->beforeSelStart = (**hTE).selStart;
  348.         ((TextDataPtr) pData)->beforeSelEnd = (**hTE).selEnd;
  349.         }
  350.     
  351.     // save start and end of the selection
  352.     ((TextDataPtr) pData)->prevSelStart = (**hTE).selStart;
  353.  
  354.     // if we didn't have any problems, then we can undo this operation
  355.     ((TextDataPtr) pData)->prevCommandID = newCommandID;
  356.     return(noErr);
  357.     
  358. // EXCEPTION HANDLING
  359. GrowTextSave:
  360. MakeStyleSave:
  361. MakeTextSave:
  362.     // can't undo because of an error 
  363.     // (at some point might warn the user, but that's probably more annoying)
  364.     ((TextDataPtr) pData)->prevCommandID = cNull;
  365.     
  366.     return(anErr);
  367.     
  368. } // SaveCurrentUndoState
  369.  
  370. // --------------------------------------------------------------------------------------------------------------
  371. static void PerformUndo(WindowDataPtr pData)
  372. {
  373.     if (((TextDataPtr) pData)->prevCommandID != cNull)
  374.         {
  375.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  376.         long        tempLong;
  377.  
  378.         // undo text
  379.         tempLong = (long) (**hTE).hText;
  380.         (**hTE).hText = ((TextDataPtr) pData)->prevText;
  381.         ((TextDataPtr) pData)->prevText = (Handle)tempLong;
  382.  
  383.         // undo length
  384.         tempLong = (long) (**hTE).teLength;
  385.         (**hTE).teLength = ((TextDataPtr) pData)->prevLength;
  386.         ((TextDataPtr) pData)->prevLength = tempLong;
  387.  
  388.         // undo style
  389.         tempLong = (long) TEGetStyleHandle(hTE);
  390.         HandToHand((Handle*)&tempLong);
  391.         TESetStyleHandle( (TEStyleHandle) ((TextDataPtr) pData)->prevStyle, hTE);
  392.         ((TextDataPtr) pData)->prevStyle = (Handle)tempLong;
  393.         
  394.         // undo selection
  395.             {
  396.             short    start, end;
  397.             
  398.             start = ((TextDataPtr) pData)->beforeSelStart;
  399.             end = ((TextDataPtr) pData)->beforeSelEnd;
  400.             TESetSelect(start, end, hTE);
  401.             }
  402.         }
  403.         
  404. } // PerformUndo
  405.  
  406.  
  407. // --------------------------------------------------------------------------------------------------------------
  408. // TEXT EDIT UTILITY FUNCTIONS
  409. // --------------------------------------------------------------------------------------------------------------
  410. static long CalculateTextEditHeight(TEHandle hTE)
  411. {
  412.     long    result;
  413.     short    length;
  414.     
  415.     result = TEGetHeight(32767, 0, hTE);
  416.     length = (**hTE).teLength;
  417.     
  418.     // Text Edit doesn't return the height of the last character, if that
  419.     // character is a <cr>.  So if we see that, we go grab the height of
  420.     // that last character and add it into the total height.
  421.     if ( (length) && ( (*(**hTE).hText)[length-1] == '\n') )
  422.         {
  423.         TextStyle    theStyle;
  424.         short        theHeight;
  425.         short        theAscent;
  426.  
  427.         TEGetStyle(length, &theStyle, &theHeight, &theAscent, hTE);
  428.         result += theHeight;
  429.         }
  430.  
  431.     return result;
  432.  
  433. } // CalculateTextEditHeight
  434.  
  435. // --------------------------------------------------------------------------------------------------------------
  436. void AdjustTE(WindowDataPtr pData, Boolean doScroll)
  437. {
  438.     TEPtr        te;
  439.     short        value;
  440.     short        prevValue;
  441.     
  442.     te = *((TextDataPtr) pData)->hTE;
  443.     prevValue = GetControlValue(pData->vScroll);
  444.     value = te->viewRect.top - te->destRect.top;
  445.     SetControlValue(pData->vScroll, value);
  446.  
  447.     te = *((TextDataPtr) pData)->hTE;
  448.     if (doScroll)
  449.         {
  450.         short    hScroll = te->viewRect.left - te->destRect.left;
  451.         short    vScroll = value - prevValue;
  452.         if ((hScroll != 0) || (vScroll != 0))
  453.             TEScroll(hScroll, vScroll, ((TextDataPtr) pData)->hTE);
  454.         }
  455.     
  456.             
  457. } // AdjustTE
  458.  
  459. // --------------------------------------------------------------------------------------------------------------
  460.  
  461. static void RecalcTE(WindowDataPtr pData, Boolean doInval)
  462. {
  463.     Rect    viewRect, destRect;
  464.     short    oldOffset;
  465.     
  466.     destRect = (**((TextDataPtr) pData)->hTE).destRect;
  467.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  468.     oldOffset = destRect.top - viewRect.top;
  469.     
  470.     viewRect = pData->contentRect;
  471.             
  472.     InsetRect(&viewRect, kMargins, kMargins);
  473.     destRect = viewRect;
  474.     
  475.     OffsetRect(&destRect, 0, oldOffset);
  476.     
  477.     (**((TextDataPtr) pData)->hTE).viewRect = viewRect;
  478.     (**((TextDataPtr) pData)->hTE).destRect = destRect;
  479.     
  480.     TECalText(((TextDataPtr) pData)->hTE);
  481.  
  482.     if (doInval)
  483.     {
  484.         WindowPtr window = FrontWindow();
  485.         if ( window )
  486.         {
  487.             Rect        rect;
  488.  
  489.             GetPortBounds( GetWindowPort( window ), &rect );
  490.             InvalWindowRect( window, &rect );
  491.         }
  492.     }
  493. } // RecalcTE
  494.  
  495. // --------------------------------------------------------------------------------------------------------------
  496.  
  497. pascal TEClickLoopUPP GetOldClickLoop(void)
  498. {
  499. // AEC, changed to obtain application data from the WRefCon
  500.      TextDataPtr    textData;
  501.  
  502.      textData = (TextDataPtr) GetWRefCon(GetWindowFromPort(GetQDGlobalsThePort()));
  503.      return textData->docClick;     
  504. } // GetOldClikLoop
  505.  
  506. pascal void TextClickLoop(void)
  507. {    
  508.     WindowPtr    window;
  509.     RgnHandle    region;
  510.     Rect        bounds;
  511.     TextDataPtr textData;
  512.  
  513.     window = FrontWindow();
  514.     region = NewRgn();
  515.     GetClip(region);                    /* save clip */
  516.     ClipRect(GetWindowPortBounds(window, &bounds));
  517.     
  518. // AEC, changed to obtain application data from the WRefCon
  519.     textData = (TextDataPtr) GetWRefCon(window);
  520.      
  521.     textData->insideClickLoop = true;
  522.         AdjustScrollBars(window, false, false, nil);
  523.     textData->insideClickLoop = false;
  524.  
  525.     SetClip(region);                    /* restore clip */
  526.     DisposeRgn(region);
  527.  
  528. } // TextClickLoop
  529.  
  530. static pascal Boolean CClikLoop(TEPtr pTE)
  531. {
  532.     CallTEClickLoopProc(GetOldClickLoop(), pTE);
  533.     
  534.     TextClickLoop();
  535.     
  536.     return(true);
  537.     
  538. } // CClikLoop
  539.  
  540. // --------------------------------------------------------------------------------------------------------------
  541. pascal void MyDrawHook ( unsigned short offset, unsigned short textLen,
  542.      Ptr textPtr, TEPtr tePtr, TEHandle teHdl )
  543. {
  544. #pragma unused    ( teHdl, tePtr)
  545.  
  546.     register short            drawLen = 0;
  547.     register Ptr            drawPtr;
  548.     
  549.     drawPtr = textPtr + (long)offset;
  550.     
  551.     while ( textLen--)
  552.         {
  553.         if ( *drawPtr == (char)0xCA &&
  554.              ( *(drawPtr - 1) == 0x0D || *tePtr->hText == textPtr) )
  555.             {
  556.             DrawText( textPtr, offset, drawLen);
  557.             DrawChar( '\040');
  558.             offset += drawLen + 1;
  559.             drawLen = 0;
  560.             }
  561.         else
  562.             {
  563.             ++drawLen;
  564.             }
  565.         ++drawPtr;
  566.         }
  567.     
  568.     DrawText( textPtr, offset, drawLen);
  569.     
  570. } // MyDrawHook
  571.  
  572.  
  573. // --------------------------------------------------------------------------------------------------------------
  574. #if 0
  575. static void DisposeOfSpeech(Boolean throwAway)
  576. {
  577.     if (gSpeechChannel)
  578.         {
  579.         (void) StopSpeech( gSpeechChannel );
  580.         if (throwAway)
  581.             {
  582.             (void) DisposeSpeechChannel( gSpeechChannel );
  583.             gSpeechChannel = nil;
  584.             }
  585.         }
  586.     if (gSpeakPtr)
  587.         {
  588.         DisposePtr(gSpeakPtr);
  589.         gSpeakPtr = nil;
  590.         }
  591.         
  592. } // DisposeOfSpeech
  593. #endif
  594.  
  595. // --------------------------------------------------------------------------------------------------------------
  596.  
  597. static Boolean FindNextPicture(Handle textHandle, short *offset, short *delta)
  598. {
  599.     long result;
  600.     
  601.     // marker at begining of document means a picture there
  602.     if ( (*offset == 0) && ( (*(unsigned char*)(*textHandle + (*offset))) == 0xCA) )
  603.         {
  604.         *delta = 1;
  605.         return true;
  606.         }
  607.  
  608.     if (gPictMarker1[0] != 0)
  609.         {
  610.         result = Munger(textHandle, *offset, &gPictMarker1[1], gPictMarker1[0], nil, 0);
  611.         if (result >= 0)
  612.             {
  613.             *offset = result;
  614.             *delta = gPictMarker1[0];
  615.             return true;
  616.             }
  617.         }
  618.  
  619.     if (gPictMarker2[0] != 0)
  620.         {
  621.         result = Munger(textHandle, *offset, &gPictMarker2[1], gPictMarker2[0], nil, 0);
  622.         if (result >= 0)
  623.             {
  624.             *offset = result;
  625.             *delta = gPictMarker2[0];
  626.             return true;
  627.             }
  628.         }
  629.         
  630.     *delta = 1;
  631.     return false;
  632.     
  633. } // FindNextPicture
  634.  
  635. // --------------------------------------------------------------------------------------------------------------
  636. static Boolean LineHasPageBreak(short lineNum, TEHandle hTE)
  637. {
  638.     Boolean        result = false;
  639.     short        offset, delta, lineStartOffset;
  640.     short        textLength;
  641.     Handle        textHandle;
  642.     short        pictIndex = 0;
  643.     
  644.     textHandle = (** hTE).hText;
  645.     lineStartOffset = (**hTE).lineStarts[lineNum];
  646.     textLength = (**hTE).lineStarts[lineNum+1];
  647.     
  648.     offset = 0;
  649.     while (offset < textLength)
  650.         {
  651.         if (FindNextPicture(textHandle, &offset, &delta))
  652.             {
  653.             Handle    formHandle;
  654.             
  655.             formHandle = Get1Resource(kFormResource, kFormResource + pictIndex);
  656.             if (formHandle)
  657.                 {
  658.                 long    options = **(long**)formHandle;
  659.                 
  660.                 ReleaseResource(formHandle);
  661.                 if ( (options & kFormFeed) && (offset >= lineStartOffset) && (offset < textLength) )
  662.                     {
  663.                     result = true;
  664.                     break;
  665.                     }
  666.                 }
  667.             ++pictIndex;
  668.             }
  669.         
  670.         offset += delta;
  671.         }
  672.         
  673.     return(result);
  674.     
  675. } // LineHasPageBreak
  676.  
  677. // --------------------------------------------------------------------------------------------------------------
  678. #define USE_PICT_SPOOL_CACHE 1
  679.  
  680. enum {kSpoolCacheBlockSize = 1024};        // 1K is arbitrary, perhaps could be tuned
  681.  
  682. static Handle    gSpoolPicture;
  683. static long        gSpoolOffset;
  684. static Handle    gSpoolCacheBlock;
  685. static long        gSpoolCacheOffset;
  686. static long        gSpoolCacheSize;
  687.  
  688. // --------------------------------------------------------------------------------------------------------------
  689. /*
  690.     ReadPartialResource is very painful over slow links such as ARA or ISDN. This code implements a simple
  691.     cache that vastly improves the performance of displaying embedded pictures over a slow network link.
  692.     The cache also improves scrolling performance in documents with many embedded pictures, even on local
  693.     volumes.
  694. */
  695. static short ReadPartialData(Ptr dataPtr, short byteCount)
  696. {
  697. #if USE_PICT_SPOOL_CACHE
  698.  
  699.     if (gSpoolCacheBlock == NULL)
  700.         {
  701.         gSpoolCacheBlock = NewHandle(kSpoolCacheBlockSize);
  702.         if (gSpoolCacheBlock != NULL)
  703.             {
  704.             long    cacheBytes    = GetResourceSizeOnDisk(gSpoolPicture) - gSpoolOffset;
  705.             
  706.             if (cacheBytes > kSpoolCacheBlockSize)
  707.                 cacheBytes = kSpoolCacheBlockSize;
  708.             
  709.             HLock(gSpoolCacheBlock);
  710.             ReadPartialResource(gSpoolPicture, gSpoolOffset, *gSpoolCacheBlock, cacheBytes);
  711.             HUnlock(gSpoolCacheBlock);
  712.             
  713.             gSpoolCacheOffset = gSpoolOffset;
  714.             gSpoolCacheSize = cacheBytes;
  715.             }
  716.         }
  717.     
  718.     // if the requested data is entirely present in the cache block, get it from there…
  719.     if (gSpoolCacheBlock != NULL &&
  720.         gSpoolOffset >= gSpoolCacheOffset && gSpoolOffset + byteCount <= gSpoolCacheOffset + gSpoolCacheSize)
  721.         {
  722.         BlockMoveData(*gSpoolCacheBlock + (gSpoolOffset - gSpoolCacheOffset), dataPtr, byteCount);
  723.         return byteCount;
  724.         }
  725.     // …else read it directly from disk, and force the cache block to be filled with new data
  726.     else
  727.         {
  728.         ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  729.         if (gSpoolCacheBlock != NULL)
  730.             {
  731.             DisposeHandle(gSpoolCacheBlock);
  732.             gSpoolCacheBlock = NULL;
  733.             }
  734.         return byteCount;
  735.         }
  736.  
  737. #else
  738.  
  739.     ReadPartialResource(gSpoolPicture, gSpoolOffset, dataPtr, byteCount);
  740.     return byteCount;
  741.     
  742. #endif
  743.     
  744. } // ReadPartialData
  745.  
  746. // --------------------------------------------------------------------------------------------------------------
  747. static pascal void GetPartialPICTData(Ptr dataPtr,short byteCount)
  748. {
  749.     while (byteCount > 0)
  750.     {
  751.         short    readBytes    = ReadPartialData(dataPtr, byteCount);
  752.         
  753.         byteCount -= readBytes;
  754.         dataPtr += readBytes;
  755.         
  756.         gSpoolOffset += readBytes;
  757.     }
  758.     
  759. } // GetPartialPICTData
  760.  
  761. // --------------------------------------------------------------------------------------------------------------
  762. static void SpoolDrawPicture(Handle spoolPicture, PicHandle pictureHeader, Rect *pRect)
  763. {
  764.     static QDGetPicUPP gGetPartialPICTData = NULL;
  765. #if USE_QD_GRAFPROCS
  766.     CQDProcs    spoolProcs;
  767.     CQDProcs    oldProcs;
  768.     CGrafPtr    thePort = GetQDGlobalsThePort();
  769. #endif
  770.  
  771.     if (!gGetPartialPICTData) {
  772.         gGetPartialPICTData = NewQDGetPicProc(GetPartialPICTData);
  773.     }
  774.  
  775.     gSpoolPicture = spoolPicture;
  776.     gSpoolOffset = sizeof(Picture);
  777.     
  778. #if USE_QD_GRAFPROCS
  779.     if (!GetPortGrafProcs(thePort, &spoolProcs))
  780.         SetStdCProcs(&spoolProcs);
  781.         
  782.     spoolProcs.getPicProc = gGetPartialPICTData;
  783.     GetPortGrafProcs(thePort, &oldProcs);
  784.     SetPortGrafProcs(thePort, &spoolProcs);
  785. #endif
  786.     DrawPicture(pictureHeader, pRect);
  787. #if USE_QD_GRAFPROCS
  788.     SetPortGrafProcs(thePort, &oldProcs);
  789. #endif
  790.     
  791.     if (gSpoolCacheBlock != NULL)
  792.         {
  793.         DisposeHandle(gSpoolCacheBlock);
  794.         gSpoolCacheBlock = NULL;
  795.         }
  796.     
  797. } // SpoolDrawPicture
  798.  
  799. // --------------------------------------------------------------------------------------------------------------
  800. static void DrawPictures( WindowDataPtr pData, TEHandle hTE)
  801. {
  802.     Handle    textHandle;
  803.     long    textLength;
  804.     short    oldResFile;
  805.     short    pictIndex;
  806.     short    numPicts;
  807.     
  808.     if (pData->resRefNum == -1)
  809.         return;
  810.         
  811.     oldResFile = CurResFile();
  812.     UseResFile(pData->resRefNum);
  813.     
  814.     numPicts = Count1Resources('PICT');
  815.     pictIndex = 0;
  816.     
  817.     if (numPicts != 0)
  818.         {
  819.         short                offset, delta;
  820.         RgnHandle            oldClip;
  821.         Rect                theRect;
  822.         Rect                viewRect;
  823.         
  824.         viewRect = theRect = (** hTE).viewRect;
  825.         // intersect our viewing area with the actual clip to avoid
  826.         // drawing over the scroll bars
  827.             {
  828.             RgnHandle    newClip = NewRgn();
  829.             
  830.             oldClip = NewRgn();
  831.             GetClip(oldClip);
  832.             RectRgn(newClip, &theRect);
  833.             SectRgn(oldClip, newClip, newClip);
  834.             SetClip(newClip);
  835.             }
  836.         textHandle = (** hTE).hText;
  837.         textLength = (** hTE).teLength;
  838.         
  839.         offset = 0;
  840.         while (offset < textLength)
  841.             {
  842.             if (FindNextPicture(textHandle, &offset, &delta))
  843.                 {
  844.                 Handle        pictHandle;
  845.                 Point        picturePoint = TEGetPoint(offset, hTE);
  846.                 
  847.                 SetResLoad(false);
  848.                 pictHandle = Get1Resource('PICT', kPictureBase + pictIndex);
  849.                 SetResLoad(true);
  850.                 if (pictHandle)
  851.                     {
  852.                     PicHandle    pictHeader = (PicHandle)NewHandle(sizeof(Picture) + sizeof(long)*8);
  853.                     
  854.                     if (pictHeader)
  855.                         {
  856.                         HLock((Handle) pictHeader);
  857.                         ReadPartialResource(pictHandle, 0, (Ptr)*pictHeader, GetHandleSize((Handle)pictHeader));
  858.                         HUnlock((Handle) pictHeader);
  859.                         
  860.                         // calculate where to draw the picture, this location is
  861.                         // computed by:
  862.                         //  1) the frame of the original picture, normalized to 0,0
  863.                         //    2) the location of the non-breaking space character
  864.                         //    3) centering the picture on the content frame horizontally
  865.                         //    4) subtracting off the line-height of the character
  866.                         
  867.                         GetPICTRectangleAt72dpi(pictHeader, &theRect);
  868.                         OffsetRect(&theRect, -theRect.left, -theRect.top);
  869.                         OffsetRect(&theRect, 
  870.                                             theRect.left +
  871.                                             ((viewRect.right - viewRect.left) >> 1) -
  872.                                             ((theRect.right - theRect.left) >> 1),
  873.                                         picturePoint.v-theRect.top - pData->vScrollAmount);
  874.     
  875.         // only draw the picture if it will be visible (vastly improves scrolling
  876.         // performance in documents with many embedded pictures)
  877.                         
  878.         //    if (RectInRgn(&theRect, GetQDthePort()->clipRgn))
  879.                             SpoolDrawPicture(pictHandle, pictHeader, &theRect);
  880.                         DisposeHandle((Handle) pictHeader);
  881.                         }
  882.                     ReleaseResource((Handle) pictHandle);
  883.                     }
  884.                 ++pictIndex;
  885.  
  886.                 offset += delta;
  887.                 }
  888.             else
  889.                 break;
  890.             }
  891.                 
  892.         SetClip(oldClip);
  893.         DisposeRgn(oldClip);
  894.         }
  895.         
  896.     UseResFile(oldResFile);
  897.     
  898. } // DrawPictures
  899.  
  900. // --------------------------------------------------------------------------------------------------------------
  901. static void UpdateFileInfo(FSSpec *pSpec, Boolean documentIsText)
  902. {
  903.     FInfo    theInfo;
  904.  
  905.     FSpGetFInfo(pSpec, &theInfo);
  906.     theInfo.fdCreator = 'ttxt';
  907.     theInfo.fdType = 'TEXT';
  908.  
  909.     // set the stationary bit, if we must
  910.     if (!documentIsText)
  911.         theInfo.fdFlags |= kIsStationery;
  912.     else
  913.         theInfo.fdFlags &= ~kIsStationery;
  914.  
  915.     FSpSetFInfo(pSpec, &theInfo);
  916.  
  917. } // UpdateFileInfo
  918.  
  919. // --------------------------------------------------------------------------------------------------------------
  920. static OSErr    TextSave(WindowDataPtr pData, Boolean resetStationary)
  921. {
  922.     OSErr    anErr;
  923.     long    amountToWrite;
  924.     int        i;
  925.     Ptr        data;
  926.     
  927.     anErr = noErr;
  928.     
  929.     // write out the text
  930.     SetFPos(pData->dataRefNum, fsFromStart, 0);
  931.     amountToWrite = (** ((TextDataPtr) pData)->hTE).teLength;
  932.     data = * (** ((TextDataPtr) pData)->hTE).hText;
  933.  
  934.     for (i=0; i < amountToWrite; i++) {
  935.         if (data[i] == '\r')
  936.             data[i] = '\n';
  937.     }
  938.  
  939.     anErr = FSWrite(pData->dataRefNum, &amountToWrite, data);
  940.     amountToWrite = (** ((TextDataPtr) pData)->hTE).teLength;
  941.     data = * (** ((TextDataPtr) pData)->hTE).hText;
  942.     for (i=0; i < amountToWrite; i++) {
  943.         if (data[i] == '\n')
  944.             data[i] = '\r';
  945.     }
  946.  
  947.     nrequire(anErr, FailedWrite);
  948.  
  949.     SetEOF(pData->dataRefNum, amountToWrite);
  950.     
  951.     if (pData->resRefNum == -1)
  952.         {
  953.         FSpCreateResFile(&pData->fileSpec, 'ttxt', pData->originalFileType, 0);
  954.         pData->resRefNum = FSpOpenResFile(&pData->fileSpec, fsRdWrPerm);            
  955.         }
  956.     else
  957.         {
  958.         // a save always makes it into file of type 'TEXT', for Save As… we 
  959.         // afterwards set it again if the user is saving as stationary.
  960.         if (resetStationary)
  961.             UpdateFileInfo(&pData->fileSpec, true);
  962.         }
  963.  
  964.     if (pData->resRefNum != -1)
  965.         {
  966.         short    oldResFile = CurResFile();
  967.         Handle    resourceHandle;
  968.         
  969.         UseResFile(pData->resRefNum);
  970.         
  971.         // remove any old sounds
  972.         resourceHandle = Get1Resource('snd ', kSoundBase);
  973.         if (resourceHandle)
  974.             {
  975.             RemoveResource(resourceHandle);
  976.             DisposeHandle(resourceHandle);
  977.             }
  978.             
  979.         // save the new sound
  980.         resourceHandle = ((TextDataPtr) pData)->soundHandle;
  981.         if (resourceHandle)
  982.             {
  983.             anErr = HandToHand(&resourceHandle);
  984.             if (anErr == noErr)
  985.                 {
  986.                 AddResource(resourceHandle, 'snd ', kSoundBase, "\p");
  987.                 anErr = ResError();
  988.                 }
  989.             nrequire(anErr, AddSoundResourceFailed);
  990.             }
  991.             
  992.         // remove any old styles
  993.         resourceHandle = Get1Resource('styl', 128);
  994.         if (resourceHandle)
  995.             {
  996.             RemoveResource(resourceHandle);
  997.             DisposeHandle(resourceHandle);
  998.             }
  999.         
  1000.         // save the new style -- get the scrap handle from the TE record
  1001.         // To do this, we must select the text -- BUT doing so through the
  1002.         // TextEdit API results in lots of flashing and annoying behavior.
  1003.         // So we just change the offsets by hand
  1004.         {
  1005.         short                oldStart, oldEnd;    
  1006.         
  1007.         oldStart = (** ((TextDataPtr) pData)->hTE).selStart;
  1008.         oldEnd   = (** ((TextDataPtr) pData)->hTE).selEnd;
  1009.         
  1010.         (** ((TextDataPtr) pData)->hTE).selStart = 0;
  1011.         (** ((TextDataPtr) pData)->hTE).selEnd = 32767;
  1012.     
  1013.         resourceHandle = (Handle) TEGetStyleScrapHandle( ((TextDataPtr) pData)->hTE );
  1014.     
  1015.         (** ((TextDataPtr) pData)->hTE).selStart = oldStart;
  1016.         (** ((TextDataPtr) pData)->hTE).selEnd = oldEnd;
  1017.         }
  1018.     
  1019.         if (resourceHandle)
  1020.             {
  1021.             AddResource(resourceHandle, 'styl', 128, "\p");
  1022.             anErr = ResError();
  1023.             nrequire(anErr, AddStyleResourceFailed);
  1024.             }
  1025.             
  1026.         AddSoundResourceFailed:
  1027.         AddStyleResourceFailed:    
  1028.         
  1029.             UpdateResFile(pData->resRefNum);
  1030.             UseResFile(oldResFile);
  1031.         }
  1032.  
  1033.         
  1034. // FALL THROUGH EXCEPTION HANDLING
  1035. FailedWrite:
  1036.  
  1037.     // if everything went okay, then clear the changed bit
  1038.     if (anErr == noErr)
  1039.     {
  1040.         (void) FlushVol("\p", pData->fileSpec.vRefNum);
  1041.  
  1042.         // we need not update the proxy here, since the file type never changes in this routine
  1043.         
  1044.         SetDocumentContentChanged( pData, false );
  1045.     }
  1046.         
  1047.     return anErr;
  1048.     
  1049. } // TextSave
  1050.  
  1051. // --------------------------------------------------------------------------------------------------------------
  1052. #if 0
  1053. static pascal void DrawTextUserItem(DialogPtr dPtr, short theItem)
  1054. /*
  1055.     Draw text icon in the location
  1056. */
  1057. {
  1058.     short        kind;
  1059.     Handle        itemHandle;
  1060.     Rect        box;
  1061.     
  1062.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  1063.     PlotIconID(&box, ttNone, ttNone, kTextIcon);
  1064.             
  1065. } // DrawTextUserItem
  1066.  
  1067. #if GENERATINGCFM
  1068.     static RoutineDescriptor gDrawTextUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawTextUserItem);
  1069.     static UserItemUPP gDrawTextUserItem = &gDrawTextUserItemRD;
  1070. #else
  1071.     static UserItemUPP gDrawTextUserItem = NewUserItemProc(DrawTextUserItem);
  1072. #endif
  1073. #endif
  1074.  
  1075. // --------------------------------------------------------------------------------------------------------------
  1076. #if 0
  1077. static pascal void DrawStationeryUserItem(DialogPtr dPtr, short theItem)
  1078. /*
  1079.     Draw stationery icon in the location
  1080. */
  1081. {
  1082.     short        kind;
  1083.     Handle        itemHandle;
  1084.     Rect        box;
  1085.     
  1086.     GetDialogItem(dPtr, theItem, &kind, &itemHandle, &box);
  1087.     PlotIconID(&box, ttNone, ttNone, kStationeryIcon);
  1088.             
  1089. } // DrawStationeryUserItem
  1090.  
  1091.  
  1092. #if GENERATINGCFM
  1093.     static RoutineDescriptor gDrawStationeryUserItemRD = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawStationeryUserItem);
  1094.     static UserItemUPP gDrawStationeryUserItem = &gDrawStationeryUserItemRD;
  1095. #else
  1096.     static UserItemUPP gDrawStationeryUserItem = NewUserItemProc(DrawStationeryUserItem);
  1097. #endif
  1098. #endif
  1099.  
  1100. // --------------------------------------------------------------------------------------------------------------
  1101. // pre and post update procs for inline input 
  1102.  
  1103. static pascal void TSMPreUpdateProc(TEHandle textH, long refCon)
  1104. {
  1105. #pragma unused (refCon)
  1106.  
  1107.     ScriptCode     keyboardScript;
  1108.     short        mode;
  1109.     TextStyle    theStyle;
  1110.  
  1111.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  1112.     mode = doFont;
  1113.     if     (!
  1114.             (
  1115.             (TEContinuousStyle(&mode, &theStyle, textH) )&& 
  1116.             (FontToScript(theStyle.tsFont) == keyboardScript) 
  1117.             )
  1118.         )
  1119.         {
  1120.         theStyle.tsFont = GetScriptVariable(keyboardScript, smScriptAppFond);
  1121.         TESetStyle(doFont, &theStyle, false, textH);
  1122.         }
  1123.         
  1124. } // TSMPreUpdateProc
  1125.  
  1126. static pascal void TSMPostUpdateProc(
  1127.                 TEHandle textH,
  1128.                 long fixLen,
  1129.                 long inputAreaStart,
  1130.                 long inputAreaEnd,
  1131.                 long pinStart,
  1132.                 long pinEnd,
  1133.                 long refCon)
  1134. {
  1135. #pragma unused (textH, fixLen, inputAreaStart, inputAreaEnd, pinStart, pinEnd)
  1136.  
  1137.     AdjustScrollBars((WindowPtr)refCon, false, false, nil);
  1138.     AdjustTE((WindowDataPtr)refCon, true);
  1139.     
  1140.     SetDocumentContentChanged( (WindowDataPtr)refCon, true );
  1141.     
  1142. } // TSMPostUpdateProc
  1143.  
  1144.  
  1145. // --------------------------------------------------------------------------------------------------------------
  1146. #if 0
  1147. static pascal short SaveDialogHook(short item, DialogPtr dPtr, Boolean *isText)
  1148. {
  1149.     short    theType;
  1150.     Handle    theHandle;
  1151.     Rect    theRect;
  1152.     short    returnValue = item;
  1153.     
  1154.  
  1155.     switch (item)
  1156.         {
  1157.         case sfHookFirstCall:
  1158.             if (GetWRefCon(GetDialogWindow(dPtr)) == sfMainDialogRefCon)
  1159.                 {
  1160.                 GetDialogItem(dPtr, iTextUserItem, &theType, &theHandle, &theRect);
  1161.                 theHandle = (Handle) gDrawTextUserItem;
  1162.                 SetDialogItem(dPtr, iTextUserItem, theType, theHandle, &theRect);
  1163.                 
  1164.                 GetDialogItem(dPtr, iStationeryUserItem, &theType, &theHandle, &theRect);
  1165.                 theHandle = (Handle) gDrawStationeryUserItem;
  1166.                 SetDialogItem(dPtr, iStationeryUserItem, theType, theHandle, &theRect);
  1167.     
  1168.                 GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  1169.                 SetControlValue((ControlHandle) theHandle, 1);
  1170.                 
  1171.                 GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  1172.                 SetControlValue((ControlHandle) theHandle, 0);
  1173.                 *isText = true;
  1174.                 }
  1175.             break;
  1176.             
  1177.         case iTextDocumentItem:
  1178.         case iTextUserItem:
  1179.             GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  1180.             SetControlValue((ControlHandle) theHandle, 1);
  1181.             
  1182.             GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  1183.             SetControlValue((ControlHandle) theHandle, 0);
  1184.             
  1185.             *isText = true;
  1186.             returnValue = sfHookNullEvent;
  1187.             break;
  1188.             
  1189.         case iStationeryDocumentItem:
  1190.         case iStationeryUserItem:
  1191.             GetDialogItem(dPtr, iTextDocumentItem, &theType, &theHandle, &theRect);
  1192.             SetControlValue((ControlHandle) theHandle, 0);
  1193.             
  1194.             GetDialogItem(dPtr, iStationeryDocumentItem, &theType, &theHandle, &theRect);
  1195.             SetControlValue((ControlHandle) theHandle, 1);
  1196.             
  1197.             *isText = false;
  1198.             returnValue = sfHookNullEvent;
  1199.             break;
  1200.         
  1201.         }
  1202.         
  1203.     return returnValue;
  1204.     
  1205. } // SaveDialogHook
  1206.  
  1207. #if GENERATINGCFM
  1208.     static RoutineDescriptor gSaveDialogHookRD = BUILD_ROUTINE_DESCRIPTOR(uppDlgHookYDProcInfo, SaveDialogHook);
  1209.     static DlgHookYDUPP gSaveDialogHook = &gSaveDialogHookRD;
  1210. #else
  1211.     static DlgHookYDUPP gSaveDialogHook = NewDlgHookYDProc(SaveDialogHook);
  1212. #endif
  1213. #endif
  1214.  
  1215. // --------------------------------------------------------------------------------------------------------------
  1216.  
  1217. #if 0
  1218. // Handle update/activate events behind Standard File
  1219. static pascal Boolean SaveDialogFilter(DialogPtr theDialog, EventRecord *theEvent,
  1220.                                       short *itemHit, void *myDataPtr)
  1221. {
  1222.     #pragma unused(myDataPtr)
  1223.  
  1224.     if (StdFilterProc(theDialog, theEvent, itemHit))
  1225.         return true;
  1226.  
  1227.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  1228.     // drastically changing how the system handles the menu bar during our alert)
  1229.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  1230.         {
  1231.         HandleEvent(theEvent);
  1232.         }
  1233.  
  1234.     return false;
  1235.  
  1236. } // SaveDialogFilter
  1237.  
  1238. #if GENERATINGCFM
  1239.     static RoutineDescriptor gSaveDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, SaveDialogFilter);
  1240.     static ModalFilterYDUPP gSaveDialogFilter = &gSaveDialogFilterRD;
  1241. #else
  1242.     static ModalFilterYDUPP gSaveDialogFilter = NewModalFilterYDProc(SaveDialogFilter);
  1243. #endif
  1244. #endif
  1245.  
  1246. // --------------------------------------------------------------------------------------------------------------
  1247.  
  1248. static OSErr    TextSaveAs(WindowPtr pWindow, WindowDataPtr pData)
  1249. {
  1250.     OSErr                anErr = noErr;
  1251.     short                oldRes, oldData;
  1252.     Boolean                documentIsText = false;
  1253.     FSSpec                fileSpec;
  1254.     FSSpec                oldFileLocation;
  1255.     Boolean                replacing;
  1256.     NavReplyRecord        navReply;
  1257.     
  1258.     // save the old references -- if there is an error, we restore them
  1259.     oldRes                = pData->resRefNum;
  1260.     oldData                = pData->dataRefNum;
  1261.     oldFileLocation        = pData->fileSpec;
  1262.     
  1263.     // ask where and how to save this document
  1264.     {
  1265.     Str255    defaultName;
  1266.     Cursor    arrow;
  1267. //    Point    where = {-1, -1};
  1268.     
  1269.     // setup for the call
  1270.     GetWTitle(pWindow, defaultName);
  1271.  
  1272.     SetCursor(GetQDGlobalsArrow(&arrow));
  1273.     
  1274.     // find out where the user wants the file
  1275.     if (gMachineInfo.haveNavigationServices)
  1276.         {
  1277.         Boolean stationery;
  1278.  
  1279.         anErr = SaveFileDialog(defaultName, pData->originalFileType, 'ttxt', MyEventProc, &fileSpec, &stationery, &replacing, &navReply);
  1280.         if ( anErr == userCanceledErr)
  1281.             anErr = eUserCanceled;
  1282.             
  1283.         documentIsText = stationery == false;
  1284.         }
  1285.     else
  1286.         {
  1287. #if(0)
  1288.         StandardFileReply    sfReply;
  1289.         
  1290.         CustomPutFile("\p", defaultName, &sfReply, 
  1291.                     kTextSaveAsDialogID, where,
  1292.                     gSaveDialogHook, gSaveDialogFilter, nil, nil, &documentIsText);
  1293.         
  1294.         // map the cancel button into a cancelling error
  1295.         if (!sfReply.sfGood)
  1296.             anErr = eUserCanceled;
  1297.  
  1298.         replacing    = sfReply.sfReplacing;
  1299.         fileSpec    = sfReply.sfFile;
  1300. #endif
  1301.         }
  1302.     }
  1303.         
  1304.     // can't replace over other types    
  1305.     if (replacing)
  1306.         {
  1307.         FInfo    theInfo;
  1308.         
  1309.         FSpGetFInfo(&fileSpec, &theInfo);
  1310.         
  1311.         if ( (theInfo.fdType != 'TEXT') && (theInfo.fdType != 'sEXT') )
  1312.             anErr = eDocumentWrongKind;
  1313.         }
  1314.     nrequire(anErr, StandardPutFile);
  1315.         
  1316.     //
  1317.     // Same file?  If so, just save.
  1318.     //
  1319.     if     (
  1320.         (replacing) && 
  1321.         (oldData != -1) && 
  1322.         (pData->fileSpec.vRefNum == fileSpec.vRefNum) &&
  1323.         (pData->fileSpec.parID == fileSpec.parID) &&
  1324.         EqualString(pData->fileSpec.name, fileSpec.name, false, false)
  1325.         )
  1326.         {
  1327.  
  1328.         anErr = TextSave(pData, true);
  1329.         
  1330.         if (anErr == noErr)
  1331.             UpdateFileInfo(&fileSpec, documentIsText);
  1332.         }
  1333.     else
  1334.         {
  1335.         // create the data file and resource fork
  1336.         (void) FSpDelete(&fileSpec);
  1337.         anErr = FSpCreate(&fileSpec, 'ttxt', 'TEXT', 0);
  1338.         FSpCreateResFile(&fileSpec, 'ttxt', 'TEXT', 0);
  1339.         nrequire(anErr, FailedCreate);
  1340.  
  1341.         // set the stationary bit, if we must
  1342.         if (!documentIsText)
  1343.             {
  1344.             FInfo    theInfo;
  1345.             
  1346.             FSpGetFInfo(&fileSpec, &theInfo);
  1347.             theInfo.fdFlags |= kIsStationery;
  1348.             FSpSetFInfo(&fileSpec, &theInfo);
  1349.             }
  1350.  
  1351.         // open both of forks
  1352.         anErr = FSpOpenDF(&fileSpec, fsRdWrPerm, &pData->dataRefNum);
  1353.         if (anErr == noErr)
  1354.             {
  1355.             pData->resRefNum = FSpOpenResFile(&fileSpec, fsRdWrPerm);
  1356.             anErr = ResError();
  1357.             }
  1358.         nrequire(anErr, FailedOpen);
  1359.  
  1360.         pData->fileSpec = fileSpec;
  1361.         // call the standard save function to do the save
  1362.         anErr = TextSave(pData, false);
  1363.             
  1364.     // FALL THROUGH EXCEPTION HANDLING
  1365.     FailedOpen:
  1366.         FSpDelete(&fileSpec);    
  1367.     FailedCreate:
  1368.     StandardPutFile:
  1369.  
  1370.         // finally, close the old files if everything went okay
  1371.         if (anErr == noErr)
  1372.             {
  1373.             if (oldRes != -1)
  1374.                 CloseResFile(oldRes);
  1375.             if (oldData != -1)
  1376.                 FSClose(oldData);
  1377.             pData->isWritable = true;
  1378.             SetWTitle(pWindow, fileSpec.name);
  1379.             
  1380.             // update the window icon
  1381.             if( gMachineInfo.haveProxyIcons )
  1382.                 SetWindowProxyFSSpec( pWindow, &fileSpec );
  1383.             }
  1384.         else
  1385.             {
  1386.             pData->resRefNum = oldRes;
  1387.             pData->dataRefNum = oldData;
  1388.             pData->fileSpec = oldFileLocation;
  1389.             }
  1390.         }
  1391.     
  1392.     // save new location- •••why here, though?  Need to do it earlier to avoid stale pData->fileSpec in the call to TextSave() above.  I  added a save and restore for the original spec in case of error. - christ
  1393.     
  1394. //    if (anErr == noErr)
  1395. //        BlockMoveData(&fileSpec, &pData->fileSpec, sizeof(FSSpec));
  1396.         
  1397.     if( anErr != noErr )
  1398.     {
  1399.         // restore oldfile location on error
  1400.         BlockMoveData( &oldFileLocation, &pData->fileSpec, sizeof(FSSpec) );
  1401.     }
  1402.     
  1403. // Return eUserCanceled so we can avoid closing/quitting if they cancel the SF dialog
  1404. //    // don't propagate this error
  1405. //    if (anErr == eUserCanceled)
  1406. //        anErr = noErr;
  1407.  
  1408.     if (gMachineInfo.haveNavigationServices)
  1409.     {
  1410.         // file must be closed in order to 'translate in place', so close both forks:
  1411.         if (pData->resRefNum != -1)
  1412.             CloseResFile(pData->resRefNum);        
  1413.         if (pData->dataRefNum != -1)
  1414.             FSClose(pData->dataRefNum);
  1415.             
  1416.         if (anErr == noErr)
  1417.             {
  1418.             anErr = CompleteSave(&fileSpec, &navReply);    // do the translation (in place)
  1419.             
  1420.             // reopen both forks:
  1421.             anErr = FSpOpenDF(&fileSpec, fsRdWrPerm, &pData->dataRefNum);
  1422.             if (anErr == noErr)
  1423.                 {
  1424.                 pData->resRefNum = FSpOpenResFile(&fileSpec, fsRdWrPerm);
  1425.                 anErr = ResError();
  1426.                 }
  1427.             
  1428.             // •• problem:
  1429.             // Save As… model fails in this case because we still have the old window open,
  1430.             // Should SimpleText close the current text window and open a new one with the translated data?
  1431.             }
  1432.     }
  1433.     
  1434.  
  1435.     return anErr;
  1436.     
  1437. } // TextSaveAs
  1438.  
  1439. // --------------------------------------------------------------------------------------------------------------
  1440. static void ApplyFace(short requestedFace, WindowPtr pWindow, WindowDataPtr pData, short commandID)
  1441. {
  1442.     TextStyle    style;
  1443.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1444.     
  1445.     SaveCurrentUndoState(pData, commandID);
  1446.     
  1447.     style.tsFace = requestedFace;
  1448.     TESetStyle(doFace + ((requestedFace != normal) ? doToggle : 0), &style, true, ((TextDataPtr) pData)->hTE);
  1449.     TECalText(((TextDataPtr) pData)->hTE);
  1450.     
  1451.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1452.     AdjustTE(pData, false);
  1453.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1454.  
  1455.     SetDocumentContentChanged( (WindowDataPtr) pData, true );    
  1456. } // ApplyFace
  1457.  
  1458. // --------------------------------------------------------------------------------------------------------------
  1459. static OSErr ApplySize(short requestedSize, WindowPtr pWindow, WindowDataPtr pData, short commandID)
  1460. {
  1461.     OSErr        anErr = noErr;
  1462.     TextStyle    style;
  1463.     short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1464.     
  1465.     SaveCurrentUndoState(pData, commandID);
  1466.  
  1467.     style.tsSize = requestedSize;
  1468.     TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1469.     TECalText(((TextDataPtr) pData)->hTE);
  1470.     if (CalculateTextEditHeight(((TextDataPtr) pData)->hTE) > 32767)
  1471.         {        
  1472.         style.tsSize = 0;
  1473.         TESetStyle(doSize, &style, true, ((TextDataPtr) pData)->hTE);
  1474.         anErr = eDocumentTooLarge;
  1475.         }
  1476.  
  1477.     oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1478.     AdjustTE(pData, false);
  1479.     AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1480.  
  1481.     SetDocumentContentChanged( (WindowDataPtr) pData, true );
  1482.     
  1483.     return anErr;
  1484.  
  1485. } // ApplySize
  1486.  
  1487. // --------------------------------------------------------------------------------------------------------------
  1488. // OOP INTERFACE ROUTINES
  1489. // --------------------------------------------------------------------------------------------------------------
  1490.  
  1491. static OSErr    TextUpdateWindow(WindowPtr pWindow, WindowDataPtr pData)
  1492. {
  1493.     Rect    updateArea = pData->contentRect;
  1494.     Rect    bounds;
  1495.  
  1496.     // be sure to also erase the area where the horizontal scroll bar
  1497.     // is missing.
  1498.     updateArea.bottom = GetWindowPortBounds(pWindow, &bounds)->bottom;
  1499.     EraseRect(&updateArea);
  1500.     
  1501.     TEUpdate(&pData->contentRect, ((TextDataPtr) pData)->hTE);
  1502.  
  1503.     DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1504.     
  1505.     DrawControls(pWindow);
  1506.     DrawGrowIcon(pWindow);
  1507.  
  1508. #if 0
  1509.     /*
  1510.         The voices thread is initially created in a suspended state; once we've received an
  1511.         update for a text window, we enable the thread. This is done because the initial
  1512.         CountVoices and GetVoiceDescription in the voices thread are quite slow, and if the
  1513.         thread is initially enabled, they delay the first update of the text window. We're
  1514.         interested in optimizing for fast display of the window's contents so we delay the
  1515.         thread until the update has occurred.
  1516.     */
  1517.     if (gVoicesThread != kNoThreadID)
  1518.         SetThreadState(gVoicesThread, kReadyThreadState, gVoicesThread);
  1519. #endif
  1520.     
  1521.     return noErr;
  1522.     
  1523. } // TextUpdateWindow
  1524.  
  1525. // --------------------------------------------------------------------------------------------------------------
  1526.  
  1527. static OSErr    TextCloseWindow(WindowPtr pWindow, WindowDataPtr pData)
  1528. {
  1529. #pragma unused (pWindow)
  1530.  
  1531.     // shut down text services
  1532.     if (pData->docTSMDoc)
  1533.         {
  1534.         FixTSMDocument(pData->docTSMDoc);
  1535.         DeactivateTSMDocument(pData->docTSMDoc);
  1536.         DeleteTSMDocument(pData->docTSMDoc);
  1537.         }
  1538. #if 0
  1539.     DisposeOfSpeech(true);
  1540. #endif
  1541.  
  1542.     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  1543.     TEDispose(((TextDataPtr) pData)->hTE);
  1544.  
  1545.     DisposeHandle(((TextDataPtr) pData)->prevText);
  1546.     DisposeHandle(((TextDataPtr) pData)->prevStyle);
  1547.     
  1548.     TextRemoveContentsMenu(pData);
  1549.  
  1550.     return noErr;
  1551.  
  1552. } // TextCloseWindow
  1553.  
  1554. // --------------------------------------------------------------------------------------------------------------
  1555.  
  1556. static OSErr    TextActivateEvent(WindowPtr pWindow, WindowDataPtr pData, Boolean activating)
  1557. {
  1558. #pragma unused (pWindow)
  1559.  
  1560.     // only modifyable docs can be active
  1561.     if (pData->originalFileType == 'TEXT')
  1562.         {
  1563.         if (activating)
  1564.             {
  1565.             TEActivate(((TextDataPtr) pData)->hTE);
  1566.             if (pData->docTSMDoc != nil)
  1567.                 ActivateTSMDocument(pData->docTSMDoc);
  1568.             }
  1569.         else
  1570.             {
  1571.             TEDeactivate(((TextDataPtr) pData)->hTE);
  1572.             if (pData->docTSMDoc != nil)
  1573.                 DeactivateTSMDocument(pData->docTSMDoc);
  1574.             }
  1575.         }
  1576.         
  1577.     // add contents menu, if appropriate        
  1578.     if (activating)
  1579.         {
  1580.         TextAddContentsMenu(pData);
  1581.         }
  1582.     else
  1583.         {
  1584.         TextRemoveContentsMenu(pData);
  1585.         }
  1586.     
  1587.     return noErr;
  1588.     
  1589. } // TextActivateEvent
  1590.  
  1591. // --------------------------------------------------------------------------------------------------------------
  1592.  
  1593. static Boolean    TextFilterEvent(WindowPtr pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1594. {    
  1595.     switch (pEvent->what)
  1596.         {
  1597.         case nullEvent:
  1598.             if (pData->originalFileType == 'TEXT')
  1599.                 {
  1600.                 if ( pWindow == FrontWindow() )
  1601.                     TEIdle(((TextDataPtr) pData)->hTE);
  1602.                 }
  1603.             
  1604. #if 0
  1605.             // if we stop speaking, ditch the channel
  1606.             if (gSpeechChannel) 
  1607.                 {
  1608.                 SpeechStatusInfo    status;                // Status of our speech channel.
  1609.                 
  1610.                 if ( 
  1611.                     (GetSpeechInfo( gSpeechChannel, soStatus, (void*) &status ) == noErr)
  1612.                     &&  (!status.outputBusy )
  1613.                     )
  1614.                     DisposeOfSpeech(true);
  1615.                 }
  1616. #endif
  1617.             break;
  1618.         }
  1619.         
  1620.     return false;
  1621.     
  1622. } // TextFilterEvent
  1623.  
  1624. // --------------------------------------------------------------------------------------------------------------
  1625.  
  1626. static OSErr    TextScrollContent(WindowPtr pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  1627. {
  1628.     GrafPtr        port = (GrafPtr) GetWindowPort(pWindow);
  1629.     RgnHandle    srcRgn, dstRgn, visRgn, clipRgn;
  1630.     Rect        viewRect;
  1631.     
  1632.     // scroll the text area
  1633.     TEScroll(deltaH, deltaV, ((TextDataPtr) pData)->hTE);
  1634.  
  1635.     // calculate the region that is uncovered by the scroll
  1636.     srcRgn = NewRgn();
  1637.         dstRgn = NewRgn();
  1638.         visRgn = NewRgn();
  1639.         clipRgn = NewRgn();
  1640.         GetPortVisibleRegion(port, visRgn);
  1641.         GetPortClipRegion(port, clipRgn);
  1642.     viewRect = (**((TextDataPtr) pData)->hTE).viewRect;
  1643.     RectRgn( srcRgn, &viewRect );
  1644.     SectRgn( srcRgn, visRgn,  srcRgn );
  1645.     SectRgn( srcRgn, clipRgn, srcRgn );
  1646.     CopyRgn( srcRgn, dstRgn );
  1647.     OffsetRgn( dstRgn, deltaH, deltaV );
  1648.     SectRgn( srcRgn, dstRgn, dstRgn );
  1649.     DiffRgn( srcRgn, dstRgn, srcRgn );
  1650.  
  1651.     // clip to this new area
  1652.     GetClip(dstRgn);
  1653.     SetClip(srcRgn);
  1654.         DrawPictures(pData, ((TextDataPtr) pData)->hTE);
  1655.     SetClip(dstRgn);
  1656.     
  1657.     // all done with these calculation regions
  1658.     DisposeRgn( srcRgn );
  1659.         DisposeRgn( dstRgn );
  1660.         DisposeRgn( visRgn );
  1661.         DisposeRgn( clipRgn );
  1662.     
  1663.     return eActionAlreadyHandled;
  1664.     
  1665. } // TextScrollContent
  1666.  
  1667. // --------------------------------------------------------------------------------------------------------------
  1668.  
  1669. static OSErr    TextKeyEvent(WindowPtr pWindow, WindowDataPtr pData, EventRecord *pEvent, Boolean isMotionKey)
  1670. {    
  1671.     OSErr    anErr = noErr;
  1672.     
  1673.     if (!(pEvent->modifiers & cmdKey)) 
  1674.         {
  1675.         char    theKey = pEvent->message & charCodeMask;
  1676.         char    theKeyCode = (pEvent->message >> 8) & charCodeMask;
  1677.         
  1678.         if ( 
  1679.             ((** ((TextDataPtr) pData)->hTE).teLength+1 > kMaxLength) &&
  1680.                 (    
  1681.                 (theKey != kDeleteKey) &&
  1682.                 (theKeyCode != kForwardDeleteKey) &&
  1683.                 (theKeyCode != kUpArrow) &&
  1684.                 (theKeyCode != kDownArrow) &&
  1685.                 (theKeyCode != kRightArrow) &&
  1686.                 (theKeyCode != kLeftArrow)
  1687.                 ) 
  1688.             )
  1689.             anErr = eDocumentTooLarge;
  1690.         else
  1691.             {
  1692.             long        oldHeight = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1693.             long        end = (**(((TextDataPtr) pData)->hTE)).selEnd;
  1694.             long        start = (**(((TextDataPtr) pData)->hTE)).selStart;
  1695.  
  1696.             ObscureCursor();
  1697.             SaveCurrentUndoState(pData, cTypingCommand);
  1698.             if (theKeyCode != kForwardDeleteKey)
  1699.                 {
  1700.                 if (pEvent->modifiers & shiftKey)
  1701.                     {
  1702.                     switch (theKeyCode)
  1703.                         {
  1704.                         case kUpArrow:
  1705.                         case kLeftArrow:
  1706.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1707.                             TESetSelect((**(((TextDataPtr) pData)->hTE)).selStart, end, ((TextDataPtr) pData)->hTE);
  1708.                             break;
  1709.                             
  1710.                         case kDownArrow:
  1711.                         case kRightArrow:
  1712.                             TESetSelect(end, end, ((TextDataPtr) pData)->hTE);
  1713.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1714.                             TESetSelect(start, (**(((TextDataPtr) pData)->hTE)).selEnd, ((TextDataPtr) pData)->hTE);
  1715.                             break;
  1716.                         
  1717.                         default:
  1718.                             TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1719.                         }
  1720.                     }
  1721.                 else
  1722.                     TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1723.                 }
  1724.             else
  1725.                 {
  1726.                 if (gTE6Version)
  1727.                     {
  1728.                     TEKey(theKey, ((TextDataPtr) pData)->hTE);
  1729.                     }
  1730.                 else
  1731.                     {
  1732.                     if     ( end < (**(((TextDataPtr) pData)->hTE)).teLength )
  1733.                         {
  1734.                         if (start == end)
  1735.                             TEKey(kRightArrowCharCode, ((TextDataPtr) pData)->hTE);
  1736.                         TEKey(kBackspaceCharCode, ((TextDataPtr) pData)->hTE);
  1737.                         }
  1738.                     }
  1739.                 }
  1740.             oldHeight -= CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  1741.             
  1742.             // AEC, changed pWindow to pData for 'TextDataPtr' casts
  1743.             ((TextDataPtr) pData)->insideClickLoop = true;
  1744.                 AdjustTE(pData, false);
  1745.                 AdjustScrollBars(pWindow, (oldHeight > 0), (oldHeight > 0), nil);
  1746.             ((TextDataPtr) pData)->insideClickLoop = false;
  1747.  
  1748.             if (!isMotionKey)
  1749.                 {
  1750.                 SetDocumentContentChanged( pData, true );
  1751.                 }
  1752.             
  1753.             }
  1754.         }
  1755.  
  1756.     return anErr;
  1757.  
  1758. } // TextKeyEvent
  1759.  
  1760. // --------------------------------------------------------------------------------------------------------------
  1761.  
  1762. static OSErr    TextContentClick(WindowPtr pWindow, WindowDataPtr pData, EventRecord *pEvent)
  1763. {
  1764.     OSErr            anErr = noErr;
  1765.     Point            clickPoint = pEvent->where;
  1766.     ControlHandle    theControl;
  1767.     RgnHandle        hilightRgn;
  1768.  
  1769.     GlobalToLocal(&clickPoint);
  1770.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  1771.         {
  1772.         if (gMachineInfo.haveDragMgr)
  1773.             {
  1774.             hilightRgn = NewRgn();
  1775.             TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  1776.  
  1777.             if (PtInRgn(clickPoint, hilightRgn))
  1778.                 {
  1779.                 SaveCurrentUndoState(pData, cTypingCommand);
  1780.                 if (!DragText(pWindow, pData, pEvent, hilightRgn))
  1781.                     anErr = eActionAlreadyHandled;
  1782.                 }
  1783.             else        
  1784.                 {
  1785.                 anErr = eActionAlreadyHandled;
  1786.                 }
  1787.     
  1788.             DisposeRgn(hilightRgn);
  1789.             }
  1790.         else
  1791.             {
  1792.             anErr = eActionAlreadyHandled;
  1793.             }
  1794.         }
  1795.  
  1796.     if ( (anErr == eActionAlreadyHandled) && (PtInRect(clickPoint, &pData->contentRect)) )
  1797.         {
  1798.         TEClick(clickPoint, (pEvent->modifiers & shiftKey) != 0, ((TextDataPtr) pData)->hTE);
  1799.         }
  1800.         
  1801.     return anErr;
  1802.     
  1803. } // TextContentClick
  1804.  
  1805. // --------------------------------------------------------------------------------------------------------------
  1806.  
  1807. static OSErr    TextAdjustSize(WindowPtr pWindow, WindowDataPtr pData, 
  1808.             Boolean *didReSize) // input: was window resized, output: t->we resized something
  1809. {
  1810. #pragma unused (pWindow)
  1811.     
  1812.     if (*didReSize)
  1813.         {
  1814.         RecalcTE(pData, true);
  1815.         AdjustTE(pData, true);
  1816.         }
  1817.     else
  1818.         {
  1819.         AdjustTE(pData, false);
  1820.         }
  1821.         
  1822.     return noErr;
  1823.     
  1824. } // TextAdjustSize
  1825.  
  1826. static OSErr    TextCommand(WindowPtr pWindow, WindowDataPtr pData, short commandID, long menuResult)
  1827. {
  1828.  
  1829.     OSErr    anErr = noErr;
  1830.     AEDesc     theSourceText = {typeNull, NULL};            // for AppleScript
  1831.     AEDesc     theResultText = {typeNull, NULL};            // for AppleScript
  1832.     OSAID     resultID;                        // for AppleScript
  1833.     Ptr     resultTextPtr;                        // for AppleScript
  1834.     Size     resultDataSize;                        // for AppleScript
  1835.  
  1836.     SetPort((GrafPtr) GetWindowPort(pWindow));
  1837.     
  1838.     if ( (pData->docTSMDoc) && (menuResult != 0) )
  1839.         FixTSMDocument(pData->docTSMDoc);
  1840.         
  1841.     switch (commandID)
  1842.         {            
  1843.         case cUndo:
  1844.             {
  1845.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1846.  
  1847.             PerformUndo(pData);
  1848.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1849.             AdjustTE(pData, false);
  1850.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1851.             RecalcTE(pData, true);
  1852.             
  1853.             SetDocumentContentChanged( pData, true );
  1854.                 
  1855.             }
  1856.             break;
  1857.             
  1858.         case cCut:
  1859.             SaveCurrentUndoState(pData, cCut);
  1860.             {
  1861.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1862.  
  1863.             TECut(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1864.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1865.             AdjustTE(pData, false);
  1866.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1867.         
  1868.             SetDocumentContentChanged( (WindowDataPtr) pData, true );
  1869.                 
  1870.             }
  1871.             break;
  1872.             
  1873.         case cCopy:
  1874.             TECopy(((TextDataPtr) pData)->hTE);    // no need for TEToScrap with styled TE record
  1875.             AdjustTE(pData, false);
  1876.             break;
  1877.             
  1878.         case cClear:
  1879.             SaveCurrentUndoState(pData, cClear);
  1880.             {
  1881.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1882.  
  1883.             TEDelete(((TextDataPtr) pData)->hTE);
  1884.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1885.             AdjustTE(pData, false);
  1886.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1887.             
  1888.             SetDocumentContentChanged( pData, true );
  1889.                 
  1890.             }
  1891.             break;
  1892.             
  1893.         case cPaste:
  1894.             SaveCurrentUndoState(pData, cPaste);
  1895.             {
  1896.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1897.  
  1898.             anErr = TEFromScrap();            
  1899.             if (anErr == noErr)
  1900.                 {                
  1901.                 // if the current length, plus the paste data, minus the data in the selection
  1902.                 // would make the document too large, say so
  1903.                 if     ( 
  1904.                         ((** ((TextDataPtr) pData)->hTE).teLength +
  1905.                         TEGetScrapLength() -
  1906.                         ((** ((TextDataPtr) pData)->hTE).selEnd-(** ((TextDataPtr) pData)->hTE).selStart)
  1907.                         )
  1908.                     > kMaxLength)
  1909.                     {
  1910.                     anErr = eDocumentTooLarge;
  1911.                     }
  1912.                 else
  1913.                     {
  1914.                     Handle    aHandle = (Handle) TEGetText(((TextDataPtr) pData)->hTE);
  1915.                     Size    oldSize = GetHandleSize(aHandle);
  1916.                     Size    newSize = oldSize + TEGetScrapLength();
  1917.                     OSErr    saveErr;
  1918.                     
  1919.                     SetHandleSize(aHandle, newSize);
  1920.                     saveErr = MemError();
  1921.                     SetHandleSize(aHandle, oldSize);
  1922.                     if (saveErr != noErr)
  1923.                         anErr = eDocumentTooLarge;
  1924.                     else
  1925.                         TEStylePaste(((TextDataPtr) pData)->hTE);
  1926.                     }
  1927.                     
  1928.                 UnloadScrap();
  1929.                 }
  1930.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1931.             AdjustTE(pData, false);
  1932.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1933.             
  1934.             SetDocumentContentChanged( pData, true );
  1935.             }
  1936.             break;
  1937.             
  1938.         case cReplace:
  1939.             SaveCurrentUndoState(pData, cReplace);
  1940.             {
  1941.             short result = ConductFindOrReplaceDialog(kReplaceWindowID);
  1942.             
  1943.             if (result == cancel)
  1944.                 break;
  1945.                 
  1946.             if (result == iReplaceAll)
  1947.                 {
  1948.                 long         newStart, newEnd;
  1949.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1950.                 
  1951.                 TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  1952.                 while (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1953.                                     (**((TextDataPtr) pData)->hTE).selStart,
  1954.                                     gFindString, gCaseSensitive, false, false,
  1955.                                     &newStart, &newEnd))
  1956.                     {
  1957.                     TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1958.                     TEDelete(((TextDataPtr) pData)->hTE);
  1959.                     TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1960.                     TESetSelect(newStart+gReplaceString[0], newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1961.  
  1962.                     SetDocumentContentChanged( (WindowDataPtr) pData, true );    
  1963.                     }
  1964.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1965.                 
  1966.                 AdjustTE(pData, false);
  1967.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1968.     
  1969.                 anErr = eActionAlreadyHandled;
  1970.                 break;
  1971.                 }
  1972.             }
  1973.         
  1974.         // fall through from replace
  1975.         case cReplaceAgain:
  1976.             SaveCurrentUndoState(pData, cReplaceAgain);
  1977.             {
  1978.             long     newStart, newEnd;
  1979.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  1980.             
  1981.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  1982.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selEnd : (**((TextDataPtr) pData)->hTE).selStart,
  1983.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  1984.                                 &newStart, &newEnd))
  1985.                 {
  1986.                 short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  1987.                 
  1988.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  1989.                 TEDelete(((TextDataPtr) pData)->hTE);
  1990.                 TEInsert(&gReplaceString[1], gReplaceString[0], ((TextDataPtr) pData)->hTE);
  1991.                 TESetSelect(newStart+gReplaceString[0], newStart+gReplaceString[0], ((TextDataPtr) pData)->hTE);                
  1992.                 oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  1993.                 
  1994.                 AdjustTE(pData, false);
  1995.                 AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  1996.  
  1997.                 SetDocumentContentChanged( pData, true );
  1998.                 }
  1999.             else
  2000.                 SysBeep(1);
  2001.  
  2002.             anErr = eActionAlreadyHandled;
  2003.             }
  2004.             break;
  2005.  
  2006.         // for AppleScript
  2007.         case cExecute:
  2008.             {
  2009.             fprintf(stderr, "Attempting to execute and applescript selection.\n");
  2010.             SaveCurrentUndoState(pData, cExecute);
  2011.             AECreateDesc(typeChar, (*(**((TextDataPtr) pData)->hTE).hText) + (**((TextDataPtr) pData)->hTE).selStart, 
  2012.                          (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart,
  2013.                          &theSourceText);
  2014.             anErr = OSACompileExecute( gOSAComponent, &theSourceText, kOSANullScript, 
  2015.                          kAECanInteract, &resultID);
  2016.             if (anErr == noErr)
  2017.             {
  2018.                 anErr = OSADisplay( gOSAComponent, resultID, typeChar, kOSAModeNull, &theResultText);
  2019.                 if (anErr == noErr) 
  2020.                 {
  2021.                     TESetSelect((**((TextDataPtr) pData)->hTE).selEnd, (**((TextDataPtr) pData)->hTE).selEnd, 
  2022.                               ((TextDataPtr) pData)->hTE);
  2023.                     resultTextPtr = NewPtr(resultDataSize = AEGetDescDataSize(&theResultText));
  2024.                     AEGetDescData(&theResultText, theResultText.descriptorType, resultTextPtr, resultDataSize);
  2025.                     TEInsert(" --> ", 5, ((TextDataPtr) pData)->hTE);
  2026.                     TEInsert(resultTextPtr, resultDataSize, ((TextDataPtr) pData)->hTE);
  2027.                     AdjustTE(pData, false);
  2028.                     DisposePtr(resultTextPtr);
  2029.                     AEDisposeDesc(&theResultText);
  2030.                 }
  2031.                 else fprintf(stderr, "OSADisplay returned error %i.\n", anErr);
  2032.             }
  2033.             else fprintf(stderr, "OSACompileExecute returned error %i.\n", anErr);
  2034.  
  2035.             anErr = OSADispose( gOSAComponent, resultID);
  2036.             AEDisposeDesc(&theSourceText);
  2037.             SetDocumentContentChanged( pData, true);
  2038.             break;
  2039.             }
  2040.  
  2041.         case cFind:
  2042.         case cFindSelection:
  2043.             if (commandID == cFind)
  2044.                 {
  2045.                 if (ConductFindOrReplaceDialog(kFindWindowID) == cancel)    
  2046.                     break;
  2047.                 }
  2048.             else
  2049.                 {
  2050.                 gFindString[0] = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  2051.                 BlockMoveData( (*(**((TextDataPtr) pData)->hTE).hText) + (**((TextDataPtr) pData)->hTE).selStart, &gFindString[1], gFindString[0]);
  2052.                 }
  2053.             
  2054.         // fall through from find or find selection
  2055.         case cFindAgain:
  2056.             {
  2057.             long     newStart, newEnd;
  2058.             Boolean    isBackwards = ((gEvent.modifiers & shiftKey) != 0);
  2059.             
  2060.             if (PerformSearch((**((TextDataPtr) pData)->hTE).hText,
  2061.                                 isBackwards ? (**((TextDataPtr) pData)->hTE).selStart : (**((TextDataPtr) pData)->hTE).selEnd,
  2062.                                 gFindString, gCaseSensitive, isBackwards, gWrapAround,
  2063.                                 &newStart, &newEnd))
  2064.                 {
  2065.                 TESetSelect(newStart, newEnd, ((TextDataPtr) pData)->hTE);
  2066.                 AdjustTE(pData, false);
  2067.                 AdjustScrollBars(pWindow, false, false, nil);
  2068.                 }
  2069.             else
  2070.                 SysBeep(1);
  2071.  
  2072.             anErr = eActionAlreadyHandled;
  2073.             }
  2074.             break;
  2075.             
  2076.         case cSelectAll:
  2077.             TESetSelect(0, (**((TextDataPtr) pData)->hTE).teLength, 
  2078.                         ((TextDataPtr) pData)->hTE);
  2079.             AdjustTE(pData, false);
  2080.             AdjustScrollBars(pWindow, false, false, nil);
  2081.             DoAdjustCursor(pWindow, nil);
  2082.             anErr = eActionAlreadyHandled;
  2083.             break;
  2084.         
  2085.         // save turns into save as if this is a new document or if the original wasn't
  2086.         // available for writing
  2087.         case cSave:
  2088.             if     (
  2089.                 (!pData->isWritable) || 
  2090.                 ( (pData->dataRefNum == -1) && (pData->resRefNum == -1) )
  2091.                 )
  2092.                 anErr = TextSaveAs(pWindow, pData);
  2093.             else
  2094.                 anErr = TextSave(pData, true);
  2095.             break;
  2096.             
  2097.         case cSaveAs:
  2098.             anErr = TextSaveAs(pWindow, pData);
  2099.             break;
  2100.             
  2101.         // SUPPORTED FONTS
  2102.         case cSelectFont:
  2103.             SaveCurrentUndoState(pData, cSelectFont);
  2104.             {
  2105.             Str255        itemName;
  2106.             TextStyle    theStyle;
  2107.             short        oldNumLines = (**(((TextDataPtr) pData)->hTE)).nLines;
  2108.             
  2109.             GetMenuItemText( GetMenuHandle( menuResult>>16 ), menuResult & 0xFFFF, itemName );
  2110.             GetFNum( itemName, &theStyle.tsFont );
  2111.             TESetStyle( doFont, &theStyle, true, ((TextDataPtr) pData)->hTE );
  2112.             TECalText(((TextDataPtr) pData)->hTE);
  2113.             oldNumLines -= (**(((TextDataPtr) pData)->hTE)).nLines;
  2114.             AdjustTE(pData, false);
  2115.             AdjustScrollBars(pWindow, (oldNumLines > 0), (oldNumLines > 0), nil);
  2116.             
  2117.             SetDocumentContentChanged( pData, true );
  2118.             }
  2119.             break;
  2120.             
  2121.             
  2122.         // SUPPORTED STYLES
  2123.         case cPlain:
  2124.             ApplyFace(normal, pWindow, pData, cPlain);
  2125.             break;
  2126.             
  2127.         case cBold:
  2128.             ApplyFace(bold, pWindow, pData, cBold);
  2129.             break;
  2130.  
  2131.         case cItalic:
  2132.             ApplyFace(italic, pWindow, pData, cItalic);
  2133.             break;
  2134.             
  2135.         case cUnderline:
  2136.             ApplyFace(underline, pWindow, pData, cUnderline);
  2137.             break;
  2138.             
  2139.         case cOutline:
  2140.             ApplyFace(outline, pWindow, pData, cOutline);
  2141.             break;
  2142.             
  2143.         case cShadow:
  2144.             ApplyFace(shadow, pWindow, pData, cShadow);
  2145.             break;
  2146.  
  2147.         case cCondensed:
  2148.             ApplyFace(condense, pWindow, pData, cCondensed);
  2149.             break;
  2150.             
  2151.         case cExtended:
  2152.             ApplyFace(extend, pWindow, pData, cExtended);
  2153.             break;
  2154.             
  2155.     
  2156.         // SUPPORTED SIZES
  2157.         case cSize9:
  2158.             anErr = ApplySize(9, pWindow, pData, cSize9);
  2159.             break;
  2160.             
  2161.         case cSize10:
  2162.             anErr = ApplySize(10, pWindow, pData, cSize10);
  2163.             break;
  2164.             
  2165.         case cSize12:
  2166.             anErr = ApplySize(12, pWindow, pData, cSize12);
  2167.             break;
  2168.             
  2169.         case cSize14:
  2170.             anErr = ApplySize(14, pWindow, pData, cSize14);
  2171.             break;
  2172.             
  2173.         case cSize18:
  2174.             anErr = ApplySize(18, pWindow, pData, cSize18);
  2175.             break;
  2176.             
  2177.         case cSize24:
  2178.             anErr = ApplySize(24, pWindow, pData, cSize24);
  2179.             break;
  2180.             
  2181.         case cSize36:
  2182.             anErr = ApplySize(36, pWindow, pData, cSize36);
  2183.             break;
  2184.             
  2185. #if 0            
  2186.         // SUPPORTED SOUND COMMANDS
  2187.         case cRecord:
  2188.             {
  2189.             Handle    tempHandle;
  2190.  
  2191.             // allocate our prefered buffer if we can, but if we can't
  2192.             // make sure that at least a minimum amount of RAM is around
  2193.             // before recording.
  2194.             tempHandle = NewHandle(kPrefBufferSize);
  2195.             anErr = MemError();
  2196.             if (anErr != noErr)
  2197.                 {
  2198.                 tempHandle = NewHandle(kMinBufferSize);
  2199.                 anErr = MemError();
  2200.                 DisposeHandle(tempHandle);
  2201.                 tempHandle = nil;
  2202.                 }
  2203.             
  2204.             // if the preflight goes okay, do the record
  2205.             if (anErr == noErr)
  2206.                 {
  2207.                 Point    where = {50, 100};
  2208.                 
  2209.                 anErr = SndRecord(nil, where, siGoodQuality, (SndListHandle*) &tempHandle);
  2210.                 if (anErr == noErr)
  2211.                     {
  2212.                     DisposeHandle(((TextDataPtr) pData)->soundHandle);
  2213.                     ((TextDataPtr) pData)->soundHandle = tempHandle;
  2214.                     
  2215.                     SetDocumentContentChanged( (WindowDataPtr) pData, true );
  2216.                     }
  2217.                 else
  2218.                     DisposeHandle(tempHandle);
  2219.                     
  2220.                 if (anErr == userCanceledErr)
  2221.                     anErr = noErr;
  2222.                 }
  2223.             }
  2224.             break;
  2225.             
  2226.         case cPlay:
  2227.             if (((TextDataPtr) pData)->soundHandle)
  2228.                 (void) SndPlay(nil, (SndListHandle) ((TextDataPtr) pData)->soundHandle, false);
  2229.             break;
  2230.  
  2231.         case cErase:
  2232.             DisposeHandle(((TextDataPtr) pData)->soundHandle);
  2233.             ((TextDataPtr) pData)->soundHandle = nil;
  2234.             
  2235.             SetDocumentContentChanged( (WindowDataPtr) pData, true );
  2236.             break;
  2237.             
  2238.         case cSpeak:
  2239.             DisposeOfSpeech(false);
  2240.             if (gSpeechChannel == nil)
  2241.                 anErr = NewSpeechChannel( &gCurrentVoice, &gSpeechChannel );
  2242.                 
  2243.             if ( anErr == noErr )
  2244.                 {
  2245.                 short    textLength, textStart;
  2246.                                     
  2247.                 // determine which text to speak
  2248.                 if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2249.                     {
  2250.                     textLength = (**((TextDataPtr) pData)->hTE).selEnd - (**((TextDataPtr) pData)->hTE).selStart;
  2251.                     textStart = (**((TextDataPtr) pData)->hTE).selStart;
  2252.                     }
  2253.                 else                                            // No text selected.
  2254.                     {
  2255.                     textLength = (**((TextDataPtr) pData)->hTE).teLength;
  2256.                     textStart = 0;
  2257.                     }
  2258.                     
  2259.                     
  2260.                 gSpeakPtr = NewPtr(textLength);
  2261.                 anErr = MemError();
  2262.                 if (anErr == noErr)
  2263.                     {
  2264.                     BlockMoveData( *((**((TextDataPtr) pData)->hTE).hText) + textStart, gSpeakPtr, (Size) textLength );
  2265.                     anErr = SpeakText( gSpeechChannel, gSpeakPtr, textLength );
  2266.                     }
  2267.                 }
  2268.             break;
  2269.  
  2270.         case cStopSpeaking:
  2271.             DisposeOfSpeech(true);
  2272.             break;
  2273.             
  2274.         case cSelectVoiceSubMenu:
  2275.                 {
  2276.                 VoiceSpec    newSpec;
  2277.                 short        i, menuIndex;
  2278.                 Str255        itemText;
  2279.                 short        theVoiceCount;
  2280.                 MenuHandle     menu = GetMenuHandle(mVoices);
  2281.                 
  2282.                 // in order to change voices, we need to ditch the speaking
  2283.                 DisposeOfSpeech(true);
  2284.  
  2285.                 // get the name of the selected voice                
  2286.                 menuIndex = menuResult & 0xFFFF;
  2287.                 GetMenuItemText(menu, menuIndex, itemText);
  2288.                 
  2289.                 if (CountVoices( &theVoiceCount ) == noErr)
  2290.                     {
  2291.                     VoiceDescription    description;        // Info about a voice.
  2292.         
  2293.                     for (i = 1; i <= theVoiceCount; ++i)
  2294.                         {
  2295.                         if ( (GetIndVoice( i, &newSpec ) == noErr)  &&
  2296.                              (GetVoiceDescription( &newSpec, &description, sizeof(description) ) == noErr ) )
  2297.                             {
  2298.                             if (IUCompString( itemText, description.name ) == 0)
  2299.                                 break;
  2300.                             }
  2301.                         }
  2302.                     }
  2303.                     
  2304.                 gCurrentVoice = newSpec;
  2305.                 for (i = CountMItems(menu); i >= 1; --i)
  2306.                     CheckItem(menu, i, (menuIndex == i));
  2307.                 }
  2308.             break;
  2309. #endif        
  2310.         
  2311.         case cSelectContents:
  2312.                 {
  2313.                 Str255    searchStr;
  2314.                 short    menuIndex;
  2315.                 long     newStart, newEnd;
  2316.                 
  2317.                 menuIndex = menuResult & 0xFFFF;
  2318.  
  2319.                 // get the search string for this menu item
  2320.                 anErr = TextGetContentsListItem(pData, menuIndex, nil, searchStr, nil);
  2321.                 
  2322.                 if (anErr == noErr)
  2323.                     {
  2324.                     if (PerformSearch(
  2325.                         (**((TextDataPtr) pData)->hTE).hText,
  2326.                         0,            // start at beginning of text
  2327.                         searchStr,
  2328.                         false,        // not case sensitive
  2329.                         false,        // forwards
  2330.                         false,        // wrap
  2331.                         &newStart, 
  2332.                         &newEnd))
  2333.                         {
  2334.                         
  2335.                         // <7>
  2336.                         
  2337.                         short     amount;
  2338.                         Point    newSelectionPt;
  2339.                         
  2340.                         // get QuickDraw offset of found text,  
  2341.                         // scroll that amount plus a line height,
  2342.                         // and add a fifth of the window for aesthetics (and  
  2343.                         // for slop to avoid fraction-of-line problems)
  2344.  
  2345.                         newSelectionPt = TEGetPoint(newEnd, ((TextDataPtr) pData)->hTE);
  2346.                         
  2347.                         amount = - newSelectionPt.v + pData->vScrollAmount;
  2348.                         amount += (pData->contentRect.bottom - pData->contentRect.top) / 5;
  2349.                         
  2350.                         SetControlAndClipAmount(pData->vScroll, &amount);
  2351.                         if (amount != 0)
  2352.                             {
  2353.                             DoScrollContent(pWindow, pData, 0, amount);
  2354.                             }
  2355.  
  2356.                         // move selection to beginning of found text 
  2357.                         // (are the Adjust calls necessary?)
  2358.                         
  2359.                         TESetSelect(newStart, newStart, ((TextDataPtr) pData)->hTE);    
  2360.                         AdjustTE(pData, false);
  2361.                         AdjustScrollBars(pWindow, false, false, nil);
  2362.  
  2363.                         }
  2364.                     else
  2365.                         {
  2366.                             // search failed
  2367.                             SysBeep(10);
  2368.                         }
  2369.                     }
  2370.                 }
  2371.             break;
  2372.         }
  2373.         
  2374.     return anErr;
  2375.     
  2376. } // TextCommand
  2377.  
  2378. // --------------------------------------------------------------------------------------------------------------
  2379.  
  2380. static OSErr    TextPreMenuAccess(WindowPtr pWindow, WindowDataPtr pData)
  2381. {
  2382. #pragma unused (pWindow, pData)
  2383.  
  2384. #if 0
  2385.     // run to completion before allowing access to the voices menu
  2386.     if (gVoicesThread != kNoThreadID)
  2387.         {
  2388.         gDontYield = true;
  2389.         SetThreadState(gVoicesThread, kReadyThreadState, gVoicesThread);
  2390.         YieldToThread(gVoicesThread);
  2391.         gDontYield = false;
  2392.         }
  2393. #endif
  2394.  
  2395.     return noErr;
  2396. }
  2397.  
  2398. // --------------------------------------------------------------------------------------------------------------
  2399.  
  2400. static OSErr    TextAdjustMenus(WindowPtr pWindow, WindowDataPtr pData)
  2401. {
  2402. #pragma unused (pWindow)
  2403.  
  2404.     // enable the commands that we support for editable text document
  2405.     if (pData->originalFileType == 'TEXT')
  2406.         {        
  2407.         if (((TextDataPtr) pData)->prevCommandID != cNull)
  2408.             EnableCommand(cUndo);
  2409.             
  2410.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2411.             {
  2412.             EnableCommand(cCut);
  2413.             EnableCommand(cCopy);
  2414.             EnableCommand(cClear);
  2415.  
  2416.             EnableCommand(cExecute);    // for AppleScript
  2417.             
  2418.             EnableCommand(cFindSelection);
  2419.             }
  2420.         
  2421.         TEFromScrap();
  2422.         if (TEGetScrapLength() > 0)
  2423.             EnableCommand(cPaste);
  2424.             
  2425.         EnableCommand(cSaveAs);
  2426.         EnableCommand(cSelectAll);
  2427.         
  2428.         EnableCommand(cFind);
  2429.         EnableCommand(cReplace);
  2430.         if (gFindString[0] != 0)
  2431.             {
  2432.             EnableCommand(cFindAgain);
  2433.             EnableCommand(cReplaceAgain);
  2434.             }
  2435.             
  2436.         // enable all fonts, select the font current, if that's what's best
  2437.         EnableCommand(cSelectFont);
  2438.         {
  2439.         short        mode = doFont;
  2440.         Str255        fontName, itemName;
  2441.         Str255        styleName;
  2442.         TextStyle    theStyle;
  2443.         Boolean        isCont;
  2444.         
  2445.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2446.         if (isCont)
  2447.             {
  2448.                 GetFontName(theStyle.tsFont, fontName);
  2449.             }
  2450.  
  2451.  
  2452.             {
  2453.             MenuHandle    menu = GetMenuHandle( mFont );
  2454.             short        count = CountMItems(menu);
  2455.             short        index;
  2456.             
  2457.             for (index = 1; index <= count; ++index)
  2458.                 {
  2459.                 short    mark;
  2460.                 
  2461.                 GetItemMark(menu, index, &mark);
  2462.                 if (isCont)
  2463.                     {
  2464.                     GetMenuItemText( menu, index, itemName );
  2465.                     
  2466.                     // don't change the checkmark if it's a heirarchichal menu, because
  2467.                     // the mark actually holds the ID of sub-menu
  2468.                     if ((mark == noMark) || (mark == checkMark))
  2469.                         {
  2470.                         CheckItem(menu, index, EqualString(itemName, fontName, true, true) );
  2471.                         }
  2472.                     else
  2473.                         {
  2474.                         // if it is a sub menu, we check there too
  2475.                         MenuHandle    subMenu = GetMenuHandle(mark);
  2476.                         short        subCount = CountMItems(subMenu);
  2477.                         short        subIndex;
  2478.                         
  2479.                         if (EqualString(itemName, fontName, true, true))
  2480.                             {
  2481.                             SetItemStyle(menu, index, underline);
  2482.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2483.                                 {
  2484.                                 GetMenuItemText(subMenu, subIndex, itemName);
  2485.                                 CheckItem(subMenu, subIndex, EqualString(itemName, styleName, true, true) );
  2486.                                 }
  2487.                             }
  2488.                         else
  2489.                             {
  2490.                             SetItemStyle(menu, index, normal);
  2491.                             for (subIndex = 1; subIndex <= subCount; ++subIndex)
  2492.                                 CheckItem(subMenu, subIndex, false );
  2493.                             }
  2494.                         }
  2495.                     }
  2496.                 else
  2497.                     {
  2498.                     if ((mark == noMark) || (mark == checkMark))
  2499.                         CheckItem(menu, index, false);
  2500.                     else
  2501.                         SetItemStyle(menu, index, normal);
  2502.                     }
  2503.                 }
  2504.             }
  2505.         }
  2506.  
  2507.         // enable the sizes, and outline what's currently valid
  2508.         {
  2509.         short        mode;
  2510.         TextStyle    theStyle;
  2511.         Boolean        isCont;
  2512.         short        whichToCheck;
  2513.         
  2514.         // find out the continuous run of sizes
  2515.         whichToCheck = 0;
  2516.         mode = doSize;
  2517.         if (TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2518.             {            
  2519.             whichToCheck = theStyle.tsSize;
  2520.             
  2521.             // default font size -> proper size
  2522.             if (whichToCheck == 0)
  2523.                 whichToCheck = GetDefFontSize();
  2524.             }
  2525.             
  2526.         // find out the font runs
  2527.         mode = doFont;
  2528.         isCont = TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE);
  2529.         
  2530.         EnableCommandCheckStyle(cSize9,  whichToCheck == 9, (isCont & RealFont(theStyle.tsFont, 9)) ? outline : normal);
  2531.         EnableCommandCheckStyle(cSize10, whichToCheck == 10, (isCont & RealFont(theStyle.tsFont, 10)) ? outline : normal);
  2532.         EnableCommandCheckStyle(cSize12, whichToCheck == 12, (isCont & RealFont(theStyle.tsFont, 12)) ? outline : normal);
  2533.         EnableCommandCheckStyle(cSize14, whichToCheck == 14, (isCont & RealFont(theStyle.tsFont, 14)) ? outline : normal);
  2534.         EnableCommandCheckStyle(cSize18, whichToCheck == 18, (isCont & RealFont(theStyle.tsFont, 18)) ? outline : normal);
  2535.         EnableCommandCheckStyle(cSize24, whichToCheck == 24, (isCont & RealFont(theStyle.tsFont, 24)) ? outline : normal);
  2536.         EnableCommandCheckStyle(cSize36, whichToCheck == 36, (isCont & RealFont(theStyle.tsFont, 36)) ? outline : normal);            
  2537.         }
  2538.         
  2539.         {
  2540.         short        mode = doFace;
  2541.         TextStyle    theStyle;
  2542.         Style        legalStyles;
  2543.         
  2544.         if (!TEContinuousStyle(&mode, &theStyle, ((TextDataPtr) pData)->hTE))
  2545.             {
  2546.             theStyle.tsFace = normal;
  2547.             EnableCommandCheck(cPlain, false);
  2548.             }
  2549.         else
  2550.             EnableCommandCheck(cPlain, theStyle.tsFace == normal);
  2551.             
  2552.         // <39> use the script manager to determine legal styles for this
  2553.         // run of text.  If the legal styles are zero (trap unimplemented),
  2554.         // then we assume all styles.
  2555.         legalStyles = GetScriptVariable(GetScriptManagerVariable(smKeyScript), smScriptValidStyles);
  2556.         if (legalStyles == 0)
  2557.             legalStyles = (Style)0xFFFF;
  2558.             
  2559.         if (legalStyles & bold)
  2560.             EnableCommandCheck(cBold,             theStyle.tsFace & bold);
  2561.         if (legalStyles & italic)
  2562.             EnableCommandCheck(cItalic,         theStyle.tsFace & italic);
  2563.         if (legalStyles & underline)
  2564.             EnableCommandCheck(cUnderline,         theStyle.tsFace & underline);
  2565.         if (legalStyles & outline)
  2566.             EnableCommandCheck(cOutline,         theStyle.tsFace & outline);
  2567.         if (legalStyles & shadow)
  2568.             EnableCommandCheck(cShadow,         theStyle.tsFace & shadow);
  2569.         if (legalStyles & condense)
  2570.             EnableCommandCheck(cCondensed,         theStyle.tsFace & condense);
  2571.         if (legalStyles & extend)
  2572.             EnableCommandCheck(cExtended,         theStyle.tsFace & extend);
  2573.         }
  2574.         
  2575.         }
  2576.  
  2577.     // enable commands related to speaking the content if we have support for that
  2578.     if (gMachineInfo.haveTTS)
  2579.         {
  2580.         // note that none of this code should depend on the actual contents of the voices menu,
  2581.         // since the voices thread might not have finished setting up the menu yet
  2582.  
  2583. #if 0        
  2584.         // if we are speaking, we can stop
  2585.         if (gSpeechChannel) 
  2586.             EnableCommand(cStopSpeaking);
  2587. #endif
  2588.  
  2589.         // even while speaking, you can re-speak or select a new voice
  2590.         EnableCommand(cSpeak);
  2591.         EnableCommand(cSelectVoice);
  2592.         EnableCommand(cSelectVoiceSubMenu);
  2593.         
  2594.         if ( (**((TextDataPtr) pData)->hTE).selEnd > (**((TextDataPtr) pData)->hTE).selStart )    // If there is a selection.
  2595.             ChangeCommandName(cSpeak, kTextStrings, iSpeakSelection);
  2596.         else
  2597.             ChangeCommandName(cSpeak, kTextStrings, iSpeakAll);
  2598.             
  2599.         }
  2600.         
  2601.     // enable the correct controls to go with sound input/output
  2602.     if (((TextDataPtr) pData)->soundHandle)
  2603.         EnableCommand(cPlay);
  2604.     if (pData->originalFileType == 'TEXT')
  2605.         {
  2606.         if (((TextDataPtr) pData)->soundHandle)
  2607.             EnableCommand(cErase);
  2608.         else
  2609.             {
  2610.             if (gMachineInfo.haveRecording)
  2611.                 EnableCommand(cRecord);
  2612.             }
  2613.         }
  2614.     
  2615.     // enable the contents menu, if any         
  2616.     (void) TextAdjustContentsMenu(pData);
  2617.     
  2618.     // enable commands that we support at all times
  2619.     if (GetControlMaximum(pData->vScroll) != 0)
  2620.         {
  2621.         EnableCommand(cNextPage);
  2622.         EnableCommand(cPreviousPage);
  2623.         }
  2624.     
  2625.     return noErr;
  2626.     
  2627. } // TextAdjustMenus
  2628.  
  2629. // --------------------------------------------------------------------------------------------------------------
  2630.  
  2631. static OSErr    TextGetDocumentRect(WindowPtr pWindow, WindowDataPtr pData, 
  2632.             LongRect * documentRectangle, Boolean forGrow)
  2633. {
  2634. #pragma unused (pWindow)
  2635.     
  2636.     Rect    theRect = pData->contentRect;
  2637.     Rect    maxRect;
  2638.     
  2639.     GetRegionBounds( GetGrayRgn(), &maxRect );
  2640.     
  2641.     if ( (!forGrow) && (!(((TextDataPtr) pData)->insideClickLoop) ) )
  2642.         RecalcTE(pData, false);
  2643.     
  2644.     theRect.bottom = CalculateTextEditHeight(((TextDataPtr) pData)->hTE);
  2645.     theRect.bottom += kMargins*2;
  2646.     theRect.right = maxRect.right;
  2647.         
  2648.     if (theRect.bottom < pData->contentRect.bottom)
  2649.         theRect.bottom = pData->contentRect.bottom;
  2650.         
  2651.     if (forGrow)
  2652.         theRect.bottom = maxRect.bottom-kScrollBarSize;
  2653.         
  2654.     RectToLongRect(&theRect, documentRectangle);
  2655.     
  2656.     return noErr;
  2657.     
  2658. } // TextGetDocumentRect
  2659.  
  2660. // --------------------------------------------------------------------------------------------------------------
  2661.  
  2662. static OSErr    TextGetBalloon(WindowPtr pWindow, WindowDataPtr pData, 
  2663.         Point *localMouse, short * returnedBalloonIndex, Rect *returnedRectangle)
  2664. {
  2665. #pragma unused (pWindow, pData, localMouse, returnedRectangle)
  2666.  
  2667.     *returnedBalloonIndex = iHelpTextContent;
  2668.     
  2669.     return noErr;
  2670.     
  2671. } // TextGetBalloon
  2672.  
  2673. // --------------------------------------------------------------------------------------------------------------
  2674.  
  2675. static long TextCalculateIdleTime(WindowPtr pWindow, WindowDataPtr pData)
  2676. {
  2677. #pragma unused (pWindow, pData)
  2678.  
  2679.     if ( (gMachineInfo.amInBackground) || (! (**(((TextDataPtr) pData)->hTE)).active) )
  2680.         return(0x7FFFFFF);
  2681.     else
  2682.         return(GetCaretTime());
  2683.         
  2684. } // TextCalculateIdleTime
  2685.  
  2686. // --------------------------------------------------------------------------------------------------------------
  2687.  
  2688. static OSErr    TextAdjustCursor(WindowPtr pWindow, WindowDataPtr pData, 
  2689.                         Point * localMouse, RgnHandle globalRgn)
  2690. {
  2691. #pragma unused (pWindow)
  2692.  
  2693.     OSErr            anErr = noErr;
  2694.     CursHandle        theCross;
  2695.     RgnHandle        hilightRgn;
  2696.     Cursor            arrow;
  2697.  
  2698.     if (gMachineInfo.haveDragMgr)
  2699.         {
  2700.         RgnHandle globalHilight;
  2701.         
  2702.         hilightRgn = NewRgn();
  2703.         TEGetHiliteRgn(hilightRgn, ((TextDataPtr) pData)->hTE);
  2704.         
  2705.         globalHilight = NewRgn();
  2706.         CopyRgn(hilightRgn, globalHilight);
  2707.         LocalToGlobalRgn(globalHilight);
  2708.         
  2709.         if (PtInRgn(*localMouse, hilightRgn))
  2710.             {
  2711.             // we're already in the hilight rgn, so we don't need mouse-moved events as long
  2712.             // as we stay there
  2713.             CopyRgn(globalHilight, globalRgn);
  2714.             
  2715.             SetCursor(GetQDGlobalsArrow(&arrow));
  2716.             DisposeRgn(hilightRgn);
  2717.             return eActionAlreadyHandled;    
  2718.             }
  2719.         else if (!EmptyRgn(hilightRgn))
  2720.             {
  2721.             // make sure we get mouse-moved events if the mouse moves into the hilight rgn,
  2722.             // so that we can change the cursor
  2723.             DiffRgn(globalRgn, globalHilight, globalRgn);
  2724.             }
  2725.     
  2726.         DisposeRgn(globalHilight);
  2727.         DisposeRgn(hilightRgn);
  2728.         }
  2729.         
  2730.     theCross = MacGetCursor(iBeamCursor);
  2731.     if (theCross)
  2732.         {
  2733.         char    oldState;
  2734.         
  2735.         oldState = HGetState((Handle) theCross);
  2736.         HLock((Handle) theCross);
  2737.         SetCursor(*theCross);
  2738.         HSetState((Handle) theCross, oldState);
  2739.         anErr = eActionAlreadyHandled;
  2740.         }
  2741.         
  2742.     return anErr;
  2743.     
  2744. } // TextAdjustCursor
  2745.  
  2746. // --------------------------------------------------------------------------------------------------------------
  2747.  
  2748. short gNilCaretProc[] = { 
  2749.                         0x584F,         // ADDQ.W    #$4, A7
  2750.                         0x4E75};        // RTS
  2751.  
  2752. // --------------------------------------------------------------------------------------------------------------
  2753.  
  2754. static OSErr    TextPrintPage(WindowPtr pWindow, WindowDataPtr pData,
  2755.                     Rect * pageRect, long *pageNum)
  2756. {
  2757. #pragma unused (pWindow)
  2758.  
  2759.     OSErr        anErr = noErr;
  2760.     short        footerHeight;
  2761.     TEHandle    hTE;
  2762.     Rect        areaForThisPage;
  2763.     short        ourPage = 1;
  2764.     Boolean        documentHasFormControl = Count1Resources(kFormResource) != 0;
  2765.     
  2766.     // calculate area for the footer (page number)
  2767.     {
  2768.     FontInfo    theInfo;
  2769.     
  2770.     TextFont(0);
  2771.     TextSize(0);
  2772.     TextFace(normal);
  2773.     GetFontInfo(&theInfo);
  2774.     footerHeight = (theInfo.ascent + theInfo.descent + theInfo.leading) << 1;
  2775.     }
  2776.     
  2777.     // duplicate the text edit record, disable the selection before swapping the new port in
  2778.     hTE = ((TextDataPtr) pData)->hTE;
  2779.     TEDeactivate(hTE);
  2780.     
  2781.     anErr = HandToHand((Handle*) &hTE);
  2782.     nrequire(anErr, DuplicateTE);
  2783.  
  2784.     // turn off outline hilighting -- because the window is disabled while
  2785.     // printing is going on, but we don't want that disabled hilight to draw
  2786.     TEFeatureFlag(teFOutlineHilite, teBitClear, ((TextDataPtr) pData)->hTE);
  2787.         
  2788.     // now HERE'S a real hack!  Under certain conditions, Text Edit will draw the
  2789.     // cursor, even if you said the edit record is inactive!  This happens when
  2790.     // the internal state sez that the cursor hasn't been drawn yet.  Lucky
  2791.     // for us, the caret is drawn through a hook, which we replace with a NOP.
  2792.     (**hTE).caretHook = (CaretHookUPP) gNilCaretProc;
  2793.     
  2794.     // point the rectangles to be the page rect minus the footer
  2795.     areaForThisPage = *pageRect;
  2796.     areaForThisPage.bottom -= footerHeight;
  2797.     
  2798.     InsetRect(&areaForThisPage, kPrintMargins, kPrintMargins);
  2799.     (**hTE).viewRect = (**hTE).destRect = areaForThisPage;
  2800.  
  2801.     // recalculate the line breaks
  2802.     TECalText(hTE);
  2803.  
  2804.     // point it at the printing port.
  2805.     (**hTE).inPort = GetQDGlobalsThePort();
  2806.  
  2807.     // now loop over all pages doing page breaking until we find our current
  2808.     // page, which we print, and then return.
  2809.     {
  2810.     Rect    oldPageHeight = (**hTE).viewRect;
  2811.     short    currentLine = 0;
  2812.     long    prevPageHeight = 0;
  2813.  
  2814.     while (ourPage <= *pageNum)
  2815.         {
  2816.         long    currentPageHeight = 0;
  2817.                     
  2818.         // calculate the height including the current page, breaks
  2819.         // when one of three things happen:
  2820.         // 1) adding another line to this page would go beyond the length of the page
  2821.         // 2) a picture needs to be broken to the next page (NOT YET IMPLEMENTED)
  2822.         // 3) we run out of lines for the document 
  2823.         // 4) if the line has a page break (defined as a non breaking space w/o a PICT)
  2824.  
  2825.         // POTENTIAL BUG CASES:
  2826.         // If a single line > the page height.  Can that happen?  If so, we need to 
  2827.         // add something to handle it.
  2828.         do
  2829.             {
  2830.             long currentLineHeight;
  2831.             
  2832.             // zero based count -- but one based calls to TEGetHeight 
  2833.             currentLineHeight = TEGetHeight(currentLine+1, currentLine+1, hTE);
  2834.                 
  2835.             // if adding this line would just be too much, break out of here
  2836.             if ((currentLineHeight + currentPageHeight) > (areaForThisPage.bottom - areaForThisPage.top))
  2837.                 break;
  2838.                 
  2839.             ++ currentLine;
  2840.             currentPageHeight += currentLineHeight;
  2841.             
  2842.             // if this line had a page break on it, break out of pagination
  2843.             if (documentHasFormControl && LineHasPageBreak(currentLine-1, hTE))
  2844.                 break;
  2845.                 
  2846.             } while (currentLine < (**hTE).nLines);
  2847.         
  2848.         // if this the page we are trying to print
  2849.         if (ourPage == *pageNum)
  2850.             {
  2851.             Str255        pageString;
  2852.             RgnHandle    oldRgn = NewRgn();
  2853.             
  2854.             // move onto the next page via offset by the previous pages -- but
  2855.             // clip to the current page height because we wouldn't want to see
  2856.             // half of a line from the next page at the bottom of a page.
  2857.             OffsetRect(&oldPageHeight, 0, -(prevPageHeight));
  2858.             oldPageHeight.bottom = oldPageHeight.top + currentPageHeight;
  2859.             (**hTE).destRect = oldPageHeight;
  2860.             
  2861.             // clip to this area as well
  2862.             areaForThisPage.bottom = areaForThisPage.top + currentPageHeight;
  2863.             GetClip(oldRgn);
  2864.             ClipRect(&areaForThisPage);
  2865.             
  2866.             // draw the edit record, plus our cool pictures    
  2867.             TEUpdate(&areaForThisPage, hTE); 
  2868.             DrawPictures(pData, hTE);
  2869.             
  2870.             // restore the clip
  2871.             SetClip(oldRgn);
  2872.             DisposeRgn(oldRgn);
  2873.             
  2874.             // Draw the page string at the bottom of the page, centered
  2875.             pageString[0] = 2;
  2876.             pageString[1] = '-';
  2877.             NumToString(*pageNum, &pageString[2]);
  2878.             pageString[0] += pageString[2];
  2879.             pageString[2] = ' ';
  2880.             pageString[++pageString[0]] = ' ';
  2881.             pageString[++pageString[0]] = '-';
  2882.             
  2883.             MoveTo(
  2884.                 pageRect->left + 
  2885.                     ((pageRect->right - pageRect->left) >> 1) - 
  2886.                     (StringWidth(pageString)>>1),
  2887.                 pageRect->bottom - kPrintMargins);
  2888.                 
  2889.             DrawString(pageString);
  2890.             
  2891.             // if we have completed all pages
  2892.             if (currentLine >= (**hTE).nLines)
  2893.                 {
  2894.                 // tell it to stop printing
  2895.                 *pageNum = -1;
  2896.                 }
  2897.                 
  2898.             // get out of here!
  2899.             break;
  2900.             }
  2901.     
  2902.         // move onto the next page via count
  2903.         ++ourPage;
  2904.         
  2905.         // and the list of pages before now includes this page we just finished
  2906.         prevPageHeight += currentPageHeight;
  2907.         
  2908.         }
  2909.     
  2910.     }
  2911.     
  2912.     
  2913.     // restore text for visible page if done
  2914.     if (*pageNum == -1)
  2915.         {
  2916.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  2917.         TECalText(((TextDataPtr) pData)->hTE);
  2918.         if (pData->originalFileType != 'ttro')
  2919.             TEActivate(((TextDataPtr) pData)->hTE);
  2920.         }
  2921.         
  2922. // FALL THROUGH EXCEPTION HANDLING
  2923.  
  2924.     // Dispose this way to avoid disposing of any owned objects
  2925.     DisposeHandle((Handle) hTE);
  2926. DuplicateTE:
  2927.  
  2928.     return anErr;
  2929.     
  2930. } // TextPrintPage
  2931.  
  2932. // --------------------------------------------------------------------------------------------------------------
  2933.  
  2934. static OSErr    TextInitData(WindowDataPtr pData)
  2935. {
  2936.     static TEClickLoopUPP gMyClickLoop = NULL;
  2937.     OSErr                anErr = noErr;
  2938.  
  2939.     if (!gMyClickLoop) {
  2940.         gMyClickLoop = NewTEClickLoopProc(CClikLoop);
  2941.     }
  2942.  
  2943.     pData->bumpUntitledCount    = true;
  2944.     
  2945.     pData->pScrollContent         = (ScrollContentProc)    TextScrollContent;
  2946.     pData->pAdjustSize             = (AdjustSizeProc)        TextAdjustSize;
  2947.     pData->pGetDocumentRect     = (GetDocumentRectProc)    TextGetDocumentRect;
  2948.     pData->pPreMenuAccess        = (PreMenuAccessProc)    TextPreMenuAccess;
  2949.     pData->pAdjustMenus             = (AdjustMenusProc)        TextAdjustMenus;
  2950.     pData->pCommand                 = (CommandProc)            TextCommand;
  2951.  
  2952.     pData->pCloseWindow         = (CloseWindowProc)        TextCloseWindow;
  2953.     pData->pFilterEvent         = (FilterEventProc)        TextFilterEvent;
  2954.     pData->pActivateEvent         = (ActivateEventProc)    TextActivateEvent;
  2955.     pData->pUpdateWindow         = (UpdateWindowProc)    TextUpdateWindow;
  2956.     pData->pPrintPage             = (PrintPageProc)        TextPrintPage;
  2957.  
  2958.     // we only support keydowns and editing for modifable docs
  2959.     if (pData->originalFileType != 'ttro')
  2960.         {
  2961.         pData->pKeyEvent             = (KeyEventProc)            TextKeyEvent;
  2962.         pData->pContentClick        = (ContentClickProc)        TextContentClick;
  2963.         pData->pAdjustCursor        = (AdjustCursorProc)        TextAdjustCursor;
  2964.         pData->pGetBalloon            = (GetBalloonProc)            TextGetBalloon;
  2965.         pData->pCalculateIdleTime    = (CalculateIdleTimeProc)    TextCalculateIdleTime;
  2966.         
  2967.         // We can always reference our Drag handlers, because they will not be called if we
  2968.         // don't have the Drag Manager available. We needn't check here (it would be redundant).
  2969.         pData->pDragTracking        = (DragTrackingProc)    TextDragTracking;
  2970.         pData->pDragReceive            = (DragReceiveProc)        TextDragReceive;
  2971.  
  2972.         pData->documentAcceptsText    = true;
  2973.         }
  2974.         
  2975.     // leave room for the grow area at bottom
  2976.     pData->hasGrow                = true;
  2977.     pData->contentRect.bottom    -= kScrollBarSize;    
  2978.     if ((pData->contentRect.right - pData->contentRect.left) > kOnePageWidth)
  2979.         pData->contentRect.right = pData->contentRect.left + kOnePageWidth;
  2980.         
  2981.     ((TextDataPtr) pData)->hTE    = TEStyleNew(&pData->contentRect, &pData->contentRect);
  2982.     anErr = MemError();
  2983.     nrequire(anErr, TENewFailed);
  2984.         
  2985.     pData->hScrollAmount        = 0;
  2986.     pData->vScrollAmount        = TEGetHeight(0, 0, ((TextDataPtr) pData)->hTE);
  2987.  
  2988.     TEAutoView(true, ((TextDataPtr) pData)->hTE);
  2989.  
  2990.     // Setup our click loop to handle autoscrolling
  2991.     ((TextDataPtr) pData)->docClick = (**(((TextDataPtr) pData)->hTE)).clickLoop;
  2992.     TESetClickLoop(gMyClickLoop, ((TextDataPtr) pData)->hTE);
  2993.     
  2994.     // initalize undo information
  2995.     ((TextDataPtr) pData)->prevCommandID = cNull;
  2996.  
  2997.     return noErr;
  2998.     
  2999. TENewFailed:
  3000.  
  3001.         DebugStr("\pTextFile: TEStyleNew returned an error." );
  3002.  
  3003.     return anErr;
  3004.     
  3005. } // TextInitData
  3006.  
  3007. // --------------------------------------------------------------------------------------------------------------
  3008.  
  3009. static OSErr    TextReadDataFork(WindowDataPtr pData)
  3010. {    
  3011.     OSErr    anErr = noErr;
  3012.     
  3013.     // if we have a data fork, read the contents into the record    
  3014.     if (pData->dataRefNum != -1)
  3015.         {
  3016.         long    dataSize;
  3017.         
  3018.         GetEOF(pData->dataRefNum, &dataSize);
  3019.         if (dataSize > kMaxLength)
  3020.             anErr = eDocumentTooLarge;
  3021.         else
  3022.             {
  3023.             Handle    tempHandle = NewHandle(dataSize);
  3024.             anErr = MemError();
  3025.             if (anErr == noErr)
  3026.                 {
  3027.                 int i;
  3028.  
  3029.                 // read the text in
  3030.                 SetFPos(pData->dataRefNum, fsFromStart, 0);
  3031.                 anErr = FSRead(pData->dataRefNum, &dataSize, * tempHandle);                
  3032.                 for (i=0; i < dataSize; i++) {
  3033.                     if ((*tempHandle)[i] == '\n')
  3034.                         (*tempHandle)[i] = '\r';
  3035.                 }
  3036.  
  3037.                 // then insert it.
  3038.                 if (anErr == noErr)
  3039.                     {
  3040.                     HLock(tempHandle);
  3041.                     TEStyleInsert(*tempHandle, dataSize, nil, ((TextDataPtr) pData)->hTE);
  3042.                     anErr = MemError();
  3043.                     }
  3044.                 DisposeHandle(tempHandle);
  3045.                 }
  3046.             
  3047.             }
  3048.         
  3049.         }
  3050.     nrequire(anErr, ReadData);
  3051.     
  3052.     return noErr;
  3053.     
  3054. ReadData:
  3055.  
  3056.     return anErr;
  3057.     
  3058. } // TextReadDataFork
  3059.  
  3060. // --------------------------------------------------------------------------------------------------------------
  3061.  
  3062. static void        TextReadResourceFork(WindowDataPtr pData)
  3063. {    
  3064.     // if we have a resource fork, read the contents
  3065.     if (pData->resRefNum != -1)
  3066.         {
  3067.         short    oldResFile = CurResFile();
  3068.         Handle    theStyle;
  3069.         
  3070.         // read the style information
  3071.         UseResFile(pData->resRefNum);
  3072.         theStyle = Get1Resource('styl', 128);
  3073.         if (theStyle)
  3074.             {
  3075.             HNoPurge(theStyle);
  3076.             TEUseStyleScrap(0, 32767, (StScrpHandle) theStyle, true, ((TextDataPtr) pData)->hTE);
  3077.             ReleaseResource(theStyle);
  3078.             }
  3079.  
  3080.         // if we have sound, load it in and detach it
  3081.         {
  3082.         Handle    soundHandle = Get1Resource('snd ', kSoundBase);
  3083.         if (soundHandle)
  3084.             {
  3085.             HNoPurge(soundHandle);
  3086.             DetachResource(soundHandle);
  3087.             ((TextDataPtr) pData)->soundHandle = soundHandle;
  3088.             }
  3089.         }
  3090.  
  3091.         UseResFile(oldResFile);
  3092.         }
  3093.             
  3094. } // TextReadResourceFork
  3095.  
  3096. // --------------------------------------------------------------------------------------------------------------
  3097.  
  3098. static void        TextFinishTESetup(WindowDataPtr pData)
  3099. {            
  3100.     // hook out drawing of the non-breaking space for read only documents,
  3101.     // for modifiable documents, enable outline hiliting (ie, when TE window
  3102.     // isn't in front, show the gray outline)
  3103.     if (pData->originalFileType == 'ttro')
  3104.         {
  3105.         static DrawHookUPP gMyDrawHook = NULL;
  3106.         UniversalProcPtr    hookRoutine;
  3107.  
  3108.         if (!gMyDrawHook) {
  3109.             gMyDrawHook = NewDrawHookProc((DrawHookProcPtr)MyDrawHook);
  3110.         }
  3111.  
  3112.         hookRoutine = (UniversalProcPtr)gMyDrawHook;
  3113.         
  3114.         TECustomHook(intDrawHook, &hookRoutine, ((TextDataPtr) pData)->hTE);
  3115.         }
  3116.     else
  3117.         {
  3118.         TEFeatureFlag(teFOutlineHilite, teBitSet, ((TextDataPtr) pData)->hTE);
  3119.         }
  3120.  
  3121.     // make a TSM document if this is editable
  3122.     if     (
  3123.         (pData->originalFileType != 'ttro') &&
  3124.         (gMachineInfo.haveTSMTE)
  3125.         )
  3126.         {
  3127.         OSType    supportedInterfaces[1];
  3128.  
  3129.         supportedInterfaces[0] = kTSMTEInterfaceType;
  3130.         
  3131.         if (NewTSMDocument(1, supportedInterfaces, 
  3132.             &pData->docTSMDoc, (long)&pData->docTSMRecHandle) == noErr)
  3133.             {
  3134.             static TSMTEPreUpdateUPP gTSMPreUpdateProc = NULL;
  3135.             static TSMTEPostUpdateUPP gTSMPostUpdateProc = NULL;
  3136.             long response;
  3137.  
  3138.             if (!gTSMPreUpdateProc) {
  3139.                 gTSMPreUpdateProc = NewTSMTEPreUpdateProc(TSMPreUpdateProc);
  3140.             }
  3141.             if (!gTSMPostUpdateProc) {
  3142.                 gTSMPostUpdateProc = NewTSMTEPostUpdateProc(TSMPostUpdateProc);
  3143.             }
  3144.  
  3145.             (**(pData->docTSMRecHandle)).textH                 = ((TextDataPtr) pData)->hTE;
  3146.             if ((Gestalt(gestaltTSMTEVersion, &response) == noErr) && (response == gestaltTSMTE1))
  3147.                 (**(pData->docTSMRecHandle)).preUpdateProc     = gTSMPreUpdateProc;
  3148.             (**(pData->docTSMRecHandle)).postUpdateProc     = gTSMPostUpdateProc;
  3149.             (**(pData->docTSMRecHandle)).updateFlag         = kTSMTEAutoScroll;
  3150.             (**(pData->docTSMRecHandle)).refCon             = (long)pData;
  3151.             }
  3152.         }
  3153.  
  3154.     // now we have added text, so adjust views and such as needed
  3155.     TESetSelect(0, 0, ((TextDataPtr) pData)->hTE);
  3156.     RecalcTE(pData, true);
  3157.     AdjustTE(pData, true);
  3158.  
  3159.     // ???? Hack to get around a 7.0 TextEdit bug.  If you are pasting a multiple
  3160.     // line clipboard into TE, *and* the TextEdit record is new, *and* the selection
  3161.     // is at the begining of the doc (0,0 as above), *and* you haven't moved the
  3162.     // cursor around at all, then TE pastes thinking it's at the end of the line,
  3163.     // when it really should be at the begining.  Then if you <cr> with the cursor
  3164.     // visible, it'll leave a copy behind.  
  3165.     
  3166.     // I'm not happy with this, but I don't know another way around the problem.
  3167.     if (pData->originalFileType != 'ttro') 
  3168.         {
  3169.         TEKey(0x1F, ((TextDataPtr) pData)->hTE);
  3170.         TEKey(0x1E, ((TextDataPtr) pData)->hTE);
  3171.         }
  3172.     
  3173.     // <39> if this is a new document, convert the "system size", "system font", and
  3174.     // "application font" into real font IDs and sizes.  This is so that
  3175.     // if someone saves this document and opens it with another script
  3176.     // system, they don't get all huffy that the font changed on them.
  3177.     // It also solves problems with cut and paste to applications too stupid
  3178.     // to know that "zero" means system size.
  3179.     if (pData->dataRefNum == -1)
  3180.         {
  3181.         TEHandle    hTE = ((TextDataPtr) pData)->hTE;
  3182.         short        mode = doAll;
  3183.         TextStyle    theStyle;
  3184.     
  3185.         TEContinuousStyle(&mode, &theStyle, hTE);
  3186.         if (theStyle.tsSize == 0)
  3187.             theStyle.tsSize = GetDefFontSize();
  3188.         if (theStyle.tsFont == systemFont)
  3189.             theStyle.tsFont = GetSysFont();
  3190.         if (theStyle.tsFont == applFont)
  3191.             theStyle.tsFont = GetAppFont();
  3192.             
  3193.         mode = doAll;
  3194.         TESetStyle(mode, &theStyle, false, hTE);
  3195.         }
  3196.  
  3197.     // Carbon TextEdit really sucks right now.
  3198.     // to work around some of the font sizing
  3199.     // problems let's try to puppet string the
  3200.     // right result.  sorry...
  3201.  
  3202.     //DebugStr("\pSetting size to 18 point." );
  3203.  
  3204.     TESetSelect(0,32000,((TextDataPtr) pData)->hTE);
  3205.     {
  3206.         TEHandle        hTE = ((TextDataPtr) pData)->hTE;
  3207.                 short           mode = doAll;
  3208.         short        saveSize;
  3209.                 TextStyle       theStyle;
  3210.  
  3211.                 TEContinuousStyle(&mode, &theStyle, hTE);
  3212.                 if (theStyle.tsSize == 0)
  3213.                         theStyle.tsSize = GetDefFontSize();
  3214.                 if (theStyle.tsFont == systemFont)
  3215.                         theStyle.tsFont = GetSysFont();
  3216.                 if (theStyle.tsFont == applFont)
  3217.                         theStyle.tsFont = GetAppFont();
  3218.  
  3219.         saveSize = theStyle.tsSize;
  3220.         theStyle.tsSize = 18;
  3221.                 TESetStyle(doSize, &theStyle, true, hTE);
  3222.         TECalText(((TextDataPtr) pData)->hTE);
  3223.                AdjustTE(pData, false);
  3224.  
  3225.         //theStyle.tsSize = saveSize;
  3226.                 //TESetStyle(doSize, &theStyle, true, hTE);
  3227.         //TECalText(((TextDataPtr) pData)->hTE);
  3228.                //AdjustTE(pData, false);
  3229.     }
  3230.     TESetSelect(0,0,((TextDataPtr) pData)->hTE);
  3231.  
  3232.     //DebugStr( "\pDone setting size.\n" ); 
  3233.  
  3234. } // TextFinishTESetup
  3235.  
  3236. // --------------------------------------------------------------------------------------------------------------
  3237.  
  3238. static void        TextCloseStationery(WindowDataPtr pData)
  3239. {
  3240.     // if stationery, use untitled and close down the files
  3241.     if (pData->originalFileType == 'sEXT')
  3242.         {
  3243.         pData->originalFileType = 'TEXT';
  3244.         pData->openAsNew = true;
  3245.         if (pData->resRefNum != -1)
  3246.             CloseResFile(pData->resRefNum);
  3247.         if (pData->dataRefNum != -1)
  3248.             FSClose(pData->dataRefNum);
  3249.         pData->resRefNum = pData->dataRefNum = -1;
  3250.         }
  3251.         
  3252. } // TextCloseStationery
  3253.  
  3254. // --------------------------------------------------------------------------------------------------------------
  3255. #if 0
  3256. static int        FindVoicePosition(MenuHandle voicesMenu, short menuCount, VoiceDescription* pdesc)
  3257. {
  3258.     short    item;
  3259.     
  3260.     for ( item = 1; item <= menuCount; ++item )
  3261.         {
  3262.         Str255    itemText;
  3263.         
  3264.         GetMenuItemText( voicesMenu, item, itemText );
  3265.         /*1st > 2nd*/
  3266.         if ( IUCompString( itemText, pdesc->name ) == 1 )
  3267.             break;                        // Found where name goes in list.
  3268.         }
  3269.         
  3270.     return item;
  3271.     
  3272. } // FindVoicePosition
  3273.  
  3274. // --------------------------------------------------------------------------------------------------------------
  3275.  
  3276. static void        TextAdd1Voice(short i, MenuHandle voicesMenu)
  3277. {
  3278.     VoiceSpec            spec;                // A voice to add to the menu.
  3279.     VoiceDescription    description;        // Info about a voice.
  3280.     short                item;
  3281.  
  3282.     if ( (GetIndVoice( i, &spec ) == noErr)  &&
  3283.          (GetVoiceDescription( &spec, &description, sizeof(description) ) == noErr ) )
  3284.         {
  3285.         short    menuCount = CountMItems( voicesMenu );
  3286.         
  3287.         // first one we are adding == get rid of item already there
  3288.         if ( (i == 1)  && (menuCount > 0) )
  3289.             {
  3290.             DeleteMenuItem( voicesMenu, 1 );
  3291.             --menuCount;
  3292.             }
  3293.             
  3294.         item = FindVoicePosition(voicesMenu, menuCount, &description);
  3295.  
  3296.         InsertMenuItem( voicesMenu, "\p ", item - 1 );
  3297.         SetMenuItemText( voicesMenu, item, description.name );
  3298.         
  3299.         CheckItem(voicesMenu, item, 
  3300.             ((gCurrentVoice.creator == spec.creator) && (gCurrentVoice.id == spec.id)) );
  3301.         }
  3302.         
  3303. } // TextAdd1Voice
  3304.  
  3305. // --------------------------------------------------------------------------------------------------------------
  3306.  
  3307. static pascal void*    VoicesThread(void *threadParam)
  3308. {
  3309. #pragma unused(threadParam)
  3310.  
  3311.     short    theVoiceCount;
  3312.     
  3313.     if (CountVoices( &theVoiceCount ) == noErr)
  3314.         {
  3315.         OSErr                anErr;
  3316.         VoiceDescription    description;        // Info about a voice.
  3317.         MenuHandle            voicesMenu = GetMenuHandle(mVoices);
  3318.  
  3319.         if (!gDontYield)
  3320.             YieldToAnyThread();
  3321.         
  3322.         anErr = GetVoiceDescription( nil, &description, sizeof(description) );
  3323.         if (anErr == noErr)
  3324.             {
  3325.             int        i;
  3326.             
  3327.             if (!gDontYield)
  3328.                 YieldToAnyThread();
  3329.             
  3330.             gCurrentVoice = description.voice;
  3331.             
  3332.             for (i = 1; i <= theVoiceCount; ++i)
  3333.                 {
  3334.                 TextAdd1Voice(i, voicesMenu);
  3335.                 
  3336.                 if (!gDontYield)
  3337.                     YieldToAnyThread();
  3338.                 }
  3339.             }
  3340.         }
  3341.     
  3342.     gVoicesThread = kNoThreadID;
  3343.     return 0;
  3344.     
  3345. } // VoicesThread
  3346.  
  3347. // --------------------------------------------------------------------------------------------------------------
  3348.  
  3349. static void        TextAddVoices(void)
  3350. {    
  3351.     // AEC, added correct callback proc creation
  3352.     ThreadEntryUPP voicesThreadUPP = NewThreadEntryProc(VoicesThread);
  3353.  
  3354.     // if we have voices, add them to the menu
  3355.     if ( (gMachineInfo.haveTTS) && (!gAddedVoices) )
  3356.         {
  3357.             OSErr    anErr = paramErr;
  3358.             
  3359.             if (gMachineInfo.haveThreads)
  3360.                 {
  3361.                 // AEC, added cast
  3362.                 anErr = NewThread(kCooperativeThread, voicesThreadUPP, NULL, 0, kNewSuspend,
  3363.                     &gThreadResults, &gVoicesThread);
  3364.                 }
  3365.  
  3366.             if (anErr != noErr)
  3367.                 VoicesThread(NULL);
  3368.                 
  3369.             gAddedVoices = true;
  3370.             
  3371.         } // end of adding voices
  3372.         
  3373. } // TextAddVoices
  3374. #endif
  3375.  
  3376. // --------------------------------------------------------------------------------------------------------------
  3377.  
  3378. static OSErr    TextMakeWindow(WindowPtr pWindow, WindowDataPtr pData)
  3379. {
  3380. //#pragma unused(pWindow)
  3381.  
  3382.     OSErr    anErr;
  3383.     
  3384.     anErr = TextInitData(pData);
  3385.     nrequire(anErr, InitData);
  3386.     
  3387.     anErr = TextReadDataFork(pData);
  3388.     nrequire(anErr, ReadData);
  3389.     
  3390.     TextReadResourceFork(pData);
  3391.     TextFinishTESetup(pData);
  3392.     TextCloseStationery(pData);
  3393.     
  3394.     if(gMachineInfo.haveProxyIcons)
  3395.     {
  3396.         // Set the view size of the scrollbar to the textedit view size (for proportional scrolling)
  3397.         SetControlViewSize( pData->vScroll, pData->contentRect.bottom - pData->contentRect.top );
  3398.  
  3399.         // make the document happy
  3400.         SetWindowProxyCreatorAndType( pWindow, 'ttxt', 'TEXT', kOnSystemDisk );
  3401.     }
  3402. #if 0
  3403.     TextAddVoices();
  3404. #endif
  3405.     
  3406.     return noErr;
  3407.     
  3408. // EXCEPTION HANDLING
  3409. ReadData:
  3410.     TEDispose(((TextDataPtr) pData)->hTE);
  3411. InitData:
  3412.     return anErr;
  3413.     
  3414. } // TextMakeWindow
  3415.  
  3416. // --------------------------------------------------------------------------------------------------------------
  3417.  
  3418. OSErr    TextPreflightWindow(PreflightPtr pPreflightData)
  3419. {    
  3420.     pPreflightData->continueWithOpen     = true;
  3421.     pPreflightData->wantVScroll            = true;
  3422.     pPreflightData->doZoom                = true;
  3423.     pPreflightData->makeProcPtr         = TextMakeWindow;
  3424.     if (pPreflightData->fileType != 'ttro')
  3425.         pPreflightData->openKind            = fsRdWrPerm;
  3426.     
  3427.     pPreflightData->storageSize         = sizeof(TextDataRecord);
  3428.  
  3429.     // get strings that mark the picture
  3430.     GetIndString(gPictMarker1, kTextStrings, iPictureMarker1);
  3431.     GetIndString(gPictMarker2, kTextStrings, iPictureMarker2);
  3432.  
  3433.     // do we need to account for bugs in older TEs?
  3434.     {
  3435.     long    version;
  3436.     
  3437.     if ( (Gestalt(gestaltTextEditVersion, &version) == noErr) && (version > gestaltTE5) )
  3438.         gTE6Version = true;
  3439.     }
  3440.     
  3441.     return noErr;
  3442.     
  3443. } // TextPreflightWindow
  3444.  
  3445. // --------------------------------------------------------------------------------------------------------------
  3446.  
  3447. void TextGetFileTypes(OSType * pFileTypes, OSType * pDocumentTypes, short * numTypes)
  3448. {
  3449.     pFileTypes[*numTypes]         = 'TEXT';
  3450.     pDocumentTypes[*numTypes]     = kTextWindow;
  3451.     (*numTypes)++;
  3452.         
  3453.     pFileTypes[*numTypes]         = 'ttro';
  3454.     pDocumentTypes[*numTypes]     = kTextWindow;
  3455.     (*numTypes)++;
  3456.         
  3457.     pFileTypes[*numTypes]         = 'sEXT';
  3458.     pDocumentTypes[*numTypes]     = kTextWindow;
  3459.     (*numTypes)++;
  3460.         
  3461. } // TextGetFileTypes
  3462.  
  3463. // --------------------------------------------------------------------------------------------------------------
  3464.  
  3465.  
  3466.  
  3467. // TextAddContentsMenu checks if there is a contents list and, if there
  3468. // is, creates a new menu handle for the contents list and fills it with 
  3469. // the appropriate visible items
  3470.  
  3471. void TextAddContentsMenu(WindowDataPtr pData)
  3472. {
  3473.     MenuHandle    contentsMenu;
  3474.     Str255        menuStr;
  3475.     short        totalItems;
  3476.     short        index;
  3477.     OSErr        err;
  3478.     
  3479.     contentsMenu = GetMenuHandle(mContents);
  3480.     require(contentsMenu == nil, ContentsMenuAlreadyInstalled);
  3481.     
  3482.     // Is there a contents list?  If so, get the menu name 
  3483.     // and the number of items in the list
  3484.     
  3485.     if (TextGetContentsListItem(pData, 0, menuStr, nil, &totalItems) == noErr)
  3486.         {
  3487.         
  3488.         // create the menu and fill it with all the items
  3489.         // listed in the string list resource
  3490.         
  3491.         contentsMenu = NewMenu(mContents, menuStr);
  3492.         require(contentsMenu != nil, CantCreateContentsMenu);
  3493.         
  3494.         for (index = 1; index < totalItems; index++)
  3495.             {
  3496.             err = TextGetContentsListItem(pData, index, menuStr, nil, nil);
  3497.             require(err == noErr, CantGetItem);
  3498.             
  3499.             AppendMenu(contentsMenu, menuStr);
  3500.             }
  3501.  
  3502.         // add the menu to the menu bar, and redraw the menu bar
  3503.         InsertMenu(contentsMenu, 0);
  3504.         DrawMenuBar();
  3505.         }
  3506.     else
  3507.         {
  3508.         // no contents, do nothing
  3509.         }
  3510.     
  3511.     return;
  3512.     
  3513. // error handling
  3514. CantGetItem:
  3515. CantCreateContentsMenu:
  3516.     
  3517.     if (contentsMenu)     DisposeMenu(contentsMenu);
  3518.  
  3519. ContentsMenuAlreadyInstalled:
  3520.  
  3521.     return;
  3522.         
  3523. } // TextAddContentsMenu
  3524.  
  3525.  
  3526.  
  3527.  
  3528. // TextRemoveContentsMenu removes the contents menu, if any,
  3529. // and redraws the menu bar
  3530.  
  3531. static void TextRemoveContentsMenu(WindowDataPtr pData)
  3532. {
  3533. #pragma unused (pData)
  3534.  
  3535.     MenuHandle    contentsMenu;
  3536.     
  3537.     contentsMenu = GetMenuHandle(mContents);
  3538.     if (contentsMenu)
  3539.         {
  3540.         DeleteMenu(mContents);
  3541.         DisposeMenu(contentsMenu);
  3542.         DrawMenuBar();
  3543.         }
  3544.  
  3545. } // TextRemoveContentsMenu
  3546.  
  3547.  
  3548.  
  3549. // TextGetContentsListItem is a general utility routine for examining the
  3550. // contents menu list, returning the menu and search strings, and returning
  3551. // the total number of entries in the contents list.
  3552. //
  3553. // Pass 0 as itemNum to retrieve the strings for the contents menu title.
  3554. //
  3555. // Pass nil for menuStr, searchStr, or totalItems if you don't want that
  3556. // info returned.
  3557. //
  3558. // Returns eDocumentHasNoContentsEntries if there is no contents string list
  3559. // resource for the specified window
  3560.  
  3561. static OSErr TextGetContentsListItem(WindowDataPtr pData, short itemNum, 
  3562.                               StringPtr menuStr, StringPtr searchStr, 
  3563.                               short *totalItems)
  3564. {
  3565.  
  3566.     OSErr    err;
  3567.     short    oldResFile;
  3568.     short    menuItemNum;
  3569.     short    searchItemNum;
  3570.     Handle    contentsStrListHandle;
  3571.     
  3572.     // if no original resource file, don't bother
  3573.     if (pData->resRefNum == -1)    
  3574.         {
  3575.         return eDocumentHasNoContentsEntries;
  3576.         }
  3577.         
  3578.     err = noErr;
  3579.     
  3580.     oldResFile = CurResFile();
  3581.     UseResFile(pData->resRefNum);
  3582.     
  3583.     // two entries per item
  3584.     //
  3585.     // first (itemNum zero) is content menu title
  3586.     // (second -- itemNum one, search string for menu title -- is unused)
  3587.     
  3588.     menuItemNum = itemNum * 2 + 1;
  3589.     searchItemNum = menuItemNum + 1;
  3590.     
  3591.     contentsStrListHandle = Get1Resource('STR#', kContentsListID);
  3592.     if (contentsStrListHandle)
  3593.         {
  3594.         if (totalItems)    *totalItems = (*(short *)*contentsStrListHandle) / 2;
  3595.  
  3596.         if (menuStr)    GetIndString(menuStr, kContentsListID, menuItemNum);
  3597.         if (searchStr)    
  3598.             {
  3599.             GetIndString(searchStr, kContentsListID, searchItemNum);
  3600.         
  3601.             if (searchStr[0] == 0)
  3602.                 {
  3603.                 // search string was empty, so use the
  3604.                 // menu string as the search string
  3605.                 
  3606.                 GetIndString(searchStr, kContentsListID, menuItemNum);
  3607.                 }
  3608.             }
  3609.         }
  3610.     else
  3611.         {
  3612.         err = eDocumentHasNoContentsEntries;
  3613.         if (totalItems)    *totalItems = 0;
  3614.         }
  3615.     
  3616.     UseResFile(oldResFile);
  3617.     
  3618.     return err;
  3619. } // TextGetContentsListItem
  3620.  
  3621.  
  3622. // TextAdjustContentsMenu enables the items in the contents menu
  3623. //
  3624. // This routine is essentially a placeholder in case the contents
  3625. // menu really were to be dynamically enabled.
  3626.  
  3627. static OSErr TextAdjustContentsMenu(WindowDataPtr pData)
  3628. {
  3629. #pragma unused (pData)
  3630.     
  3631.     EnableCommand(cSelectContents);
  3632.     return(noErr);
  3633.     
  3634. } // TextAdjustContentsMenu
  3635.  
  3636.