TweetFollow Us on Twitter

Apr 00 Getting Started

Volume Number: 16 (2000)
Issue Number: 4
Column Tag: Getting Started

Opening a File

by Dan Parks Sydow

How a program opens a file and displays that file's contents in a window

In past Getting Started articles we've had occasion to have our example programs open files. For instance, back in February of 1999 the SoundPlayer program opened a sound file, and a few months back, in December of 1999, our MoreQT program opened two QuickTime movie files. In both those examples our programs relied on the desired files being in the same folder as the application, and we had the applications take control of opening those files. That's not always the way it works in the real world. Your user-friendly program might offer the user the opportunity to select the file to open. This month you see how to write a program that does just that. In particular we'll examine how a program opens a text file and displays that file's text in a window. Many of the techniques you read about here apply to opening other types of files - such as sound, picture, and movie files - as well.

Windows and File Data

This article's example program opens a window that displays the text that's stored in a text file. In doing that the program opens a text file and reads the file's text to memory. The program keeps a handle to the data in memory, and associates that handle with one particular window. While the example program doesn't allow for the opening of a second window, we want to establish the foundation for a program that does. And if a program is capable of opening two, three, or thirty windows each displaying the text from a different file, we certainly want to make sure that the program displays the proper text in the proper window. This is true for a program that opens text files, but it's also true for any program that displays the contents of files in widows.

One such scheme for associating a file's contents with a particular window is to employ the use of a document record data type. This application-defined data type holds information about any one of a program's windows. While such a data type can include any number of fields, in our simple example one suffices:

typedef  struct 
{
   TEHandle   windText;
   
} WindData, *WindDataPtr, **WindDataHandle;

As you'll see ahead, a TEHandle is an Apple-defined data type that serves as a handle to editable text (with the TE standing for TextEdit). So our structure of type WindData exists to keep track of a single handle that references a block of memory that holds the text copied from a file. The above snippet also defines a pointer to such a structure (a WindDataPtr) and a handle to such a structure (a WindDataHandle). To create a new WindData structure and return a handle to it, make use of the Toolbox function NewHandleClear().

WindData	theData;

theData = (WindDataHandle)NewHandleClear( sizeof(WindData) ); 

Our WindData structure is initially empty - we'll soon remedy that by filling its one field (the windText TEHandle field) with a reference to some text. Before doing that, let's open a window:

WindowPtr	theWindow;

theWindow = GetNewWindow( 128, nil, (WindowPtr)-1L );

At this point we have memory space reserved for a WindData structure, and we have an open window - but we don't have a connection between the two. That is, there is no association between the theData data structure and the theWindow window. To do that we make a call to the Toolbox function SetWRefCon().

SetWRefCon( theWindow, (long)theData );

A WindowPtr is a pointer to a WindowRecord. One of the fields of a WindowRecord is refCon - a long that can be used to hold any four bytes of data. Programmers often use the refCon field of a window as a reference to application-defined data that pertains to that window. That's what we're doing here. Four bytes doesn't sound like much room to store data, and it's not. So these four bytes are instead used to hold a pointer (or a handle) that lead to a memory block of any size - and it's in this memory block that the application-defined window information is stored. As you ponder this concept, keep in mind two memory-related issues. First, a handle is a pointer to a pointer. That is, a handle is a type of pointer. Second, a pointer is held in four bytes of memory. The above call to SetWRefCon() accepts a WindowPtr as its first argument and any long as its second argument. By storing a reference to application-data in the second argument we wed the WindData structure to a window.

Opening a File

To this point we've created an empty data structure and associated it with a window. But we haven't opened an existing text file, brought that file's text into memory, and then created a tie between the text in memory and a window. We'll open the file now.

Your program can display the standard open file dialog box to give the user the ability to open any existing text file. A call to the Toolbox function StandardGetFilePreview() does the trick:

SFTypeList				typeList = { 'TEXT', 0, 0, 0 };
StandardFileReply	reply;

StandardGetFilePreview( nil, 1, typeList, &reply );

