TweetFollow Us on Twitter

Shapes in Quickdraw
Volume Number:2
Issue Number:12
Column Tag:ABC's of C

Drawing Shapes with Quickdraw

By Bob Gordon , Contributing Editor

One problem with writing software is that friends often want to know what it does. With most of these columns, frankly, there has not been much to show. This time, though, you can use the mouse to draw some simple shapes on the screen. Of course your friends will point out that MacDraw does everything we're going to do, and much better, too. But this one, we did ourselves, and that makes all the difference!

QuickDraw Shapes and Operations

QuickDraw can draw a number of shapes. This month we are going to cover only the simple shapes. (I define simple shapes as those that can be drawn directly without first defining what the shape is.) The simple shapes are:

Rectangles

Ovals (ellipses and circles)

Round-cornered rectangles

Arcs and wedges

To this list we could also add lines. The functions to draw these shapes are built into the Mac's ROM.

In drawing the shape, QuickDraw can do a number of different operations:

Frame draws a hollow outline

Paint fills the shape with the current grafPort's pen pattern and mode.

Erase fills the shape with the background pattern

Invert inverts all pixels within the shape: black becomes white and white becomes black

Fill fills the shapes with a specified pattern.

In general, these operations work identically for all the shapes. The shape is drawn, and the pen's location is not changed. Except for Fill, the operations require only the information needed to define the shape. Fill also needs to know the pattern to use. Since we have no easy way to select a pattern (we haven't done custom menus yet, so we haven't made a pattern selection menu), this operation will not be used.

The Toolbox supplies a separate function for each shape and operation. There are, for example, five functions to draw rectangles:FrameRect(), PaintRect(), EraseRect(), InvertRect(), and FillRect(). I wanted to select the shape and operation from menus and then draw with the mouse as with MacDraw.

Fig. 1 Our Drawing Program

Alternative Designs

One way to do this (assuming we had the menus built) would be to use a switch (case) statement based on the shape and operation:

switch (shape)
 case rect:
 switch (operation)
  case Frame:
  case Paint:
  case Erase:
  case Invert:
  case Fill:
  case oval:
 switch (operation)
 
 et cetera 

This is a sort of brute force method. It would work, but it involves several pages of code to make a couple of simple selections.

Another approach is to take advantage of C's ability to pass and use pointers to functions. To do this we obtain the addresses of the functions and arrange them in a double subscripted array. The problem of selection reduces to a problem of indexing into the array.

Pointers to Functions

Just as we can get the address of a variable and then assign a pointer to it, we can get the address of a function, assign a pointer to it and use the pointer to access the function. Kernighan and Ritchie discuss this on page 114.

There are several step in using a pointer to a function. First, to get the address of a function, just use the name of the function without the parentheses. If fr_rect() is a function, fr_rect will be the address of the of the function. To use a function in this manner we must create a variable that is a pointer to a function. The following line does this:

 short  (*drfunc)();

This rather strange looking line declares drfunc to be a pointer to a function that returns a short. Note that the parentheses around *afunc are required. Without them we would have:

 short  *drfunc();

a function that returns a pointer to a short. I could not use this declaration directly in the program but had to use a typedef:

 typedef short (*drfunc)();

Once this type is established, we can define some variables. In the program there is a variable, draw.

 drfunc draw;

For the moment assume that draw points to a function (it has been assigned a value). To use draw we do the following:

 (*draw)(par1,par2);

Again, the parentheses are needed to indicate that draw is a pointer to a function and we want to execute the function it points to. (Without the parentheses draw would be a function that returns a pointer). It is important to pass the proper number of variables. Since the call to the function is made through a pointer, compilers can do very little checking.

The Drawing Functions

I had originally hoped to avoid writing a bunch of drawing functions, but this plan changed. First, Lightspeed C does not allow us to take the address of Toolbox functions. They even have a special error message. This probably has to do with how they access the Toolbox and make the translation from C to Pascal calling conventions. This meant I would need at least a surround function for each Toolbox routine I wished to use.

Secondly, I wanted to draw lines in the same way as the other shapes. Since the Toolbox does not handle lines as shapes, I would have to do it myself. This was complicated by the fact that the initial versions of these functions expected to receive rectangles. Rectangles are used to define all the shapes. With lines, though, it meant taking apart the rectangle, figuring out which corner was the beginning, and keeping track of it. Needless to say, the line functions got very strange looking. To simplify the line functions, all the functions now receive two points. This had the side benefit of simplifying the oval functions, but it required that the surround routines create a rectangle before calling the Toolbox function.

