TweetFollow Us on Twitter

Jun 01 PowerPlant Workshop

Volume Number: 17 (2001)
Issue Number: 06
Column Tag: PowerPlant Workshop

Basic Windows

by Aaron Montgomery

How one goes about writing a PowerPlant application

These Articles

This is the third article in a series of articles about Metrowerks' PowerPlant application framework. The first article introduced how the framework deals with commands and the second article discussed the debugging facilities of the framework. This article focuses on the window classes in the framework. This series assumes familiarity with the C++ language, the Macintosh Toolbox API and the CodeWarrior IDE. The articles were written using CodeWarrior 6 with the net-update to IDE 4.1.0.3 and no other modifications. Throughout this and future articles, I will assume that you are using the class browser and the debugger to explore the PowerPlant code more fully.

Panes, Views and Windows (oh my)

Windows provide your users the ability to view their data and so they will be a fundamental part of your user interface. PowerPlant helps you in providing this visual interface through its LPane, LView and LWindow classes. An LPane is something that is drawn on the screen. An LView is an LPane that may contain other LPanes (which allows you to design a visual hierarchy). An LWindow is usually the topmost LView in a visual hierarchy and it is derived from both LView and LCommander. The PowerPlant Book spends multiple chapters on these classes so you can (correctly) assume that I will leave a lot unsaid. The purpose of this article is to introduce you to Constructor and some of the basic code needed to work with the visual hierarchy in PowerPlant.

Constructing Windows

My general pattern of development is to first set up the visual hierarchy in Constructor and then write the code to support that hierarchy. This ordering is not required and I frequently go back and forth between Constructor and the CodeWarrior IDE. For the sake of keeping the article short, we will inspect the AppResources.ppob file first and the source code second. The application uses three windows: a document window for text editing, a dialog that requests information from the user and a floating window for displaying information to the user. We will examine the document window first.

Double-clicking the AppResources.ppob in the project window should launch Constructor. You can do this resource editing in Resourcerer (using templates provided) or in ResEdit (if you are really got at hex), but I have always found Constructor more convenient. The discussion here is limited (remember, this is an introduction to PowerPlant, not a reference) but you can find more information in the Constructor User Guide on the CodeWarrior CD. Double-click the "Document Window" resource (you may need to expand the Windows & Views group to see it) and select Show Object Hierarchy from the Layout menu. A document window has three nested views: an LWindow object containing an LScrollerView object containing an LTextEditView object (well, not quite, but we will get to that in a moment). Open a Property Inspector window (from the Windows menu) and select the LWindow from the Hierarchy window. Most of the properties should be familiar to you if you have worked with Macintosh window. You can learn about many of the ones that aren't familiar by adjusting them and then running the application. The first property is the Class ID that is used by PowerPlant to determine exactly what sits in each resource, we will explain it later, but for now you will want to leave it untouched.

Inside the LWindow is an LScrollerView that handles the scrollbars for the window. Click on it in the Hierarchy window and look at its properties. LScrollerView is derived from LPane and the first set of values is used by the LPane base class. The Pane ID is necessary if you need to have access to this pane from your source code, since I don't access the scrollbars from the source code I'll leave it set to 0. Just like the LWindow resource, don't change the Class ID number. The LScrollerView is 2 pixels higher and wider than the enclosing window (and placed one so that it extends 1 pixel beyond each edge). I can never remember the appropriate values for the Height and Width properties so I usually dig around in the PowerPlant stationary or PowerPlant examples and examine the values there. This LScrollerView is bound to all four sides (so it will expand in all directions if the window expands). The second set of properties is specific to the LScrollerView class. The first is the thickness of the scrollbars as well as their positioning. Again, I don't typically remember these off the top of my head and so I end up looking in stationary and example files. By setting the horizontal scrollbar's Left and Right properties to -1 we cause it to be absent. The vertical bar is set to 0 from the top and 15 from the bottom (more values gleamed from other sample resources). The Scrolling View ID is the Pane ID of the pane that the scrollbars will control (in this case, it is the LTextEditView). I have activated Live Scrolling.

