TweetFollow Us on Twitter

File Finder DA for HFS
Volume Number:2
Issue Number:3
Column Tag:C Workshop

Lost File Finder DA for HFS

By Mike Schuster, Software Engineer, MacTutor Contributing Editor

Apple designed the new Macintosh Plus Standard File dialog package to provide an easy method for finding files nested within the hierarchical file structure of a mounted volume. At any given time, the dialog displays a sorted list of the files and folders contained within a "current folder". If the desired file is not in that folder, and therefore not in the displayed list, you must first find the folder that contains the desired file.

If you believe that the desired file is in one of the "sibling folders" listed, you select that sibling and click on the Open button. The selected folder becomes the new current folder, and its contents are displayed. You may have to repeat this process if the file is nested several levels deep.

On the other hand, if the file is not contained within any sibling of the current folder, then you choose some "ancestor" as a new current folder from the pop-up menu at the top of the dialog. You then either select the desired file from the list, or repeatedly open sibling and/or ancestor folders until you find the file.

Fig. 1 Moving up to an ancestor

This manual traversal of the hierarchy can be quite tedious if you have forgotten your file's location within the hierarchy. It reminds me of an Easter Egg hunt. When working with files in several folders, I find it quite easy to accidently save a file without noticing which folder the file was placed, especially after quickly typing its name and and an Enter key. Obviously, some sort of global search function would be handy. This is the subject of this month's article.

A User Interface for Global Search

Here's what I have in mind. As soon as an application displays an Open file dialog, I'd like to type the first few letters of my file's name. If the file is contained in the current folder, an Enter key is sufficently to complete the dialog. Otherwise, I'd like to enter some command key combination to force standard file to search the hierarchy for a file whose name matches the letters I've already typed.

As soon as standard file finds a candidate, it should automatically make the folder containing the candidate the current folder as well as select the candidate. At this point, I can either accept the candidate by typing an Enter key, or resume the search by entering the command key combination once again. I'd like to be able to repeat this process until either standard file has exhaustively searched the whole file hierarchy, or until I find the desired file.

In retrospect, such a scheme might take a while on a large file system, so some way of aborting the search should be available. That way, if I get tired waiting, I can always go back and hunt for eggs myself.

A Design using a Dialog Hook

When I first considered an implementation for such a global search, it seemed that rather large changes and additions to the standard file package might be required. This turned out not to be the case. I was able to implement all of the above user interface without any modifications to the standard file package using a C language routine passed as the filterProc argument to the SFPGetFile trap!

My filterProc routine implements the search by generating a series of "synthetic" events that cause standard file to sequentially traverse the file hierarchy until a candidate match is found. You can think of it as a simple state machine that methodically selects each file and folder from the list one at a time in turn. Periodically, the machine uses the Open button and pop-up menu at appropriate times to select new folders in which to continue the search. As soon as a candidate file is found, filterProc stops creating synthetic events and begins sending null events through unchanged, allowing the user to accept the selection, continue the search, or use any of the dialog's buttons and controls in the standard way.

Rather than generating a sequence of mouse down events in the appropriate dialog controls, the filterProc generates the following set of cursor key events, which are understood by the new standard file package:

Fig. 2 Command key selections

Typing a Tab key is equivalent to clicking on the Drive button. Typing an down or up arrow key is equivalent to scrolling the list of files and folders and selecting the next item below, or previous item above in the list, respectively. If the currently selected item is a folder, then the Command Ø combination is equivalent to clicking on the Open button. Similarly, the Command combination is equivalent to selecting the current folder's immediate parent from the pull-down menu.

In addition to implementing cursor keys, the new standard file package dynamically updates the fields of its standard file reply record argument while the dialog is open:

Fig. 3 Standard File Reply Record Argument

If the currently selected item in the dialog is a file, then the reply's file type and file name length fields are both postive. In this case, the file type field contains the four letter finder document type of the file. If the selected item is a folder, only the file type field is positive. It contains the folder's file catalog directory identifier. Finally, if neither a file nor a folder is selected, both the file type and name length fields are zero.

