TweetFollow Us on Twitter

Prograph Undo
Volume Number:11
Issue Number:3
Column Tag:Visual Programming

Filters & Sieves in Prograph CPX

Fooling the user gets the job undone

By Kurt Schmucker, Apple Computer, Inc.

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

Command objects as implemented in most Macintosh frameworks provide an excellent way to implement undo for most types of user actions in most types of applications - most, but not all.

Typically, the command object stores enough information to be able to perform the action, to be able to undo the action, and to be able to redo the action. In response to a user action, your code allocates an appropriate command object, initializes that object, and then returns that object to the framework. The framework will immediately send the “DoIt” message to this object to have the desired action performed. The framework holds onto that command object and will automatically send the “UndoIt” and “RedoIt” messages to the object if the user chooses the Undo or Redo from the Edit menu. The command object, using its stored data, will then undo or redo the operation. This completely relieves the developer from the responsibility for the run-time handling of the Undo menu item. [1]

This scheme works as long as there is enough memory and speed to undo and redo the operation (which is the whole point of implementing the command object.) Unfortunately, this is not always the case. For example, consider a structured drawing program like MacDraw or Canvas. In such a program, the user can select any subset of the objects that have been drawn and bring those selected objects “to the front,” that is, re-order them in the z-coordinate. It would not be possible to store the old order of the objects, in a reasonable amount of memory, in order to be able to effect an undo. (Admittedly, the definition of ‘reasonable’ is subjective. The straightforward use of command objects for this Bring to Front operation would require memory proportional to the number of objects in the entire drawing. The approach explained here requires only memory for the number of objects in the selection.)

The problem is that sometimes, implementing undo with the basic command object is inefficient, impractical, or actually impossible. One way around this difficulty is to use the technique of filters and sieves in conjunction with command objects. Neither MacApp, TCL, nor the Prograph ABCs implement filters and sieves, although the Lisa Toolkit did.[2] I have added a rudimentary version of filters and sieves to the Prograph ABCs, and this article is about that implementation and about how you can make use of this technique in your applications.

To illustrate these ideas, I have implemented two small applications that use the technique of filters and sieves: an icon editor, and a modified version of the MiniQuadWorld application.[2] The Prograph version of both these apps is on the associated MacTech Magazine diskette, along with the its full source code (in standard Prograph project and section files).

Introduction to Filters and Sieves

Filters and sieves address situations where there is just not a reasonable way to implement undo for the user. The operations in question are not those for which undo is simply impossible - how do you get the LaserWriter to suck back in the paper after you have issued a print command, for example - but rather those operations that from the point of view of the average user should be undoable, but which you know will take too long, consume too much space, or are just computationally intractable. So, if these operations are too difficult to undo in reality, then we will just fool the user into thinking that they are undoable. And the way to do that is to fool the user into thinking that we have ever done them at all: that way we never really have to undo them!

Let’s consider a very small example. Suppose we are implementing a structured graphics editor like MacDraw or Canvas. Further suppose that we want to provide the user with the ability to change the fill pattern of a shape, and that to undo this operation is extremely difficult. (Yes, I know that undoing this type of operation is actually trivial. Let’s just pretend that it is extremely difficult.) Yet, we want to provide the user with the ability to undo fill pattern changes. We can provide undo for this operation if we just fool that user that we have ever performed the fill pattern change in the first place. Since we haven’t really changed the fill pattern, then undoing it is easy-we just stop fooling the user and the status quo, which never really changed, is again revealed. We fool the user by modifying the drawing code something like this:

PROCEDURE TShapeGraphicalView.Draw(area: Rect);

 PROCEDURE FilterAndDrawShape(shape: TShape);
 VAR tempShape: TShape;
 BEGIN
 IF shape <> newlyPatternedShape
 THEN shape.Draw {this shape is NOT the special one }
 ELSE BEGIN
 tempShape := shape.Clone;
 tempShape.SetFillPattern(newFillPattern);
 tempShape.Draw;
 tempShape.Free;
 END
 END;
BEGIN
 SELF.fDocument.EachShapeDo(FilterAndDrawShape);
END;