The other requirement for accessing the function via a pointer is that all the functions must receive the same number of parameters. In addition to a rectangle, the round rectangle functions get two parameters describing the oval width and height used in drawing the rounded corner. These are fixed at 20. (Do try varying these. I was able to make some very ugly rounded rectangles). The arcs receive a start angle and the number of degress of angle to move (arcAngle in Toolbox terms). I fixed the arc angle at 90°. The start angle and the sign of the arc angle are adjusted as necessary to make the arc move correctly. These arcs were modeled after the ones in MacDraw.

The result of all this is a single function, drdraw(), that draws all the simple shapes under mouse control!

Speaking of the Mouse

Another addition to the program this month is the code that changes the cursor. While it is not yet complete (it doesn't know about the window borders where the controls would go), it does change the cursor as the mouse moves around. AdjustCursor() receives a pointer to a window record as a parameter and sets the cursor to the crosshairs (cursor number two) if the mouse is in the window and to the arrow if the mouse is not. The arrow cursor itself is not one of the four standard cursors but is defined in Quickdraw.h. AdjustCursor() is called once each time through the event loop.

There are two other points that deserve some mention. One is that several sets of coordinates can be active at once. GetMouse(), for example, returns the mouse's position relative to the current or active window. The window's boundaries, however, are defined in terms of the screen or global coordinates. The functions LocalToGlobal() and GlobalToLocal() will switch a point's coordinate system. Without the switch, the cursor changes magically out in the middle of the screen.

Next Time

We only did the simple shapes this time. I decided to wait on the others pending a discussion of the memory manager. Many of the functions we have used so far make use of the memory manager, and to do some things, we must be able to grab bits of memory. After that (I expect the program to be very brief - just long enough to demonstrate the operation) we'll return to Quickdraw and draw some more shapes.

The Program

/* abc.h 
 *
 * Local definitions to improve readability
 *
 */*
 
#defineTrue 1
#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 external routines */

extern  char*CtoPstr(); /* String conversion routines */
extern  char*PtoCstr(); /* return a pointer to a char */


/*  Quickdraw Drawing Program
 *    Shows off basic quickdraw shapes
 * and operations.
 * By Bob Gordon for MacTutor.
 * 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"  /* Events.h */
 #include "WindowMgr.h" /* Window.h */
 #include "MenuMgr.h"/* Menu.h */
 /* defines for menu ID's */
 #defineMdesk    100
 #defineMfile    101
 #defineMedit    102
 #defineMshape 103
 #defineMop 104
 /* File */
 #defineiNew1
 #defineiClose 2
 #defineiQuit    3
 /* Edit */
 #defineiUndo    1
 #defineiCut3
 #defineiCopy    4
 #defineiPaste 5
 
 /* Global variables */
 
 MenuHandle menuDesk;/* menu handles */
 MenuHandle menuFile;
 MenuHandle menuEdit;
 MenuHandle menuShape;
 MenuHandle menuOp;
 WindowPtrtheWindow;
 WindowRecord  windowRec;
 Rect   dragbound;
 Rect   limitRect;
 
main()
{
 initsys(); /* system initialization */
 initapp(); /* application initialization */
 eventloop();
}

/* system initialization */
initsys() 
{
 InitGraf(&thePort); /* these two lines done */
 InitFonts();    /* automatically by Mac C */
 InitWindows();
 InitCursor();
 InitMenus();
 theWindow = Nil;/*indicates no window */
 SetRect(&dragbound,0,0,512,250);
 SetRect(&limitRect,60,40,508,244);
}

/* 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"));
 InsertMenu (menuShape, 0);
 
 menuOp = NewMenu(Mop, CtoPstr("Operation"));
 AppendMenu (menuOp,CtoPstr("Frame;Paint; Erase;Invert;"));
 InsertMenu (menuOp, 0);
 DrawMenuBar();
}
 
/* Event Loop */
eventloop()
{
 EventRecordtheEvent;
 char   c;
 short  windowcode;
 WindowPtrww;

 while(True)
 {
 if (theWindow)      
 { 
 EnableItem(menuFile,2);  
 DisableItem(menuFile,1);
 AdjustCursor(theWindow);
 }
 else   
 { 
 EnableItem(menuFile,1);
 DisableItem(menuFile,2);
 }
 if (GetNextEvent(everyEvent,&theEvent))
 switch(theEvent.what)  
 { 
 case keyDown:   
 c = theEvent.message & charCodeMask;
 if (theEvent.modifiers & cmdKey)
 domenu(MenuKey(c));
 else if (theWindow)
 break;
 case mouseDown:
 domousedown(&theEvent);
 break;
 default:
 break;
 } 
 }
}

/* Arrow or Plus Cursor shape */
AdjustCursor(w)
 WindowRecord  *w;
{
 Point  pt;
 CursHandle curs;
 
 GetMouse(&pt);
 LocalToGlobal(&pt);
 if (PtInRgn(pt,w->contRgn))
 {
 curs = (Cursor **)GetCursor(2); 
 SetCursor(*curs); 
 }
 else
 {
 SetCursor(&arrow);
 }
}

/* domousedown
 * handle mouse down events
 */
domousedown(er)
 EventRecord*er;
{
 short  windowcode;
 WindowPtrwhichWindow;
 short  ingo;
 long   size;
 long   newsize;
 RgnPtr rp;
 Rect   box;
 Rect   *boxp;
 
 windowcode = FindWindow(er->where, 
 &whichWindow);
 switch (windowcode)
 {
 case inDesk:
 if (theWindow notequal 0)
 {
 HiliteWindow(theWindow, False);
 DrawGrowIcon(theWindow);
 }
 break;
 case inMenuBar:
 domenu(MenuSelect(er->where));
 break;
 case inSysWindow:
 SysBeep(1);
 break;
 case inContent:
 if (whichWindow equals theWindow)
 {
 HiliteWindow(whichWindow,True);
 DrawGrowIcon(whichWindow);
 drdraw(whichWindow);
 }
 break;
 case inDrag:
 DragWindow(whichWindow, 
   er->where, &dragbound);
 DrawGrowIcon(whichWindow);
 break;
 case inGrow:
 break;
 case inGoAway:
 ingo = TrackGoAway(whichWindow,er->where);
 if (ingo)
 {
 CloseWindow(whichWindow);
 theWindow = Nil;
 }
 break;
 }
}

/* domenu
 * handles menu activity
 * simply a dispatcher for each
 * menu.
 */
domenu(mc)
 long   mc; /* menu result */
{
 short  menuId;
 short  menuitem;

 menuId = HiWord(mc);
 menuitem = LoWord(mc);
 switch (menuId)
 {
 case Mdesk : break;
 /* not handling DA's */
 case Mfile : dofile(menuitem);
  break;
 case Medit : /* all disabled */
  break;
 case Mshape: doshape(menuitem);
  break;
 case Mop   : dooper(menuitem);
  break;
 }
 HiliteMenu(0);
}

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

dooper(item)
 short  item;
{
 static short  lastitem;
 
 CheckItem (menuOp, lastitem,False);
 CheckItem (menuOp, item, True);
 if (item == 5)
 {
   item = 0;
   SysBeep(1);
   }
 droper(lastitem = item);
}

dofile(item)
 short  item;
{
 char   *title1; /*title for window */
 Rect   boundsRect;
 
 switch (item)
 {
 case iNew :/* open the window */
 title1 = "ABC Window";
 SetRect(&boundsRect,50,50,400,200);
 theWindow = NewWindow(&windowRec, &boundsRect,CtoPstr(title1), True, 
documentProc,
(WindowPtr) -1, True, 0);
 DrawGrowIcon(theWindow);
 PtoCstr(title1);
 DisableItem(menuFile,1);
 EnableItem(menuFile,2);
 break;
 case iClose :   /* close the window */
 CloseWindow(theWindow);
 theWindow = Nil;
 DisableItem(menuFile,2);
 EnableItem(menuFile,1);
 break;
 case iQuit :    /* Quit */
 ExitToShell();
 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;

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);
}

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;
 }
}

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_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);
}

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

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);
}

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_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);
}
 