Before discussing the particulars of working with StandardGetFilePreview(), a quick Mac OS X note is in order. You may be familiar with the Toolbox function StandardGetFile(). That routine has a parameter list that's identical to the list for StandardGetFilePreview(). And both functions perform essentially the same task - they each display a standard open dialog box. The difference is that StandardGetFilePreview() is capable of displaying a small view of a part of a selected file. For a selected QuickTime movie, for instance, the dialog box will show a thumbnail image of the first frame of the movie. For our text file-opening example the dialog box will display the first several words in the selected text file (see Figure 3). Here's where Mac OS X comes into play. If you don't consider the display of a preview of the first bit of a text file to be of particular importance to the user, then you might consider using StandardGetFile() rather than StandardGetFilePreview(). Before making that move, consider this. StandardGetFile() won't be supported as a Carbon function for Mac OS X development, while StandardGetFilePreview() will be supported. So you'd be wise to use StandardGetFilePreview().

The first three arguments to StandardGetFilePreview() tell the File Manager which types of files to display in the dialog box. The open dialog box can readily display all types of files, or just files of up to four different types (such as text files, picture files, movie files, and so forth). If your application is capable of opening more than four different types, and you want the open dialog box to display files of each of these types (but not files of all types), you'll need to pass a pointer to a filter function as the first argument. This application-defined filter function will then serve as the means of displaying the appropriate files. If your program is capable of opening four or fewer types of files, then pass nil as the first argument to indicate that no filter function is used.

In the second argument to StandardGetFilePreview() pass the number of file types to be displayed. We'll pass 1 since it's assumed our program only works with one type of file - text files).

The third argument is of type SFTypeList. The SFTypeList variable is a list that specifies the four-character file type (surrounded in single quotes) of each of the types of files your program can work with. Fill the SFTypeList variable upon declaration, as shown above. A text file has a file type of 'TEXT', so that serves as one of the four elements in the above list. Fill the list with four values, using a 0 for each unused file type. Assuming a program is to only display text files in the open dialog box, there'll be three 0's in the list. Other common file types are 'PICT' for a picture file and 'moov' for a QuickTime movie file.

The last argument is a pointer to a variable of type StandardFileReply. When the user clicks on the Open or Cancel button in the standard open file dialog box, the StandardGetFilePreview() fills the members of the StandardFileReply structure. You'll be interested in two of the many members of this structure: sfGood and sfFile. By examining the value of sfGood your program can determine if the user clicked on the Open button (sfGood will have a value of true) or the Cancel button (sfGood will have a value of false).

if ( reply.sfGood == false )
	// handle canceling of file opening
else
	// handle opening of selected file

If the sfGood field has a value of true, your program should go on to open the selected file. An FSSpec - a file system specification - for that file is held in the sfFile field of the StandardFileReply variable filled in by StandardGetFilePreview(). Pass a pointer to this FSSpec as the first argument to the Toolbox function FSpOpenDF().

short	fileRefNum;

FSpOpenDF( &reply.sfFile, fsRdPerm, &fileRefNum );

