TweetFollow Us on Twitter

Print Dialogs
Volume Number:8
Issue Number:2
Column Tag:C Workshop

Related Info: Printing Manager Adding Items to the Print Dialogs

Modifying Print Dialogs

Tech Note #95 and beyond

By Greg Wilson, Etobicoke, Ontario, Canada

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

About the author

Greg Wilson is a Toronto area Macintosh consultant. He has been programming the Macintosh since 1985. His first article for MacTutor shows how to add Filter Routines and disable items in Print Dialogs.

The Problem

I programmed myself into a hole the other day. I was (am, seem to forever be) writing a game which can be played by several people over the network. Every time I passed through my main event loop I checked to see if there were any network messages to be processed. Unfortunately, there was a problem if the user selected a menu command which brought up a modal dialog, for example, when selecting the “About ” menu item. I had used the Alert function to handle the modal dialog like this:

/* 1*/

void ShowAboutMeDialog(void)
{
 short  itemHit;
 itemHit = Alert(rAboutAlert, nil);
 } /* ShowAboutMeDialog */

The program would sit on the line

/* 2 */

itemHit = Alert(rAboutAlert, nil);

This handles the window until the user hit an item, and so my main event loop network message check wouldn’t get executed and messages could be missed. I had to ensure that network messages weren’t missed (or timed out) while the user was sitting in a modal dialog.

The solution was to use a “Filter Proc” when calling ModalDialog directly, or indirectly through Alert, SFGetFile, etc which pass the FilterProc to ModalDialog. This Filter Proc would get called every time ModalDialog detected an event. My filter simply checked to see if there were any messages, processed them and then let ModalDialog handle the event in the usual way. Similar code has been seen in MacTutor numerous times in the past, my version (Net_filter) is shown in the sample code which follows the article. It gets invoked by changing the troublesome line to:

/* 3 */

itemHit = Alert(rAboutAlert, &Net_filter);

Pride Goeth

Before I got injured patting myself on the back, it became obvious that this would not work for Print Dialogs. There are two routines to conduct the modal print dialogs. The PrJobDialog and PrStlDialog functions conduct modal dialogs for defining the characteristics of the Print Job (quality, copies, page range, etc ) and the Page Setup (paper orientation, paper size etc) respectively. Neither of these accept a Filter Proc function. I could see no easy way around this, so I did the logical thing. I decided to leave it for later and hope the problem went away.

Of course things went from bad to worse. The BitMaps that I painstaking added to the display and printing didn’t print on an ImageWriter when the user selected Draft Mode. This was because Draft Mode is designed to just print the characters in the native character set of the device. BitMaps aren’t supported. PrGeneral was supposed to allow for printing of draft mode BitMaps under certain restrictions, but it seemed like it wouldn’t allow me to do what I needed. I wanted to disable the Draft Mode radio button; that way the user could never select Draft Mode printing, and so my BitMaps would always print. I knew from my experience, being unable to get a Filter Proc into these dialogs, that there was no way to patch a hook into these routines. Yet there must be some way to get at the dialogs since several commercial applications were disabling the button, and others were adding items to the dialogs. I now had two problems, so some action was required.

Salvation came in the form of Tech Note #95. This little beauty was built to explain how to add items to the Printing Manager’s dialogs. But more importantly for me, it showed the format of the data structures and described the procedures and functions required by PrStlDialog and PrJobDialog used to create and display the dialog information.

Inside PrJobDialog

When the PrJobDialog is called, it really only calls another function like this:

/* 4 */

ok = PrDlgMain(PrDlgStrctHndl,&PrJobinit);

(PrStlDialog operates similarly, except PrStlInit is used in the call to PrDlgMain. From now on I’ll just refer to PrJobDialog since that’s where Draft mode is used.)

PrDlgMain actually does all the work, and the first thing it does is call the PrJobInit routine. PrJobInit sets up a dialog record and puts it into a data structure. The data structure is defined in Printing.h like this:

/* 5 */

struct TPrDlg {
DialogRecord Dlg;
ModalFilterProcPtr pFltrProc;
PItemProcPtr pItemProc; 
THPrint hPrintUsr;
Boolean fDoIt;
Boolean fDone;
long lUser1;
long lUser2;
long lUser3;
long lUser4;
/*Plus more stuff needed by the particular printing dialog.*/
};

typedef struct TPrDlg TPrDlg;
typedef TPrDlg *TPPrDlg;