Inside the LScrollerView is something based on an LTextEditView. If you look at an untouched LTextEditView, the Class ID is txtv, but this has a Class ID of Htxv. We are not going to be using a LTextEditView in our application, but rather a subclass called CHTMLTextView. Since the subclass doesn't need any more data than what is in a LTextEditView resource, the easiest way to create a resource for a CHTMLTextView is to use the LTextEditView template and then change the Class ID. The effects of this change will be discussed below when we talk about the source code. I've discussed the LPane properties in the last paragraph. Since I will try to access this pane from the source code, I needed to give it an "honest" Pane ID and I choose Text since this is the text view. The choice to give the LTextEditView a 2 pixel edge is again based on looking at various samples. The LView properties are left untouched (and are explained in The PowerPlant Book).

The LTextEditView properties contain TextEdit flags that affect the Macintosh TextEdit structure. I have set Word Wrap on (since I have eliminated the horizontal scrollbar, this is important). The TextTraits is a resource ID and checking the appropriate resource in Constructor shows that these traits set the font size to 12 and the font to Monaco (since it is mono-spaced). The TEXT resource ID is the location of the original text in the window (the TEXT resource is in the file AppResource.rsrc since I use Resourcerer to edit it). In some later article, I might talk about how to create templates so that you can add data to be edited in Constructor for your classes.

Before looking at the other two windows, a natural question might arise: where did these I get these classes to put into the hierarchy? You can create a new window resource by dragging an item from the Windows tab of the Catalog window (choose Catalog from the Window menu) into your resource file's window. Then you open the window you just created and build the hierarchy within this resource by dragging items from the Catalog Window into the resource's window. You should probably spend some time just browsing your options. One thing to notice is that some visual features appear more than once. In the Views tab, you will find an LScroller, an LActiveScroller and an LScrollerView. The first two pre-date appearance classes but I cannot seem to find any PowerPlant documentation that helps you make this determination. If you add these to your window and then try to register them in your source code (a step described below), you will be required to add files to your project. This is an indication that a newer class is available. Other examples are the LRadioGroup in the Other tab (replaced by the LRadioGroupView in the Views tab) and LEditField in the Panes tab (replaced by LEditText in the Appearance tab). The easiest way to determine what to use is to dig around in the sample ppob files provided by Metrowerks.

The next window, Glossary Window, is a floating window. The actual steps in constructing it are not very different from the steps required in the previous example. The only things I will point out are that the LTextEditView is set to be neither Selectable nor Editable.

The final window is a dialog box. This is created by dragging an LGADialog from the Catalog window to your resource file's window. You can then create the visual hierarchy in the same manner as for regular windows. You will need to set the Default Button ID and the Cancel Button ID. This will allow your dialog to correctly handle user requests from the keyboard. When you actually create those buttons, you will set their messages to msg_OK and msg_Cancel and check "Is Default Button" for the OK button as well. We will discuss the messaging mechanism in PowerPlant in more depth in the fifth article, if you cannot wait, look in The PowerPlant Book.

The only other changes are the removal of the Bad Things menu, the addition of two commands to the Edit menu and the addition of an Insert and a Glossary menu. You should be able to determine how these were created and what they do from the first and second articles so I will move on to discussing the changes to the source code.

Checking that the class hierarchy has been constructed correctly can be done using the Visual Hierarchy menu item in the Debug menu. This will present a floating window displaying all of the hierarchies of the front-most regular application windows. You can even identify panes by moving the mouse over them and watching the pane information turn green in the Visual Hierarchy window.

Changes To Earlier Classes

