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.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Adobe Creative Cloud 2.2.0.129 - Access...
Adobe Creative Cloud costs $49.99/month (or less if you're a previous Creative Suite customer). Creative Suite 6 is still available for purchase (without a monthly plan) if you prefer. Introducing... Read more
Tower 2.2.3 - Version control with Git m...
Tower is a powerful Git client for OS X that makes using Git easy and more efficient. Users benefit from its elegant and comprehensive interface and a feature set that lets them enjoy the full power... Read more
Apple Java 2015-001 - For OS X 10.7, 10....
Apple Java for OS X 2015-001 installs the legacy Java 6 runtime for OS X 10.11 El Capitan, OS X 10.10 Yosemite, OS X 10.9 Mavericks, OS X 10.8 Mountain Lion, and OS X 10.7 Lion. This package is... Read more
Adobe Muse CC 2015 2015.0.1 - Design and...
Muse CC 2015 is available as part of Adobe Creative Cloud for as little as $14.99/month (or $9.99/month if you're a previous Muse customer). Muse CS6 is still available for purchase (without a... Read more
Adobe Illustrator CC 2015 19.1.0 - Profe...
Illustrator CC 2015 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Illustrator customer). Illustrator CS6 is still available for... Read more
Corel Painter 14.1.0.1105 - Digital art...
Corel Painter helps you create astonishing art in a variety of media. Paint with vivid oil paints, fluid water colors, and earthy charcoals. Corel Painter flawlessly recreates the tones and textures... Read more
Pacifist 3.5.4 - Install individual file...
Pacifist opens up .pkg installer packages, .dmg disk images, .zip, .tar. tar.gz, .tar.bz2, .pax, and .xar archives and more, and lets you extract or install individual files out of them. This is... Read more
Merlin Project 3.1.0.40305 - Project man...
Merlin Project is for those of you who are responsible for complex projects. Simple lists of tasks won't suffice. Good planning raises questions about the dependencies of activities on each other,... Read more
DM1 2.0 - Advanced drum machine. (Commer...
DM1 is an advanced Drum Machine. It turns your computer into a fun and creative beat making machine. Easy and fast to use, loaded with 86 superb electronic drum kits and beautiful hyper-realistic... Read more
Posterino 3.2.1 - Create posters, collag...
Posterino offers enhanced customization and flexibility including a variety of new, stylish templates featuring grids of identical or odd-sized image boxes. You can customize the size and shape of... Read more

Mazes of Karradash (Games)
Mazes of Karradash 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: The city of Karradash is under attack: the monsters of the Shadow Realms are emerging from the depths.No adventurer is... | Read more »
Battle Golf is the Newest Game from the...
Wrassling was a pretty weird - and equally great - little wressling game. Now the developers, Folmer Kelly and Colin Lane, have turned their attention to a different sport: golfing. This is gonna be weird. [Read more] | Read more »
Qbert Rebooted has the App Store Going...
The weird little orange... whatever... is back, mostly thanks to that movie which shall remain nameless (you know the one). But anyway it's been "rebooted" and now you can play the fancy-looking Qbert Rebooted on iOS devices. [Read more] | Read more »
Giant Monsters Run Amok in The Sandbox...
So The Sandbox has just hit version number 1.99987 (seriously), and it's added a lot more stuff. Just like every other update, really. [Read more] | Read more »
Fish Pond Park (Games)
Fish Pond Park 1.0.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.0 (iTunes) Description: Nurture an idyllic slice of tourist's heaven into the top nature spot of the nation, furnishing it with a variety of... | Read more »
Look after Baby Buddy on your Apple Watc...
Parigami Gold is the new premium version of the match three puzzler that includes Apple Watch support and all new content. You won't simply be sliding tiles around on your wrist, the Apple Watch companion app is an all new mini-game in itself. You'... | Read more »
Swallow all of your opponents as the big...
Eat all of the opposition and become the largest ball in Battle of Balls now available in the App Store and Google Play. Battle of Balls pits you against other opponents in real time and challenges you to eat more balls and grow larger than all of... | Read more »
PAC-MAN Championship Edition DX (Games)
PAC-MAN Championship Edition DX 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: It’s Your World. EAT IT! Get ready for more ghost chain gobbling and frantic action in PAC-MAN® CE-DX! The... | Read more »
incurve (Games)
incurve 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Get ready for 2 different gravities Goal is to hit as many white dots on your way up.When you're touching the screen, the dots have a... | Read more »
Crossy Road has its Own Merch Store Now....
Do you like Crossy Road? I mean do you really like Crossy Road? Well then you're in luck! Hipster Whale has opened up a Crossy Road store, so you can show off your fandom via official T-shirts. [Read more] | Read more »

Price Scanner via MacPrices.net

Apple restocks refurbished Mac minis for up t...
The Apple Store has restocked Apple Certified Refurbished 2014 Mac minis, with models available starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: - 1.4GHz... Read more
13-inch 2.5GHz MacBook Pro on sale for $899,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $899.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $200 off MSRP. Price is... Read more
21-inch 2.9GHz iMac on sale for $1299, save $...
Best Buy has the 21″ 2.9GHz iMac on sale today for $1299.99 on their online store. Choose free shipping or free local store pickup (if available). Their price is $200 off MSRP, and it’s the lowest... Read more
Free Image Sizer 1.3 for iOS Offers Photo Edi...
Xi’An, China based G-Power has announced the release of Image Sizer 1.3 for the iPhone, iPad, and iPod touch, an important update to their free photo editing app. Image Sizer’s collection of easy to... Read more
Sale! 13″ 1.6GHz/128GB MacBook Air for $899,...
B&H Photo has the 13″ 1.6GHz/128GB MacBook Air on sale for $899 including free shipping plus NY tax only. Their price is $100 off MSRP, and it’s the lowest price available for this model. Read more
13-inch Retina MacBook Pros on sale for $100...
Best Buy has 13-inch Retina MacBook Pros on sale for $100 off MSRP on their online store. Choose free shipping or free local store pickup (if available). Prices are for online orders only, in-store... Read more
Will BMW’s i3 Electric Vehicle Be The Automo...
The German-language business journal Manager Magazin’s Michael Freitag reports that Apple and the German performance/luxury automaker Bayerishe Motoren Werke (BMW) are back at far-reaching... Read more
Sale! $250 off 15-inch Retina MacBook Pro, $2...
B&H Photo has lowered their price for the 15″ 2.2GHz Retina MacBook Pro to $1749, or $250 off MSRP. Shipping is free, and B&H charges NY sales tax only. They have the 27″ 3.3GHz 5K iMac on... Read more
Global Smartphone Market Posts 11.6% Year-Ove...
According to the latest preliminary data released from the International Data Corporation (IDC) Worldwide Quarterly Mobile Phone Tracker, smartphone vendors shipped a total of 337.2 million units... Read more
15-inch and 13-inch Retina MacBook Pros on sa...
B&H Photo has 15″ & 13″ Retina MacBook Pros on sale for up to $180 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1819 save $180 - 15″ 2.... 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
*Apple* Professional Services: Business Anal...
**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
Software Engineer, *Apple* Watch - Apple (U...
…the team that is revolutionizing the watch! As a software engineer on the Apple Watch team, you will be responsible for building world-class applications and frameworks Read more
*Apple* and Windows Desktop Support Engineer...
…protected veterans status or any other characteristic protected by law. * ACSP ( Apple Certified Support Professional) / ACMT ( Apple Certified Mac Technician) Read more
*Apple* Certified Desktop Support Technician...
* Apple Certified Desktop Support Technician-San Diego, CA-Ongoing Contract Position* At*CompuCom*, you're more than just a number. Our employee relationship managers Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.