typedef pascal TPPrDlg (*PDlgInitProcPtr)(THPrint hPrint);

Then the PrJobInit function returns a pointer of type TPPrDlg which points to this structure. PrDlgMain then calls ShowWindow, passing the DialogRecord window (Dlg), and finally calls ModalDialog (passing, luckily enough, the ModalFilterProcPtr).

So, if I could get at this TPrDlg record, I could change the ModalFilterProcPtr and disable the Draft mode item in the DialogRecord window. Two bugs fixed for the price of one!

Today, On “How To Do It ”

As luck would have it, Tech Note #95 even showed how to get access to this TPrDlg record. Instead of using the PrJobDialog routine, I call PrDlgMain myself, substituting my own init routine in the call so it looks like this:

/* 6 */

if (!PrDlgMain(hPrintRec,&MyJobDlgInit))
 /* there was an error */

My init routine, MyJobDlgInit, calls the original init routine, PrJobInit, lets it set everything up, then modifies what we need.

/* 7 */

/* call PrJobInit to get pointer to the invisible job dialog */
PrtJobDialog = PrJobInit(hPrintRec);
if (PrError() != noErr)
 return nil;

/* set up the filter function */
PrtJobDialog->pFltrProc = (ModalFilterProcPtr)&Net_filter;
 
/* disable the draft mode now */
DisableDraftMode(PrtJobDialog);

The routine DisableDraftMode is responsible for disabling the Draft Mode radio button of the print dialog record pointed to by PrtJobDialog. DisableDraftMode determines how many items in the list via ItemList, then loops for this number so each item can be checked. GetDitem is used to determine what type of item it is.

Type Checking

Types can have the high bit set if they are disabled, so I dropped this bit by ANDing the type with 7F (0111111). Radio buttons are controls, so the type check is made for ctrlItem + radCtrl (Control item and Radio Button Control).

The title is then found via the lowercase version of getctitle. I did it like this so that a C string is returned as opposed to Pascal format. By looping and checking for “Draft” as the title of the control, we aren’t relying on the Draft item being a certain item number in the Dialog. After all, Tech Note #95 has warned that “If you depend on the Draft button being a particular number and we change the Draft button’s item number, your program may not work”. When coding this for a real application, I would put the “Draft” string in the resource file to make localization easier.

Once the draft item is found, it is unselected by SetCtlValue and finally disabled by passing a value of 255 to HiliteControl.

Having disabled and possibly unselected the Draft item, the dialog is cleaned up by making sure that at least one of the buttons is selected. The item numbers of the Best and Faster modes are saved, so we try and turn one of these on.

SoapBox Derby

I should point out that there is a difference between a disabled item and an inactive control. If the item is disabled (and we could disable it by SetDItem) it cannot be selected. However, it is not drawn in dimmed text. The item looks like it can be clicked on, and if the user clicks on it, the radio button turns on then off right away. This means they might try again and again and start to think something is wrong with the mouse etc . When a control is set to inactive via HiLiteControl, it cannot be selected and IS dimmed. This gives feedback to the user and says “You can’t pick this, it isn’t a valid option right now”. Furthermore, the consistency of the Mac interface is preserved and the world is saved for peace, democracy and future generations. End of soapbox.

Adding Items to the ItemList

In fact, my code is a lot simpler to write than Tech Note #95’s. The Tech Note shows how to add items to the Dialog. The field Items in the Dialog Record contains a handle to a list of items. This list is documented briefly in Inside Macintosh, but the sample code in Tech Note #95 gives a more complete picture. The ItemList itself contains an item, dlgmaxindex, which tells the number of Items in the list (minus 1). Following dlgmaxindex is each of the DITLs. Each of these DITLs contains a Handle to the item, it’s rect, type and datalength followed by datalength bytes of variable data. There is no Trap to add a DITL to a DialogRecord’s ItemList and since each DITL is a variable length field within the Item List, the AppendDITL routine must do some fancy pointer manipulation. We will only look at and change the existing DITL’s so we can use the existing Dialog and Control Manager routines, but we will use the ItemList definition to get the variable. I’ve kept the ItemList structure and other definitions as in Tech Note #95 in case you wish to expand the sample to include a C version of the AppendDITL routine.

Example Source

