TweetFollow Us on Twitter

Life
Volume Number:3
Issue Number:9
Column Tag:Macintosh II

Life, Quickdraw & The Picker

By Steven Sheets, Contributing Editor, Hoffman Estates,. IL

Steve wins our Program of the Month award for his excellent treatment of the whole subject of color quickdraw as presented in this example program. Enjoy an extra $50 on MacTutor, and thanks for sharing the technology!

Color Life

Life is one of the oldest computer “Games”. Besides being an extremely interesting mathematical puzzle, even a black and white Life program can provide hypnotising graphic animation. It is almost appropriate that this column’s first Color Quickdraw example is this classic program. Using the full color of the Macintosh //, Life is more spell binding than ever! [Yeah, now if Apple would ever ship us some color monitors maybe we’ll see what this program can really do some day! -Ed]

This article will detail the development of a Color Quickdraw program, Color Life. It will explain how to check for Color Quickdraw, how create Color Grafports/Windows, how the Color RGB Model works, how Color Drawing works, how to use the Color Picker Package and how to create Pop Up Menus. First however it will explain what the game of Life is about....

Game of Life

Life simulates the life and death of a group of cells from one generation to the next. Usually the goal is to find some stable life form (ie. one that will not die). Life was originally designed by Prof. John Conway at the University of Cambridge. It first appeared in Martin Gardner’s “Mathematical Games” column of Scientific American in October 1970. Scientific American and Byte Magazine are two of the main sources of good articles on Life.

The rules of Life are simple. The game is played on a two dimensional grid of a certain size. Each spot (or cell) on the grid can be alive or dead (empty). Also each cell is surrounded by it’s 8 neighboring cells, which can also be alive or dead. The number of living cells around the center cell becomes very important in calculating deaths and births each turn.

Every turn (usually called a generation), a living cell can die or live on to the next generation. Also a empty cell can have a birth (ie. cell becomes alive). If a live cell is surrounded by 2 or 3 cells this generation, it will survive till the next generation. If a live cell is surrounded by 1 or less cells this generation, it dies (from starvation). If the cell is surrounded by 4 or more cells this generation, it dies (from over crowding). Finally if a empty cell is surrounded by exactly 3 living neighbors, there is a birth there. Next generation a new cell exists at that spot.

From these simple rules, Life emerges.

Fig. 1 Color Quickdraw (cmd-shift-3 only works in black and white!)

Color QuickDraw

The very first thing a Macintosh // Color program has to check for is what environment the program is running in. The program is not actually looking for a Macintosh //; it requires Color Quickdraw to run. While the Macintosh // is now the only computer with Color Quickdraw, it may not be in the future (upgraded Mac //, Mac SE with Color Quickdraw expansion card or even a Mac ///).

No matter what the computer, if Color Quickdraw exists on the machine, the two high bits of the low-memory global ROM85 (word at $028E) will be cleared (set to zero). By examining this memory location, a program can discover if Color Quickdraw exists. The check for Color Quickdraw should be done after the normal Mac Initialization (InitGraf, InitFont, InitWindow, etc.), but before any Color Quickdraw commands are called (creating Color Grafports or color Pixel Patterns). If Color Quickdraw does not exist, the program should politely inform the user that he needs a Macintosh //. A system bomb is not a polite way of informing the user!

Color Grafports & Color Windows

Once the program knows Color Quickdraw exists, it can create and work with Color Grafports. All the new Color Quickdraw commands must be done on a Color Grafport, not an old-style Grafport. All the older Quickdraw commands have been expanded so they can work with either a Grafport or a Color Grafport (also called cGrafport). The cGrafport data structure is the same size as the older Grafport structure. While many data fields are the same, some have been changed. The exact format is not important for this program since none of the fields are accessed directly. The new cGrafports are allocated in manners similar to the old Grafports (either a NewPtr call or a pointer to a holding Variable). However, instead of using the Quickdraw commands OpenPort, InitPort and ClosePort, the new Color Quickdraw commands OpenCPort, InitCPort and CloseCPort are used to open, init and close a Color Grafport.

In practise, the new Color Quickdraw Port commands are used as often as the older Quickdraw Port commands; that is almost never. The vast majority of the Macintosh programs draw on a Window (which keeps track of the Grafport itself). Generally the only time a Macintosh program directly manipulates a Grafport is when using an off screen bitmap.

The Window Manager has been expanded to include Color Windows. The changes in the Window Manager are similar to the changes in Color Quickdraw. There is a new Color Window Record of the same size as the old Window Record. All of the field are the same except the Port field now contains a cGrafPort instead of a Grafport. All existing Window Manager calls have been expanded to so that either a Window Pointer or a Color Window Pointer can be used with them. Where the NewWindow or GetNewWindow command was used to create a window, the NewCWindow and GetNewCWindow commands create a color window. The two new commands even have the same parameters and purpose (create a window from scratch or from a window resource) as the old commands. They just return a Color Window instead (complete with a correctly created Color Grafport).

RGB Color

To understand exactly how Color Drawing is handled on the newly created Color Grafport/Windows, the RGB color model must be reviewed. Color Quickdraw uses a RGB color model as an conceptional model for all drawing. A color is defined as 3 non-signed integer values (0-65535). This defines the strength of the Red, Green and Blue portions of the color. A Black color would have the values 0,0,0, while white would have 65535, 65535, 65535. Blue would be 0, 65535, 0, while Purple would be 65535, 0, 65535. There are 1,777,216 distinct colors in this model. When something is conceptionally drawn, it must be in a color defined by this model.

In reality, the number of colors that can be drawn at one time, is dependent on the pixel depth of the color device. Most graphics devices are video card, but they can be almost anything (printer, off screen bit map, etc.). A color device that can allocate 4 bits of memory for each pixel (pixel depth of 4), can use 16 colors at one time (2 to the 4th power). A color device that has pixel depth of 8, can have 256 colors at one time (2 to the 8th power). Remember that usually the user can set what pixel depth the video card is using by using the Monitor portion of the Control Panel. A 8 pixel depth card may be only using 4, 2 or even 1 pixels at a specific moment. When color Quickdraw tries to draw in a specific RGB color, it uses the Color Manager to find the closest match on the graphics device. That is the color that is actually displayed.

A good example of this would be trying to display a light, medium and dark shade of red. A video card which has been set (and as enough memory) for a pixel depth of 8 would probably have a few shades of red. Even if the shades were not the exact RGB colors requested, the user would at least know that one shade was darker and one shade lighter than regular red when the colors were displayed on the screen. Suppose then the user set the video card pixel level to 4, thus only having 16 colors at one time. There most likely would only be one red color being used by the video card at one time. Chances are that the programs 3 red RGB shades would match to the cards single red color (it would be the closest match for any of them). The user would then not be able to tell the 3 colors apart on the screen.

