home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 2 / MacMania 2.toast / Demo's / Tools&Utilities / Programming / MPS disk 1.0.1 / Chapter 09 / DEF's / Windoid.c < prev   
Encoding:
C/C++ Source or Header  |  1992-05-19  |  15.4 KB  |  486 lines  |  [TEXT/KAHL]

  1. #include "GestaltEqu.h"
  2.  
  3. /*******************************************************************************
  4.  
  5.     Function prototypes. Normally, these would be put in a .h file so that any
  6.     clients of this module could access the exported routines. However, this
  7.     file contains the code for an entirely self-contained WDEF; there _are_ no
  8.     exported routines.
  9.  
  10. *******************************************************************************/
  11.  
  12. void    DoDraw(WindowPeek theWindow, long param);
  13. long    DoHit(WindowPeek theWindow, long param);
  14. void    DoCalcRgns(WindowPeek theWindow);
  15.  
  16. pascal void    DoDrawAll(short depth, short deviceFlags, GDHandle targetDevice,
  17.                 WindowPeek theWindow);
  18. pascal void    ToggleGoAway(short depth, short deviceFlags, GDHandle targetDevice,
  19.                 WindowPeek theWindow);
  20.  
  21. void    DrawGoAwayBox(WindowPeek theWindow, Boolean tracked);
  22.  
  23. void    SyncPorts(void);
  24.  
  25. void    GetContentRect(WindowPeek theWindow, Rect *theRect);
  26. void    GetStrucRect(WindowPeek theWindow, Rect *theRect);
  27. void    GetGoAwayRect(WindowPeek theWindow, Rect *theRect);
  28.  
  29.  
  30. /*******************************************************************************
  31.  
  32.     main
  33.  
  34.     Entry point for the WDEF. Note that, because the WDEF is called by the Mac
  35.     Toolbox, main() is declared as “pascal.” This indicates that the Pascal
  36.     calling convention should be used.
  37.  
  38.     Our entry point doesn’t do much. First, it saves the current port and
  39.     calls SyncPorts(), which switches over to the color window manager port if
  40.     we are running on a color Mac. As a courtesy to all of our drawing
  41.     routines, we then save the pen state. Next, we call a subroutine
  42.     appropriate to the type of operation we are asked to perform. Note that we
  43.     support only wDraw, wHit, and wCalcRgns. We do not support wNew, wDispose,
  44.     wGrow, wDrawGIcon, wZoomIn or wZoomOut. Before leaving, we restore the
  45.     initial Penn State and GrafPort.
  46.  
  47. *******************************************************************************/
  48. pascal long main(    short varCode,            // variation code of the window
  49.                     WindowPeek theWindow,    // window to operate on
  50.                     short message,            // operation to perform
  51.                     long param)                // extra information
  52. {
  53.     long        result;
  54.     GrafPtr        oldPort;
  55.     PenState    oldPenState;
  56.  
  57.     GetPort(&oldPort);
  58.     SyncPorts();
  59.  
  60.     GetPenState(&oldPenState);
  61.  
  62.     result = 0;
  63.     switch (message) {
  64.         case wDraw:
  65.             DoDraw(theWindow, param);
  66.             break;
  67.         case wHit:
  68.             result = DoHit(theWindow, param);
  69.             break;
  70.         case wCalcRgns:
  71.             DoCalcRgns(theWindow);
  72.             break;
  73.     }
  74.  
  75.     SetPenState(&oldPenState);
  76.  
  77.     SetPort(oldPort);
  78.     return result;
  79. }
  80.  
  81.  
  82. /*******************************************************************************
  83.  
  84.     DoDraw
  85.  
  86.     The function that does all of the drawing. What we draw depends on the
  87.     value of “param”, which is passed to us by the Window Manager. If param is
  88.     zero, we draw the entire frame in its standard state. If param is
  89.     wInGoAway, wInZoomIn or wInZoomOut, we have to toggle the state of those
  90.     boxes. We don’t have a zoom box in our window, so we don’t respond to
  91.     wInZoomIn or wInZoomOut.
  92.  
  93.     Our drawing is done through the System 7.0 DeviceLoop() function. This
  94.     function calls a drawing routine for each set of graphics devices that
  95.     have different drawing characteristics. For instance, if we we’re running
  96.     on a Mac with two monitors, one in 1 bit mode and the other in 8 bit mode,
  97.     our drawing routine is called once for each device. On the other hand, if
  98.     both monitors are both in 8 bit mode and are also identical in every other
  99.     way, our drawing routine is called only once.
  100.  
  101. *******************************************************************************/
  102. void DoDraw(WindowPeek theWindow, long param)
  103. {
  104.     DeviceLoopDrawingProcPtr    procToCall;
  105.  
  106.     if (theWindow->visible) {
  107.  
  108.         switch (param) {
  109.             case 0:
  110.                 procToCall = (DeviceLoopDrawingProcPtr) DoDrawAll;
  111.                 break;
  112.             case wInGoAway:
  113.                 procToCall = (DeviceLoopDrawingProcPtr) ToggleGoAway;
  114.                 break;
  115.             default:
  116.                 return;            /* Blow out on anything we don’t recognize. */
  117.         }
  118.         DeviceLoop(    theWindow->strucRgn,    /* Region we’ll be drawing in.    */
  119.                     procToCall,                /* Function to do the drawing.    */
  120.                     (long) theWindow,        /* Data passed to our routine.    */
  121.                     0);                        /* Flags - none used here.        */
  122.     }
  123. }
  124.  
  125.  
  126. /*******************************************************************************
  127.  
  128.     DoHit
  129.  
  130.     Given the point passed to us in param, determine what part of the window
  131.     was hit. The only values we return here are wInContent, wInGoAway,
  132.     wInDrag, and wNoHit.
  133.  
  134.     Take special note of the way we check to see if the user clicked in the
  135.     close box or not. We only check to see if the window has its goAwayFlag
  136.     set or not before we check the mouse click location. In most WDEFs, you’d
  137.     also have to check to see if the window is active or not by checking the
  138.     window’s hilited flag. However, we don’t do so here because this WDEF is
  139.     for floating windows. These windows have a special flavor of always
  140.     acting and looking active. For this reason, we allow clicks on the close
  141.     box even if the window is not the frontmost.
  142.  
  143. *******************************************************************************/
  144. long DoHit(WindowPeek theWindow, long param)
  145. {
  146.     Point    where;
  147.     Rect    tempRect;
  148.  
  149.     where.v = HiWord(param);
  150.     where.h = LoWord(param);
  151.  
  152.     if (PtInRgn(where, theWindow->contRgn)) {
  153.  
  154.         return wInContent;
  155.  
  156.     } else if (PtInRgn(where, theWindow->strucRgn)) {
  157.  
  158.         if (theWindow->goAwayFlag) {
  159.             GetGoAwayRect(theWindow, &tempRect);
  160.             if (PtInRect(where, &tempRect)) {
  161.                 return wInGoAway;
  162.             }
  163.         }
  164.  
  165.         return wInDrag;
  166.     }
  167.     return wNoHit;
  168. }
  169.  
  170.  
  171. /*******************************************************************************
  172.  
  173.     DoCalcRgns
  174.  
  175.     Calculate the content and structure regions for our window. When we are
  176.     called, the window’s contRgn and strucRgn fields have already been
  177.     initialized with handles to empty regions. All we have to do is fill in
  178.     the regions with the appropriate data.
  179.  
  180.     This is easy. For the content region, we just get the content rectangle
  181.     and call QuickDraw’s RectRgn() routine on it. The structure region is a
  182.     little trickier because of the shadow. What we do is get the rectangle for
  183.     the frame, but not including the shadow. We turn that into a region and
  184.     save it. We then take the rectangle we just used and offset it down and to
  185.     the right a single pixel. Again, we turn that rectangle into a region. The
  186.     final step involves merging those two regions together with a call to
  187.     UnionRgn().
  188.  
  189. *******************************************************************************/
  190. void DoCalcRgns(WindowPeek theWindow)
  191. {
  192.     Rect        tempRect;
  193.     RgnHandle    tempRgn;
  194.  
  195.     GetContentRect(theWindow, &tempRect);
  196.     RectRgn(theWindow->contRgn, &tempRect);
  197.  
  198.     GetStrucRect(theWindow, &tempRect);
  199.     RectRgn(theWindow->strucRgn, &tempRect);
  200.  
  201.     OffsetRect(&tempRect, 1, 1);
  202.  
  203.     tempRgn = NewRgn();
  204.     RectRgn(tempRgn, &tempRect);
  205.     UnionRgn(tempRgn, theWindow->strucRgn, theWindow->strucRgn);
  206.     DisposeRgn(tempRgn);
  207. }
  208.  
  209.  
  210. /*******************************************************************************
  211.  
  212.     DoDrawAll
  213.  
  214.     This function is responsible for drawing the entire frame in its standard
  215.     state. Since our window is not very complicated, there isn’t much to do.
  216.  
  217.     First, we draw the outline of the window. This is quickly done with a
  218.     FrameRect and a few MoveTo’s and LineTo’s.
  219.  
  220.     Next comes the tricky part: the drawing of dotted pattern in the drag
  221.     area. The problem here is that we are drawing in the Window Manager’s
  222.     port, which is in global coordinates. This means that any patterns drawn
  223.     will be aligned to the global origin. However, when drawing the pattern in
  224.     the drag area, we want the pattern to be aligned to the window’s origin,
  225.     not the global origin.
  226.  
  227.     Here’s an example to show the problem. Assume that when we first draw the
  228.     window, the drag area looks like this:
  229.  
  230.     +---------------------------------------------------+
  231.     | * +-+ * * * * * * * * * * * * * * * * * * * * * * |
  232.     | * +-+ * * * * * * * * * * * * * * * * * * * * * * |
  233.     +---------------------------------------------------+
  234.     |                                                   |
  235.     |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
  236.  
  237.     However, the drag area of a window drawn just one pixel over to the right
  238.     would look like this:
  239.  
  240.      +---------------------------------------------------+
  241.      |* *+-+* * * * * * * * * * * * * * * * * * * * * * *|
  242.      |* *+-+* * * * * * * * * * * * * * * * * * * * * * *|
  243.      +---------------------------------------------------+
  244.      |                                                   |
  245.      |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
  246.  
  247.     See the difference? In the first window, the dotted pattern leaves a white
  248.     one pixel border on the left and right side of the drag area. In the
  249.     second window, the dots are drawn right next to the edges of the drag
  250.     area.
  251.  
  252.     To take care of this problem, we have to alter our pattern based on our
  253.     window’s location. This is the same thing that QuickDraw does when it
  254.     draws patterns in the content area of your window. Once the pattern is
  255.     determined, we draw the drag area with a call to FillRect().
  256.  
  257.     Finally, we call a subroutine to draw the GoAway box.
  258.  
  259.     Note that, if our WDEF supported different appearances depending on the
  260.     hilited state of the window, we would have to check for that here and draw
  261.     accordingly. We can do this check by looking at the “hilited” field of the
  262.     window record.
  263.  
  264. *******************************************************************************/
  265. pascal void DoDrawAll(short depth, short deviceFlags, GDHandle targetDevice,
  266.                       WindowPeek theWindow)
  267. {
  268.     Rect        tempRect;
  269.     FontInfo    fontInfo;
  270.     Point        titleLocation;
  271.     Pattern        framePattern;
  272.     long        patternSeed;
  273.  
  274.     PenNormal();
  275.     GetStrucRect(theWindow, &tempRect);
  276.  
  277.     //
  278.     // Draw the frame
  279.     //
  280.  
  281.     FrameRect(&tempRect);
  282.  
  283.     //
  284.     // Draw the shadow
  285.     //
  286.  
  287.     MoveTo(tempRect.left+1, tempRect.bottom);
  288.     LineTo(tempRect.right, tempRect.bottom);
  289.     LineTo(tempRect.right, tempRect.top+1);
  290.  
  291.     //
  292.     // Draw the line at bottom of title bar
  293.     //
  294.  
  295.     tempRect.bottom = tempRect.top + 11;
  296.     InsetRect(&tempRect, 1, 1);
  297.     MoveTo(tempRect.left, tempRect.bottom);
  298.     LineTo(tempRect.right, tempRect.bottom);
  299.  
  300.     //
  301.     // Choose a pattern seed that is properly aligned horizontally.
  302.     //
  303.  
  304.     if (tempRect.left & 1)
  305.         patternSeed = 0x00550055;
  306.     else
  307.         patternSeed = 0x00AA00AA;
  308.  
  309.     //
  310.     // Align the seed vertically if needed. Shifting the
  311.     // pattern left by 8 bits moves each row of the pattern
  312.     // up a single row. The top row is discarded, which is OK
  313.     // for our purposes. The bottom row is filled with zeros,
  314.     // which OK, since that matches the discarded to row.
  315.     //
  316.  
  317.     if (tempRect.top & 1)
  318.         patternSeed <<= 8;
  319.  
  320.     //
  321.     // A pattern is 8 bytes long, and we’ve just finished creating
  322.     // a value that represents the first 4. Jam that 4 byte value
  323.     // into the full 8 bytes of our pattern.
  324.     //
  325.  
  326.     *(long *) &framePattern[4] = *(long *) &framePattern[0] = patternSeed;
  327.  
  328.     FillRect(&tempRect, framePattern);
  329.  
  330.     DrawGoAwayBox(theWindow, FALSE);
  331. }
  332.  
  333.  
  334. /*******************************************************************************
  335.  
  336.     ToggleGoAway
  337.  
  338.     Called when the Toolbox is tracking the mouse after the user clicks on the
  339.     GoAway box. Nothing fancy here: we just invert the box.
  340.  
  341. *******************************************************************************/
  342. pascal void ToggleGoAway(short depth, short deviceFlags, GDHandle targetDevice,
  343.                          WindowPeek theWindow)
  344. {
  345.     Rect    tempRect;
  346.  
  347.     GetGoAwayRect(theWindow, &tempRect);
  348.     InsetRect(&tempRect, 1, 1);
  349.     InvertRect(&tempRect);
  350. }
  351.  
  352.  
  353. /*******************************************************************************
  354.  
  355.     DrawGoAwayBox
  356.  
  357.     Draw the GoAway box. It is up to us to check to see if the window actually
  358.     has a GoAway box. If we supported a zoom box or grow box, we would have to
  359.     check for those as well in their corresponding draw routines.
  360.  
  361.     Note that we don’t check to see if the window is active or not before
  362.     drawing the close box. Normally, we would, as inactive windows don’t draw
  363.     their close boxes or allow clicks on them. However, this WDEF is for
  364.     floating windows. These windows have a special flavor of always acting and
  365.     looking active. For this reason, we draw the close box even if the window
  366.     is not the frontmost.
  367.  
  368. *******************************************************************************/
  369. void DrawGoAwayBox(WindowPeek theWindow, Boolean tracked)
  370. {
  371.     short    index;
  372.     Rect    tempRect;
  373.  
  374.     if (theWindow->goAwayFlag) {
  375.         PenNormal();
  376.         GetGoAwayRect(theWindow, &tempRect);
  377.         EraseRect(&tempRect);
  378.         InsetRect(&tempRect, 1, 1);
  379.         FrameRect(&tempRect);
  380.     }
  381. }
  382.  
  383.  
  384. /*******************************************************************************
  385.  
  386.     SyncPorts
  387.  
  388.     When our WDEF is entered, the port is set to the WMgrPort. This is an
  389.     old-fashioned GrafPort that allows us to draw only in black and white. If
  390.     we want to draw in color, we have to switch over to the WMgrCPort. When
  391.     doing so, we have to make sure that the two ports are synchronized (the
  392.     Window Manager doesn’t normally keep all of the fields of both ports in
  393.     sync with each other -- probably for performance reasons).
  394.  
  395. *******************************************************************************/
  396. void SyncPorts()
  397. {
  398.     GrafPtr        bwPort;
  399.     CGrafPtr    colorPort;
  400.     long        value;
  401.     OSErr        err;
  402.  
  403.     err = Gestalt(gestaltQuickdrawVersion, &value);
  404.  
  405.     if ((err == noErr) && (value >= gestalt8BitQD)) {
  406.  
  407.         GetWMgrPort(&bwPort);
  408.         GetCWMgrPort(&colorPort);
  409.         SetPort((GrafPtr) colorPort);
  410.  
  411.         BlockMove (&bwPort->pnLoc, &colorPort->pnLoc, 10);    // pnLoc[4],
  412.                                                             // pnSize[4],
  413.                                                             // pnMode[2]
  414.         BlockMove (&bwPort->pnVis, &colorPort->pnVis, 14);    // pnVis[2],
  415.                                                             // txFont[2],
  416.                                                             // txFace[2],
  417.                                                             // txMode[2],
  418.                                                             // txSize[2],
  419.                                                             // spExtra[4]
  420.         PenPat(bwPort->pnPat);
  421.         BackPat(bwPort->bkPat);
  422.     }
  423. }
  424.  
  425.  
  426. /*******************************************************************************
  427.  
  428.     GetContentRect
  429.  
  430.     Get the rectangle (in global coordinates) for the window’s content area.
  431.     We get this rectangle by starting with the window’s portRect. We then
  432.     convert the rectangle to global coordinates by temporarily setting the
  433.     port to the window and calling LocalToGlobal on the TopLeft and
  434.     BottomRight corners of the rectangle.
  435.  
  436. *******************************************************************************/
  437. void GetContentRect(WindowPeek theWindow, Rect *theRect)
  438. {
  439.     GrafPtr    oldPort;
  440.  
  441.     *theRect = theWindow->port.portRect;
  442.     GetPort(&oldPort);
  443.     SetPort((GrafPtr) theWindow);
  444.     LocalToGlobal((Point *) &(theRect->top));
  445.     LocalToGlobal((Point *) &(theRect->bottom));
  446.     SetPort(oldPort);
  447. }
  448.  
  449.  
  450. /*******************************************************************************
  451.  
  452.     GetStrucRect
  453.  
  454.     Get the rectangle used for the frame (structure) of our window. We base
  455.     this rectangle on the content rectangle, so we get that first. We then
  456.     tweak it into the right shape for the frame rectangle. Note that the
  457.     rectangle we return does _not_ include the window’s shadow. There’s no
  458.     good reason for doing this except that it makes the drawing and region
  459.     calculating routines a little easier to implement.
  460.  
  461. *******************************************************************************/
  462. void GetStrucRect(WindowPeek theWindow, Rect *theRect)
  463. {
  464.     GetContentRect(theWindow, theRect);
  465.     theRect->top -= 10;
  466.     InsetRect(theRect, -1, -1);
  467. }
  468.  
  469.  
  470. /*******************************************************************************
  471.  
  472.     GetGoAwayRect
  473.  
  474.     Return the rectangle to be used for the GoAway box.
  475.  
  476. *******************************************************************************/
  477. void GetGoAwayRect(WindowPeek theWindow, Rect *theRect)
  478. {
  479.     GetStrucRect(theWindow, theRect);
  480.     theRect->top += 1;
  481.     theRect->left += 7;
  482.     theRect->bottom = theRect->top + 9;
  483.     theRect->right = theRect->left + 9;
  484. }
  485.  
  486.