home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / DTS.Draw / Drag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-05-20  |  13.9 KB  |  557 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Drag.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19.  
  20.  
  21. /*****************************************************************************/
  22.  
  23.  
  24.  
  25. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  26. #include "App.defs.h"        /* Get various application definitions.            */
  27. #include "App.protos.h"        /* Get the prototypes for the application.        */
  28.  
  29. #ifndef __FOLDERS__
  30. #include <Folders.h>
  31. #endif
  32.  
  33. #ifndef __GESTALTEQU__
  34. #include <GestaltEqu.h>
  35. #endif
  36.  
  37. #ifndef __TREEOBJ2__
  38. #include "TreeObj2.h"
  39. #endif
  40.  
  41. static pascal OSErr DefaultDragTrackingHandler(DragTrackingMessage msg, WindowPtr wnd, void *refcon, DragReference dragRef);
  42. static pascal OSErr DefaultDragReceiveHandler(WindowPtr wnd, void *refcon, DragReference dragRef);
  43.  
  44. static OSErr    GetDragData(FileRecHndl frHndl, Point pt, Handle *retDragData, RgnHandle *retDragRgn);
  45. static Boolean    DropLocationIsFinderTrash(AEDesc *dropLocation);
  46.  
  47. static Boolean    gCanAcceptItems, gWindowHilited;
  48.  
  49.  
  50.  
  51. /*****************************************************************************/
  52. /*****************************************************************************/
  53.  
  54.  
  55.  
  56. #pragma segment Drag
  57. Boolean    DragDropAvailable(void)
  58. {
  59.     long    result;
  60.  
  61.     if (Gestalt(gestaltDragMgrAttr, &result)) return (false);
  62.     
  63.     if (!(result & (1 << gestaltDragMgrPresent))) return(false);
  64.  
  65. #ifdef powerc
  66.     if (!(result & (1 << gestaltPPCDragLibPresent))) return(false);
  67. #endif
  68.  
  69.     return(true);
  70. }
  71.  
  72.  
  73.  
  74. /*****************************************************************************/
  75.  
  76.  
  77.  
  78. #pragma segment Drag
  79. OSErr    ManageDragHandlers(WindowPtr window, Boolean install)
  80. {
  81.     OSErr                            err;
  82.     static DragTrackingHandlerUPP    dragTrackingUPP;
  83.     static DragReceiveHandlerUPP    dragReceiveUPP;
  84.  
  85.     if (!DragDropAvailable()) return(paramErr);
  86.  
  87.     if (!dragTrackingUPP) {
  88.         dragTrackingUPP = NewDragTrackingHandlerProc(DefaultDragTrackingHandler);
  89.         dragReceiveUPP  = NewDragReceiveHandlerProc (DefaultDragReceiveHandler);
  90.     }
  91.  
  92.     if (install) {
  93.         err = InstallTrackingHandler(dragTrackingUPP, window, nil);
  94.         if (!err)
  95.             err = InstallReceiveHandler(dragReceiveUPP, window, nil);
  96.         return(err);
  97.     }
  98.     else {
  99.         RemoveTrackingHandler(dragTrackingUPP, window);
  100.         RemoveReceiveHandler(dragReceiveUPP, window);
  101.         return(noErr);
  102.     }
  103. }
  104.  
  105.  
  106.  
  107. /*****************************************************************************/
  108.  
  109.  
  110.  
  111. #pragma segment Drag
  112. static pascal OSErr DefaultDragTrackingHandler(DragTrackingMessage msg, WindowPtr wnd, void *refcon, DragReference dragRef)
  113. {
  114. #ifndef __MWERKS__
  115. #pragma unused (refcon)
  116. #endif
  117.  
  118.     unsigned short        count, index;
  119.     unsigned long        flavorFlags, attr;
  120.     ItemReference        theItem;
  121.     RgnHandle            rgn;
  122.     Point                theMouse, localMouse;
  123.     OSErr                err;
  124.     Rect                contRct;
  125.  
  126.     if ((msg != dragTrackingEnterHandler) && (!gCanAcceptItems)) return(noErr);
  127.  
  128.     GetDragAttributes(dragRef, &attr);
  129.  
  130.     switch (msg) {
  131.  
  132.         case dragTrackingEnterHandler:
  133.             /* We get called with this message the first time that a drag enters ANY
  134.             ** window in our application. Check to see if all of the drag items contain
  135.             ** 'DDOC'. We only accept a drag if all of the items in the drag can be accepted. */
  136.  
  137.             gCanAcceptItems = true;
  138.             CountDragItems(dragRef, &count);
  139.  
  140.             for (index = 1; index <= count; index++) {
  141.                 GetDragItemReferenceNumber(dragRef, index, &theItem);
  142.                 err = GetFlavorFlags(dragRef, theItem, 'DDOC', &flavorFlags);
  143.                 if (err != noErr) {
  144.                     gCanAcceptItems = false;
  145.                     break;
  146.                 }
  147.             }
  148.             break;
  149.  
  150.         case dragTrackingEnterWindow:
  151.             /* We receive an EnterWindow message each time a drag enters one of our
  152.             ** application's windows. We initialize our global variables for tracking
  153.             ** the drag through the window. */
  154.  
  155.             break;
  156.  
  157.         case dragTrackingInWindow:
  158.  
  159.             /* We receive InWindow messages as long as the mouse is in one of our windows
  160.             ** during a drag. We draw the window highlighting when we get these messages. */
  161.  
  162.             GetDragMouse(dragRef, &theMouse, 0L);
  163.             localMouse = theMouse;
  164.             GlobalToLocal(&localMouse);
  165.  
  166.             /* Show or hide the window highlighting when the mouse enters or leaves the
  167.             ** TextEdit field in our window (we don't want to show the highlighting when
  168.             ** the mouse is over the window title bar or over the scroll bars). */
  169.  
  170.             GetContentRect(wnd, &contRct);
  171.             if (attr & dragHasLeftSenderWindow) {
  172.                 if (PtInRect(localMouse, &contRct)) {
  173.                     if (!gWindowHilited) {
  174.                         RectRgn(rgn = NewRgn(), &contRct);
  175.                         ShowDragHilite(dragRef, rgn, true);
  176.                         DisposeRgn(rgn);
  177.                     }
  178.                     gWindowHilited = true;
  179.                 } else {
  180.                     if (gWindowHilited)
  181.                         HideDragHilite(dragRef);
  182.                     gWindowHilited = false;
  183.                 }
  184.             }
  185.             break;
  186.  
  187.         case dragTrackingLeaveWindow:
  188.             if ((gWindowHilited) && (attr & dragHasLeftSenderWindow))
  189.                 HideDragHilite(dragRef);
  190.  
  191.             gWindowHilited = false;
  192.             break;
  193.  
  194.         case dragTrackingLeaveHandler:
  195.             break;
  196.  
  197.     }
  198.  
  199.     return(noErr);
  200. }
  201.  
  202.  
  203.  
  204. /*****************************************************************************/
  205.  
  206.  
  207.  
  208. #pragma segment Drag
  209. static pascal OSErr DefaultDragReceiveHandler(WindowPtr wnd, void *refcon, DragReference dragRef)
  210. {
  211. #ifndef __MWERKS__
  212. #pragma unused (refcon)
  213. #endif
  214.  
  215.     OSErr                err;
  216.     unsigned short        items, index;
  217.     short                cnum;
  218.     ItemReference        theItem;
  219.     DragAttributes        attr;
  220.     Handle                data;
  221.     Size                dataSize;
  222.     short                mouseDownModifiers, mouseUpModifiers, dragMove;
  223.     Point                mouseLoc, pinnedMouseLoc, initialLoc, locPt;
  224.     TreeObjHndl            dragRoot, root, cobj;
  225.     FileRecHndl            frHndl;
  226.     ClickInfo            click;
  227.     Boolean                change;
  228.  
  229.     if (!gCanAcceptItems) return(dragNotAcceptedErr);
  230.  
  231.     SetPort(wnd);
  232.  
  233.     GetDragAttributes(dragRef, &attr);
  234.     GetDragModifiers(dragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  235.  
  236.     dragMove = (
  237.         (attr & dragInsideSenderWindow) &&
  238.         (!((mouseDownModifiers | mouseUpModifiers) & optionKey))
  239.     );
  240.  
  241.     GetDragMouse(dragRef, &mouseLoc, &pinnedMouseLoc);
  242.     locPt = mouseLoc;
  243.     GlobalToLocal(&locPt);
  244.  
  245.     frHndl = (FileRecHndl)GetWRefCon(wnd);
  246.     root   = (*frHndl)->d.doc.root;
  247.     NewDocumentUndo(frHndl);
  248.  
  249.     CountDragItems(dragRef, &items);
  250.  
  251.     change = false;
  252.     for (index = 1; index <= items; ++index) {
  253.  
  254.         /* Get the item's reference number, so we can refer to it. */
  255.  
  256.         GetDragItemReferenceNumber(dragRef, index, &theItem);
  257.  
  258.         /* Try to get the flags for a 'DDOC' flavor. If this returns noErr,
  259.         ** then we know that a 'DDOC' flavor exists in the item. */
  260.  
  261.         err = GetFlavorDataSize(dragRef, theItem, 'DDOC', &dataSize);
  262.  
  263.         if (!err) {
  264.  
  265.             data = NewHandle(dataSize);
  266.             if (!data) return(memFullErr);
  267.  
  268.             HLock(data);
  269.             GetFlavorData(dragRef, theItem, 'DDOC', *data, &dataSize, 0L);
  270.             HUnlock(data);
  271.             dragRoot = NewRootObj(ROOTOBJ, 0);
  272.             err      = HReadTree(dragRoot, data);
  273.             DisposeHandle(data);
  274.             if (err) {
  275.                 DisposeObjAndOffspring(dragRoot);
  276.                 return(err);
  277.             }
  278.  
  279.             if (dragMove) {
  280.                 if (wnd == FrontWindowOfType(kwIsDocument, true)) {
  281.                     GetDragOrigin(dragRef, &initialLoc);
  282.                     click.message = CLICKDRAG;
  283.                     click.offset.h = mouseLoc.h - initialLoc.h;
  284.                     click.offset.v = mouseLoc.v - initialLoc.v;
  285.                     for (cnum = (*root)->numChildren; cnum;) {
  286.                         cobj = GetChildHndl(root, --cnum);
  287.                         if (mDerefCommon(cobj)->selected) {
  288.                             ModifyChild(MOVE_EDIT, root, cnum, true);
  289.                             DoFTreeMethod(cobj, CLICKMESSAGE, (long)&click);
  290.                             change = true;
  291.                         }
  292.                     }
  293.                 }
  294.                 else dragMove = false;
  295.             }
  296.  
  297.             if (!dragMove) {
  298.                 if (!change)
  299.                     DoTreeSelect(root, SELECTOFF);        /* Turn off current document selections. */
  300.                 for (cnum = (*dragRoot)->numChildren; cnum;) {
  301.                     cobj = GetChildHndl(dragRoot, --cnum);
  302.                     click.message = CLICKDRAG;
  303.                     click.offset = locPt;
  304.                     DoFTreeMethod(cobj, CLICKMESSAGE, (long)&click);
  305.                     CopyChild(DRAGDROP_EDIT, dragRoot, cnum, root, 0, true);
  306.                     change = true;
  307.                 }
  308.             }
  309.  
  310.             DisposeObjAndOffspring(dragRoot);
  311.         }
  312.     }
  313.  
  314.     if (change) {
  315.         BeginContent(wnd);
  316.         DoImageDocument(frHndl);
  317.         EndContent(wnd);
  318.         SetWindowDirty(wnd);
  319.         gWindowHilited = false;
  320.     }
  321.  
  322.     return(noErr);
  323. }
  324.  
  325.  
  326.  
  327. /*****************************************************************************/
  328. /*****************************************************************************/
  329. /*****************************************************************************/
  330.  
  331.  
  332.  
  333. #pragma segment Drag
  334. static OSErr    GetDragData(FileRecHndl frHndl, Point pt, Handle *retDragData, RgnHandle *retDragRgn)
  335. {
  336.     TreeObjHndl    root, cobj;
  337.     Handle        dragData;
  338.     RgnHandle    dragRgn, rgn;
  339.     short        numChildren, cnum;
  340.     OSErr        err;
  341.     ClickInfo    click;
  342.     Rect        rr;
  343.  
  344.     *retDragData = nil;
  345.     *retDragRgn  = nil;
  346.  
  347.     root     = (*frHndl)->d.doc.root;
  348.     dragData = NewHandle(0);
  349.     dragRgn  = NewRgn();
  350.  
  351.     numChildren = (*root)->numChildren;
  352.     (*root)->numChildren = 0;
  353.     err = HWriteTree(root, dragData);
  354.     (*root)->numChildren = numChildren;
  355.     if (err) {
  356.         DisposeHandle(dragData);
  357.         return(err);
  358.     }
  359.     (*(TreeObjHndl)dragData)->numChildren = mDerefRoot(root)->numSelected;
  360.  
  361.     for (cnum = 0; cnum < (*root)->numChildren; ++cnum) {
  362.         cobj = GetChildHndl(root, cnum);
  363.         if (mDerefCommon(cobj)->selected) {
  364.             DoFTreeMethod(cobj, GETRGNMESSAGE, (long)dragRgn);
  365.             click.message = CLICKDRAG;
  366.             click.offset.h = -pt.h;
  367.             click.offset.v = -pt.v;
  368.             DoFTreeMethod(cobj, CLICKMESSAGE, (long)&click);    /* Set click-point to 0,0. */
  369.             err = HWriteTree(cobj, dragData);
  370.             click.offset = pt;
  371.             DoFTreeMethod(cobj, CLICKMESSAGE, (long)&click);    /* Fix it up. */
  372.             if (err) {
  373.                 DisposeHandle(dragData);
  374.                 DisposeRgn   (dragRgn);
  375.                 return(err);
  376.             }
  377.         }
  378.     }
  379.     if (GetHandleSize((Handle)dragRgn) > 10000) {
  380.         root = (*frHndl)->d.doc.root;
  381.         rr   = GetSelectedArea(root);
  382.         RectRgn(dragRgn, &rr);
  383.         click.message = HITTESTOBJ;
  384.         click.localEvent.where = pt;
  385.         cobj = DoTreeHitTest(root, &click);
  386.         if (cobj) {
  387.             rgn = (RgnHandle)DoTreeObjMethod(cobj, GETRGNMESSAGE, (long)dragRgn);
  388.             DiffRgn(dragRgn, rgn, dragRgn);
  389.             DisposeRgn(rgn);
  390.         }
  391.     }
  392.  
  393.     *retDragData = dragData;
  394.     *retDragRgn  = dragRgn;
  395.     return(noErr);
  396. }
  397.  
  398.  
  399.  
  400. /*****************************************************************************/
  401.  
  402.  
  403.  
  404. #pragma segment Drag
  405. static Boolean    DropLocationIsFinderTrash(AEDesc *dropLocation)
  406. {
  407.     OSErr            err;
  408.     AEDesc            dropSpec;
  409.     FSSpec            *theSpec;
  410.     CInfoPBRec        thePB;
  411.     short            trashVRefNum;
  412.     long            trashDirID;
  413.  
  414.     /* Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  415.     ** it can't be coerced into an FSSpec, then it couldn't have been the Trash. */
  416.  
  417.     if (
  418.         (dropLocation->descriptorType != typeNull) &&
  419.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)
  420.     ) {
  421.         HLock(dropSpec.dataHandle);
  422.         theSpec = (FSSpec *)*dropSpec.dataHandle;
  423.  
  424.         /* Get the directory ID of the given dropLocation object. */
  425.  
  426.         thePB.dirInfo.ioCompletion = 0L;
  427.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  428.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  429.         thePB.dirInfo.ioFDirIndex = 0;
  430.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  431.  
  432.         err = PBGetCatInfo(&thePB, false);
  433.  
  434.         HUnlock(dropSpec.dataHandle);
  435.         AEDisposeDesc(&dropSpec);
  436.  
  437.         if (err != noErr)
  438.             return(false);
  439.  
  440.         /* If the err is not a directory, it must not be the Trash. */
  441.  
  442.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  443.             return(false);
  444.  
  445.         /* Get information about the Trash folder. */
  446.  
  447.         FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  448.  
  449.         /* If the directory ID of the dropLocation object is the same as the directory ID
  450.         ** returned by FindFolder, then the drop must have occurred into the Trash. */
  451.  
  452.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  453.             return(true);
  454.     }
  455.  
  456.     return(false);
  457. }
  458.  
  459.  
  460.  
  461. /*****************************************************************************/
  462.  
  463.  
  464.  
  465. #pragma segment Drag
  466. OSErr    DoDrag(FileRecHndl frHndl, EventRecord *event)
  467. {
  468.     OSErr            err;
  469.     Handle            dragData;
  470.     RgnHandle        dragRgn, rgn, oldClip;
  471.     DragReference    dragRef;
  472.     Point            pt;
  473.     Rect            rr;
  474.     DragAttributes    attr;
  475.     AEDesc            dropLoc;
  476.     short            mouseDownModifiers, mouseUpModifiers, dragCopy, i;
  477.     TreeObjHndl        root, cobj;
  478.     WindowPtr        oldPort;
  479.     PicHandle        pic;
  480.  
  481.     if (!DragDropAvailable()) return(paramErr);        /* No mgr, no drag. */
  482.  
  483.     err = NewDrag(&dragRef);
  484.     if (err) return(err);
  485.  
  486.     pt = event->where;
  487.     GlobalToLocal(&pt);
  488.     err = GetDragData(frHndl, pt, &dragData, &dragRgn);
  489.     if (err) return(err);
  490.  
  491.     pt.h = pt.v = 0;
  492.     LocalToGlobal(&pt);
  493.     OffsetRgn(dragRgn, pt.h, pt.v);
  494.  
  495.     HLock(dragData);
  496.     err = AddDragItemFlavor(dragRef, 1, 'DDOC', *dragData, GetHandleSize(dragData), 0);
  497.     DisposeHandle(dragData);
  498.     if (err) {
  499.         DisposeRgn(dragRgn);
  500.         DisposeDrag(dragRef);
  501.         return(err);
  502.     }
  503.  
  504.     oldPort = SetFilePort(frHndl);
  505.     GetClip(oldClip = NewRgn());
  506.     rr = GetDataArea(root = (*frHndl)->d.doc.root);
  507.     pic = OpenPicture(&rr);
  508.     ClipRect(&rr);
  509.     for (i = (*root)->numChildren; i;) {
  510.         cobj = GetChildHndl(root, --i);
  511.         if (mDerefCommon(cobj)->selected)
  512.             DoTreeDraw(cobj, DRAWOBJ);
  513.     }
  514.     ClosePicture();
  515.     SetClip(oldClip);
  516.     DisposeRgn(oldClip);
  517.     SetPort(oldPort);
  518.     HLock((Handle)pic);
  519.     err = AddDragItemFlavor(dragRef, 1, 'PICT', *pic, GetHandleSize((Handle)pic), 0);
  520.     KillPicture(pic);
  521.  
  522.     rr = (*dragRgn)->rgnBBox;
  523.     SetDragItemBounds(dragRef, 1, &rr);
  524.  
  525.     CopyRgn(dragRgn, rgn = NewRgn());
  526.     InsetRgn(rgn, 1, 1);
  527.     DiffRgn(dragRgn, rgn, dragRgn);
  528.     DisposeRgn(rgn);
  529.     err = TrackDrag(dragRef, event, dragRgn);
  530.  
  531.     if ((err) && (err != userCanceledErr)) {
  532.         DisposeDrag(dragRef);
  533.         DisposeRgn(dragRgn);
  534.         return(err);
  535.     }
  536.  
  537.     GetDragAttributes(dragRef, &attr);
  538.     if (!(attr & dragInsideSenderApplication)) {
  539.         GetDropLocation(dragRef, &dropLoc);
  540.         GetDragModifiers(dragRef, 0L, &mouseDownModifiers, &mouseUpModifiers);
  541.         dragCopy = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  542.  
  543.         if ((!dragCopy) && (DropLocationIsFinderTrash(&dropLoc)))
  544.             DoDelete(frHndl);
  545.  
  546.         AEDisposeDesc(&dropLoc);
  547.     }
  548.  
  549.     DisposeDrag(dragRef);
  550.     DisposeRgn(dragRgn);
  551.  
  552.     return(noErr);
  553. }
  554.  
  555.  
  556.  
  557.