home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / BlobMgr / Library Folder / Click.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-21  |  11.1 KB  |  397 lines  |  [TEXT/KAHL]

  1. /*
  2.  * BlobClick() and support routines. BlobClick() is the basic blob
  3.  * transaction handler.  Other routines and variables are used
  4.  * to modify the way BlobClick() functions.
  5.  *
  6.  * unglueZoom determines whether BlobClick() will zoom an outline of the
  7.  * receptor drag region back to the donor when a donor is unglued
  8.  * from a receptor by double-clicking.
  9.  *
  10.  * badDragZoom determines whether the outline of a dragged donor will
  11.  * be zoomed back where it was dragged from if it's not dragged
  12.  * somewhere it can be glued to.
  13.  *
  14.  * The permission booleans control the types of transactions that
  15.  * BlobClick() is allowed to perform:
  16.  *
  17.  * ungluePerm is true if double-clicking detaches globs from receptors.    
  18.  * xferPerm is true if globs can be transferred between receptors.    
  19.  * swapPerm is true if globs can be swapped between receptors.
  20.  * dupPerm is true if globs can be duplicated onto other receptors.
  21.  * replacePerm is true if transfers, swaps and duplications can cause
  22.  * a receptor's glob to be replaced by another.  Note that if
  23.  * replacePerm is false, transfers and duplications can only be made
  24.  * to empty receptors, and swaps fail altogether.
  25.  *
  26.  * Swaps take precedence over duplications, and both take precedence
  27.  * over transfers, if two or more of these flags are on.  All are
  28.  * subject to the value of the replacement flag, as noted above.
  29.  *
  30.  * lastWhen and lastWhere are for double-click detection.
  31.  * If lastWhen is set to 0, the next click will not be interpreted
  32.  * as a double-click.  Since that's how it's initialized, BlobClick()
  33.  * never mistakes the first click passed to it for a double-click.
  34.  *
  35.  * BlobClick() only saves the click time and location when the user
  36.  * clicks in a receptor blob drag region and releases the mouse without
  37.  * moving it, since that's the only time a double-click is relevant.
  38.  *
  39.  * bcAdvise is the BlobClick() filter function.  If it's nil, BlobClick()
  40.  * does all the work itself.  If bcFilter is set to the address of
  41.  * some function, that function is consulted in certain circumstances
  42.  * to see whether to continue processing or not.  If the filter returns
  43.  * false, BlobClick() terminates early.
  44.  *
  45.  * bcResult holds the result of the last call to BlobClick().  db1,
  46.  * db2, rb1 and rb2 are set to indicate which blobs were involved in
  47.  * any transaction that occurs (these are known as the cast (as in
  48.  * cast of thousands) of the transaction).  The result code and the
  49.  * cast may be obtained by the host program with the BClickResult()
  50.  * routine, and have meanings as follows:
  51.  *
  52.  * Glue: db1 = donor glued, rb1 = receptor glued to, db2 = donor replaced by
  53.  *        db1 (nil if none), rb2 nil.
  54.  * Unglue: db1 = donor unglued, rb1 = receptor unglued from, db2, rb2 nil.
  55.  * Transfer: db1 = donor transferred, rb1 = source receptor,
  56.  *        rb2 = destination receptor, db2 = donor replaced by db1 (nil if none).
  57.  * Duplication: same as transfer, except db1 = donor duplicated.
  58.  * Swap: db1 = donor originally on rb1, db2 = donor originally on rb2,
  59.  *        rb1 = source receptor, rb2 = dest receptor.
  60.  *
  61.  * The result code and the cast are undefined if a filter function
  62.  * causes BlobClick() to terminate early.
  63.  *
  64.  * srcRect and dstRect are used by BlobClick() and BadDrag to keep
  65.  * track of how to do zoomback on bad drags.
  66.  */
  67.  
  68. # include    "BlobMgr.h"
  69.  
  70.  
  71. static Boolean FindReceptor (Point thePoint, BlobSetHandle rSet, BlobHandle *r);
  72.  
  73.  
  74. static Boolean    unglueZoom = true;
  75. static Boolean    badDragZoom = true;
  76.  
  77. static Boolean    ungluePerm = true;
  78. static Boolean    xferPerm = true;
  79. static Boolean    replacePerm = true;
  80. static Boolean    dupPerm = false;
  81. static Boolean    swapPerm = true;
  82.  
  83.  
  84. static long    lastWhen = 0L;
  85. static Point    lastWhere;
  86.  
  87. static BAdvisoryProcPtr bcAdvise = (BAdvisoryProcPtr) nil;
  88.  
  89. static short        bcResult;
  90. static BlobHandle    db1;
  91. static BlobHandle    db2;
  92. static BlobHandle    rb1;
  93. static BlobHandle    rb2;
  94.  
  95. static Rect            srcRect, dstRect;
  96.  
  97.  
  98. /*
  99.  * Set or get the flags determining zoomback behavior on bad drags
  100.  * and unglue transactions
  101.  */
  102.  
  103. pascal void
  104. SetBCZoomFlags (Boolean uGlueZoom, Boolean bDragZoom)
  105. {
  106.     unglueZoom = uGlueZoom;
  107.     badDragZoom = bDragZoom;
  108. }
  109.  
  110.  
  111. pascal void
  112. GetBCZoomFlags (Boolean *uGlueZoom, Boolean *bDragZoom)
  113. {
  114.     *uGlueZoom = unglueZoom;
  115.     *bDragZoom = badDragZoom;
  116. }
  117.  
  118.  
  119. /*
  120.  * Set or get the flags determining transaction permissions
  121.  */
  122.  
  123. pascal void
  124. SetBCPermissions (Boolean canUnglue, Boolean canXfer, Boolean canDup,
  125.                     Boolean canSwap, Boolean canRep)
  126. {
  127.     ungluePerm = canUnglue;
  128.     xferPerm = canXfer;
  129.     dupPerm = canDup;
  130.     swapPerm = canSwap;
  131.     replacePerm = canRep;
  132. }
  133.  
  134.  
  135. pascal void
  136. GetBCPermissions (Boolean *canUnglue, Boolean *canXfer, Boolean *canDup,
  137.                     Boolean *canSwap, Boolean *canRep)
  138. {
  139.     *canUnglue = ungluePerm;
  140.     *canXfer = xferPerm;
  141.     *canDup = dupPerm;
  142.     *canSwap = swapPerm;
  143.     *canRep = replacePerm;
  144. }
  145.  
  146.  
  147. /*
  148.  * Set or get the address of the BlobClick advisory filter routine.
  149.  * Pass nil to SetBCFilter to turn the filter off.
  150.  */
  151.  
  152. pascal void
  153. SetBCAdvisory (BAdvisoryProcPtr p)
  154. {
  155.     bcAdvise = p;
  156. }
  157.  
  158.  
  159. pascal void
  160. GetBCAdvisory (BAdvisoryProcPtr *p)
  161. {
  162.     *p = bcAdvise;
  163. }
  164.  
  165.  
  166. /*
  167.  * FindReceptor is called with the point at which the mouse was released
  168.  * after a donor or a receptor's glob was dragged.  It returns true if
  169.  * the mouse was released in a receptor that has an undimmed drag region
  170.  * and either (i) has no glob, or (ii) has a glob but replacement
  171.  * permission is on.  Otherwise it returns false.
  172.  *    thePoint    point at which mouse released
  173.  *    rSet        receptor set
  174.  *    *r            return receptor hit in this
  175.  */
  176.  
  177. static Boolean
  178. FindReceptor (Point thePoint, BlobSetHandle rSet, BlobHandle *r)
  179. {
  180.     return (FindBlob (thePoint, rSet, r)
  181.             && GetBDrawMode (*r, inDragBlob) == normalDraw
  182.             && (BGlob (*r) == nil || replacePerm));
  183. }
  184.  
  185.  
  186. /*
  187.  * BlobClick() -- blob transaction handler
  188.  */
  189.  
  190. pascal void
  191. BlobClick (Point thePt, long t, BlobSetHandle dSet, BlobSetHandle rSet)
  192. {
  193. BlobHandle    d, r1, r2;
  194. Point        thePoint;
  195. long        dragDelta;
  196. Boolean    badDrag = true;    /* assume the worst */
  197.  
  198.     thePoint = thePt;            /* do setup:  make local copy of point */
  199.     bcResult = 0;                /* and initialize status variables */
  200.     db1 = db2 = rb1 = rb2 = nil;
  201.  
  202.     /*
  203.      * Check for double-click if unglue permission is on.  If so and both
  204.      * clicks were in drag region of a non-dim receptor that has a glob
  205.      * glued to it, unglue the glob.  (Note that the hit-tests only work
  206.      * if the blob is not dimmed - which is as things should be.)
  207.      */
  208.     if (ungluePerm && t - lastWhen <= GetDblTime ())
  209.     {
  210.         if (rSet != nil
  211.             && FindBlob (thePoint, rSet, &r1) == inDragBlob
  212.             && TestBlob (r1, lastWhere) == inDragBlob)
  213.         {
  214.             if (bcAdvise == nil || (*bcAdvise) (advUnglue, r1))
  215.             {
  216.                 db1 = BGlob (r1);
  217.                 rb1 = r1;
  218.                 bcResult = bcUnglue;
  219.  
  220.                 if (unglueZoom)
  221.                     ZUnglueGlob (r1);
  222.                 else
  223.                     UnglueGlob (r1);
  224.             }
  225.         }
  226.         lastWhen = 0L;
  227.         return;
  228.     }
  229.  
  230.     lastWhen = 0L;
  231.  
  232.     /*
  233.      * See if the mouse was clicked in a donor blob.  If so, drag an
  234.      * outline around.  If the dragged outline ends up somewhere it
  235.      * can be glued to, do so, else zoom the outline back if appropriate.
  236.      */
  237.  
  238.     if (dSet != nil && FindBlob (thePoint, dSet, &d) == inDragBlob)
  239.     {
  240.         if (bcAdvise != nil && (*bcAdvise) (advDClick, d) == false)
  241.             return;        /* advisory says "quit" */
  242.  
  243.         dragDelta = DTrackBlob (d, inDragBlob, thePoint);
  244.         if (dragDelta != badDragResult && dragDelta != 0)
  245.         {
  246.             thePoint.h += LoWord (dragDelta);
  247.             thePoint.v += HiWord (dragDelta);
  248.             srcRect = dstRect = BDragBox (d);
  249.             OffsetRect (&dstRect, LoWord (dragDelta), HiWord (dragDelta));
  250.             if (rSet != nil && FindReceptor (thePoint, rSet, &r1))
  251.             {
  252.                 if (bcAdvise == nil || (*bcAdvise) (advGlue, r1))
  253.                 {
  254.                     db1 = d;
  255.                     db2 = BGlob (r1);
  256.                     rb1 = r1;
  257.                     bcResult = bcGlue;
  258.                     GlueGlob (d, r1);
  259.                     badDrag = false;
  260.                 }
  261.             }
  262.             if (badDrag && badDragZoom)
  263.                 BMgrZoomRect (&dstRect, &srcRect);
  264.         }
  265.     }
  266.  
  267.     /*
  268.      * Mouse was not clicked in a donor.  If it was clicked in a receptor,
  269.      * a glob may have been dragged back to the donor, or to another
  270.      * receptor, so get ready to process possible unglue or inter-receptor
  271.      * transaction.  Can quit early if no receptor set was given
  272.      * (rSet = nil) or no inter-receptor or unglue transactions are allowed.
  273.      */
  274.     else if (rSet != nil && (ungluePerm || xferPerm || swapPerm || dupPerm))
  275.     {
  276.         if (FindBlob (thePoint, rSet, &r1) == inDragBlob && BGlob (r1) != nil)
  277.         {
  278.             if (bcAdvise != nil && (*bcAdvise) (advRClick, r1) == false)
  279.                 return;        /* advisory says "quit" */
  280.  
  281.             /*
  282.              * If the mouse is released without being moved, then save the click
  283.              * info, since it might be the first click of a double-click.  There's
  284.              * no need to check any further for a possible transaction since the
  285.              * glob wasn't dragged anywhere.
  286.              */
  287.             if ((dragDelta = DTrackBlob (r1, inDragBlob, thePoint)) == 0L)
  288.             {
  289.                 lastWhen = t;        /* save click info for possible */
  290.                 lastWhere = thePt;    /* double-click next time */
  291.             }
  292.             else if (dragDelta != badDragResult)
  293.             {
  294.                 thePoint.h += LoWord (dragDelta);
  295.                 thePoint.v += HiWord (dragDelta);
  296.  
  297.                 /*
  298.                  * Was the glob dragged back to it's owner?  If so, unglue it if
  299.                  * unglue permission is on.  Can't use TestBlob, 'cause that'll
  300.                  * be false if the donor is dimmed.
  301.                  */
  302.                 d = BGlob (r1);
  303.                 if (ungluePerm && BlobActive (d)
  304.                         && (PtInRgn (thePoint, (**d).dragRgn)
  305.                             || PtInRgn (thePoint, (**d).statRgn)))
  306.                 {
  307.                     if (bcAdvise == nil || (*bcAdvise) (advUnglue, r1))
  308.                     {
  309.                         db1 = d;
  310.                         rb1 = r1;
  311.                         bcResult = bcUnglue;
  312.                         UnglueGlob (r1);    /* no zooming */
  313.                     }
  314.                     return;
  315.                 }
  316.  
  317.                 srcRect = dstRect = BDragBox (r1);
  318.                 OffsetRect (&dstRect, LoWord (dragDelta), HiWord (dragDelta));
  319.  
  320.                 if (FindReceptor (thePoint, rSet, &r2) && r1 != r2)
  321.                 {
  322.  
  323.                     /*
  324.                      * Now know where the glob was dragged, so possibly have some kind
  325.                      * of inter-receptor transaction.  If replaces aren't allowed, then
  326.                      * don't continue unless the receptor that was dragged to has no glob.
  327.                      * Otherwise do whichever of swapping, duplicating or transferring is
  328.                      * allowed.  Precedence is in that order if more than one of them is
  329.                      * allowed.
  330.                      */
  331.                     rb1 = r1;            /* set cast now, but they */
  332.                     rb2 = r2;            /* won't be meaningful if */
  333.                     db1 = BGlob (r1);    /* no transaction is performed */
  334.                     db2 = BGlob (r2);
  335.                     if (db2 != nil && swapPerm)
  336.                     {
  337.                         if (bcAdvise == nil || (*bcAdvise) (advSwap, r2))
  338.                         {
  339.                             SwapGlob (r1, r2);
  340.                             bcResult = bcSwap;
  341.                             badDrag = false;
  342.                         }
  343.                     }
  344.                     else if (dupPerm)
  345.                     {
  346.                         if (CanGlue (db1))
  347.                         {
  348.                             if (bcAdvise == nil || (*bcAdvise) (advDup, r2))
  349.                             {
  350.                                 DupGlob (r1, r2);    /* duplicate blob */
  351.                                 bcResult = bcDup;
  352.                                 badDrag = false;
  353.                             }
  354.                         }
  355.                     }
  356.                     else if (xferPerm)
  357.                     {
  358.                         if (bcAdvise == nil || (*bcAdvise) (advXfer, r2))
  359.                         {
  360.                             TransferGlob (r1, r2);    /* transfer blob */
  361.                             bcResult = bcXfer;
  362.                             badDrag = false;
  363.                         }
  364.                     }
  365.                 }
  366.                 if (badDrag && badDragZoom)
  367.                     BMgrZoomRect (&dstRect, &srcRect);
  368.             }
  369.         }
  370.     }
  371. }
  372.  
  373.  
  374. /*
  375.  * Result result code of last call to BlobClick.
  376.  */
  377.  
  378. pascal short
  379. BClickResult (void)
  380. {
  381.     return (bcResult);
  382. }
  383.  
  384.  
  385. /*
  386.  * Result cast of characters involved in last call to BlobClick().
  387.  */
  388.  
  389. pascal void
  390. BClickCast (BlobHandle *d1, BlobHandle *d2, BlobHandle *r1, BlobHandle *r2)
  391. {
  392.     *d1 = db1;
  393.     *d2 = db2;
  394.     *r1 = rb1;
  395.     *r2 = rb2;
  396. }
  397.