The Color Quickdraw commands RGBForeColor and RGBBackColor set the RGB value of the Foreground and Background color. Remember that when called, these routine use the Color Manager to determine the best RGB color match of the graphics device that is being drawn on. This is the true RGB color that will be displayed on the screen.

All text is drawn in the Foreground Color. Copybits will display bitmaps in the Foreground and Background Colors . Set pixels will appear in the Foreground Color; unset pixels will appear in the Background Color. The Foreground and Background Colors may also effect the drawing of a new Color Quickdraw data structure, the Pix Pattern.

Fig. 2 Our menus for the game of life

Color Pix Patterns

Most commonly used Quickdraw drawing commands are done with the Pen. The Move and Moveto commands use the current setting of the pen. The various Paint commands (PaintRect, PaintOval, PaintPoly, etc.) also use the pen’s settings. Old Quickdraw would draw the pen using the Pen Pattern; Color Quickdraw draws using the Pen’s Color Pattern or Pix Pattern. In old Quickdraw, similar to the Pen Pattern, there is Background Pattern. All Erase commands (EraseRect, EraseOval, ErasePoly, etc.) used the Background Pattern to draw with. With Color Quickdraw, similar to the Pen’s Pix Pattern, there is a Background Pix Pattern. What these two Pix Pattern displays depend on how the Pattern was created and thus what type of Pix Pattern it is.

There are 3 types of Pix Patterns. A Pix Pattern variable is a handled to a very elaborate data structure (including handles to other more complex data structures). For this discussion the actual values and formats of the data type is not important.

The simplest Pix Pattern is a monochrome Pix Pattern. Basically the old Quickdraw’s 8 by 8 Pattern is expanded to a Pix Pattern with no preset colors. Instead the monochrome Pix Pattern always uses the current RGB Foreground and Background color to draw in. Where bits (pixels) in the pattern are set, the pixel is drawn in the Foreground color, where they are cleared, it is drawn in the Background color. Changing the Foreground or Background color, does not change the Pix Pattern. Any future drawing with the Pix Pattern will be in the new colors.

The Foreground and Background Pix Patterns can be reset by using the old PenPat and BackPat command. On a Color Quickdraw Grafport/Window, these commands reset the Foreground and Background Pix Pattern to a newly created monochrome Pix Pattern (using the Pattern that is passed as the model). Thus by setting RGB Foreground Color to red, the RGB Background color to white and the PenPat to a brick pattern, the various Pen Drawing commands would draw with a red and white brick pattern. If RGB Foreground Color was suddenly set to Blue, the Pen Drawing commands would draw with a blue and white brick pattern. Thus the PenPat and BackPat commands are two methods to set the Pen Pix Pattern and the Background Pix Pattern.

The second type of Pix Pattern is the RGB Pix Pattern. This Pix Pattern contains an approximation of a certain RGB color. To make it, first a empty Pix Pattern must be created using the NewPixPat function. This Pix Pattern has no value at this point. Then the Pix Pattern and a specific RGB value is passed to MakeRGBPat. This reconstructs the Pix Pattern (it is a handle, remember) into a RGB Pix Pattern. If there is an RGB color used by the Graphics device that is close enough to the RGB color, that entire Pix Pattern is set to that color. If there is not an RGB color that close, Quickdraw tries to construct a Pix Pattern of that approximates that RGB pattern. It does this by mixing 2 RGB colors the graphic device allows in the Pix Pattern. This process is called dithering. In the above shades of Red example, imagine if the three red shades were used to create three Pix Patterns. On a 8 Pixel video card, the three Pix Patterns would probably display a solid red pattern, each red a different shade. When the video card was set to 4 Pixel mode, the medium shade would probably still remain a solid red pattern. However the lighter red Pix Pattern would display a pattern mixing red and white, while the darker red Pix Pattern would display a pattern mixing red and black. When running the example program, play with the Pixel depth of the video Card (using the Monitor portion of the Control Panel) to get an idea of how these Pix Patterns appear. Notice that every time the Pixel depth changes, the RGB Pix Pattern seems to be recalculated without ever calling the MakeRGBPat routine.

The last type of Pix Pattern is the Full Color Pix Pattern. This is the most powerful Pix Pattern. It can have almost any size and any color or combination of colors. This Pix Pattern can be created using the NewPixPat, and then having the data structure stuffed with the correct values. The other, easier method of creating this Pix Pattern is to read it in from a resource file using the GetPixPat function. The Pix Pattern read in can be of any type, but usually GetPixPat is used only for Full Color ones. This type of Pix Pattern (and example of the resource data structure) is not used in the example program.

Once the Pix Patterns are created, the new Color Quickdraw commands PenPixPat and BackPixPat can be used to set the Pen Pix Pattern and Background Pix Pattern. Remember Pix Patterns are handles. Calling the PenPixPat and BackPixPat command (or the PenPat and BackPat command) does not dispose of the old Pix Pattern if it was set using PenPixPat and BackPixPat. The call only resets the handle in use; the original data remains unchanged. Pix Pattern handles created by the program (using NewPixPattern or GetPixPattern) are safe and can be reused. This is not true of Pix Patterns created using PenPat or BackPat. Somehow Color Quickdraw keeps track of Pix Patterns created this way, and disposes of them when they are not needed (the program does not have to do this).

Fig. 3 The Life Game in Living(?) Color(Pictured is the seventh generation; the other generations are in color and cannot be captured)

Other Color Commands

Besides using the older Quickdraw commands, Color Quickdraw has also been expanded to include six new color operations; FillCRect, FillCOval, FillCRoundRect, FillCArc, FillCRgn, FillCPoly. They are similar to the old Quickdraw Fill commands (FillRect, FillOval, FillRoundRect, FillArc, FillRgn, FillPoly). However instead of being passed a specific Pattern to fill the graphics area, they are passed a specific color Pix Pattern (of any of the 3 defined types). The graphic area is then filled with the specific color Pattern. These commands completely by pass the existing Foreground RGB Color, Background RGB Color, Foreground Pix Pattern and Background Pix Pattern.

Fig. 4 The Color Picker Dialog in black and white

Color Picker Package

While the program has select a specific RGB color, a user may wish to pick a new color (ex. a paint program). Having the user pick the exact color he wants and provide a good interface to do this, is not a trivial task. Fortunately (and after a lot of good forethought by who ever developed Color Quickdraw) there exists a standardized way of letting the user to this; the Color Picker Package. The Color Picker Package, like all Packages, is not a set of Rom resident routines. The Package is stored as a system resource, to be read in when the program needs it. But like the Standard File Package (with it’s SFGetFile and SFPutFile dialogs), the Color Picker can be used easily by any program. The Picker will be the standard way of selecting RGB colors and should be quickly learned by all Macintosh users, just as SFGetFile and SFPutFile were. Also like SFGetFile and SFPutFile, if there are any upgrades to the Color Picker Package, a program using the Picker will automatically use the new version without having to be modified.

