TweetFollow Us on Twitter

Contextual Menu Basics

Volume Number: 14 (1998)
Issue Number: 1
Column Tag: Toolbox Techniques

Contextual Menu Basics

by Steve Sheets

Adding Support for the new Mac OS 8 Contextual Menu API; even for applications running without Apple's Contextual Menu Manager

A New OS, A New API, A New Logical Interface

The original thought that went into the Macintosh design was "ease of use". This would be the computer for "the rest of us" who were computer novices or worse. After awhile, novices become power users who want quicker ways to do things they already know how to do. You can see this in the menus of the Macintosh. The user has menu items that he can select with the mouse, but he also has Command-key short cuts. You also can see it in drawing palettes that allow simple commands, yet can be double clicked for more in depth configuration.

The contextual menu feature introduced with Mac OS 8 continues this idea. While holding down the Control key, a user can click the mouse while above some information he wants to manipulate. The information could be selected text, a picture, some database tables or anything we traditionally think of as exportable data. A popup menu appears to provide him with all the commands related to this data. Even if no data is selected, the user can see commands related to the window, document or application.

Contextual menus provide no new commands that are not already available. Rather it groups the commands in a quick table to be read and selected. Instead of selecting the data and then selecting the command, with one quick click, the user can see what can be accomplish. Searching for familiar commands becomes much easier. (For example, "is Find under Edit, View or Text?") Even if the user knows exactly where the command is in the normal menu, a Control-click on the selected data is faster. Power users will quickly adopt Contextual Menus to get their work done.

Whenever Apple introduced a new "logical interface", they provide a new API to developers so that we can provide this new feature to users. Luckily, the API for contextual menu is very small and involves concepts similar to those in the Drag Manager or even the Scrap Manager. This article explains what a developer must know to simply add contextual menus to his application. As an additional benefit, the article will show how easily Contextual-Menu-like features can be added to an application runing an older version of Mac OS, where Apple has not provided the Contextual Menu Manager.

Extension Modules

There is one exception to the idea that Contextual Menus should not provide commands not listed anywhere else. On a PowerPC Mac with the Contextual Menu API installed, extension modules can add additional functions. These code fragments must be installed in a special folder in the System Folder before startup. They are then loaded and called whenever a contextual menu is created. By looking at the data selected (text, picture, file, folder or even no data), the extensions can add commands to the contextual menu. These plug-ins have only read access to the selected data, so they can not modify the data, but they can return results in the Clipboard or by sending an Apple event. The hosting application need not concern itself with an extension module; all of a plug-in's functions are done without the application's knowledge. The Contextual Menu Extension Module API will be explained in an upcoming article. Until then, just be aware that it is available, but only supported on PowerPC machines.

Adding Commands

The key to determining what to put into a contextual menu is the emphasis -- contextual menus. Ignore that the feature is a popup menu. There are many locations to place commands, or allow short cuts to global settings. The user has selected some content, which he wants to modify. Add commands that are based on the content of what is clicked. In a drawing program, clicking drawing elements (circles, rect, lines) should show the commands that affect the element (rotate, change line thickness, change color). In a word processor, commands that change the style of the text or the arrangement of the document are good choices. Window commands can be available, even if the user clicks a portion of the window that has no data. Think twice before adding an application command. For example, Quit is a poor choice; it already has a short cut in the menu. The API provides a way for sub menus also to be added to the contextual menu. If too many commands need to be added, this is a good way to subdivide the list.

The API

There are only 3 new calls in the Contextual Menu API. In their easiest explanation, the calls initialize the API, check if the contextual menu should appear, and display the Contextual Menu. These functions should be called only if the API is installed on the Mac. Use Gestalt to check if this is the case. The gestaltContextualMenuPresent bit of the gestaltContextual- MenuAttr Gestalt attribute indicates if the Contextual Menu Manager is installed.

If the Contextual Menu Manager is installed, call InitContextualMenus(). This will register the application as a client of the Contextual Menu Manager. The routine returns noErr (0) if the initialization succeeds. This check & initialization should occur immediately after the standard toolbox initialization.

Listing 1: ContextualMenuFun.cp

