TweetFollow Us on Twitter

Meters
Volume Number:7
Issue Number:9
Column Tag:Pascal Forum

Related Info: Quickdraw

Meter Windows

By Walt Davis, Raleigh, NC

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

[Walt is an electrical engineer who, until recently, worked for a NASA contractor based in the Washington, DC area. He developed Mac software for NASA that simulates the flow of high-speed data from space-based instruments and platforms through various space- and ground-based networks to users on the ground. He currently works for Alcatel Network Systems in Raleigh, North Carolina helping Alcatel develop fiber-optic telecommunications systems that will someday bring fiber into your home and to your Mac.]

Is the Meter Running?

No news is not necessarily good news. Especially when your application is performing a time consuming task (e.g. lengthy I/O or number crunching) and you’ve neglected to implement a mechanism to provide feedback on the task’s progress. As a conscientious programmer you’ve changed the arrow cursor to the watch cursor or maybe even harnessed the VBL interrupt to show a rotating watch cursor during these lengthy pauses, but still it’s not enough. The user really has no idea how far into the task the application has progressed or how much longer until the task is completed. Forcing the user to rely on the familiar sound of the disk-drive access or the sight of the disk-access light blinking during long pauses when such a rich graphical interface is available is either laziness, sloppiness, or rudeness on the part of the programmer. It is the programmer’s responsibility to see to it that the user always has a warm, fuzzy feeling about what the application is doing at any particular time. It is the programmer’s responsibility to create the illusion that the user is in control at all times.

Actually, I’ve evolved to this position over time due to my experiences as a user of my own software. I’ve written time consuming applications such as discrete-event simulations and fast Fourier transforms where a simple watch cursor just won’t cut it. And now that Apple has a full line of Macs with different processor and coprocessor configurations, applications that take a few seconds on one Mac platform may take several minutes on another Mac platform. With all this uncertainty, developers can be certain of one thing: your software will be used in ways and in environments that you’ve never considered (if you’re lucky!). You may not be able to test your software in all these environments, but you can add some simple features to your software that give it a consistent feel across these environments.

Meet the Meter

For these reasons, I’ve developed a set of procedures that support what I call the Meter Window (MW). The MW functions as a visual feedback mechanism for the progress of a task. The MW simply displays the title of the task and fills in a horizontal box as the task is completed. It is easy to expand on this idea of visual feedback and develop much more elaborate feedback graphics, but the primary goal of the MW is to provide as much feedback as possible with the minimum increase in the completion time of the task at hand. The MW is ideal for long repetitive loop processes but it can be adapted for any long process that can be decomposed into steps. The MW is also helpful during development and software testing to show how far into a task a problem occurs. In a multi-step task, refreshing the MW at each step results in a crude form of code profiler that shows the relative length of each step and helps identify which steps are good targets for code optimization.

Meter Nuts and Bolts

The enclosed code, written in Think Pascal version 2.0, is a unit used to initialize, display, update, and destroy the MW as well as code for a unit that creates a simple example application that demonstrates the MW operation. The self-contained MW unit provides full MW functionality for any Think Pascal project through five simple procedure calls: mWindowInit, mWindowDraw, mWindowTitle, mWindowUpdate, and mWindowKill. The mWindowInit procedure creates the MW data structures and draws the empty window in the center of the screen. It works with different size monitors because it uses the the QuickDraw global screenbits.bounds rectangle to determine the size of the screen. The mWindowDraw procedure draws the empty meter box with 5% graduations as well as the other meter box annotation (you can customize the graduation scale for your own application). The mWindowTitle procedure accepts a Str255 parameter, mStr, and draws the mStr parameter as the MW title. You can notify the user which part of the task or subtask is currently active through the MW title by repeatedly calling mWindowTitle procedure with a different mStr parameter. The mWindowUpdate procedure fills in the meter box from left to right based on the values of two integer parameters; curI and maxI. The curI parameter corresponds to the current step in a process with maxI steps. For example, mWindowUpdate(0,50) draws an empty meter box; mWindowUpdate(25,50) draws a half-filled meter box; and mWindowUpdate(50,50) draws a full meter box. mWindowKill destroys the MW and all associated data structures.