The C source code which follows is composed of two main modules. The NoDraft.c source is the part of the basic boilerplate shell that I use. It originated on some developer CD somewhere and has been stripped down to the basics to conserve trees. It really just sets up a window and menubar then waits for the user to select “Print ”. The important stuff all happens in the Dialogs.c module. This module contains the DisableDraftMode, and MyJobDlgInit, as well as the routines needed to setup the print dialogs, the filter function, etc.

Summary

The example which follows shows how to include a filter function and how to disable the Draft Mode button on a print dialog. If you need to add items to a dialog, Tech Note #95 shows you how.

Listing NoDraft.h
/*-----------------
#MultiFinder-Aware Simple NoDraft Application
#
#NoDraft
#
#NoDraft.h- noDraft header manifest
#
#NoDraft is an example application that 
#demonstrates how to modify Print Dialogs.
#
----------------*/

#define kMinSize 128 /* min size (K) */

#define kPrefSize128 /* pref’d size (K) */

/* Indicies into STR# resources */
#define eWrongMachine1
#define eSmallSize 2
#define eNoMemory3
#define eNoWindow4
#define eUnknown 1

#define rMenuBar 128 /* app’s menu bar */
#define rAboutAlert128  /* about alert */
#define rUserAlert 129  /* error alert */
#define rWindow  128 /* A window */
#define kErrStrings128  /* error strings */

#define kSysEnvironsVersion 1

#define kOSEvent app4Evt  /* used by MF */
#define kSuspendResumeMessage 1  
#define kResumeMask1 
#define kMouseMovedMessage0xFA
#define kNoEvents0

#define mApple   128 /* Apple menu */
#define iAbout   1

#define mFile    129 /* File menu */
#define iNew1
#define iClose   4
#define iPSetUp  9
#define iPrint   10

#define iQuit    12

#define mEdit    130 /* Edit menu */
#define iUndo    1
#define iCut3
#define iCopy    4
#define iPaste   5
#define iClear   6

#define abs(x)   ( (x) < 0 ? -(x) : (x))
#define MAX(x, y)( (x) > (y) ? (x) : (y) )

#define kMinHeap 21 * 1024
#define kMinSpace8 * 1024

/* used to set enable/disable flags of a menu */

#define AllItems 0b1111111111111111111111111111111
/* 31 flags worth*/
#define NoItems  0b0000000000000000000000000000000
#define MenuItem10b0000000000000000000000000000001
#define MenuItem20b0000000000000000000000000000010
#define MenuItem30b0000000000000000000000000000100
#define MenuItem40b0000000000000000000000000001000
#define MenuItem50b0000000000000000000000000010000
#define MenuItem60b0000000000000000000000000100000
#define MenuItem70b0000000000000000000000001000000
#define MenuItem80b0000000000000000000000010000000
#define MenuItem90b0000000000000000000000100000000
#define MenuItem10 0b0000000000000000000001000000000
#define MenuItem11 0b0000000000000000000010000000000
#define MenuItem12 0b0000000000000000000100000000000

#define cr0x0d
#define enter  0x03

#define delay_length 1024
Listing NoDraft_types.h
/*-----------------
#MultiFinder-Aware Simple NoDraft Application
#
#NoDraft_types.h - C type defs for NoDraft
#
---------------*/

struct DITLItem {
 Handle itmHndl;
 Rect   itmRect;
 SignedByte itmType;
 SignedByte itmData;
 };

typedef struct DITLItem DITLItem;
typedef DITLItem *pDITLItem, **hDITLItem;

struct ItemList {
 short  dlgMaxIndex;
 DITLItem DITLItems;
 };

typedef struct ItemList ItemList;
typedef ItemList *pItemList, **hItemList;

static TPPrDlg PrtJobDialog;
Listing Dialogs.c
/*----------------------
#MultiFinder-Aware Simple NoDraft Application
#
#Dialogs.c -Dialog handling code
#
---------------------- */

#include <Values.h>
#include <Types.h>
#include <Resources.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Events.h>
#include <Windows.h>
#include <Menus.h> 
#include <TextEdit.h>
#include <Dialogs.h>
#include <Desk.h>
#include <ToolUtils.h>
#include <Memory.h>
#include <SegLoad.h>
#include <OSUtils.h>
#include <OSEvents.h>
#include <DiskInit.h>
#include <Packages.h>
#include <Controls.h>
#include <Printing.h>
#include <Traps.h>
#include <NoDraft.h>
#include <Nodraft_types.h>

pascal TPPrDlg MyJobDlgInit();

THPrint hPrintRec; 

/*---------------------- */

