TweetFollow Us on Twitter

Rolling MDEF
Volume Number:10
Issue Number:6
Column Tag:Getting Started

Related Info: Menu Manager Custom Menus

Rolling Your Own MDEFs

Help your menus get the picture

By Dave Mark, MacTech Magazine Regular Contributing Author

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

This month’s program is a departure from our traditional applications. Though we do build an application, it only serves as a tester for our MENU definition procedure. When your application includes a menu bar made up of a series of MENU resources, you’ll normally take advantage of the standard menu definition procedure (better known as an MDEF) provided by the Menu Manager.

Every time you call MenuSelect() (in response to a command-key equivalent or to a click in the menu bar), the Menu Manager takes over, drawing and erasing menus in response to your mouse clicks. Every menu specifies an MDEF resource (we’ll get to that when we create the tester’s MENU resources later in the column). Each time a menu is drawn, the MDEF specified by the menu is called to draw the menu’s contents and to highlight the appropriate item as the mouse moves.

This month, we’re going to write our own MDEF, compile it into an MDEF resource, then write a test program to test the MDEF. Rather than rewrite the standard MDEF, our MDEF will create a menu of PICT resources, as opposed to text.

This MDEF is actually a rewrite of the MDEF from Volume II of the Mac Primer. Besides being a little long in the tooth (i.e., old and funky), the original broke some Toolbox rules that caused the Thought Police to pay me a visit...

Creating the MDEF Project

Create a folder called MDEF Files in your Development folder. Launch THINK C and create a new project named MDEF.Π. Add MacTraps to the project. Next, create a new source code file, save it as MDEF.c, and add it to the project.

Type in this source code in the MDEF.c window:


/* 1 */
#define kTopMargin 1
#define kLeftMargin2

void  DoSizeMessage( MenuHandle menu, Rect *menuRectPtr );
void  DoDrawMessage( MenuHandle menu, Rect *menuRectPtr );
void  DoChooseMessage( MenuHandle menu, Rect *menuRectPtr,
  Point hitPt, short *whichItemPtr );
void  InvertItem( short itemNumber, short itemHeight, 
  Rect *menuRectPtr );
void  DrawCenteredPict( PicHandle pic, Rect *rectPtr );
void  CalcitemHeightAndWidth( short basePICTid, short numPICTs,
 short *widthPtr, short *heightPtr );
void  GetNumPICTs( MenuHandle menu, short *baseIDPtr,
 short *numPICTsPtr );

main


/* 2 */
pascal void main( short message,
 MenuHandle menu,
 Rect *menuRectPtr,
 Point hitPt,
 short *whichItemPtr )
{
 switch( message )
 {
 case mDrawMsg:
 DoDrawMessage( menu, menuRectPtr );
 break;
 case mChooseMsg:
 DoChooseMessage( menu,menuRectPtr,hitPt,whichItemPtr );
 break;
 case mSizeMsg:
 DoSizeMessage( menu, menuRectPtr );
 break;
 }
}

DoSizeMessage


/* 3 */
void  DoSizeMessage( MenuHandle menu,
 Rect *menuRectPtr )
{
 short  basePICTid, numPICTs, maxPICTWidth, maxPICTHeight;
 
 GetNumPICTs( menu, &basePICTid, &numPICTs );
 CalcitemHeightAndWidth( basePICTid, numPICTs, &maxPICTWidth,
   &maxPICTHeight );
 
 (**menu).menuWidth = maxPICTWidth + 2 * kLeftMargin;
 (**menu).menuHeight = (maxPICTHeight + kTopMargin*2) * numPICTs;
}

DoDrawMessage


/* 4 */
void  DoDrawMessage( MenuHandle menu,
 Rect *menuRectPtr )
{
 short  basePICTid, numPICTs, maxPICTWidth, 
 maxPICTHeight, itemHeight, i;
 Rect   r, tempRect;
 PicHandlepic;
 
 GetNumPICTs( menu, &basePICTid, &numPICTs );
 CalcitemHeightAndWidth(  basePICTid, numPICTs, 
 &maxPICTWidth, &maxPICTHeight );
 
 itemHeight = maxPICTHeight + kTopMargin * 2;
 
 r.top = menuRectPtr->top + kTopMargin;
 r.left = menuRectPtr->left + kLeftMargin;
 r.bottom = r.top + maxPICTHeight;
 r.right = r.left + maxPICTWidth;
 
 for ( i=0; i<numPICTs; i++ )
 {
 pic = GetPicture( basePICTid + i );
 
 DrawCenteredPict( pic, &r );
 
 OffsetRect( &r, 0, itemHeight );
 }
}

