TweetFollow Us on Twitter

Diamond WDEF
!see also "Diamond WDEF Correction"
Volume Number:7
Issue Number:10
Column Tag:C Workshop

Related Info: Window Manager

Diamond WDEF

By Marisano James, Lancaster, PA

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks. See also Diamond WDEF Correction for code correction.

[Marisano James is a senior at Franklin and Marshall College where he works under the supervision of Dr. Jay Anderson, head of the Computer Science program, without whose early encouragement and support this article would not have been possible.]

Introduction

Over the years since MacTutor’s introduction, several window definition routines have been discussed by its authors. To my knowledge, none of these WDEFs have focused on non-rectangular windows. I believe this is because non-rectangular windows are often created for amusement instead of serious application. If this observation is correct, it identifies an interesting objection since MacTutor is read by many hobbyists. However, another justification for the aforementioned scarcity may be that non-rectangular windows do not operate as quickly as rectangular ones. This is because they must manipulate regions as opposed to rectangles. In this article I will address these issues and propose solutions. I think that this document and the code it discusses contain an element of discovery - discovery of new methods of approaching problems that allow us to see how we sometimes create our own restrictions. I believe this to be the best justification for this excursion.

Following is a discussion of the most significant difficulties I encountered while composing a WDEF for the manipulation of diamond shaped windows. Whenever a new technique is introduced I have explained why such a modification is useful. These explanations are designed to allow one to deduce the utility of my solutions, including in what other situations they might be useful.

Before proceeding I will describe the main characteristics of diamond windows. Diamond windows are shaped like rectangular windows tilted 45°. Because of this rotation their titles are drawn on a 45° slant. They expand and shrink from around their centers and may or may not contain any of the following: a go-away region, a grow region, and a zoom region.

Auxiliary Data

Non-rectangular windows will almost certainly use auxiliary data, and since this information is likely to be window specific, it should not be stored in the WDEF itself. This is where the predefined message wNew can be used to advantage.1 Within the routine designed to respond to this message, you can allocate memory for a window’s data. It is important that you also create a structure for your auxiliary data and include it wherever your non-rectangular windows are used. This way your code will compile and your debugger will interpret your structure’s members correctly. Below is a listing of the structure used in the diamond window WDEF.

/* 1 */

typedef struct {
 Rect   myRect, stdRect;
 Booleanresizable, zoomedOut;
 PolyHandle goAwayPoly, growPoly, zoomPoly;
 RgnHandletitleRgn, goAwayRgn, growRgn, zoomRgn;
 short  midX, midY, halfTitleSpace, leftStop,
 numChars;
} DataRecord, DataRecordHdl;

Illustration 1: a Diamond Window

static variables

The A5 register normally points to an application’s global variables. However, WDEFs are not applications and so the register is unavailable for use in them. This means the QuickDraw globals are not directly accessible to WDEFs. The A4 register can be used to reference the QuickDraw globals, or it can point to the WDEF’s own global variables. Diamond windows make use of the second approach. This methodology coupled with the use of static variables, allows the QuickDraw patterns to be redefined with the same names and used in precisely the same manner as within applications. This is possible because static variables allow ‘local’ globals to have the same names, but different data, as true globals. If a file redefines globals using static variables, then the variable with the larger scope is unavailable in that file.

main()

The first thing the diamond window WDEF does is set up the A4 register so that the WDEF can use global variables Thereafter main() determines whether or not the WDEF was called to create a new window. If so, the actions taken by it are discussed in the section dedicated to the wNew message. If not, the affected window’s dataHandle is transferred to the global variable dataRecordHdl. Then this handle’s data is transferred into another global called dataRecord. This prevents the WDEF from having to double dereference the data’s handle every time it needs to alter a window’s auxiliary information. At the completion of the dispatching routine, the current data is BlockMoved() into the memory location held by the previous information.

wNew

When the diamond window WDEF encounters wNew messages it does not transfer data from the window’s dataHandle. That procedure would be time consuming and possibly dangerous since the uninitialized handle could be referencing sensitive memory locations. Instead, control is passed to InitNewWindow(). This function sets many of the window’s characteristics, such as the shape of its goAway box -- if it has one. Later, when a diamond window is resized, or its position is changed, the WDEF simply offsets the regions created by InitNewWindow() instead of actually recalculating them since their shapes and sizes do not change. However, this is not true of the content, structure, or title regions, which have no such guarantees about their sizes and therefore must be recalculated. Finally, InitNewWindow() provides a valid handle to memory where the auxiliary information can be stored after the WDEF has been exited. Because of this, the BlockMove() executed at the end of the WDEF performs a useful service by storing the window’s auxiliary data in memory referenced by this handle.

wGrowMsg

Diamond windows grow and shrink around their centers. This means that whenever the grow icon is dragged, all four corners move that same distance away from or toward the center of the window. This behavior ensures that diamond windows retain their shape. It also creates an interesting problem. When a window is being resized, the Window Manager repeatedly calls the WDEF with the message parameter equal to wGrowMsg until the mouse button is released. During this time, a pointer to the global coordinates of a modified portRect are passed to the routine that draws the growImage. I say modified portRect because when a window is resized the effected rectangle is actually a copy of the window’s portRect. Then, if so directed, QuickDraw will replace the values stored in the portRect with those of the modified copy. Finally the content and structure regions of the resized window are rebuilt accordingly. At any rate, the Rect pointer was intended for use with rectangular windows. In rectangular windows, the top left hand corner is kept stationary while the bottom right hand one is allowed to pivot. This enables such windows to be resized both horizontally and vertically. However, diamond windows only respond to mouse movement along the y-axis when being resized. In order to extract information only pertaining to the vertical direction, GrowWindowGrow() subtracts the y-coordinate of the middle of the window from the y-coordinate of the bottom of the dereferenced Rect pointer. The resultant value is then used as the length of each side.

A diamond window with a grow region will also have a slider area which is drawn in response to the wDrawGIcon message. To prevent drawing from occurring within this area a clipRgn is used. For the sake of speed and simplicity, this clipRgn is automatically created and maintained by the WDEF itself. This method creates some complications of its own however, to be discussed in the External Window Support section.

wNEW_TITLE

Title drawing is also more complicated for diamond windows than for rectangular ones. The WDEF must decide if the whole title can be displayed, whether or not it will fit down the diagonal of one half the window, etc. These things depend on the size of the window, but for a given title, the WDEF can determine threshold window sizes. However, if the title is changed, the WDEF must be informed so that it can recalculate the transitional window sizes. To this affect, the function SetDiamondWTitle() calls the WDEF with the diamond window specific message wNEW_TITLE. The WDEF then installs the new title into the window record’s titleHandle member and checks to see if the window is visible. If so, the WDEF makes the Window Manager port the current port and stores the present clip region into it. Then the clip is set to the window’s titleRgn and ClipAbove() is called to determine what portion of the titleRgn is visible. Finally, DrawWindow() is called to redraw the window with the new clipRgn enforced.

wDispose

In order to operate efficiently, auxiliary memory must be released when a diamond window is dismissed. This is done in response to the wDispose message. The function WindowDisposal() releases memory occupied by the members of the dataRecord structure. At the end of the function the dataHandle itself is released. This is permissible because the WDEF is exited prematurely when a wDispose message is serviced, thus preventing the final BlockMove() from corrupting memory by moving data into memory referenced by an invalid handle.

External Window Support

Diamond windows require greater support outside the WDEF than do rectangular ones. In a program supporting both rectangular and diamond shaped windows, I have found it useful to store descriptive numbers in the refCon members of all window records in order to differentiate between the two types. The program can either include a separate window handling routine for diamond shaped windows, or the refCon member can be tested within each case statement where it is necessary to provide alternative instructions for diamond shaped windows.

The Slider Area

Additional support is required when using sliders with diamond shaped windows because no drawing will occur if the clipRgn is not suspended before Draw1Control() is called for each slider. Despite this, it is desirable to have the WDEF maintain the clipRgn automatically, since all of the window’s other controls should be drawn with the clipRgn enforced. Therefore, a diamond window’s sliders should be clearly distinguishable from other controls, so that they can be drawn without the clipRgn’s range restrictions. This can be accomplished by coding each slider’s refCon member.