void FlashDialogItem(the_dialog, theItem)

DialogPtr the_dialog;
short   theItem;

 {
 short  itemType;
 Rect   itemRect;
 Handle itemHdl;
 long   longdelay, longticks;
 
 GetDItem(the_dialog, theItem, &itemType, &itemHdl, &itemRect); 
 
  if ( itemHdl != nil) {
 HiliteControl( (ControlHandle) itemHdl, 1);
 longdelay = longticks = 6;
 Delay(longdelay, &longticks);
 } 
 } 

/*---------------------- */

void checknetwork()

 {
 static int count = 0;  
 /*I use this routine to check for AppleTalk™
 messages rather than include all that code,
 I just beep every once in a while to let 
 you know it is working, you can change
 delay_length to increase/decrease
 freq of Beeps */
 
 if ( count++ > delay_length ) {
 SysBeep(3);
 count = 0;
 }
 } /* end of checking the network */
 
/*---------------------- */

pascal Boolean  Net_filter(theDialog, theEvent, itemHit)

DialogPtr theDialog;
EventRecord *theEvent;
short   *itemHit;
 
 {
 
 int    key;
 Booleanhandled_event;

 handled_event = false; /* did we handle event */
 
/* do whatever you have to for event */
 
 switch ( theEvent->what) {
 
 case nullEvent:
 checknetwork();
 break;
 
 case keyDown:
 key = theEvent->message & charCodeMask;
 if (( key == cr) || ( key == enter ))
 {
 FlashDialogItem(theDialog, ok); 
 *itemHit = ok;
 handled_event = true;
 }
 break;
 
 case updateEvt:
 /*we get all kinds of update events here
 some for our modal dialog and even some
 for the different windows ( in some cases) 
 rather than try and process these so that
 we do get real null events when
 we can check the network, just check the
 network and let the update events get
 processed later */

 checknetwork();
 break;
 
 case activateEvt:
 break;
 
 default:
 /* Don’t know what we got here, but */
 /* but check the network in case we */
 /* get stuck on this event like we */
 /* did with updates */
 
 checknetwork();
 break;
 }
 
/* return boolean to tell modal dialog to */
/* handle the event or not */
 
 return (handled_event);
 }

/*---------------------- */

void ShowAboutMeDialog(void)
/* my about  dialog */
 
 {
 short  itemHit;

 itemHit = Alert(rAboutAlert, &Net_filter);

 } /* ShowAboutMeDialog */

/*---------------------- */

void DisableDraftMode(thePrintDialog)

TPPrDlg thePrintDialog;

/* disable the ability to print out draft */
/* mode since we can’t print our bitmaps */
 
 {
 DialogPeek theDialogPtr;
 short  i;
 short  num_items; /* how many in list */
 
 hItemListhItems;/* Handle to DLOG’s list */

 short  item_no;
 short  draft_item_no;
 short  better_item_no;
 short  best_item_no;
 short  the_type;/* vars from GetDitem */
 Handle the_item;
 Rect   the_box;
 
 char   *the_title = “            “;
 
 short  the_and_type;
 
 char   *draft_title, *better_title;
 char   *best_title;
 
 Booleandraft_selected;
 short  draft_value;
 
 /* we are given a DialogPtr which is a ptr */
 /* to the print dialog */
 /* scan through all the items, and if you */
 /* get one called draft, disable it */
 
 /* how many items are there to scan */
 
 theDialogPtr = (DialogPeek)thePrintDialog;

 hItems = (hItemList) (theDialogPtr->items);
 num_items = (*hItems)->dlgMaxIndex + 1;

 draft_title = “Draft”;
 better_title = “Faster”;
 best_title = “Best”;
 draft_selected = false;
 
 for (i = 1; i <= num_items; i++ )
 {
 /* get the item */
 
 item_no = i;
 
 GetDItem( (DialogPtr) theDialogPtr, item_no, &the_type, &the_item, &the_box);
 
 /* used to bit and with 7F */
 the_and_type = the_type & 0X7F;
 switch ( the_and_type )  { 
 /* is it a radio button */
 case ctrlItem + radCtrl:
 getctitle( (ControlHandle) the_item, the_title);
 if ((*the_title) == (*draft_title))
 {
 draft_value = GetCtlValue((ControlHandle) the_item);
 if ( draft_value > 0 ) 
 {
 draft_selected = true;
 draft_value = 0;
 SetCtlValue((ControlHandle) the_item, draft_value);
 }
 HiliteControl( (ControlHandle) the_item, 255);
 /*comment the previous line and uncomment */
 /*the next two lines of code to see the */
 /*difference between disabling item */
 /*and inactivating control */

 /* the_and_type = the_type | 0X80; */
 /* SetDItem( (DialogPtr) theDialogPtr, item_no, the_and_type, the_item, 
&the_box); */
   draft_item_no = item_no;
  }
 else
 if ((*the_title) == (*better_title) ) 
   better_item_no = item_no;
 else
 if ((*the_title) == (*best_title) ) 
  best_item_no = item_no;
 
 break;
 } /* end of switch */
 
 } /* of for loop */
 
 } /* of disabling the printing in draft mode */
 
