TweetFollow Us on Twitter

Object Lists
Volume Number:6
Issue Number:2
Column Tag:Jörg's Folder

Related Info: Quickdraw

C++ Object Lists

By Jörg Langowski, MacTutor Editorial Board

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

“C++ object lists in windows”

Our third example application in C++ deals with another common aspect of Macintosh programming: Documents that have several objects associated and on which some action has to be performed. One of the best known examples would be a MacDraw document, where a document can contain (almost) any number of lines, polygons, rectangles, etc. All these objects have to be drawn when the window is updated, have to be moved or changed otherwise when the mouse is clicked on them, etc.

The classical example of late binding in object-oriented programming is, in fact, just this type of program: a window is created that contains a number of objects. We don’t know which type of objects or how many the window will contain at run time; all we know is that they must be drawn somehow.

Linked lists and late binding

For this type of problem, one usually creates a linked list of objects which is associated with the window. New objects can be added to or removed from the list. Drawing the contents of the window simply means traversing the list from beginning to end and drawing every object that is found. And here run-time binding comes in: since we don’t know the exact type of the object myObj that we’ll find at any particular place in the list, we’ll simply write Draw->myObj and let the run time code decide which particular drawing method should be used.

The example is illustrated by a piece of code from listing 2:

// 1

void TListDoc::DrawWindow(void)
{TObjLink* temp;
 SetPort(fDocWindow);
 EraseRect(&fDocWindow->portRect);
 if (fObjList->NumObjs() != 0)
 for (temp = fObjList->Header(); 
 temp != nil; temp = temp->GetNext())
 { SysBeep(1);  // to let something happen
 temp->GetmyObj()->Draw(qd.gray);  }
} // DrawWindow

TListDoc (listing 2) is our document class, the window that contains the objects to be drawn. These objects are contained in a list fObjList of type TObjList (listing 3), which consists of list elements of type TObjLink. Each list element is an object that contains two instance variables: a pointer to the next element in the list, fNext, and a pointer to the element’s contents fmyObj. In our case, fmyObj is an object of type TDisplObj, with the subclasses TRect, TOval, and TRoundRect. Fig. 1 shows its structure.

Fig. 1: structure of the TObjList class

The contents of a list element are not accessible directly, and the public function GetMyObj() is provided for returning a pointer to the object referred to by a particular list element.

List elements can be easily inserted into and removed from structures such as TObjList by simply changing the fNext pointers. The AddObj and RemoveObj functions are provided for this purpose. The list is linear, only one link per list element is defined. If one wanted to create branched lists like those in Lisp, one would have to define two links, both of which might alternatively point to other list elements or to simple objects (‘atoms’). This is not what we want for our list of display objects, which will just be traversed from beginning to end; thus we create a simple linear list.

Traversing the list is achieved by the for() statement at the end of the function definition; the variable temp contains a pointer to successive list elements, and the end is reached when a list element has no successor (GetNext() returns nil). Each list element’s “draw object” is then drawn using temp->GetmyObj()->Draw(qd.gray). This assumes that the object returned by GetMyObj() has a Draw method; the generic object class TListObj must have such a method defined as virtual for implementation in one of the subclasses. If the subclass does not define Draw(), the parent class’ Draw() is called, causing an error message.

The objects that may be drawn have a fixed location in the window; they are defined in the TListDoc initializer and are added to or removed from each document’s display list according to choices from a three-item menu. The menu item strings are adjusted depending on whether the object can be added or removed.

In a ‘real’ application, you would add a DoContent() method to the definition of a display object which allows some specific behavior when the mouse is clicked inside the object’s rectangle. This method would be invoked from the document’s DoContent() method; one would traverse the display list as before, check which object, if any, the mouse is clicked in, and call its DoContent() method.

I have also provided a virtual DoIdle() method which you may call for periodic actions inside one of the objects, for instance a clock display or some blinking text.

You should run the application and watch its behavior closely; note in particular that the order in which the three objects are drawn is different depending on the order in which the menu items were initially selected. This is plausible, because the objects are inserted into the list one before the other, and later drawn in the same order (last inserted, first drawn). Note also that update events are generated for the partially visible windows and that a beep accompanies the drawing of each object, even if that drawing takes place in a hidden part of a window and is therefore clipped.