Establishing a clipRgn further restricting drawing in the content region of a diamond window also deserves to be discussed. It should be sufficient to save the former clipRgn, create and install a new one for use within the drawing routine, and finally, to reinstall the original clipRgn. Note that SetClip() creates and uses a copy of the region passed to it. It is therefore necessary to dispose of the original region after a call to SetClip().

Illustration 2: a Diamond’s portRect

The portRect

The portRect does not encompass the entire contentRgn of a diamond shaped window. Although this in itself is not problematic because the Window Manager sets aside enough memory for all onscreen drawing, offscreen drawing is another issue. Normally, offscreen buffers are set to the dimensions of the portRect. This is sufficient for diamond shaped windows possessing growRgns because the clipRgn used with such windows is completely enveloped by the portRect. However, when a programmer wishes to establish an offscreen BitMap or GrafPort for a diamond window without a grow region, the portBits.bounds member of the buffer should be of the same dimensions as the window’s portRect inset -26 pixels on each side. This will also have bearing on the rowBytes member of the BitMap and the amount of memory necessary for the actual buffer.

DragWindow()

Directly before calling DragWindow(), the titleWidth member of the window record is set to TRUE. If part of a window which was previously offscreen is moved onscreen as a result of a drag, DragWindow() will call the appropriate WDEF with the wDraw argument. The diamond window WDEF checks the value of theWindow->titleWidth each time wDraw is called for a visible window. If theWindow->titleWidth is TRUE, the WDEF will recalculate the window’s regions so that any newly visible portions of the window will be drawn. This recalculation must occur before any drawing takes place. If DragWindow() calls the diamond window WDEF, theWindow->titleWidth is set to FALSE.

Upon returning from DragWindow(), the calling routine checks to see if the titleWidth member is still set to TRUE. If so, the window’s regions are recalculated. This allows DoHit() to operate properly. If the region’s position’s were not recalculated at this time, it would be necessary to do so every time the mouse button is pressed with the cursor inside the window. This is not an efficient use of time and the resultant pause would be very noticeable because the updating would occur before a requested operation instead of after one. However, there is no need to recalculate the region’s positions if the titleWidth member of the window is FALSE, because this means the window’s regions were already updated when DragWindow() called the WDEF.

The titleWidth member is used in this manner because it is not used for any other purpose and otherwise it would be necessary to extract the information from the window’s dataHandle - a task requiring considerably more dereferencing.

GrowWindow()

A size restriction should always be passed when GrowWindow() is called to resize a diamond window. Otherwise a user will be able to pull the bottom of the window over its top. In order for size restrictions to work properly, the value passed to GrowWindow() must be adjusted to account for the rate at which diamond windows grow and shrink.

Initially windHeight holds the hi word of the value returned by GrowWindow() -- the low word is inconsequential since only movement along the vertical axis effects a diamond window’s size. If the variable windHeight were passed to SizeWindow() without modification, the window would be incorrectly resized. The reason for this becomes clear if you think carefully about what is actually being stored in the variable. When a diamond window is resized, the new size is only adjusted from the bottom. However, diamond windows grow and shrink around their centers. It is therefore necessary to alter the window’s size at its top by the same amount it was changed at its bottom. This amounts to subtracting the difference in sizes between the size of the old portRect and the value stored in windHeight (the difference can be positive or negative). This value, called offset, is also used to properly offset the window in the horizontal and vertical directions.

SetDiamondWTitle()

Instead of calling SetWTitle() to change a diamond window’s title, call SetDiamondWTitle(). This function in turn calls the WDEF, passing the new title as an argument.

Final Notes

The CallPascalL() routine

When making a WDEF that is called using the CallPascalL() routine, be careful not to call the WDEF using the standard predefined messages. These have special significance to the Window Manager, and will generally not operate as intended. Instead, define your own message and have the WDEF respond to it. Simply stack the two case statements so that they apply to the same instructions.

contRgn and strucRgn

One of the most difficult aspects of creating WDEFs is making the clipRgns and strucRgns correspond correctly to one another. To help solve this difficulty I found it useful to make an application, open a standard window using it, and to draw the clipRgn and strucRgn within it. The application should erase the clipRgn after drawing the window’s frame because this is what the Window Manager does when it first draws a window, or any part of a window that was just uncovered. For this reason, the window will not have an outline if the strucRgn is not larger than the contRgn. Also, in order for a window to have a drop shadow, the strucRgn should to be large enough to contain that graphic. During this stage of window development it is important to remember that the QuickDraw pen draws one pixel down and one pixel to the right of the actual pen location.

Entering the WDEF source code

WDEFs are CODE resources. Before you can actually compile a CODE resource, you need to tell the compiler that that is your intention. If you are using THINK C, under the Project menu select “Set Project Type ”. When the dialog box appears fill it in as follows:

Illustration 3: the Set Project Type Dialog Box

Once you have successfully created your WDEF resource you will need to make a new, standard project in which to place WorkDiamondWinds.c, the program that helps control the diamond windows. Do not forget to open the resource containing the WDEF code. When THINK C opens a project it looks in that folder for a resource file having the same name as the project but with “.rsrc” appended to it. If it finds one, it will automatically open that file. Note that any special characters used to indicate that a file is a project are considered part of its name.

Footnote

1 The Window Manager requests actions from a WDEF by sending it messages. Depending on the nature of the request, the WDEF may return a meaningful message of its own. If this does not sound familiar, take a cursory glance at Inside Macintosh Volume 1, Chapter nine, “Defining Your Own Windows”.

/* ============================================================

File: Diamond.h
Function: Header file for Diamond Windows.
History: Last modification -- 04/25/91 -- Marz.

============================================================ */

#include <stddef.h>

#define MAKE_EVEN0xFFFE

typedef struct   {
 Rect   myRect, stdRect;
 Booleanresizable, zoomedOut;
 PolyHandle goAwayPoly, growPoly, zoomPoly;
 RgnHandletitleRgn, goAwayRgn, growRgn, zoomRgn;
 short  midX, midY, halfTitleSpace, leftStop, numChars;
} DataRecord, **DataRecordHdl;

/* ==========================================================

File: DiamondDef.c
Function: Defines the Diamond Window.
History: 7/4/90  &  8/26/90 - 12/1/90  &  2/15/91 - 4/22/91 :
 Marz. 

============================================================ */

#include “DiamondDef.h”
#include <SetUpA4.h>


#define wJUST_DRAGGED50
#define wNEW_TITLE 51
#define DRAW_WINDOW_FRAME 0

#define ZOOM_MASK8
#define NO_GROW_MASK 4

#define SCREEN_MARGIN3
#define FRAME_WIDTH25
#define SHADOW   3
#define LEFT_SIDETRUE
#define FULL_RESERVE 51
#define TOP_RESERVE30
#define BOTTOM_RESERVE    13

#define FIRST_CHAR 1


static Pattern   white = {0}, black = {0xFF, 0xFF, 0xFF,
 0xFF,  0xFF,0xFF, 0xFF, 0xFF}, gray = {0xAA, 0x55, 0xAA,
 0x55, 0xAA, 0x55, 0xAA, 0x55};

DataRecorddataRecord;
DataRecordHdl    dataRecordHdl;

/* ========================================================= */