Meter Madness

To use the MW, an application need only include the MW unit in the Think Pascal project and make the appropriate MW procedure calls. The application, through the MW unit interface, is responsible for initializing the MW module, determining when to draw the MW, displaying the proper MW title, updating the MW as the task progresses, and destroying the MW when the task is complete.

The code included with this article consists of two units; one (MeterWindow) contains the procedures necessary to implement the MW, the other (MWMain) contains a small event shell and a procedure that illustrates the MW unit interface and demonstrates the MW functionality. Figure 1 shows the Think Pascal project (MWDemoProject) necessary to build the MW demo.

Figure 1: MW Demo Project

The event shell first creates the menu and the Meter menu and then handles the menu item selections in a small event loop. This event loop supports Desk Accessory items in the menu and the ‘Run Dumb’, ‘Run Smart’, and ‘Quit’ items in the Meter menu.

The ‘Run Dumb’ item performs the same loop processing task as the ‘Run Smart’ item but with the MW operation disabled. The MW is disabled to illustrate the effect of the long loop processing without the MW feedback. Except for the watch cursor, the user is clueless as to which process is currently active and how long until the process is finished. Run the demo and compare the execution times between the ‘Run Dumb’ and ‘Run Smart’ items to see how little it costs in execution time to implement the MW.

Selecting the ‘Run Smart’ item runs the same loop processing task with the MW enabled. The DoMeterWindowDemo procedure handles the MW unit interface for the demo. The first thing the DoMeterWindowDemo procedure does is test to see which processor is active. The SysEnvirons toolbox call returns information about the current hardware and software platform in the dWorld parameter. This information includes, besides the processor type, whether Color QuickDraw is present, whether a math-coprocessor is present, and which version of the System software is running. The SysEnvirons call is a convenient way for the software to determine the current operating environment and, if necessary, adjust accordingly. In this demo the loop lengths are adjusted based on the processor type. This is needed because the MW demo loop on a 68000 based Mac will only be a blur on the screen of a 68020 or 68030 based Mac.

Demo Details

The DoMeterWindowDemo procedure uses two local variables (meterHit, m) and a constant (meterGrade) to control the MW display. The meterGrade constant corresponds to the graduations on the MW meter box; i.e. 5% equals 20 graduations. The meterHit variable contains the number of loops that correspond to one graduation (5%) of the loop processing and is computed by dividing the total number of loop iterations, loopsize, by the meterGrade value. The m variable is used as a step counter in the loop and is compared with the meterHit variable every loop to determine when another 5% is complete.

This demo consists of three nested loops; an n loop, an i loop, and a j loop. The outer most loop, n, loops 5 times, each time redrawing the MW with a new title. The middle loop, i, loops loopsize (say that 5 times fast) times for each of the n loops and is used to fill the meter box in 5% graduations. The inner most loop, j, is a dummy loop and used only as a delay to update the meter box. The number of j loops, innerloop, is adjusted based on the current processor type.

The mWindowInit procedure is called prior to entering the first n loop. In each of the 5 n loops the MW is redrawn (mWindowDraw) and the MW title is updated (mWindowTitle(iStr)). The step counter variable, m, is initialized prior to entering the i loop and, for each iteration of the i loop, is compared with the meterHit variable to determine if another 5% of the loop has been completed. If it has, another portion of the meter box is filled in (mWindowUpdate(i,loopsize)) and the step counter reset to 1. Following the completion of all loops, the mWindowKill procedure is called to destroy any MW data structures.

Happy Metering

In conclusion, the MW is a simple example of a graphic feedback mechanism that adds a professional touch to software that seems to “go away” during long internal processes. It allows you to show the user that you’re not hiding anything, even if you are.