Miscellaneous

We have now seen three examples of small Macintosh applications built around the application framework provided with the MPW C++ environment. Although this application framework is not MacApp, it implements a great deal of the basic functionality of a typical Macintosh application, and I like it for its simplicity. I was almost tempted to write ‘simplicity and speed’ there, but compiling this month’s example still takes me 3-5 minutes every time I make a change. MacApp might be still slower, but I’ll look into that in one of my next columns. The C++ environment allows working with the MacApp libraries, all the necessary header files are provided; so far I haven’t taken a closer look, but that will come soon.

[For fast compiling, one thing that Apple recommends is to include only the header files absolutely necessary for a piece of code, and control those includes through the use of compile-time variables, such as

#ifndef __TYPES__

#include <Types.h>

#endif

I haven’t systematically done this for my code, and probably could speed it up quite a bit that way].

Still, C++ is a change for someone used to the speed of a ‘compile-and-go’ environment like Mach2; I suppose when you give up a lot of the important features of C++, Think C 4.0 will allow you to do object-oriented programming in C with much shorter cycle times. A comparison of the relative merits of Think’s Object C and MPW C++ will follow in one of the next columns.

Also to follow: some more recent changes to the C++ definitions laid out in Stroustrup’s first book, ‘The C++ Programming Language’. The change from C++ 1.0 to C++ 2.0 included, among other things, the addition of protected fields to classes, and multiple inheritance. So far we have used mainly features that were already defined in C++ 1.0 (we did use protected fields, however). The next column will give some examples of the more advanced features.

At last, I have to apologize for a bug that was still present in my last example: when you tried to use the scroll bars’ arrows or page regions, the scroll bars would scroll with the rest of the window and thus disappear from underneath the mouse pointer! That can be funny to watch, but is not really what we want in an application. The reason was that I called SetOrigin too early, namely from within the control action procedure. So, to fix that bug, all you have to do is: 1. remove the calls to SetOrigin from the control action procedures HActionProc and VActionProc; 2. change the code after default: near the end of the definition of TMacTutorGrow:DoContent to:

//2

if ( control == fDocVScroll )
{
 value -= TrackControl(control, mouse, 
 (ProcPtr) VActionProc);
 SetOrigin(tRect.left,tRect.top-value);
}
else 
{
 value -= TrackControl(control, mouse,
 (ProcPtr) HActionProc);
 SetOrigin(tRect.left-value,tRect.top);
}
AdjustScrollSizes();
break;

and your scroll bars should work (approximately) correctly.

Listing 1: 


/*--------------------------------------------------
#DocListApp
#
#Examples for object lists associated with windows
#J. Langowski / MacTutor 1989
#----------------------------------------------------  */
#include <Types.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Events.h>
#include <OSEvents.h>
#include <Controls.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <Desk.h>
#include <Scrap.h>
#include <ToolUtils.h>
#include <Memory.h>
#include <SegLoad.h>
#include <Files.h>
#include <OSUtils.h>
#include <Traps.h>
#include <StdLib.h>
#include “TDocument.h”
#include “TApplication.h”
#include “DocListApp.h”
#include “DisplList.h”
#include “ListDoc.h”

// Methods for our application class
// a few of them taken ‘as is’ from Apple’s example
// (see previous two columns)

TDocListApp::TDocListApp(void)
{
 Handle menuBar;

 // read menus into menu bar
 menuBar = GetNewMBar(rMenuBar);
 // install menus
 SetMenuBar(menuBar);
 DisposHandle(menuBar);
 // add DA names to Apple menu
 AddResMenu(GetMHandle(mApple), ‘DRVR’);
 DrawMenuBar();
 // create empty mouse region
 fMouseRgn = NewRgn();
 // create a single empty document
 DoNew();
}

// Tell TApplication class how much heap we need
long TDocListApp::HeapNeeded(void)
{return (kMinSize * 1024);}