This information, combined with the directory identifier of the current folder (which is contained in a low memory global location), provide filterProc with just enough information to allow it to traverse the hierarchy via a sequence of cursor keys. It accomplishes the global search without a single I/O call to the file system! One nice side effect of this scheme is the animation of the dialog as folders are opened and closed and as the item's themselves are selected and scrolled.

The "Lost File Finder" Desk Accessory

The Lost File Finder desk accessory contains an implementation of my global search scheme. The accessory presents a standard file dialog listing all of the files in the current folder. After typing a few letters, you enter the Command-space combination to begin (or continue) a search. The Command-period combination aborts a search. Since standard file displays the last current folder when opening a new dialog, you can use the accessory first to find the folder containing your file, and then use your application's open command to open it.

The filterProc routine in the accessory is composed of three parts. The first parts saves key events in a key buffer. This buffer is used to find potential candidate files. The second part handles the Command-space and Command-period commands. The third part is a simple state machine that generates the approprate cursor key events needed to traverse the file catalog.

The state machine is built from three states, named NULLSTATE, NEXTSTATE, and SEARCHSTATE. In NULLSTATE, null events are sent through unchanged. In NEXTSTATE, successive items in the current folder are selected. If the selected item is a file, its name is compared with the contents of the key buffer. If the selected item is a folder, then it is made the new current folder. When the end of the item list is encountered, the current folder is closed and its parent is opened. At this time, the state machine moves to SEARCHSTATE, in which successive items in the parent's folder are skipped until the just closed folder is encountered. Then the state machine returns to NEXTSTATE, and once again begins considering successive items. Of course, the root folder is handled as a special case.

In NEXTSTATE, when a file item is selected, filterProc compares its name with the contents of the key buffer. If they match, the state machine moves to NULLSTATE, and filterProc waits for further input from the user. Otherwise, the state machine stays in NEXTSTATE and continues the search.

The filterProc routine maintains two extra reply records named lastreply and startreply. Lastreply contains information on the previous item selected. It is used to detect the end of the item list, and is required since a Ø at when the last item in the list is selected has no effect. Startreply contains information on the item that was selected when the global search was begun. It is used to terminate the search when the file hierarchy has been completely traversed.

The accessory accesses three global low memory values. ISHFS is nonzero if the Hierarchical File System is installed on the Macintosh.

SFFolder contains the directory identifier of the current folder. KEYTHRESH contains the current keyboard repeat threshold value, which is used to flush the contents of the key buffer at approprate intervals.

In addition to these values, the accessory modifies the location

*(int *) (thedialog->refcon - 624)

where thedialog is a pointer to standard file's dialog window. The event record's modifiers field is returned by filterProc in this location. The dialog's refcon contains a copy of standard file's frame pointer register A6. I discovered the proper offset value by disassembling the routine that standard file passes to ModalDialog as its own filterProc.

The accessory will only work with the standard file and system folder distributed with the Macintosh Plus. The system folder distributed with the original HD-20 apparently does not contain the recent extensions to standard file. You can use the Mac Plus system folder on an original 512K Mac, but not on a 128K Mac. I'm using the system folder contained on the "Macintosh Plus Programmer's Package, Mac Disk 1, Jan 1986" distributed by Apple at the January Developers Conference. It works find in the startup drawer of my General Computer HyperDrive internal hard disk.

The following is the C language implementation of the Lost File Finder accessory. Since it contains no assembly language routines, it should be easy to port to your personal development system. One thing to watch out for, however, is that the accessory's open routine closes the accessory with a call on CloseDeskAcc. Make sure that your development system's desk accessory interface glue can handle this gracefully.


char keys[MAXKEYS + 1]; /* key buffer (pascal string) */

extern boolean getreplyeventfilter();