void    CalcRgns(WindowPeek   theWindow)
{
/* Calculates the structure, content, and title regions of diamond windows 
when they are created or after they have been dragged or resized.
*/
GrafPtr savedPort;
Rect    theRect, strucRect, polyRect;
short   halfPort, halfStruc, xOffset, yOffset;

 theRect = theWindow->port.portRect;
/* The contentRgn extends beyond the portRect.  This means origin is 
automatically offset so that it begins just outside the scroll bar area, 
regardless of whether or not the window actually has a grow region.
*/
 halfPort = halfStruc = (theRect.right - theRect.left) >> 1;
 halfStruc += 26;
/* This routine needs the portRect in Global coordinates for drawing 
in the WMgr port. By switching the current port to the window’s port 
and pretending a rectangle is two points this can be achieved.
*/ 
 GetPort(&savedPort);
 SetPort(theWindow);
 LocalToGlobal(&topLeft(theRect));
 LocalToGlobal(&botRight(theRect));
 SetPort(savedPort); 
 if (EqualRect(&theRect, &dataRecord.stdRect))
 {
 /* window was resized by being zoomed, don’t change dataRecord.myRect, 
it holds the previous size of the portRect for use with inZoomIn.
 */
 strucRect = theRect;
 dataRecord.zoomedOut = TRUE;
 }
 else
 {
 /* window has been resized w/o being zoomed, save the new window size 
for later inZoom-ing.
 */
 dataRecord.myRect = strucRect = theRect;
   dataRecord.zoomedOut = FALSE;
  }
/* Get the strucRect.  dataRecord.midX and dataRecord.midY are the Global 
coordinates of the middle of the window.
*/
 InsetRect(&strucRect, -26, -26);
 dataRecord.midX = (strucRect.left + strucRect.right) >> 1;
 dataRecord.midY = (strucRect.top + strucRect.bottom) >> 1;
/* Get the strucRgn*/
 OpenRgn();
 MoveTo(strucRect.left, dataRecord.midY);
 Line(halfStruc, -halfStruc);
 Line(2, 0);
 Line(halfStruc, halfStruc);
 Line(0, 1);
 Line(-halfStruc, halfStruc);
 Line(-2, 0);
 Line(-halfStruc, -halfStruc);
 Line(0, -1);
 CloseRgn(theWindow->strucRgn);

/* Define the titleRgn.  There are a couple of notes of interest here.
 1)ErasePoly() actually calls NewRgn() and makes a region out of the 
polygon in order to know what to erase.
 2)I only used titlePoly to erase the title area anyway so why not just 
make it a region and save time?
*/
 OpenRgn();
 MoveTo(theRect.left - 26, dataRecord.midY);
 Line(26, 0);
 Line(halfPort, halfPort);
 Line(halfPort, -halfPort);
 Line(26, 0);
 Line(-halfStruc, halfStruc);
 Line(-halfStruc, -halfStruc);
 CloseRgn(dataRecord.titleRgn);
/* Define the content region. */
 halfStruc --;
 OpenRgn();
 MoveTo(dataRecord.midX, theRect.top - 25);
 Line(halfStruc, halfStruc);
 Line(-25, 0);
 Line(-halfPort, halfPort);
 Line(-halfPort, -halfPort);
 Line(-25, 0);
 Line(halfStruc, -halfStruc);
 CloseRgn(theWindow->contRgn);
/* The following lines offset various window parts to the updated locations. 
 The window parts are only updated if they are present.
*/
 if (theWindow->goAwayFlag)
 {
 HLock(dataRecord.goAwayPoly);
 polyRect = (**(dataRecord.goAwayPoly)).polyBBox;
 HUnlock(dataRecord.goAwayPoly);
 xOffset = -polyRect.left + strucRect.left + 18;
 yOffset = -polyRect.top + dataRecord.midY + 5;
 OffsetPoly(dataRecord.goAwayPoly, xOffset, yOffset);
 OffsetRgn(dataRecord.goAwayRgn, xOffset, yOffset);
 }
 if (theWindow->spareFlag)
 {
 HLock(dataRecord.zoomPoly);
 polyRect = (**(dataRecord.zoomPoly)).polyBBox;
 HUnlock(dataRecord.zoomPoly);
 xOffset = -polyRect.left + strucRect.right - 34;
 yOffset = -polyRect.top + dataRecord.midY + 5;
 OffsetPoly(dataRecord.zoomPoly, xOffset, yOffset);
 OffsetRgn(dataRecord.zoomRgn, xOffset, yOffset);
 }
 if (dataRecord.resizable)
 {
 HLock(dataRecord.growPoly);
 polyRect = (**(dataRecord.growPoly)).polyBBox;
 HUnlock(dataRecord.growPoly);
 xOffset = -polyRect.left + dataRecord.midX - 8;
 yOffset = -polyRect.top + strucRect.bottom - 24;
 OffsetPoly(dataRecord.growPoly, xOffset, yOffset);
 OffsetRgn(dataRecord.growRgn, xOffset,yOffset);
/* Make the clipRgn which will prevent drawing from occuring within the 
slider area.  This region will later have to be suspended to allow for 
the drawing of the sliders themselves.
When Working with clipRgns it is important to remember how the Mac coordinate 
system works. Recall that the pen hangs down and to the right one pixel 
of the actual location of the pen.  The following few lines compensate 
for this
*/
 SetPort(theWindow);
 OpenRgn();
 theRect = theWindow->port.portRect;
 MoveTo(theRect.left, theRect.top + halfPort);
 Line(1, 0);
 Line(halfPort - 1, -halfPort + 1);
 Line(halfPort - 1, halfPort - 1);
 Line(1, 0);
 Line(-halfPort, halfPort);
 Line(-halfPort, -halfPort);
 CloseRgn(theWindow->port.clipRgn);
 SetPort(savedPort);
 }
/* Find how much space is available in each window half for writing the 
title in.
*/
 dataRecord.halfTitleSpace = halfStruc - FULL_RESERVE;
}


void    ToggleGoAwayBox()
{
/* Indicates when the close region of a diamond window is selected by 
drawing an asterisk-like symbol within the region.  First the image is 
drawn into an offscreen BitMap using StuffHex(), then the image is tranferred 
to the WMgrPort by means of the CopyBits() procedure.
*/
BitMap  closeBitMap;
GrafPtr windMngrPort;
Rect    tempRect, myRect;

 closeBitMap.baseAddr = NewPtr(0x34);
 StuffHex(closeBitMap.baseAddr, “002000200020012400A80000”);
 StuffHex(closeBitMap.baseAddr + 12,
 “0F8F800000A801240020002000200”);
 closeBitMap.rowBytes = 2;
 closeBitMap.bounds.top = closeBitMap.bounds.left = 0;
 closeBitMap.bounds.bottom = closeBitMap.bounds.right = 13;
 myRect = closeBitMap.bounds;
 tempRect = (**dataRecord.goAwayPoly).polyBBox;
 OffsetRect(&myRect, tempRect.left + 2, tempRect.top + 2);
 GetWMgrPort(&windMngrPort);
 CopyBits(&closeBitMap, &windMngrPort->portBits,
 &closeBitMap.bounds,
 &myRect, srcXor, NULL);
 DisposPtr(closeBitMap.baseAddr);
}


void    ToggleZoomBox()
{
/* As ToggleGoAwayBox().
*/
BitMap  zoomBitMap;
GrafPtr windMngrPort;
Rect    tempRect, myRect;

 zoomBitMap.baseAddr = NewPtr(0x34);
 StuffHex(zoomBitMap.baseAddr, “0020002000A0016400A80010”);
 StuffHex(zoomBitMap.baseAddr + 12,
 “0F8F801000A8016400A0002000200”);
 zoomBitMap.rowBytes = 2;
 zoomBitMap.bounds.top = zoomBitMap.bounds.left = 0;
 zoomBitMap.bounds.bottom = zoomBitMap.bounds.right = 13;
 myRect = zoomBitMap.bounds;
 tempRect = (**dataRecord.zoomPoly).polyBBox;
 OffsetRect(&myRect, tempRect.left + 2, tempRect.top + 2);
 GetWMgrPort(&windMngrPort);
 CopyBits(&zoomBitMap, &windMngrPort->portBits, &zoomBitMap.bounds,
 &myRect, srcXor, NULL);
 DisposPtr(zoomBitMap.baseAddr);
}


void    EraseTitleSpace(Boolean   leftSide, short   length)
{
/* Clears space for the window’s title to be drawn in.
*/
RgnHandle theRgn;

 theRgn = NewRgn();
 OpenRgn();
 if (leftSide)
 {
 Line(length, length);
 Line(11, -11);
 Line(-length, -length);
 Line(-11, 11);
 }
 else
 {
 Line(length, -length);
 Line(-11, -11);
 Line(-length, length);
 Line(11, 11);
 }
 CloseRgn(theRgn);
 EraseRgn(theRgn);
 DisposeRgn(theRgn);
}


