home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / WASTE 1.1 / WEMouse.c < prev    next >
Encoding:
Text File  |  1994-11-02  |  25.8 KB  |  985 lines  |  [TEXT/MPCC]

  1. // { WASTE PROJECT }
  2. // { Mouse Clicks and support for Drag and Drop }
  3.  
  4. // { Copyright © 1993-1994 Marco Piovanelli }
  5. // { All Rights Reserved }
  6.  
  7. #include "WASTEIntf.h"
  8. #include <Folders.h>
  9.  
  10. static DragSendDataUPP WESendFlavorUPP = NULL;
  11.  
  12. pascal OSErr _WEExtractFlavor(DragReference theDrag, ItemReference theItem,
  13.                         FlavorType theType, Handle *hFlavor)
  14. {
  15.     Size theSize;
  16.     OSErr err;
  17.  
  18.     *hFlavor = nil;
  19.  
  20.     // { get size of flavor data }
  21.     err = GetFlavorDataSize(theDrag, theItem, theType, &theSize);
  22.     if (err != noErr) return err;
  23.  
  24.     // { allocate a block that size }
  25.     err = _WEAllocate(theSize, kAllocClear + kAllocTemp, hFlavor);
  26.     if (err != noErr) return err;
  27.  
  28.     // { get the flavor }
  29.     HLock(*hFlavor);
  30.     err = GetFlavorData(theDrag, theItem, theType, *hFlavor, &theSize, 0);
  31.     HUnlock(*hFlavor);
  32.  
  33.     return err;
  34. } // { _WEExtractFlavor }
  35.  
  36. pascal Boolean _WEDragTastesGood(DragReference theDrag)
  37. {
  38.     unsigned short dragItemIndex, numDragItems;
  39.     ItemReference theItem;
  40.     FlavorFlags theFlags;
  41.     short objectIndex;
  42.     OSType objectType;
  43.     OSErr err;
  44.  
  45.     // { count items in this theDrag }
  46.     err = CountDragItems(theDrag, &numDragItems);
  47.     if (err != noErr) return false;
  48.  
  49.     for (dragItemIndex = 1; dragItemIndex <= numDragItems; dragItemIndex++)
  50.     {
  51.         // { get item reference number for current drag item }
  52.         err = GetDragItemReferenceNumber(theDrag, dragItemIndex, &theItem);
  53.         if (err != noErr) return false;
  54.  
  55.         // { see if this drag item contains a text flavor }
  56.         err = GetFlavorFlags(theDrag, theItem, kTypeText, &theFlags);
  57.         if (err == badDragFlavorErr)
  58.         {
  59.             // { see if this drag item contains a flavor matching one of the registered object types }
  60.             objectIndex = 0;
  61.             while (_WEGetIndObjectType(objectIndex, &objectType) == noErr)
  62.             {
  63.                 err = GetFlavorFlags(theDrag, theItem, objectType, &theFlags);
  64.                 if (err != badDragFlavorErr)
  65.                     break;  // { enclosing while }
  66.                 objectIndex = objectIndex + 1;
  67.             } // { while }
  68.         }
  69.  
  70.         if (err != noErr)
  71.             return false;
  72.     } // { for }
  73.  
  74.     return true;
  75.  
  76. } // { _WEDragTastesGood }
  77.  
  78. pascal void _WEUpdateDragCaret(long offset, WEHandle hWE)
  79. {
  80.     long ticks;
  81.     WEPtr pWE;
  82.  
  83.     // { the WE record must be already locked }
  84.     pWE = *hWE;
  85.  
  86.     // { get current time }
  87.     ticks = TickCount();
  88.  
  89.     if (offset == pWE->dragCaretOffset)
  90.     {
  91.  
  92.         // { drag caret offset didn't change; blink the caret }
  93.         if ((GetCaretTime() < ticks - pWE->caretTime) &&
  94.             (offset != kInvalidOffset))
  95.         {
  96.             _WEDrawCaret(pWE->dragCaretOffset, hWE);
  97.             pWE->flags = pWE->flags ^ BSL(1, weFDragCaretVisible);
  98.             pWE->caretTime = ticks;
  99.         }
  100.     }
  101.     else
  102.     {
  103.  
  104.         // { drag caret offset did change }
  105.         // { hide old caret, if it's showing }
  106.         if (BTST(pWE->flags, weFDragCaretVisible))
  107.             _WEDrawCaret(pWE->dragCaretOffset, hWE);
  108.  
  109.         // { show new caret (unless offset is kInvalidOffset) }
  110.         if (offset != kInvalidOffset)
  111.         {
  112.             _WEDrawCaret(offset, hWE);
  113.             BSET(pWE->flags, weFDragCaretVisible);
  114.             pWE->caretTime = ticks;
  115.         }
  116.         else
  117.         {
  118.             BCLR(pWE->flags, weFDragCaretVisible);
  119.         }
  120.     
  121.         // { remember drag caret offset }
  122.         pWE->dragCaretOffset = offset;
  123.     }
  124. } // { _WEUpdateDragCaret }
  125.  
  126. pascal OSErr WETrackDrag(DragTrackingMessage theMessage,DragReference theDrag,
  127.                                     WEHandle hWE)
  128. {
  129.     WEPtr pWE;
  130.     DragAttributes attributes;
  131.     Point mouse;
  132.     RgnHandle tmpRgn;
  133.     LongPt thePoint;
  134.     long offset, ticks;
  135.     char edge;
  136.     Boolean saveWELock;
  137.     OSErr err;
  138.     Point zeroPoint = {0, 0};
  139.  
  140.     // { lock the WE record }
  141.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  142.     pWE = *hWE;
  143.  
  144.     // { dispatch on theMessage }
  145.     switch(theMessage)
  146.     {
  147.         case dragTrackingEnterWindow:
  148.             // { determine whether we can accept this drag }
  149.             if (_WEDragTastesGood(theDrag))
  150.                 BSET(pWE->flags, weFCanAcceptDrag);
  151.             else
  152.                 BCLR(pWE->flags, weFCanAcceptDrag);
  153.  
  154.             // { reset clickTime }
  155.             pWE->clickTime = 0;
  156.             break;
  157.  
  158.         case dragTrackingInWindow:
  159.             if (BTST(pWE->flags, weFCanAcceptDrag))
  160.             {
  161.  
  162.                 // { get drag attributes }
  163.                 err = GetDragAttributes(theDrag, &attributes);
  164.                 if (err != noErr)
  165.                     goto cleanup;
  166.  
  167.                 // { get current mouse location in local coordinates }
  168.                 err = GetDragMouse(theDrag, &mouse, &zeroPoint);
  169.                 if (err != noErr)
  170.                     goto cleanup;
  171.                 GlobalToLocal(&mouse);
  172.  
  173.                 if (PtInRgn(mouse, pWE->viewRgn))
  174.                 {
  175.                     // { mouse is in text area }
  176.                     // { hilite the text rectangle, if we haven't already }
  177.                     // { and if the drag has left sender window since drag tracking started }
  178.                     if ((!BTST(pWE->flags, weFHilited)) &&
  179.                         (attributes & dragHasLeftSenderWindow) != 0)
  180.                     {
  181.                         tmpRgn = NewRgn();
  182.                         CopyRgn(pWE->viewRgn, tmpRgn);
  183.                         InsetRgn(tmpRgn, -kTextMargin, -kTextMargin);
  184.                         ShowDragHilite(theDrag, tmpRgn, true);
  185.                         DisposeRgn(tmpRgn);
  186.                         BSET(pWE->flags, weFHilited);
  187.                     }
  188.  
  189.                     // { hide the caret }
  190.                     if (BTST(pWE->flags, weFCaretVisible))
  191.                         _WEBlinkCaret(hWE);
  192.  
  193.                     // { get text offset corresponding to mouse location }
  194.                     WEPointToLongPoint(mouse, &thePoint);
  195.                     offset = WEGetOffset(&thePoint, &edge, hWE);
  196.  
  197.                     // { if offset is within the original selection range, don't display drag feedback }
  198.                     if (theDrag == pWE->currentDrag)
  199.                         if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  200.                             offset = kInvalidOffset;
  201.  
  202.                     // { provide a drag feedback in the form of a blinking caret }
  203.                     _WEUpdateDragCaret(offset, hWE);
  204.  
  205.                     // { clear clickTime }
  206.                     pWE->clickTime = 0;
  207.                 }
  208.                 else
  209.                 {
  210.  
  211.                     // { mouse is outside text area }
  212.                     // { dehilite the text rectangle, if it's hilited }
  213.                     if (BTST(pWE->flags, weFHilited))
  214.                     {
  215.                         HideDragHilite(theDrag);
  216.                         BCLR(pWE->flags, weFHilited);
  217.                     }
  218.  
  219.                     // { hide the drag caret, if it's showing }
  220.                     _WEUpdateDragCaret(kInvalidOffset, hWE);
  221.  
  222.                     // { if the mouse has been remaining outside the view region for 10 ticks or more }
  223.                     // { and this drag was created by this WE instance, call the click loop routine }
  224.                     if (theDrag == pWE->currentDrag)
  225.                     {
  226.                         ticks = TickCount();
  227.                         if (pWE->clickTime == 0)
  228.                             pWE->clickTime = ticks;
  229.                         else if (ticks > pWE->clickTime + kAutoScrollDelay)
  230.                             if (pWE->clickLoop != nil)
  231.                                 (*(WEClickLoopProcPtr)pWE->clickLoop)(hWE);
  232.                     }
  233.                 }
  234.             }
  235.             break; // { case dragTrackingInWindow }
  236.  
  237.         case dragTrackingLeaveWindow:
  238.  
  239.             // { drag has left this window }
  240.             // { dehilite the text area if necessary }
  241.             if (BTST(pWE->flags, weFHilited))
  242.             {
  243.                 HideDragHilite(theDrag);
  244.                 BCLR(pWE->flags, weFHilited);
  245.             }
  246.  
  247.             // { hide the drag caret, if it's showing }
  248.             _WEUpdateDragCaret(kInvalidOffset, hWE);
  249.  
  250.             break;
  251.  
  252.         default:
  253.             ;
  254.     } // { case theMessage }
  255.  
  256.     // { clear result code }
  257.     err = noErr;
  258.  
  259. cleanup:
  260.     // { unlock the WE record }
  261.     _WESetHandleLock((Handle)hWE, saveWELock);
  262.  
  263.     // { return result code }
  264.     return err;
  265.  
  266. } // { WETrackDrag }
  267.  
  268. pascal OSErr WEReceiveDrag(DragReference theDrag, WEHandle hWE)
  269. {
  270.     WEPtr pWE;
  271.     WEActionHandle hAction;
  272.     Point mouse;
  273.     short downModifiers, upModifiers;
  274.     LongPt dropLocation;
  275.     long insertionOffset, insertionLength;
  276.     long sourceStart, sourceEnd;
  277.     long destStart, destEnd;
  278.     long delta;
  279.     unsigned short dragItemIndex, numDragItems;
  280.     ItemReference theItem;
  281.     Handle hText, hStyles, hSoup, hObjectData;
  282.     short objectIndex;
  283.     OSType objectType;
  284.     GrafPtr savePort;
  285.     short intPasteAction;
  286.     short saveUndoSupport, saveInhibitRecal;
  287.     char dropEdge, space;
  288.     Boolean isMove, isBackwards;
  289.     Boolean saveWELock;
  290.     OSErr err;
  291.     short zero = 0;
  292.     Point zeroPoint = {0, 0};
  293.  
  294.     isMove = false;
  295.     hText = nil;
  296.     hStyles = nil;
  297.     hSoup = nil;
  298.     hObjectData = nil;
  299.  
  300.     // { stop any ongoing inline input session }
  301.     WEStopInlineSession(hWE);
  302.  
  303.     // { lock the WE record }
  304.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  305.     pWE = *hWE;
  306.  
  307.     // { set up the port }
  308.     GetPort(&savePort);
  309.     SetPort(pWE->port);
  310.  
  311.     // { hide the drag caret }
  312.     _WEUpdateDragCaret(kInvalidOffset, hWE);
  313.  
  314.     // { refuse this drag if we don't like its flavors  :-) }
  315.     err = badDragFlavorErr;
  316.     if (_WEDragTastesGood(theDrag) == false)
  317.         goto cleanup;
  318.  
  319.     // { get drag modifiers }
  320.     err = GetDragModifiers(theDrag, &zero, &downModifiers, &upModifiers);
  321.     if (err != noErr)
  322.         goto cleanup;
  323.  
  324.     // { get drop location in local coordinates }
  325.     err = GetDragMouse(theDrag, &mouse, &zeroPoint);
  326.     if (err != noErr)
  327.         goto cleanup;
  328.     GlobalToLocal(&mouse);
  329.  
  330.     // { for the drag to be accepted, the drop location must be within the view region }
  331.     err = dragNotAcceptedErr;
  332.     if (PtInRgn(mouse, pWE->viewRgn) == false)
  333.         goto cleanup;
  334.  
  335.     // { get drop offset into the text }
  336.     WEPointToLongPoint(mouse, &dropLocation);
  337.     insertionOffset = WEGetOffset(&dropLocation, &dropEdge, hWE);
  338.  
  339.     // { destStart/destEnd define the range to highlight at the end of the drag }
  340.     destStart = insertionOffset;
  341.  
  342.     // { drag originated from this same window? }
  343.     if (theDrag == pWE->currentDrag)
  344.     {
  345.  
  346.         // { sourceStart/sourceEnd define the range to delete at the end of the move }
  347.         sourceStart = pWE->selStart;
  348.         sourceEnd = pWE->selEnd;
  349.  
  350.         // { remember text length before insertion }
  351.         delta = pWE->textLength;
  352.  
  353.         // { if insertion offset is within the original selection range, abort the drag }
  354.         // (*err = dragNotAcceptedErr;*)
  355.         if (_WEOffsetInRange(insertionOffset, dropEdge, sourceStart, sourceEnd))
  356.             goto cleanup;
  357.  
  358.         // { if the drag originated from this window, a move, }
  359.         // { rather than a copy, should be performed }
  360.         // { Exception: the option key may be held down at mouse-down }
  361.         // { or mouse-up time to force a copy operation. }
  362.  
  363.         isMove = (((downModifiers | upModifiers) & optionKey) == 0);
  364.         isBackwards = (insertionOffset <= sourceStart);
  365.     } // { if intra-window drag }
  366.  
  367.     // { clear null style }
  368.     BCLR(pWE->flags, weFUseNullStyle);
  369.  
  370.     // { hide selection highlighting }
  371.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  372.  
  373.     // { if undo support is enabled, create a new action so we'll be able to undo the insertion }
  374.     if (BTST(pWE->flags, weFUndoSupport))
  375.     {
  376.         WEClearUndo(hWE);
  377.         if (WENewAction(insertionOffset, insertionOffset, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  378.             if (WEPushAction(hAction) != noErr)
  379.                 { ; }
  380.     }
  381.  
  382.     // { count items in this drag }
  383.     err = CountDragItems(theDrag, &numDragItems);
  384.     if (err != noErr)
  385.         goto cleanup;
  386.  
  387.     for (dragItemIndex = 1; dragItemIndex<=numDragItems; dragItemIndex++)
  388.     {
  389.         // { get item reference number for current drag item }
  390.         err = GetDragItemReferenceNumber(theDrag, dragItemIndex, &theItem);
  391.         if (err != noErr)
  392.             goto cleanup;
  393.  
  394.         // { see if this drag item contains a text flavor }
  395.         err = _WEExtractFlavor(theDrag, theItem, kTypeText, &hText);
  396.         if (err == noErr)
  397.         {
  398.             // { extract accompanying styles and soup, if any }
  399.             err = _WEExtractFlavor(theDrag, theItem, kTypeStyles, &hStyles);
  400.             if (err != noErr && err != badDragFlavorErr)
  401.                 goto cleanup;
  402.             err = _WEExtractFlavor(theDrag, theItem, kTypeSoup, &hSoup);
  403.             if (err != noErr && err != badDragFlavorErr)
  404.                 goto cleanup;
  405.  
  406.             // { any extra space added because of intelligent cut-and-paste rules will use the }
  407.             // { style attributes set at the insertion point }
  408.             if (dragItemIndex == 1)
  409.             {
  410.                 pWE->selStart = insertionOffset;
  411.                 pWE->selEnd = insertionOffset;
  412.                 _WESynchNullStyle(hWE);
  413.             }
  414.  
  415.             // { get text length }
  416.             insertionLength = GetHandleSize(hText);
  417.             destEnd = insertionOffset + insertionLength;
  418.  
  419.             // { insert the new text at the insertion point }
  420.             HLock(hText);
  421.             err = _WEInsertText(insertionOffset, *hText, insertionLength, hWE);
  422.             _WEForgetHandle(&hText);
  423.             if (err != noErr)
  424.                 goto cleanup;
  425.  
  426.             // { adjust deletion range length in undo buffer }
  427.             _WEAdjustUndoRange(insertionLength, hWE);
  428.  
  429.             // { apply the accompanying styles, if any }
  430.             if (hStyles != nil)
  431.             {
  432.                 err = _WEApplyStyleScrap(insertionOffset, destEnd,
  433.                         (StScrpHandle)hStyles, hWE);
  434.                 if (err != noErr)
  435.                     goto cleanup;
  436.                 _WEForgetHandle(&hStyles);
  437.             }
  438.  
  439.             // { apply the accompanying soup, if any }
  440.             if (hSoup != nil)
  441.             {
  442.                 err = _WEApplySoup(insertionOffset, hSoup, hWE);
  443.                 if (err != noErr)
  444.                     goto cleanup;
  445.                 _WEForgetHandle(&hSoup);
  446.             }
  447.  
  448.             // { determine whether an extra space should be added before or after the inserted text }
  449.             intPasteAction = _WEIntelligentPaste(insertionOffset, destEnd, hWE);
  450.  
  451.             // { add the extra space, if necessary }
  452.             if (intPasteAction != weDontAddSpaces)
  453.             {
  454.                 space = 32;
  455.                 if (intPasteAction == weAddSpaceOnLeftSide)
  456.                 {
  457.                     err = _WEInsertText(insertionOffset, &space, 1, hWE);
  458.                     if (err != noErr)
  459.                         goto cleanup;
  460.  
  461.                     destEnd = destEnd + 1;
  462.  
  463.                     // { if an extra space is inserted in front of all dropped items, }
  464.                     // { don't count it when eventually highlighting the destination range }
  465.                     if (dragItemIndex == 1)
  466.                         destStart = destStart + 1;
  467.                 }
  468.                 else
  469.                 {
  470.                     err = _WEInsertText(destEnd, &space, 1, hWE);
  471.                     if (err != noErr)
  472.                         goto cleanup;
  473.                 }
  474.  
  475.                 insertionLength = insertionLength + 1;
  476.                 _WEAdjustUndoRange(1, hWE);
  477.             } // { if extra space }
  478.  
  479.         }
  480.         else if (err == badDragFlavorErr)
  481.         {
  482.  
  483.             // { no text flavor: there must be a flavor matching one of the registered object types }
  484.             objectIndex = 0;
  485.             while (_WEGetIndObjectType(objectIndex, &objectType) == noErr)
  486.             {
  487.                 err = _WEExtractFlavor(theDrag, theItem, objectType, &hObjectData);
  488.                 if (err == noErr)
  489.                     break; // { enclosing while }
  490.                 if (err != badDragFlavorErr)
  491.                     goto cleanup;
  492.                 objectIndex = objectIndex + 1;
  493.             } // { while }
  494.  
  495.             if (err != noErr)
  496.                 goto cleanup;
  497.  
  498.             // { set insertion point on first iteration (*after* extracting flavors, in case we are }
  499.             // { doing an intra-window move, otherwise our send proc would be confused) }
  500.             if (dragItemIndex == 1)
  501.             {
  502.                 pWE->selStart = insertionOffset;
  503.                 pWE->selEnd = insertionOffset;
  504.             }
  505.  
  506.             // { insert the object, but without touching undo or redrawing the text }
  507.             saveUndoSupport = WEFeatureFlag(weFUndoSupport, weBitClear, hWE);
  508.             saveInhibitRecal = WEFeatureFlag(weFInhibitRecal, weBitSet, hWE);
  509.             err = WEInsertObject(objectType, hObjectData, zeroPoint, hWE);
  510.             WEFeatureFlag(weFUndoSupport, saveUndoSupport, hWE);
  511.             WEFeatureFlag(weFInhibitRecal, saveInhibitRecal, hWE);
  512.             if (err != noErr)
  513.                 goto cleanup;
  514.  
  515.             insertionLength = 1;
  516.             destEnd = insertionOffset + 1;
  517.             _WEAdjustUndoRange(1, hWE);
  518.         }
  519.         else
  520.             goto cleanup;
  521.  
  522.         // { advance insertion offset for subsequent drag items, if any }
  523.         insertionOffset = insertionOffset + insertionLength;
  524.  
  525.     } // { for }
  526.  
  527.     if (isMove)
  528.     {
  529.         // { adjust source range }
  530.         if (isBackwards)
  531.         {
  532.             delta = delta - pWE->textLength;
  533.             sourceStart = sourceStart - delta;
  534.             sourceEnd = sourceEnd - delta;
  535.         }
  536.  
  537.         // { extend range according to intelligent cut-and-paste rules }
  538.         _WEIntelligentCut(&sourceStart, &sourceEnd, hWE);
  539.  
  540.         // { if undo support is enabled, create a new action so we'll be able to undo the deletion }
  541.         if (BTST(pWE->flags, weFUndoSupport))
  542.             if (WENewAction(sourceStart, sourceEnd, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  543.                 if (WEPushAction(hAction) != noErr)
  544.                     { ; }
  545.  
  546.         // { delete source range }
  547.         delta = pWE->textLength;
  548.         err = _WEDeleteRange(sourceStart, sourceEnd, hWE);
  549.         if (err != noErr)
  550.             goto cleanup;
  551.  
  552.         // { adjust destination range }
  553.         if (isBackwards == false)
  554.         {
  555.             delta = delta - pWE->textLength;
  556.             destStart = destStart - delta;
  557.             destEnd = destEnd - delta;
  558.         }
  559.  
  560.     } // { if isMove }
  561.  
  562.     // { select the range encompassing all items dropped }
  563.     pWE->selStart = destStart;
  564.     pWE->selEnd = destEnd;
  565.  
  566.     // { redraw the text }
  567.     if (isMove)
  568.         if (sourceStart < destStart)
  569.             err = _WERedraw(sourceStart, destEnd, hWE);
  570.         else
  571.             err = _WERedraw(destStart, sourceEnd, hWE);
  572.     else
  573.         err = _WERedraw(destStart, destEnd, hWE);
  574.  
  575. cleanup:
  576.     // { dispose of temporary handles }
  577.     _WEForgetHandle(&hText);
  578.     _WEForgetHandle(&hStyles);
  579.     _WEForgetHandle(&hSoup);
  580.  
  581.     // { restore the port }
  582.     SetPort(savePort);
  583.  
  584.     // { unlock the WE record }
  585.     _WESetHandleLock((Handle)hWE, saveWELock);
  586.  
  587.     // { return result code }
  588.     return err;
  589.  
  590. } // { WEReceiveDrag }
  591.  
  592. pascal OSErr _WESendFlavor(FlavorType theType, void *dragSendRefCon, Handle hWE,
  593.                     DragReference theDrag)
  594. {
  595.     WEPtr pWE;
  596.     long selStart, selEnd;
  597.     WEObjectDescHandle hObjectDesc;
  598.     Handle hItem;
  599.     Boolean disposeItem;
  600.     OSErr err;
  601.  
  602.     pWE = *(WEHandle)hWE;
  603.     selStart = pWE->selStart;
  604.     selEnd = pWE->selEnd;
  605.     disposeItem = false;
  606.     hItem = nil;
  607.  
  608.     // { see if the selection contains an embedded object whose type matches the flavortype }
  609.     if ((selEnd - selStart == 1) && _WEIsEmbeddedObject(selStart, &hObjectDesc, (WEHandle)hWE)
  610.         && ((*hObjectDesc)->objectType == theType))
  611.         hItem = (*hObjectDesc)->objectDataHandle;
  612.     else
  613.     {
  614.  
  615.         // { allocate a temporary handle to hold a copy of the requested flavor }
  616.         err = _WEAllocate(0, kAllocTemp, &hItem);
  617.         if (err != noErr)
  618.             goto cleanup;
  619.         disposeItem = true;        // { dispose of hItem when done }
  620.  
  621.         // { identify the requested flavor type as either 'TEXT', 'styl' or 'SOUP' }
  622.         if (theType == kTypeText)
  623.             err = WECopyRange(selStart, selEnd, hItem, nil, nil, (WEHandle)hWE);
  624.         else if (theType == kTypeStyles)
  625.             err = WECopyRange(selStart, selEnd, nil, hItem, nil, (WEHandle)hWE);
  626.         else if (theType == kTypeSoup)
  627.             err = WECopyRange(selStart, selEnd, nil, nil, hItem,(WEHandle)hWE);
  628.         else
  629.             err = badDragFlavorErr;
  630.  
  631.         if (err != noErr)
  632.             goto cleanup;
  633.  
  634.     }
  635.  
  636.     // { set the drag flavor data }
  637.     HLock(hItem);
  638.     err = SetDragItemFlavorData(theDrag, (ItemReference)hWE, theType, *hItem,
  639.             GetHandleSize(hItem), 0);
  640.     HUnlock(hItem);
  641.  
  642. cleanup:
  643.  
  644.     // { clean up }
  645.     if (disposeItem)
  646.         _WEForgetHandle(&hItem);
  647.  
  648.     // { return result code }
  649.     return err;
  650.  
  651. } // { _WESendFlavor }
  652.  
  653. pascal Boolean _WEDraggedToTrash(DragReference theDrag)
  654. {
  655.  
  656.     // { return TRUE if the drop location of the specified drag is the trash }
  657.  
  658.     const short bDirectoryAttr = 4;
  659.  
  660.     AEDesc dropLocation, coercedDropLocation;
  661.     CInfoPBRec pb;
  662.     FSSpecPtr pSpec;
  663.     short trashVRefNum;
  664.     long trashDirID;
  665.     Boolean draggedToTrash;
  666.  
  667.     draggedToTrash = false;
  668.     dropLocation.dataHandle = nil;
  669.     coercedDropLocation.dataHandle = nil;
  670.  
  671.     // { get drop location }
  672.     if (GetDropLocation(theDrag, &dropLocation) != noErr)
  673.         goto cleanup;
  674.  
  675.     // { do nothing if dropLocation is a null descriptor }
  676.     if (dropLocation.descriptorType == typeNull)
  677.         goto cleanup;
  678.  
  679.     // { try to coerce the descriptor to a file system specification record }
  680.     if (AECoerceDesc(&dropLocation, typeFSS, &coercedDropLocation) != noErr)
  681.         goto cleanup;
  682.  
  683.     // { lock the data handle of the coerced descriptor }
  684.     HLock(coercedDropLocation.dataHandle);
  685.     pSpec = *(FSSpecHandle)coercedDropLocation.dataHandle;
  686.  
  687.     // { determine the directory ID of the drop location (assuming it's a folder!) }
  688.     _WEBlockClr((Ptr)&pb, sizeof(pb));
  689.     pb.hFileInfo.ioVRefNum = pSpec->vRefNum;
  690.     pb.hFileInfo.ioDirID = pSpec->parID;
  691.     pb.hFileInfo.ioNamePtr = pSpec->name;
  692.     if (PBGetCatInfoSync(&pb) != noErr)
  693.         goto cleanup;
  694.  
  695.     // { make sure the specified file system object is really a directory }
  696.     if (!BTST(pb.hFileInfo.ioFlAttrib, bDirectoryAttr))
  697.         goto cleanup;
  698.  
  699.     // { find the directory ID of the trash folder }
  700.     if (FindFolder(pSpec->vRefNum, kTrashFolderType, kDontCreateFolder, &trashVRefNum, &trashDirID)
  701.         != noErr)
  702.         goto cleanup;
  703.  
  704.     // { compare the two directory IDs: if they're the same, the drop location is the trash }
  705.     if (pb.dirInfo.ioDrDirID == trashDirID)
  706.         draggedToTrash = true;
  707.  
  708. cleanup:
  709.     // { clean up }
  710.     AEDisposeDesc(&dropLocation);
  711.     AEDisposeDesc(&coercedDropLocation);
  712.  
  713.     return draggedToTrash;
  714. } // { _WEDraggedToTrash }
  715.  
  716. pascal OSErr _WEDrag(Point mouseLoc, short modifiers, long clickTime, WEHandle hWE)
  717. {
  718.     WEPtr pWE;
  719.     WEObjectDescHandle hObjectDesc;
  720.     EventRecord theEvent;
  721.     RgnHandle dragRgn, tmpRgn;
  722.     Rect dragBounds;
  723.     Point portDelta;
  724.     GrafPtr savePort;
  725.     OSErr err;
  726.     
  727.     dragRgn = nil;
  728.     tmpRgn = nil;
  729.     pWE = *hWE;
  730.     pWE->currentDrag = kNullDrag;
  731.  
  732.     // { set up the port }
  733.     GetPort(&savePort);
  734.     SetPort(pWE->port);
  735.  
  736.     // { fabricate an EventRecord for TrackDrag }
  737.     theEvent.what = mouseDown;
  738.     theEvent.message = 0;
  739.     theEvent.when = clickTime;
  740.     theEvent.where = mouseLoc;
  741.     LocalToGlobal(&theEvent.where);
  742.     theEvent.modifiers = modifiers;
  743.  
  744.     // { before seeing the dotted outline, the user must move the mouse a certain }
  745.     // { distance away from the initial mouse location; this allows a short click in the selection }
  746.     // { area to set the insertion point instead of starting a drag-and-drop sequence }
  747.     err = noDragErr;
  748.     if (WaitMouseMoved(theEvent.where) == false)
  749.         goto cleanup;
  750.  
  751.     // { create a drag object }
  752.     err = NewDrag(&pWE->currentDrag);
  753.     if (err != noErr)
  754.         goto cleanup;
  755.  
  756.     // {$IFC WASTE_DEBUG}
  757. //        _WEAssert(pWE->currentDrag != kNullDrag, 'Zero is a valid drag reference (??)');
  758.     // {$ENDC}
  759.  
  760.     // { if the selection range contains only an embedded object, }
  761.     // {  use its object kind as flavor type }
  762.     if ((pWE->selEnd - pWE->selStart == 1) && _WEIsEmbeddedObject(pWE->selStart, &hObjectDesc, hWE))
  763.     {
  764.         err = AddDragItemFlavor(pWE->currentDrag, (ItemReference)hWE, (*hObjectDesc)->objectType,
  765.                 nil, 0, 0);
  766.         if (err != noErr)
  767.             goto cleanup;
  768.     }
  769.     else
  770.     {
  771.     
  772.         // { add a 'TEXT' flavor to the drag }
  773.         err = AddDragItemFlavor(pWE->currentDrag, (ItemReference)hWE, kTypeText, nil, 0, 0);
  774.         if (err != noErr)
  775.             goto cleanup;
  776.  
  777.         // { add a 'styl' flavor to the drag }
  778.         err = AddDragItemFlavor(pWE->currentDrag, (ItemReference)hWE, kTypeStyles, nil, 0, 0);
  779.         if (err != noErr)
  780.             goto cleanup;
  781.  
  782.         // { add a 'SOUP' flavor to the drag }
  783.         err = AddDragItemFlavor(pWE->currentDrag, (ItemReference)hWE, kTypeSoup, nil, 0, 0);
  784.         if (err != noErr)
  785.             goto cleanup;
  786.  
  787.     }
  788.  
  789.     // { since we didn't provide the flavor data for any of the above flavors, }
  790.     // { we need supply a data send callback }
  791.     if (WESendFlavorUPP == NULL) WESendFlavorUPP = NewDragSendDataProc(_WESendFlavor);
  792.     err = SetDragSendProc(pWE->currentDrag, WESendFlavorUPP, 0);
  793.     if (err != noErr)
  794.         goto cleanup;
  795.  
  796.     // { get hilite region }
  797.     dragRgn = WEGetHiliteRgn(pWE->selStart, pWE->selEnd, hWE);
  798.  
  799.     // { we need just the outline of this region }
  800.     tmpRgn = NewRgn();
  801.     CopyRgn(dragRgn, tmpRgn);
  802.     InsetRgn(tmpRgn, 1, 1);
  803.     DiffRgn(dragRgn, tmpRgn, dragRgn);
  804.     DisposeRgn(tmpRgn);
  805.  
  806.     // { and we need it in global coordinates }
  807.     portDelta.v = 0;
  808.     portDelta.h = 0;
  809.     LocalToGlobal(&portDelta);
  810.     OffsetRgn(dragRgn, portDelta.h, portDelta.v);
  811.  
  812.     // { set the bounds of the drag }
  813.     dragBounds = (*dragRgn)->rgnBBox;
  814.     err = SetDragItemBounds(pWE->currentDrag, (ItemReference)hWE, &dragBounds);
  815.     if (err != noErr)
  816.         goto cleanup;
  817.  
  818.     // { track the drag }
  819.     err = TrackDrag(pWE->currentDrag, &theEvent, dragRgn);
  820.     if (err != noErr)
  821.         goto cleanup;
  822.  
  823.     // { if the selection was dragged to the trash, delete it }
  824.     if (_WEDraggedToTrash(pWE->currentDrag))
  825.     {
  826.         err = WEDelete(hWE);
  827.         if (err != noErr)
  828.             goto cleanup;
  829.     }
  830.  
  831.     // { clear result code }
  832.     err = noErr;
  833.  
  834. cleanup:
  835.     // { dispose of the drag }
  836.     if (pWE->currentDrag != kNullDrag)
  837.     {
  838.         DisposeDrag(pWE->currentDrag);
  839.         pWE->currentDrag = kNullDrag;
  840.     }
  841.  
  842.     // { dispose of the drag region }
  843.     if (dragRgn != nil)
  844.         DisposeRgn(dragRgn);
  845.  
  846.     // { restore the port }
  847.     SetPort(savePort);
  848.  
  849.     // { return result code }
  850.     return err;
  851. } // { _WEDrag }
  852.  
  853. pascal void WEClick(Point mouseLoc, short modifiers, long clickTime, WEHandle hWE)
  854. {
  855.     WEPtr pWE;
  856.     LongPt thePoint;
  857.     long offset, anchor;
  858.     long rangeStart, rangeEnd;
  859.     char edge;
  860.     Boolean isMultipleClick;
  861.     Boolean saveWELock;
  862.  
  863.     // { stop any ongoing inline input session }
  864.     WEStopInlineSession(hWE);
  865.  
  866.     // { lock the WE record }
  867.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  868.     pWE = *hWE;
  869.  
  870.     // { hide the caret if it's showing }
  871.     if (BTST(pWE->flags, weFCaretVisible))
  872.         _WEBlinkCaret(hWE);
  873.  
  874.     // { find click offset }
  875.     WEPointToLongPoint(mouseLoc, &thePoint);
  876.     offset = WEGetOffset(&thePoint, &edge, hWE);
  877.  
  878.     // { determine whether this click is part of a sequence }
  879.     isMultipleClick = ((clickTime < pWE->clickTime + GetDblTime()) && (offset == pWE->clickLoc));
  880.  
  881.     // { remember click time, click offset and edge value }
  882.     pWE->clickTime = clickTime;
  883.     pWE->clickLoc = offset;
  884.     pWE->clickEdge = edge;
  885.  
  886.     if ((modifiers & shiftKey) == 0)
  887.     {
  888.  
  889.         // { is this click part of a sequence or is it a single click? }
  890.         if (isMultipleClick)
  891.         {
  892.             pWE->clickCount = pWE->clickCount + 1;
  893.  
  894.             // { a double (triple) click creates an anchor-word (anchor-line) }
  895.             if (pWE->clickCount > 1)
  896.                 WEFindLine(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  897.             else
  898.                 WEFindWord(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  899.  
  900.             offset = pWE->anchorStart;
  901.  
  902.         }
  903.         else
  904.         {
  905.         
  906.             // { single-click }
  907.             // { if the Drag Manager is available and the click went in the selection range, }
  908.             // { this click may be the beginning of a drag gesture }
  909.             if (BTST(pWE->flags, weFDragAndDrop))
  910.                 if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  911.                     if (_WEDrag(mouseLoc, modifiers, clickTime, hWE) != noDragErr)
  912.                         goto cleanup;
  913.  
  914.             pWE->clickCount = 0;
  915.             anchor = offset;
  916.         }
  917.     }
  918.     else
  919.     {
  920.     
  921.         // { if the shift key was down, use the old anchor offset found with the previous click }
  922.         if (BTST(pWE->flags, weFAnchorIsEnd))
  923.             anchor = pWE->selEnd;
  924.         else
  925.             anchor = pWE->selStart;
  926.     }
  927.  
  928.     // { set the weFMouseTracking bit while we track the mouse }
  929.     BSET(pWE->flags, weFMouseTracking);
  930.  
  931.     // { MOUSE TRACKING LOOP }
  932.     do
  933.     {
  934.  
  935.         // { get text offset corresponding to mouse position }
  936.         WEPointToLongPoint(mouseLoc, &thePoint);
  937.         offset = WEGetOffset(&thePoint, &edge, hWE);
  938.  
  939.         // { if we're selecting words or lines, pin offset to a word or line boundary }
  940.         if (pWE->clickCount > 0)
  941.         {
  942.             if (pWE->clickCount > 1)
  943.                 WEFindLine(offset, edge, &rangeStart, &rangeEnd, hWE);
  944.             else
  945.                 WEFindWord(offset, edge, &rangeStart, &rangeEnd, hWE);
  946.  
  947.             // { choose the word/line boundary and the anchor that are farthest away from each other }
  948.             if (offset > pWE->anchorStart)
  949.             {
  950.                 anchor = pWE->anchorStart;
  951.                 offset = rangeEnd;
  952.             }
  953.             else
  954.             {
  955.                 offset = rangeStart;
  956.                 anchor = pWE->anchorEnd;
  957.             }
  958.         }
  959.  
  960.         // { set the selection range from anchor point to current offset }
  961.         WESetSelection(anchor, offset, hWE);
  962.  
  963.         // { call the click loop callback, if any }
  964.         if (pWE->clickLoop != nil)
  965.             if ((*(WEClickLoopProcPtr)pWE->clickLoop)(hWE) == false)
  966.                 break;
  967.  
  968.         // { update mouse position }
  969.         GetMouse(&mouseLoc);
  970.  
  971.     } while(WaitMouseUp());
  972.  
  973.     // { clear the weFMouseTracking bit }
  974.     BCLR(pWE->flags, weFMouseTracking);
  975.  
  976.     // { redraw the caret immediately if the selection range is empty }
  977.     if (anchor == offset)
  978.         _WEBlinkCaret(hWE);
  979.  
  980. cleanup:
  981.     // { unlock the WE record }
  982.     _WESetHandleLock((Handle)hWE, saveWELock);
  983.  
  984. } // { WEClick }
  985.