typedef short  (*drfunc)();
drfunc  a[][5] = {fr_line,fr_rect,fr_oval,fr_rort,fr_arc,
 pt_line,pt_rect,pt_oval,pt_rort,pt_arc,
 er_line, er_rect,er_oval, er_rort,er_arc,
  in_line,in_rect,in_oval, in_rort,in_arc};
drinit()
{
 short  i;
 for (i = 0; i < 20; shapa[i++].kind = 0)
 ;
 shapdx = 0;
}
drshape(code)
 short  code;
{
 shapa[shapdx].kind = code;
}
droper(code)
 short  code;
{
 shapa[shapdx].oper = code;
}
drsize(r)
 Rect *r;
{
 shapa[shapdx].size = *r;
}
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;
 
 SetPort((GrafPtr)w);
 GetMouse(&startpt);
 lastpt = startpt;
 PenMode(patXor);
 PenPat(gray);
 shapx = shapa[shapdx].kind - 1;
 operx = shapa[shapdx].oper - 1;
 if ((shapx < 0) or (operx < 0))   /* to prevent trying */
 return;/* to use unselected items */
 frame = a[0][shapx];/* get address of frame func */
 draw  = a[operx][shapx]; /* get addr of shape/oper func */
 do{
 GetMouse(&endpt);
 thispt = endpt;
 LocalToGlobal(&endpt);
 if (PtInRgn(endpt,w->contRgn) and 
 not EqualPt(thispt,lastpt))
 {
 (*frame)(startpt,lastpt);
 (*frame)(startpt,thispt);
 lastpt = thispt;
 }
 }
 while (StillDown());
 (*frame)(startpt,thispt);
 PenMode(patCopy);
 PenPat(black);
 (*draw)(startpt,thispt); 
}