Check if Manager Available, if so Init It

   long a_result;

   if (Gestalt(gestaltContextualMenuAttr, &a_result) == noErr)
      g_have_contextual_menus = BitTst(&a_result, 
                                       31-gestaltContextualMenuPresent);
   else
      g_have_contextual_menus = false;

   if (g_have_contextual_menus)
      g_have_contextual_menus = (InitContextualMenus()==noErr);

To indicate he wants the contextual menu, the user does a special click on some selected data. For Mac OS 8, this is done by holding down the Control key while doing a standard mouse click. However, there is no reason that in the future this special click could not be done some other way (such as a right mouse click on a two-button mouse). For this reason, the Contextual Menu Manager provides a call that checks if the special click is being done. IsShowContextualMenuClick() should be called after the application is passed any non-NullEvent event. The function is passed the event record and returns true if the special click occured. If the function returns false, the program should continue normally.

Listing 2: ContextualMenuFun.cp

Parse Event, checking if the event is Contextual Menu special click. If so, call routine to handle this.

      a_flag = WaitNextEvent(everyEvent, &g_record, 10, NULL);
      if (g_have_contextual_menus) {

//   if Contextual Menus event,

         if (IsShowContextualMenuClick(&g_record)) {
//   Handle Contextual Menus

            G_ContextualMenu(g_record);
            a_flag = false;            
         }
      }
      
//   Handle Normal Events if flag true

The most important routine of the Contextual Menu API is ContextualMenuSelect(). It should be called only when IsShowContextualMenuClick() has returned true. Not only does this routine have several parameters, the application must do several preparation tasks before it can be called.

Assuming the application knows which window was chosen, the application should check if this window is the currently selected window. If it is not, it should immediately be made the current selected window, and redrawn. Next, if some content data was selected, some sort of visual feedback should be drawn to indicate this. Inverting, either the data or the outline of the data is a common method to show what was chosen.

Next, a menu handle should be created and inserted into the menu bar just as if the PopUpMenuSelect() call was going to be made. At this point, the various menu items can be added to the menu handle. Sub-menus also can be added. If the Contextual Menu API adds any items to the list (Help or plug-ins), the Contextual Menu Manager will handle parsing these items; your application need not worry about them.