The FSpOpenDF() function finds the requested file and opens its data fork. If a permission level of the Apple-defined constant fsRdPerm is specified, the application will only be able to read the file (a value of fsRdWrPerm can be used if your program needs to also be able to alter the file's contents). After opening the file, FSpOpenDF() returns a file reference number. It is this number that your application can then use to reference the file.

Bringing a File's Text Into Memory

With a file open, it's time to bring that file's contents into memory. The Toolbox function FSRead() will take care of that, but first we need to do a little preparatory work:

long		fileLength;
Ptr		textBuffer;

GetEOF( fileRefNum, &fileLength );
SetFPos( fileRefNum, fsFromStart, 0 );

textBuffer = NewPtr( fileLength );

GetEOF() gets the size in bytes of the contents of a file. Here we use the file reference number returned by FSpOpenDF() to tell GetEOF() which file we're interested in. SetFPos() moves the file mark - the position marker used to keep track of the current position to read from or write to - to a particular byte location in a file. The Apple-defined constant fsFromStart tells SetFPos() to count from the start of the file, while the third argument indicates how many bytes to move the file mark. Here we're moving the file mark to the very start of the file (start counting from the start of the file, fsFromStart, and then move the mark 0 bytes). The ability to move the mark isn't important to our opening a text file, but it is important in some other cases. For instance, a file of some file types (such as a file of type 'PICT') includes several bytes of header information that is unrelated to the main data in the file. When opening a picture file a program will set the file mark past this header information before reading the file's contents.

GetEOF() returned the number of bytes in the open file, and we use that value when creating a new buffer (a block of memory) in which we'll store the file's contents. The call to NewPtr() returns a pointer to the memory block. Then, it's time to read the file's contents, storing the file data in the block of memory referenced by the textBuffer pointer.

FSRead( fileRefNum, &fileLength, textBuffer );

The call to FSRead() reads in the text from the text file with the reference number fileRefNum. The data has now been stored in memory, but we want our program to be able to easily work with the text. To do that, create a text edit record. A call to TENew() creates such a record. Before creating the text edit record, set up two rectangles. The destination rectangle is the area in which text is drawn. The view rectangle is the area in which text is displayed. While the boundaries of these two rectangles are often the same, they don't have to be. If the view rectangle is inset from the destination rectangle, then when it comes time to display the edit record text in a window, some text will be clipped. Figure 1 shows a view rectangle that's smaller than a destination rectangle. Figure 2 shows a window displaying text of a text edit record that uses the rectangles from Figure 1.

Rect				destRect;
Rect				viewRect;   
TEHandle		textHandle;

SetRect( &destRect, 10, 10, 410, 270 );
viewRect = destRect;
textHandle = TENew( &destRect, &viewRect );


Figure 1.A view rectangle that's smaller than the destination rectangle.


Figure 2.Displaying text from a text edit record that uses the rectangles from Figure 1.

The above snippet creates a destination rectangle 400 pixels across and 260 pixels in height. We'll soon be placing this rectangle snuggly in a window, so giving the destination rectangle a left boundary and top boundary of 10 means that there'll be small left and top margins (as opposed to the text touching the left and top of the window in which it's displayed). The view rectangle is set to the same coordinates as the destination rectangle, and these two Rects are used in the call to TENew().

TENew() creates a new empty text edit record. A call to TESetText() fills the new record with text.

HLock( (Handle)textHandle );
	TESetText( textBuffer, fileLength, textHandle );   
HUnlock( (Handle)textHandle );

The call to TESetText() moves the text from the buffer referenced by the textBuffer pointer to the memory referenced by the TEHandle textHandle. Keeping in mind that during routine memory management the system can move memory referenced by a handle, but can't move memory referenced by a pointer, we play it safe and lock the handle. Doing that prevents the system from moving the memory referenced by textHandle while TESetText() is copying text from one area of memory to another.

Associating a File's Contents With a Window

A file is open and it's contents are held in a block of memory that's referenced by a handle (the textHandle variable). And an application-defined data structure (a WindData structure) that includes a handle as its one field (the windText field), and that is associated with a window (theWindow), is also held in memory. If we now create a tie between the handle that references the file's contents in memory (textHandle) and the handle that is a part of the application-defined data structure in memory (windText), we in effect associate the file contents with the window.

(**theData).windText = textHandle;

The above line dereferences the handle twice to allow access to the structure member windText. The windText member is of type TEHandle, as is the textHandle variable - so we can simply give one the value of the other. After the above code executes windText references the same block of memory as textHandle.

At this point our program now has a simple means of accessing the additional data associated with a window. All we need to do is follow the path from the window's refCon field to the WindData structure. To store a handle in the refCon field we called the Toolbox function SetWRefCon(). To retrieve that same handle we now call the Toolbox function GetWRefCon().

long		windRefCon;

windRefCon = GetWRefCon( theWindow );

The variable windRefCon now holds a value that is a handle to the window's WindData structure. The refCon field held this value as a long, whereas WindData is of our application-defined type WindDataHandle. So we need to typecast it to our specific type in order to make use of it.

WindDataHandle	theData;

theData = (WindDataHandle)windRefCon;

Double-dereferencing the handle makes the field of the structure accessible. If we want to access the text edit record we've associated with the window, we can assign the structure's windText field to a TEHandle variable.

TEHandle		textHandle;

textHandle = (**theData).windText;

Having a TEHandle means we can work with the text edit record the handle references. Specifically we'll want to update the text edit record. Updating the record draws the record's text to a specified port. If we make that port the window's port (as defined by the window's portRect field), the text gets drawn to the window. We'll first erase any existing window content and then draw the entire text edit record text to the window.

SetPort( theWindow );
EraseRect( &(*theWindow).portRect );
TEUpdate( &(*theWindow).portRect, textHandle );

TEUpdate() draws the text edit record's text in a rectangle bounded by the coordinates of the view rectangle and display rectangle used in the creation of the text edit record (by the call to TENew()).

OpenTextFile

This month's program is OpenTextFile. When you run OpenTextFile you'll be presented with the dialog box pictured in Figure 3.


Figure 3.The OpenTextFile dialog box with the preview section expanded.

When you click on a text file in the dialog box file list you'll see the first several words from that file displayed under the Preview heading at the left side of the open file dialog box - as shown in Figure 3. If you uncheck the Show Preview checkbox the dialog box will collapse and the preview information will disappear (see Figure 4).


Figure 4.The OpenTextFile dialog box with the preview section collapsed.

When you select a text file and click the Open button, the open file dialog box is dismissed and a new window holding the contents of the selected text file appears - as shown in Figure 5.


Figure 5.The OpenTextFile program displaying the contents of a text file.

If the selected file consists of a modest amount of text, the entire contents of the file will be displayed in the window. If the file holds a good deal of text, some of the text will be clipped at the bottom of the window. The OpenTextFile program doesn't support scrollable text (that's material for another Getting Started article!), so text that doesn't fit in the window is lost. When finished, click the mouse button to end the program.