void    DrawChars(startSpot, stopSpot, theTitle)
short   startSpot, stopSpot;
PtrtheTitle;
{
/* Draws the characters of the window’s title on a diagonal slant.  If 
(start == FIRST_CHAR) then text is drawn on a downward slant, otherwise 
text is drawn on an upward slant beginning with the character specified 
by startSpot.
*/
char    ch;
short   yOffset, index, charWidth, xAdjust;

 if (startSpot == FIRST_CHAR)
 yOffset = 8;
 else
 yOffset = -8;
 index = startSpot;
 while (index <= stopSpot)
 {
 ch = *(theTitle + index);
 charWidth = CharWidth(ch); 
 xAdjust = charWidth >> 1;
 Move(-xAdjust, 0);
 DrawChar(ch);
 Move(-(charWidth - xAdjust) + 8, yOffset);
 index ++;
 }
}


void    DrawTitle(WindowPeek   theWindow, Rect   strucRect)
{
/* Decides how text should appear, depending on the size of the window 
and its title. Positions the pen and oversees text drawing.
*/
PtrtheTitle;
short   totalWidth, oneSide, adjust, penOffset,
 mostChars, leftChars;
StringHandletitleHandle;

 titleHandle = theWindow->titleHandle;
 HLock(titleHandle);
 theTitle = (Ptr) *titleHandle;
 totalWidth = (dataRecord.numChars << 3) + 10;
 oneSide = (strucRect.right - strucRect.left) >> 1;
 if (totalWidth <= dataRecord.halfTitleSpace)
 {
 adjust = ((oneSide - totalWidth) >> 1) + 7;
 MoveTo(strucRect.left + adjust, dataRecord.midY + adjust);
 EraseTitleSpace(LEFT_SIDE, totalWidth);
 Move(16, 7);
 DrawChars(FIRST_CHAR, dataRecord.numChars, theTitle);
 }
 else
 {
 mostChars = dataRecord.numChars - dataRecord.leftStop;
 if (mostChars < dataRecord.leftStop)
 mostChars = dataRecord.leftStop;
 if ((mostChars << 3) < (dataRecord.halfTitleSpace))
 {
 /* word break on space   */
 penOffset = (dataRecord.leftStop << 3) + 5;
 MoveTo(dataRecord.midX - BOTTOM_RESERVE - 3,
  strucRect.bottom - BOTTOM_RESERVE - 3);
 Move(-penOffset, -penOffset);
 EraseTitleSpace(LEFT_SIDE, penOffset);
 Move(14, 5);
 DrawChars(FIRST_CHAR, dataRecord.leftStop, theTitle);
 /* don’t forget to remove the excess space */
 penOffset = (dataRecord.numChars - dataRecord.leftStop -
 1) << 3;
 MoveTo(dataRecord.midX + BOTTOM_RESERVE + 3,
 strucRect.bottom - BOTTOM_RESERVE - 3);
 EraseTitleSpace(!LEFT_SIDE, penOffset + 5);
 Move(1, -8);
 DrawChars(dataRecord.leftStop + 2, dataRecord.numChars,
 theTitle);
 }
 else
 {
 /* no word break on space*/
 leftChars = dataRecord.halfTitleSpace >> 3;
 penOffset = dataRecord.halfTitleSpace + 4;
 MoveTo(dataRecord.midX - BOTTOM_RESERVE - 3,
 strucRect.bottom - BOTTOM_RESERVE - 3);
 Move(-penOffset, -penOffset);
 EraseTitleSpace(LEFT_SIDE, penOffset);
 Move(14, 5);
 DrawChars(FIRST_CHAR, leftChars, theTitle);
 MoveTo(dataRecord.midX + BOTTOM_RESERVE + 3,
 strucRect.bottom - BOTTOM_RESERVE - 3);
 mostChars = leftChars << 1;
 if (mostChars > dataRecord.numChars)
 {
 mostChars = dataRecord.numChars;
 penOffset = ((dataRecord.numChars - leftChars) << 3) +
 4;
 }
 EraseTitleSpace(!LEFT_SIDE, penOffset);
 Move(0, -7);
 DrawChars(leftChars + 1, mostChars, theTitle);
 }
 }
 HUnlock(titleHandle);
}


void    DrawWindow(WindowPeek   theWindow, long   param)
{
/* Draws diamond window frames and titles as appropriate.
*/
Rect    strucRect;
short   halfPort, halfStruc, index;

 if (theWindow->visible)
 switch(LoWord(param))
 {
 case wInGoAway:
 ToggleGoAwayBox();
 break;
 case wInZoomIn:
 case wInZoomOut:
 ToggleZoomBox();
 break;
 default:
/* Draw window frame.*/
 if (theWindow->titleWidth)
 {
 CalcRgns(theWindow);
 theWindow->titleWidth = FALSE;
 }
 if (dataRecord.zoomedOut)
 strucRect = dataRecord.stdRect;
 else
 strucRect = dataRecord.myRect;
 InsetRect(&strucRect, -26, -26);
 halfStruc = halfPort = (strucRect.right -
 strucRect.left) >> 1;
 halfPort -= 26;
 MoveTo(strucRect.left, dataRecord.midY);
 Line(halfStruc, -halfStruc);
 PenSize(3, 1);
 Line(halfStruc, halfStruc);
 Line(-halfStruc, halfStruc);
 PenSize(1, 1);
 Line(-halfStruc, -halfStruc);
/* Draw content stuff*/
 EraseRgn(dataRecord.titleRgn);
/* Outline the content region {and the title region}  */
 Line(26, 0);
 Line(halfPort, halfPort);
 Line(halfPort, -halfPort);
 Line(25, 0);
 if (theWindow->hilited)
 {
 MoveTo(strucRect.left + 23, dataRecord.midY + 2);
 index = halfStruc - 3;
 for (halfStruc = index - 20; halfStruc < index;
 halfStruc += 4)
 {
 Line(halfStruc, halfStruc);
 Line(halfStruc, -halfStruc);
 Move(-(halfStruc << 1) - 4, 0);
 }
 if (theWindow->goAwayFlag)
 {
 PenPat(&white);
 PenSize(1, 1);
 MoveTo(strucRect.left + 17, dataRecord.midY +
 12);
 Line(8, -8);
 Move(0, 20);
 Line(10, -10);
 PenPat(&black);
 EraseRgn(dataRecord.goAwayRgn);
 FramePoly(dataRecord.goAwayPoly);
 }
 if (theWindow->spareFlag)
 {
 PenPat(&white);
 PenSize(1, 1);
 MoveTo(strucRect.right - 17, dataRecord.midY +
 12);
 Line(-8, -8);
 Move(0, 20);
 Line(-10, -10);
 PenPat(&black);
 EraseRgn(dataRecord.zoomRgn);
 FramePoly(dataRecord.zoomPoly);
 }
 if (dataRecord.resizable)
 {
 InsetRgn(dataRecord.growRgn, -2, 0);
 EraseRgn(dataRecord.growRgn);
 InsetRgn(dataRecord.growRgn, 2, 0);
 FramePoly(dataRecord.growPoly);
 }
 } /* if window is hilited*/
 DrawTitle(theWindow, strucRect);
 break;
 } /* end switch */
}