This code enables the view to check whether each shape in the document is a special shape that should not be drawn in the normal manner, but rather in a special way - in this example with a new fill pattern. Thus the shapes in the document are filtered through a sieve that allows most shapes to pass through and thus be drawn unchanged. But this sieve catches one special shape, the one referenced by the variable newlyPatternedShape, doesn’t let this shape be drawn, but rather clones it, changes the fill pattern of the clone, draws the clone, and then throws the clone away. This is conceptually represented in Figure 1. (The small code fragment above is not at all representative of the way in which filters and sieves would actually be used, nor of how they would be added to an application framework. It is merely illustrative of the idea of filtering. More realistic code will be shown later.)

Figure 1. Conceptual model of a filter that alters the display of a single object
in the view presented to the user.

What we have done here is to present to the user the illusion that the shape’s fillPattern has changed. In reality, of course, it has not. Thus, if the user wants to undo this action, all we have to do is nil out the newlyPatternedShape reference and re-draw the view. The old pattern will automatically be drawn. If, on the other hand, the user wants to continue modifying the drawing, then we need only actually perform the pattern change just before we execute the next request from the user. With this pattern change thus ‘committed’, we need only nil out the newlyPatternedShape reference-there is no need to re-draw the view since the screen is already showing the shape drawn with the new pattern. At this point the pattern change operation cannot be undone.

It is possible to consider using filters and sieves when the data of the application is inherently structured, as is the case in graphics editor like MacDraw or Canvas, a database application, word processor, etc. It is not so easy to apply when the data of the application is unstructured, as in a bitmap editor. In an application with an unstructured store, it is not reasonable to have a filter that will distinguish one of the unstructured bits in the document and process it differently. However, a technique that can sometimes be used to get an effect similar to that of filters and sieves is the technique of transparencies. Like filters and sieves, transparencies enable you to present to the user the illusion of having performed some operation, when in reality you have not done so. As with filters and sieves, this enables you to easily undo the operation. With transparencies, you superimpose additional image data in such a way that it can add to or subtract from the actual image data. Figure 2 illustrates the conceptual model of transparencies. The technique of filters and sieves, and the technique of transparencies are similar, so I will describe an implementation that will add both techniques to the Prograph ABCs.

Figure 2. Conceptual model of a transparency that adds to (or can subtract from)
the display of unstructured data in the view presented to the user.

Filters and Sieves in an Application Framework

The code fragment shown in the last section is not representative of the way in which you would use filters and sieves in a large application. This is because the approach in that fragment would lead to a situation in which the quirks of each operation’s undo requirements would be reflected in the structure of the view’s Draw method - the depths of unmodularity! A more uniform, modular, and scalable mechanism is needed. One way to do this is to combine the notions of filters and sieves with that of command objects. Each command object would have a Filter method that would traverse the objects in the document, and enable that command object to specially process certain objects, in whatever way is necessary for the operation that the command implements. The view would call the current command’s Filter method to perform tasks like drawing or processing of mouse clicks.

Let’s sketch out how this more modular approach would work for the same operation examined above: changing a shape’s fill pattern in a structured graphics editor. Assume the existence of TShapeView, TShapeDocument, and TChangeFillPatternCommand classes that provide the standard behaviors that you would expect in an application framework like MacApp, TCL, or the ABCs. In addition to this standard behavior, command objects have a FilterAndDo method, documents have an EachShapeDo method, and views have a slightly modified Draw method as shown here (expressed in a generic textual object-oriented language that just happens to look a lot like Object Pascal):

PROCEDURE TShapeGraphicalView.Draw(area: Rect);

 PROCEDURE FilterAndDrawShape(shape: TShape);
 BEGIN
 shape.Draw;
 END;
BEGIN
 SELF.fDocument.EachShapeDo(FilterAndDrawShape);
END;

PROCEDURE TShapeDocument.EachShapeDo(
 PROCEDURE DoToShape(shape: TShape));
VARtempShape:  TShape;
 shapeList: TList;
BEGIN
 IF gLastCommand <> NIL THEN
 IF gLastCommand.fDone THEN
   IF gLastCommand.fFiltering
 THEN gLastCommand.FilterAndDo(DoToShape)
 ELSE BEGIN
 shapeList := SELF.GetShapeList;
 tempShape := shapeList.First;
 WHILE (tempShape <> NIL) DO
 BEGIN
 DoToShape(tempShape);
 tempShape := shapeList.Next;
 END;
 END;
END;

PROCEDURE TChangeFillPatternCommand.FilterAndDo(
 PROCEDURE DoToShape(shape: TShape));