Listing:  MeterWindow

{------------------------------------------------}
{MeterWindow Unit.                            }
{This unit contains all the functions and       }
{procedures needed to support the   initializing, }
{displaying, updating, and destroying of the    }
{Meter Window.                                  }

unit MeterWindow;
interface
 procedure mWindowInit;
 procedure mWindowDraw;
 procedure mWindowTitle (mStr: Str255);
 procedure mWindowUpdate (curI, maxI: longint);
 procedure mWindowKill;

implementation
{Constants to control the size and placement of }
{the Meter Window title and meter box.          }
 const
  mRx = 13;
  mRy = 24;
  mRw = 250;
  mRh = 16;

  tRx = 13;
  tRy = 1;
  tRw = 253;
  tRh = 16;

 var
  mWPtr: WindowPtr;

{------------------------------------------------}
{mWindowInit procedure                        }
{This procedure initializes the meter window and}
{draw it in the middle of the screen.           }

 procedure mWindowInit;

{The constants for the height and width of the  }
{Meter Window.                                  }
  const
   mWwidth = 280;
   mWheight = 60;

  var
   savePort: GrafPtr;
   iRect: Rect;
   mwXOffset, mwYOffset: integer;

 begin
  SetRect(iRect, 0, 0, mWwidth, mWheight);

{Create the Meter Window with a window          }
{definition ID equal to 1.                      }
  mWPtr := NewWindow(nil, iRect, ‘’, false, 1, Pointer(-1), false, longint(0));

{Figure out the x and y offsets in pixels to put}
{the Meter Window in the center of this screen. }
{After that move the window there and display   }
{the empty window.                              }
  iRect := screenBits.bounds;
  mwXOffset := integer(round(((iRect.right - iRect.left) - (mWptr^.portRect.right 
- mWptr^.portRect.left)) / 2));
  mwYOffset := integer(round(((iRect.bottom - iRect.top) - (mWptr^.portRect.bottom 
- mWptr^.portRect.top)) / 2));
  MoveWindow(mWPtr, mwXOffset, mwYOffset, false);
  ShowWindow(mWPtr);
 end;

{------------------------------------------------}
{mWindowDraw procedure                        }
{This procedure draws the meter box with its    }
{graduations and the corresponding annotation.  }

 procedure mWindowDraw;

{These constatns control the placement of the 5%}
{and 10% graduations in the meter box based on a}
{meter box that is 250 pixels wide.             }
  const
   fiveSize = 1;
   fiveStep = 12;
   tenSize = 3;
   tenStep = 25;

  var
   i: integer;
   mRect: Rect;
   savePort: GrafPtr;

 begin
  GetPort(savePort);
  SetPort(mWPtr);

{Erase the Meter Window in case something is    }
{already drawn there.                           }
  EraseRect(mWPtr^.portRect);

{Set up the meter box in the mRect rectangle.}
  SetRect(mRect, mRx, mRy, mRx + mRw, mRy + mRh);
  TextFont(0);
  TextSize(12);
  PenSize(1, 1);

{Draw the frame around the meter box rectangle.}
  InsetRect(mRect, -1, -1);
  FrameRect(mRect);
  InsetRect(mRect, 1, 1);

{Draw the first 5% graduation then let the      }
{following loop do the rest.                    }
  MoveTo(mRect.left + fiveStep, mRect.top - 1);
  Line(0, fiveSize);
  MoveTo(mRect.left + fiveStep, mRect.bottom);
  Line(0, -fiveSize);
  for i := 1 to 9 do
   begin
    MoveTo(mRect.left + (i * tenStep) - 1, mRect.top - 1);
    Line(0, tenSize);
    MoveTo(mRect.left + (i * tenStep) - 1, mRect.bottom);
    Line(0, -tenSize);
    MoveTo(mRect.left + (i * tenStep) + fiveStep, mRect.top - 1);
    Line(0, fiveSize);
    MoveTo(mRect.left + (i * tenStep) + fiveStep, mRect.bottom);
    Line(0, -fiveSize);
   end;