Creating the OpenTextFile Resources

Start your resource development by creating a new folder named OpenTextFile in your main CodeWarrior folder. Launch ResEdit and create a new resource file named OpenTextFile.rsrc. Specify that the OpenTextFile folder act as the resource file's destination. This resource file will hold just three resources, two of which you've created for each of our examples: the one ALRT and one DITL. Collectively ALRT 128 and DITL 128 define the program's error-handling alert. If the OpenTextFile program encounters a serious problem while executing, then this alert appears and the program quits.

The remaining one resource is WIND 128. The placement of this window isn't critical, but you will want to make it large enough to hold the text from a small text file. If you download the project files from MacTech's ftp site at <ftp://ftp.mactech.com/src/mactech/volume16_2000/16.04.sit>, then you'll find that you have an example text file named Joyce.text. To provide a window large enough to hold all of the text from this file, use the window coordinates shown in Figure 6.


Figure 6.The OpenTextFile resources.

Creating the OpenTextFile Project

Create a new project by running CodeWarrior and choosing New Project from the File menu. Use the MacOS:C_C++:MacOS Toolbox:MacOS Toolbox Multi-Target project stationary for the new project. Uncheck the Create Folder check box, then click the OK button. Now name the project OpenTextFile.mcp and choose the existing OpenTextFile folder as the project's destination.

Add the OpenTextFile.rsrc resource file to the project and then remove the SillyBalls.rsrc file. If you want, go ahead and remove the ANSI Libraries folder - this project won't be making use of any ANSI C libraries.

Now create a new source code window by choosing New from the File menu.. Save the window, giving it the name OpenTextFile.c. Choose Add Window from the Project menu to add the new empty file to the project. Now remove the SillyBalls.c placeholder file from the project window. You're now ready to type in the source code.

If you want to save yourself some work, log on to the Internet and head to MacTech's ftp site at ftp://ftp.mactech.com/src/mactech/volume16_2000/16.04.sit. There you'll find the OpenTextFile source code file available for downloading.

Walking Through the Source Code

OpenTextFile begins with the definition of a couple of constants. The constant kALRTResID holds the ID of the ALRT resource used to define the error-handling alert. kWINDResID holds the ID of the WIND resource used to define the window that's to hold the text from a user-selected text file.

/********************* constants *********************/

#define		kALRTResID						128 
#define		kWINDResID						128

Now we define our own data structure - the WindData structure that holds supplemental window information.

/****************** data structures ******************/

typedef  struct 
{
   TEHandle   windText;
   
} WindData, *WindDataPtr, **WindDataHandle;

We'll use a global variable to keep track of the one window that the program opens.

/****************** global variables *****************/

WindowPtr  gTextWindow;

Next come the program's function prototypes.

/********************* functions *********************/

void		ToolBoxInit( void );
void		OpenExistingTextFile( void );
void		UpdateTextWindow( void );
void		DoError( Str255 errorString );

