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
$100.57
Apple Inc.
+0.04
MSFT
$44.95
Microsoft Corpora
-0.38
GOOG
$584.49
Google Inc.
-2.37

MacTech Search:
Community Search:

Software Updates via MacUpdate

Tidy Up 3.0.15.0 - Find duplicate files...
Tidy Up is a complete duplicate finder and disk-tidiness utility. With Tidy Up you can search for duplicate files and packages by the owner application, content, type, creator, extension, time... Read more
Parallels Desktop 10.0 - Run Windows app...
Parallels Desktop is simply the world's bestselling, top-rated, and most trusted solution for running Windows applications on your Mac. With Parallels Desktop for Mac, you can seamlessly run both... Read more
Apple Final Cut Pro X 10.1.3 - Professio...
Apple Final Cut Pro X is a professional video editing solution.Completely redesigned from the ground up, Final Cut Pro adds extraordinary speed, quality, and flexibility to every part of the post-... Read more
Apple Compressor 4.1.3 - Adds power and...
Compressor adds power and flexibility to Final Cut Pro X export. Customize output settings, work faster with distributed encoding, and tap into a comprehensive set of delivery features. Powerful... Read more
Chromium 36.0.1985.143 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. FreeSMUG-Free OpenSource Mac User Group build is... Read more
Macgo Blu-ray Player 2.10.6.1691 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple Motion 5.1.2 - Create and customiz...
Apple Motion is designed for video editors, Motion 5 lets you customize Final Cut Pro titles, transitions, and effects. Or create your own dazzling animations in 2D or 3D space, with real-time... Read more
A Better Finder Rename 9.39 - File, phot...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
PopChar X 6.6 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
MacUpdate Desktop 6.0.2 - Install Mac ap...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more

Latest Forum Discussions

See All

Cubic Castles Review
Cubic Castles Review By Rob Thomas on August 20th, 2014 Our Rating: :: CASTLE CRAFTINGiPad Only App - Designed for the iPad Some ridiculously frustrating camera issues aside, Cubic Castles is a pretty neat, voxel-based crafting... | Read more »
Space Colors – Tips, Tricks, Strategies,...
Hello Cadets: Want to know what we thought about this hectic space combat/roguelike? Check out our Space Colors review! Space Colors is a cool shooter/roguelike from Team Chaos. You travel from planet to planet across a huge galaxy and complete a... | Read more »
Tap Sports Baseball – Tips, Tricks, and...
Tap Sports Baseball is a pretty simple game to learn, but that doesn’t mean it’s an easy game to master, by any means. To start your batting career off well, we thought we’d give you the heads up on some handy tips and tricks. Hey Batter-Batter:... | Read more »
Tap Sports Baseball Review
Tap Sports Baseball Review By Jennifer Allen on August 20th, 2014 Our Rating: :: LET'S PLAY BALLUniversal App - Designed for iPhone and iPad Tap Sports Baseball is briefly fun but lacks some important features.   | Read more »
Earn to Die 2 Set to Drive in to the App...
Earn to Die 2 Set to Drive in to the App Store Later This Year Posted by Ellis Spice on August 20th, 2014 [ permalink ] Not Doppler has announced that Earn to Die 2, a sequel to their successful game | Read more »
Frontier Heroes Review
Frontier Heroes Review By Andrew Fisher on August 20th, 2014 Our Rating: :: BLAZES NO TRAILSUniversal App - Designed for iPhone and iPad Despite awesome visuals and great music, Frontier Heroes just doesn’t quite deliver enough fun... | Read more »
Echo Prime is Now on Sale for $0.99
Echo Prime is Now on Sale for $0.99 Posted by Jessica Fisher on August 20th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Star Realms Review
Star Realms Review By Andrew Fisher on August 20th, 2014 Our Rating: :: A STAR IS BORNUniversal App - Designed for iPhone and iPad Star Realms is an excellent adaptation of an outstanding deck-builder. With great visuals and an... | Read more »
This. Is. SPRINGFIELD! War comes to The...
This. Is. SPRINGFIELD! | Read more »
One Tap RPG Review
One Tap RPG Review By Campbell Bird on August 20th, 2014 Our Rating: :: DUNGEON SLIDERUniversal App - Designed for iPhone and iPad This casual arcade game introduces some very light rpg elements into its fantasy-themed pachinko... | Read more »

Price Scanner via MacPrices.net

Apple now offering certified refurbished 2014...
 The Apple Store is now offering Apple Certified Refurbished 2014 MacBook Airs for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Best Buy’s College Student Deals: $100 off Ma...
Take an additional $100 off all MacBooks and iMacs, $50 off iPad Airs and iPad minis, at Best Buy Online with their College Students Deals Savings, valid through September 6th. Anyone with a valid .... Read more
MacBook Airs on sale for $100 off MSRP, free...
B&H Photo has three 2014 MacBook Airs on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels Desktop and LoJack for Laptops... Read more
Razer Taipan Mouse For Gamers And Non-Gamers...
If you’re a serious gamer on either Mac or Windows PCs, a serious gaming mouse is a necessity for first-tier performance. However, even if like me you’re not much of a gamer, there’s still a strong... Read more
15-inch 2.2GHz MacBook Pro on sale for $1899,...
Adorama has the new 15″ 2.2GHz Retina MacBook Pro on sale for $1899 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP, and it’s the lowest price available for this... Read more
Mid-Size Tablet Shootout Posted: iPad mini wi...
I ‘m curious about how many iPads Apple is actually selling these days. It’s been widely rumored and anticipated that new models with A8 SoCs, 2 GB of RAM, 8 megapixel cameras, and fingerprint... Read more
The 15 Biggest iPad Air Problems And How To A...
What’s this? Fifteen “biggest” problems with the iPad Air? Does that mean there are a lot of smaller problems as well? Say it isn’t so! My old iPad 2 has manifested no hardware problems in three... Read more
TYLT Syncable-Duo, 2-in-1 USB Cable With Appl...
TYLT has introduced the Syncable-Duo, a universal cable solution for charging and syncing data to smartphones and tablets. The Syncable-Duo eliminates the need for multiple cables by incorporating... Read more
Save up to $140 off MSRP with Apple refurbish...
Apple is offering Certified Refurbished iPad Airs for up to $140 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free. Stock tends to come and go with some of these... Read more
2.5GHz Mac mini on sale for $549, save $50
B&H Photo has the 2.5GHz Mac mini on sale for $549.99 including free shipping. That’s $50 off MSRP, and B&H will also include a free copy of Parallels Desktop software. NY sales tax only. 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
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant - Apple (United...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** Being a Business Manager at an Apple Store means you're the catalyst for businesses to discover and leverage the power, ease, and flexibility of Apple Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** At the Apple Store, you connect business professionals and entrepreneurs with the tools they need in order to put Apple solutions to work in their Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.