{Now draw the appropriate meter box annotation.}
  MoveTo(mRect.left + 65, mRect.bottom + mRh);
  DrawString(‘Percent Complete’);
  MoveTo(mRect.left - 5, mRect.bottom + mRh);
  DrawString(‘0%’);
  MoveTo(mRect.right - 20, mRect.bottom + mRh);
  DrawString(‘100%’);
  SetPort(savePort);
 end;

{------------------------------------------------}
{mWindowTitle procedure                       }
{This procedure draws the title of the meter    }
{window in the tRect rectangle.                 }

 procedure mWindowTitle;

  var
   savePort: GrafPtr;
   tRect: Rect;

 begin
  GetPort(savePort);
  SetPort(mWPtr);

{Set up the title rectangle in the tRect        }
{structure, erase the rectangle, then draw the  }
{title string in the rectangle.                 }
  SetRect(tRect, tRx, tRy, tRx + tRw, tRy + tRh);
  EraseRect(tRect);
  MoveTo(tRect.left, tRect.bottom);
  DrawString(mStr);
  SetPort(savePort);
 end;

{------------------------------------------------}
{mWindowUpdate procedure                      }
{This procedure fills in the meter box based on }
{the curI and maxI parameters.  CurI is the     }
{current step in the process with maxI steps.   }

 procedure mWindowUpdate;

  var
   pDone: integer;
   mRect: Rect;
   savePort: GrafPtr;

 begin
  GetPort(savePort);
  SetPort(mWPtr);

{Determine which percent of the process is done }
{and load it in pDone.                          }
  if maxI = 0 then
   pDone := 0
  else
   pDone := integer(round(curI / maxI * 100));

{Just in case the percent done is greater than 100%.}
  if pDone > 100 then
   pDone := 100;
  SetRect(mRect, mRx, mRy, mRx + mRw, mRy + mRh);

{Since the meter box is 250 pixels wide, each   }
{percent corresponds to 2.5 pixels.             }
  mRect.right := mRect.left + integer(round((pDone * 2.5)));
  FillRect(mRect, black);
  SetPort(savePort);
 end;

{------------------------------------------------}
{mWindowKill procedure                        }
{This procedure disposes of the Meter Window    } 
{data structure.                                }

 procedure mWindowKill;

 begin
  DisposeWindow(mWPtr);
 end;
end.

{------------------------------------------------}
{MWMain Unit.                                 }
{This unit contains the event loop and the loop }
{procedure for the MacTutor demo.               }
Listing:  MWMain

program MWMain;

 uses
  MeterWindow;

{------------------------------------------------}
{DoMeterWindowDemo procedure                  }
{This procedure demonstrates the Meter Window   }
{interface.  It uses three nested loops to      } 
{create a dummy task that is slow enough to show}
{the Meter Window functionality.  This procedure}
{accepts one parameter, runSmart.  When runSmart}
{is true, the Meter Window is displayed during  }
{the loop execution; if runSmart is false the   }
{then loop is executed but without creating the }
{Meter Window.                                  }

 procedure DoMeterWindowDemo (runSmart: boolean);

  const
   outLoop = 5;
   loopsize = 500;
   meterGrade = 20;

  var
   i, j, m, n: integer;
   myCursor: CursHandle;
   meterhit, k, innerloop: longint;
   dWorld: SysEnvRec;
   rnum: OSErr;
   vReq: integer;
   iStr: Str255;

 begin
{First determine which processor is in this Mac }
{and then adjust the innerloop parameter        }
{accordingly.  This is done so that the loop    }
{will not execute too quickly on high-powered Macs. }
  vReq := 1;
  rnum := SysEnvirons(vReq, dWorld);
  if rnum = 0 then
   begin
    if dWorld.processor = env68000 then
     innerloop := loopsize
    else if dWorld.processor = env68010 then
     innerloop := loopsize
    else if dWorld.processor = env68020 then
     innerloop := 20 * loopsize
    else
     innerloop := 20 * loopsize;
   end
  else
   innerloop := loopsize;