DoChooseMessage


/* 5 */
void  DoChooseMessage(  MenuHandle menu,
 Rect *menuRectPtr,
 Point hitPt,
 short *whichItemPtr )
{
 short  basePICTid, selectedItem, numPICTs, 
 maxPICTWidth, maxPICTHeight, itemHeight;
 Rect   r;
 
 GetNumPICTs( menu, &basePICTid, &numPICTs );
 CalcitemHeightAndWidth(  basePICTid, numPICTs, 
 &maxPICTWidth, &maxPICTHeight );
 
 itemHeight = (2 * kTopMargin) + maxPICTHeight;
 
 if ( PtInRect( hitPt, menuRectPtr ) )
 {
 selectedItem = ( (hitPt.v - menuRectPtr->top) / 
 itemHeight ) + 1;
 
 if ((*whichItemPtr > 0) && (*whichItemPtr != selectedItem))
 {
 InvertItem( *whichItemPtr, itemHeight, menuRectPtr );
 }
 
 if ( *whichItemPtr != selectedItem )
 {
 *whichItemPtr = selectedItem;
 InvertItem( *whichItemPtr, itemHeight, menuRectPtr );
 }
 }
 else if ( *whichItemPtr > 0 )
 {
 InvertItem( *whichItemPtr, itemHeight, menuRectPtr );
 *whichItemPtr = 0;
 }
}

InvertItem


/* 6 */
void  InvertItem( short itemNumber,
 short itemHeight,
 Rect *menuRectPtr )
{
 Rect r;
 
 r = *menuRectPtr;
 
 r.top += ( (itemNumber-1) * itemHeight );
 r.bottom = r.top + itemHeight;
 
 InvertRect( &r );
}

DrawCenteredPict


/* 7 */
void  DrawCenteredPict( PicHandle pic,
 Rect *rectPtr )
{
 Rect pictRect;
 
 pictRect = (**pic).picFrame;
 
 OffsetRect( &pictRect, rectPtr->left - pictRect.left,
    rectPtr->top  - pictRect.top);
 OffsetRect( &pictRect,(rectPtr->right - pictRect.right)/2,
   (rectPtr->bottom - pictRect.bottom)/2);
 
 DrawPicture( pic, &pictRect );
}

CalcitemHeightAndWidth


/* 8 */
void  CalcitemHeightAndWidth( short basePICTid,
 short numPICTs,
 short *widthPtr,
 short *heightPtr )
{
 short  i;
 Rect   r;
 PicHandlepic;
 
 *widthPtr = 0;
 *heightPtr = 0;
 
 for ( i=0; i<numPICTs; i++ )
 {
 pic = GetPicture( basePICTid + i );
 r = (**pic).picFrame;
 
 if ( r.bottom - r.top > *heightPtr )
 *heightPtr = r.bottom - r.top;
 
 if ( r.right - r.left > *widthPtr )
 *widthPtr = r.right - r.left;
 }
}

GetNumPICTs


/* 9 */
void  GetNumPICTs( MenuHandle menu,
 short *baseIDPtr,
 short *numPICTsPtr )
{
 Handle longHandle;
 long retrievedLong;
 short  menuID;
 
 menuID = (**menu).menuID;
 
 longHandle = GetResource( 'long', menuID );
 
 retrievedLong = (*((long *)(*longHandle)));
 
 *baseIDPtr = HiWord( retrievedLong );
 *numPICTsPtr = LoWord( retrievedLong );
}

Save your source code. Next, select Set Project Type... from the Project menu. When the dialog appears, click on the Code Resource radio button. Now make your dialog box look like the one in Figure 1. Be sure to check every single field!

Figure 1. The Set Project Type... dialog box.

The File Type and Creator fields will make your MDEF file look like a ResEdit document. That way, when you double-click on the file MDEF.rsrc (which we’re about to create) you’ll automatically launch ResEdit.

Why do this? Well, if we create the MDEF resource in a file, we’ll eventually want to copy the MDEF into the resource file (or application) that will use the MDEF. We’ll most likely do this in ResEdit.

Now select Build Code Resource... from the Project menu. You’ll be prompted to name the file the MDEF resource will be copied to. There are two approaches you can use. If you plan on using this resource in more than one application, you should save it as its own resource file. That’s what we’ll do. Enter the name MDEF.Π.rsrc and press the Save button.