The main call in the Color Picker Package is the GetColor function. The routine is passed a point at which the dialog is drawn (the dialog is centered if the point is set to 0,0), a prompt string, an in going RGB color and a VAR to an out going RGB color. The routine will display and run the Picker dialog. The function will return False if the user presses Cancel. If he presses OK, the function will return True and the out going RGB variable will contain the new RGB color. If that RGB color is being used for something, remember to update everything (RGB variables, PixMaps, Color Windows, etc.).

PopUp Menus

One of the new user interfaces on the Macintosh is the Popup Menu. This menu is similar to the normal Menu Bar based menu, but can appear anywhere on a window. This feature is not exclusive to the Macintosh //. The latest System/Finder has been patched so that any Macintosh with the Enhanced Roms or later (Mac 512KE, MacPlus, Mac SE, Mac //) have the feature. While the PopUp Menu call (PopupMenuSelect) is not documented in the current release of Inside Macintosh V from APDA, the Rom call is implemented in the current release of MPW from APDA.

The Menu to be used as a Popup menu must be created the same way any other Menu is created (with the NewMenu or GetMenu commands). The Menu must then be added to the Menu List with the InsertMenu command. However unlike normal menus, the beforeID parameter of the InsertMenu call must be set to -1. This places the Menu in the Menu List, but does not displays it. When a mouse down occurs at the correct spot in a window, the PopupMenuSelect Function is used. This function is passed the Popup Menu to be displayed, the vertical and horizontal position of where the popup menu should appear (in global coordinates) and the number of the menu item that initially should appear under the cursor. Depending on the position, this menu can be scrolling. The function return a long integer value, exactly like the MenuSelect or MenuKey function, that contains the Menu ID number and Item number in the High and Low words. After the call is made (and the menu disappears), the Menu should be removed from the menu list.

The Code

After explaining the new calls, the actual program is fairly simple. It displays a Life game in color. Depending on how old the cell is (0 generations or dead, 1 generation, 2 generation, etc.), a different RGB color is used to display it. Cells 7 generations or older use the same color (usually they have stabilized). The RGB values are converted into RGB Pix Patterns. Try different color combos. Having a black background and lighter cells create a striking image. Play with the number of Pixels the video card is using and see the effect.

Click in the Life window to create or erase the Life cells. Use the Action Menu to cycle through various numbers of generations. Use the Show Menu (and About Life Menu) to display the other windows. Click in the Color Window to use a Popup Menu to select a color to change and then use the Color Picker to create a new color. Finally use the Save and Load Menus to create and use Life Documents. Notice that the Save and Load commands saves the RGB colors also. The program has a long, but instructive drawing routines (specifically for the Quickdraw Window) that provide examples of how the old and new Drawing Operations (Rect, Oval, Arc, Polygon, etc.) work. It also includes a number of useful procedures including how to identify if color quickdraw is running. A PopUpMenu routine illustrates getting colors with the color picker as well. This should provide a wealth of useful information since Inside Mac volume 5 is pretty much useless due to it’s inaccurate information.

The program was written in Lightspeed Pascal, and MPW 2.0 and used the new Build feature of MPW to automatically compile itself. Of course the program can be compiled/linked by hand. The source includes the Pascal file, Resource file and Make file for the MPW version, and the main, globals unit and color stuff unit for the LS Pascal version. The LSP version is shown here and is virtually identical to the MPW version, except that it is broken into units to keep the main segment under 32K, and the resources are given in RMaker format. If you are typing it into MPW, note the include file names required in the comment at the top of the program. It should be easily ported to either Turbo Pascal or TML Pascal as well. Both the MPW and LSP versions are included on the source code disk for this issue if you don’t want to type it in. Be sure to use a version of your compiler which supports the new Inside Macintosh Volume 5 traps and equates. For LS Pascal, this is version 1.1, which was released last month at the Boston Expo.

The Life algorithm used here is a brute force method, there are plenty of more elegant (translate faster) ones around. This program was designed to be readable first, speedy second. If it was not for the faster speed of the Mac //, this program would almost be to slow.

Next Issue Palatte Manipulation and Animation!


PROGRAM ColorLife;

{A small Mac // Color Application written by Steve Sheets. It plays the 
original Life game. It also demonstrates some of the new commands of 
Color Quickdraw. LS Pascal version.}

{Memtypes, Quickdraw, OSIntf, ToolIntf, PackIntf, PickerIntf;}

 USES
 ROM85, ColorQuickDraw, ColorMenuMgr, ColorWindowMgr, PickerIntf, myColorGlobals, 
ColorStuff;

{Called if Color Quickdraw does not exists to explain why the program 
can not be run.}

 PROCEDURE DoSorry;
 VAR
 n : integer;
 BEGIN
 n := Alert(sorryID, NIL);
 END;

{Standard Init calls to set Macintosh up (regardless if it is a Mac // 
or not).}

 PROCEDURE DoInit;
 BEGIN
 InitGraf(@thePort);
 InitFonts;
 FlushEvents(everyEvent, 0);
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs(NIL);
 InitCursor;
 END;

{Does the setup for this program (only if there is Color Quickdraw). 
Makes the Menus, clears the Dishes, Sets the Colors, make the Color Patterns 
and sets them, Make the windows, and inits in other variable that need 
to be set.}

 PROCEDURE DoSetup;
 VAR
 tempS : str255;
 tempR : rect;
 n : INTEGER;
 BEGIN
 appleMenu := GetMenu(appleID);
 AddResMenu(appleMenu, ‘DRVR’);
 InsertMenu(appleMenu, 0);

 fileMenu := GetMenu(fileID);
 InsertMenu(fileMenu, 0);

 editMenu := GetMenu(editID);
 InsertMenu(editMenu, 0);

 actionMenu := GetMenu(actionID);
 InsertMenu(actionMenu, 0);

 showMenu := GetMenu(showID);
 InsertMenu(showMenu, 0);

 popupMenu := GetMenu(popupID);
 InsertMenu(popupMenu, -1);

 DrawMenuBar;

 ClearDish(OldDish);
 ClearDish(CurDish);

 SetMyColors(0, -1, -1, -1);
 SetMyColors(1, 0, -1, -1);
 SetMyColors(2, 0, -1, 0);
 SetMyColors(3, -1, -1, 0);
 SetMyColors(4, -1, 0, 0);
 SetMyColors(5, -1, 0, -1);
 SetMyColors(6, 0, 0, -1);
 SetMyColors(7, 0, 0, 0);
 CWhite.red := -1;
 CWhite.green := -1;
 CWhite.blue := -1;

 FOR n := 0 TO MaxAge DO
 BEGIN
 myPixMap[n] := NewPixPat;
 MakeRGBPat(myPixMap[n], myColors[n])
 END;

 aboutWindow := GetNewCWindow(aboutID, NIL, POINTER(-1));
 SetPortColors(aboutWindow);

 colorWindow := GetNewCWindow(colorID, NIL, POINTER(-1));
 SetPortColors(colorWindow);

 GetIndString(tempS, strID, 2);
 CenterRect(HQuick, VQuick, tempR);
 quickWindow := NewCWindow(NIL, tempR, tempS, FALSE, noGrowDocProc, POINTER(-1), 
TRUE, 0);
 SetPortColors(quickWindow);

 GetIndString(tempS, strID, 1);
 CenterRect(HLife, VLife, tempR);
 lifeWindow := NewCWindow(NIL, tempR, tempS, TRUE, noGrowDocProc, POINTER(-1), 
TRUE, 0);
 SetPortColors(lifeWindow);

 SetRect(bigRect, -32000, -32000, 32000, 32000);
 doneFlag := FALSE;
 END;