{Figure out how many loops equal 5% of the total loop.}
  meterHit := longint(round(loopsize / meterGrade));

  myCursor := GetCursor(WatchCursor);
  SetCursor(myCursor^^);

{This is always the first call to the           }
{MeterWindow unit.  It initializes and displays }
{the Window Meter.                              }
  if runSmart then
   mWindowInit;

  for n := 1 to outLoop do
   begin
{For each of the n loops, create a new Meter Window title. }
    NumToString(n, iStr);
    iStr := concat(‘MacTutor Demo - Loop ‘, iStr);
    iStr := concat(iStr, ‘ of 5.’);

{Now eraes the Meter Window and redraw it with a}
{new title.                                     }
    if runSmart then
     begin
        mWindowDraw;
        mWindowTitle(iStr);
     end;
    m := 1;
    for i := 1 to loopsize do
     begin
{When m = meterHit the another 5% of the total  }
{loop has been completed and it is time to      }
{update the meter box in the Meter Window.      }
     if m = meterHit then
        begin
        m := 1;
        if runSmart then
        mWindowUpdate(i, loopsize);
        end
     else
        m := m + 1;
     k := 0;

{This is the dummy innerloop that is adjusted   }
{based on the current processor.                }
     for j := 1 to innerloop do
        k := k + 1;
     end;
   end;

{All done!  Now destroy the Meter Window.}
  if runSmart then
   mWindowKill;
  InitCursor;
 end;

{------------------------------------------------}
{This is the main procedure for this demo.  It  }
{sets up the Apple and Meter menus and handles  }
{the selection of items from these menus.       }

 const
  AppleID = 1;
  MeterID = 2;
  AboutItem = 1;
  RunDItem = 1;
  RunSItem = 2;
  QuitItem = 3;

 var
  appleMenu, meterMenu: MenuHandle;
  myTitle: string[1];
  daName: Str255;
  dEvent: EventRecord;
  TimeToQuit: boolean;
  dPart, dItem, dMenu, daNum: integer;
  dWindow: WindowPtr;
  dChoice: longint;

begin
 InitCursor;

{Set up the menus.}
 myTitle := ‘ ‘;
 myTitle[1] := CHR(appleMark);
 appleMenu := NewMenu(AppleID, myTitle);
 AddResMenu(appleMenu, ‘DRVR’);
 InsertMenu(appleMenu, 0);
 meterMenu := NewMenu(MeterID, ‘Meter’);
 AppendMenu(meterMenu, ‘Run Dumb’);
 AppendMenu(meterMenu, ‘Run Smart’);
 AppendMenu(meterMenu, ‘Quit’);
 InsertMenu(meterMenu, 0);
 DrawMenuBar;

 TimeToQuit := false;
{The TimeToQuit variable is set to true when the}
{Quit item is selected from the Meter menu.     }
 while not TimeToQuit do
  begin
   SystemTask;
   if GetNextEvent(everyEvent, dEvent) then
    begin

{Only handle mousedown events in this simple demo.}
     case dEvent.what of
        MouseDown: 
         begin
        dPart := FindWindow(dEvent.where, dWindow);
        case dPart of

{The SysWindow mousedown is for DA’s            } 
      InSysWindow: 
        SystemClick(dEvent, dWindow);
        InMenuBar: 
        begin

{Figure out which menu item was selected.}
        dChoice := MenuSelect(dEvent.where);
        dItem := LoWord(dChoice);
        dMenu := HiWord(dChoice);
        case dMenu of
        AppleID: 
         begin

{If an Apple menu item was selected, then do the item.}
        GetItem(appleMenu, dItem, daName);
        daNum := OpenDeskAcc(daName);
        end;
        MeterID: 
        begin
        case dItem of