The main() function of OpenTextFile begins with the initialization of the Toolbox. After that the application-defined function OpenExistingTextFile() does just that. Just before displaying text in the window via the application-defined function UpdateTextWindow() we display the previously hidden window by calling the Toolbox function ShowWindow(). A simple while loop waits for a click of the mouse button. When that click occurs main() (and the program) ends.

/********************** main *************************/

void		main( void )
{ 
	ToolBoxInit();
      
	OpenExistingTextFile();

	ShowWindow( gTextWindow );

	UpdateTextWindow();
      
	while ( !Button() )
		;
}

The Toolbox initialization function ToolBoxInit() remains the same as previous versions.

/******************* ToolBoxInit *********************/

void  ToolBoxInit( void )
{
   InitGraf( &qd.thePort );
   InitFonts();
   InitWindows();
   InitMenus();
   TEInit();
   InitDialogs( nil );
   InitCursor();
}

The OpenExistingTextFile() function takes care of most of the tasks discussed in this article. The routine starts with a number of variable declarations.

/*************** OpenExistingTextFile ****************/

void	OpenExistingTextFile( void )
{
	SFTypeList				typeList = { 'TEXT', 0, 0, 0 };
	StandardFileReply	reply;
	short						fileRefNum;
	long							fileLength;
	Ptr							textBuffer;
	Rect							destRect;
	Rect							viewRect;   
	WindDataHandle		theData;
	TEHandle					textHandle;

StandardGetFilePreview() is called to display the standard open file dialog box. After the user dismisses the dialog box, the sfGood field of the returned reply variable is examined. If the user canceled, the program calls DoError() to quit.

	StandardGetFilePreview( nil, 1, typeList, &reply );
   
	if ( reply.sfGood == false )
		DoError( "\pError selecting a file." );

If the user didn't cancel, but instead made a selection, a new window is opened. A call to the Toolbox function SetWTitle() sets the window's title bar title to the name of the selected file. After that the window's port is made the current port to ensure that the eventual display of text occurs in this new window.

	gTextWindow = GetNewWindow( kWINDResID, nil, 
														(WindowPtr)-1L);
	if ( gTextWindow == nil )
		DoError( "\pError attempting to open a new window." );
	
	SetWTitle( gTextWindow, reply.sfFile.name );

	SetPort( gTextWindow );

Next, a new TEHandle is created. This handle will serve as a reference to the text that we'll soon be reading in from the selected file.

	SetRect( &destRect, 10, 10, 410, 270 );
	viewRect = destRect;
	textHandle = TENew( &destRect, &viewRect );

A call to FSpOpenDF() opens the selected file, and calls to GetEOF() and SetFPos() determine the file length in bytes and set the file mark to the start of the file.

	FSpOpenDF( &reply.sfFile, fsRdPerm, &fileRefNum );

	GetEOF( fileRefNum, &fileLength );
	SetFPos( fileRefNum, fsFromStart, 0 );

Now we create a file buffer - an area in memory in which we temporarily store data. Into that memory we read the contents of the user-selected file. After that we transfer the data to the text edit record by way of a call to TESetText().

	textBuffer = NewPtr( fileLength );

	FSRead( fileRefNum, &fileLength, textBuffer );

	HLock( (Handle)textHandle );
		TESetText( textBuffer, fileLength, textHandle );   
	HUnlock( (Handle)textHandle );

We've got the file's contents stored in a text edit record. Now we need to create a new structure of the application-defined type WindData and set that structure's windText field to reference the text edit record.

	theData = (WindDataHandle)NewHandleClear(sizeof(WindData));   
	(**theData).windText = textHandle;

Finally, let's set the window's refCon field to hold the handle that references the WindData data structure in memory. Then when we need to access the window information (the text edit record) for this window we'll be able to retrieve it.

	SetWRefCon( gTextWindow, (long)theData );
}

The primary use of the application-defined data structure is to hold the text that's to be displayed in a window. To display that text for the first time - and to update, or refresh, the text in the window at any time - UpdateTextWindow() is called. UpdateTextWindow() begins with the declaration of a few variables.

/***************** UpdateTextWindow ******************/