{Standard Main Progrom Loop, loops til done flag is marked.  Handles 
drags, go Aways, keys, and menu the same way.  Updates and mouse downs 
in windows, call the specific routine to handle those events.}

PROCEDURE MainLoop;
VAR
 myWindow : WindowPtr;
 myChar : CHAR;
 myEvent : EventRecord;
 myPort : grafptr;
BEGIN
REPEAT
SystemTask;
IF GetNextEvent(everyEvent, myEvent) THEN
CASE myEvent.what OF
mouseDown : 
 CASE FindWindow(myEvent.where, myWindow) OF
 inSysWindow : 
 SystemClick(myEvent, myWindow);
 inMenuBar : 
 DoCommand(MenuSelect(myEvent.where));
 inDrag : 
 DragWindow(myWindow, myEvent.where, bigRect);
 inGoAway : 
 IF TrackGoAway(myWindow, myEvent.where) THEN
 HideWindow(myWindow);
 inContent : 
 IF myWindow <> frontWindow THEN
 SelectWindow(myWindow)
 ELSE IF myWindow = lifeWindow THEN
 DoLifeClick(myEvent.where)
 ELSE IF myWindow = colorWindow THEN
 DoColorClick(myEvent.where);
 OTHERWISE
 BEGIN
 END;
 END;

keyDown, autoKey : 
 BEGIN
 myChar := CHR(BitAnd(myEvent.message, charCodeMask));
 IF BitAnd(myEvent.modifiers, cmdKey) <> 0 THEN
 DoCommand(MenuKey(myChar));
 END;

updateEvt : 
 BEGIN
 GetPort(myPort);
 myWindow := WindowPtr(myEvent.message);
 BeginUpdate(myWindow);
 SetPort(myWindow);
 IF myWindow = lifeWindow THEN
 DoLifeDraw
 ELSE IF myWindow = colorWindow THEN
 DoColorDraw
 ELSE IF myWindow = aboutWindow THEN
 DoAboutDraw
 ELSE IF myWindow = quickWindow THEN
 DoQuickDraw;
 EndUpdate(WindowPtr(myEvent.message));
 SetPort(myPort);
 END;

OTHERWISE
END;
UNTIL doneFlag;
END;

{Main Program.  First calls DoInit to do the standard inits.  Then checks 
if Color Quickdraw Exists.  If so, does Setup and Mainloop.  If not, 
call DoSorry to explain.}
BEGIN
 DoInit;
 IF ColorQDExists THEN
 BEGIN
 DoSetup;
 MainLoop;
 END
 ELSE
 DoSorry;
END.


UNIT ColorStuff;

INTERFACE

 USES
 ROM85, ColorQuickDraw, ColorMenuMgr, ColorWindowMgr, PickerIntf, myColorGlobals;

 PROCEDURE ClearDish (VAR J : Dish);
 PROCEDURE SetMyColors (i, r, g, b : integer);
 PROCEDURE SetPortColors (W : windowptr);
 PROCEDURE CenterRect (h, v : INTEGER;
 VAR R : rect);
 PROCEDURE DoCommand (mResult : LONGINT);
 PROCEDURE DoColorClick (Where : point);
 PROCEDURE DoLifeClick (Where : point);
 PROCEDURE DoLifeDraw;
 PROCEDURE DoColorDraw;
 PROCEDURE DoAboutDraw;
 PROCEDURE DoQuickDraw;
 FUNCTION ColorQDExists : boolean;

IMPLEMENTATION