VARtempShape:  TShape;
 shapeList: TList;
BEGIN
 shapeList := SELF.GetShapeList;
 tempShape := shapeList.First;
 WHILE (tempShape <> NIL) DO
 BEGIN
 IF (tempShape <> 
 SELF.fNewlyPatternedShape)
 THEN DoToShape(tempShape); 
    {this shape is NOT the special one }
 ELSE BEGIN
 tempShape := tempShape.Clone;
 tempShape.SetFillPattern(newFillPattern);
 DoToShape(tempShape); 
 tempShape.Free;
 END;
 tempShape := shapeList.Next;
 END;
END;

Or, as expressed in Prograph, as shown in Figure 3.

Figure 3. Basic use of filters and sieves with command objects

Filters and Sieves in CPX

Approach

There were two somewhat conflicting goals in my approach to adding filters, sieves, and transparencies (FST) to the Prograph ABCs: my first goal was to produce a design that was good enough to use on several Prograph projects I was working on, and hopefully, would be useful to other Prograph developers. The second goal was to modify the ABCs as little as possible so that the FST section would be easy to port to new versions of the ABCs as they were released. These goals are in conflict because to FST requires modifications throughout the command handling apparatus of a framework, thus I had to make some compromises.

I had to be able to distinguish a command that used FST from a command that didn’t. I also had to add an extra operation in the command handling algorithm: committing a FST command operation just before it was disposed. I had to have both the filtering mechanism for structured documents and the transparency mechanism for unstructured ones. Thus, the obvious steps looked like:

• adding a boolean ‘Filtering?’ attribute to Command

• adding a ‘Commit Command’ step to the Commander’s processing of commands

• adding a Filter & Do method to Command, making use of the Prograph inject mechanism to have this single method accomplish different tasks, and

• adding a new drawing step (transparencies) to the already multilevel drawing algorithm used in the ABCs.

Adding a boolean ‘Filtering?’ attribute to Command

I expected this to be the easiest step, but it turned out to be the hardest. While adding an attribute to a class in Prograph is just a single mouse click, there are a lot of side effects when that class is the ancestor to a large number of classes with aliases in many different sections, and when there are instances of that class (or its subclasses) scattered throughout your application. The Command class is the ancestor of more than 15 classes in the ABCs, and instances of many of these classes (e.g., Menu Behavior, (Click or Draw) Behavior, etc.) are created by the editors while you are designing your application with Prograph’s graphical interface builder, the ABEs. In fact, after I finally realized that adding the attribute was not going to work, just backing out of this change took over an hour.

I still needed to add something like a boolean attribute to Command. What I ended up doing was using a method as a virtual attribute. Since the value of the Filtering? attribute that I wanted to add was not going to change for the lifetime of any particular instance, and since the value would be the same for all instances of a given Command subclass, I added a method Filtering? to Command that just returns FALSE. When you subclass Command to make a filtering command, you just overshadow the Filtering? method and change its implementation to return TRUE.

Adding a ‘Commit Command’ step to the Commander

This turned out to be very easy. In the standard version of the ABCs, the Commander disposes of the last command object as the first step in processing a new one (in Commander/Do Command). While I could have just instructed Prograph programmers using my FST unit to perform any commit action in their command subclass’s Dispose method, I felt this was probably something that would be a common source of programming errors, so I added a Commit method to Command, and a call to this method in Commander/Do Command. Most filtering subclasses will override Commit to perform the operation associated with the command, and store the results in the document.

Adding a Filter and Do method to Command

This turned out to be less elegant than I hoped for, primarily because the differing arities for the different uses of Filter & Do make a single implementation of Filter & Do impossible. When the task to be done by the filtering command object is drawing, then there is no return value from the operation. However, when the task is determining the set of objects currently shown to the user, there is a return value, the list of objects. And when the task is processing a mouse click, there is an additional input value, the click point.

The inject operator in Prograph is powerful and it lets you do some really nifty stuff, but the methods that use inject have the same arity matching requirements as any other method. This arity difference meant that one single abstract Filter & Do method could not be designed. In effect, what I had to do was to implement three methods: Filter-&-Do-one-input-and-no-return-value, a Filter-&-Do-with-one-input-and-one-return-value and a Filter-&-Do-with-two-inputs-and-one-return-value. It didn’t feel real elegant, but there didn’t seem to be any way around it. (I guess I shouldn’t feel so bad, since even the designers of Smalltalk had to make exactly the same compromise that I did. The Smalltalk equivalent of inject is perform; if you look at the Smalltalk sources you will see the inelegant but necessary perform: (one arguments), perform: with:(two arguments), perform: with: with: (three arguments), and perform: withArguments: (many arguments).)