unsigned long TDocListApp::SleepVal(void)
{
 unsigned long sleep;
 const long kSleepTime = 0x7fffffff; 
 sleep = kSleepTime;
 if ((!fInBackground))
 { sleep = GetCaretTime();}
 return sleep;
}

void TDocListApp::AdjustMenus(void)
{
 WindowPtrfrontmost;
 MenuHandle menu;
 Boolean undo,cutCopyClear,paste;

 TListDoc* fCurListDoc = (TListDoc*) fCurDoc;

 frontmost = FrontWindow();

 menu = GetMHandle(mFile);
 if ( fDocList->NumDocs() < kMaxOpenDocuments )
   EnableItem(menu, iNew);
   // New is enabled when we can open more documents 
 else DisableItem(menu, iNew);
 if ( frontmost != (WindowPtr) nil )
   EnableItem(menu, iClose);
   // Close is enabled when there is a window to close 
 else DisableItem(menu, iClose);

 undo = false; cutCopyClear = false; paste = false;
 
 if ( fCurListDoc == nil )
   {  // all editing is enabled for DA windows 
 undo = true;  cutCopyClear = true;
 paste = true;  }
   
 menu = GetMHandle(mEdit);
 if ( undo )EnableItem(menu, iUndo);
 else   DisableItem(menu, iUndo);
 
 if ( cutCopyClear )
   {  EnableItem(menu, iCut);
 EnableItem(menu, iCopy);
 EnableItem(menu, iClear);} 
 else
   {  DisableItem(menu, iCut);
 DisableItem(menu, iCopy);
 DisableItem(menu, iClear); }
   
 if ( paste )  EnableItem(menu, iPaste);
 else   DisableItem(menu, iPaste);
 
 menu = GetMHandle(myMenu);
 EnableItem(menu, item1); EnableItem(menu, item2);
 EnableItem(menu, item3);
 if (fCurListDoc->fItem1Set)
 SetItem(GetMHandle(myMenu),item1, “\pRoundRect-”);
 else SetItem(GetMHandle(myMenu),item1, “\pRoundRect+”);
 if (fCurListDoc->fItem2Set)
 SetItem(GetMHandle(myMenu),item2, “\pOval-”);
 else SetItem(GetMHandle(myMenu),item2, “\pOval+”);
 if (fCurListDoc->fItem3Set)
 SetItem(GetMHandle(myMenu),item3, “\pRect-”);
 else SetItem(GetMHandle(myMenu),item3, “\pRect+”);
} // AdjustMenus

void TDocListApp::DoMenuCommand(short menuID, short menuItem)
{short  itemHit; Str255 daName;
 short  daRefNum;WindowPtrwindow;
 TListDoc* fCurListDoc = (TListDoc*) fCurDoc;

 window = FrontWindow();
 switch ( menuID )
   {  case mApple:
 switch ( menuItem )
   {  case iAbout: // About box
 itemHit = Alert(rAboutAlert, nil);
 break;
 default: // DAs etc.
 GetItem(GetMHandle(mApple), menuItem, daName);
 daRefNum = OpenDeskAcc(daName);
 break; }
 break;
 case mFile:
 switch ( menuItem )
   {  case iNew: DoNew(); break;
 case iClose:
 if (fCurListDoc != nil)
   {  fDocList->RemoveDoc (fCurListDoc);
 delete fCurListDoc; }
 else CloseDeskAcc(((WindowPeek)
 fWhichWindow)->windowKind);
 break;
 case iQuit:Terminate();  break; }
 break;
 case mEdit:
 // call SystemEdit for DA editing & MultiFinder 
 if ( !SystemEdit(menuItem-1) )
   {  switch ( menuItem )
   {  case iCut: break;
 case iCopy: break;
 case iPaste: break;
 case iClear: break; }
   }
 break;
 case myMenu:
 if (fCurListDoc != nil) 
 { switch ( menuItem )
   {  case item1:
 if (fCurListDoc->fItem1Set)
fCurListDoc->fObjList->RemoveObj(fCurListDoc->obj1);
 else
fCurListDoc->fObjList->AddObj(fCurListDoc->obj1);
 fCurListDoc->fItem1Set = !fCurListDoc->fItem1Set;
 break;
 case item2:
 if (fCurListDoc->fItem2Set)
fCurListDoc->fObjList->RemoveObj(fCurListDoc->obj2);
 else
fCurListDoc->fObjList->AddObj(fCurListDoc->obj2);
 fCurListDoc->fItem2Set = !fCurListDoc->fItem2Set;
 break;
 case item3: 
 if (fCurListDoc->fItem3Set)
fCurListDoc->fObjList->RemoveObj(fCurListDoc->obj3);
 else
fCurListDoc->fObjList->AddObj(fCurListDoc->obj3);
 fCurListDoc->fItem3Set = !fCurListDoc->fItem3Set;
 break; }

 InvalRect(&(window->portRect));
 fCurListDoc->DoUpdate(); }
 break; }
 HiliteMenu(0);  } // DoMenuCommand

