home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / Tool Chest / Development Platforms / AppsToGo / AppsToGo.src / DTS.Lib / CIconControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-18  |  12.4 KB  |  464 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         cciconcontrol.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. /* CIcon control theory of operation:
  20. **
  21. ** The CIcon control stores the id passed to it in the refCon field of the control.
  22. ** When the defProc is called, it draws the 'cicn' smartly using the GWLayers CIcon
  23. ** utilities.  (These utilities can draw the 'cicn', even if PlotCIcon isn't available.)
  24. **
  25. ** When the CIcon control is hit, it draws that 'cicn' number plus 1.  So, if your id
  26. ** for the control is 1000, 'cicn' #1000 is the up-state of the button, and #1001 is
  27. ** the down state.  It doesn't just invert the base 'cicn', as that doesn't allow for
  28. ** some effects, such as buttons that push in when clicked on.
  29. **
  30. ** If there is no base+1 'cicn', then the base 'cicn' is inverted for hilite==1 state.
  31. **
  32. ** For hilite==255, if there is a base+2 'cicn', it is drawn, otherwise the base 'cicn' is used.
  33. **
  34. ** base, base+1, and base+2 are slight simplifications.  The actual case is base, base+n, base+n+n,
  35. ** where n is the number of 'cicn's necessary to tile the control rect.
  36. ** 'cicn' #base is upper-left, #base+1 is just to the right, etc.
  37. **
  38. ** A variant usage of the CIcon control is a family of buttons.  Instead of tracking
  39. ** the single CIcon control, and having it revert back to the unclicked state after
  40. ** release, this variation is used for buttons that push in and stick.
  41. **
  42. ** The below rules apply for variant #1 of the CIcon control:
  43. **
  44. ** • The user clicks on a CIcon control.  As usual, the hilite==1 state is displayed.
  45. ** • In addition to the above, any CIcon control in the window with the same family
  46. **   is redrawn in hilite==0 state, and it's value is turned off.  The CIcon control
  47. **   that was clicked on has it's value immediately set to 1.
  48. ** • Any variant #1 CIcon control when drawn in hilite==0 state draws as if it is in
  49. **   hilite==1 state.  This means that once the control is clicked on, it's stuck in
  50. **   hilite==1 state.
  51. ** • The hilite==255 rules still apply.
  52. ** • To allow for a value of 1 to be stored, the max value for the control has to be
  53. **   at least 1.
  54. ** • A third state for the control is optional.  The third state is for double-clicking
  55. **   the control down.  Many applications want to make a tool either a one-shot use
  56. **   or permanent use tool.  By allowing double-clicking on the CIcon control, we can
  57. **   accomplish this.
  58. ** • The value for a double-clicked CIcon control is 2.  This indicates to the
  59. **   control that it should draw the 'cicn' #base+n+n.
  60. ** • To allow for a value of 2, the max value of the control has to be at least 2.
  61. ** • The max value for the CIcon control is used as the family number.  The family number
  62. **   is used to determine which other CIcon control to turn off when this control is
  63. **   clicked down.  Due to this other usage of the max value, bit-0 of the max value
  64. **   is used to determine if double-clicking is allowed.
  65. **       odd  family number (max value), single-clicking only.
  66. **       even family number (max value), double-clicking feature enabled.
  67. */
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72.  
  73.  
  74.  
  75. #ifndef __CICONCONTROL__
  76. #include "CIconControl.h"
  77. #endif
  78.  
  79. #ifndef __CONTROLS__
  80. #include <Controls.h>
  81. #endif
  82.  
  83. #ifndef __DTSLib__
  84. #include "DTS.Lib.h"
  85. #endif
  86.  
  87. #ifndef __ERRORS__
  88. #include <Errors.h>
  89. #endif
  90.  
  91. #ifndef __GWLAYERS__
  92. #include "GWLayers.h"
  93. #endif
  94.  
  95. #ifndef __MEMORY__
  96. #include <Memory.h>
  97. #endif
  98.  
  99. #ifndef __PACKAGES__
  100. #include <Packages.h>
  101. #endif
  102.  
  103. #ifndef __RESOURCES__
  104. #include <Resources.h>
  105. #endif
  106.  
  107. #ifndef __STRINGUTILS__
  108. #include "StringUtils.h"
  109. #endif
  110.  
  111. #ifndef __UTILITIES__
  112. #include "Utilities.h"
  113. #endif
  114.  
  115.  
  116.  
  117. /*****************************************************************************/
  118.  
  119.  
  120.  
  121. typedef struct cdefRsrcJMP {
  122.     long    moveInst;
  123.     long    jsrInst;
  124.     short    jmpInst;
  125.     long    jmpAddress;
  126. } cdefRsrcJMP;
  127. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  128.  
  129. typedef struct CbtnInfo {
  130.     short        resIconID;
  131.     short        hicons, vicons;
  132.     short        variant;
  133. } CbtnInfo;
  134. typedef CbtnInfo *CbtnInfoPtr, **CbtnInfoHndl;
  135.  
  136.  
  137.  
  138. /*****************************************************************************/
  139.  
  140.  
  141.  
  142. short    gCIconCtl = rCIconCtl;
  143.  
  144. extern ControlHandle    gWhichCtlHit;
  145. extern Boolean            gWhichCtlDbl;
  146. extern Boolean            gWhichCtlTracking;
  147.  
  148. static pascal long        CCIconCtl   (short varCode, ControlHandle ctl, short msg, long parm);
  149. static pascal void        CCIconAction(ControlHandle ctl, short part);
  150. static cdefRsrcJMPHndl    gCDEF;
  151.  
  152.  
  153.  
  154. /*****************************************************************************/
  155. /*****************************************************************************/
  156. /*****************************************************************************/
  157.  
  158.  
  159.  
  160. #pragma segment Controls
  161. static pascal long    CCIconCtl(short varCode, ControlHandle ctl, short msg, long parm)
  162. {
  163.     Rect                    viewRct, r;
  164.     short                    resIconID;
  165.     CbtnInfoHndl            info;
  166.     short                    hicons, vicons, i, h, v, variant;
  167.     short                    thisHilite, keepHilite;
  168.     CIconHandle                icon;
  169.     ControlHandle            cc;
  170.     WindowPtr                ww;
  171.  
  172.     if (!(info = (CbtnInfoHndl)(*ctl)->contrlRfCon)) return(0);
  173.  
  174.     viewRct    = (*ctl)->contrlRect;
  175.     thisHilite = (*ctl)->contrlHilite;
  176.     variant    = (*info)->variant;
  177.  
  178.     switch (msg) {
  179.  
  180.         case drawCntl:
  181.  
  182.             resIconID = (*info)->resIconID;
  183.             hicons    = (*info)->hicons;
  184.             vicons    = (*info)->vicons;
  185.  
  186.             if (variant == 1) {
  187.                 if (thisHilite < 2)
  188.                     thisHilite = (*ctl)->contrlValue;
  189.                 if (thisHilite == 2)
  190.                     thisHilite = 255;
  191.             }
  192.  
  193.             switch (thisHilite) {
  194.                 case 1:
  195.                 case 255:
  196.                     i = (thisHilite == 1) ? (resIconID + vicons * hicons) :
  197.                                             (resIconID + 2 * vicons * hicons);
  198.                     if (!Get1Resource('cicn', i)) i = resIconID;
  199.                     if (Get1Resource('cicn', i)) {
  200.                         (*info)->resIconID = i;
  201.                         keepHilite = (*ctl)->contrlHilite;
  202.                         (*ctl)->contrlHilite = 99;
  203.                         CCIconCtl(varCode, ctl, msg, parm);
  204.                         (*ctl)->contrlHilite = keepHilite;
  205.                         (*info)->resIconID = resIconID;
  206.                         if (thisHilite == 1)
  207.                             if (i == resIconID)
  208.                                 InvertRect(&viewRct);
  209.                                     /* If there is no hilite==1 'cicn', invert the base 'cicn'. */
  210.                     }
  211.                     break;
  212.                 default:
  213.                     if (!Get1Resource('cicn', resIconID)) {
  214.                         EraseRect(&viewRct);
  215.                         FrameRect(&viewRct);
  216.                         break;
  217.                     }
  218.                     for (i = v = 0; v < vicons;) {
  219.                         for (h = 0; h < hicons; ++i) {
  220.                             if (icon = ReadCIcon(resIconID + i)) {
  221.                                 r.top    = viewRct.top + 64 * v;
  222.                                 r.left   = viewRct.left + 64 * h;
  223.                                 r.bottom = r.top  + 64;
  224.                                 r.right  = r.left + 64;
  225.                                 if (++v == vicons)
  226.                                     r.bottom = viewRct.bottom;
  227.                                 if (++h == hicons)
  228.                                     r.right = viewRct.right;
  229.                                 DrawCIcon(icon, r);
  230.                                 KillCIcon(icon);
  231.                             }
  232.                         }
  233.                     }
  234.                     break;
  235.             }
  236.             break;
  237.  
  238.         case testCntl:
  239.  
  240.             if ((*ctl)->contrlHilite == 255) return(0);
  241.                 /* Control disabled, so no click.  (We probably don't get called in this case. */
  242.  
  243.             if (variant != 1) return(PtInRect(*(Point *)&parm, &viewRct));
  244.                 /* Don't make user call WhichControl unless the variant
  245.                 ** is for double-clicking.  (Then it has to be called.) */
  246.  
  247.             if (ctl != gWhichCtlHit) return(0);
  248.                 /* WhichControl already found the control hit.  Unless it is the one
  249.                 ** found by WhichControl, consider it unhit. */
  250.  
  251.             if (gWhichCtlTracking) return(1);
  252.                 /* We already handled it, but the control manager is insistent. */
  253.  
  254.             if (!(*ctl)->contrlValue) {            /* Turn off any other controls in this family. */
  255.                 ww = (*ctl)->contrlOwner;
  256.                 for (cc = nil; cc = CCIconNext(ww, cc, 1, true);) {
  257.                     if (cc != ctl) {
  258.                         if (((*cc)->contrlMax & 0xFFF0) == ((*ctl)->contrlMax & 0xFFF0)) {
  259.                             if ((*cc)->contrlValue) {
  260.                                 (*cc)->contrlValue  = 0;
  261.                                 (*cc)->contrlHilite = 0;
  262.                                 CCIconCtl(varCode, cc, drawCntl, 0);
  263.                             }
  264.                         }
  265.                     }
  266.                 }
  267.             }
  268.  
  269.             (*ctl)->contrlValue = 1;
  270.             if (gWhichCtlDbl)                            /* If user double-clicked... */
  271.                 if (!((*ctl)->contrlMax & 0x01))        /* If double-clicking allowed... */
  272.                     (*ctl)->contrlValue = 2;
  273.  
  274.             CCIconCtl(varCode, ctl, drawCntl, 0);
  275.  
  276.             gWhichCtlTracking = true;
  277.             return(1);
  278.  
  279.             break;
  280.  
  281.         case calcCRgns:
  282.         case calcCntlRgn:
  283.             if (msg == calcCRgns)
  284.                 parm &= 0x00FFFFFF;
  285.             RectRgn((RgnHandle)parm, &viewRct);
  286.             break;
  287.  
  288.         case initCntl:
  289.             break;
  290.  
  291.         case dispCntl:
  292.             DisposeHandle((Handle)(*ctl)->contrlRfCon);
  293.             break;
  294.  
  295.         case posCntl:
  296.             break;
  297.  
  298.         case thumbCntl:
  299.             break;
  300.  
  301.         case dragCntl:
  302.             break;
  303.  
  304.         case autoTrack:
  305.             break;
  306.     }
  307.  
  308.     return(0);
  309. }
  310.  
  311.  
  312.  
  313. /*****************************************************************************/
  314.  
  315.  
  316.  
  317. #pragma segment Controls
  318. ControlHandle    CCIconNew(WindowPtr window, Rect *r, Boolean vis, short val, short min, short max,
  319.                           short viewID, short refcon)
  320. {
  321.     WindowPtr        oldPort;
  322.     Rect            viewRct;
  323.     Boolean            err;
  324.     ControlHandle    viewCtl;
  325.     CbtnInfoHndl    info;
  326.     short            hicons, vicons;
  327.  
  328.     GetPort(&oldPort);
  329.     SetPort(window);
  330.  
  331.     viewRct = *r;
  332.     viewCtl = nil;
  333.     info    = nil;
  334.  
  335.     err = false;
  336.  
  337.     if (!gCDEF) {
  338.         gCDEF = (cdefRsrcJMPHndl)GetResource('CDEF', (viewID / 16));
  339.         if (gCDEF) {
  340.             (*gCDEF)->jmpAddress = (long)CCIconCtl;
  341.             FlushInstructionCache();    /* Make sure that instruction caches don't kill us. */
  342.         }
  343.         else err = true;
  344.     }
  345.  
  346.     if (!err) {
  347.         info = (CbtnInfoHndl)NewHandleClear(sizeof(CbtnInfo));
  348.         if (info) {
  349.             hicons = (viewRct.right - viewRct.left - 1) / 64 + 1;
  350.             if (hicons < 1) hicons = 1;
  351.             vicons = (viewRct.bottom - viewRct.top - 1) / 64 + 1;
  352.             if (vicons < 1) vicons = 1;
  353.     
  354.             (*info)->resIconID = refcon;
  355.             (*info)->hicons    = hicons;
  356.             (*info)->vicons    = vicons;
  357.             (*info)->variant   = (viewID & 0x0F);
  358.         }
  359.         else err = true;
  360.     }
  361.  
  362.     if (!err) {
  363.         viewCtl = NewControl(window, &viewRct, "\p", vis, val, min, max, viewID, (long)info);
  364.         if (viewCtl)
  365.             SetCtlAction(viewCtl, (ProcPtr)CCIconAction);
  366.         else
  367.             err = true;
  368.     }
  369.  
  370.     SetPort(oldPort);
  371.  
  372.     if (err) {        /* Oops.  Somebody wasn't happy. */
  373.         if (info)
  374.             DisposeHandle((Handle)info);
  375.                 /* viewCtl exists only if no error, so we just have to possibly clean up info. */
  376.     }
  377.  
  378.     return(viewCtl);
  379. }
  380.  
  381.  
  382.  
  383. /*****************************************************************************/
  384.  
  385.  
  386.  
  387. #pragma segment Controls
  388. ControlHandle    CCIconNext(WindowPtr window, ControlHandle ctl, short dir, Boolean justActive)
  389. {
  390.     ControlHandle    nextCtl, priorCtl;
  391.  
  392.     if (!window) return(nil);
  393.     if (!gCDEF)  return(nil);
  394.  
  395.     if (dir > 0) {
  396.         if (!ctl)
  397.             ctl = ((WindowPeek)window)->controlList;
  398.         else
  399.             ctl = (*ctl)->nextControl;
  400.         while (ctl) {
  401.             if ((!justActive) || ((*ctl)->contrlVis)) {
  402.                 if ((!justActive) || ((*ctl)->contrlHilite != 255)) {
  403.                     if (*(*ctl)->contrlDefProc == *(Handle)gCDEF)
  404.                         return(ctl);
  405.                             /* The handle may be locked, which means that the hi-bit
  406.                             ** may be on, thus invalidating the compare.  Dereference the
  407.                             ** handles to get rid of this possibility. */
  408.                 }
  409.             }
  410.             ctl = (*ctl)->nextControl;
  411.         }
  412.         return(ctl);
  413.     }
  414.  
  415.     nextCtl = ((WindowPeek)window)->controlList;
  416.     for (priorCtl = nil; ;nextCtl = (*nextCtl)->nextControl) {
  417.         if ((!nextCtl) || (nextCtl == ctl)) return(priorCtl);
  418.         if ((!justActive) || ((*nextCtl)->contrlVis)) {
  419.             if ((!justActive) || ((*nextCtl)->contrlHilite != 255)) {
  420.                 if (*(*ctl)->contrlDefProc == *(Handle)gCDEF)
  421.                     priorCtl = nextCtl;
  422.                         /* The handle may be locked, which means that the hi-bit
  423.                         ** may be on, thus invalidating the compare.  Dereference the
  424.                         ** handles to get rid of this possibility. */
  425.             }
  426.         }
  427.     }
  428. }
  429.  
  430.  
  431.  
  432. /*****************************************************************************/
  433.  
  434.  
  435.  
  436. #pragma segment Controls
  437. Boolean    IsCIconCtl(ControlHandle ctl)
  438. {
  439.     if (*(*ctl)->contrlDefProc == *(Handle)gCDEF) return(true);
  440.         /* The handle may be locked, which means that the hi-bit
  441.         ** may be on, thus invalidating the compare.  Dereference the
  442.         ** handles to get rid of this possibility. */
  443.     return(false);
  444. }
  445.  
  446.  
  447.  
  448. /*****************************************************************************/
  449.  
  450.  
  451.  
  452. static pascal void    CCIconAction(ControlHandle ctl, short part)
  453. {
  454.     static short    lastPart = 0;
  455.  
  456.     if (lastPart != part) {
  457.         lastPart = part;
  458.         CCIconCtl(0, ctl, drawCntl, part);
  459.     }
  460. }
  461.  
  462.  
  463.  
  464.