Just as PowerPlant has two implementation files for the UDebugging.h header, there are three implementation files for the UDesktop.h header. The basic implementation is UDesktop.cp, UFloatingDesktop.cp should be used if you use floating windows. Finally, UWMgr20Desktop.cp should be used if you know that the code will be run using version 2.0 of the Window Manager. This project uses UFloatingDesktop.cp because it does contain floating windows, but does not require any of the UWMgr20Desktop.cp implementations.

There are no code changes in the main() function so we turn our attention to the CDocumentApp class. We remove all the Bad Things code from the previous article and add the Lookup command (which is always enabled).

This moves us to the CTextDocument class. Although very little of the code is new, we will explain some of the existing lines of code for the first time. We start with this classes constructor:

CTextDocument.cp
CTextDocument::CTextDocument(
	LCommander*		inSuper)
	: LSingleDoc(inSuper)
{
	mWindow = LWindow::CreateWindow(PPob_TextWindow, this );
	ValidateObject_(mWindow);
	
	myTextView =
		FindPaneByID_(mWindow, kTextView, CHTMLTextView);
	ValidateObject_(myTextView);
	mWindow->SetLatentSub(myTextView);

	NameNewDoc();
	
	mWindow->Show();
}

The LSingleDoc class (CTextDocument's superclass) has an LWindow* member data named mWindow. We use this store a pointer to the document's window. The call to CreateWindow() takes a resource id as well as a commander. In the first article, you learned that the commander will be the window's supercommander. Here we discuss how PowerPlant builds the LWindow from the resource. The actual conversion from resource data to class object is done by the PowerPlant classes called UReanimator and URegistrar. PowerPlant opens the resource as an LStream. Then PowerPlant reads the first four characters of the resource and looks these up in a static table that associates four-character codes to class constructors taking an LStream as input. If the four-character code is found, then the associated constructor is used to create the object. If the four-character code is not found, an exception is thrown. You can witness such an event if you replace the Class ID of the CHTMLTextView object in the Document Window with HTXT and then run the application. The natural question now is how PowerPlant knows which constructors are associated with which Class IDs. The static table used is generated by calls to the macro RegisterClass_(). This will associate the class's class_ID four-character code with the class's stream constructor. You will need to make sure all of your PowerPlant resources are properly registered before you attempt to read them in from the file. You can use the Validate PPob… and Validate All PPobs menu items from the Debug menu to verify that you have made all of the necessary registrations.

Once we have a pointer to the window, a natural thing to do will be to try to access the things in the window. In this particular instance, we will want to establish the text view as the latent subcommander for the window (meaning that when the window is placed on-duty, the text view takes control). You can access the subpanes of a window using the FindPaneByID_() macro. This macro takes the window, the Pane ID and the type of that pane as its input. If you access a pane with this macro, you will need to make sure that the pane has a unique Pane ID. The FindPaneByID_() will attempt to find the subpane using the given information and cast it to the appropriate type (throwing an exception if either of these operations fail). Now that we have a pointer to the appropriate commander, we can establish it as the latent subcommander.

CHTMLTextView

The CHTMLTextView class derives from the LTextEditView class and adds some features appropriate for editing HTML code. We examine some of the class declaration first.

CHTMLTextView.h
class CHTMLTextView
: public PowerPlant::LTextEditView
{
public:
	enum { class_ID = FOUR_CHAR_CODE(‘Htxv') };

					CHTMLTextView();
					CHTMLTextView(PowerPlant::LStream* inStream);
	
	//remainder omitted
};

The first thing to notice is that the class defines class_ID to be Htxt so that the RegisterClass_() macro will work correctly. You should choose class_IDs that contain some uppercase letters (because PowerPlant reserves those which are all lowercase).

Before presenting the code, I'll explain what CHTMLTextView does that a regular LTextEditView does not do. A CHTMLTextView knows how to insert a few of the HTML tags into the document, when it does this insertion, it places a marker (the character •) in places which should be filled out by the user. The user can then use the Goto Next Marker and Goto Previous Marker items in the Edit menu to jump from their current location to the next (or previous) marker. (If you use Alpha as a text editor, then this behavior probably looks familiar). The CHTMLTextView is also capable of inserting appropriate HTML tag templates (using the marker for items to be supplied by the user). Some of the code allowing CHTMLTextView to handle these requests is below.

InsertTag() in CHTMLTextView.cp

void CHTMLTextView::InsertTag(const std::string& inOpen,
	const std::string& inClose)
{
	TEHandle theTextEditH = GetMacTEH();

StHandleLocker	theLock(reinterpret_cast(theTextEditH));

	short theStart = (**theTextEditH).selStart;
	short theEnd = (**theTextEditH).selEnd;
	
	if (theStart == theEnd)
	{
		string theMarker = "";;
		theMarker += Marker();
		Insert(theMarker.c_str(), theMarker.length());
		theEnd = theStart + 1;
	}
	
	SetSelectionRange(theEnd, theEnd);
	Insert(inClose.c_str(), inClose.length());
	SetSelectionRange(theStart, theStart);
	Insert(inOpen.c_str(), inOpen.length());
	SetSelectionRange(theStart, theStart);
}

We obtain the TEHandle directly with a call to GetMacTEH() and then use a StHandleLocker to lock the handle until the StHandleLocker's destructor is called. Although it is unlikely that the lock is currently needed and so the lock is unnecessary, that might make the code fragile if the PowerPlant code changed. Furthermore, because the lock is controlled by an object on the stack, I feel confident that the lock will be released. We obtain the starting and ending of the selection directly from the TEHandle. If no item is selected, we insert a marker into the text and make it the current selection. Then we insert the tag close and tag open and place the cursor at the start of the tag. The code needed to go to the next marker has a similar flavor.

GotoNextMarker() in CHTMLTextView.cp
void CHTMLTextView::GotoNextMarker()
{
	TEHandle theTextEditH = GetMacTEH();

	StHandleLocker
		theLock1(reinterpret_cast(theTextEditH));

	short theEnd = (**theTextEditH).selEnd;
	short theLength = (**theTextEditH).teLength;
	Handle	theTextH = (**theTextEditH).hText;
	
	StHandleLocker	theLock2(theTextH);
	
	for (short thePosition = theEnd;
		thePosition != theEnd - 1;
		++thePosition)
	{
		if (thePosition > theLength)
		{
			thePosition = 0;
		}
		if ( *((*theTextH) + thePosition ) == Marker() )
		{
			SetSelectionRange(thePosition, thePosition + 1);
			return;
		}
	}
}

Both of these panes use PowerPlant's code to handle drawing and user inter-action. If you need to override these (and other) routines, you need to be aware of how PowerPlant factors the work. There is both a Draw() and a DrawSelf() method, similarly, there is both a Click() and a ClickSelf() method. You should override the DrawSelf() method and call Draw() method in your source. The Draw() method will do the necessary set up, then call the DrawSelf() method and then do the necessary tear down.

CGlossary

The CGlossary class provides a first glimpse into how dialogs are handled in PowerPlant (they will be discussed more in depth in the fifth article). The first thing to notice is that CGlossary's constructor is private and unimplemented. This is because the class currently has no data and only static methods. The class could have been implemented as a namespace at this point, but it has been written as a class to allow for the possibility of future improvements (for example, storing the glossary data in an associative array in RAM). The class only has two methods: RegisterClasses() and Lookup(). RegisterClasses() does the usual task of registering those visual classes which are used by the CGlossary class and is not presented here. The Lookup() method is overloaded with two implementations. The first takes no arguments, presents the user with a dialog asking the tag to look up, and then invokes the second version. The second version takes a string argument and presents the user with the tag's glossary information (if it exists). Their code is presented below.

void CGlossary::Lookup()
{
	unsigned char theString[256];
	theString[0] = ‘\0';

	StDialogHandler	theHandler(k_PPob_GlossaryDialog, LCommander::GetTopCommander());
	LWindow*		theDialog = theHandler.GetDialog();

	LEditText*		theField
		= FindPaneByID_(theDialog,
				k_PaneIDT_Lookup, LEditText);

	theField->SetDescriptor(StringLiteral_(""));
	theField->SelectAll();
	theDialog->SetLatentSub(theField);
	theDialog->Show();

	while (true)
	{
		MessageT	hitMessage = theHandler.DoDialog();

		if (hitMessage == msg_Cancel)
		{
			return;
		}
		else if (hitMessage == msg_OK)
		{
			theField->GetDescriptor(theString);
			break;
		}
	}

	Lookup(theString);
}

We use a StDialogHandler to manage the dialog that obtains user input (more on this in the fifth article). We pass in the resource ID of the appropriate dialog and obtain the dialog window from the Handler. We use FindPaneByID_() to find the LEditText in that window and present the window to the user. We then enter a loop calling the DoDialog() method of theHandler repeatedly. This will return information about what items were hit in the dialog. If the Cancel button was hit, we simply leave the function. If the OK button was hit, we obtain the user's input using the GetDescriptor() method of the LEditText and break from the loop. The handler ignores any other action by the user. Once the dialog has been dismissed we call the other Lookup() method (because the OK button was hit).

void CGlossary::Lookup(Str255 inTerm)
{
	StResource	theDefinition;
	
	try
	{
		theDefinition.GetResource(k_ResType_TEXT, inTerm,
			true, true);
	}
	catch (...)
	{
		theDefinition.GetResource(k_ResType_TEXT,
			k_Str255_Unknown, true, true);
	}
	
	LWindow*	theWindowP
		= LWindow::CreateWindow(k_PPob_GlossaryWindow,
				LCommander::GetTopCommander());
	LTextEditView*	theDefnP
		= FindPaneByID_(theWindowP, k_PaneIDT_Definition,
				LTextEditView);
	theDefnP->SetTextHandle(theDefinition);
	
	LStaticText* theTermP
		= FindPaneByID_(theWindowP, k_PaneIDT_Term,
				LStaticText);
	LStr255	theString = StringLiteral_("<");
	theString += inTerm;
	theString += StringLiteral_(">");
	theTermP->SetDescriptor(theString);
}

The second version of Lookup() is responsible for actually determining and displaying information about the tag. The information is stored as resources of type TEXT with the name of the tag used as the name of the resource. We obtain the resource using a StResource that will dispose of the resource when its destructor is called. We first try to obtain a resource with the tag's name and if that fails, we use a generic error message stored in a resource with the name "Unknown". The first true in the GetResource() call tells the method to Throw_ if there is an error and the second true tells the method to only look in the current resource file.

Once we have the text we create a window, use some FindPaneByID_()'s to obtain pointers to some text fields and fill them in. We use the LStr255 class from PowerPlant for the second field. This class is described in a MacTech article written by John C. Daub and provides a lot of the functionality normally found in the C++ string class for Pascal style strings (of length up to 255). The StringLiteral_ macro converts its input into Pascal style strings. Currently it is very simple, but using the macro will make it easier to adjust if the PowerPlant API changes to C style strings at some future point.

Concluding Remarks

Well, that provides a user interface to your application. Although it was quick and sketchy, it should give you some feel for how PowerPlant handles windows. We explore how PowerPlant works with files in the next article (which is the other half of PowerPlant's LDocument class). The core portion of this series will be completed in the fifth article where we cover how PowerPlant handles dialogs. Once the core portion is completed, I am open for suggestions on where to go next, so if you have any ideas, please e-mail me.

PowerPlant References

References that are particularly appropriate for this article are the following:

  • "Panes", "Views", "Windows" chapters in The PowerPlant Book
  • John C. Daub's "The Ultra-Groovy LString Class" in MacTech, January 1999.

Aaron teaches in the Mathematics Department at Central Washington University in Ellensburg, WA. Outside of his job, he spends time riding his mountain bike, watching movies and entertaining his wife and two sons. You can email him at montgoaa@cwu.edu, try to catch his attention in the newsgroup comp.sys.mac.oop.powerplant or visit his web site at mac69108.math.cwu.edu:8080/.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Google Chrome 53.0.2785.143 - Modern and...
Google Chrome is a Web browser by Google, created to be a modern platform for Web pages and applications. It utilizes very fast loading of Web pages and has a V8 engine, which is a custom built... Read more
Chromium 53.0.2785.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. Version 53.0.2785.143: [Security Fix] High CVE-2016-... Read more
QuickBooks 2015 16.1.7.1524 R8 - Financi...
Save 20% on QuickBooks Pro for Mac today through this special discount link QuickBooks 2015 helps you manage your business easily and efficiently. Organize your finances all in one place, track... Read more
Sierra Cache Cleaner 11.0.1 - Clear cach...
Sierra Cache Cleaner is an award-winning general purpose tool for macOS X. SCC makes system maintenance simple with an easy point-and-click interface to many macOS X functions. Novice and expert... Read more
Default Folder X 5.0.7 - Enhances Open a...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click on... Read more
Safari Technology Preview 10.1 - The new...
Safari Technology Preview contains the most recent additions and improvements to WebKit and the latest advances in Safari web technologies. And once installed, you will receive notifications of... Read more
Tweetbot 2.4.4 - Popular Twitter client.
Tweetbot is a full-featured OS X Twitter client with a lot of personality. Whether it's the meticulously-crafted interface, sounds and animation, or features like multiple timelines and column views... Read more
Google Earth 7.1.7.2602 - View and contr...
Google Earth gives you a wealth of imagery and geographic information. Explore destinations like Maui and Paris, or browse content from Wikipedia, National Geographic, and more. Google Earth combines... Read more
Things 2.8.8 - Elegant personal task man...
Things is a task management solution that helps to organize your tasks in an elegant and intuitive way. Things combines powerful features with simplicity through the use of tags and its intelligent... Read more
Firefox 49.0.1 - Fast, safe Web browser.
Firefox offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals and casual... Read more

Cybird’s latest release - BFB Champions...
Launched in the UK in early September, BFB Champions’ newest update is loaded with great new features, and looks set to outshine the original version by taking it out of soft launch and giving it a new lease of life. | Read more »
3 apps to boost your focus
As someone who works from home, my workspace is a minefield of distraction. Cats, tasty snacks, the wind blowing past my window, that cleaning that I suddenly can’t put off any longer. If I let distraction takes its course, I find that soon half... | Read more »
4 games like Burly Men at Sea to inspire...
Burly Men at Sea is out today and it looks a treat. It tells the tale of three Scandinavian fishermen who leave the humdrum of their daily lives to go exploring. It’s a beautiful folksy story that unfurls as you interact with the environment... | Read more »
3 reasons you need to play Kingdom: New...
Developed by a tag team of indie developers - Thomas "Noio" van den Berg and Marco "Licorice" Bancale - Kingdom is a vibrant medieval fantasy adventure that casts players as a king or queen who must expand their empire by exploring the vasts lands... | Read more »
JoyCity have launched a brand new King o...
Great news for all of you Game of Dice fans out there - JoyCity have just released a brand new limited edition pack with a really cool twist. The premise of Game of Dice is fairly straightforward, asking you to roll dice to navigate your way around... | Read more »
Burly Men at Sea (Games)
Burly Men at Sea 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Burly Men at Sea is a folktale about a trio of large, bearded fishermen who step away from the ordinary to seek adventure. | Read more »
3 tips for catching the gnarliest waves...
Like a wave breaking on the shore, Tidal Rider swept its way onto the App Store charts this week settling firmly in the top 10. It’s a one-touch high score-chaser in which you pull surfing stunts while dodging seagulls and collecting coins. The... | Read more »
The beginner's guide to destroying...
Age of Heroes: Conquest is 5th Planet Games’ all new turn-based multiplayer RPG, full of fantasy exploration, guild building, and treasure hunting. It’s pretty user-friendly as far as these games go, but when you really get down to it, you’ll find... | Read more »
Infinite Tanks (Games)
Infinite Tanks 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: | Read more »
Agatha Christie - The ABC Murders (FULL)...
Agatha Christie - The ABC Murders (FULL) 1.0 Device: iOS Universal Category: Games Price: $6.99, Version: 1.0 (iTunes) Description: Agatha Christie: The ABC Murders Your weapon is your knowledge. Your wits will be put to the ultimate... | Read more »

Price Scanner via MacPrices.net

Apple refurbished 2015 13-inch MacBook Airs a...
Apple has Certified Refurbished 2015 13″ MacBook Airs available starting at $759. An Apple one-year warranty is included with each MacBook, and shipping is free: - 2015 13″ 1.6GHz/4GB/128GB MacBook... Read more
MacBook Airs on sale for up to $100 off MSRP
B&H Photo has 13″ and 11″ MacBook Airs on sale for up to $100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 11″ 1.6GHz/128GB MacBook Air: $799 $100 MSRP - 11″ 1.6GHz/256GB... Read more
Apple refurbished 12-inch 128GB iPad Pros ava...
Apple has Certified Refurbished 12″ Apple iPad Pros available for up to $160 off the cost of new iPads. An Apple one-year warranty is included with each model, and shipping is free: - 32GB 12″ iPad... Read more
Phone2Action Unveils New Voter Turnout Techno...
Phone2Action, a leading digital advocacy platform, today launched its Tech to Vote Civic Action Center digital advocacy and communications platform on National Voter Registration Day September 27.... Read more
Apple & Deloitte Team Up to Help Business...
Apple and international professional services firm Deloitte have announced a partnership to help companies quickly and easily transform their workflow dynamics by maximizing the power, ease-of-use,... Read more
Chop Commute – See Traffic and Drive Times on...
Shrewsbury, Massachusetts based Indie developer, InchWest has released Chop Commute 1.61, a Mac app that takes the guesswork out of daily commute by showing real-time traffic and drive times right on... Read more
12-inch 32GB WiFi iPad Pros on sale for $50 o...
B&H Photo has 12″ 32GB WiFi Apple iPad Pros on sale for $50 off MSRP, each including free shipping. B&H charges sales tax in NY only: - 12″ Space Gray 32GB WiFi iPad Pro: $749 $50 off MSRP -... Read more
Recent price drops on refurbished iPad minis...
Apple recently dropped prices on several Certified Refurbished iPad mini 4s and 2s as well as iPad Air 2s. An Apple one-year warranty is included with each model, and shipping is free: - 16GB iPad... Read more
Apple refurbished Mac minis available startin...
Apple has Certified Refurbished Mac minis available 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
13-inch 2.5GHz MacBook Pro available for $928...
Overstock has the 13″ 2.5GHz MacBook Pro available for $927.99 including free shipping. Their price is $171 off MSRP. Read more

Jobs Board

*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
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
*Apple* Wireless Lead - T-ROC - The Retail O...
…wealth of knowledge in wireless sales and activations to the Beautiful and NEW APPLE Experience store within MACYS.. THIS role, APPLE Wireless Lead, isbrandnewas Read more
Lead *Apple* Advocate - T-ROC - The Retail...
…Company, is proud of its unprecedented relationship with our partner and client, APPLE ,in bringing amazing" APPLE ADVOCATES"to "non" Apple store locations. Read more
*Apple* Advocate - T-ROC - The Retail Outsou...
…Company, is proud of its unprecedented relationship with our partner and client, APPLE ,in bringing amazing" APPLE ADVOCATES"to "non" Apple store locations. Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.