The second approach we could have taken would be to click on the Merge checkbox, then entering the name of the resource file of the application that will be using the MDEF. If you do this, the MDEF resource will be copied into the application’s resource file, which is really what you want. We’ll do the same thing by hand.

Creating the Tester Resources

Now that your MDEF is complete, you’ll build an application to test the sucker. We’ll start by building the project resources. Start ResEdit and create a new file in the MDEF Files folder called Tester.Π.rsrc. Now open the file MDEF.Π.rsrc, click on the MDEF icon, and select Copy from the Edit menu. Now click on the Tester.Π.rsrc window and select Paste from the Edit menu. The MDEF icon should appear in the Tester.Π.rsrc window. When you double-click on the MDEF icon, you should see a single MDEF with an ID of 128. So far, so good.

Next, create a WIND resource with an ID of 128, a top of 40, Left of 2, Height of 160, and Width of 200. Click on the second icon from the left for the window type. Make the window not visible.

Next, create an MBAR resource (with ID 128) with four menus in it, numbered 128, 129, 130, and 131.

Next, create four MENU resources using the pictures in Figure 2 as a guide. Note that MENU 131 has a title (Pictures) but no items.

Figure 2. The four MENU resources.

Now comes a critical step. Open up the MENU editor to edit MENU 131. Select Edit Menu & MDEF ID... from the MENU menu. A dialog box will appear allowing you to set the MENU and MDEF resource IDs for this MENU. Change the MDEF ID from 0 to 128. Leave the MENU ID as is.

Be sure you change the MDEF id. If you don’t, the program will not work!

Next, you’ll create a custom resource that will tell the MDEF the resource ID of the first PICT to display, as well as the number of PICTs to display in the menu. Close all the windows till you are back in the main Tester.Π.rsrc window. Now select Create New Resource from the Resource menu. When prompted for a resource type, enter the four characters long. Since ResEdit doesn’t have a ‘long’ template, it will throw you into the hex/ASCII editor. Use the hex side and enter the hex number 00800005. For a peek at mine, check out Figure 3.

Figure 3. The long resource.

Next, select Get Resource Info from the Resource menu to change the resource ID to 131. You must make this change, so the MDEF will associate this resource with MENU 131.

The first two bytes of the long resource tell the MDEF to start off with PICT 128 (in hex, that’s 0080) and to use 5 PICT resources in a row (128, 129, 130, 131, and 132). Oh, by the way, I called this resource long because it is always 4 bytes in length.

Once you’ve changed the long resource ID to 131 (you did do that, didn’t you?) you are ready to create the PICT resources. Create five of them, being sure that they are numbered from 128 to 132. Color is fine. For best results, you might want to keep all of them around the size of an icon. My five are shown in Figure 4.

Figure 4. My five PICTs.

Save your resource file and quit ResEdit.

Creating the Tester Project

In THINK C, create a new project in the MDEF Files folder called Tester.Π. Add MacTraps to the project. Create a new source code file named Tester.c and add it to the project. Here’s the source code:


/* 10 */
#define kWindowResID 128
#define kMBARResID 128
#define kNULLStorage 0L
#define kMoveToFront (WindowPtr)-1L
#define kSleep   60L

#define mApple   128
#define iAbout   1

#define mFile    129
#define iQuit    1

#define mPICT    131


/*  Globals  */

Boolean gDone;
short   gCurPICTid;

/*  Functions  */

void  ToolboxInit( void );
void  MenuBarInit( void );
void  WindowInit( void );
void  EventLoop( void );
void  DoEvent( EventRecord *eventPtr );
void  HandleMouseDown( EventRecord *eventPtr );
void  HandleMenuChoice( long menuChoice );
void  HandleAppleChoice( short item );
void  HandleFileChoice( short item );
void  HandlePICTChoice( short item );
void  DoUpdate( WindowPtr window );
void  DrawPictInWindow( PicHandle pic, WindowPtr window );
short GetBasePICTid( short menuID );

main


/* 11 */
void  main( void )
{
 ToolboxInit();
 MenuBarInit();
 WindowInit();
 
 gCurPICTid = GetBasePICTid( mPICT );
 
 EventLoop();
}

ToolboxInit


/* 12 */
void  ToolboxInit( void )
{
 InitGraf( &thePort );
 InitFonts();
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs( NULL );
 InitCursor();
}