{The Run Dumb item was selected.  Do the loop   }
{processing without displaying the Meter Window.}
        RunDItem:
        DoMeterWindowDemo(false);

{Do the loop processing and display the Meter Window. }
        RunSItem:
        DoMeterWindowDemo(true);

{Quit and go home.}
        QuitItem:
        TimeToQuit := true;
        otherwise
        end;
        end;
        otherwise
        end;
        HiliteMenu(0);
        end;
        otherwise
        end;
        end;
      otherwise
        end;
    end;
  end;
end.
 
AAPL
$105.22
Apple Inc.
+0.39
MSFT
$46.13
Microsoft Corpora
+1.11
GOOG
$539.78
Google Inc.
-4.20

MacTech Search:
Community Search:

Software Updates via MacUpdate

OS X Server 4.0 - For OS X 10.10 Yosemit...
Designed for OS X and iOS devices, OS X Server makes it easy to share files, schedule meetings, synchronize contacts, develop software, host your own website, publish wikis, configure Mac, iPhone,... Read more
TotalFinder 1.6.12 - 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. Tab-based... Read more
BusyCal 2.6.3 - Powerful calendar app wi...
BusyCal is an award-winning desktop calendar that combines personal productivity features for individuals with powerful calendar sharing capabilities for families and workgroups. BusyCal's unique... Read more
calibre 2.7 - Complete e-library managem...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
Skitch 2.7.3 - Take screenshots, annotat...
With Skitch, taking, annotating, and sharing screenshots or images is as fun as it is simple.Communicate and collaborate with images using Skitch and its intuitive, engaging drawing and annotating... Read more
Delicious Library 3.3.2 - Import, browse...
Delicious Library allows you to import, browse, and share all your books, movies, music, and video games with Delicious Library. Run your very own library from your home or office using our... Read more
Art Text 2.4.8 - Create high quality hea...
Art Text is an OS X application for creating high quality textual graphics, headings, logos, icons, Web site elements, and buttons. Thanks to multi-layer support, creating complex graphics is no... Read more
Live Interior 3D Pro 2.9.6 - Powerful an...
Live Interior 3D Pro is a powerful yet very intuitive interior designing application. View Video Tutorials It has every feature of Live Interior 3D Standard, plus some exclusive ones: Create multi... Read more
The Hit List 1.1.7 - Advanced reminder a...
The Hit List manages the daily chaos of your modern life. It's easy to learn - it's as easy as making lists. And it's powerful enough to let you plan, then forget, then act when the time is right.... Read more
jAlbum Pro 12.2.4 - Organize your digita...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code!... Read more

Latest Forum Discussions

See All

Rami Ismail Opens Up distribute​() for D...
Rami Ismail Opens Up distribute​() for Developers Posted by Jessica Fisher on October 24th, 2014 [ permalink ] Rami Ismail, Chief Executive of Business and Development at indie game studio | Read more »
Great Hitman GO Goes on Sale and Gets Ne...
Great Hitman GO Goes on Sale and Gets New Update – Say That Three Times Fast Posted by Jessica Fisher on October 24th, 2014 [ permalink ] | Read more »
Rival Stars Basketball Review
Rival Stars Basketball Review By Jennifer Allen on October 24th, 2014 Our Rating: :: RESTRICTIVE BUT FUNUniversal App - Designed for iPhone and iPad Rival Stars Basketball is a fun mixture of basketball and card collecting but its... | Read more »
Rubicon Development Makes Over a Dozen o...
Rubicon Development Makes Over a Dozen of Their Games Free For This Weekend Only Posted by Jessica Fisher on October 24th, 2014 [ permalink ] | Read more »
I Am Dolphin Review
I Am Dolphin Review By Jennifer Allen on October 24th, 2014 Our Rating: :: NEARLY FIN-TASTICUniversal App - Designed for iPhone and iPad Swim around and eat nearly everything that moves in I Am Dolphin, a fun Ecco-ish kind of game... | Read more »
nPlayer looks to be the ultimate choice...
Developed by Newin Inc, nPlayer may seem like your standard video player – but is aiming to be the best in its field by providing high quality video play performance and support for a huge number of video formats and codecs. User reviews include... | Read more »
Fighting Fantasy: Caverns of the Snow Wi...
Fighting Fantasy: Caverns of the Snow Witch Review By Jennifer Allen on October 24th, 2014 Our Rating: :: CLASSY STORYTELLINGUniversal App - Designed for iPhone and iPad Fighting Fantasy: Caverns of the Snow Witch is a sterling... | Read more »
A Few Days Left (Games)
A Few Days Left 1.01 Device: iOS Universal Category: Games Price: $3.99, Version: 1.01 (iTunes) Description: Screenshots are in compliance to App Store's 4+ age rating! Please see App Preview for real game play! **Important: Make... | Read more »
Toca Boo (Education)
Toca Boo 1.0.2 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0.2 (iTunes) Description: BOO! Did I scare you!? My name is Bonnie and my family loves to spook! Do you want to scare them back? Follow me and I'll... | Read more »
Intuon (Games)
Intuon 1.1 Device: iOS Universal Category: Games Price: $.99, Version: 1.1 (iTunes) Description: Join the battle with your intuition in a new hardcore game Intuon! How well do you trust your intuition? Can you find a needle in a... | Read more »