{Given Integer value (corrsponding to a string stored in STR# resource), 
draw that string, centered on point h,v in c color.}

 PROCEDURE DrawCenter (N, h, v, c : integer);
 VAR
 S : str255;
 BEGIN
 RGBForeColor(myColors[c]);
 GetIndString(S, strID, N);
 MoveTo(h - (StringWidth(S) DIV 2), v);
 DrawString(S);
 END;

{Given a Dish, Clear all the cells (set to 0).}

 PROCEDURE ClearDish; {(var J : Dish);}
 VAR
 h, v : integer;
 BEGIN
 FOR h := 0 TO HEdgeMax DO
 FOR v := 0 TO VEdgeMax DO
 J[h, v] := 0;
 END;

{Given a Color Window Port, set the Pen and Background color Patterns 
(stored in myPicMap in position 0 & 1, respectively).}

 PROCEDURE SetPortColors;{(W : windowptr);}
 BEGIN
 SetPort(W);
 BackPixPat(myPixMap[0]);
 PenPixPat(myPixMap[1]);
 END;

{Given a Color Window Port, calls SetPortColors to set Pen and Background 
color Patterns and forces on update by invalidating a big Rectangle.}

 PROCEDURE ForceUpdate (W : windowptr);
 BEGIN
 SetPortColors(W);
 InvalRect(bigRect);
 END;

{Given a Color Window Port, saves the current Port, calls UpdateOne and 
then restore the current port.}

 PROCEDURE UpdateOne (W : windowptr);
 VAR
 tempPort : grafptr;
 BEGIN
 GetPort(tempPort);
 ForceUpdate(W);
 SetPort(tempPort);
 END;

{Saves the current Port, calls ForceUpdate for all the windows to update 
them all and then restore the current port.}

 PROCEDURE UpdateAll;
 VAR
 tempPort : grafptr;
 BEGIN
 GetPort(tempPort);
 ForceUpdate(aboutWindow);
 ForceUpdate(colorWindow);
 ForceUpdate(quickWindow);
 ForceUpdate(lifeWindow);
 SetPort(tempPort);
 END;

{Given a Color Window Port, if it is not in front (or invisible), Shows 
it and Select it (make it the top window).}

 PROCEDURE ShowIt (W : windowptr);
 BEGIN
 IF FrontWindow <> W THEN
 BEGIN
 ShowWindow(W);
 SelectWindow(W);
 END;
 END;

{Loads a new Dish and Dish Colors from disk.  If successful, remakes 
the Dishes Color Patterns and updates all the windows (using UpdateAll).}

PROCEDURE DoLoad;
 VAR
 tempReply : SFReply;
 tempType : SFTypeList;
 tempP : point;
 Ref, n : INTEGER;
 tempE : OSErr;
 tempLong : longint;
BEGIN
tempP.v := 40;
tempP.h := 40;
tempType[0] := FileType;
SFGetFile(tempP, ‘’, NIL, 1, tempType, NIL, tempReply);
Ref := 0;
IF tempReply.good THEN
 BEGIN
 tempE := FSOpen(tempReply.fname, tempReply.vrefnum, Ref);
 IF tempE = noErr THEN
 BEGIN
 tempLong := SIZEOF(Dish);
 tempE := FSRead(Ref, tempLong, @CurDish);
 IF tempE = noErr THEN
 BEGIN
 tempLong := SIZEOF(DishColor);
 tempE := FSRead(Ref, tempLong, @myColors);
 IF tempE = noErr THEN
 BEGIN
 tempE := FSClose(Ref);
 Ref := 0;
 FOR n := 0 TO MaxAge DO
 MakeRGBPat(myPixMap[n], myColors[n]);
 UpdateAll;
 END;
 END;
 END;
 IF Ref <> 0 THEN
 tempE := FSClose(Ref);
 END;
END;

{Save the current Dish and Dish Colors to disk.}

PROCEDURE DoSave;
VAR
 tempReply : SFReply;
 tempP : point;
 Ref : INTEGER;
 tempE : OSErr;
 tempLong : longint;
BEGIN
tempP.v := 40;
tempP.h := 40;
SFPutFile(tempP, ‘’, ‘’, NIL, tempReply);
IF tempReply.good THEN
BEGIN
tempE := FSDelete(tempReply.fname, tempReply.vrefnum);
 IF (tempE = noErr) OR (tempE = fnfErr) THEN
 BEGIN
 tempE := Create(tempReply.fname, tempReply.vrefnum, CreatorType, FileType);
 IF tempE = noErr THEN
 BEGIN
 tempE := FSOpen(tempReply.fname, tempReply.vrefnum, Ref);
 IF tempE = noErr THEN
 BEGIN
 tempLong := SIZEOF(Dish);
 tempE := FSWrite(Ref, tempLong, @CurDish);
 IF tempE = noErr THEN
 BEGIN
 tempLong := SIZEOF(DishColor);
 tempE := FSWrite(Ref, tempLong, @myColors);
 IF tempE = noErr THEN
 BEGIN
 tempE := FSClose(Ref);
 Ref := 0;
 END;
 END;
 END;
 END;
 END;
IF Ref <> 0 THEN
 tempE := FSClose(Ref);
END;
END;

{Given a cells horiztonal and vertical position (in cell position, not 
screen position), return the screen position Rectangle that contains 
that cell.}

 PROCEDURE MakeRect (h, v : integer;
 VAR R : rect);
 VAR
 n : integer;
 BEGIN
 n := (h * Big);
 R.right := n;
 R.left := n - big;
 n := (v * Big);
 R.bottom := n;
 R.top := n - big;
 END;

{Given a cells horiztonal and vertical position (in cell position, not 
screen position) and the correct screen rectangle holding that cell,} 
draws the Cell in the correct pattern.  It will only erase the cell if 
this call was not done by an update event (update events erase everything).}

PROCEDURE DrawRect (h, v : INTEGER;
 R : rect;
 UpEvent : BOOLEAN);
BEGIN
 IF CurDish[h, v] = 1 THEN
 PaintRect(R)
 ELSE IF (CurDish[h, v] > 1) AND (CurDish[h, v] <= MaxAge) THEN
 FillCRect(R, myPixMap[CurDish[h, v]])
 ELSE IF NOT UpEvent THEN
 EraseRect(R);
END;

{Given a point in the window where a mouse down occured (global coordinates), 
flips the cell at that point (erasing one that there} or making on in 
an empty space).  Then loops for every cell the} mouse pass over (drawing 
or erasing cells) until the button is released.}

PROCEDURE DoLifeClick;{(Where : point);}
VAR
 h, v : integer;
 tempPort : grafptr;
 tempR : rect;
 empty : BOOLEAN;

 PROCEDURE CalcWhere;
 BEGIN
 h := (Where.h DIV big) + 1;
 v := (Where.v DIV big) + 1;
 END;

 PROCEDURE DoDraw;
 BEGIN
 MakeRect(h, v, tempR);
 DrawRect(h, v, tempR, FALSE);
 END;

 PROCEDURE DoClick;
 BEGIN
 IF (h > 0) AND (h <= HEdge) AND (v > 0) AND (v <= VEdge) THEN
 BEGIN
 IF Empty THEN
 BEGIN
 IF CurDish[h, v] = 0 THEN
 BEGIN
 CurDish[h, v] := 1;
 DoDraw;
 END;
 END
 ELSE
 BEGIN
 IF CurDish[h, v] <> 0 THEN
 BEGIN
 CurDish[h, v] := 0;
 DoDraw;
 END;
 END;
 END;
 END;

BEGIN
 GetPort(tempPort);
 SetPort(lifeWindow);
 GlobalToLocal(Where);
 CalcWhere;
 empty := (CurDish[h, v] = 0);
 DoClick;
 REPEAT
 GetMouse(where);
 CalcWhere;
 DoClick;
 UNTIL NOT Button;

 SetPort(tempPort);
END;

{Given a mouse down point in the Color Window, display a pop up menu 
listing the RGB colors.  If a color is selected, call the Color Picker 
(GetColor) to select a new color.  If a color is picked, recalculate 
the Color Pattern (for that color) and Update all the windows (using 
UpdateAll).}

PROCEDURE DoColorClick;{(Where : point);}
 VAR
 tempLong : LONGINT;
 M, I : INTEGER;
 tempP : point;
 tempS : str255;
 ColorIt : RGBColor;
BEGIN
tempLong := PopUpMenuSelect(popupMenu, Where.v, Where.h, 1);
 M := HiWord(tempLong);
 I := LoWord(tempLong) - 1;
 IF (M = popupID) AND (0 <= I) AND (I <= MaxAge) THEN
 BEGIN
 tempP.v := 0;
 tempP.h := 0;
 GetIndString(tempS, strID, 3);
 ColorIt := myColors[I];
 IF GetColor(tempP, tempS, ColorIt, ColorIt) THEN
 BEGIN
 myColors[i] := ColorIt;
 MakeRGBPat(myPixMap[i], myColors[i]);
 UpdateAll;
 END;
 END;
END;