MenuBarInit


/* 13 */
void  MenuBarInit( void )
{
 Handle menuBar;
 MenuHandle menu;
 
 menuBar = GetNewMBar( kMBARResID );
 SetMenuBar( menuBar );

 menu = GetMHandle( mApple );
 AddResMenu( menu, 'DRVR' );
 
 DrawMenuBar();
}

WindowInit


/* 14 */
void  WindowInit( void )
{
 WindowPtrwindow;
 
 window = GetNewWindow( kWindowResID,kNULLStorage,kMoveToFront);
 
 if ( window == NULL )
 {
 SysBeep( 20 );  /* Couldn't load WIND */
 ExitToShell();
 }
 
 SetPort( window );
 ShowWindow( window );
}

EventLoop


/* 15 */
void  EventLoop( void )
{
 EventRecordevent;
 
 gDone = false;
 while ( gDone == false )
 {
 if ( WaitNextEvent( everyEvent, &event, kSleep, nil ) )
 DoEvent( &event );
 }
}

DoEvent


/* 16 */
void  DoEvent( EventRecord *eventPtr )
{
 char theChar;
 
 switch ( eventPtr->what )
 {
 case mouseDown: 
 HandleMouseDown( eventPtr );
 break;
 case keyDown:
 case autoKey:
 theChar = eventPtr->message & charCodeMask;
 if ( (eventPtr->modifiers & cmdKey) != 0 ) 
 HandleMenuChoice( MenuKey( theChar ) );
 break;
 case updateEvt:
 DoUpdate( (WindowPtr)eventPtr->message );
 break;
 }
}

HandleMouseDown


/* 17 */
void  HandleMouseDown( EventRecord *eventPtr )
{
 WindowPtrwindow;
 short  thePart;
 long   menuChoice;
 
 thePart = FindWindow( eventPtr->where, &window );
 switch ( thePart )
 {
 case inMenuBar:
 menuChoice = MenuSelect( eventPtr->where );
 HandleMenuChoice( menuChoice );
 break;
 case inSysWindow: 
 SystemClick( eventPtr, window );
 break;
 case inDrag : 
 DragWindow( window, eventPtr->where, &(screenBits.bounds) );
 break;
 }
}

HandleMenuChoice


/* 18 */
void  HandleMenuChoice( long menuChoice )
{
 short  menu;
 short  item;
 
 if ( menuChoice != 0 )
 {
 menu = HiWord( menuChoice );
 item = LoWord( menuChoice );
 
 switch ( menu )
 {
 case mApple:
 HandleAppleChoice( item );
 break;
 case mFile:
 HandleFileChoice( item );
 break;
 case mPICT:
 HandlePICTChoice( item );
 break;
 }
 HiliteMenu( 0 );
 }
}

HandleAppleChoice


/* 19 */
void  HandleAppleChoice( short item )
{
 MenuHandle appleMenu;
 Str255 accName;
 short  accNumber;
 
 switch ( item )
 {
 case iAbout:
 SysBeep( 20 );
 break;
 default:
 appleMenu = GetMHandle( mApple );
 GetItem( appleMenu, item, accName );
 accNumber = OpenDeskAcc( accName );
 break;
 }
}

HandleFileChoice


/* 20 */
void  HandleFileChoice( short item )
{
 switch ( item )
 {
 case iQuit :
 gDone = true;
 break;
 }
}

HandlePICTChoice


/* 21 */
void  HandlePICTChoice( short item )
{
 WindowPtrwindow;
 
 window = FrontWindow();
 
 EraseRect( &window->portRect );
 InvalRect( &window->portRect );
 
 gCurPICTid = GetBasePICTid( mPICT ) + item - 1;
}

DoUpdate


/* 22 */
void  DoUpdate( WindowPtr window )
{
 PicHandlepic;
 
 BeginUpdate( window );
 
 pic = GetPicture( gCurPICTid );

 if ( pic == NULL )
 {
 SysBeep( 20 );  /* Couldn't load PICT */
 ExitToShell();
 }
 
 DrawPictInWindow( pic, FrontWindow() );
 
 EndUpdate( window );
}

DrawPictInWindow


/* 23 */
void  DrawPictInWindow( PicHandle pic,
 WindowPtr window )
{
 Rect   pictRect, windRect;
 
 pictRect = (**pic).picFrame;
 
 windRect = window->portRect;
 
 OffsetRect(&pictRect, windRect.left - pictRect.left,
 windRect.top - pictRect.top);
 OffsetRect(&pictRect,(windRect.right - pictRect.right)/2,
 (windRect.bottom - pictRect.bottom)/2);
 
 DrawPicture( pic, &pictRect );
}