void    NewTitle(WindowPeek   theWindow, Ptr   theTitle)
{
/* Resets the handle size for the window’s title, then BlockMove()s the 
new information into it.  Next the space closest to the true center of 
the title is found and recorded in the variable leftStop {if no space 
is found the entire title’s length is stored in leftStop}.  This information, 
coupled with the window’s current size, is used to determine how the 
the window’s title should be drawn {all on the left side, split at a 
space and part drawn on each side, etc.}.
 NewTitle() should only be called when the window is given a new title.
*/
char    ch;
GrafPtr savedPort;
PtrmiddleChar;
RgnHandle theRgn;
short   leftChars, index;
StringHandletitleHandle;
WindowPeekwindMgrPort;

 dataRecord.numChars = *theTitle;
 titleHandle = theWindow->titleHandle;
 SetHandleSize(titleHandle, dataRecord.numChars + 1);
 BlockMove(theTitle, *titleHandle, dataRecord.numChars + 1);
 HLock(titleHandle);
 leftChars = dataRecord.numChars >> 1; /* divide in half */
 middleChar = theTitle + leftChars;
 for (index = 0; index <= leftChars; index ++)
 {
 ch = *(middleChar - index);
 if (ch == 0x20)
 {
 dataRecord.leftStop = leftChars - index - 1;
 break;
 }
 else
 {
 ch = *(middleChar + index);
 if (ch == 0x20)
 {
 dataRecord.leftStop = leftChars + index - 1;
 break;
 }
 }
 }
 if (index > leftChars)
 dataRecord.leftStop = dataRecord.numChars;
 HUnlock(titleHandle);
 /* When dataRecord.titleRgn == NULL, the window 
 is new and therefore its
 titleRgn has not yet been properly set up by 
 CalcRgns(). Setting a clipRgn
 to an uninitialized region is a fatal error so 
 SetClip() should NOT be called
 under this circumstance.
 */
 if ((theWindow->visible) && (dataRecord.titleRgn != NULL))
 {
 GetPort(&savedPort);
 GetWMgrPort(&windMgrPort);
 SetPort(windMgrPort);
 theRgn = NewRgn();
 GetClip(theRgn);
 /* The visRgn does not include any of the window’s strucRgn, only the 
visible portion of its contRgn. There’s no point in dealing with the 
entire visible strucRgn, just the part of it used to display the window’s 
title since that’s all that’s being altered.  So the WDEF just sets the 
window manager’s clipRgn to the window’s titleRgn and calls ClipAbove() 
to subtract the strucRgns of all visible window’s in front of the window 
whos title has been changed.
 */
 SetClip(dataRecord.titleRgn);
 ClipAbove(theWindow);
 DrawWindow(theWindow, DRAW_WINDOW_FRAME);
 SetClip(theRgn);
 /* SetClip() copies the specified region into the clipRgn member of 
the current port.  It’s up to the programmer to release the memory occupied 
by the original region.
 */
 DisposeRgn(theRgn);
 SetPort(savedPort);
 }
}


void    DrawGrowIcon(WindowPeek   theWindow)
{
/* Draws the part of the grow icon that is in the content region of the 
window. It is not feasible to draw the gIcon as part of the window frame 
because, although the icon is properly drawn with much less effort, it 
is often times incorrectly erased by the Window Manager when the window 
is resized {the WManager has to erase the content region to ensure that 
no desktop slop shows through}.
*/
GrafPtr savedPort;
PenStatesavedPenState;
Rect    portRect;
RgnHandle clipRgn;
short   halfPort;

 if (dataRecord.resizable)
 if (theWindow->visible)
 {
 GetPort(&savedPort);
 SetPort(theWindow);
 GetPenState(&savedPenState);
 PenNormal();
 clipRgn = NewRgn();
 GetClip(clipRgn);
 /* Must take into consideration the coordinates of the origin in case 
SetOrigin() has been called with values other than (0, 0).  Remember, 
this drawing occurs within the window’s own grafport.
 */
 portRect = theWindow->port.portRect;
 halfPort = (portRect.right - portRect.left) >> 1;
 MoveTo(portRect.left, portRect.top + halfPort);
 portRect.top -= 26;
 ClipRect(&portRect);
 Line(halfPort, -halfPort);
 Line(0, -25);
 Move(0, 25);
 Line(halfPort, halfPort);
 SetPenState(&savedPenState);
 SetClip(clipRgn);
 DisposeRgn(clipRgn);
 SetPort(savedPort);
 }
}


void    GrowWindowGrow(theWindow, rectPtr)
WindowPeektheWindow;
Rect    *rectPtr;
{
/* Draws the grow image for the window.  Diamond windows grow and shrink 
at twice the normal rate and only movement in the y-direction effects 
them when they are being resized.
 To find the new length of each side, (*rectPtr).bottom is subtracted 
from dataRecord.midY, the y-coordinate of the middle of the window.
*/
short   length, longLength;

 length = longLength = rectPtr->bottom - dataRecord.midY - 1;
 longLength += 26;
 MoveTo(dataRecord.midX - length, dataRecord.midY);
 PenPat(&white);
 Line(length, -length);
 Line(length, length);
 Line(-length, length);
 Line(-length, -length);
 PenPat(&gray);
 Line(-26, 0);
 Move(longLength, -length);
 Line(0, -26);
 Move(length, longLength);
 Line(26, 0);
 PenPat(&white);
 Line(-longLength, longLength);
 Line(-longLength, -longLength);
 Line(longLength, -longLength);
 Line(longLength, longLength);
/* The Window Manager automatically resets the pen pattern to black when 
it stops calling this function.
*/
}


void    SetUpStdZoom(void)
{
/* Sets up the stdState Rect so the window will be zoom ready.  This 
rectangle is always centered onscreen and does not take into account 
any maximum window size you may entend to enforce using GrowWindow().
*/
GrafPtr windMPort;
Rect    theRect;
short   xDistance, yDistance;

 GetPort(&windMPort);
 theRect = windMPort->portRect;
 InsetRect(&theRect, SCREEN_MARGIN, SCREEN_MARGIN +
 FRAME_WIDTH);
 theRect.top += MBarHeight;
 theRect.right -= SHADOW;
 theRect.bottom -= 2;
/* Make sure x and yDistance are even. */
 xDistance = (theRect.right - theRect.left) & MAKE_EVEN;
 yDistance = (theRect.bottom - theRect.top) & MAKE_EVEN;
 if (xDistance > yDistance)
 {
 OffsetRect(&theRect, (xDistance - yDistance) >> 1, 0);
 theRect.right = theRect.left + yDistance;
 }
 else if (yDistance > xDistance)
 {
 OffsetRect(&theRect, 0, (yDistance - xDistance) >> 1);
 theRect.bottom = theRect.top + xDistance;
 }
 dataRecord.stdRect = theRect;
}


void    InitNewWindow(WindowPeek   theWindow)
{
/*   Sets up various window regions and allocates memory necessary for 
the operation of diamond shaped windows.
*/
Rect    theRect;
Str255  theTitle;

 theWindow->strucRgn = NewRgn();
 theWindow->contRgn = NewRgn();
 if (theWindow->goAwayFlag)
 {
 dataRecord.goAwayPoly = OpenPoly();
 MoveTo(0, 0);
 Line(8, -8);
 Line(8, 8);
 Line(-8, 8);
 Line(-8, -8);
 ClosePoly();
 dataRecord.goAwayRgn = NewRgn();
 OpenRgn();
 FramePoly(dataRecord.goAwayPoly);
 CloseRgn(dataRecord.goAwayRgn);
 }
 if (dataRecord.resizable)
 {
 dataRecord.growPoly = OpenPoly();
 MoveTo(0, 0);
 Line(8, -8);
 Line(8, 8);
 Line(-8, 8);
 Line(-8, -8);
 Line(5, -5);
 Line(-2, -2);
 Line(5, -5);
 Line(5, 5);
 Line(-1, 1);
 ClosePoly();
 dataRecord.growRgn = NewRgn();
 OpenRgn();
 MoveTo(0, 0);
 Line(5, -5);
 Line(-2, -2);
 Line(5, -5);
 Line(5, 5);
 Line(-2, 2);
 Line(5, 5);
 Line(-8, 8);
 Line(-8, -8);
 CloseRgn(dataRecord.growRgn);
 }
 if (theWindow->spareFlag)
 {
 dataRecord.zoomPoly = OpenPoly();
 MoveTo(5, -5);
 Line(3, -3);
 Line(8, 8);
 Line(-8, 8);
 Line(-8, -8);
 Line(5, -5);
 Line(5, 5);
 Line(-5, 5);
 ClosePoly();
 dataRecord.zoomRgn = NewRgn();
 OpenRgn();
 FramePoly(dataRecord.goAwayPoly);
 CloseRgn(dataRecord.zoomRgn);
 SetUpStdZoom();
 dataRecord.zoomedOut = FALSE;
 }
/* Initialize the global variable dataRecordHdl so data can be transferred 
from the global variable dataRecord into the window’s private storage 
area.
*/
 dataRecordHdl = (DataRecordHdl) 
 NewHandle(sizeof(DataRecord));
 theWindow->dataHandle = (Handle) dataRecordHdl;
 theRect = theWindow->port.portRect;
 if (theRect.bottom > theRect.right)
 {
 /* To ensure that each side of a window’s portRect is even, the value 
is ANDed with MAKE_EVEN, that is 0xFFFE.
 */
 theRect.right &= MAKE_EVEN;
 theRect.bottom = theRect.right;
 }
 else
 {
 theRect.bottom &= MAKE_EVEN;
 theRect.right = theRect.bottom;
 }
 theWindow->port.portRect = theRect;
/* Save title information that will allow diamond window title to be 
drawn more quiclky.
*/
 BlockMove(*(theWindow->titleHandle), theTitle, 0xFF);
 dataRecord.titleRgn = NULL;
 NewTitle(theWindow, (Ptr) theTitle);
 dataRecord.titleRgn = NewRgn();
}