{Draws all the cells in the Life window.}

 PROCEDURE DoLifeDraw;
 VAR
 h, v : integer;
 tempR : rect;
 BEGIN
 EraseRect(bigRect);
 FOR h := 1 TO HEdge DO
 FOR v := 1 TO VEdge DO
 BEGIN
 MakeRect(h, v, tempR);
 DrawRect(h, v, tempR, TRUE);
 END;
 END;

{Draws all the colors in the Color window.}

 PROCEDURE DoColorDraw;
 VAR
 tempR : rect;
 n, start, finish : INTEGER;
 BEGIN
 SetRect(tempR, 0, 0, colorSize, colorSize);
 EraseRect(tempR);
 FOR n := 1 TO MaxAge DO
 BEGIN
 start := (360 * (n - 1)) DIV MaxAge;
 finish := (360 * n) DIV MaxAge;
 IF n = 1 THEN
 PaintArc(tempR, start, finish - start)
 ELSE
 FillCArc(tempR, start, finish - start, myPixMap[n]);
 END;
END;

{Draws the About Window (information about the program and a spirograph 
type of desgin).}

 PROCEDURE DoAboutDraw;
 VAR
 h, v, n : INTEGER;
 BEGIN
 EraseRect(bigRect);

 TextSize(24);
 DrawCenter(4, HAbout DIV 2, 120, 7);
 DrawCenter(5, HAbout DIV 2, 150, 6);
 DrawCenter(6, HAbout DIV 2, 180, 5);

 FOR n := 1 TO StepSize - 1 DO
 BEGIN
 h := n * HStep;
 v := n * VStep;

 PenPixPat(myPixMap[4]);
 MoveTo(0, v);
 LineTo(HAbout - h, 0);

 PenPixPat(myPixMap[3]);
 MoveTo(h, 0);
 LineTo(HAbout, v);

 PenPixPat(myPixMap[2]);
 LineTo(HAbout, v);
 LineTo(HAbout - h, VAbout);

 PenPixPat(myPixMap[1]);
 MoveTo(h, VAbout);
 LineTo(0, v);
 END;
 END;