void TDocListApp::DoNew(void)
{TListDoc* tMyDoc;
 
 tMyDoc = new TListDoc(rDocWindow);
 if (tMyDoc != nil) fDocList->AddDoc(tMyDoc);
} // DoNew

void TDocListApp::Terminate(void)
{ExitLoop();} // Terminate

TDocListApp *gTheApplication;

int main(void)
{gTheApplication = new TDocListApp;
 if (gTheApplication == nil) return 0;
 gTheApplication->EventLoop();
 return 0;}
Listing 2: ListDoc.cp

#include <Types.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Events.h>
#include <Controls.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <Desk.h>
#include <Scrap.h>
#include <ToolUtils.h>
#include <Memory.h>
#include <SegLoad.h>
#include <Files.h>
#include <OSUtils.h>
#include <Traps.h>
#include <StdLib.h>
#include “TDocument.h”
#include “DisplList.h”
#include “ListDoc.h”

// create and delete the document window
TListDoc::TListDoc(short resID) : (resID)
{Rect brect;
 fItem1Set = fItem2Set = fItem3Set = false;
 SetRect(&brect,10,100,70,140);
 obj1 = new TRoundRect(brect);
 SetRect(&brect,90,100,150,140);
 obj2 = new TOval(brect);
 SetRect(&brect,170,100,230,140);
 obj3 = new TRect(brect);
 fObjList = new TObjList();
 ShowWindow(fDocWindow);  }

TListDoc::~TListDoc(void)
{delete fObjList;
 HideWindow(fDocWindow);  }

void TListDoc::DoUpdate(void)
{BeginUpdate(fDocWindow); 
 if ( ! EmptyRgn(fDocWindow->visRgn) ) 
   {  DrawWindow();}
 EndUpdate(fDocWindow);   }

// Draw all objects contained in the list. 
void TListDoc::DrawWindow(void)
{TObjLink* temp;
 SetPort(fDocWindow);
 EraseRect(&fDocWindow->portRect);
 if (fObjList->NumObjs() != 0)
 for (temp = fObjList->Header(); 
 temp != nil; temp = temp->GetNext())
 { SysBeep(1);  // to let something happen
 temp->GetmyObj()->Draw(qd.gray);  }
} // DrawWindow
Listing 3: DisplList.cp

#include <Types.h>
#include <QuickDraw.h>
#include <Events.h>
#include <StdLib.h>
#include “DisplList.h”

TObjLink::TObjLink(TObjLink *n, TDisplObj *v)
{fNext = n; fmyObj = v;   }

TObjList::TObjList(void)
{fHeader = nil; fNumObjs = 0; }

void TObjList::AddObj(TDisplObj* obj)
{if (obj != nil)
 { TObjLink* temp;
 temp = new TObjLink(fHeader,obj);
 fHeader = temp;
 fNumObjs++;}
}

void TObjList::RemoveObj(TDisplObj* obj)
{if (obj != nil)
 { TObjLink* temp;
 TObjLink* last;
 last = nil;
 for (temp = fHeader; temp != nil; 
 temp = temp->GetNext())
   if (temp->GetmyObj() == obj)
 {  if (last == nil) 
 // if first item in list, just set first
 fHeader = temp->GetNext();
   else last->SetNext(temp->GetNext());
   delete temp;  // free the TObjLink
   fNumObjs--;   return;  }
   else last = temp; }
}