long    DoHit(WindowPeek   theWindow, Point   mouseClick)
{
/* Determines if a diamond window was hit by a mouseClick.  If so, returns 
the region in which the click occured.
*/
 if (theWindow->visible)
 if (theWindow->hilited)
 {
 if (theWindow->goAwayFlag)
 if (PtInRgn(mouseClick, dataRecord.goAwayRgn))
 return(wInGoAway);
 if (dataRecord.resizable)
 if (PtInRgn(mouseClick, dataRecord.growRgn))
 return(wInGrow);
 if (theWindow->spareFlag)
 if (PtInRgn(mouseClick, dataRecord.zoomRgn))
 return(dataRecord.zoomedOut ? wInZoomIn :
 wInZoomOut);
 if (PtInRgn(mouseClick, dataRecord.titleRgn))
 return(wInDrag);
 if (PtInRgn(mouseClick, theWindow->contRgn))
 return(wInContent);
 }
 else   /* if window is not hilited  */
 {
 if (PtInRgn(mouseClick, dataRecord.titleRgn))
 return(wInDrag);
 if (PtInRgn(mouseClick, theWindow->strucRgn))
 return(wInContent);
 }
}


void    WindowDisposal(WindowPeek   theWindow)
{
/* Releases memory occupied by auxiliary window data.
*/

 DisposeRgn(dataRecord.titleRgn);
 if (theWindow->goAwayFlag)
 {
 KillPoly(dataRecord.goAwayPoly);
 DisposeRgn(dataRecord.goAwayRgn);
 }
 if (dataRecord.resizable)
 {
 KillPoly(dataRecord.growPoly);
 DisposeRgn(dataRecord.growRgn);
 }
 if (theWindow->spareFlag)
 {
 KillPoly(dataRecord.zoomPoly);
 DisposeRgn(dataRecord.zoomRgn);
 }
 DisposHandle(theWindow->dataHandle);
}


pascal long main(varCode, theWindow, message, param)
short   varCode;
WindowPeektheWindow;
short   message;
long    param;
{
/* Sets up registers, copies dataHandle information into global variables, 
dispatches control to other routines, and copies updated information 
back into the window’s dataHandle.
*/
long    returnVal = 0L;

 RememberA0();
 SetUpA4();
 if (message != wNew)
 {
 dataRecordHdl = (DataRecordHdl) theWindow->dataHandle;
 BlockMove(*dataRecordHdl, &dataRecord, sizeof(DataRecord));
 }
 switch(message)
 {
 case wDraw:
 DrawWindow(theWindow, param);
 break;
 case wHit:
 returnVal = DoHit(theWindow, topLeft(param));
 break;
 case wJUST_DRAGGED:
 theWindow->titleWidth = FALSE;
 case wCalcRgns:
 CalcRgns(theWindow);
 break;
 case wNew:
 theWindow->spareFlag = varCode & ZOOM_MASK;
 dataRecord.resizable = !(varCode & NO_GROW_MASK);
 InitNewWindow(theWindow);
 break;
 case wDispose:
 WindowDisposal(theWindow);
 RestoreA4();
 return(returnVal);
 break;
 case wDrawGIcon:
 DrawGrowIcon(theWindow);
 break;
 case wGrow:
 GrowWindowGrow(theWindow, (Rect *) param);
 break;
 case wNEW_TITLE:
 NewTitle(theWindow, (Ptr) param);
 break;
 }
 HLock(dataRecordHdl);
 BlockMove(&dataRecord, *dataRecordHdl, sizeof(DataRecord));
 HUnlock(dataRecordHdl);
 RestoreA4();
 return(returnVal);
}



/* ============================================================

File: WorkDiamondWinds.c
Function: Controls Diamond Windows.
History: 7/4/90  &  8/26/90 - 1/16/91  &  2/15/91  &  4/15/91 -
 4/22/91 : Marz. 

This file contains additional code illustrating the appropriate methodology 
for operating Diamond Windows.

============================================================ */

#include“WindowMgr.h”
#include“DiamondDef.h”


#define NO_VARIATION 0
#define wJUST_DRAGGED50
#define wNEW_TITLE 51

#define FLUSH_EVENTS 0
#define VISIBLE  TRUE
#define DIAMOND_DOC_PROC_PLUS_ZOOM 16008
#define NO_GROW_DIAMOND_PROC16012
#define PLACE_IN_FRONT    -1L
#define GO_AWAY_BOXTRUE
#define NULL_REF_NUM 0


typedef struct ZoomInfo {
 short  top;
 short  midX;
 short  length;
} ZoomInfo;


EventRecord theEvent;

/* ========================================================= */


void    ZoomDiamondFrame(theWindow, expand)
WindowPeektheWindow;
Boolean expand;
{
/* This function expands or shrinks a series of diamonds around the About 
Diamond when it is being Zoomed.  It’s a very nice effect because it 
takes the user’s mind off how long it’s taking her or his window’s contents 
to be redrawn and it also it looks cool.
*********************** Acknowledgement ***********************
 I found a routine for drawing zoom indicator lines around windows in 
the Febuary 1987 issue of MacTutor.  I modified the original code written 
by Jean Decombe so that it would work well with diamond shaped windows 
and optimized it a touch by performing all division operations as bit 
shifts, using the Line() command instead of LineTo(), and by using the 
% {mod} function to reduce the number of necessary storage areas {normally 
rectangles, ZoomInfos in this case}.
*/
GrafPtr savedPort, deskPort;
Rect    startRect, endRect, zoomRect;
long    unit;
short   index, topDiff, leftDiff, bottomDiff, rightDiff;
ZoomInfozoomSpot[4], holder;

 startRect = ((WStateData*) *(theWindow->dataHandle))
 ->stdState;
 endRect = ((WStateData*) *(theWindow->dataHandle))
 ->userState;
 InsetRect(&startRect, -26, -26);
 InsetRect(&endRect, -26, -26);
 if (expand)
 {
 zoomRect = startRect;
 startRect = endRect;
 endRect = zoomRect;
 }
 topDiff = endRect.top - startRect.top;
 leftDiff = endRect.left - startRect.left;
 bottomDiff = endRect.bottom - startRect.bottom;
 rightDiff = endRect.right - startRect.right;
 GetPort(&savedPort);
 GetWMgrPort(&deskPort);
 InitPort(deskPort);
 PenMode(notPatXor);
 PenPat(&white);
 for (index = 0; index <= 20; index ++)
 {
 if (index >= 4)
 {
 holder = zoomSpot[index % 4];
 MoveTo(holder.midX, holder.top);
 Line(holder.length, holder.length);
 Line(-holder.length, holder.length);
 Line(-holder.length, -holder.length);
 Line(holder.length, -holder.length);
 }
 if (index <= 16)
 {
 zoomRect = startRect;
 unit = ((expand ? index + 1: 33 - index) * index) >> 1;
 zoomRect.top += (unit * topDiff) >> 7;
 zoomRect.left += (unit * leftDiff) >> 7;
 /* zoomRect.bottom never needs to be adjusted because diamond shaped 
windows grow in the horizontal and vertical directions at the same rate.
 */
 zoomRect.right += (unit * rightDiff) >> 7;
 holder.top = zoomRect.top;
 holder.midX = (zoomRect.right + zoomRect.left) >> 1;
 holder.length = (zoomRect.right - zoomRect.left) >> 1;
 MoveTo(holder.midX, holder.top);
 Line(holder.length, holder.length);
 Line(-holder.length, holder.length);
 Line(-holder.length, -holder.length);
 Line(holder.length, -holder.length);
 zoomSpot[index % 4] = holder;
 }
 }
 PenMode(patCopy);
 PenPat(&black);
 SetPort(savedPort);
}