{Draws the Quickdraw Window that displays the major Color Quickdraw commands 
(similar to the Lisa version).}

 PROCEDURE DoQuickDraw;
 VAR
 tempRect : Rect;
 tempPat : Pattern;
 tempPoly : PolyHandle;
 tempRgn : RgnHandle;
 BEGIN
{Erase Background}
 EraseRect(bigRect);
{Draws Squares}
 PenPixPat(myPixMap[1]);
 MoveTo(0, 150);
 LineTo(450, 150);
 MoveTo(150, 0);
 LineTo(150, 300);
 MoveTo(300, 0);
 LineTo(300, 300);
{Draws Titles}
 DrawCenter(7, 75, 20, 2);
 DrawCenter(8, 225, 20, 3);
 DrawCenter(9, 375, 20, 4);
 DrawCenter(10, 75, 170, 5);
 DrawCenter(11, 225, 170, 6);
 DrawCenter(12, 375, 170, 7);
{Draw Rectangles}
 SetRect(tempRect, 15, 25, 95, 105);
 RGBForeColor(myColors[1]);
 RGBBackColor(CWhite);
 PenPat(Black);
 FrameRect(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 RGBForeColor(myColors[2]);
 GetIndPattern(tempPat, 0, 31);
 PenPat(tempPat);
 PaintRect(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 PenPixPat(myPixMap[3]);
 PaintRect(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 RGBForeColor(myColors[4]);
 RGBBackColor(myColors[5]);
 GetIndPattern(tempPat, 0, 32);
 PenPat(tempPat);
 PaintRect(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 FillCRect(tempRect, myPixMap[6]);
{Draw Ovals}
 SetRect(tempRect, 165, 25, 245, 105);
 RGBForeColor(myColors[7]);
 RGBBackColor(CWhite);
 PenPat(Black);
 FrameOval(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 RGBForeColor(myColors[1]);
 GetIndPattern(tempPat, 0, 33);
 PenPat(tempPat);
 PaintOval(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 PenPixPat(myPixMap[2]);
 PaintOval(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 RGBForeColor(myColors[3]);
 RGBBackColor(myColors[4]);
 GetIndPattern(tempPat, 0, 34);
 PenPat(tempPat);
 PaintOval(tempRect);

 OffSetRect(tempRect, QOff, QOff);
 FillCOval(tempRect, myPixMap[5]);

{Draw Round Rectangles}
 SetRect(tempRect, 315, 25, 395, 105);
 RGBForeColor(myColors[6]);
 RGBBackColor(CWhite);
 PenPat(Black);
 FrameRoundRect(tempRect, 20, 20);

 OffSetRect(tempRect, QOff, QOff);
 RGBForeColor(myColors[7]);
 GetIndPattern(tempPat, 0, 35);
 PenPat(tempPat);
 PaintRoundRect(tempRect, 20, 20);

 OffSetRect(tempRect, QOff, QOff);
 PenPixPat(myPixMap[1]);
 PaintRoundRect(tempRect, 20, 20);

 OffSetRect(tempRect, QOff, QOff);
 RGBForeColor(myColors[2]);
 RGBBackColor(myColors[3]);
 GetIndPattern(tempPat, 0, 36);
 PenPat(tempPat);
 PaintRoundRect(tempRect, 20, 20);

 OffSetRect(tempRect, QOff, QOff);
 FillCRoundRect(tempRect, 20, 20, myPixMap[4]);

{Draw Polygons}
 tempPoly := OpenPoly;
 SetRect(tempRect, 15, 175, 95, 255);
 MoveTo(95, 175);
 LineTo(65, 215);
 LineTo(95, 255);
 LineTo(15, 255);
 LineTo(15, 215);
 LineTo(55, 175);
 LineTo(95, 175);
 ClosePoly;

 RGBForeColor(myColors[5]);
 RGBBackColor(CWhite);
 PenPat(Black);
 FramePoly(tempPoly);

 OffSetPoly(tempPoly, QOff, QOff);
 RGBForeColor(myColors[6]);
 GetIndPattern(tempPat, 0, 37);
 PenPat(tempPat);
 PaintPoly(tempPoly);

 OffSetPoly(tempPoly, QOff, QOff);
 PenPixPat(myPixMap[7]);
 PaintPoly(tempPoly);

 OffSetPoly(tempPoly, QOff, QOff);
 RGBForeColor(myColors[1]);
 RGBBackColor(myColors[2]);
 GetIndPattern(tempPat, 0, 38);
 PenPat(tempPat);
 PaintPoly(tempPoly);

 OffSetPoly(tempPoly, QOff, QOff);
 FillCPoly(tempPoly, myPixMap[3]);

 KillPoly(tempPoly);

{Draw Arcs}
 SetRect(tempRect, 165, 175, 265, 275);
 RGBForeColor(myColors[4]);
 RGBBackColor(CWhite);
 PenPat(Black);
 FrameArc(tempRect, 198, 72);

 RGBForeColor(myColors[5]);
 GetIndPattern(tempPat, 0, 15);
 PenPat(tempPat);
 PaintArc(tempRect, 126, 72);

 PenPixPat(myPixMap[6]);
 PaintArc(tempRect, 270, 72);

 RGBForeColor(myColors[7]);
 RGBBackColor(myColors[1]);
 GetIndPattern(tempPat, 0, 16);
 PenPat(tempPat);
 PaintArc(tempRect, 342, 72);

 OffSetRect(tempRect, 20, 0);
 FillCArc(tempRect, 54, 72, myPixMap[2]);

{Draw Regions}
 tempRgn := NewRgn;
 OpenRgn;
 SetRect(tempRect, 315, 175, 395, 255);
 FrameRoundRect(tempRect, 20, 20);
 InsetRect(tempRect, 10, 10);
 FrameOval(tempRect);
 CloseRgn(tempRgn);

 RGBForeColor(myColors[3]);
 RGBBackColor(CWhite);
 PenPat(Black);
 FrameRgn(tempRgn);

 OffSetRgn(tempRgn, QOff, QOff);
 RGBForeColor(myColors[4]);
 GetIndPattern(tempPat, 0, 17);
 PenPat(tempPat);
 PaintRgn(tempRgn);

 OffSetRgn(tempRgn, QOff, QOff);
 PenPixPat(myPixMap[5]);
 PaintRgn(tempRgn);

 OffSetRgn(tempRgn, QOff, QOff);
 RGBForeColor(myColors[6]);
 RGBBackColor(myColors[7]);
 GetIndPattern(tempPat, 0, 18);
 PenPat(tempPat);
 PaintRgn(tempRgn);

 OffSetRgn(tempRgn, QOff, QOff);
 FillCRgn(tempRgn, myPixMap[1]);

 DisposeRgn(tempRgn);
 END;

{Steps through a generation of growth/death of the Dish.  Stores the 
old Graf port, then sets the Life window as the new one.  Copies the 
current dish into the old dish, then checks each cells one by one. Calculates 
the number of cells around it, and decides deaths, lifes and births. 
 If the new value does not match the old value} (ie. change), redraw 
that window.}

 PROCEDURE DoStep;
 VAR
 n, h, v, c : INTEGER;
 tempPort : grafptr;
 tempR : rect;
 BEGIN
 GetPort(tempPort);
 SetPort(lifeWindow);
 BlockMove(@CurDish, @OldDish, SIZEOF(Dish));

 FOR h := 1 TO HEdge DO
 FOR v := 1 TO VEdge DO
 BEGIN
 n := 0;
 IF OldDish[h - 1, v - 1] <> 0 THEN
 n := n + 1;
 IF OldDish[h, v - 1] <> 0 THEN
 n := n + 1;
 IF OldDish[h + 1, v - 1] <> 0 THEN
 n := n + 1;
 IF OldDish[h - 1, v] <> 0 THEN
 n := n + 1;
 IF OldDish[h + 1, v] <> 0 THEN
 n := n + 1;
 IF OldDish[h - 1, v + 1] <> 0 THEN
 n := n + 1;
 IF OldDish[h, v + 1] <> 0 THEN
 n := n + 1;
 IF OldDish[h + 1, v + 1] <> 0 THEN
 n := n + 1;
 c := OldDish[h, v];
 IF (c = 0) AND (n = 3) THEN
 CurDish[h, v] := 1
 ELSE IF (c <> 0) AND ((n = 3) OR (n = 2)) THEN
 BEGIN
 IF c < MaxAge THEN
 CurDish[h, v] := c + 1;
 END
 ELSE
 CurDish[h, v] := 0;

 IF CurDish[h, v] <> c THEN
 BEGIN
 MakeRect(h, v, tempR);
 DrawRect(h, v, tempR, FALSE);
 END;
 END;

 SetPort(tempPort);
 END;

{Standard Menu Command command procedures.  Takes card of Desk Accessories, 
About (show About Window), New (clear jar and update Life window), Load/Save 
(call DoLoad/DoSave), Quit (mark Done Flag), Action Menus  (Step, Step 
10 Times or Step until a Mouse down) and Show Menus  (show one of the 
3 other windows).}
 PROCEDURE DoCommand;{(mResult : LONGINT);}
 VAR
 theItem : INTEGER;
 theMenu : INTEGER;
 name : Str255;
 temp : INTEGER;
 tempBool : BOOLEAN;
 tempEvent : EventRecord;
 BEGIN
 theItem := LoWord(mResult);
 theMenu := HiWord(mResult);

 CASE theMenu OF
 appleID : 
 IF (theItem = 1) THEN
 ShowIt(aboutWindow)
 ELSE
 BEGIN
 GetItem(appleMenu, theItem, name);
 temp := OpenDeskAcc(name);
 END;
 fileID : 
 CASE theItem OF
 1 : 
 BEGIN
 ClearDish(CurDish);
 UpdateOne(lifeWindow);
 END;
 2 : 
 DoLoad;
 3 : 
 DoSave;
 5 : 
 doneFlag := TRUE;
 OTHERWISE
 END;
 editID : 
 tempBool := SystemEdit(theItem - 1);
 actionID : 
 CASE theItem OF
 1 : 
 DoStep;
 2 : 
 FOR temp := 1 TO 10 DO
 DoStep;
 3 : 
 WHILE NOT GetNextEvent(mDownMask, tempEvent) DO
 DoStep;
 OTHERWISE
 END;
 showID : 
 CASE theItem OF
 1 : 
 ShowIt(lifeWindow);
 2 : 
 ShowIt(colorWindow);
 3 : 
 ShowIt(quickWindow);
 OTHERWISE
 END;
 OTHERWISE
 END;
 HiliteMenu(0);
 END;

{Checks if Color Quickdraw Exists on this machine.}

 FUNCTION ColorQDExists; { : boolean;}
 CONST
 ROM85Loc = $28E;
 TwoHighMask = $C000;
 VAR
 WordPtr : ^Integer;
 ROM85Value : integer;
 BEGIN
 WordPtr := pointer(ROM85Loc);
 ROM85Value := WordPtr^;
 ColorQDExists := (BitAnd(ROM85Value, TwoHighMask) = 0);
 END;

{Given a h and v size, calculates a Rectangle that is centered on the 
screen.}

PROCEDURE CenterRect; {(h, v : INTEGER; var R : rect);}
 VAR
 Hoff, Voff : INTEGER;
 BEGIN
 Hoff := (ScreenBits.bounds.right - h) DIV 2;
 VOff := (ScreenBits.bounds.bottom - v) DIV 2;
 IF VOff < 35 THEN
 VOff := 35;
 SetRect(R, Hoff, VOff, Hoff + h, Voff + v);
 END;

{Given RGB values (3 integers) and pos (which , places the values in 
the Dish Color in the correct position.}
 PROCEDURE SetMyColors;{(i, r, g, b : integer);}
 BEGIN
 myColors[i].red := r;
 myColors[i].green := g;
 myColors[i].blue := b;
 END;
END.

UNIT myColorGlobals;

INTERFACE

 USES
 ColorQuickDraw;
{Constants: various Resource IDs, File Type and Creator Signitures, Maximum 
recorded Age of Cell, Horizontal/Vertical size of Dish (containing cells), 
Size of cells, Size of other windows and other size information.}
 CONST
 appleID = 256;
 fileID = 257;
 editID = 258;
 actionID = 259;
 showID = 260;
 popupID = 261;

 strID = 256;
 sorryID = 256;
 aboutID = 256;
 colorID = 257;

 FileType = ‘life’;
 CreatorType = ‘life’;

 MaxAge = 7;
 VEdge = 50;
 VEdgeMax = 51;
 HEdge = 70;
 HEdgeMax = 71;
 Big = 8;
 VLife = 400; {Big * VEdge}
 HLife = 560; {Big * HEdge}

 VQuick = 300;
 HQuick = 450;
 QOff = 10;

 colorSize = 300;
 StepSize = 30;
 HStep = 20;
 VStep = 10;
 HAbout = 600; {StepSize * HStep}
 VAbout = 300; {StepSize * VStep}

{Types: Age range, Dish info (containing all cells & age), Dish Colors 
(containing RGB colors), Dish Color Patterns (RGB info converted to a 
Color Pattern).}
 TYPE
 Age = 0..MaxAge;
 Dish = PACKED ARRAY[0..HEdgeMax, 0..VEdgeMax] OF Age;
 DishColor = PACKED ARRAY[0..MaxAge] OF RGBColor;
 DishPixMap = PACKED ARRAY[0..MaxAge] OF PixPatHandle;

{Variables: Menus (including PopupMenu), Windows, RGB Colors, Color Patterns, 
large Rect (used for drag & updates), Done Flag and Current & 0ld Dishes.}
 VAR
 appleMenu, fileMenu, editMenu, actionMenu, showMenu, popupMenu : MenuHandle;
 lifeWindow, aboutWindow, colorWindow, quickWindow : WindowPtr;
 myColors : DishColor;
 myPixMap : DishPixMap;
 bigRect : Rect;
 doneFlag : BOOLEAN;
 OldDish, CurDish : Dish;
 CWhite : RGBColor;

IMPLEMENTATION

END.


* Life.R
* Resources for Color Life
* © 1987 by Steve Sheets for MacTutor
* 
*
Life.RSRC
????????

Type LIFE = STR 
 ,0
Color Life Application - Version 1.0 by Steve Sheets

Type FREF
  ,128 (0)
APPL 0
  ,129 (0)
LIFE 1

Type BNDL
  ,128 (0)
LIFE 0
ICN#
0 128 1 129 
FREF
0 128 1 129 

TYPE STR#
 ,256
12
Life
Quickdraw
Select Color:
Color Life by Steve Sheets
Demo of Color Quickdraw
on the Mac //
Rectangles
Ovals
RoundRectangles:
Arcs
Polygons
Regions

TYPE WIND
 ,256
About Life
40 20 340 620
Invisible Goaway
4
0

TYPE WIND
 ,257
Colors
40 170 340 470
Invisible Goaway
4
0

* Menus
*   ,ID (attributes)
*   menu title (an Apple symbol is \ 14 in hex)
*   menu items, ( means it’s initially disabled.
*   (-  means a disabled line of dashes.
*   A trailing /Q, etc. means a command-key.
Type MENU
  ,256
\14
About Life 
(-

Type MENU
  ,257
File
New
Load
Save
(-
Quit/Q

Type MENU
  ,258
Edit
Undo/Z
(-
Cut/X
Copy/C
Paste/V
Clear

Type MENU
  ,259
Action
Step/S
Ten Steps/T
Loop til Button/L

Type MENU
  ,260
Show
Life
Colors
Quickdraw

Type MENU
 ,261
x
Select Background Color [Age 0]
Select Foreground Color [Age 1]
Select Age 2 Color
Select Age 3 Color
Select Age 4 Color
Select Age 5 Color
Select Age 6 Color
Select Age 7 [&older] Color


* ------ Dialogs --------
* Program Messages Dialog box...
type DLOG
 ,257
Program Messages
100 100 200 400
Visible NoGoAway
1
0
257

type DITL
 ,257
3
BtnItem Enabled
65 230 95 285
OK

StatText Disabled
15 60 85 222 
^0\0D^1\0D^2\0D^3

IconItem Disabled
10 10 42 42
1

* ------ Alerts ------------

* Program error alerts...
type ALRT
 ,256
40 106 140 406
256
4444

* A Dialog or Alert Item List
*   ,ID (attributes)
*   number of items in list
*   type of item
*   top left bottom right
*   message
Type DITL
  ,256
2

button 
60 120 80 180 
OK

staticText Disabled
20 10 40 290
Sorry, this program only runs on a Mac //

* misc resources

* An icon list for the icon
*   ,ID (attributes)
*   Data is Hex data (.H)
*   the icon data: 32 lines of 8 hex chars each
*   the icon mask: 32 lines of 8 hex chars each
Type ICN# = GNRL
  ,128 (0)
.H
0000 0000 EE00 0EEE EE00 0EEE EE00 0EEE
0000 0000 E000 000E E000 000E E000 000E
0000 0000 000E 00E0 000E 00E0 000E 00E0
0000 0000 00EE 0000 00EE 0000 00EE 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0EE0 0000 0EE0 0000 0EE0 0000
0000 0000 E00E 00EE E00E 00EE E00E 00EE
0000 0000 0EE0 00EE 0EE0 00EE 0EE0 00EE
* [2] 
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE 

* An icon list for the icon
*   ,ID (attributes)
*   Data is Hex data (.H)
*   the icon data: 32 lines of 8 hex chars each
*   the icon mask: 32 lines of 8 hex chars each
Type ICN# = GNRL
  ,129 (0)
.H
1FFF FC00 1000 0600 1000 0500 1000 0480
1000 0440 1360 3420 1360 37F0 1000 0010
1300 00D0 1300 00D0 1000 0010 1001 8610
1001 8610 1000 0010 100D 8010 100D 8010
1000 0010 1000 0010 1000 0010 1000 0010
106C 0010 106C 0010 1000 0010 1301 86D0
1301 86D0 1000 0010 106C 06D0 106C 06D0
1000 0010 1000 0010 1000 0010 1FFF FFF0
* [2] 
1FFF FC00 1FFF FE00 1FFF FF00 1FFF FF80
1FFF FFC0 1FFF FFE0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more

Jobs Board

Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.