/*---------------------- */

pascal TPPrDlg MyJobDlgInit(hPrint)
 
THPrint hPrint;
 
 {
 /* call PrJobInit to get pointer to the */
 /* invisible job dialog */
 PrtJobDialog = PrJobInit(hPrintRec);
 if (PrError() != noErr)
 return nil;

 /* set up the filter function */
 PrtJobDialog->pFltrProc = (ModalFilterProcPtr)&Net_filter;
 
 /* disable the draft mod now */
 
 DisableDraftMode(PrtJobDialog);

 /* PrDlgMain expects pointer to modified */
 /* dialog to be returned.... */
 
 return ( PrtJobDialog );
 
 } /* myJobDlgInit */

/*----------------------*/

OSErr DoPrintDialog()

 {
 hPrintRec = (THPrint) NewHandle(sizeof(TPrint));
 PrintDefault(hPrintRec);
 PrValidate(hPrintRec);
 if (PrError() != noErr)
 return PrError();

 /* Here’s the line that does it all! */
 if (!PrDlgMain(hPrintRec,&MyJobDlgInit))
 return 2;

 if (PrError() != noErr)
 return PrError();

 /* that’s all for now */
}
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

TotalFinder 1.12.2 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Features... Read more
Duet 2.3.0.3 - 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 $9.99 iOS companion app. Version 2.3.0.3:... Read more
FileMaker Pro Advanced 18.0.3 - Powerful...
FileMaker Pro Advanced is the tool you use to create a custom app. You also use FileMaker Pro Advanced to access your app on a computer. Start by importing data from a spreadsheet or using a built-in... Read more
OsiriX Lite 10.0.6 - 3D medical image pr...
OsiriX Lite is an image processing software dedicated to DICOM images (".dcm" / ".DCM" extension) produced by medical equipment (MRI, CT, PET, PET-CT, ...) and confocal microscopy (LSM and BioRAD-PIC... Read more
Ableton Live 10.1.5 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
Burn 2.7.8 - 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
Malwarebytes 4.0.30.3073 - Adware remova...
Malwarebytes (was AdwareMedic) helps you get your Mac experience back. Malwarebytes scans for and removes code that degrades system performance or attacks your system. Making your Mac once again your... Read more
Acorn 6.5.3 - Bitmap image editor.
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Fantastical 2.5.13 - Create calendar eve...
Fantastical is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event... Read more
A Better Finder Rename 11.05 - 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

Latest Forum Discussions

See All

Eternal Warfare is a new idle clicker fo...
Idle games are a popular genre on mobile, they might not be to everyone's taste but they're made with such regularity and receive a lot of downloads, so it's hard to argue it's not big business. Eternal Warfare is set to join the sea of idle games... | Read more »
New heroes and balance updates set to ar...
It feels like Hearthstone: Battlegrounds only launched yesterday, and already the auto batter addition to Blizzard's megahit card game is set to receive new heroes and balance updates. [Read more] | Read more »
Pre-register for Hello Kitty AR: Kawaii...
Hello Kitty — the cute cat that launched a multi-billion-pound franchise — has been brought to life… sort of. Sanrio has teamed up with the Bublar Group to create a new mobile game that uses AR tech to turn the real world into Hello Kitty’s... | Read more »
Gorgeous and tranquil puzzler Spring Fal...
One-man indie studio SPARSE//GameDev has now launched its tranquil puzzler, Spring Falls. It's described as "a peaceful puzzle game about water, erosion, and watching things grow". [Read more] | Read more »
Black Desert Mobile gets an official rel...
Pearl Abyss has just announced that its highly-anticipated MMO, Black Desert Mobile, will launch globally for iOS and Android on December 11th. [Read more] | Read more »
Another Eden receives new a episode, cha...
Another Eden, WFS' popular RPG, has received another update that brings new story content to the game alongside a few new heroes to discover. [Read more] | Read more »
Overdox guide - Tips and tricks for begi...
Overdox is a clever battle royale that changes things up by adding MOBA mechanics and melee combat to the mix. This new hybrid game can be quite a bit to take in at first, so we’ve put together a list of tips to help you get a leg up on the... | Read more »
Roterra Extreme - Great Escape is a pers...
Roterra Extreme – Great Escape has been described by developers Dig-It Games as a mini-sequel to their acclaimed title Roterra: Flip the Fairytale. It continues that game's tradition of messing with which way is up, tasking you with solving... | Read more »
Hearthstone: Battlegrounds open beta lau...
Remember earlier this year when auto battlers were the latest hotness? We had Auto Chess, DOTA Underlords, Chess Rush, and more all gunning for our attention. They all had their own reasons to play, but, at least from where I'm standing, most... | Read more »
The House of Da Vinci 2 gets a new gamep...
The House of Da Vinci launched all the way back in 2017. Now, developer Blue Brain Games is gearing up to deliver a second dose of The Room-inspired puzzling. Some fresh details have now emerged, alongside the game's first official trailer. [Read... | Read more »

Price Scanner via MacPrices.net

B&H offers $100 discounts on 4-Core and 6...
B&H Photo has new 4-Core and 6-Core Mac minis in stock and on sale today for $100 off Apple’s MSRP. Prices start at $699. Overnight shipping is free to many addresses in the US: – 3.6GHz Quad-... Read more
Save $200 today on a 2019 13″ MacBook Air wit...
Apple has a full line of Certified Refurbished 2019 13″ MacBook Airs available starting at only $929 and up to $200 off the cost of new Airs. Each MacBook features a new outer case, comes with a... Read more
New Verizon Pre-Black Friday 2019 deal: Buy o...
Buy one new Apple iPhone 11 model or 2018 iPhone XS model at Verizon and get a second one for free. One new line of service required. Offer is valid from November 21, 2019 to November 27, 2019. Here... Read more
AirPods with Wireless Charging Case on sale t...
Abt Electronics has 2019 AirPods with the Wireless Charging Case on sale today for $163 shipped. Their price is $36 off Apple’s MSRP, and it’s currently the cheapest price for these AirPods from any... Read more
Apple continues to offer 2017 13″ Dual-Core n...
Apple has Certified Refurbished 2017 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros still available starting at $1019. An standard Apple one-year warranty is included with each model, outer cases... Read more
Save up to $120 on the new 16″ MacBook Pro at...
Apple’s resellers are starting to receive stock of new 16″ MacBook Pros, and the first set of sales & deals are now available: (1) Amazon 16″ MacBook Pros start on sale for $100-$116 off Apple’s... Read more
Apple Watch Series 3 models on sale at Amazon...
Amazon has Apple Watch Series 3 GPS models on sale for $30 off MSRP, starting at only $169. There prices are the lowest we’ve ever seen for these models from any Apple reseller. Choose Amazon as the... Read more
The ‘Mac Potpourri’ Mailbag: Edition #1- Info...
COMMENTARY: 11.20.19- Welcome to the inaugural edition of the “Mac Potpourri” Mailbag where we take a look at correspondence received from readers of this column from all over the world who write in... Read more
13″ 2.4GHz MacBook Pros available for up to $...
Apple has a full line of Certified Refurbished 2019 13″ 2.4GHz 4-Core Touch Bar MacBook Pros available starting at $1529 and up to $300 off MSRP. Apple’s one-year warranty is included, shipping is... Read more
New at T-Mobile: Switch to T-Mobile, and get...
T-Mobile is offering a free 64GB iPhone 8 for new customers who switch to T-Mobile and open a new line of service. Eligible trade-in required, and discount applied over a 24 month period. The fine... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**747303BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Store NUmber or Department:** 001413-Cypress-Store **Job Description:** **What Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**743221BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Store NUmber or Department:** 000230-Greenwood-Store **Job Description:** At Best Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**747338BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Store NUmber or Department:** 000254-Superstition Springs-Store **Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**745516BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Store NUmber or Department:** 001101-Manhattan-Store **Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**746655BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Store NUmber or Department:** 002518-Atlantic Center-Store **Job Description:** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.