Boolean DoMouseDown(thePart, theWindow)
short   thePart;
WindowPtr theWindow;
{
/* Handles mouse clicks.  Returns TRUE if the window was clicked in its 
close box.
*/
Boolean disposedWindow = FALSE;
Handle  wDefHdl;
long    newSize;
Rect    growRect, sizeRestrict, theRect;
RgnHandle theRgn, strucRectRgn;
short   adjust, offset, currentSize;
WindowPeekwindPkr;

 if (theWindow != NULL)
 SetPort(theWindow);
 switch(thePart)
 {
 case inDesk:
 case inSysWindow:
 SystemClick(&theEvent, theWindow);
 break;
 case inDrag:
 windPkr = (WindowPeek) theWindow;
/* The Window Manger automatically calls WDEFs passing the wDraw message 
when part of a window is dragged back onscreen after having been offscreen. 
 This segment of code sets the dragged member of the DataRecord structure, 
if the WDEF is called the dragged member will be set to FALSE.  If it 
remains TRUE after the DragWindow() procedure has been executed then 
the window’s regions need to be updated and the WDEF is called to achieve 
this.
 All this is necessary because when a window is dragged offscreen and 
then back on, the WDEF must recalculate its regions BEFORE redrawing 
the window, so that the full visible portion of the window is redrawn.
*/
 windPkr->titleWidth = TRUE;
 DragWindow(theWindow, theEvent.where,
 &screenBits.bounds);
/* The WDEF should only be called here when <dataRecord>dragged == FALSE. 
Otherwise the diamond window’s regions would be recalculated twice {what 
a waste}.  The Diamond WDEF automatically sets <dataRecord>.dragged to 
FALSE when called with the wJUST_DRAGGED message.
*/
 if (windPkr->titleWidth)
 {
 wDefHdl = windPkr->windowDefProc;
 HLock(wDefHdl);
 CallPascalL(NO_VARIATION, windPkr, wJUST_DRAGGED, 0L,
 *wDefHdl);
 HUnlock(wDefHdl);
 }
 if (theWindow != FrontWindow())
 if (!(theEvent.modifiers & cmdKey))
 SelectWindow(theWindow);
 break;
 case inGrow:
 /* The size restriction is actually 50 for the lower limit and 400 for 
the upper limit.  However, due to the nature of Diamond Windows only 
half these values are used.
 */
 growRect = theWindow->portRect;
 currentSize = (growRect.right - growRect.left) >> 1;
 sizeRestrict.top = sizeRestrict.left = currentSize + 28;
 sizeRestrict.bottom = sizeRestrict.right = currentSize +
 200;
 newSize = GrowWindow(theWindow, theEvent.where,
 &sizeRestrict);
/* adjust originally holds a value which is too large, again due to the 
rate at which diamond windows grow and shrink.  This value is ANDed with 
0xFFFE to ensure that the resultant number is even.  The result must 
then be manipulated so that it corresponds to the actual new size the 
window should be.
*/
/* Only HiWord() because movement left or right is inconsequential.
*/
 adjust = HiWord(newSize) & MAKE_EVEN;
 if ((adjust != 0) && (adjust != (currentSize << 1)))
 {
 /* in case the origin has been offset */
 offset = (adjust - (growRect.right - growRect.left));
 LocalToGlobal(&topLeft(growRect));
 ShowHide(theWindow, FALSE);
 SizeWindow(theWindow, adjust + offset, adjust +
 offset, FALSE);
 MoveWindow(theWindow, growRect.left - offset,
 growRect.top - offset, TRUE);
 ShowHide(theWindow, TRUE);
 }
 break;
 case inGoAway:
 if (TrackGoAway(theWindow, theEvent.where))
 {
 DisposeWindow(theWindow);
 disposedWindow = TRUE;
 }
 break;
 case inContent:
 if (theWindow != FrontWindow())
 SelectWindow(theWindow);
 break;
 case inZoomIn:
 case inZoomOut:
 if (TrackBox(theWindow, theEvent.where, thePart))
 {
 ZoomDiamondFrame((WindowPeek) theWindow, thePart ==
 inZoomOut);
 ShowHide(theWindow, FALSE);
 ZoomWindow(theWindow, thePart, TRUE);
 ShowHide(theWindow, TRUE);
 }
 break;
 }
 return(disposedWindow);
}


void    UpdateDiamond(theWindow)
WindowPeektheWindow;
{
Rect    rgnRect = {20, 10, 180, 190};

 PaintRect(&rgnRect);
}


void    SetDiamondWTitle(theWindow, newTitle)
PtrtheWindow;
Str255  newTitle;
{
/* Use the following lines to change a diamond window’s title.
*/
Handle  wDefHdl;

 wDefHdl = ((WindowPeek) theWindow)->windowDefProc;
 HLock(wDefHdl);
 CallPascalL(0, theWindow, wNEW_TITLE, newTitle, *wDefHdl);
 HUnlock(wDefHdl);
}


void  DiamondWindowTest()
{
/* Operates diamond shaped windows demonstrating many of the WDEF’s abilities.
*/
GrafPtr oldPort;
Handle  wDefHdl;
Rect    windRect = {50, 50, 215, 215}, theRect;
short   openWindows, thePart;
Str255  newTitle = “\pThe Only Window”;
WindowPtr theWindow;

 NewWindow(NULL, &windRect, “\pDiamond Window”, VISIBLE,
 DIAMOND_DOC_PROC_PLUS_ZOOM, PLACE_IN_FRONT, GO_AWAY_BOX,
 NULL_REF_NUM);
/* When you wish to define diamond windows from resource definitions, 
everything should be set up normally.  Then, once you are satisfied with 
the configuration, go back and add an offset to the procID member to 
reflect the use of a custom WDEF.  The offset is the ID number assigned 
to the WDEF multiplied by 16.  In this case the ID number was 1000, so 
the offset is (16 * 1000) or 16000.  However, DIAMOND_DOC_PROC_PLUS_ZOOM 
= 16008.  The additional 8 specifies that the window should have one 
of each of the following regions: grow, goAway, and zoom.
*/
/* these will be equalized by the WDEF */
 InsetRect(&windRect, -20, -40);
 OffsetRect(&windRect, 130, 55);
 NewWindow(NULL, &windRect, “\pAnother Diamond Window”,
 VISIBLE, NO_GROW_DIAMOND_PROC, PLACE_IN_FRONT,
 GO_AWAY_BOX, NULL_REF_NUM);
 openWindows = 2;
 do
 {
 WaitNextEvent(everyEvent, &theEvent, 0L, 0L);
 theWindow = (WindowPtr) theEvent.message;
 if (theWindow != NULL)
 SetPort(theWindow);
 switch(theEvent.what)
 {
 case mouseDown:
 thePart = FindWindow(theEvent.where, &theWindow);
 if (DoMouseDown(thePart, theWindow))
 {
 openWindows --;
 if (openWindows == 1)
 {
 theWindow = FrontWindow();
 SetDiamondWTitle(theWindow, newTitle);
 /* Demostration of SetOrigin()    */
 SetPort(theWindow);
 SetOrigin(-30, -50);
 windRect = theWindow->portRect;
 /* Must also offset the clipRgn so that it corresponds with the new 
window coordinates.
 */
 if ( (**((DataRecordHdl) ((WindowPeek)
 theWindow)->dataHandle)).resizable)
 OffsetRgn(theWindow->clipRgn, -30, -50);
 else
 InsetRect(&windRect, -26, -26);
 EraseRect(&windRect);
 InvalRect(&windRect);
 }
 }
 break;
 case activateEvt:
 case updateEvt:
 GetPort(&oldPort);
 SetPort(theWindow);
 DrawGrowIcon(theWindow);
 BeginUpdate(theWindow);
 UpdateDiamond((WindowPeek) theWindow);
 EndUpdate(theWindow);
 SetPort(oldPort);
 break;
 }
 }
 while (openWindows > 0);
}