GetBasePICTid


/* 24 */
short GetBasePICTid( short menuID )
{
 Handle longHandle;
 long retrievedLong;
 
 longHandle = GetResource( 'long', menuID );
 
 retrievedLong = (*((long *)(*longHandle)));
 
 return( HiWord( retrievedLong ) );
}

Once the code is typed in, save your changes and run this puppy.

Running the MDEF Tester

When you run your application, the first thing you should see is a window with PICT 128 centered in it. Now, for the big moment. Drumroll, please! Click your mouse on the Pictures menu. A menu should appear with your five PICTs in it. Select a picture. The selected picture should appear in the window. Figure 5 shows my menu, with Clarus the Dog-Cow selected. Moof!

Figure 5. My Pictures menu, with the second picture selected.

Till Next Month

Next month, we’ll walk through the code and talk about code resources in general. Till then, Daniel and I will be busy putting together his new swing set. Later...

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

VOX 3.5.5 - Music player that supports m...
VOX just sounds better! The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features and support for all audio formats you should ever need.... Read more
WiFiSpoof 3.9 - Change your WiFi MAC add...
WiFiSpoof quickly and easily allows you to change your WiFi MAC address via hot-key or the system menu bar. Startup optimizations Fixed an issue with getting the correct Address for some devices... Read more
FileMaker Pro 19.6.3.302 - Quickly build...
FileMaker Pro is the tool you use to create a custom app. You also use FileMaker Pro to access your app on a computer. Start by importing data from a spreadsheet or using a built-in Starter app to... Read more
RapidWeaver 9.0.0 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
Burn 3.1.6 - Easily burn data, audio, vi...
Burn... There are a lot of ways to approach burning discs. Burn keeps it simple, but still offers a lot of advanced options. Create data discs with advanced data settings like, file permissions, the... Read more
XMind Pro 22.11.3656 - Popular mind mapp...
XMind Pro claims to be the most popular mind-mapping tool on the planet. You can use it to work through an individual brainstorming session and it will record and collect inspirations as you go. It... Read more
A Better Finder Rename 11.58 - File, pho...
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
Duet 3.2.0.0 - Use your iPad as an exter...
Duet is the first app that allows you to use your iDevice as an extra display for your Mac using the Lightning or 30-pin cable. Note: This app requires a iOS companion app. Release notes were... Read more
iStat Menus 6.70 - Monitor your system r...
iStat Menus lets you monitor your system right from the menubar. Included are 8 menu extras that let you monitor every aspect of your system. Features: CPU - Monitor cpu usage. 7 display modes,... Read more
Opera 95.0.4635.25 - High-performance We...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more

Latest Forum Discussions

See All