Rather than pick method names that were so generic as to be almost meaningless, I decided to name the three Filter & Do variants in a way that suggested their most common uses: Filter & Draw, Filter Virtual Document, and Filter & Process Click. All filtering commands must override all of these methods.

Adding transparencies

Adding transparencies to the ABCs turned out to be quite straightforward. After the standard View/Draw Content method executes, the Draw Virtual Content checks to see if there is a filtering command currently being stored by the Commander. If so, then that command is given the opportunity to draw its ‘transparency’ on top of the already drawn view. (See Figure 4.) It is up to the individual application and command to determine how to store this virtual data, and how to make sure that the virtual data can both add to and subtract from the view of the data shown to the user.

Figure 4. Adding transparencies to the ABCs.

Using Filters and Sieves

I implemented two small applications that use my FST section. While these applications are far from being full-bodied applications, they do show how these techniques can be used in real products.

Structured Graphics Editor

The small structured graphics editor that I built has one window that uses ordinary vanilla command objects, and one that uses command objects with filters and sieves. (See Figure 5.) There are only 4 operations that a user of this app can perform on the geometric shapes shown in the window: select any number of shapes, clear any number of shapes, rotate any number of shapes by 45°, and recolor a single shape. Of these operations, all are undoable except selection. In the standard window (with vanilla commands) undo for each of the undoable operations is provided with the standard undo mechanism, but in the filtering window, undo for all three of the commands is provided for with filters and sieves. Thus, examining the source for this application will enable you to quickly compare and contrast these two approaches to undo. I purposely designed and implemented these windows to be as independent as possible to assist you in this comparison. While it would have been possible to have either as the subclass of the other, I believed that this would have been an arbitrary code sharing decision, and not a conceptually clean design that would be the easiest to learn from.

Figure 5. The small structured graphics editor
modified to use filters and sieves for undo.

This application is a modification of the MiniQuadWorld app described in an earlier MacTech Magazine article. I tried to make the minimal modifications to this app to add the use of filters and sieves for undo, so this app is indicative of the kind of work you would have to do to add filters and sieves to an existing application.

Let’s examine what I had to do to add filters and sieves to this app. First, I had to make a new document subclass that let the current command control how a search for a particular object in the document was conducted when the user clicked in the window’s view. This meant that I had to modify the Find Quad under the Click method, but this was the only method I had to change. (I did have to add one new Document method: Get Virtual QuadList.) Since I was not using transparency in this app, my view class descends just from View and not from View with Transparency. I did have to modify this view’s Draw method to let the command object manage the drawing operation. A new window subclass was needed only because the ABCs require a new window subclass for every kind of window, not because of filters and sieves. I then added new command classes for each of the undoable operations.

To make a command subclass use filters & sieves involved the following steps (see Figure 6):

Figure 6. The method overloading necessary to use filters and sieves
in a command object.

• Make sure that the command’s Do, Undo, and Redo just set things up for the particular state desired, as opposed to actually performing the command’s basic operation (e.g., clearing)

• Overload the Filtering? method and change its return value to TRUE

• Overload the Filter & Draw and the Filter & Process Click methods to control drawing and mouse click operations when the command has been executed but not committed. Figures 7 and 8 show these methods for the Filtered Rotate Quad Cmd class.

• Add a method to return the set of objects currently being shown to the user, the virtual document data. In this application, this method is named Get Virtual Quadlist.

Figure 7. The Filtered Rotate Quad Cmd/Filter & Draw method.

Figure 8. The Filtered Rotate Quad Cmd/Filter
& Process Click method.

Icon Editor

Figure 9 shows the main window of the small icon editor I wrote that uses transparencies to implement undo for the icon editing operation. (The functionality of this small app is modeled after the icon editing app used in the MacApp 3.0 tutorial.)

Figure 9. The main window of the icon editing app
which uses transparencies to implement undo for the editing operation.

Transparencies turned out to be a very natural and easy way to implement undo in this application. Figure 10 shows the important pieces of code involved in the view’s drawing operation.