void  UpdateTextWindow( void )
{
	WindDataHandle		theData;
	long							windRefCon;
	TEHandle					textHandle;

Next we set the port to the window to update to ensure that the text we're about to display does indeed end up in the desired window.

	SetPort( gTextWindow );

A call to GetWRefCon() retrieves from the window's refCon field the handle to the window's supplemental data. Typecasting is then performed to coerce the long variable to a handle to the WindData structure.

	windRefCon = GetWRefCon( gTextWindow );
   
	theData = (WindDataHandle)windRefCon;

Now we retrieve the text edit handle and store it in the local TEHandle variable textHandle.

	textHandle = (**theData).windText;

Calls to EraseRect() and TEUpdate() clear the window and write the text from the text edit record to the window.

	EraseRect( &(*gTextWindow).portRect );
	TEUpdate( &(*gTextWindow).portRect, textHandle );
}

DoError() is unchanged from prior versions. A call to this function results in the posting of an alert that holds an error message. After the alert is dismissed the program ends.

/********************** DoError **********************/

void		DoError( Str255 errorString )
{
	ParamText( errorString, "\p", "\p", "\p" );
	
	StopAlert( kALRTResID, nil );
	
	ExitToShell();
}

Running OpenTextFile

Run OpenTextFile by choosing Run from CodeWarrior's Project menu. After the code is compiled, CodeWarrior launches the OpenTextFile program and displays the standard open file dialog box. Use this dialog box to select any text file on your local drives. If you click the Cancel button, the program will quit. If you instead select a text file, that file will be opened and its contents will be displayed in a new window. Clicking the mouse button ends the program.

Till Next Month...

We've covered a lot of ground this month. Here you saw how to create an application-defined data structure that can be used to hold any type of information, and you saw how to bind that structure with a window. You also saw how to display the standard open dialog box to give the user the power to choose a text file to open. You now know how to open a text file, copy that file's contents to memory, associate that file's text with a window, and display that same text to a window. If you'd like to know more about files, consider browsing the Files volume of Inside Macintosh. Doing that will provide you with a preview of the code you'll see next month when we delve deeper into working with files...

 
AAPL
$100.41
Apple Inc.
-0.17
MSFT
$45.11
Microsoft Corpora
+0.16
GOOG
$583.46
Google Inc.
-1.03

MacTech Search:
Community Search:

Software Updates via MacUpdate

Videobox 4.1.1 - Download Flash video th...
Videobox allows you to quickly and easily download Flash video from most all of the popular video sites on the internet. Videobox will convert the video into a native Quicktime format so it's ready... Read more
Web Snapper 3.3.5 - Capture entire Web p...
Web Snapper lets you capture Web pages exactly as they appear in your browser. You can send them to a file as images or vector-based, multi-page PDFs. It captures the whole Web page - eliminating... Read more
Picasa 3.9.138 - Organize, edit, and sha...
Picasa and Picasa Web Albums allows you to organize, edit, and upload your photos to the Web from your computer in quick, simple steps. Arrange your photos into folders and albums and erase their... Read more
Tidy Up 3.0.15.0 - Find duplicate files...
Tidy Up is a complete duplicate finder and disk-tidiness utility. With Tidy Up you can search for duplicate files and packages by the owner application, content, type, creator, extension, time... Read more
Parallels Desktop 10.0 - Run Windows app...
Parallels Desktop is simply the world's bestselling, top-rated, and most trusted solution for running Windows applications on your Mac. With Parallels Desktop for Mac, you can seamlessly run both... Read more
Apple Final Cut Pro X 10.1.3 - Professio...
Apple Final Cut Pro X is a professional video editing solution.Completely redesigned from the ground up, Final Cut Pro adds extraordinary speed, quality, and flexibility to every part of the post-... Read more
Apple Compressor 4.1.3 - Adds power and...
Compressor adds power and flexibility to Final Cut Pro X export. Customize output settings, work faster with distributed encoding, and tap into a comprehensive set of delivery features. Powerful... Read more
Chromium 36.0.1985.143 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. FreeSMUG-Free OpenSource Mac User Group build is... Read more
Macgo Blu-ray Player 2.10.6.1691 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple Motion 5.1.2 - Create and customiz...
Apple Motion is designed for video editors, Motion 5 lets you customize Final Cut Pro titles, transitions, and effects. Or create your own dazzling animations in 2D or 3D space, with real-time... Read more

Latest Forum Discussions

See All