TDisplObj::TDisplObj(Rect r)  { fBoundRect = r; }

TRoundRect::TRoundRect(Rect r) : (r)
 // Calls base class constructor
{fOvalWidth = 20;
 fOvalHeight = 15; }

void TRoundRect::Draw(Pattern pat)
{PenNormal();
 FillRoundRect(&fBoundRect, fOvalWidth, fOvalHeight, pat);
 FrameRoundRect(&fBoundRect, fOvalWidth, fOvalHeight);   }

void TRoundRect::Erase()
{EraseRoundRect(&fBoundRect, fOvalWidth, fOvalHeight);   }

TRect::TRect(Rect r) : (r) {}

void TRect::Draw(Pattern pat)
{PenNormal();
 FillRect(&fBoundRect, pat);
 FrameRect(&fBoundRect);  }

void TRect::Erase()
{EraseRect(&fBoundRect);  }

TOval::TOval(Rect r) : (r) {}

void TOval::Draw(Pattern pat)
{PenNormal();
 FillOval(&fBoundRect, pat);
 FrameOval(&fBoundRect);  }

void TOval::Erase()
{EraseOval(&fBoundRect);  }
Listing 4: Header Files

DocListApp.h

// Constants, resource definitions, etc.
 // basically the same as in last month’s example
#define kErrStrings 129

/* The following are indicies into STR# resources. */
#define eNoMemory1
#define eNoWindow2

#define kMinSize 48 // min heap needed in K

#define rMenuBar 128 /* application’s menu bar */
#define rAboutAlert128  /* about alert */
#define rDocWindow 128  /* application’s window */

#define mApple 128 /* Apple menu */
#define iAbout 1
#define mFile  129 /* File menu */
#define iNew1
#define iClose 4
#define iQuit  12
#define mEdit  130 /* Edit menu */
#define iUndo  1
#define iCut3
#define iCopy  4
#define iPaste 5
#define iClear 6
#define myMenu 131   /* Sample menu */
#define item1  1
#define item2  2
#define item3  3

class TDocListApp : public TApplication {
public:
 TDocListApp(void);// Our constructor
private:
 // routines from TApplication we are overriding
 long HeapNeeded(void);
 unsigned long SleepVal(void);
 void AdjustMenus(void);
 void DoMenuCommand(short menuID, short menuItem);
 // our own routines
 void DoNew(void);
 void Terminate(void);
};

const short kMaxOpenDocuments = 4;

ListDoc.h

// List document class
//
class TListDoc : public TDocument {
  protected:
 void DrawWindow(void);
 public:
   Boolean fItem1Set,fItem2Set,fItem3Set;
 TDisplObj  *obj1,*obj2,*obj3;
 TObjList*fObjList;// list of objects to be drawn
 TListDoc(short resID);
 ~TListDoc(void);
 
 // methods from TDocument we override
 void DoUpdate(void);
};

const int kMinDocDim = 40;

DisplList.h

