TweetFollow Us on Twitter

Polygons and Regions
Volume Number:3
Issue Number:5
Column Tag:ABC's of C

Polygons and Regions as Quickdraw Objects

By Bob Gordon, Contributing Editor, Minneapolis, MN

Stars! The reason we have stars this month is because Jane, the seven year old who hangs out around here, wanted them. We also have triangles, pentagons, hexagons, and a large H-like thing. The point of all this was to use QuickDraw functions to draw polygons and regions. These functions were integrated into the program two columns back so we now have one function that can draw rectangles, rounded rectangles, arcs, ovals, lines, polygons, and regions. The program is fairly straight forward, but it took a while to get it to work correctly. At this point, our program is a fairly complex, and complete implementation of some of the more advanced features of quickdraw. The major operations of quickdraw are supported including Frame, Paint, Erase and Invert. The only one missing is Fill. Implementing these functions for polygons and regions extends the program to the most general quickdraw constructions. The next level of complexity would be to implement pictures. Figure 1 shows some of the paint type drawings we can construct.

We have also improved on our Mac user interface design. We now support desk accessories. Since this opens up the possibility of having a second window obscure our drawing, we also have to implement update events to restore the drawing after a DA has covered part of our window. So we need an update event as well. Since our program draws directly to the screen, what we have is a kind of MacPaint approach, where even though our quickdraw objects are generalized polygons and regions, after they are drawn, our screen is simply a bit map. This lends itself to an obvious approach for update events: simply copy the window to an off-screen bit map and then use copybits to update the window. This is the approach we have used this month. Other improvements to the user interface might be to made our window growable and to add multi-window capability. Improvements to our "paint" program would be to make it into a "draw" program where each object on the screen retains it's "object-oriented" nature rather than becoming a bit map. This would also require a more complex update function to re-draw the screen from the object definitions, rather than from the saved bip map image. We will look at this approach next month as we generalize our quickdraw objects even further.

C Review

One of the most confusing things about C is the use of pointers and handles from a Pascal machine definition. Let us try to review how this is done. Suppose we have an event record declared as theEvent and we wish to pass this to a subroutine. This is normally done by passing the address of the event record structure by using the & function. Hence, we might do something like Foo(&theEvent). This would pass the address of theEvent to the routine Foo. Now what do we do in Foo? The answer is we prepare the variable with which we accept this address:

Foo(myEvent)
 Event Record  *myEvent;

What this means is that myEvent is a pointer to an event record and it is this address that is being passed to Foo; *myEvent is the actual event record, dereferenced from myEvent. Hence throughout Foo, we are using the pointer to our event record. This means it must be dereferenced first before being used to access fields of myEvent. There are two ways to do this in C:

point = (*myEvent).where;
point = myEvent->where;

The same approach extends to handles as the next example shows:

rect = (**teRecHandle).viewRect;
rect = (*teRecHandle)->viewRect;

Notice that if we try to use something like

point = myEvent.where; 

within Foo, the Lightspeed compiler will return a message saying that "where" is not a field of myEvent. This is because myEvent is a pointer to theEvent, not our event record structure itself.

This is futher complicated by situations where we must pass the data structure itself, and not a pointer to it. Normally this is not allowed in C. However, "Macinized" C compilers allow us to pass the actual data by calling Foo(theEvent) instead of calling Foo(&theEvent). This often leads to another problem in C. When calling routines that modify the variables passed to it as VAR variables, we must pass the address of the variable as in "&theEvent", rather than simply "theEvent". This often leads to compiler errors when we fail to insert the required & for a VAR parameter. In pascal, the programmer does not need a different syntax for VAR values so you don't have to pay much attention to which parameters are VAR's and which are not. When you have this error, you'll get a message that says something like wrong size for a pascal variable. This is a clue that the & is missing in the call. An example of this is the SetPort and GetPort trap calls which are defined as:

SetPort(gp)
 GrafPtr gp;

GetPort(gp)
 GrafPtr *gp;

Now when we call these routines, for SetPort we pass the window pointer for our window as in:

SetPort(myWindowPtr);

But when we call GetPort to save the current port in a temporary window pointer, we pass the address of our variable as in the following because it is a VAR parameter:

GetPort(&myTempPtr);

For some reason, very few C books, even the Macintosh ones, do not fully explain pointers and handles in C in relation to pointers and handles in Pascal, from which all the toolbox trap calls are defined. For more on this subject, and on memory management, see chapter six on memory management in our book, Using the Macintosh Toolbox with C by Sybex, which we are more or less following in this series.

Fig. 1 Polygons & Regions in this month's paint program

Polygons

My first idea for polygons was to develop a function that would collect points for a polygon on the fly. After selecting "Open Poly" from a menu, you would use the mouse to place points on the screen, lines would connect them, and after selecting "Close Poly," you could reproduce the new polygon at will with the all purpose drawing function, that is the central feature of our program. I got part of it written when I discovered that it would not work: I kept getting some bomb. I think this happened because after a polygon is opened it collects all line drawing information until the polygon is closed. This apparently includes such things as the lines involved in menus so doing almost anything created a multitude of lines. I did not investigate further, but decided to simply create some new shapes (the aforementioned pentagons, hexagons, and stars) that we could draw. This may imply that a pallette of drawing commands, maintained by the list manager would be more appropriate. This would be another possible design improvement in our program that could be pursued. (Gee, no wonder Mac programming never ends. The number of things you can add or improve on seems endless!)

Creating the new shapes was relatively straight forward. I simply picked an arbitrary rectangle to work in and computed where the end points should be on pencil and paper. Then it was a simple matter to use lineto() functions to connect the dots. You will notice that the star is drawn the way you learned as a child (Jane has a lot of influence around here). To draw a star that would be completely filled in (by the paint operation), we would have to have ten points and do lineto's to describe the edges. Another opportunity for you to modify our program!

It was after I had built the first "make polygon" routine that I ran into my second problem. I installed the various polygon drawing verbs into the all purpose draw function [see the dr.c code listing and the drDraw routine] and ran the program. As soon as I tried to do anything with a polygon I got a divide by zero error. I traced the problem to a call to MapPoly(). MapPoly() is a QuickDraw function that scales a polygon from one rectangle to another. In the process it changes the size and aspect ratio of the polygon. Just what we need to control the size and aspect of the polygon with the mouse. Unhappily it didn't work. It seems the source and destination rectangle need to differ slightly. Inseting the source rectangle solved the problem. Another approach you can try, and which I played around with, is writing yur own MapPoly() function. There is another QuickDraw function called MapPt() which does the same scaling operation on a point. It is a relatively simple task to build a function that maps all the points in a polygon. The only hitch was in knowing what a polygon looks like.

