home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Text / SimpleText Sample / TextDrag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-17  |  20.7 KB  |  778 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        TextDrag.c
  3.  
  4.     Contains:    Text document dragging support for SimpleText
  5.  
  6.     Version:    SimpleText 1.4 or later
  7.  
  8. ** Copyright 1993-1996 Apple Computer. All rights reserved.
  9. **
  10. **    You may incorporate this sample code into your applications without
  11. **    restriction, though the sample code has been provided "AS IS" and the
  12. **    responsibility for its operation is 100% yours.  However, what you are
  13. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  14. **    after having made changes. If you're going to re-distribute the source,
  15. **    we require that you make it clear in the source that the code was
  16. **    descended from Apple Sample Code, but that you've made changes.
  17.  
  18. */
  19.  
  20.  
  21. #include "MacIncludes.h"
  22.  
  23. #include "TextFile.h"
  24.  
  25. #pragma segment Text
  26.  
  27.  
  28. // --------------------------------------------------------------------------------------------------------------
  29. // FORWARD EXTERNAL DECLARES
  30. // --------------------------------------------------------------------------------------------------------------
  31.  
  32. extern Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType);
  33. extern Boolean IsDropInFinderTrash(AEDesc *dropLocation);
  34. extern OSErr SaveCurrentUndoState(WindowDataPtr pData, short newCommandID);
  35.  
  36. // --------------------------------------------------------------------------------------------------------------
  37. // INTERNAL DEFINES
  38. // --------------------------------------------------------------------------------------------------------------
  39.  
  40. // Dragging private globals
  41.  
  42. extern Boolean    gCanAccept;
  43.  
  44. // --------------------------------------------------------------------------------------------------------------
  45. // GLOBALS USED ONLY BY THESE ROUTINES
  46. // --------------------------------------------------------------------------------------------------------------
  47.  
  48. static Boolean    gCaretShow;
  49. static long        gCaretTime;
  50. static short    gCaretOffset, gLastOffset, gInsertPosition;
  51. static Boolean    gCursorInContent;
  52.  
  53. static unsigned long gAutoScrollTicks;
  54.  
  55. // --------------------------------------------------------------------------------------------------------------
  56. // INTERNAL ROUTINES
  57. // --------------------------------------------------------------------------------------------------------------
  58.  
  59. extern void AdjustTE(WindowDataPtr pData, Boolean doScroll);
  60.  
  61. // --------------------------------------------------------------------------------------------------------------
  62. //
  63. // GetSelectionSize -
  64. //
  65.  
  66. static short GetSelectionSize(TEHandle hTE)
  67. {
  68.  
  69.     return((**(hTE)).selEnd - (**(hTE)).selStart);
  70.  
  71. } // GetSelectionSize
  72.  
  73.  
  74. // --------------------------------------------------------------------------------------------------------------
  75. //
  76. // GetSelectedTextPtr
  77. //
  78.  
  79. static Ptr GetSelectedTextPtr(TEHandle hTE)
  80. {
  81.  
  82.     return((*(**(hTE)).hText) + (**(hTE)).selStart);
  83.  
  84. } // GetSelectedTextPtr
  85.  
  86.  
  87. // --------------------------------------------------------------------------------------------------------------
  88. //
  89. //    TEIsFrontOfLine - Given a text offset and a TextEdit handle, returns true if the given
  90. //                      offset is at the beginning of a line start.
  91. //
  92.  
  93. static short TEIsFrontOfLine(short textOffset, TEHandle hTE)
  94. {
  95.  
  96.     short theLine = 0;
  97.  
  98.     if ((**hTE).teLength == 0)
  99.         return true;
  100.  
  101.     if (textOffset >= (**hTE).teLength)
  102.         return( (*((**hTE).hText))[(**hTE).teLength - 1] == 0x0D );
  103.  
  104.     while ((**hTE).lineStarts[theLine] < textOffset)
  105.         theLine++;
  106.  
  107.     return( (**hTE).lineStarts[theLine] == textOffset );
  108.  
  109. } // TEIsFrontOfLine
  110.  
  111.  
  112. // --------------------------------------------------------------------------------------------------------------
  113. //
  114. //    TEGetLine - Given an offset and a TextEdit handle, returns the line number that contains the offset.
  115. //
  116.  
  117. static short TEGetLine(short textOffset, TEHandle hTE)
  118. {    
  119.  
  120.     short theLine = 0;
  121.  
  122.     if (textOffset > (**hTE).teLength)
  123.         return((**hTE).nLines);
  124.     else
  125.         {
  126.         while ((**hTE).lineStarts[theLine] < textOffset)
  127.             ++theLine;
  128.         }
  129.         
  130.     return theLine;
  131.  
  132. } // TEGetLine
  133.  
  134.  
  135. // --------------------------------------------------------------------------------------------------------------
  136. //
  137. //    DrawCaret - Draws a caret in a TextEdit field at the given offset by inverting the image of the
  138. //                caret onto the screen. DrawCaret expects the port to be set to the port that the
  139. //                TextEdit record is in.
  140. //
  141.  
  142. static void DrawCaret(short textOffset, TEHandle hTE)
  143. {    
  144.  
  145.     Point theLoc;
  146.     short lineHeight, theLine;
  147.  
  148.     //
  149.     //    Get the coordinates and the line of the offset to draw the caret.
  150.     //
  151.  
  152.     theLoc  = TEGetPoint(textOffset, hTE);
  153.     theLine = TEGetLine(textOffset, hTE);
  154.     
  155.     // %%% Most heinously bogus - 21-Dec-93 JM3
  156.     //
  157.     //    For some reason, TextEdit dosen't return the proper coordinates
  158.     //    of the last offset in the field if the last character in the record
  159.     //    is a carriage return. TEGetPoint returns a point that is one line
  160.     //    higher than expected. The following code fixes this problem.
  161.  
  162.     if ((textOffset == (**hTE).teLength) && (*((**hTE).hText))[(**hTE).teLength - 1] == 0x0D)
  163.         theLoc.v += TEGetHeight(theLine, theLine, hTE);
  164.  
  165.     //
  166.     //    Always invert the caret when drawing.
  167.     //
  168.  
  169.     PenMode(patXor);
  170.  
  171.     //
  172.     //    Get the height of the line that the offset points to.
  173.     //
  174.  
  175.     lineHeight = TEGetHeight(theLine, theLine, hTE);
  176.  
  177.     //
  178.     //    Draw the appropriate caret image.
  179.     //
  180.  
  181.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  182.     Line(0, 1 - lineHeight);
  183.  
  184.     PenNormal();
  185.  
  186. } // DrawCaret
  187.  
  188.  
  189. // --------------------------------------------------------------------------------------------------------------
  190. //
  191. //    HitTest - Given a point in global coordinates, HitTest returns an offset into the text if the
  192. //              point is inside the given TERecord. If the point is not in the text, HitTest returns
  193. //              -1.
  194. //
  195.  
  196. static short HitTest(Point theLoc, TEHandle hTE)
  197. {    
  198.  
  199.     WindowRef    theWindow;
  200.     short        textOffset = -1;
  201.  
  202.     if (FindWindow(theLoc, &theWindow) == inContent)
  203.         {
  204.         SetPort((GrafPtr) GetWindowPort(theWindow));
  205.         GlobalToLocal(&theLoc);
  206.  
  207.         if (PtInRect(theLoc, &((** hTE).viewRect)) && PtInRect(theLoc, &((** hTE).viewRect)))
  208.             {
  209.             textOffset = TEGetOffset(theLoc, hTE);
  210.  
  211.             if ((TEIsFrontOfLine(textOffset, hTE)) && (textOffset) &&
  212.                 ((*((** hTE).hText))[textOffset - 1] != 0x0D) &&
  213.                 (TEGetPoint(textOffset - 1, hTE).h < theLoc.h))
  214.                 {
  215.                 --textOffset;
  216.                 }
  217.             }
  218.         }
  219.  
  220.     return textOffset;
  221.  
  222. } // HitTest
  223.  
  224.  
  225. // --------------------------------------------------------------------------------------------------------------
  226. //
  227. // GetCharAtOffset - Given a text offset and a TEHandle, returns the character located at that offset in
  228. //                     the TERecord.
  229. //
  230.  
  231. static char GetCharAtOffset(short offset, TEHandle hTE)
  232. {
  233.  
  234.     if (offset < 0)
  235.         return 0x0D;
  236.  
  237.     return(((char *) *((**hTE).hText))[offset]);
  238.  
  239. } // GetCharAtOffset
  240.  
  241.  
  242. // --------------------------------------------------------------------------------------------------------------
  243. //
  244. // WhiteSpace - Determines if the input character is white space.
  245. //
  246.  
  247. static Boolean WhiteSpace(char theChar)
  248. {
  249.  
  250.     return((theChar == ' ') || (theChar == 0x0D));
  251.  
  252. } // WhiteSpace
  253.  
  254.  
  255. // --------------------------------------------------------------------------------------------------------------
  256. //
  257. // WhiteSpaceAtOffset - Given a text offset into a TERecord, determines if the character at that location is
  258. //                        whitespace.
  259. //
  260.  
  261. static Boolean WhiteSpaceAtOffset(short offset, TEHandle hTE)
  262. {
  263.  
  264.     char theChar;
  265.  
  266.     if ((offset < 0) || (offset > (**hTE).teLength - 1))
  267.         return true;
  268.  
  269.     theChar = ((char *) *((**hTE).hText))[offset];
  270.     return(WhiteSpace(theChar));
  271.  
  272. } // WhiteSpaceAtOffset
  273.  
  274.  
  275. // --------------------------------------------------------------------------------------------------------------
  276. //
  277. // InsertTextAtOffset -
  278. //
  279.  
  280. static short InsertTextAtOffset(short textOffset, char *theBuf, long textSize, StScrpHandle styleHand, TEHandle hTE)
  281. {
  282.     short    charactersAdded = 0;
  283.     
  284.     if (textSize == 0)
  285.         return charactersAdded;
  286.  
  287.     //    If we're inserting at the end of a word and the selection does not begin with
  288.     //    a space, insert a space before the insertion.
  289.  
  290.     if (!WhiteSpaceAtOffset(textOffset - 1, hTE) &&
  291.          WhiteSpaceAtOffset(textOffset, hTE) &&
  292.         !WhiteSpace(theBuf[0]))
  293.         {
  294.  
  295.         TESetSelect(textOffset, textOffset, hTE);
  296.         TEKey(' ', hTE);
  297.         ++textOffset;
  298.         ++charactersAdded;
  299.         }
  300.  
  301.     //    If we're inserting at the beginning of a word and the selection does not end
  302.     //    with a space, insert a space after the insertion.
  303.  
  304.     if ( WhiteSpaceAtOffset(textOffset - 1, hTE) &&
  305.         !WhiteSpaceAtOffset(textOffset, hTE) &&
  306.         !WhiteSpace(theBuf[textSize - 1]))
  307.         {
  308.  
  309.         TESetSelect(textOffset, textOffset, hTE);
  310.         TEKey(' ', hTE);
  311.         ++charactersAdded;
  312.         }
  313.  
  314.     // Before we insert this text, make sure we set the selection range to a single character.
  315.     // This assures that we won't overwrite the text in the previous selection.
  316.     
  317.     TESetSelect(textOffset, textOffset, hTE);
  318.     TEStyleInsert(theBuf, textSize, styleHand, hTE);
  319.  
  320.     return charactersAdded;
  321.     
  322. } // InsertTextAtOffset
  323.  
  324. // --------------------------------------------------------------------------------------------------------------
  325. // OOP INTERFACE ROUTINES
  326. // --------------------------------------------------------------------------------------------------------------
  327.  
  328. OSErr TextDragTracking(WindowRef pWindow, void *pData, DragReference theDragRef, short message)
  329. {
  330. #pragma unused(pWindow)
  331.  
  332.     unsigned long    attributes;
  333.     RgnHandle        hilightRgn;
  334.     Point            localMouseLoc, dragMouseLoc;
  335.     short            textOffset;
  336.     long            theTime = TickCount();
  337.     WindowDataPtr    theData = (WindowDataPtr) pData;
  338.  
  339.     GetDragAttributes(theDragRef, &attributes);
  340.  
  341.     switch(message)
  342.         {
  343.         case kDragTrackingEnterWindow:
  344.  
  345.             gCanAccept = IsOnlyThisFlavor(theDragRef, 'TEXT');
  346.  
  347.             gCaretTime   = theTime;
  348.             gCaretOffset = gLastOffset = -1;
  349.             gCaretShow   = true;
  350.  
  351.             gCursorInContent = false;
  352.             gAutoScrollTicks = 0;
  353.  
  354.             break;        
  355.  
  356.         case kDragTrackingInWindow:
  357.  
  358.             if (gCanAccept)
  359.                 {            
  360.                 GetDragMouse(theDragRef, &dragMouseLoc, 0L);
  361.                 localMouseLoc = dragMouseLoc;
  362.                 GlobalToLocal(&localMouseLoc);
  363.  
  364.                 if (attributes & kDragInsideSenderWindow)
  365.                     {
  366.                     short deltaV = 0;
  367.  
  368.                     if ((localMouseLoc.v < 16) && (localMouseLoc.v > 0))
  369.                         deltaV = theData->vScrollAmount;
  370.  
  371.                     if (localMouseLoc.v > GetWindowPort(pWindow)->portRect.bottom - 16)
  372.                         deltaV = - theData->vScrollAmount;
  373.  
  374.                     if (deltaV == 0)
  375.                         {
  376.                         gAutoScrollTicks = 0;
  377.                         }
  378.                     else
  379.                         {
  380.                         if (gAutoScrollTicks == 0)
  381.                             {
  382.                             gAutoScrollTicks = theTime;
  383.                             }
  384.                         else
  385.                             {
  386.                             if (theTime - gAutoScrollTicks > 10)    // 10 ticks to start is what the H.I. doc says
  387.                                 {
  388.                                 // remove the drag-destination caret if it's showing
  389.                                 if (gCaretOffset != -1)
  390.                                     {
  391.                                     DrawCaret(gCaretOffset, ((TextDataPtr) pData)->hTE);
  392.                                     gCaretOffset = -1;
  393.                                     }
  394.     
  395.                                 SetControlAndClipAmount(theData->vScroll, &deltaV);
  396.                                 if (deltaV != 0)
  397.                                     {
  398.                                     DragPreScroll(theDragRef, 0, deltaV);
  399.                                     DoScrollContent(pWindow, theData, 0, deltaV);
  400.                                     DragPostScroll(theDragRef);
  401.                                     }
  402.                                 
  403.                                 gAutoScrollTicks = theTime - 7;    // let's wait 3 more ticks until next jump
  404.                                 }
  405.                             }
  406.                         }
  407.                     }
  408.  
  409.                 if (attributes & kDragHasLeftSenderWindow)
  410.                     {
  411.                     if (PtInRect(localMouseLoc, &(theData->contentRect)))
  412.                         {
  413.                         if (!gCursorInContent)
  414.                             {
  415.                             hilightRgn = NewRgn();
  416.                             RectRgn(hilightRgn, &theData->contentRect);
  417.                             ShowDragHilite(theDragRef, hilightRgn, true);                    
  418.                             DisposeRgn(hilightRgn);
  419.                             }
  420.                         gCursorInContent = true;
  421.                         }
  422.                     else
  423.                         {
  424.                         if (gCursorInContent)
  425.                             {
  426.                             HideDragHilite(theDragRef);
  427.                             gCursorInContent = false;
  428.                             }
  429.                         }
  430.                     }
  431.                 }
  432.  
  433.             textOffset = HitTest(dragMouseLoc, ((TextDataPtr)pData)->hTE);
  434.  
  435.             //    If this application is the sender, do not allow tracking through
  436.             //    the selection in the window that sourced the drag.
  437.  
  438.             if (attributes & kDragInsideSenderWindow)
  439.                 {
  440.                 if ((textOffset >= (**((TextDataPtr)pData)->hTE).selStart) &&
  441.                     (textOffset <= (**((TextDataPtr)pData)->hTE).selEnd))
  442.                     {
  443.                         textOffset = -1;
  444.                     }
  445.                 }
  446.  
  447.             gInsertPosition = textOffset;
  448.  
  449.             //    Reset the flashing counter if the offset has moved. This makes the
  450.             //    caret blink only after the caret has stopped moving long enough.
  451.  
  452.             if (textOffset != gLastOffset)
  453.                 {
  454.                 gCaretTime = theTime;
  455.                 gCaretShow = true;
  456.                 }
  457.             
  458.             gLastOffset = textOffset;
  459.  
  460.             //    Flash the caret, blinky-blinky-blinky.
  461.  
  462.             if (theTime - gCaretTime > GetCaretTime())
  463.                 {
  464.                 gCaretShow = !gCaretShow;
  465.                 gCaretTime = theTime;
  466.                 }
  467.             
  468.             if (!gCaretShow)
  469.                 textOffset = -1;
  470.  
  471.             //    If the caret offset has changed, move the caret on the screen.
  472.  
  473.             if (textOffset != gCaretOffset)
  474.                 {
  475.                 if (gCaretOffset != -1)
  476.                     DrawCaret(gCaretOffset, ((TextDataPtr)pData)->hTE);
  477.  
  478.                 if (textOffset != -1)
  479.                     DrawCaret(textOffset, ((TextDataPtr)pData)->hTE);
  480.                 }
  481.  
  482.             gCaretOffset = textOffset;
  483.             break;
  484.  
  485.         case kDragTrackingLeaveWindow:
  486.     
  487.             //    If the caret is on the screen, remove it.
  488.  
  489.             if (gCaretOffset != -1)
  490.                 {
  491.                 DrawCaret(gCaretOffset, ((TextDataPtr)pData)->hTE);
  492.                 gCaretOffset = -1;
  493.                 }
  494.  
  495.             // Remove the window hilighting, if any.
  496.     
  497.             if ((gCursorInContent) && (attributes & kDragHasLeftSenderWindow))
  498.                 HideDragHilite(theDragRef);
  499.  
  500.             break;
  501.             
  502.           } // switch (message)
  503.     
  504.     return noErr;
  505.  
  506. } // TextDragTracking
  507.  
  508.  
  509. // --------------------------------------------------------------------------------------------------------------
  510.  
  511. OSErr TextDragReceive(WindowRef pWindow, void *pData, DragReference theDragRef)
  512. {
  513.  
  514.     OSErr            error;
  515.     unsigned short    items, index;
  516.     DragAttributes    attributes;
  517.     ItemReference    theItem;
  518.     Ptr                textData;
  519.     StScrpHandle    styleHand;
  520.     Size            textSize, styleSize, totalTextSize;
  521.     short            mouseDownModifiers, mouseUpModifiers, moveText, selStart, selEnd;
  522.     long            totalTextStart;
  523.     long            additionalChars;
  524.     TEHandle        hTE;
  525.     Boolean            wasActive;
  526.     
  527.     if ((!gCanAccept)  || (gInsertPosition == -1))
  528.         return dragNotAcceptedErr;
  529.  
  530.     hTE = ((TextDataPtr) pData)->hTE;
  531.  
  532.     // We're going to try our best to insert some text, so first save off the beginning of where
  533.     // we'll do it.
  534.  
  535.     totalTextStart = gInsertPosition;
  536.     totalTextSize = 0L;
  537.  
  538.     // draw in this window, and activate the text editing record so that selections
  539.     // happen properly
  540.  
  541.     SetPort((GrafPtr) GetWindowPort(pWindow));
  542.  
  543.     wasActive = (*hTE)->active != 0;    // can't test window == FrontWindow (might not be front app)
  544.     if (!wasActive)
  545.         TEActivate(hTE);
  546.  
  547.     GetDragAttributes(theDragRef, &attributes);
  548.     GetDragModifiers(theDragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  549.  
  550.     moveText = (attributes & kDragInsideSenderWindow) &&
  551.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  552.  
  553.     //    Loop through all of the drag items contained in this drag and snag all of the 'TEXT'.
  554.  
  555.     CountDragItems(theDragRef, &items);
  556.     
  557.     for (index = 1; index <= items; index++)
  558.         {
  559.         //    Get the item's reference number, so we can refer to it.
  560.  
  561.         GetDragItemReferenceNumber(theDragRef, index, &theItem);
  562.  
  563.         //    Try to get the size for a 'TEXT' flavor. If this returns noErr,
  564.         //    then we know that a 'TEXT' flavor exists in the item.
  565.         
  566.         error = GetFlavorDataSize(theDragRef, theItem, 'TEXT', &textSize);
  567.  
  568.         if (error == noErr)
  569.             {        
  570.             // If the current length, plus the drag data would make the document too large, say so.
  571.  
  572.             if (((*hTE)->teLength + textSize) > kMaxLength)
  573.                 return eDocumentTooLarge;
  574.  
  575.             textData = NewPtr(textSize);
  576.     
  577.             // If we couldn't get a chunk of memory for the text, bail.
  578.  
  579.             if(textData == 0L)
  580.                 return memFullErr;
  581.  
  582.             GetFlavorData(theDragRef, theItem, 'TEXT', textData, &textSize, 0);
  583.  
  584.             // Let's see if there is an optional 'styl' flavor.
  585.  
  586.             styleHand = 0L;
  587.             
  588.             error = GetFlavorDataSize(theDragRef, theItem, 'styl', &styleSize);
  589.  
  590.             // If there was no 'styl' data, or it somehow was zero in length, don't
  591.             // attempt to insert it along with the text, 'cause we'd fail miserably.
  592.  
  593.             if ((error == noErr) && (styleSize != 0))
  594.                 {
  595.                 styleHand = (StScrpHandle) NewHandle(styleSize);
  596.             
  597.                 // If we couldn't get a chunk of memory for the styles, also bail.
  598.  
  599.                 if (styleHand == 0L)
  600.                     {    
  601.                     DisposePtr(textData);
  602.                     return memFullErr;
  603.                     }
  604.  
  605.                 HLock((Handle) styleHand);
  606.                 GetFlavorData(theDragRef, theItem, 'styl', *styleHand, &styleSize, 0L);
  607.                 HUnlock((Handle) styleHand);            
  608.                 }
  609.  
  610.             //    If the caret or highlighting is on the screen, remove it/them.
  611.     
  612.             if (gCaretOffset != -1)
  613.                 {
  614.                 DrawCaret(gCaretOffset, hTE);
  615.                 gCaretOffset = -1;
  616.                 }
  617.             
  618.             if (attributes & kDragHasLeftSenderWindow)
  619.                 HideDragHilite(theDragRef);
  620.  
  621.             // save away any changes, so that we can undo them
  622.             SaveCurrentUndoState(pData, cTypingCommand);
  623.             
  624.             // If this window is also the sender, delete the source selection text if the
  625.             // option key is not being held down.
  626.  
  627.             if (moveText)
  628.                 {
  629.                 selStart = (**hTE).selStart;
  630.                 selEnd   = (**hTE).selEnd;
  631.                 
  632.                 if ( WhiteSpaceAtOffset(selStart - 1, hTE) &&
  633.                     !WhiteSpaceAtOffset(selStart,     hTE) &&
  634.                     !WhiteSpaceAtOffset(selEnd - 1,   hTE) &&
  635.                      WhiteSpaceAtOffset(selEnd,       hTE))
  636.                     {
  637.                      
  638.                      if (GetCharAtOffset(selEnd, hTE) == ' ')
  639.                         (**hTE).selEnd++;
  640.                     }
  641.                 
  642.                 if (gInsertPosition > selStart)
  643.                     {
  644.                     selEnd = (**hTE).selEnd;
  645.                     gInsertPosition -= (selEnd - selStart);
  646.                     totalTextStart -= (selEnd - selStart);
  647.                     }
  648.                 
  649.                 TEDelete(hTE);    
  650.                 }
  651.  
  652.             // We can finally insert the text and style data into our record.
  653.  
  654.             additionalChars = InsertTextAtOffset(gInsertPosition, textData, textSize, styleHand, hTE);
  655.             
  656.             // In case we're inserting multiple chunks of text, we need to update the location of where we
  657.             // need to insert the next block.
  658.  
  659.             gInsertPosition += textSize + additionalChars;
  660.             totalTextSize += textSize + additionalChars;
  661.             
  662.             DisposePtr(textData);
  663.             if (styleHand)
  664.                 DisposeHandle((Handle) styleHand);
  665.             }
  666.         }
  667.  
  668.     // Select everything we've just inserted.
  669.  
  670.     TESetSelect(totalTextStart, totalTextStart + totalTextSize, hTE);
  671.  
  672.     AdjustTE(pData, false);
  673.     AdjustScrollBars(pWindow, false, false, nil);
  674.     ((WindowDataPtr) pData)->changed = true;
  675.  
  676.     // if we had to activate the edit record, deactivate it after we are all done
  677.     if (!wasActive)
  678.         TEDeactivate(hTE);
  679.  
  680.     return noErr;
  681.  
  682. } // TextDragReceive
  683.  
  684.  
  685. // --------------------------------------------------------------------------------------------------------------
  686.  
  687. Boolean DragText(WindowRef pWindow, void *pData, EventRecord *pEvent, RgnHandle hilightRgn)
  688. {
  689.  
  690.     Point            theLoc = {0,0};
  691.     RgnHandle        dragRegion, tempRegion;
  692.     DragReference    theDragRef;
  693.     StScrpHandle    theStyleHand = 0L;
  694.     OSErr            error;
  695.     AEDesc            dropLocation;
  696.     DragAttributes    attributes;
  697.     short            mouseDownModifiers, mouseUpModifiers, copyText;
  698.  
  699.     //    Copy the hilight region into dragRegion and offset it into global coordinates.
  700.  
  701.     CopyRgn(hilightRgn, dragRegion = NewRgn());
  702.     LocalToGlobal(&theLoc);
  703.     OffsetRgn(dragRegion, theLoc.h, theLoc.v);
  704.  
  705.     //    Wait for the mouse to move or the mouse button to be released. If the mouse button was
  706.     //    released before the mouse moves, return false. Returing false from DragText means that
  707.     //    a drag operation did not occur.
  708.  
  709.     if (!WaitMouseMoved(pEvent->where))
  710.         return false;
  711.  
  712.     NewDrag(&theDragRef);
  713.  
  714.     AddDragItemFlavor(theDragRef, 1, 'TEXT', GetSelectedTextPtr(((TextDataPtr)pData)->hTE), GetSelectionSize(((TextDataPtr)pData)->hTE), 0);
  715.  
  716.     theStyleHand = TEGetStyleScrapHandle(((TextDataPtr)pData)->hTE);
  717.  
  718.     // Just be a little paranoid and see if we did get a handle.
  719.  
  720.     if (theStyleHand)
  721.         {
  722.         HLock((Handle) theStyleHand);
  723.         AddDragItemFlavor(theDragRef, 1, 'styl', (Ptr) *theStyleHand, GetHandleSize((Handle) theStyleHand), 0);
  724.         DisposeHandle((Handle) theStyleHand);
  725.         }
  726.  
  727.     //    Set the item's bounding rectangle in global coordinates.
  728.  
  729.     SetDragItemBounds(theDragRef, 1, &(**dragRegion).rgnBBox);
  730.  
  731.     //    Prepare the drag region.
  732.  
  733.     tempRegion = NewRgn();
  734.     CopyRgn(dragRegion, tempRegion);
  735.     InsetRgn(tempRegion, 1, 1);
  736.     DiffRgn(dragRegion, tempRegion, dragRegion);
  737.     DisposeRgn(tempRegion);
  738.  
  739.     //    Drag the text. TrackDrag will return userCanceledErr if the drop whooshed back for any reason.
  740.  
  741.     error = TrackDrag(theDragRef, pEvent, dragRegion);
  742.  
  743.     if ((error != noErr) && (error != userCanceledErr))
  744.         return true;
  745.  
  746.     //    Check to see if the drop occurred in the Finder's Trash. If the drop occurred
  747.     //    in the Finder's Trash and a copy operation wasn't specified, delete the
  748.     //    source selection. Note that we can continute to get the attributes, drop location
  749.     //    modifiers, etc. of the drag until we dispose of it using DisposeDrag.
  750.  
  751.     GetDragAttributes(theDragRef, &attributes);
  752.     if (!(attributes & kDragInsideSenderApplication))
  753.         {
  754.         GetDropLocation(theDragRef, &dropLocation);
  755.  
  756.         GetDragModifiers(theDragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  757.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  758.  
  759.         if ((!copyText) && (IsDropInFinderTrash(&dropLocation)))
  760.             {
  761.             TEDelete(((TextDataPtr)pData)->hTE);
  762.             AdjustTE(pData, false);
  763.             AdjustScrollBars(pWindow, false, false, nil);
  764.             ((WindowDataPtr) pData)->changed = true;
  765.             }
  766.  
  767.         AEDisposeDesc(&dropLocation);
  768.         }
  769.  
  770.     // Dispose of this drag, 'cause we're done.
  771.  
  772.     DisposeDrag(theDragRef);
  773.  
  774.     DisposeRgn(dragRegion);
  775.  
  776.     return true;
  777.  
  778. } // DragText