BoxPop Review
BoxPop Review By Jennifer Allen on August 21st, 2014 Our Rating: :: UNTAXING PUZZLESUniversal App - Designed for iPhone and iPad Move from tile to tile to clear the board in this slightly too simple and easy puzzle game.   | Read more »
Breakdown Buddy Review
Breakdown Buddy Review By Jennifer Allen on August 21st, 2014 Our Rating: :: FLAWED CONCEPTiPhone App - Designed for the iPhone, compatible with the iPad There’s a good idea here, but Breakdown Buddy isn’t as useful as it tries to... | Read more »
jamstik, the Guitar You Can Take Everywh...
jamstik, the Guitar You Can Take Everywhere, is Now Available for Purchase Posted by Jessica Fisher on August 21st, 2014 [ permalink ] Zivix has announced that their guitar-in-your-pocket, | Read more »
Star Walk 2 - Guide to the Sky Day and N...
Star Walk 2 - Guide to the Sky Day and Night 1.0.0 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0.0 (iTunes) Description: The next generation of the best-selling Star Walk, winner of Apple Design Award, used by... | Read more »
Forks Over Knives Review
Forks Over Knives Review By Jennifer Allen on August 21st, 2014 Our Rating: :: TASTY AND HEALTHYUniversal App - Designed for iPhone and iPad Want some tasty new recipe ideas but want to keep it healthy? Forks Over Knives is the... | Read more »
Invaders! From Outer Space (Games)
Invaders! From Outer Space 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: | Read more »
Dementia: Book of the Dead (Games)
Dementia: Book of the Dead 1.00 Device: iOS Universal Category: Games Price: $2.99, Version: 1.00 (iTunes) Description: EXCLUSIVE CONTENT ONLY ON THE APP STORE. Medieval England. Times of knights, witches and hunters. What other... | Read more »
Wan Nyan Slash (Games)
Wan Nyan Slash 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Wan Nyan Slash is an infinite adorable demon slaying slashing action game! Play as the wandering samurai Wan and Nyan as they... | Read more »
Fallin Love - The Game of Love (Games)
Fallin Love - The Game of Love 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: GRAVITATE AROUND LOVE | Read more »
Ancient Battle: Hannibal (Games)
Ancient Battle: Hannibal 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: | Read more »

Price Scanner via MacPrices.net

21-inch iMacs on sale for up to $150 off MSRP
B&H Photo has 21″ iMacs on sale for up to $150 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 2.7GHz iMac: $... Read more
27-inch 3.2GHz iMac on sale for $1698, save $...
Abt has the 27″ 3.2GHz iMac on sale for $1698 including free shipping. Their price is $101 off MSRP. Read more
Mac Backup Guru 2.0 Drive Backup/Cloneing Uti...
Mac Backup Guru developer MacDaddy has released Mac Backup Guru 2.0, offering new and enhanced advanced features, such as bootable backups, synchronised volumes and folders, and a Snapshot mode that... Read more
Operate GE’s New Free-Standing KItchen Range...
Think you accidentally left the oven on? Switch it off while on the go. The new free-standing Profile™ Series gas and electric ranges are GE’s second cooking appliances, following their wall oven, to... Read more
Parallels Announces Parallels Desktop 10 for...
The no. 1-selling software for running Windows applications on a Mac becomes an even easier choice for millions of consumers and IT professionals worldwide with the launch of the most powerful... Read more
Apple now offering certified refurbished 2014...
 The Apple Store is now offering Apple Certified Refurbished 2014 MacBook Airs for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Best Buy’s College Student Deals: $100 off Ma...
Take an additional $100 off all MacBooks and iMacs, $50 off iPad Airs and iPad minis, at Best Buy Online with their College Students Deals Savings, valid through September 6th. Anyone with a valid .... Read more
MacBook Airs on sale for $100 off MSRP, free...
B&H Photo has three 2014 MacBook Airs on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels Desktop and LoJack for Laptops... Read more
Razer Taipan Mouse For Gamers And Non-Gamers...
If you’re a serious gamer on either Mac or Windows PCs, a serious gaming mouse is a necessity for first-tier performance. However, even if like me you’re not much of a gamer, there’s still a strong... Read more
15-inch 2.2GHz MacBook Pro on sale for $1899,...
Adorama has the new 15″ 2.2GHz Retina MacBook Pro on sale for $1899 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP, and it’s the lowest price available for this... Read more

Jobs Board

Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.