According to Inside Macintosh, a polygon looks like:

struct Polygon
 {
 short  polySize;
 Rect   polyBBox;
 Point  polyPoints[];
 }

The array polyPoints is variable length. polySize specifies the total length of the polygon (in bytes) including the space needed to hold polySize and polyBBox (10 bytes). By stepping through the array and calling MapPt on all the points, we can re-create the MapPoly function. The problem with MapPoly() apparently has to do with the source rectangle. I was using the polyBBox as the source. By copying polyBBox to another rectangle and Inseting it to make it one point larger or smaller all around, eliminates the problem. Now, I don't know if there is a better way to do this, and I have not gone in with a debugger to see what is actually going on, so any thoughts in this area will be appreciated.

Regions

Regions are one of the more amazing capabilities built into QuickDraw. This program only introduces them. I am going to ignore all the things one can do with regions and simply focus on getting a region on the screen.

Bascially, I had the same problems with regions as I did with polygons. The function MapRgn() caused a divide by zero error (I only solved the polygon problem after I solved it for regions), and I decided I did not want to write a MapRgn(). A region looks like:

struct Region
 {
 short  rgnSize;
 Rect   rgnBBox;
 /* optional region definition data */
 }

Unlike a polygon, there is not a hint of what is inside a region.

The region included in the program is very simple-it is made of three rectangles. (It could have easily been a polygon. The reason it looks like an "H" is because I wanted to write "HELLO" in REALLY BIG LETTERS. Some other time.) A region (the optional region definition data) is a series of horizontal slices of the object. Each slice starts with the y-coordinate followed by a series of x-coordinates and terminates with a 32767. After the last slice is another 32767. The x-coordinates turn the pen on and off so that drawing starts at the first one continues to the second, is off to the third, and on to the fourth, etc. The region defined in the program looks like:

 0 0  1040
 5032767
 2510 4032767
 3510 4032767
 600  1040
 503276732767

A region can be quite large. Try replacing the FrameRect() calls in makeregion() with FrameRoundRect() or FrameOval() and see what happens. In any case, we never have to deal with the inside of the region structure because QuickDraw provides all sorts of routines for manipulating them.

As with the polygons, the capabilities to draw regions with all the drawing operations have been added to the general purpose drawing routine.

Some Comments on the Program

The functions that handle regions and polygons (e.g. fr_poly() and fr_regn()) do not operate on the originals. Instead they operate on a copy of the polygon (or region). This was to avoid possible problems of distortion because the mouse drawing routine starts with a two-by-two rectangle (I never tried it without the copy, however). Since only one polygon is active at a time, there is a single temporary polygon space. The size of this space is by a call to setpolytemp() in each of the four make-polygon functions. setpolytemp() receives the size of the new polygon as a parameter and creates a handle to a polygon if it was not already created. On subsequent calls setpolytemp() compares the received size with the size it already has and makes the polytemp size larger if necessary. This way the space needed to make a copy of the largest (in bytes) polygon is readily available. The region is different. There is a QuickDraw function, CopyRgn() that copies a region. It handles the memory allocation.

The region and polygons are created during drinit() which is called once at the beginning of the program.

Since there is only one set of polygon (and region) drawing verbs, there needed to be away to specify which polygon to use. A selection from the shape menu ends up calling drshape() where the polygon based shapes are all changed into polygons and the correct one to use is placed in the global polycurrent. The various polygon functions use polycurrent as the original polygon.

A Bug

When drawing the polygon shapes (star, pentagon, etc.) there was sometimes a dot left where the mouse button is first pressed. Obviously I was not erasing it correctly, and it took some time to figure it out. Finally, the problem was solved by carefully making both the starting and ending points even coordinates so the generalized draw function would not try to draw a single point polygon. I think the dot problem is solved for all the polygon structures but if not, study the initialization of the draw fuunction loop variables for a better answer.

Update Events

Figures 2 and 3 show that our program now supports desk accessories. We also must manage our menus to turn on and off the edit menu when a system window for a DA is active. This is done by calling a menu adjust type routine in our main loop. It can be tricky however to identify all the user possibilities for activating one window over another so that the menus correctly reflect the state of the machine. The check menu routine does this function by checking all the possible combinations of having our window be on the screen and be the front window. If it is not on the screen, or on the screen and not the front window (must be behind a DA window) then our menus must look different.

Fig. 2 DA covers our drawing!

When we select a new window, we also define an off-screen bit map using the characteristics of our window. Both the window and the bit map are defined relative to screenBits.bounds. This quickdraw global defines the current screen size and by making all drawing relative to it, means our program will work on a Macintosh II or large screen addition, which use a different screen size than the present Macintosh. In our init routine, we set up our window related rectanges relative to screenBits.bounds to achieve this video independence.

Study the new window routine to see how the bit map is allocated with NewPtr. When the window is closed, we release the pointer to the bit map as well so that the next New Window call can create another off-screen bit map. Here are the new global declarations for our bit map:

 BitMap offMap;
 Rect   copyRect;
 Rect   drawRect;
The new window code that creates the bit map is shown next:
 drawRect=(*theWindow).portRect;
 copyRect=drawRect;
 offMap.rowBytes = screenBits.rowBytes;
 mapBytes = offMap.rowBytes*(screen.bottom-screen.top);
 offMap.baseAddr=NewPtr(mapBytes);
 offMap.bounds=screen;

CopyBits(&offMap,&offMap,&copyRect,&copyRect,srcXor,Nil);

The number of bytes in a row also changes on the Macintosh II so again, we define the rowbytes field of our bit map by using the current screen device defined by quickdraw in screenBits.rowbytes. Since the screen resolution also changes, we determine the number of pixel lines from screenBits.bounds, which we have copied into the screen rectangle. The number of bytes to allocate for the bit map is then the bytes per row times the number of rows in the display, which we store as mapBytes.

Fig. 3 SelectWindow generates Update Event CopyBits restores our drawing.

Once the bit map is created, we must then determine the most opportune moment to save the window contents and restore it from the bit map. The CopyBits function is used for this purpose. In our update event routine, we use CopyBits to blast the off-screen bit map back on screen in the portRect of our window. We do this whenever the update event is generated for our window. In particular, we do it when the new window function generates an update event to create our window. This led to the chicken and the egg problem of which comes first: the window or the saved bit map! The solution was to erase the bit map when it is first created so that the first update event for our window won't fill our window with a random display of memory in graphics form! Erasing our bit map is done by just doing a CopyBits on itself.

