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

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        TRectObj.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1992-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. /* See the files "=How to write your app" and "=Using TreeObj.c" for information
  20. ** on this function. */
  21.  
  22. /* This file implements the messages for the rect object. */
  23.  
  24.  
  25.  
  26. /*****************************************************************************/
  27.  
  28.  
  29.  
  30. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  31. #include "App.protos.h"        /* Get the prototypes for the application.        */
  32.  
  33. #ifndef __OSEVENTS__
  34. #include <OSEvents.h>
  35. #endif
  36.  
  37. #ifndef __OSUTILS__
  38. #include <OSUtils.h>
  39. #endif
  40.  
  41. #ifndef __QUICKDRAW__
  42. #include <Quickdraw.h>
  43. #endif
  44.  
  45. #ifndef __STRING__
  46. #include <String.h>
  47. #endif
  48.  
  49. #ifndef __TREEOBJ2__
  50. #include "TreeObj2.h"
  51. #endif
  52.  
  53. #ifndef __UTILITIES__
  54. #include "Utilities.h"
  55. #endif
  56.  
  57.  
  58.  
  59. /**********************************************************************/
  60.  
  61.  
  62.  
  63. RGBColor    gBorderColor  = {0, 0, 0};
  64. RGBColor    gContentColor = {0xFFFF, 0xFFFF, 0xFFFF};
  65. short        gPenHeight = 1;
  66. short        gPenWidth  = 1;
  67.  
  68.  
  69.  
  70. /**********************************************************************/
  71.  
  72.  
  73.  
  74. #pragma segment DrawObjects
  75. long    TRectObj(TreeObjHndl hndl, short message, long data)
  76. {
  77.     TreeObjHndl    root, shndl;
  78.     Rect        rct, oldRct, *rptr, grabber;
  79.     short        hit, fileRefNum, dx1, dx2, dy1, dy2, h, v, ptype;
  80.     short        fv, fh, flip, noDraw, w;
  81.     Point        where, begMouse, curMouse, offset;
  82.     Boolean        selected;
  83.     EventRecord    option;
  84.     ClickInfo    *click;
  85.     RgnHandle    rgn, accumRgn;
  86.     OSErr        err;
  87.     RGBColor    rgb, rgb2;
  88. #if VH_VERSION
  89.     char        *cptr;
  90. #endif
  91.  
  92.     switch (message) {
  93.         case INITMESSAGE:
  94.                 /* This is called either with a sub-message of CREATEINIT or WINDOWINIT.
  95.                 ** The sub-message CREATEINIT is sent in when the object is initially created.
  96.                 ** The sub-message WINDOWINIT is sent in when the object belongs to a document
  97.                 ** that was just assigned to a window.  Note that the object could be created
  98.                 ** into a document that already has a window.  In this case, only the
  99.                 ** CREATEINIT sub-message will be received.  It is the object's responsibility
  100.                 ** to determine if the document has a window when CREATEINIT is called.
  101.                 ** An additional possible sub-message is NOWINDOWINIT.  In this case, the
  102.                 ** document is being disconnected from a window, and therefore objects have
  103.                 ** to convert back to their non-window state.  Both the WINDOWINIT and
  104.                 ** NOWINDOWINIT sub-messages would be application-specific, and sending
  105.                 ** the sub-messages to the objects would be the application's responsibility. */
  106.             mDerefCommon(hndl)->penHeight    = gPenHeight;
  107.             mDerefCommon(hndl)->penWidth     = gPenWidth;
  108.             mDerefCommon(hndl)->borderColor  = gBorderColor;
  109.             mDerefCommon(hndl)->content      = true;
  110.             mDerefCommon(hndl)->contentColor = gContentColor;
  111.             break;
  112.  
  113.         case FREEMESSAGE:
  114.             if (mDerefCommon(hndl)->selected) {
  115.                 root = GetRootHndl(hndl);
  116.                 if ((*root)->type == ROOTOBJ)
  117.                     mDerefRoot(root)->numSelected--;
  118.                         /* Here the root object can be either the document root
  119.                         ** or the undo root.  The FREEOBJMESSAGE is due to the
  120.                         ** object being disposed of.  It may be being disposed of
  121.                         ** from either the document or undo side.  If it is being
  122.                         ** disposed of out of the undo side, we don't care about
  123.                         ** the selection status.  If it is being disposed of out
  124.                         ** of the document side, then we do care, as we will have
  125.                         ** one less selected object.  The numSelected count needs
  126.                         ** to reflect this. */
  127.             }
  128.             break;
  129.  
  130.         case COPYMESSAGE:
  131.                 /* CopyOneChild was called (indirectly) and an object has been
  132.                 ** cloned.  At this point the data area has already been copied.
  133.                 ** If the data area holds references to other handles, these
  134.                 ** handles need to be copied so that the object has its own copies.
  135.                 ** Since the data was copied from the original child, the fields
  136.                 ** holding references to handles actually contain the same reference
  137.                 ** that the original child contains.  To create unique handles for
  138.                 ** the copy, this object should pass itself an INITOBJMESSAGE to
  139.                 ** initially create unique handles for the copy.
  140.                 ** The data parameter for this message is the handle of the object
  141.                 ** that was copied.  It is possible that the copy won't be an exact
  142.                 ** copy, and that there should be some differences between the
  143.                 ** original and the copy.  By having a reference to the original,
  144.                 ** these situations can be resolved. */
  145.             break;
  146.  
  147.         case UNDOMESSAGE:
  148.             root  = GetRootHndl(hndl);
  149.             if ((*root)->type == ROOTOBJ) {
  150.                 ptype = (*((*hndl)->parent))->type;
  151.                 switch (data) {
  152.                     case UNDOFROMDOC:
  153.                         if (mDerefCommon(hndl)->selected == true) {
  154.                             mDerefCommon(hndl)->selected = false;
  155.                             mDerefRoot(root)->numSelected--;
  156.                         }        /* If a selected object is moving from the document into the */
  157.                         break;    /* undo, reflect this in the numSelected count in the root.  */
  158.  
  159.                     case UNDOTODOC:
  160.                         mDerefCommon(hndl)->selected = false;
  161.                         if (ptype != GROUPOBJ) {
  162.                             mDerefCommon(hndl)->selected = true;
  163.                             mDerefRoot(root)->numSelected++;
  164.                         }        /* An object moving into the document needs to be selected
  165.                                 ** so the user can see what changed.  The select status while
  166.                                 ** in the undo doesn't matter, as the user can't see what is in
  167.                                 ** the undo. */
  168.                         break;
  169.                 }
  170.             }
  171.             break;
  172.  
  173.         case CONVERTMESSAGE:
  174.                 /* This is called to convert any data that loses meaning when saved.
  175.                 ** It is called with a TOFILE or a FROMFILE sub-message.
  176.                 **
  177.                 ** For the CONVERTTOID sub-message:
  178.                 **
  179.                 ** If this object has any references to handles elsewhere in the document,
  180.                 ** these references lose meaning when saved to disk.  When the file is
  181.                 ** read in, the handle references will not coorelate to what is currently
  182.                 ** in ram.
  183.                 ** Prior to starting a file save, all of the objects in the tree are
  184.                 ** assigned sequential id numbers.  You can replace handle references
  185.                 ** with the 4-byte treeID.  To convert the handle to a treeID, just
  186.                 ** do something like the following:
  187.                 **     Hndl2ID(&DerefMyObject(hndl)->thingToConvert);
  188.                 **
  189.                 ** For the CONVERTTOHNDL sub-message:
  190.                 **
  191.                 ** Once the file is opened and completely loaded into ram, there may be
  192.                 ** objects that have references to elsewhere in the file that were stored
  193.                 ** as treeID's.  These need to be converted back to handle references.
  194.                 ** To do this, do something like the below:
  195.                 **      ID2Hndl(hndl, &DerefMyObject(hndl)->thingToConvert);
  196.                 */
  197.             break;
  198.  
  199.         case FREADMESSAGE:
  200.             fileRefNum = data;
  201.             err = ReadTreeObjData(hndl, fileRefNum);
  202.             if (!err)
  203.                 mDerefCommon(hndl)->selected = false;
  204.             return(err);
  205.             break;
  206.  
  207.         case FWRITEMESSAGE:
  208.             fileRefNum = data;
  209.             return(WriteTreeObjData(hndl, fileRefNum));
  210.             break;
  211.  
  212.         case HREADMESSAGE:
  213.             err = HReadTreeObjData(hndl, (Handle)data);
  214.             if (!err)
  215.                 mDerefCtl(hndl)->selected = false;
  216.             return(err);
  217.             break;
  218.  
  219.         case HWRITEMESSAGE:
  220.             return(HWriteTreeObjData(hndl, (Handle)data));
  221.             break;
  222.  
  223.         case HITTESTMESSAGE:
  224.             click = (ClickInfo *)data;
  225.             where = click->localEvent.where;
  226.             hit   = 0;
  227.             switch (click->message) {
  228.                 case HITTESTOBJ:
  229.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  230.                     if (PtInRect(where, &rct)) {
  231.                         rgn = (RgnHandle)DoTreeObjMethod(hndl, GETRGNMESSAGE, 0);
  232.                         if (PtInRgn(where, rgn))
  233.                             hit = -1;
  234.                         DisposeRgn(rgn);
  235.                     }
  236.                     break;
  237.                 case HITTESTGRABBER:
  238.                     rct = mDerefCommon(hndl)->rect;
  239.                     if (mDerefCommon(hndl)->selected) {
  240.                         grabber = rct;
  241.                         InsetRect(&grabber, -4, -4);
  242.                         if (PtInRect(where, &grabber)) {
  243.                             GetMouse(&curMouse);
  244.                             for (;;) {
  245.                                 if (where.v < (rct.top + 4)) {
  246.                                     if (where.h <  (rct.left + 4) ) {
  247.                                         hit = 1;
  248.                                         flip = (VFLIPOBJ | HFLIPOBJ);
  249.                                         where.h  = rct.right;
  250.                                         where.v  = rct.bottom;
  251.                                         offset.h = rct.left - curMouse.h;
  252.                                         offset.v = rct.top  - curMouse.v;
  253.                                         break;
  254.                                     }
  255.                                     if (where.h >= (rct.right - 4)) {
  256.                                         hit = 2;
  257.                                         flip = VFLIPOBJ;
  258.                                         where.h  = rct.left;
  259.                                         where.v  = rct.bottom;
  260.                                         offset.h = rct.right - curMouse.h;
  261.                                         offset.v = rct.top   - curMouse.v;
  262.                                         break;
  263.                                     }
  264.                                 }
  265.                                 if (where.v >= (rct.bottom - 4)) {
  266.                                     if (where.h <  (rct.left + 4) ) {
  267.                                         hit = 3;
  268.                                         flip = HFLIPOBJ;
  269.                                         where.h  = rct.right;
  270.                                         where.v  = rct.top;
  271.                                         offset.h = rct.left   - curMouse.h;
  272.                                         offset.v = rct.bottom - curMouse.v;
  273.                                         break;
  274.                                     }
  275.                                     if (where.h >= (rct.right - 4)) {
  276.                                         hit = 4;
  277.                                         flip = 0;
  278.                                         where.h  = rct.left;
  279.                                         where.v  = rct.top;
  280.                                         offset.h = rct.right  - curMouse.h;
  281.                                         offset.v = rct.bottom - curMouse.v;
  282.                                         break;
  283.                                     }
  284.                                 }
  285.                                 break;
  286.                             }
  287.                         }
  288.                     }
  289.                     break;
  290.                 case CANBETARGET:
  291.                     return(false);
  292.                     break;
  293.                 case CANTAKEKEYS:
  294.                     return(false);
  295.                     break;
  296.             }
  297.             if (hit) {
  298.                 for (; (*(root = (*hndl)->parent))->type != ROOTOBJ; hndl = root) {};
  299.                     /* Group objects can not be hit directly.  Only the base
  300.                     ** objects can be hit.  However, if a base object is hit,
  301.                     ** we want to return the top-most group object that
  302.                     ** contains it.  Walk the tree up until the root object
  303.                     ** is hit.  The object below this is top-most group object,
  304.                     ** or the base object doesn't belong to a group. */
  305.                 if (click->message == HITTESTGRABBER) {
  306.                     if (mDerefRoot(root)->numSelected == 1) {
  307.                         click->localEvent.where = where;
  308.                         click->offset           = offset;
  309.                         click->oldFlip = click->newFlip = flip;
  310.                     }
  311.                     else hit = 0;
  312.                 }
  313.             }
  314.             if (!hit)
  315.                 hndl = nil;
  316.             click->grabber = hit;
  317.             return((long)hndl);
  318.             break;
  319.  
  320.         case GETRGNMESSAGE:
  321.             rgn      = NewRgn();
  322.             accumRgn = (RgnHandle)data;
  323.             if (accumRgn)
  324.                 if (GetHandleSize((Handle)accumRgn) > 10000)
  325.                     return((long)rgn);
  326.             rct = mDerefCommon(hndl)->rect;
  327.             RectRgn(rgn, &rct);
  328.             if (accumRgn)
  329.                 UnionRgn(rgn, accumRgn, accumRgn);
  330.             return((long)rgn);
  331.             break;
  332.  
  333.         case GETOBJRECTMESSAGE:
  334.         case GETBBOXMESSAGE:
  335.             rptr  = (Rect *)data;
  336.             *rptr = mDerefCommon(hndl)->rect;
  337.             break;
  338.  
  339.         case SETOBJRECTMESSAGE:
  340.             rptr = (Rect *)data;
  341.             mDerefCommon(hndl)->rect = *rptr;
  342.             break;
  343.  
  344.         case SECTOBJRECTMESSAGE:
  345.             rptr = (Rect *)data;
  346.             SectRect(rptr, &(mDerefCommon(hndl)->rect), &rct);
  347.             if (!EmptyRect(&rct)) return(true);
  348.             break;
  349.  
  350.         case DRAWMESSAGE:
  351.             rct = mDerefCommon(hndl)->rect;
  352.             h   = mDerefCommon(hndl)->penHeight;
  353.             w   = mDerefCommon(hndl)->penWidth;
  354.             PenSize(w, h);
  355.             switch (data) {
  356.                 case DRAWOBJ:
  357.                     if (gQDVersion)
  358.                         GetForeColor(&rgb);
  359.                     ForeColor(whiteColor);
  360.                     if (gQDVersion) {
  361.                         rgb2 = mDerefRect(hndl)->contentColor;
  362.                         RGBForeColor(&rgb2);
  363.                     }
  364.                     PaintRect(&rct);
  365.                     ForeColor(blackColor);
  366.                     if (gQDVersion) {
  367.                         rgb2 = mDerefRect(hndl)->borderColor;
  368.                         RGBForeColor(&rgb2);
  369.                     }
  370.                     FrameRect(&rct);
  371.                     if (gQDVersion)
  372.                         RGBForeColor(&rgb);
  373.                     break;
  374.                 case ERASEOBJ:
  375.                     EraseRect(&rct);
  376.                     break;
  377.                 case DRAWSELECT:
  378.                     if (mDerefCommon(hndl)->selected) {
  379.                         for (h = v = 1; (h > -1); h -= (v ^= 1)) {
  380.                             grabber.top  = v ? rct.top  : rct.bottom;
  381.                             grabber.left = h ? rct.left : rct.right;
  382.                             grabber.bottom = (grabber.top  -= 3) + 6;
  383.                             grabber.right  = (grabber.left -= 3) + 6;
  384.                             InvertRect(&grabber);
  385.                         }
  386.                     }
  387.                     break;
  388.                 case DRAWGHOST:
  389.                     PenMode(patXor);
  390.                     FrameRect(&rct);
  391.                     break;
  392.                 case DRAWMASK:
  393.                     FillRect(&rct, (ConstPatternParam)&qd.black);
  394.                     break;
  395.             }
  396.             PenNormal();
  397.             break;
  398.  
  399.         case PRINTMESSAGE:
  400.             DoTreeObjMethod(hndl, DRAWMESSAGE, DRAWOBJ);
  401.             break;
  402.  
  403. #if VH_VERSION
  404.         case VHMESSAGE:
  405.             cptr = ((VHFormatDataPtr)data)->data;
  406.             ccatchr(cptr, 13, 2);
  407.             ccat   (cptr, "$10: TRectObj:");
  408.             ccatchr(cptr, 13, 1);
  409.             ccat   (cptr, "  $00: selected     = ");
  410.             ccatdec(cptr, mDerefRect(hndl)->selected);
  411.             ccatchr(cptr, 13, 1);
  412.  
  413.             rct = mDerefRect(hndl)->rect;
  414.             ccat      (cptr, "  $02: rect         = ($");
  415.             ccatpadhex(cptr, 0, 4, 4, rct.top);
  416.             ccat      (cptr, ",$");
  417.             ccatpadhex(cptr, 0, 4, 4, rct.left);
  418.             ccat      (cptr, ",$");
  419.             ccatpadhex(cptr, 0, 4, 4, rct.bottom);
  420.             ccat      (cptr, ",$");
  421.             ccatpadhex(cptr, 0, 4, 4, rct.right);
  422.             ccat      (cptr, ")");
  423.             ccatchr   (cptr, 13, 1);
  424.             ccat      (cptr, "  $0A: penHeight    = ");
  425.             ccatdec   (cptr, mDerefRect(hndl)->penHeight);
  426.             ccatchr   (cptr, 13, 1);
  427.             ccat      (cptr, "  $0C: penWidth    = ");
  428.             ccatdec   (cptr, mDerefRect(hndl)->penWidth);
  429.             ccatchr   (cptr, 13, 1);
  430.             ccat      (cptr, "  $0E: borderColor  = ($");
  431.             ccatpadhex(cptr, 0, 4, 4, mDerefRect(hndl)->borderColor.red);
  432.             ccat      (cptr, ",$");
  433.             ccatpadhex(cptr, 0, 4, 4, mDerefRect(hndl)->borderColor.green);
  434.             ccat      (cptr, ",$");
  435.             ccatpadhex(cptr, 0, 4, 4, mDerefRect(hndl)->borderColor.blue);
  436.             ccat      (cptr, ")");
  437.             ccatchr   (cptr, 13, 1);
  438.             ccat      (cptr, "  $1$: content      = ($");
  439.             ccatdec   (cptr, mDerefRect(hndl)->content);
  440.             ccatchr   (cptr, 13, 1);
  441.             ccat      (cptr, "  $16: contentColor = ($");
  442.             ccatpadhex(cptr, 0, 4, 4, mDerefRect(hndl)->contentColor.red);
  443.             ccat      (cptr, ",$");
  444.             ccatpadhex(cptr, 0, 4, 4, mDerefRect(hndl)->contentColor.green);
  445.             ccat      (cptr, ",$");
  446.             ccatpadhex(cptr, 0, 4, 4, mDerefRect(hndl)->contentColor.blue);
  447.             ccat      (cptr, ")");
  448.             return(true);
  449.             break;
  450. #endif
  451.  
  452.         case COMPAREMESSAGE:
  453.             return(DefaultEqualTreeObjData(hndl, (TreeObjHndl)data));
  454.             break;
  455.  
  456. /* End of standard messages.  Start of object-specific messages. */
  457.  
  458.         case CLICKMESSAGE:
  459.             click = (ClickInfo *)data;
  460.             switch (click->message) {
  461.                 case CLICKSELECT:
  462.                     for (shndl = hndl; (*(root = (*shndl)->parent))->type != ROOTOBJ; shndl = root) {};
  463.                     selected = mDerefCommon(shndl)->selected;
  464.                     if ((!selected) || (click->localEvent.modifiers & shiftKey)) {
  465.                         if (shndl == hndl)
  466.                             DoTreeObjMethod(hndl, SETSELECTMESSAGE, SELECTTOGGLE);
  467.                                 /* If the object doesn't belong to a group, then we want to change
  468.                                 ** the select status.  If it belongs to a group, the group object
  469.                                 ** is responsible for maintaining the select status. */
  470.                     }
  471.                     break;
  472.                 case CLICKDRAG:
  473.                     OffsetRect(&(mDerefCommon(hndl)->rect), click->offset.h, click->offset.v);
  474.                     break;
  475.             }
  476.             break;
  477.  
  478.         case KEYMESSAGE:
  479.             break;
  480.  
  481.         case SETSELECTMESSAGE:
  482.             noDraw = (data & SELECTNODRAW);
  483.             data  ^= noDraw;
  484.  
  485.             selected = mDerefCommon(hndl)->selected;
  486.             if (selected != data) {        /* The select status is other than desired. */
  487.                 if (!noDraw) {
  488.                     mDerefCommon(hndl)->selected = true;
  489.                     DoTreeObjMethod(hndl, DRAWMESSAGE, DRAWSELECT);
  490.                         /* Force a draw-toggle of the selection grabbers. */
  491.                 }
  492.                 mDerefCommon(hndl)->selected = (selected ^= true);
  493.                 root = GetRootHndl(hndl);
  494.                 if (selected)
  495.                     mDerefRoot(root)->numSelected++;
  496.                 else
  497.                     mDerefRoot(root)->numSelected--;
  498.                         /* Change the global numSelected value to reflect the new
  499.                         ** number of selected objects. */
  500.             }
  501.             break;
  502.  
  503.         case GETSELECTMESSAGE:
  504.             return(mDerefCommon(hndl)->selected);
  505.             break;
  506.  
  507.         case SIZEMESSAGE:
  508.             DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&oldRct);
  509.             click    = (ClickInfo *)data;
  510.             begMouse = click->localEvent.where;
  511.             GetMouse(&curMouse);
  512.             curMouse.h += click->offset.h;
  513.             curMouse.v += click->offset.v;
  514.             dx1 = dx2 = (curMouse.h - begMouse.h);
  515.             dy1 = dy2 = (curMouse.v - begMouse.v);
  516.             if (dx2 < 0)
  517.                 dx2 = -dx2;
  518.             if (dy2 < 0)
  519.                 dy2 = -dy2;
  520.             OSEventAvail(nullEvent, &option);
  521.             if ((*hndl)->type == EXTSELECTOBJ) option.modifiers = 0;
  522.             if (option.modifiers & shiftKey) {
  523.                 if (dx2 > dy2)
  524.                     dx2 = dy2;
  525.                 else
  526.                     dy2 = dx2;
  527.             }
  528.             if (dx1 < 0)
  529.                 dx2 = -dx2;
  530.             if (dy1 < 0)
  531.                 dy2 = -dy2;
  532.             curMouse.h = begMouse.h + dx2;
  533.             curMouse.v = begMouse.v + dy2;
  534.             fv = (curMouse.v < begMouse.v) ? VFLIPOBJ : 0x00;
  535.             fh = (curMouse.h < begMouse.h) ? HFLIPOBJ : 0x00;
  536.             rct.top    = (fv) ? curMouse.v : begMouse.v;
  537.             rct.bottom = (fv) ? begMouse.v : curMouse.v;
  538.             rct.left   = (fh) ? curMouse.h : begMouse.h;
  539.             rct.right  = (fh) ? begMouse.h : curMouse.h;
  540.             mDerefCommon(hndl)->rect = rct;
  541.             click->oldFlip = click->newFlip;
  542.             click->newFlip = (fv | fh);
  543.             return(!EqualRect(&rct, &oldRct));
  544.             break;
  545.  
  546.     }
  547.  
  548.     return(noErr);
  549. }
  550.  
  551.  
  552.  
  553.