void    main()
{
/* Initializes the ToolBox and calls the window handling function.
*/
PtrreservedMem;
Rect    screenRect;
short   index;

 MaxApplZone();
 InitGraf(&thePort);
 InitFonts();
 InitWindows();
 TEInit();
 InitDialogs(NULL);
 InitMenus();
 FlushEvents(everyEvent, FLUSH_EVENTS);
 InitCursor();
 DiamondWindowTest();
 ExitToShell();
}

 
AAPL
$116.47
Apple Inc.
+0.16
MSFT
$47.98
Microsoft Corpora
-0.72
GOOG
$537.50
Google Inc.
+2.67

MacTech Search:
Community Search:

Software Updates via MacUpdate

Cobook 3.0.7 - Intelligent address book....
Cobook Contacts is an intuitive, engaging address book. Solve the problem of contact management with Cobook Contacts and its simple interface and powerful syncing and integration possibilities.... Read more
StatsBar 1.9 - Monitor system processes...
StatsBar gives you a comprehensive and detailed analysis of the following areas of your Mac: CPU usage Memory usage Disk usage Network and bandwidth usage Battery power and health (MacBooks only)... Read more
Cyberduck 4.6 - FTP and SFTP browser. (F...
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more
Maya 2015 - Professional 3D modeling and...
Maya is an award-winning software and powerful, integrated 3D modeling, animation, visual effects, and rendering solution. Because Maya is based on an open architecture, all your work can be scripted... Read more
Evernote 6.0.1 - Create searchable notes...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
calibre 2.11 - Complete e-library manage...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
Herald 5.0.1 - Notification plugin for M...
Note: Versions 2.1.3 (for OS X 10.7), 3.0.6 (for OS X 10.8), and 4.0.8 (for OS X 10.9) are no longer supported by the developer. Herald is a notification plugin for Mail.app, Apple's Mac OS X email... Read more
Firetask 3.7 - Innovative task managemen...
Firetask uniquely combines the advantages of classical priority-and-due-date-based task management with GTD. Stay focused and on top of your commitments - Firetask's "Today" view shows all relevant... Read more
TechTool Pro 7.0.6 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PhotoDesk 3.0.1 - Instagram client for p...
PhotoDesk lets you view, like, comment, and download Instagram pictures/videos! (NO Uploads! / Image Posting! Instagram forbids that! AND you *need* an *existing* Instagram account). But you can do... Read more

Latest Forum Discussions

See All

Ubisoft Gives Everyone Two New Ways to E...
Ubisoft Gives Everyone Two New Ways to Earn In-Game Stuff for Far Cry 4 Posted by Jessica Fisher on November 21st, 2014 [ permalink ] | Read more »
Golfinity – Tips, Tricks, Strategies, an...
Dig this: Would you like to know what we thought of being an infinite golfer? Check out our Golfinity review! Golfinity offers unlimited ways to test your skills at golf. Here are a few ways to make sure your score doesn’t get too high and your... | Read more »
Dark Hearts, The Sequel to Haunting Meli...
Dark Hearts, The Sequel to Haunting Melissa, is Available Now Posted by Jessica Fisher on November 21st, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Meowza! Toyze Brings Talking Tom to Life...
Meowza! | Read more »
Square Enix Announces New Tactical RPG f...
Square Enix Announces New Tactical RPG for Mobile, Heavenstrike Rivals. Posted by Jessica Fisher on November 21st, 2014 [ permalink ] With their epic stories and gorgeous graphics, | Read more »
Quest for Revenge (Games)
Quest for Revenge 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: The great Kingdom of the west has fallen. The gods ignore the prayers of the desperate. A dark warlord has extinguished... | Read more »
Threadz is a New Writing Adventure for Y...
Threadz is a New Writing Adventure for You and Your Friends Posted by Jessica Fisher on November 21st, 2014 [ permalink ] In the tradition of round-robin storytelling, | Read more »
SteelSeries Stratus XL Hardware Review
Made by: SteelSeries Price: $59.99 Hardware/iOS Integration Rating: 4 out of 5 stars Usability Rating: 4.5 out of 5 stars Reuse Value Rating: 4.25 out of 5 stars Build Quality Rating: 4.5 out of 5 stars Overall Rating: 4.31 out of 5 stars | Read more »
ACDSee (Photography)
ACDSee 1.0.0 Device: iOS iPhone Category: Photography Price: $1.99, Version: 1.0.0 (iTunes) Description: Capture, perfect, and share your photos with ACDSee. The ACDSee iPhone app combines an innovative camera, a powerful photo... | Read more »
ProTube for YouTube (Entertainment)
ProTube for YouTube 2.0.2 Device: iOS Universal Category: Entertainment Price: $1.99, Version: 2.0.2 (iTunes) Description: ProTube is the ultimate, fully featured YouTube app. With it's highly polished design, ProTube offers ad-free... | Read more »

Price Scanner via MacPrices.net

Save up to $400 with Apple refurbished 2014 1...
The Apple Store has restocked Apple Certified Refurbished 2014 15″ Retina MacBook Pros for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and shipping... Read more
New 13-inch 1.4GHz MacBook Air on sale for $8...
 Adorama has the 2014 13″ 1.4GHz/128GB MacBook Air on sale for $899.99 including free shipping plus NY & NJ tax only. Their price is $100 off MSRP. B&H Photo has the 13″ 1.4GHz/128GB MacBook... Read more
Apple Expected to Reverse Nine-Month Tablet S...
Apple and Samsung combined accounted for 62 percent of the nearly 36 million branded tablets shipped in 3Q 2014, according to early vendor shipment share estimates from market intelligence firm ABI... Read more
Stratos: 30 Percent of US Smartphone Owners t...
Stratos, Inc., creator of the Bluetooth Connected Card Platform, has announced results from its 2014 Holiday Mobile Payments Survey. The consumer survey found that nearly one out of three (30 percent... Read more
2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has lowered their price on the new 1.4GHz Mac mini to $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new... Read more
Check Apple prices on any device with the iTr...
MacPrices is proud to offer readers a free iOS app (iPhones, iPads, & iPod touch) and Android app (Google Play and Amazon App Store) called iTracx, which allows you to glance at today’s lowest... Read more
64GB iPod touch on sale for $249, save $50
Best Buy has the 64GB iPod touch on sale for $249 on their online store for a limited time. Their price is $50 off MSRP. Choose free shipping or free local store pickup (if available). Sale price for... Read more
15″ 2.2GHz Retina MacBook Pro on sale for $17...
 B&H Photo has the 2014 15″ 2.2GHz Retina MacBook Pro on sale for $1799.99 for a limited time. Shipping is free, and B&H charges NY sales tax only. B&H will also include free copies of... Read more
New Logitech AnyAngle Case/Stand Brings Flexi...
Logitec has announced the newest addition to its suite of tablet products — the Logitech AnyAngle. A protective case with an any-angle stand for iPad Air 2 and all iPad mini models, AnyAngle is the... Read more
Notebook PC Shipments Rise Year-Over-Year as...
According to preliminary results from the upcoming DisplaySearch Quarterly Mobile PC Shipment and Forecast Report, the global notebook PC market grew 10 percent year-over-year in Q3’14 to 49.4... Read more

Jobs Board

*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Store Leader Program - College Gradu...
Job Description: Job Summary As an Apple Store Leader Program agent, you can continue your education as you major in the art of leadership at the Apple Store. You'll Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.