The 10 Best Arcade Archives Beat-Em-Ups...
A little while ago, we put up a list of our favorite shoot-em-ups in Hamster’s Arcade Archives line-up. There are hundreds of games available thanks the weekly releases, so it can be a little hard to find the cream of the crop if you’re coming in a... | Read more »
Cyan announces iOS port for Myst to cele...
One of the best things about old games being remastered and ported is it gives people the chance to try those old games that were slightly too difficult when they were younger, and thanks to indie studio Cyan, mobile users can now experience the... | Read more »
‘Castle Crumble’ From ‘Spire Blast’ Deve...
Spire Blast developers Orbital Knight have released Castle Crumble () on Apple Arcade today. If you played Spire Blast you will right at home here, and I think I like it more based on the first few levels I played. It features cannonballs, magic... | Read more »
SwitchArcade Round-Up: ‘Life is Strange...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for February 2nd, 2023. We’ve actually got a little news to look at today, and that’s where we’ll start things. There are also a ton of new games, some of them very good (Life is Strange... | Read more »
‘Dead Space’ Remake Steam Deck Review –...
Need for Speed Unbound was one of the first new current generation only games to impress me on Steam Deck. I’ve played many games that have been ported to PC from other platforms and also games built for PC from the start, but it is always great... | Read more »
The Stunning Remake of ‘Myst’ Is Coming...
Indie developer Cyan who developed the 2021 reimagining of the classic Myst have just announced that it is coming to iOS as Myst Mobile. Myst Mobile will bring the complete experience to iPhone and iPad devices in the future. Considering the... | Read more »
Psychedelic Horror Experience ‘Happy Gam...
Amanita Design’s psychedelic horror experience Happy Game is out now on iOS and Android worldwide following its debut on Nintendo Switch and PC platforms a little while ago. Happy Game is a standard paid game on iOS and Android, and not included... | Read more »
Genre-Mixing Interactive Story ‘Delete A...
Following the release of developer Patrones & Escondites’s Unmemory back in 2020, we learned about Delete After Reading last year. Delete After Reading is about a trio of friends setting out to rescue one of the greatest video games of all... | Read more »
Out Now: ‘Valiant Hearts: Coming Home’,...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »
SwitchArcade Round-Up: ‘PowerWash Simula...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for February 1st, 2023. It’s a slower day today, at least at the time of writing. Now that I’ve said that Nintendo will probably announce a Direct or something. At any rate, there are a... | Read more »

Price Scanner via MacPrices.net

Deal Alert! Apple 10th-generation 64GB WiFi i...
Tiger Direct has the Apple 10th-generation 64GB WiFi iPad (in Silver) in stock today and on sale for $224.99 shipped. Their price is $225 (50%) off Apple’s MSRP. That’s an amazing deal, and we... Read more
New sale at Sams Club: Take $60 off every App...
Sams Club has Apple Watch Series 8 models on sale for $60 off Apple’s MSRP from now through Valentine’s Day 2023. $45 Sams Club Membership required. Note that sale prices are for online orders only,... Read more
Apple to update HomePod mini, AirPods Max, an...
Analyst Ming-Chi Kuo, in his latest post, predicts that Apple will update their ‘acoustic’ products next year sometime. He believes that it will be in the middle of 2024 before Apple will release the... Read more
Open-Box 10th generation iPads available for...
QuickShip Electronics has open-box return 10th-generation 64GB WiFi iPads, in Silver & Blue, in stock and on sale for $379 on their eBay store right now, free express delivery included. According... Read more
Verizon is offering a 23% ($30) discount on...
Verizon has 2nd-generation Apple AirPods on sale for $99.99 on their online store for a limited time. Their price is $30 (23%) off Apple’s MSRP, and it’s the lowest price available for in-stock... Read more
Rare sale: Apple HomePod minis for $84.99 at...
Verizon has HomePod minis on sale for $84.99 shipped for a limited time, $14 off Apple’s MSRP. Their sale price applies to all mini colors and represents a rare HomePod sale price. Verizon service is... Read more
New M2 Mac minis in stock and on sale for $30...
Apple retailer Expercom has new M2 Mac minis in stock and on sale today for up to $65 off MSRP, starting at $569. Shipping is free. You can optionally purchase an AppleCare+ plan along with your mini... Read more
Week’s Best MacBook Air Deals: 13″ M2 MacBook...
Apple has standard-configuration 13″ M2 MacBook Airs in stock and on sale starting at only $1079, Certified Refurbished, and ranging up to $150 off MSRP. Apple also has numerous Airs with build-to-... Read more
New sale: Save $155 on the 30-Core 14″ M2 Max...
Apple retailer Expercom has the 30-Core 14″ M2 Max MacBook Pro in stock today and on sale for $155 off MSRP. In addition to their MacBook Pro sale prices, take $50 off AppleCare+ when purchased along... Read more
First significant sale prices on Apple’s new...
Apple retailer Expercom has 14″ M2 Pro MacBook Pros in stock today and on sale for up to $125 off MSRP. In addition to their MacBook Pro sale prices, take $50 off AppleCare+ when purchased along with... Read more

Jobs Board

Sales Floor Associate - *Apple* Blossom Mal...
Sales Floor Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Sales Floor Assistant - *Apple* Blossom Mal...
Sales Floor Assistant - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
*Apple* Tech Support Analyst - Sesame Worksh...
Sesame Workshop is looking for an Apple Tech Support Analyst. We're looking for someone with an enthusiastic personality, articulate communication skills and a Read more
*Apple* Systems Administrator - JAMF - Activ...
…Administration **Duties and Responsibilities** + Configure and maintain the client's Apple Device Management (ADM) solution. The current solution is JAMF supporting Read more
MacOS X / *Apple* Support Engineer - Royal...
MacOS X / Apple Desktop Support Engineer, on-site in New York, NY The Desktop Support Group is looking for an endpoint engineer with a focus on supporting MacOS and Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.