abs(num)
 short  num;
{
 if (num < 0 )
 return -num;
 return num;
/* num < 0 ? return -num : return num ;*/
}   
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

CleanMyMac 3.9.2 - $39.95
CleanMyMac makes space for the things you love. Sporting a range of ingenious new features, CleanMyMac lets you safely and intelligently scan and clean your entire system, delete large, unused files... Read more
Printopia 3.0.4 - Share Mac printers wit...
Run Printopia on your Mac to share its printers to any capable iPhone, iPad, or iPod Touch. Printopia will also add virtual printers, allowing you to save print-outs to your Mac and send to apps.... Read more
Tinderbox 7.3.1 - 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
ExpanDrive 6.1.6 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
Printopia 3.0.4 - Share Mac printers wit...
Run Printopia on your Mac to share its printers to any capable iPhone, iPad, or iPod Touch. Printopia will also add virtual printers, allowing you to save print-outs to your Mac and send to apps.... Read more
Tinderbox 7.3.1 - 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
ExpanDrive 6.1.6 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
VOX 3.0.1 - Music player that supports m...
VOX just sounds better! The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features and support for all audio formats you should ever need.... Read more
Merlin Project 4.3.3 - $289.00
Merlin Project is the leading professional project management software for OS X. If you plan complex projects on your Mac, you won’t get far with a simple list of tasks. Good planning raises... Read more
Mac DVDRipper Pro 7.1 - Copy, backup, an...
Mac DVDRipper Pro is the DVD backup solution that lets you protect your DVDs from scratches, save your batteries by reading your movies from your hard disk, manage your collection with just a few... Read more

Latest Forum Discussions

See All

Animal Crossing: Pocket Camp mid-game gu...
By this point, you've probably hit or surpassed mid-way inAnimal Crossing: Pocket Camp.The game changes quite a bit as your focus slowly shifts from endlessly fulfilling requests for campsite buddies to improving your amenities. Here are a few tips... | Read more »
Mille Bornes (Games)
Mille Bornes 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: | Read more »
Blob gets a new look in Give It Up! 3
Blob makes his triumphant return, as Yoozoo Games and Invictus Gaming have joined forces to create Give It Up! 3, the third in a series of delightful action-adventure games featuring our wobbly friend Blob. In this newest adventure, you’ll get to... | Read more »
148Apps' Ultimate Guide to Black Fr...
Black Friday is here, and there are a whole lot of discounts running right now for folks on the lookout for new mobile devices, accessories, and yes, even games. Here's a helpful rundown of what you'll find both in stores and online. Happy... | Read more »
The best Black Friday mobile game deals
Black Friday's upon us, and if you've happened to nab a fancy new phone during the week's big savings, you might be searching for some new games to fill up space on your new gadget. There are a lot of great games on sale right now for Black Friday... | Read more »
The best mobile games to play while your...
Thanksgiving is a time to reconnect with loved ones, eat lots of food, and all of that jazz, but once the festivities start to wind down, folks tend to head to the couch to watch whatever football is happening for Turkey Day. | Read more »
The best Black Friday deals for Apple ga...
Black Friday is hours away at this point, but many popular retailers are getting a jump on things with plenty of pre-Black Friday sales already available. Many of those early bird sales including some sharp discounts on the latest Apple phones... | Read more »
The Inner World 2 (Games)
The Inner World 2 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Solve mind-bending puzzles in a world full of mystery and save the family of the flute-noses! Their dynasty has been... | Read more »
warbot.io wants you for the robot wars
Fans of epic gundam-style battles will find a lot to love in warbot.io, the first game for up and coming developer Wondersquad. The game saw a lot of success when it first launched for browsers and Facebook, and now even more people are getting the... | Read more »
Uncover alien mysteries in cross-genre s...
If the Alien franchise taught us anything, it’s that landing on a strange planet at the behest of a faceless corporation is probably asking for trouble. And Eldritch Game’s Deliria doesn’t prove otherwise. In 2107, Dimension LG7 is rich with... | Read more »