/* desk accessory open routine */
int accopen(dctl, pb)
 dctlentry *dctl;
 ptr pb;
 {
 point where;
 
 /* initialize state machine */
 state = NULLSTATE;
 *keys = 0;
 keywhen = 0l;
 
 /* display dialog */
 where.a.h = 82; 
 where.a.v = 50;
 sfpgetfile(&where, "", 0l, -1, 0l, 0l, &reply, -4000, &getreplyeventfilter);
 
 /* close ourselves */
 closedeskacc(dctl->dctlrefnum);
 
 return 0;
/*
 * lost file finder, version 1.0
 * find lost files with standard file
 *
 * copyright (c) 1986 by mike schuster for MacTutor.
 * all rights reserved.
 */

/* macintosh headers */
#include <acc.h> /* This file published last month  */
#include <dialog.h>
#include <device.h>
#include <event.h>
#include <qdvars.h>

/* c headers */
#include <string.h>
#include <ctype.h>

/* desk accessory header */
ACC
 (
 0x0400,/* accctl */
 0,/* no seconds */
 0,/* no events */
 0,/* no menu */
 16,    /* length */
 "Lost File Finder " /* title */
 )

/* standard file key event offset */
#define KEYOFFSET 4096

/* states for filtering machinery */
#define NULLSTATE 0
#define NEXTSTATE 1
#define SEARCHSTATE 2

/* standard cursor control keys */
#define NEXTKEY 0x1f
#define PREVKEY 0x1e
#define DOWNKEY -0x1f
#define UPKEY -0x1e
#define HOMEKEY 0x1d
#define SEARCHKEY ' '
#define QUITKEY '.'


#define isfile(reply) (reply)->fname[0]
 /* nonzero if file selected */
#define isfolder(reply) !(reply)->fname[0]   
 /* nonzero if folder selected */
#define isnull(reply) (!(reply)->ftype && !(reply)->fname[0]) 
 /* nonzero if nothing selected */
#define ISHFS (*(int *) 0x3f6 > 0) 
 /* nonzero if hfs installed */
#define SFFOLDER *(long *) 0x398   
 /* current standard file folder */
#define KEYTHRESH *(int *) 0x18e   
 /* keyboard repeat threshold */
#define ROOTFOLDER 2 
 /* directory id of root folder */

/* standard file reply typedef, with ftype defined as long */
typedef struct
 {
 boolean good;
 boolean copy;
 long ftype;
 int vrefnum;
 int version;
 char fname[64];
 } sfreply;

sfreply reply;   /* current standard file reply */
sfreply lastreply; /* last standard file reply */
sfreply startreply;/* starting standard file reply */
long startfolder;/* starting folder */
long searchfolder; /* folder to search for */
int state;/* current state of filtering machinery */

#define MAXKEYS 24 /* maximum length of key buffer */
long keywhen;    /* time of last keydown */
char keys[MAXKEYS + 1]; /* key buffer (pascal string) */


extern boolean getreplyeventfilter();

/* desk accessory open routine */
int accopen(dctl, pb)
 dctlentry *dctl;
 ptr pb;
 {
 point where;
 
 /* initialize state machine */
 state = NULLSTATE;
 *keys = 0;
 keywhen = 0l;
 
 /* display dialog */
 where.a.h = 82; 
 where.a.v = 50;
 sfpgetfile(&where, "", 0l, -1, 0l, 0l, &reply, -4000, &getreplyeventfilter);
 
 /* close ourselves */
 closedeskacc(dctl->dctlrefnum);
 
 return 0;
 }

/* null desk accessory close routine */
int accclose(dctl, pb)
 dctlentry *dctl;
 ptr pb;
 {
 return 0;
 }

/* null desk accessory control routine */
int accctl(dctl, pb)
 dctlentry *dctl;
 ptr pb;
 {
 return 0;
 }

/* null desk accessory prime routine */
int accprime(dctl, pb)
 dctlentry *dctl;
 ptr pb;
 {
 return 0;
 }

/* null desk accessory status routine */
int accstatus(dctl, pb)
 dctlentry *dctl;
 ptr pb;
 {
 return 0;
 }


/* case insensitive pascal string compare, return zero */
/* if equal. If prefix is nonzero, then a and b are equal  */
/* if a is a prefix of b */
int pstrcmp(a, b, prefix)
 register char *a;
 register char *b;
 int prefix;
 {
 register int n;
 
 if (prefix ? *a > *b : *a != *b)
 return 1;
 for (n = *a++ & 0xff, b++; n && tolower(*a++) == tolower(*b++); n--)
 ;
 return n;
 }

/* copy a standard file reply */
sfreply *replycpy(a, b)
 sfreply *a;
 sfreply *b;
 {
 blockmove(b, a, (long) sizeof(sfreply));
 return a;
 }

/* compare two standard file replies, return zero if equal */
int replycmp(a, b)
 sfreply *a;
 sfreply *b;
 {
 return (a->ftype == b->ftype) ? pstrcmp(a->fname, b->fname, 0) : 1;
 }

/* indicate a failure by sounding off */
int fail(thestate)
 int thestate;
 {
 sysbeep(4);
 return thestate;
 }

/* return a key to standard file, and update the current state */
int theitemhit(thedialog, itemhit, thekey, thestate)
 windowrecord *thedialog;
 int *itemhit;
 int thekey;
 int thestate;
 {
/* save synthetic event modifiers word in proper place */
*(int *) (thedialog->refcon - 624) = thekey < 0 ? cmdkey : 0;

/* return the desired key with the appropriate itemhit offset */
*itemhit = KEYOFFSET + (thekey < 0 ? -thekey : thekey);

 /* update the current state and return */
 state = thestate;
 return -1;
 }

/* get standard file reply event filter */
pascal boolean getreplyeventfilter(thedialog, theevent, itemhit)
 windowrecord *thedialog;
 eventrecord *theevent;
 int *itemhit;
 {
 int c;
 
 switch (theevent->what)
 {
 case nullevent:
 switch (state)
 {
 /* look for the last opened folder in parent's item list */
 /* change to NEXTSTATE when found */
 case SEARCHSTATE:
 if (isfolder(&reply) && reply.ftype == searchfolder)
 {
 replycpy(&lastreply, &reply);
 state = NEXTSTATE;
 }
 return theitemhit(thedialog, itemhit, NEXTKEY, state);
 break;
 
 /* search for candidate files */
 case NEXTSTATE:
 /* handle end of file/folder list */
 if (isnull(&reply) || !replycmp(&reply, &lastreply))
 {
 /* return to first item in list in a flat file catalog */
 if (!ISHFS)
 return theitemhit(thedialog, itemhit, HOMEKEY, fail(NULLSTATE));

 /* return to first item in list if in root of a hierarchical file catalog 
*/
 else if (SFFOLDER == ROOTFOLDER)
 {
 if (SFFOLDER == startfolder && !replycmp(&reply, &startreply))
 state = fail(NULLSTATE);
 replycpy(&lastreply, &reply);
 return theitemhit(thedialog, itemhit, HOMEKEY, state);
 }

 /* otherwise return to parent folder and search for current folder */
 else
 {
 searchfolder = SFFOLDER;
 return theitemhit(thedialog, itemhit, UPKEY, SEARCHSTATE);
 }
 }
 
 /* handle a selected file */
 else if (isfile(&reply))
 {
 /* check to see if complete catalog was searched */
 if (SFFOLDER == startfolder && !replycmp(&reply, &startreply))
 state = fail(NULLSTATE);

 /* check to see if a candiate was found */
 else if (!pstrcmp(keys, reply.fname, 1))
 state = NULLSTATE;

 /* otherwise, continue the search */
 else
 {
 replycpy(&lastreply, &reply);
 if (isnull(&startreply))
 replycpy(&startreply, &reply);
 return theitemhit(thedialog, itemhit, NEXTKEY, state);
 }
 }
 
 /* handle a selected folder */
 else
 {
 /* check to see if complete catalog was searched */
 if (reply.ftype == startfolder && !replycmp(&reply, &startreply))
 state = fail(NULLSTATE);

 /* otherwise, open the sibling folder and continue the search */
 else
 {
 if (isnull(&startreply))
 replycpy(&startreply, &reply);
 return theitemhit(thedialog, itemhit, DOWNKEY, state);
 }
 }
 break;
 default:
 break;
 }
 break;
 
     case keydown:
 /* handle keydown event */
 c = theevent->message & 0xff;
 
 /* if not command key, place in key buffer */
 if (!(theevent->modifiers & cmdkey))
 {
 /* reset buffer if threshold has expired */
 if (theevent->when > keywhen + KEYTHRESH)
 *keys = 0;
 
 /* add to key buffer */
 if ((*keys & 0xff) < MAXKEYS)
 {
 keys[(*keys & 0xff) + 1] = c;
 (*keys)++;
 }
 
 /* update time */
 keywhen = theevent->when;
 }
 
 /* handle the initiation of a search */
 else if (c == SEARCHKEY)
 {
 /* initialize last reply, starting folder and reply */
 replycpy(&lastreply, &reply);
 startfolder = SFFOLDER;
 replycpy(&startreply, &reply);
 
 /* force nextkey and initialize state machine */
 return theitemhit(thedialog, itemhit, NEXTKEY, NEXTSTATE);
 }
 
 /* handle the termination of a search */
 else if (c == QUITKEY)
 return theitemhit(thedialog, itemhit, NEXTKEY, fail(NULLSTATE));
 break;
 }
 return 0;
 }

A New Standard File

Since my global search requires the addition of a dialog hook, it can't be easily retrofitted into existing applications like MacWrite and MacPaint. The obvious solution is to append the filterProc code to the end of the standard file package resource and insert in the interface the appropriate interface calls to it. Of course, if a search capability were build directly into standard file, it might be nice, for speed, to avoid the dialog animation as the search proceeds.

 
AAPL
$98.77
Apple Inc.
-0.26
MSFT
$44.00
Microsoft Corpora
+0.03
GOOG
$589.11
Google Inc.
-1.49

MacTech Search:
Community Search:

Software Updates via MacUpdate

OS X Yosemite Wallpaper 1.0 - Desktop im...
OS X Yosemite Wallpaper is the gorgeous new background image for Apple's upcoming OS X 10.10 Yosemite. This wallpaper is available for all screen resolutions with a source file that measures 5,418... Read more
Acorn 4.4 - Bitmap image editor. (Demo)
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Bartender 1.2.20 - Organize your menu ba...
Bartender lets you organize your menu bar apps. Features: Lets you tidy your menu bar apps how you want. See your menu bar apps when you want. Hide the apps you need to run, but do not need to... Read more
TotalFinder 1.6.2 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Tab-based... Read more
Vienna 3.0.0 RC 2 :be5265e: - RSS and At...
Vienna is a freeware and Open-Source RSS/Atom newsreader with article storage and management via a SQLite database, written in Objective-C and Cocoa, for the OS X operating system. It provides... Read more
VLC Media Player 2.1.5 - Popular multime...
VLC Media Player is a highly portable multimedia player for various audio and video formats (MPEG-1, MPEG-2, MPEG-4, DivX, MP3, OGG, ...) as well as DVDs, VCDs, and various streaming protocols. It... Read more
Default Folder X 4.6.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... Read more
TinkerTool 5.3 - Expanded preference set...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Audio Hijack Pro 2.11.0 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Intermission 1.1.1 - Pause and rewind li...
Intermission allows you to pause and rewind live audio from any application on your Mac. Intermission will buffer up to 3 hours of audio, allowing users to skip through any assortment of audio... Read more

Latest Forum Discussions

See All

To The End Review
To The End Review By Lee Hamlet on July 29th, 2014 Our Rating: :: A VICIOUS CYCLEUniversal App - Designed for iPhone and iPad To The End will test players’ patience, timing, and dedication as they try to navigate all 13 levels in... | Read more »
Traps n’ Gemstones Review
Traps n’ Gemstones Review By Campbell Bird on July 28th, 2014 Our Rating: :: CASTLEVANIA JONESUniversal App - Designed for iPhone and iPad Fight mummies, dig tunnels, and ride a runaway minecart to discover ancient secrets in this... | Read more »
The Phantom PI Mission Apparition Review
The Phantom PI Mission Apparition Review By Jordan Minor on July 28th, 2014 Our Rating: :: GHOSTS BUSTEDUniversal App - Designed for iPhone and iPad The Phantom PI is an exceedingly clever and well-crafted adventure game.   | Read more »
More Stubies Are Coming Your Way in a Ne...
More Stubies Are Coming Your Way in a New Update Posted by Jessica Fisher on July 28th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
The Great Prank War Review
The Great Prank War Review By Nadia Oxford on July 28th, 2014 Our Rating: :: PRANKING IS SERIOUS BUSINESSUniversal App - Designed for iPhone and iPad Though short, The Great Prank War offers an interesting and fun mix of action and... | Read more »
Marvel Contest of Champions Announced at...
Marvel Contest of Champions Announced at Comic-Con Posted by Jennifer Allen on July 28th, 2014 [ permalink ] Announced over the weekend at San Diego Comic-Con was the fairly exciting looking Marvel Contest of Champions. | Read more »
Teenage Mutant Ninja Turtles Review
Teenage Mutant Ninja Turtles Review By Jennifer Allen on July 28th, 2014 Our Rating: :: DULL SWIPINGUniversal App - Designed for iPhone and iPad The pizza power is weak when it comes to this Teenage Mutant Ninja Turtles game.   | Read more »
Exploration Focused Puzzle Game Beatbudd...
Exploration Focused Puzzle Game Beatbuddy Set to Make Transition from PC to iOS this September Posted by Jennifer Allen on July 28th, 2014 [ permalink ] | Read more »
PlanetHD
PlanetHD By Nadia Oxford on July 28th, 2014 Our Rating: :: SPACE MADNESSUniversal App - Designed for iPhone and iPad PlanetHD will keep players busy for a while, though its unpredictable physics are a handful to deal with.   | Read more »
This Week at 148Apps: July 21-25, 2014
Another Week of Expert App Reviews   At 148Apps, we help you sort through the great ocean of apps to find the ones we think you’ll like and the ones you’ll need. Our top picks become Editor’s Choice, our stamp of approval for apps with that little... | Read more »

Price Scanner via MacPrices.net

Apple updates MacBook Pros with slightly fast...
Apple updated 13″ and 15″ Retina MacBook Pros today with slightly faster Haswell processors. 13″ models now ship with 8GB of RAM standard, while 15″ MacBook Pros ship with 16GB across the board. Most... Read more
Apple drops price on 13″ 2.5GHz MacBook Pro b...
The Apple Store has dropped their price for the 13″ 2.5GHz MacBook Pro by $100 to $1099 including free shipping. Read more
Apple drops prices on refurbished 2013 MacBoo...
The Apple Store has dropped prices on Apple Certified Refurbished 13″ and 15″ 2013 MacBook Pros, with model now available starting at $929. Apple’s one-year warranty is standard, and shipping is free... Read more
iOS 8 and OS X 10.10 To Support DuckDuckGo As...
Writing for Quartz, Dan Frommer reports that Apple’s forthcoming iOS 8 and OS X 10.10 operating systems version updates will allow users to select DuckDuckGo as their default search engine. He notes... Read more
U.K. Hospital Using iPods and iPads To Record...
British news journal GazetteLive’s. Ian McNeal notes that the old “an apple a day keeps the doctor away” proverb is being turned on its head at http://southtees.nhs.uk/hospitals/james-cook/ James... Read more
13-inch 2.5GHz MacBook Pro on sale for $1099,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $1099.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $100 off MSRP. Price is... Read more
Roundup of Apple refurbished MacBook Pros, th...
The Apple Store has Apple Certified Refurbished 13″ and 15″ MacBook Pros available for up to $400 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. Their prices... Read more
Record Mac Shipments In Q2/14 Confound Analys...
A Seeking Alpha Trefis commentary notes that Apple’s fiscal Q3 2014 results released July 22, beat market predictions on earnings, although revenues were slightly lower than anticipated. Apple’s Mac’... Read more
Intel To Launch Core M Silicon For Use In Not...
Digitimes’ Monica Chen and Joseph Tsai, report that Intel will launch 14nm-based Core M series processors specifically for use in fanless notebook/tablet 2-in-1 models in Q4 2014, with many models to... Read more
Apple’s 2014 Back to School promotion: $100 g...
 Apple’s 2014 Back to School promotion includes a free $100 App Store Gift Card with the purchase of any new Mac (Mac mini excluded), or a $50 Gift Card with the purchase of an iPad or iPhone,... Read more

Jobs Board

Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**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
Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**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
*Apple* Solutions Consultant (ASC) - Apple (...
**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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.