Price Scanner via MacPrices.net

Weekend sale: 13-inch 128GB MacBook Air for $...
Best Buy has the 2014 13-inch 1.4GHz 128GB MacBook Air on sale for $849.99, or $150 off MSRP, on their online store. Choose free home shipping or free local store pickup (if available). Price valid... Read more
Nimbus Note Cross=Platform Notes Utility
Nimbus Note will make sure you never forget or lose your valuable data again. Create and edit notes, save web pages, screenshots and any other type of data – and share it all with your friends and... Read more
NewerTech’s Snuglet Makes MagSafe 2 Power Con...
NewerTech has introduced the Snuglet, a precision-manufactured ring designed to sit inside your MagSafe 2 connector port, providing a more snug fit to prevent your power cable from unintentional... Read more
Apple Planning To Sacrifice Gross Margins To...
Digitimes Research’s Jim Hsiao says its analysts believe Apple is planning to sacrifice its gross margins to save its tablet business, which has recently fallen into decline. They project that Apple’... Read more
Who’s On Now? – First Instant-Connect Search...
It’s nighttime and your car has broken down on the side of the highway. You need a tow truck right away, so you open an app on your iPhone, search for the closest tow truck and send an instant... Read more
13-inch 2.5GHz MacBook Pro on sale for $949,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $949.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $150 off MSRP. Price is... Read more
Save up to $125 on Retina MacBook Pros
B&H Photo has the new 2014 13″ and 15″ Retina MacBook Pros on sale for up to $125 off MSRP. Shipping is free, and B&H charges NY sales tax only. They’ll also include free copies of Parallels... Read more
Apple refurbished Time Capsules available sta...
The Apple Store has certified refurbished Time Capsules available for up to $60 off MSRP. Apple’s one-year warranty is included with each Time Capsule, and shipping is free: - 2TB Time Capsule: $255... Read more
Textilus New Word, Notes and PDF Processor fo...
Textilus is new word-crunching, notes, and PDF processor designed exclusively for the iPad. I haven’t had time to thoroughly check it out yet, but it looks great and early reviews are positive.... Read more
WD My Passport Pro Bus-Powered Thunderbolt RA...
WD’s My Passport Pro RAID solution is powered by an integrated Thunderbolt cable for true portability and speeds as high as 233 MB/s. HighlightsOverviewSpecifications Transfer, Back Up And Edit In... Read more

Jobs Board

*Apple* Solutions Consultant - Apple Inc. (U...
…important role that the ASC serves is that of providing an excellent Apple Customer Experience. Responsibilities include: * Promoting Apple products and solutions Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.