class TDisplObj {
  public:
 virtual void Draw(Pattern) 
 {DebugStr(“\pCall to TDisplObj::Draw”);}
 virtual void Erase()  
 {DebugStr(“\pCall to TDisplObj::Erase”);}
 virtual void DoContent()
 {DebugStr(“\pCall to TDisplObj::DoContent”);}
 virtual void DoIdle() { /* do nothing by default*/ }
  protected:
 // Only accessible to public descendants
 TDisplObj(Rect r);
 Rect fBoundRect;
  private:
 Boolean HiLiteState;// true : highlighted
};
class TObjLink {
 friend class TObjList;
  // TObjList may access our private members
 TObjLink*fNext; // the link to the next object
 TDisplObj* fmyObj;// the object this link refers to
 // our constructor can take args for convenience;
 // they default to nil.
 TObjLink(TObjLink *n = nil, TDisplObj *v = nil);
  public:
 inline TObjLink*GetNext() { return fNext; };
 inline TDisplObj* GetmyObj() { return fmyObj; };
 inline voidSetNext(TObjLink* aLink) { fNext = aLink; };
 inline void   SetmyObj(TDisplObj* aObj) { fmyObj = aObj; };
};
class TObjList {
 TObjLink*fHeader; // the first link in our list
 int  fNumObjs;  // the number of elements in the list
public:
 TObjList(void); // our constructor
 inline TObjLink* Header() { return fHeader; }
 inline int NumObjs() { return fNumObjs; }
 void AddObj(TDisplObj* obj);
 void RemoveObj(TDisplObj* obj);
};
class TRoundRect : public TDisplObj {
  public:
 TRoundRect(Rect r);
 virtual void Draw(Pattern);
 virtual void Erase();
  private:
 short fOvalWidth, fOvalHeight;
 // curvature of rounded corners 
};
class TOval : public TDisplObj {
  public:
 TOval(Rect r);
 virtual void Draw(Pattern);
 virtual void Erase();
};
class TRect : public TDisplObj {
  public:
 TRect(Rect r);
 virtual void Draw(Pattern);
 virtual void Erase();
};

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Capto 1.2.9 - $29.99
Capto (was Voila) is an easy-to-use app that takes capturing, recording, video and image editing to the next level. With an intelligent file manager and quick sharing options, Capto is perfect for... Read more
Opera 51.0.2830.40 - High-performance We...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more
GarageSale 7.0.13 - Create outstanding e...
GarageSale is a slick, full-featured client application for the eBay online auction system. Create and manage your auctions with ease. With GarageSale, you can create, edit, track, and manage... Read more
1Password 6.8.7 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Evernote 7.0.1 - Create searchable notes...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
MacUpdate Desktop 6.2.0 - $20.00
MacUpdate Desktop brings seamless 1-click app installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on... Read more
HoudahSpot 4.3.5 - Advanced file-search...
HoudahSpot is a versatile desktop search tool. Use HoudahSpot to locate hard-to-find files and keep frequently used files within reach. HoudahSpot will immediately feel familiar. It works just the... Read more
EtreCheck 4.0.4 - For troubleshooting yo...
EtreCheck is an app that displays 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 Communities to... Read more
WhatsApp 0.2.8361 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
iClock 4.2 - Customize your menubar cloc...
iClock is a menu-bar replacement for Apple's default clock but with 100x features. Have your Apple or Google calendar in the menubar. Have the day, date, and time in different fonts and colors in the... Read more

Latest Forum Discussions

See All

Disc Drivin' 2 Guide - Tips for the...
We're all still playing quite a bit of Disc Drivin' 2 over here at 148Apps, and we've gotten pretty good at it. Now that we've spent some more time with the game and unlocked more powerups, check out some of these more advanced tips: | Read more »
Alto's Odyssey Guide - How to Tackl...
Alto’s Odyssey is a completely stunning and serene runner, but it can also be a bit tricky. Check out these to try and keep your cool while playing this endless runner: Don’t focus too much on tasks [Read more] | Read more »
Here's everything you need to know...
Alto's Odyssey is a really, really good game. If you don't believe me, you should definitely check out our review by clicking this link right here. It takes the ideas from the original Alto's Adventure, then subtly builds on them, creating... | Read more »
Alto's Odyssey (Games)
Alto's Odyssey 1.0.1 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.1 (iTunes) Description: Just beyond the horizon sits a majestic desert, vast and unexplored. Join Alto and his friends and set off on an endless... | Read more »
Vainglory 5v5: Everything you need to kn...
Vainglory just got bigger. [Read more] | Read more »
Check out these 5 games that are a lot l...
So you're in love with Minecraft, but you're looking for something else to play as well? You've come to the right place then, because this list is all about games that are a bit like Minecraft. Some of them, more than others. [Read more] | Read more »
Our top 5 characters from casual RPG Cre...
Creature Quest definitely lives up to its name with a host of collectible creatures based on fantasy tales and world mythologies. To celebrate Creature Quest’s first birthday, we’re going to lay out what we think are the five best characters in the... | Read more »
Around the Empire: What have you missed...
Did you know that Steel Media has a whole swathe of other sites dedicated to all aspects of mobile gaming? Sure you'll get the very best iPhone news, reviews, and opinions right here at 148Apps, but we don't want you missing out on a single piece... | Read more »
All the best games on sale for iPhone an...
Oh hi there, and welcome to our round-up of the best games that are currently on sale for iPhone and iPad. You thought I didn't see you there, did you, skulking behind the bushes? Trust me though, the bushes aren't where the best deals are. The... | Read more »
The Battle of Polytopia Guide - How to H...
A new update just released for The Battle of Polytopia (formerly Super Tribes), which introduces online multiplayer. For all the fans of Midjiwan’s lite take on Civilization, this is certainly welcome news, but playing online isn’t as easy and... | Read more »