Now call ContextualMenuSelect(). Pass it the created menu handle, the point that was clicked (in global coordinates), a flag that must always be set to false (originally intended to indicate if the clicked area has a Balloon Help item, but now reserved for Apple's use), the Help type needed, the Help string, a pointer to the Apple event descriptor handling the selected data (named inSelection), a pointer to a long integer (resulting outUserSelectionType to explain what happened), and two pointers to short integers (resulting menu ID & menu item).

The Help type parameter can be one of three values: kCMHelpItemNoHelp, kCMHelpItemAppleGuide, kCMHelpItemOtherHelp. If it is kCMHelpItemNoHelp, then there is no help associated with this contextual menu. The Contextual Menu Manager will put the word "Help" in the front of the menu, but disable it. If the parameter is kCMHelpItemAppleGuide, the Contextual Menu Manager will put the name of the main Apple Guide file into the front of the menu. If the parameter is kCMHelpItemOtherHelp, the Contextual Menu Manager will put the name passed in the Help string into the front of the menu.

If there is no selected data (or if the application does not want to pass the data out), the inSelection parameter can be NULL. Otherwise, it should be a valid Apple event descriptor. After the ContextualMenuSelect() call, the application is responsible for disposing of this descriptor if needed. For simple data structures, the descriptor has a handle to the data (be it text or a picture) as well as the data type. Applications that support scriptability can pass an object specifier pointing to the selected data as the descriptor. This allows some Contextual Menu extensions to send an Apple event back to the current application to change the selected data. Refer to Inside Mac: Interapplication Communication for more information about Apple event descriptors and object specifiers.

If the ContextualMenuSelect() call returns successfully, its result is noErr (0). In this case, outUserSelectionType will indicate what happened. If the user fails to select a menu or a plug-in, the constant kCMNothingSelected is returned. If the user selected Show Balloon Help, the Contextual Menu Manager will handle this call, and the constant kShowBalloonSelected is returned (the application should do nothing). If the user selected the Help menu item, kCMShowHelpSelected is returned. The Application should call the appropriate code, Apple Guide or another help system, to display the appropriate help window. If the user selected a menu item, kCMMenuItemSelected is returned, and the menu ID & menu item are also returned.

Before the application parses the result, it should remove any hiliting it placed on the data. The commands the user selected might change this data, so inverting it later may give the wrong results. The application should delete and dispose of the menu handle. Lastly, if the Apple event descriptor was created using Apple event calls, it should be disposed. Listing 3 shows an example of how to setup the basic ContextualMenuSelect() call:

Listing 3: ContextualMenuFun.cp

Sample ContextualMenuSelect call to handle creating & displaying menu. 

      UInt32 a_type;
      SInt16 a_menu_id;
      UInt16 a_menu_item;
      Boolean a_balloon_available = false;
      Str255 a_help_str = "\pContextualMenuFun Help...";
      UInt32 a_help_type = kCMHelpItemOtherHelp;
      AEDesc a_descr;
      AEDesc* a_descr_ptr = NULL;
//   Create Menu
      MenuHandle a_menu_handle = NewMenu(255, "\pX");
      if (a_menu_handle) {
         SetPort(a_window_ptr);
//   If Window not front most, bring it front and draw on it
         if (FrontWindow()!=a_window_ptr) {
            SelectWindow(a_window_ptr);
            G_Draw(a_window_ptr);
         }
         
//   If Color Window, hilite picture (invert frame)
         if (a_window_ptr!=g_window_about) {
            SetRect(&g_rect, 0, 0, 200, 200);
            PenMode(srcXor);
            FrameRect(&g_rect);
            PenNormal();
         }
      
//   Insert Menu into Menu list
         InsertMenu(a_menu_handle, -1);
//   Fill Item on menu base on Front Window
         G_ContextualMenu_Fill(a_menu_handle, a_window_ptr);      
//   Fill Descriptor with Pic, if possible
         a_descr_ptr = &a_descr;
         a_descr.descriptorType = typePict;
         a_descr.dataHandle = (Handle) g_pic_1;
//   Call to Handle Popup
         OSStatus a_status = ContextualMenuSelect(a_menu_handle, 
            p_event_record.where, a_balloon_available,
            a_help_type, a_help_str, a_descr_ptr, &a_type,
            &a_menu_id, &a_menu_item);
//   Unhilite
         if (a_window_ptr!=g_window_about) {
            SetRect(&g_rect, 0, 0, 200, 200);
            PenMode(srcXor);
            FrameRect(&g_rect);
            PenNormal();
         }
//   If call was ok,
         if (a_status==noErr) {
//      If normal item selected, handle it
            if (a_type==kCMMenuItemSelected) {
               G_ContextualMenu_Action(a_menu_id, a_menu_item, 
                     a_window_ptr);
            }
//      If help selected, handle it
            else if (a_type==kCMShowHelpSelected) {
               if (g_window_about) {
                  ShowWindow(g_window_about);
                  SelectWindow(g_window_about);
               } 
            }
         }

//   Delete Menu
         DeleteMenu(255);
         DisposeMenu(a_menu_handle);
      }

Poor Man's Contextual Menu

Contextual menus were introduced with Mac OS 8. The Contextual Menu Manager also can be installed seperately in any Mac OS 7.6 and later system; although, this is uncommon and is not approved by Apple. While it is always nice to support the latest API, what about the majority of your users who will not be using Mac OS 8 for a time (if ever)? That is where the code G_PoorMan_ContextualMenu provides similar contextual menu functions for these older machines.

G_PoorMan_ContextualMenu uses the PopupMenuSelect() function to create it's own contextual menu. The extension modules will not work with Poor Man's Contextual Menu. Remember, plug-ins are not supported under Mac OS 8 running on a 68040 machine. The code calls the same functions as the normal contextual menu handler, so the developer only has to modify G_ContextualMenu_Fill & G_ContextualMenu_Action for it to work under Contextual Menu Manager API or the G_PoorMan_ContextualMenu code.

G_PoorMan_ContextualMenu must be called whenever the user has clicked the mouse within a window (such as inContent, inDrag, and inGoAway regions). The routine is passed the window the user selected and returns True if this action invokes a Poor Man's Contextual Menu. In that case, the program should not continue to process the event because it has been dealt with.

The routine first checks to make sure that the Contextual Menu API is not installed, and that the current event is a mouse-down in a window. Then it creates a menu handle, to be used by the PopUpMenuSelect() routine. Just like the Contextual Menu API, the Poor Man version has to bringing the window to the top and giving feedback to the selected item. Then the Help item is added to the menu. This is different from the Contextual Menu API, which handles the Help menu. Since this menu may appear on the Help window, the code accounts for this by disabling the Help item in that case. The last bit of setup has G_ContextualMenu_Fill being called for the menu items to be added.

Once the menu is fully created, PopUpMenuSelect() is called to process the popup menu. If an item is selected, the menu item number is examined. If it is one, then the Help menu item was selected, and the Help window should appear. Any other number would cause G_ContextualMenu_Action to be called with the correct number. Finally the temporary popup-menu handle is deleted and disposed.

Listing 4: G_PoorMan_ContextualMenu

Poor Man Contextual Menu (ie. no Manager), Return true if event handled

Boolean G_PoorMan_ContextualMenu(WindowPtr p_window_ptr)
{
   Boolean a_flag = false;
//   No Manager, event mouse down in Window & Control Key down
   if (!g_have_contextual_menus) {
      if ((g_record.what==mouseDown) && p_window_ptr) {
         if ((g_record.modifiers & controlKey)!=0) {
//   Event handled here!
            a_flag = true;
            Str255 a_temp_str = "\p?";
            MenuHandle a_menu_handle = NewMenu(255, a_temp_str);
            if (a_menu_handle) {
               SetPort(p_window_ptr);

//   If Window not front most, bring it front and draw on it
               if (FrontWindow()!=p_window_ptr) {
                  SelectWindow(p_window_ptr);
                  G_Draw(p_window_ptr);
               }
               
//   If Color Window, hilite picture (invert frame) 
               if (p_window_ptr!=g_window_about) {
                  SetRect(&g_rect, 0, 0, 200, 200);
                  PenMode(srcXor);
                  FrameRect(&g_rect);
                  PenNormal();
               }
            
//   Insert Menu into Menu list
               InsertMenu(a_menu_handle, -1);
               
//   Add About (Dim if About Window on top) 
               if (p_window_ptr==g_window_about)
                  AppendMenu(a_menu_handle, 
                     "\p(About ContextualMenuFun...;(-");
               else
                  AppendMenu(a_menu_handle, 
                     "\pAbout ContextualMenuFun...;(-");

//   Fill Item on menu base on Front Window
               G_ContextualMenu_Fill(
                        a_menu_handle, p_window_ptr);
//   Call to Handle Popup
               long a_result = PopUpMenuSelect(a_menu_handle,
                  g_record.where.v, g_record.where.h, 1);
//   Unhilite
               if (p_window_ptr!=g_window_about) {
                  SetRect(&g_rect, 0, 0, 200, 200);
                  PenMode(srcXor);
                  FrameRect(&g_rect);
                  PenNormal();
               }
//   If call was ok, 
               if (a_result) {
                  short a_menu_id = HiWord(a_result);
                  short a_menu_item = LoWord(a_result);
                  if (a_menu_id==255) {
//      If help selected, handle it
                     if (a_menu_item==1) {
                        if (g_window_about) {
                           ShowWindow(g_window_about);
                           SelectWindow(g_window_about);
                        } 
                     }
                     else {
//      If normal item selected, handle it (Adjust for Help Menu Item) 
                        a_menu_item -= 2;
                        G_ContextualMenu_Action(a_menu_id,
                           a_menu_item, p_window_ptr);
                     }
                  }
               }
//   Delete Menu
               DeleteMenu(255);
               DisposeMenu(a_menu_handle);
            }
         }
      }
   }
   return a_flag;
}

Sample Code

ContextualMenuFun was created to show how easy it is to provide contextual menus to the user. Most Macintosh developers will recognize their way around the standard portions of the program. All the code to handle details of a Contextual Menu is inside of the G_ContextualMenu routine. The Non-Contextual Menu Manager version of the code is inside G_PoorMan_ContextualMenu. Both routines call G_ContextualMenu_Fill & G_ContextualMenu_Action. G_ContextualMenu_Fill is passed the menu handle & selected window pointer, and fills the menu with items. Assuming the user has chosen a command, G_ContextualMenu_Action is passed the clicked window, the selected menu ID & menu item. G_ContextualMenu_Action parses this information, and takes action. The code was written to be expanded, which is why menu ID is passed. Remember, sub menus can be added so the menu ID is not always the same. The selected window is passed so commands can be more window and content sensitive.

The new "logical interface" begs for an object oriented approach to handling the contextual menus. A clicked visual object calls the standard routine to set up the contextual menu, using the API or not, which calls a virtual method to fill in the menu handle with commands. The standard routine would then display the menu. Assuming the user selects an item, another virtual method would be called to take action. Controls and field objects could even put their commands into the menu and then call the owning window for the window to put in its own commands, and so on up the command chain. Commercial or in-house frameworks easily could be modified this way to support the Contextual Menu API.


Steve Sheets has been happily programming the Macintosh since 1983, which makes him older than he wishes, but not as young as he acts. Being a native Californian, his developer wanderings have led him to Northern Virginia. For those interested, his non-computer interests involve his family (wife & 2 daughters), Society for Creative Anachronism (Medieval Reenactment) and Martial Arts (Fencing & Tai Chi). He is currently an independent developer, taking on any and all projects and can be reached by sending mail to MageSteve@aol.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Macs Fan Control 1.5.0.0 - Monitor and c...
Macs Fan Control allows you to monitor and control almost any aspect of your computer's fans, with support for controlling fan speed, temperature sensors pane, menu-bar icon, and autostart with... Read more
Daylite 6.7.7 - Dynamic business organiz...
Daylite helps businesses organize themselves with tools such as shared calendars, contacts, tasks, projects, notes, and more. Enable easy collaboration with features such as task and project... Read more
OpenOffice 4.1.7 - Free and open-source...
OpenOffice.org is both an Open Source product and a project. The product is a multi-platform office productivity suite. It includes the key desktop applications, such as a word processor, spreadsheet... Read more
Backup and Sync 3.46 - File backup and s...
Backup and Sync (was Google Drive) is a place where you can create, share, collaborate, and keep all of your stuff. Whether you're working with a friend on a joint research project, planning a... Read more
iClock 5.5 - Customizable menu bar clock...
iClock replaces the old Apple's default menu bar clock with more features, customization and increases your productivity. Features: Have your Apple or Google calendar instantly available from the... Read more
Garmin Express 6.18.0.0 - Manage your Ga...
Garmin Express is your essential tool for managing your Garmin devices. Update maps, golf courses and device software. You can even register your device. Update maps Update software Register your... Read more
MarsEdit 4.3.5 - Quick and convenient bl...
MarsEdit is a blog editor for OS X that makes editing your blog like writing email, with spell-checking, drafts, multiple windows, and even AppleScript support. It works with with most blog services... Read more
Xcode 11.0 - Integrated development envi...
Xcode includes everything developers need to create great applications for Mac, iPhone, iPad, and Apple Watch. Xcode provides developers a unified workflow for user interface design, coding, testing... Read more
DaisyDisk 4.8 - $9.99
DaisyDisk allows you to visualize your disk usage and free up disk space by quickly finding and deleting big unused files. The program scans your disk and displays its content as a sector diagram... Read more
VMware Fusion 11.5.0 - Run Windows apps...
VMware Fusion and Fusion Pro - virtualization software for running Windows, Linux, and other systems on a Mac without rebooting. The latest version includes full support for Windows 10, macOS Mojave... Read more

Latest Forum Discussions

See All

Marvel Strike Force is adding Agent Coul...
Marvel Strike Force, the popular squad-based RPG, is set to receive a bunch of new content over the next few weeks. [Read more] | Read more »
Lots of premium games are going free (so...
You may have seen over the past couple weeks a that a bunch of premium games have suddenly become free. This isn’t a mistake, nor is it some last hurrah before Apple Arcade hits, and it’s important to know that these games aren’t actually becoming... | Read more »
Yoozoo Games launches Saint Seiya Awaken...
If you’re into your anime, you’ve probably seen or heard of Saint Seiya. Based on a shonen manga by Masami Kurumada, the series was massively popular in the 1980s – especially in its native Japan. Since then, it’s grown into a franchise of all... | Read more »
Five Nights at Freddy's AR: Special...
Five Nights at Freddy's AR: Special Delivery is a terrifying new nightmare from developer Illumix. Last week, FNAF fans were sent into a frenzy by a short teaser for what we now know to be Special Delivery. Those in the comments were quick to... | Read more »
Rush Rally 3's new live events are...
Last week, Rush Rally 3 got updated with live events, and it’s one of the best things to happen to racing games on mobile. Prior to this update, the game already had multiplayer, but live events are more convenient in the sense that it’s somewhat... | Read more »
Why your free-to-play racer sucks
It’s been this way for a while now, but playing Hot Wheels Infinite Loop really highlights a big issue with free-to-play mobile racing games: They suck. It doesn’t matter if you’re trying going for realism, cart racing, or arcade nonsense, they’re... | Read more »
Steam Link Spotlight - The Banner Saga 3
Steam Link Spotlight is a new feature where we take a look at PC games that play exceptionally well using the Steam Link app. Our last entry talked about Terry Cavanaugh’s incredible Dicey Dungeons. Read about how it’s a great mobile experience... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »

Price Scanner via MacPrices.net

2019 iMacs available for up to $350 off MSRP,...
Apple has Certified Refurbished 2019 21″ & 27″ iMacs now available starting at $929 and up to $350 off the cost of new models. Apple’s one-year warranty is standard, shipping is free, and each... Read more
Get a clearance 2018 13″ 2.3GHz Quad-Core Tou...
Apple has Certified Refurbished 2018 13″ 2.3GHz 4-Core Touch Bar MacBook Pros available starting at $1489. Apple’s one-year warranty is included, shipping is free, and each MacBook has a new outer... Read more
11″ WiFi iPad Pros on sale today for up to $2...
Amazon has new 2018 Apple 11″ WiFi iPad Pros in stock today and on sale for up to $200 off Apple’s MSRP. These are the same iPad Pros sold by Apple in its retail and online stores. Be sure to select... Read more
Select 12″ iPad Pros on sale for $200 off App...
Amazon has select 2018 Apple 12″ iPad Pros in stock today and on sale for $200 off Apple’s MSRP. These are the same iPad Pros sold by Apple in its retail and online stores. Be sure to select Amazon... Read more
Get one of Apple’s new 2019 iPhone 11 models...
Boost Mobile is offering the new 2019 Apple iPhone 11, iPhone 11 Pro, and 11 Pro Max for $100 off MSRP. Their discount reduces the cost of an iPhone 11 to $599 for the 64GB models, $899 for the 64GB... Read more
13″ 1.4GHz Silver MacBook Pros on sale for $1...
B&H Photo has new 2019 13″ 1.4GHz 4-Core Touch Bar Silver MacBook Pros on sale for $100 off Apple’s MSRP. Overnight shipping is free to many addresses in the US. These are the same MacBook Pros... Read more
4-core and 6-core 2018 Mac minis available at...
Apple has Certified Refurbished 2018 Mac minis available on their online store for $120-$170 off the cost of new models. Each mini comes with a new outer case plus a standard Apple one-year warranty... Read more
$250 prepaid Visa card with any Apple iPhone,...
Xfinity Mobile will include a free $250 prepaid Visa card with the purchase of any new iPhone, new line activation, and transfer of phone number to Xfinity Mobile. Offer is valid through October 27,... Read more
Sprint is offering the 64GB Apple iPhone 11 P...
Sprint has the new 64GB iPhone 11 Pro available for $12.50 per month for new customers with an eligible trade-in in of iPhone 7 or newer. That’s down from their standard monthly lease of $41.67. The... Read more
Final week: Apple’s 2019 Back to School Promo...
Purchase a new Mac using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email address qualify for the discount... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**734646BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 001220-Issaquah-Store **Job Description:** The Read more
Best Buy *Apple* Computing Master - Best Bu...
**734517BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000685-El Camino Real-Store **Job Description:** **What does Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**734830BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 000423-West Broad-Store **Job Description:** At Best Buy, our Read more
Systems Analyst ( *Apple* & Android) (Jo...
Systems Analyst ( Apple & Android) (Job ID: 572513) + 11751 Meadowville Ln, Chester, VA 23836, USA + Full-time Company Description Computer Consultants International, Read more
*Apple* Mobile App Developer - eiWorkflow So...
…eiWorkflow Solutions, LLC is currently looking for a consultant for the following role. Apple Mobile App Developer Tasks the role will be performing: ? Mobile App Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.