Price Scanner via MacPrices.net

Apple 9″ 32GB iPad for $80 off on Walmart onl...
Snag a 9.7″ 32GB WiFi iPad for $249 on Walmart’s online store as part of their Black Friday sale. That’s $80 off MSRP, and it’s the lowest price available for an iPad so far this season. Sale price... Read more
Apple Black Friday sale for 2017: $150 Apple...
BLACK FRIDAY Apple has posted their Black Friday deals for 2017. Receive a $150 Apple gift card with the purchase of select Macs and up to $100 with various iPads, iPhones, and Apple Watches. The... Read more
Black Friday 2017: Where to find the best dea...
B&H Photo has 15″ and 13″ MacBook Pros on sale for up to $200 off MSRP as part of the Black Friday and Holiday sale. Shipping is free, and B&H charges sales tax for NY & NJ residents only... Read more
Black Friday 2017: Where to find the best dea...
B&H Photo has 12″ MacBooks on sale for $150 off MSRP as part of the Black Friday and Holiday sale. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 12″ 1.2GHz... Read more
Black Friday 2017: Where to find the best dea...
B&H Photo has 10.5″ iPad Pros in stock today and on sale for up to $130 off MSRP. Each iPad includes free shipping, and B&H charges sales tax in NY & NJ only: – 10.5″ 64GB WiFi iPad Pro... Read more
Black Friday 2017: Where to find the best dea...
B&H Photo has 13″ MacBook Airs on sale for up to $150 off MSRP as part of the Black Friday and Holiday sale. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13″... Read more
Black Friday 2017: Where to find the best dea...
B&H Photo has 27″ and 21″ iMacs on sale for up to $200 off MSRP as part of the Black Friday and Holiday sale. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 27... Read more
Black Friday 2017: Where to find the best dea...
B&H Photo has Mac minis on sale for $100 off MSRP as part of their Black Friday sale, each including free shipping plus NY & NJ sales tax only: – 1.4GHz Mac mini: $399 $100 off MSRP – 2.6GHz... Read more
Black Friday 2017: Find the best deals and lo...
Scan our exclusive price trackers for the latest Black Friday 2017 sales & deals and the lowest prices available on Apple Macs, iPads, and gear from Apple’s authorized resellers. We update the... Read more
Black Friday: 27″ 3.4GHz iMac for $1599, save...
Amazon has the 27″ 3.4GHz Apple iMac on sale for $1599.99 as part of their Black Friday sale. That’s $200 off MSRP, and shipping is free. Their price is currently the lowest price available for this... Read more

Jobs Board

Information Systems Engineer, *Apple* Retai...
# Information Systems Engineer, Apple Retail Engineering Job Number: 112895982 Santa Clara Valley, California, United States Posted: 02-Oct-2017 Weekly Hours: 40.00 Read more
Commerce Engineer, *Apple* Media Products (...
# Commerce Engineer, Apple Media Products (New York City) Job Number: 113028813 New York City, New York, United States Posted: 20-Sep-2017 Weekly Hours: 40.00 **Job Read more
Business Development Manager, *Apple* Pay -...
# Business Development Manager, Apple Pay Job Number: 112919084 Santa Clara Valley, California, United States Posted: 18-Aug-2017 Weekly Hours: 40.00 **Job Summary** Read more
Digital Marketing Media Planner, *Apple* Se...
# Digital Marketing Media Planner, Apple Services Job Number: 113080212 Culver City, California, United States Posted: 03-Oct-2017 Weekly Hours: **Job Summary** Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.