home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / WASTE 1.1a4 / WASTE Source / WEMouse.p < prev    next >
Encoding:
Text File  |  1994-11-13  |  30.8 KB  |  1,140 lines  |  [TEXT/PJMM]

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