home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Technology Seed / Jan. '98 ATS.toast / NavServices1.0b3 / Navigation Services SDK / Examples / SimpleText / SimpleText ƒ / TextFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-01-12  |  88.5 KB  |  3,214 lines  |  [TEXT/MPS ]

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