Figure 10. The transparency drawing code for the icon view.
This code retrieves the data stored in the transparency and draws that data
in a manner similar to, but not identical with, the main drawing code of the view.
The difference is that the transparency has to be able to, in effect,
erase portions of what was drawn by the main drawing code.

What Worked and What Didn’t

This all worked about like I expected it would, although with a few small surprises. Because much of the ABCs standard functionality (saving, printing, etc.) is not implemented with undoable commands, and because I was now fooling the user by presenting something that wasn’t really stored in the document, I had to take care not the fool the framework also. For example, when printing or saving, I had to make sure that any filtering command was committed first.

Certainly, if Prograph International added FST to a future release of the ABCs, they would be able to do a smoother job of integrating, making the job of the application implementor a little easier. Until then, I hope my filtering unit is of some use.

References

[1] Schmucker, Kurt. “Commands and Undo in Prograph CPX”, MacTech Magazine, January 1995.

[2] Schmucker, Kurt. Object Oriented Programming for the Macintosh, Hayden Book Company, 1986.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Capture One 11.0.1.40 - RAW workflow sof...
Capture One is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 400 high-end cameras -- straight out of the box. It offers... Read more
GraphicConverter 10.5.4 - $39.95
GraphicConverter is an all-purpose image-editing program that can import 200 different graphic-based formats, edit the image, and export it to any of 80 available file formats. The high-end editing... Read more
Dash 4.1.3 - Instant search and offline...
Dash is an API documentation browser and code snippet manager. Dash helps you store snippets of code, as well as instantly search and browse documentation for almost any API you might use (for a full... Read more
Microsoft OneNote 16.9 - Free digital no...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more
DEVONthink Pro 2.9.17 - Knowledge base,...
Save 10% with our exclusive coupon code: MACUPDATE10 DEVONthink Pro is your essential assistant for today's world, where almost everything is digital. From shopping receipts to important research... Read more
OmniGraffle 7.6 - Create diagrams, flow...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
iFinance 4.3.7 - Comprehensively manage...
iFinance allows you to keep track of your income and spending -- from your lunchbreak coffee to your new car -- in the most convenient and fastest way. Clearly arranged transaction lists of all your... Read more
Opera 50.0.2762.58 - 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
Microsoft Office 2016 16.9 - Popular pro...
Microsoft Office 2016 - Unmistakably Office, designed for Mac. The new versions of Word, Excel, PowerPoint, Outlook and OneNote provide the best of both worlds for Mac users - the familiar Office... Read more
SoftRAID 5.6.4 - High-quality RAID manag...
SoftRAID allows you to create and manage disk arrays to increase performance and reliability. SoftRAID allows the user to create and manage RAID 4 and 5 volumes, RAID 1+0, and RAID 1 (Mirror) and... Read more

Latest Forum Discussions

See All

Around the Empire: What have you missed...
Around this time every week we're going to have a look at the comings and goings on the other sites in Steel Media's pocket-gaming empire. We'll round up the very best content you might have missed, so you're always going to be up to date with the... | Read more »
The 7 best games that came out for iPhon...
Well, it's that time of the week. You know what I mean. You know exactly what I mean. It's the time of the week when we take a look at the best games that have landed on the App Store over the past seven days. And there are some real doozies here... | Read more »
Popular MMO Strategy game Lords Mobile i...
Delve into the crowded halls of the Play Store and you’ll find mobile fantasy strategy MMOs-a-plenty. One that’s kicking off the new year in style however is IGG’s Lords Mobile, which has beaten out the fierce competition to receive Google Play’s... | Read more »
Blocky Racing is a funky and fresh new k...
Blocky Racing has zoomed onto the App Store and Google Play this week, bringing with it plenty of classic kart racing shenanigans that will take you straight back to your childhood. If you’ve found yourself hooked on games like Mario Kart or Crash... | Read more »
Cytus II (Games)
Cytus II 1.0.1 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.1 (iTunes) Description: "Cytus II" is a music rhythm game created by Rayark Games. It's our fourth rhythm game title, following the footsteps of three... | Read more »
JYDGE (Games)
JYDGE 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: Build your JYDGE. Enter Edenbyrg. Get out alive. JYDGE is a lawful but awful roguehate top-down shooter where you get to build your... | Read more »
Tako Bubble guide - Tips and Tricks to S...
Tako Bubble is a pretty simple and fun puzzler, but the game can get downright devious with its puzzle design. If you insist on not paying for the game and want to manage your lives appropriately, check out these tips so you can avoid getting... | Read more »
Everything about Hero Academy 2 - The co...
It's fair to say we've spent a good deal of time on Hero Academy 2. So much so, that we think we're probably in a really good place to give you some advice about how to get the most out of the game. And in this guide, that's exactly what you're... | Read more »
Everything about Hero Academy 2: Part 3...
In the third part of our Hero Academy 2 guide we're going to take a look at the different modes you can play in the game. We'll explain what you need to do in each of them, and tell you why it's important that you do. [Read more] | Read more »
Everything about Hero Academy 2: Part 2...
In this second part of our guide to Hero Academy 2, we're going to have a look at the different card types that you're going to be using in the game. We'll split them up into different sections too, to make sure you're getting the most information... | Read more »

Price Scanner via MacPrices.net

Deals on clearance 15″ Apple MacBook Pros wit...
B&H Photo has clearance 2016 15″ MacBook Pros available for up to $800 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 15″ 2.7GHz Touch Bar MacBook Pro... Read more
Apple restocked Certified Refurbished 13″ Mac...
Apple has restocked a full line of Certified Refurbished 2017 13″ MacBook Airs starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: – 13″ 1.8GHz/8GB/128GB... Read more
How to find the lowest prices on 2017 Apple M...
Apple has Certified Refurbished 13″ and 15″ 2017 MacBook Pros available for $200 to $420 off the cost of new models. Apple’s refurbished prices are the lowest available for each model from any... Read more
The lowest prices anywhere on Apple 12″ MacBo...
Apple has Certified Refurbished 2017 12″ Retina MacBooks available for $200-$240 off the cost of new models. Apple will include a standard one-year warranty with each MacBook, and shipping is free.... Read more
Apple now offering a full line of Certified R...
Apple is now offering Certified Refurbished 2017 10″ and 12″ iPad Pros for $100-$190 off MSRP, depending on the model. An Apple one-year warranty is included with each model, and shipping is free: –... Read more
27″ iMacs on sale for $100-$130 off MSRP, pay...
B&H Photo has 27″ iMacs on sale for $100-$130 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 27″ 3.8GHz iMac (MNED2LL/A): $2199 $100 off MSRP – 27″ 3.... Read more
2.8GHz Mac mini on sale for $899, $100 off MS...
B&H Photo has the 2.8GHz Mac mini (model number MGEQ2LL/A) on sale for $899 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP. Read more
Apple offers Certified Refurbished iPad minis...
Apple has Certified Refurbished 128GB iPad minis available today for $339 including free shipping. Apple’s standard one-year warranty is included. Their price is $60 off MSRP. Read more
Amazon offers 13″ 256GB MacBook Air for $1049...
Amazon has the 13″ 1.8GHz/256B #Apple #MacBook Air on sale today for $150 off MSRP including free shipping: – 13″ 1.8GHz/256GB MacBook Air (MQD42LL/A): $1049.99, $150 off MSRP Read more
9.7-inch 2017 WiFi iPads on sale starting at...
B&H Photo has 9.7″ 2017 WiFi #Apple #iPads on sale for $30 off MSRP for a limited time. Shipping is free, and pay sales tax in NY & NJ only: – 32GB iPad WiFi: $299, $30 off – 128GB iPad WiFi... Read more

Jobs Board

*Apple* Data Center Site Selection and Strat...
# Apple Data Center Site Selection and Strategy Research Analyst Job Number: 83708609 Santa Clara Valley, California, United States Posted: 18-Jan-2018 Weekly Hours: Read more
Security Engineering Coordinator, *Apple* R...
# Security Engineering Coordinator, Apple Retail Job Number: 113237456 Santa Clara Valley, California, United States Posted: 18-Jan-2018 Weekly Hours: 40.00 **Job Read more
Firmware Engineer - *Apple* Accessories - A...
# Firmware Engineer - Apple Accessories Job Number: 113422485 Santa Clara Valley, California, United States Posted: 18-Jan-2018 Weekly Hours: 40.00 **Job Summary** Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Store Leader - Retail District Manag...
Job Description:Job SummaryAs more and more people discover Apple , they visit our retail stores seeking ways to incorporate our products into their lives. It's your Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.