doupdate(updateWindow)
 WindowPtrupdateWindow;
{
GrafPtr temp;

GetPort(&temp);
SetPort(updateWindow);
BeginUpdate(updateWindow);
if ((theWindow) and (theWindow=updateWindow ))
CopyBits(&offMap,&(*updateWindow).portBits,
 &copyRect,&drawRect,srcCopy,Nil);
EndUpdate(updateWindow);
SetPort(temp);
}

Once our update event was working, the next problem was to fine the opportune time to call our SaveWindow routine which does a CopyBits in the opposite direction, saving the portRect of our window to our bit map. This proved to be the hardest part of all! The various interactions of the DA windows with our windows and the order in which activate and deactivate events are generated, and the manner in which the window manager draws and re-draws windows, all combined to make this a frustrating little problem. Since our program handles two events, a key down and a mouse down, this seem to be the obvious place to save the window. The key down event worked fine. When a key was pressed, the window was saved, then the key was processed. The mousedown was another story! I was continually having problems with the save window capturing the wrong set of pixels! The problem was finally solved by being more careful about when a mousedown event in the content region of our window should call SelectWindow, which generates activate and udpate events for the window.

Here is the save window routine, and the content event which calls it:

SaveWindow()
{
CopyBits(&(*theWindow).portBits,&offMap,&drawRect,
 &copyRect,srcCopy,Nil);  
}

case inContent:
 if (whichWindow equals theWindow)
 {
 if (whichWindow notequal FrontWindow())
 SelectWindow(whichWindow);
 else
 if (CursorInUse() equals 2)
 {
 drdraw(whichWindow);
 SaveWindow(); 
 }
 }
break;

A Note from a Reader

Kirk Kerekes of Tulsa OK provides some additional information on the surround functions used for the general purpose drawing routine. As I mentioned when we started building these drawing routines, LightSpeed C does not allow us to take the address of Toolbox functions. Mr. Kerekes points out that in Lightspeed C, the stack based traps are handled with an automatic in-line exception; there is no "glue" function. Since there is no function, there is no address. We can use address passing with ToolBox traps, however by using GetTrapAddress() to retrieve the address of the trap and pass them using the CallPascal() function. This is described on page 9-7 of the LightSpeed manual. The following little program (courtesy of Mr. Kerekes) illustrates the technique.

/* callpascal
 *
 * demonstrates use of CallPascal() 
 * address passing in LightSpeed C
 *
 * By Kirk Kerekes
 * (with some additional comments by Bob Gordon)
 * 
 */
 
 #include "QuickDraw.h"
 #include "OSUtil.h"
 #include "Window.h"
 
 #defineNil 0L
 
 pascal voidCallPascal();
 
 main()
 {
 Rect   testrect;
 Rect   wrect;
 WindowPtrthewindow;
 
 InitGraf(&thePort);
 InitWindows();
 InitFonts();
 InitMenus();
 InitDialogs(GetTrapAddress(0xA9F4)); 
 /* A9F4 = ExitTo Shell */
 
 SetRect(&wrect, 10, 10, 500, 300);
 thewindow = NewWindow (Nil, &wrect, "\pGetTrapAddress", 
  TRUE, 2, -1L, FALSE, Nil);
 SetPort(thewindow);
 /* make a rectangle */
 SetRect(&testrect, 20, 20, 100, 200); 
 /* fill it with gray, the usual way */
 FillRect(&testrect, gray);  
 /* now make it smaller */
 InsetRect(&testrect, 10, 10); 
 /* fill it with black with the Trap Address 
    technique.  The addresses are listed in a
    table in Inside Macintosh.  Note that by
    looking at the code you would have very 
    little idea what this does. */
 CallPascal(&testrect, black, GetTrapAddress(0xA8A5));
 
 while (!Button())
 ;
 
 DisposeWindow(thewindow);
 
 }

He points out that this technique does result in more obscure code.

Final Comments

I received a couple goodies in the mail over the last two months. The first was the new version of LightSpeed C. More on this next time, I hope. The other was the Best of MacTutor. I especially recomend the C Workshop series by Bob Denny. Anyone who has come this far in ABC's of C should benefit from reading his columns. The Pascal Procedures column by Chris Derossi is also highly relevant to what is going on here. See his Introduction to QuickDraw (p. 228) and QuickDraw does Regions! on page 231.

The other comment before presenting the program deals with where we go from here. We have been more or less following Using the Macintosh ToolBox with C. They will begin working on a text editor in a few chapters. Since the one thing I have heard from people is that they don't wish to see another text editor, I had wondered about what sort of project would be fun, useful, and illustratrative. Unless there are objections (or a better idea), I think we'll continue doing drawing. We may end up with some sort of mini MacDraw.

Next time we'll return to quickdraw and try to draw polygons, regions, and pictures in a more general way so they retain their object oriented nature like MacDraw.


/* Quickdraw Example
 *
 * Compiled with LightspeedC
 *
 * Important note for Mac C users:
 * Every place you see event->where,
 * replace it with &event->where
 */
 
 #include "abc.h"
 #include "Quickdraw.h"
 #include "EventMgr.h"
 #include "WindowMgr.h"
 #include "MenuMgr.h"
 #include "FontMgr.h"

 /* defines for menu ID's */
 
 #defineMdesk    100
 #defineMfile    101
 #defineMedit    102
 #defineMshape   106
 #defineMop 107
 
 /* File */
 #defineiNew1
 #defineiClose   2
 #defineiQuit    3
 
 /* Edit */
 #defineiUndo    1
 #defineiCut3
 #defineiCopy    4
 #defineiPaste   5
 #defineiClear   6
 
 /* Global variables */
 
 MenuHandle menuDesk;/* menu handles */
 MenuHandle menuFile;
 MenuHandle menuEdit;
 MenuHandle menuShape;
 MenuHandle menuOp;
 
 WindowPtrtheWindow;
 WindowRecord    windowRec;
 Rect   dragbound;
 Rect   screen;
 Rect   boundsRect; 
 
 BitMap offMap;
 Rect   copyRect;
 Rect   drawRect;
 
main()
{
 initsys(); /* system initialization */
 initapp(); /* application initialization */
 eventloop();
}

crash()
{
 ExitToShell(); /* we are dead folks */
}