Price Scanner via MacPrices.net

Amazon restocks MacBook Pros with models avai...
Amazon has restocked 15″ and 13″ Apple MacBook Pros with models on sale for up to $251 off MSRP. Shipping is free. Note that stock of some Macs may come and go (and some sell out quickly), so check... Read more
Lowest price of the year: 15″ 2.8GHz Apple Ma...
Amazon has the 2017 Space Gray 15″ 2.8GHz MacBook Pro on sale today for $251 off MSRP. Shipping is free: – 15″ 2.8GHz Touch Bar MacBook Pro Space Gray (MPTR2LL/A): $2148, $251 off MSRP Their price is... Read more
Apple restocks full line of Certified Refurbi...
Apple has restocked a full line of Apple Certified Refurbished 2017 13″ MacBook Pros for $200-$300 off MSRP. A standard Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
Lowest sale price available for 13″ 1.8GHz Ma...
Focus Camera has the 2017 13″ 1.8GHz/128GB Apple MacBook Air on sale today for $829 including free shipping. Their price is $170 off MSRP, and it’s the lowest price available for a current 13″... Read more
21-inch 2.3GHz iMac on sale for $999, $100 of...
B&H Photo has the 2017 21″ 2.3GHz iMac (MMQA2LL/A) in stock and on sale for $999 including free shipping plus NY & NJ tax only. Their price is $100 off MSRP. Read more
Apple refurbished Mac minis in stock again st...
Apple has restocked Certified Refurbished Mac minis starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: – 1.4GHz Mac mini: $419 $80 off MSRP – 2.6GHz Mac... Read more
Tuesday MacBook Deals: $250 off 15″ 2.9GHz Ma...
Adorama has the Silver 15″ 2.9GHz Apple MacBook Pro on sale today for $250 off MSRP. Shipping is free, and Adorama charges sales tax for residents in NY & NJ only: – 15″ 2.9GHz Silver MacBook Pro... Read more
Save up to $350 with these Apple Certified Re...
Apple has a full line of Certified Refurbished iMacs available for up to $350 off original MSRP. Apple’s one-year warranty is standard, and shipping is free. The following models are available: – 27... Read more
B&H offers $200 discount on Silver 15″ Ma...
B&H Photo has Silver 15″ Apple MacBook Pros on sale for $200 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 15″ 2.8GHz Touch Bar MacBook Pro Silver (... Read more
12″ Apple iPad Pro Sale of the Year! Models u...
B&H Photo has 12″ #iPad Pros on sale for up to $150 off MSRP. Shipping is free, and B&H charges sales tax in NY & NJ only: – 12″ 64GB WiFi iPad Pro: $719 $80 off MSRP – 12″ 256GB WiFi... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, 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
*Apple* Solutions Consultant - Apple (United...
# Apple Solutions Consultant Job Number: 113523441 Orange, CA, California, United States Posted: 21-Feb-2018 Weekly Hours: 40.00 **Job Summary** Are you passionate Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Sr. Experience Designer, Today at *Apple* -...
# Sr. Experience Designer, Today at Apple Job Number: 56495251 Santa Clara Valley, California, United States Posted: 18-Jan-2018 Weekly Hours: 40.00 **Job Summary** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.