/* system initialization */
initsys() 
{
InitGraf(&thePort);/* these two lines done */
InitFonts();/* automatically by Mac C */
InitWindows();
InitMenus();
TEInit();
InitDialogs(&crash);
InitCursor();
FlushEvents(everyEvent,0);
 
theWindow = Nil; /*indicates no window */
screen=screenBits.bounds;
SetRect(&dragbound,screen.left+4,screen.top+24,
 screen.right-4,screen.bottom-4);
SetRect(&boundsRect,screen.left+30,screen.top+50,
 screen.right-30,screen.bottom-50);
}

/*
 * application initialization
 */
initapp()
{
 setupmenu();
 drinit();
}

setupmenu()
{
menuDesk = NewMenu(Mdesk,CtoPstr("\24"));
AddResMenu (menuDesk, 'DRVR');
InsertMenu (menuDesk, 0);
 
menuFile = NewMenu(Mfile, CtoPstr("File"));
AppendMenu (menuFile,CtoPstr("New/N;Close;Quit/Q"));
InsertMenu (menuFile, 0);
 
menuEdit = NewMenu(Medit, CtoPstr("Edit"));
AppendMenu (menuEdit,CtoPstr("Undo/Z;(-;Cut/X;Copy/C;    Paste/V;Clear"));
InsertMenu (menuEdit, 0);
 
menuShape = NewMenu(Mshape, CtoPstr("Shape"));
AppendMenu (menuShape,CtoPstr("Line;Rectangle;Oval;
 Round Rectangle;Arc"));
AppendMenu(menuShape,CtoPstr("Triangle;Pentagon;
 Hexagon;Pentagram"));
AppendMenu (menuShape,CtoPstr("Region"));
InsertMenu (menuShape, 0);
 
menuOp = NewMenu(Mop, CtoPstr("Operation"));
AppendMenu (menuOp,CtoPstr("Frame;Paint;Erase;Invert"));
InsertMenu (menuOp, 0);   
DrawMenuBar();
}
 
/* Event Loop 
 * Loop forever until Quit
 */
eventloop()
{
EventRecord theEvent;
char    c;
 
while(True)
{
SystemTask();
CheckMenus();
if (GetNextEvent(everyEvent,&theEvent))
 switch(theEvent.what)    
 { /* only check key and */
 case keyDown:   /* mouse down events */
 if ((theWindow) and (theWindow equals FrontWindow()) )
 SaveWindow(); 
 c = theEvent.message & charCodeMask;
 if (theEvent.modifiers & cmdKey)
 domenu(MenuKey(c));
 else if (theWindow)
 SysBeep(5);
 break;
 case mouseDown: 
 domousedown(&theEvent);
 break;
 case updateEvt:
 doupdate((WindowPtr)theEvent.message);
 break;
 case activateEvt:
 doactivate(&theEvent);
 break;
 default:
 break;
 }
 
}
}

/* CheckMenus
 * Update menu bar if window active
 */
CheckMenus()
{
if ((theWindow) and (theWindow equals FrontWindow()))
 { 
 EnableItem(menuFile,iClose); 
 DisableItem(menuFile,iNew);
 DisableItem(menuEdit,0);
 EnableItem(menuShape,0);
 EnableItem(menuOp,0);
 CursorAdjust(theWindow);
 }
else    
 {
 if ((theWindow) and (theWindow notequal FrontWindow()))
 { 
 DisableItem(menuFile,iNew);
 DisableItem(menuFile,iClose);
 EnableItem(menuEdit,0);
 DisableItem(menuShape,0);
 DisableItem(menuOp,0);
 }
 else
 {
 if ((theWindow equals Nil) and (FrontWindow() equals          
 Nil))
 {
 EnableItem(menuFile,iNew);
 DisableItem(menuFile,iClose);
 DisableItem(menuEdit,0);
 DisableItem(menuShape,0);
 DisableItem(menuOp,0);
 }
 else
 {
 EnableItem(menuFile,iNew);
 DisableItem(menuFile,iClose);
 EnableItem(menuEdit,0);
 DisableItem(menuShape,0);
 DisableItem(menuOp,0);
 
 }
 }
 }
}

/* off screen bitmap */
SaveWindow()
{
CopyBits(&(*theWindow).portBits,&offMap,&drawRect,
 &copyRect,srcCopy,Nil);  
}
 
/* domousedown
 * handle mouse down events
 */
domousedown(er)
 EventRecord*er;
{
short   windowcode;
WindowPtr whichWindow;
short   ingo;
 
windowcode = FindWindow(er->where, &whichWindow);              
 
switch (windowcode)
 {
 case inDesk:
 if (theWindow notequal 0)
 {
 HiliteWindow(theWindow, False);
 }
 break;
 case inMenuBar:
 if ((theWindow) and (theWindow equals FrontWindow()))
 SaveWindow(); 
 domenu(MenuSelect(er->where));
 break;
 case inSysWindow:
 SystemClick(er,whichWindow);
 break;
 case inContent:
 if (whichWindow equals theWindow)
 {
 if (whichWindow notequal FrontWindow())
 SelectWindow(whichWindow);
 else
 if (CursorInUse() equals 2)
 {
 drdraw(whichWindow);
 SaveWindow(); 
 }
 }
 break;
 case inDrag:
 DragWindow(whichWindow,er->where, &dragbound);
 break;
 case inGoAway:
 ingo = TrackGoAway(whichWindow,er->where);
 if (ingo)
 {
 CloseWindow(whichWindow);
 theWindow = Nil;
 }
 break;
 default:
 break;
 }
}

doupdate(updateWindow)
 WindowPtrupdateWindow;
{
GrafPtr temp;

GetPort(&temp);
SetPort(updateWindow);
BeginUpdate(updateWindow);
if ((theWindow) and (theWindow=updateWindow ))
CopyBits(&offMap,&(*updateWindow).portBits,
 &copyRect,&drawRect,srcCopy,Nil);
EndUpdate(updateWindow);
SetPort(temp);
}

doactivate(er)
 EventRecord*er;
{
WindowPtr eventwindow;

eventwindow=(WindowPtr)(er->message);
if (er->modifiers & activeFlag)
 {
 }
else  /* deactivate */
 {
 }
}
 
/* domenu
 * handles menu activity
 * simply a dispatcher for each
 * menu.
 */
domenu(mc)
 long   mc; /* menu result */
{
 short  menuId;
 short  menuitem;
 char   daName[64];
 GrafPtrtemp;
 short  accItem;
 
 menuId = HiWord(mc);
 menuitem = LoWord(mc);
 
 switch (menuId)
 {
 case Mdesk : {
 GetItem(menuDesk,menuitem,daName);
 GetPort(temp);
 accItem=OpenDeskAcc(daName);
 SetPort(temp);
 break;
 }
 case Mfile : dofile(menuitem);
 break;
 case Medit :{
 if (not SystemEdit(menuitem-1))
 break;
 }
 case Mshape: doshape(menuitem);
  break;
 case Mop: dooper(menuitem);
  break;
 default:
 break;
 }
 HiliteMenu(0);
}

doshape(item)
 short  item;
{
static shortlastitem = 0;
 
CheckItem (menuShape,lastitem,False);
 CheckItem (menuShape,item,True);
 lastitem = item;
 drshape(item);
}

dooper(item)
 short  item;
{
static shortlastitem = 0;
 
CheckItem (menuOp, lastitem,False);
CheckItem (menuOp, item, True);
droper(lastitem = item);  
}
 
/* dofile
 * handles file menu
 */
dofile(item)
 short  item;
{
char    *title1; /* first title for window */
long    mapBytes;
switch (item)
 {
 case iNew :/* open the window */
 title1 = "ABC Window";
 theWindow = NewWindow(&windowRec, &boundsRect,
 CtoPstr(title1),True,noGrowDocProc,
 (WindowPtr) -1, True, 0);
 PtoCstr(title1);
 
 drawRect=(*theWindow).portRect;
 copyRect=drawRect;
 offMap.rowBytes = screenBits.rowBytes;
 mapBytes = offMap.rowBytes*(screen.bottom-screen.top);
 offMap.baseAddr=NewPtr(mapBytes);
 offMap.bounds=screen;
 CopyBits(&offMap,&offMap,&copyRect,
 &copyRect,srcXor,Nil);   
 break;
 
 case iClose :   /* close the window */
 CloseWindow(theWindow);
 DisposPtr(offMap.baseAddr);
 theWindow = Nil;
 break;
 
 case iQuit :    /* Quit */
 ExitToShell();
 break;
 
 default:
 break; 
 }
}


/* 
 * dr.c
 *
 * drawing routines
 */
 
 #include "abc.h"
 #include "quickdraw.h"
 #include "windowMgr.h"
 
struct shapes
 {
 short  kind;
 Rect size;
 short  oper;
 };
 
struct shapes   shapa[20];
short   shapdx;
PolyHandletriangle;
PolyHandlepentagon;
PolyHandlehexagon;
PolyHandlepentagram;
RgnHandle theregion;
RgnHandle tempregion;

PolyHandlepolytemp = 0;
PolyHandlepolycurrent;
short   phpts;


/*
 * Quickdraw surround functions.
 * These functions provide a consistent 
 * interface (at some loss of generality) to
 * all the quickdraw drawing functions.
 */
 
 /* FRAMING */

fr_poly(startpt,endpt)
 Point  startpt, endpt;
{
 Rect   drt;
 Rect   srt;
 
Pt2Rect(startpt,endpt,&drt);
BlockMove(&(*polycurrent)->polyBBox, &srt,sizeof(Rect));
BlockMove(*polycurrent,*polytemp,(*polycurrent)->polySize);
InsetRect(&srt, -1, -1);
MapPoly(polytemp,&srt, &drt);
FramePoly(polytemp);
}
 
fr_regn(startpt, endpt)
 Point  startpt, endpt;
{
 Rect   r, r1;
 
CopyRgn(theregion,tempregion);
BlockMove(&(*tempregion)->rgnBBox, &r1,sizeof(Rect));
InsetRect(&r1,-1,-1);
Pt2Rect(startpt, endpt,&r);
MapRgn(tempregion, &r1, &r);
FrameRgn(tempregion);
}

fr_line(startpt,endpt)
 Point  startpt,endpt;
{
MoveTo(startpt.h,startpt.v);
LineTo(endpt.h,endpt.v);
}

fr_rect(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
FrameRect(&rt);
}

fr_oval(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
FrameOval(&rt);
}

fr_rort(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
FrameRoundRect(&rt,20,20);
}


fr_arc(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 Rect trt;
 short  sa;
 short  aa;
 
Pt2Rect(startpt,endpt,&rt);
cp_arc(&rt,&trt,&sa,&aa);
FrameArc (&trt,sa,aa);
}

/* ERASING */

er_line(startpt,endpt)
 Point  startpt,endpt;
{
 GrafPtrgp;
 Patterntpat;
 
GetPort(&gp);
BlockMove(gp->pnPat,&tpat,8);
PenPat(gp->bkPat);
MoveTo(startpt.h,startpt.v);
LineTo(endpt.h,endpt.v);
PenPat(&tpat);
}

er_poly(startpt,endpt)
 Point  startpt, endpt;
{
 Rect   drt;
 Rect   srt;
 
Pt2Rect(startpt,endpt,&drt);
BlockMove(*polycurrent,*polytemp,(*polycurrent)->polySize);
BlockMove(&(*polycurrent)->polyBBox,&srt,sizeof(Rect));
InsetRect(&srt, -1, -1);
MapPoly(polytemp,&srt,&drt);
ErasePoly(polytemp);
}

er_regn(startpt, endpt)
 Point  startpt, endpt;
{
 Rect   r, r1;
 
CopyRgn(theregion,tempregion);
BlockMove(&(*tempregion)->rgnBBox, &r1,sizeof(Rect));
InsetRect(&r1,-1,-1);
Pt2Rect(startpt, endpt,&r);
MapRgn(tempregion, &r1, &r);
EraseRgn(tempregion);
}

er_rect(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
EraseRect(&rt);
}

er_oval(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
EraseOval(&rt);
}

er_rort(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
EraseRoundRect(&rt,20,20);
}

er_arc(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 Rect trt;
 short  sa;
 short  aa;
 
Pt2Rect(startpt,endpt,&rt);
cp_arc(&rt,&trt,&sa,&aa);
EraseArc (&trt,sa,aa);
}

/* PAINTING */

pt_line(startpt,endpt)
 Point  startpt,endpt;
{
 GrafPtrgp;
 Patterntpat;
 
MoveTo(startpt.h,startpt.v);
LineTo(endpt.h,endpt.v);
}

pt_poly(startpt,endpt)
 Point  startpt, endpt;
{
 Rect   drt;
 Rect   srt;
 
Pt2Rect(startpt,endpt,&drt);
 BlockMove(*polycurrent,*polytemp,(*polycurrent)->polySize);
BlockMove(&(*polycurrent)->polyBBox,&srt,sizeof(Rect));
InsetRect(&srt, -1, -1);
MapPoly(polytemp,&srt,&drt);
PaintPoly(polytemp);
}

pt_regn(startpt, endpt)
 Point  startpt, endpt;
{
 Rect   r, r1;
 
CopyRgn(theregion,tempregion);
BlockMove(&(*tempregion)->rgnBBox, &r1,sizeof(Rect));
InsetRect(&r1,-1,-1);
Pt2Rect(startpt, endpt,&r);
MapRgn(tempregion, &r1, &r);
PaintRgn(tempregion);
}

pt_rect(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
PaintRect(&rt);
}

pt_oval(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
PaintOval(&rt);
}

pt_rort(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
PaintRoundRect(&rt,20,20);
}


pt_arc(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 Rect trt;
 short  sa;
 short  aa;
 
Pt2Rect(startpt,endpt,&rt);
cp_arc(&rt,&trt,&sa,&aa);
PaintArc (&trt,sa,aa);
}

/* INVERTING */

in_line(startpt,endpt)
 Point  startpt,endpt;
{
 GrafPtrgp;
 short  tpnMode;
 
GetPort(&gp);
tpnMode = gp->pnMode;
PenMode(patXor);
MoveTo(startpt.h,startpt.v);
LineTo(endpt.h,endpt.v);
PenMode(tpnMode);
}

in_rect(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
InvertRect(&rt);
}

in_poly(startpt,endpt)
 Point  startpt, endpt;
{
 Rect   drt;
 Rect   srt;
 
Pt2Rect(startpt,endpt,&drt);
 BlockMove(*polycurrent,*polytemp,(*polycurrent)->polySize);
srt=(*polytemp)->polyBBox;
InsetRect(&srt,1,1);
MapPoly(polytemp,&srt,&drt);
InvertPoly(polytemp);
}

in_regn(startpt, endpt)
 Point  startpt, endpt;
{
 Rect   r, r1;
 
CopyRgn(theregion,tempregion);
BlockMove(&(*tempregion)->rgnBBox, &r1,sizeof(Rect));
InsetRect(&r1,-1,-1);
Pt2Rect(startpt, endpt,&r);
MapRgn(tempregion, &r1, &r);
InvertRgn(tempregion);
}

in_oval(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
InvertOval(&rt);
}

in_rort(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 
Pt2Rect(startpt,endpt,&rt);
InvertRoundRect(&rt,20,20);
}

in_arc(startpt,endpt)
 Point  startpt,endpt;
{
 Rect rt;
 Rect trt;
 short  sa;
 short  aa;
 
Pt2Rect(startpt,endpt,&rt);
cp_arc(&rt,&trt,&sa,&aa);
InvertArc (&trt,sa,aa);
}


/* ARC COMPUTATION
 * The arc is fixed at 90 degrees. This
 * function (used in all the arc functions
 * above) computes the correct rectangle, 
 * start angle, and arc angle given the
 * input rectangle (and 90 degrees).
 *
 * This makes drawing the arcs consistent
 * with drawing the other shapes.
 */
cp_arc(irt,ort,startangle,arcangle)
 Rect *irt;
 Rect *ort;
 short  *startangle;
 short  *arcangle;
{
 short  dh;
 short  dv;
 static Point  anchor;
 
 dh = irt->right - irt->left;
 dv = irt->bottom - irt->top;
 if (not (dh | dv))
 {
 anchor.v = irt->top;
 anchor.h = irt->left;
 }
 *ort = *irt;
 
 if (irt->left equals anchor.h)
 if (irt->top < anchor.v)
 {
 ort->left -= dh;
 ort->top -= dv;
 *startangle = 180;
 *arcangle = -90;
 }
 else
 {
 ort->left -= dh;
 ort->bottom += dv;
 *startangle = 0;
 *arcangle = 90;
 }
 else
 if (irt->top < anchor.v)
 {
 ort->top -= dv;
 ort->right += dh;
 *startangle = 180;
 *arcangle = 90;
 }
 else
 {
 ort->right += dh;
 ort->bottom += dv;
 *startangle = 0;
 *arcangle = - 90;
 }
}

 
typedef short  (*drfunc)();

drfunc  a[][7] = 
  {fr_line,fr_rect,fr_oval,fr_rort,fr_arc, fr_poly,fr_regn,
pt_line,pt_rect,pt_oval,pt_rort,pt_arc, pt_poly,pt_regn,
er_line,er_rect,er_oval,er_rort,er_arc, er_poly,er_regn,
in_line,in_rect,in_oval,in_rort,in_arc, in_poly,in_regn};

/* INITIALIZE DRAWING
 * Init all kinds in shape array (not used
 * yet and make polygons
 */
 
drinit()
{
 short  i;
 
 for (i = 0; i < 20; shapa[i++].kind = 0);
 
 shapdx = 0;
 maketriangle();
 makepentagon();
 makehexagon();
 makepentagram();
 makeregion();
}

/* SET SHAPE
 * This sets the shape code to use
 *  and sets it in the current shape
 *  entry (only one is used so far).
 * In the case of polygons, it translates
 *  the shape code to the polygon code and
 *  sets the polygon to use in the
 *  global, polycurrent.
 */
drshape(code)
 short  code;
{
 switch (code)
 {
 case 6: 
 polycurrent = triangle;
 break;
 case 7:
 polycurrent = pentagon;
 code = 6;
 break;
 case 8:
 polycurrent = hexagon;
 code = 6;
 break;
 case 9:
 polycurrent = pentagram;
 code = 6;
 break;
 case 10:
 code = 7;
 break;
 }
 shapa[shapdx].kind = code;
}

/* SET OPERATION
 * Sets operation in shape array
 *  (only one used).  Also sets
 *  cursor to use.
 */
droper(code)
 short  code;
{
 shapa[shapdx].oper = code;
 CursorToUse(2);
}

drdraw(w)
 WindowRecord  *w;
{
 Point  startpt;
 Point  thispt;
 Point  endpt;
 Point  lastpt;
 Rect   thisrt;
 Rect   lastrt;
 GrafPtrport;
 drfunc frame;
 drfunc draw;
 short  angle;
 short  dv,dh;
 Point  sp;
 Point  tp;
 Point  lp;
 short  shapx;
 short  operx;
 short  x;
 short  y;
 
 SetPort((GrafPtr)w);
 PenMode(patXor);
 PenPat(gray);
 shapx = shapa[shapdx].kind - 1;
 operx = shapa[shapdx].oper - 1;
 if ((shapx < 0) or (operx < 0)) 
 return;
 frame = a[0][shapx];/* get address of frame func */
 draw  = a[operx][shapx]; /* get addr shape/oper func */
 
 GetMouse(&startpt);
 x=startpt.h;
 y=startpt.v;
 if (x%2 notequal 0)
 x=x+1;
 if (y%2 notequal 0)
 y=y+1;
 startpt.h=x;
 startpt.v=y;
 lastpt=startpt;
 
 do{
 GetMouse(&endpt);
 
 x=endpt.h;
 y=endpt.v;
 if (x%2 notequal 0)
 x=x+1;
 if (y%2 notequal 0)
 y=y+1;
 endpt.h=x;
 endpt.v=y;
 
 thispt = endpt;
 LocalToGlobal(&endpt);
 if (PtInRgn(endpt,w->contRgn) and 
 not EqualPt(thispt,lastpt))
 {
 if (not EqualPt(startpt,lastpt))
 (*frame)(startpt,lastpt);
 (*frame)(startpt,thispt);
 lastpt = thispt;
 }
 }
 while (StillDown());
 
 (*frame)(startpt,thispt);
 PenMode(patCopy);
 PenPat(black);
 (*draw)(startpt,thispt); 
}

/*
 * Make New Shapes 
 *  These routines define polygons that
 *  are available from the menu.  Each 
 *  simply defines a polygon (assigning
 *  it to the global variable of the
 *  appropriate name).
 * NO ERROR CHECKING IS DONE ON THE
 * MEMORY OPERATIONS.
 */

maketriangle()
{
 short  err;
 
 triangle = OpenPoly();
 MoveTo(20,20);
 Line(20,0);
 Line(-10,-20);
 Line(-10,20);
 ClosePoly();
 setpolytemp((*triangle)->polySize);
}

makepentagon()
{
 pentagon = OpenPoly();
 MoveTo(50,0);
 Line(48,35);
 Line(-19,65);
 Line(-58,0);
 Line(-19,-65);
 Line(48,-35);
 ClosePoly();
 setpolytemp((*pentagon)->polySize);
}

makehexagon()
{
 hexagon = OpenPoly();
 MoveTo (21,0);
 Line(58,0);
 Line(28,50);
 Line(-28,50);
 Line(-58,0);
 Line(-28,-50);
 Line(28,-50);
 ClosePoly();
 setpolytemp((*hexagon)->polySize);
}

makepentagram()
{
 pentagram = OpenPoly();
 MoveTo(50,0);
 Line(30,90);
 Line(-78,-55);
 Line(96,0);
 Line(-78,55);
 Line(30,-90);
 ClosePoly();
 setpolytemp((*pentagram)->polySize);
}

/* The original of the polygon is not 
 *  changed when it is scaled and 
 *  displayed.  Instead a copy is used.
 *  Since the program has no idea how
 *  big a space to reserve for the copy, 
 *  setpolytemp() adjusts the size 
 *  reserved for the global handle
 *  polytemp.
 * NO ERROR CHECKING IS DONE ON THE 
 * MEMORY OPERATIONS.
 */
setpolytemp(size)
 short  size;
{
 if (polytemp equals 0) 
 polytemp = (PolyHandle)NewHandle(size);
 else if (size > GetHandleSize(polytemp))
 SetHandleSize(polytemp,size);
} 
 
makeregion()
{
 Point  sp,ep;
 Rect   r;
 
 sp.h = 10;
 sp.v = 10;
 ep.h = 60;
 ep.v = 60;
 theregion = NewRgn();
 OpenRgn();
 SetRect(&r,0,0,10,60);
 FrameRect(&r);
 SetRect(&r,40,0,50,60);
 FrameRect(&r);
 SetRect(&r,10,25,40,35);
 FrameRect(&r);
 CloseRgn(theregion);
 tempregion = NewRgn();
}


#include"abc.h"
#include"Quickdraw.h"
#include"windowMgr.h"

short   currentcursor;

CursorAdjust(w)
 WindowRecord  *w;
{
 Point  pt;
 CursHandle curs;
 
GetMouse(&pt);
LocalToGlobal(&pt);
if ((PtInRgn(pt,w->contRgn)) 
    and (currentcursor
            notequal 0))
   {

  curs = (Cursor **)GetCursor
          (currentcursor);
  SetCursor(*curs); 
 }
else
 {
 SetCursor(&arrow);
 }
}

CursorInUse()
{
 return(currentcursor);
}

CursorToUse(c)
 short  c;
{
 currentcursor = c;
}


/* abc.h 
 *
 * Local definitions to 
 *
 */
 
#define True1
#define False    0
#define Nil 0
#define and &&
#define or||
#define not !
#define equals   ==
#define notequal !=

/* unsigned char,longs, shorts
 * (unsigned longs may not be 
 *  available with all compilers
 */
#define uchar    unsigned char
#define ushort   unsigned short
#define ulong    unsigned long

/* General purpose routines */

extern  char*CtoPstr(); 
extern  char*PtoCstr(); 
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

BusyCal 2.6.6 - Powerful calendar app wi...
BusyCal is an award-winning desktop calendar that combines personal productivity features for individuals with powerful calendar sharing capabilities for families and workgroups. BusyCal's unique... Read more
MegaSeg 5.9.6 - Professional MP3 DJ appl...
MegaSeg is a complete solution for pro audio/video DJ mixing, radio automation, and music scheduling with rock-solid performance and an easy-to-use design. Mix with visual waveforms and Magic... Read more
KeyCue 7.5 - Displays all menu shortcut...
Need more than one license? Get KeyCue Family for just $15.99. KeyCue lets you view available Command key shortcuts. Simply press and hold the Command key to view a list of all available keyboard... Read more
Apple Pro Video Formats 2.0.1 - Updates...
Apple Pro Video Formats brings updates to Apple's professional-level codes for Final Cut Pro X, Motion 5, and Compressor 4. Version 2.0.1: Support for the following professional video codecs Apple... 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
EtreCheck 2.2 - For troubleshooting your...
EtreCheck is a simple little app to display the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support... Read more
OmniOutliner Pro 4.2 - Pro version of th...
OmniOutliner Pro is a flexible program for creating, collecting, and organizing information. Give your creativity a kick start by using an application that's actually designed to help you think. It's... Read more
VLC Media Player 2.2.1 - Popular multime...
VLC Media Player is a highly portable multimedia player for various audio and video formats (MPEG-1, MPEG-2, MPEG-4, DivX, MP3, OGG, ...) as well as DVDs, VCDs, and various streaming protocols. It... Read more
Nisus Writer Pro 2.1.1 - Multilingual wo...
Nisus Writer Pro is a powerful multilingual word processor, similar to its entry level products, but brings new features such as table of contents, indexing, bookmarks, widow and orphan control,... Read more
Tinderbox 6.2.0 - Store and organize you...
Tinderbox is a personal content management assistant. It stores your notes, ideas, and plans. It can help you organize and understand them. And Tinderbox helps you share ideas through Web journals... Read more

Lifeline... (Games)
Lifeline... 1.1 Device: iOS Universal Category: Games Price: $2.99, Version: 1.1 (iTunes) Description: Lifeline is a playable, branching story of survival against all odds. Using your iPhone, iPad, or Apple Watch, you will help... | Read more »
Pandemic: The Board Game Has Gone Univer...
Don't let the virus win! Now you can download Pandemic: The Board Game, by F2Z Digital Media, for all of your iOS devices. The app is based on the fantastic board game by Z-man games. As employees of the CDC, you and your friends will have to work... | Read more »
Get Ready to Read Bloomberg Business on...
Fans of Bloomberg Business will soon be able to get all their news on the Apple Watch. The app lets you get the top headlines on your main screen and bookmark stories to read later. Using the motion detection in the Apple Watch, the headlines are... | Read more »
Watch This Homerun is Batting for the Ap...
Eyes Wide Games' Watch This Homerun is purportedly the first sports game coming to the Apple Watch, where you'll be up to bat as the pitcher tries to out-manuever you with fastballs, curveballs, and changeups. Using one-touch controls you can try to... | Read more »
Field Trip Can Take You on a Guided Tour...
Field Trip, by Google’s Niantic Labs, is an exploration app that gives you details about the awesome places you can discover wherever you find yourself. The app can show you local history, delicious restraunts, the best places to shop, and places to... | Read more »
Watch Your Six - SPY_WATCH is Infiltrati...
SPY_WATCH, by Bossa Studios, is a new game designed for the Apple Watch. Runmor has it your spy agency has fallen out of favor. To save it, you'll need to train-up a spy and send them on missions to earn you a stunningly suspicious reputation and... | Read more »
Both Halo: Spartan Assault and Halo: Spa...
Halo: Spartan Assault and Halo: Spartan Strike, by Microsoft, have officially landed on the App Store. Spartan Assault pits you against the Covenant with missions geared to tell the story of the origin of Spartan Ops. In Spartan Strike you'll delve... | Read more »
The Apple Watch Could Revolutionize the...
It’s not here yet but there’s that developing sneaky feeling that the Apple Watch, despite its price tag and low battery life, might yet change quite a lot about how we conduct our lives. While I don’t think it’s going to be an overnight... | Read more »
Mad Skills Motocross 2 Version 2.0 is He...
Mad Skills Motocross 2 fans got some good news this week as Turborilla has given the game its biggest update yet. Now you'll have access to Versus mode where you can compete against your friends in timed challenges. Turborilla has implemented a... | Read more »
Kids Can Practice Healthy Living With Gr...
Bobaka is releasing a new interactive book called Green Riding Hood  in May. The app teaches kids about yoga and organic style of life through mini-games and a fun take on the classic Little Red Riding Hood fairy tale. | Read more »

Price Scanner via MacPrices.net

Sale! 15-inch Retina MacBook Pros for up to $...
 MacMall has 15″ Retina MacBook Pros on sale for up to $255 off MSRP. Shipping is free: - 15″ 2.2GHz Retina MacBook Pro: $1794.99 save $205 - 15″ 2.5GHz Retina MacBook Pro: $2244.99 save $255 Adorama... Read more
New 2015 MacBook Airs on sale for up to $75 o...
Save up to $75 on the purchase of a new 2015 13″ or 11″ 1.6GHz MacBook Air at the following resellers. Shipping is free with each model: 11" 128GB MSRP $899 11" 256GB... Read more
Clearance 13-inch Retina MacBook Pros availab...
B&H Photo has leftover 2014 13″ Retina MacBook Pros on sale for up to $250 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.6GHz/128GB Retina MacBook Pro: $1129... Read more
Clearance 2014 MacBook Airs available startin...
B&H Photo has clearance 2014 MacBook Airs available for up to $200 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 11″ 128GB MacBook Air: $729 $170 off original MSRP... Read more
16GB iPad mini 3 on sale for $349, save $50
B&H Photo has the 16GB iPad mini 3 WiFi on sale for $349 including free shipping plus NY sales tax only. Their price is $50 off MSRP, and it’s the lowest price available for this model. Read more
Mac minis on sale for up to $75 off, starting...
MacMall has Mac minis on sale for up to $75 off MSRP including free shipping. Their prices are the lowest available for these models from any reseller: - 1.4GHz Mac mini: $459.99 $40 off - 2.6GHz Mac... Read more
Taichi Temple First Tai Chi Motion Sensor App...
Zhen Wu LLC has announced the official launch of Taichi Temple 1.0, the first motion sensor app for Tai Chi, offering a revolutionary new way to de-compress, relax and exercise all at the same time.... Read more
CleanExit – Erase your Hard Drive Quickly, Se...
CleanExit works on both Macs and PCs, securely and permanently deleting all files from any type of hard drive, flash-based drive or camera media card making the files permanently unrecoverable.... Read more
250 iPhone 6 Tips eBook Released for $1.99
Bournemouth, UK based iOS Guides has released 250 iPhone 6 Tips, a new eBook available in the iBookstore that reveals a wealth of tips and tutorials for iPhone 6 and iPhone 6 Plus. Priced at $1.99,... Read more
TigerText Introduces First Secure Enterprise...
TigerText, a provider of secure, real-time messaging for the enterprise, has announced the launch of TigerText for the Apple Watch. TigerText for the Apple Watch enables users to securely send and... Read more

Jobs Board

*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
DevOps Software Engineer - *Apple* Pay, iOS...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Pay - Site Reliability Engineer - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
Sr. Technical Services Consultant, *Apple*...
**Job Summary** Apple Professional Services (APS) has an opening for a senior technical position that contributes to Apple 's efforts for strategic and transactional Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.