TweetFollow Us on Twitter

Sprocket Menus 4
Volume Number:11
Issue Number:8
Column Tag:Getting Started

Sprocket Menus, Part 4

By Dave Mark, MacTech Magazine Regular Contributing Author

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

Last month, we added code to our SprocketPicText project to implement the Style and most of the Size submenus that appear underneath the Text menu when a TTextWindow is the frontmost window. This month, we’re going to finish up the Size menu by implementing the Other... item. We’ll also add code to implement the Font submenu. Finally, we’ll add the code to the TPictureWindow to implement the two items in the Picture window that appears whenever a TPictureWindow is the frontmost window.

A DLOG and a DITL

We’ll start off by adding a single DLOG resource and its associated DITL resource to SprocketStarter.rsrc. The two resources will implement a dialog that prompts the user to type in a new font size. The dialog will be brought up when the user selects Other... from the Size menu.

• Start by duplicating last month’s SprocketStarter folder (be sure you remove objects first if you care about disk space).

Don’t duplicate the Sprocket folder, since it hasn’t changed from last month. Last month’s Sprocket-Starter folder was named “SprocketPicText.04/25/95”. Name the new copy “SprocketPicText.05/31/95”.

Open up your new Sprocket-Starter folder and launch either the 68K or PowerMac SprocketStarter project. When your project window appears, double-click on the file named SprocketStarter.rsrc to launch your favorite resource editor.

• Create a new DLOG with a resource ID of 1000, a top of 71, left of 97, bottom of 146, and a right of 363. Be sure you select the modal dialog window type.

• Create a DITL with a resource ID of 1000.

• Create a pushbutton (item#1) with the text OK, a top of 42, left of 184, bottom of 62, and a right of 253.

• Create a pushbutton (item#2) with the text Cancel, a top of 42, left of 99, bottom of 62, and a right of 168.

• Create a static text item (item#3) with the text Font size:, a top of 13, left of 13, bottom of 29, and a right of 81.

• Create an editable text item (item#4) with no text, a top of 13, left of 91, bottom of 29, and a right of 250.

Figure 1 shows the finished dialog, as displayed by Resorcerer.

Figure 1. The DLOG and DITL resources, as displayed by Resorcerer.

TextWindow.h

Quit your resource editor, saving your changes, and return to CodeWarrior. Now open the file TextWindow.h and make these changes:

• Add these lines to the top of the file, just before the enum:

#include <Fonts.h>

const longkCancelButtonPressed = 0L;
const short iNumberEditTextField = 4;
const short kGetNumberDialogResID = 1000;

• Add these member function declarations to the TTextWindow class:

 virtual long    DoNumberDialog( void );
 virtual void    SetOtherMenuItemString( void );
 virtual void    PascalStringCat( Str255 dest, Str255 source );
 virtual void    AdjustFontMenu( void );
 virtual Boolean DoMenuSelection( short menu, short item );

TextWindow.cp

Close TextWindow.h and open the file TextWindow.cp. Make these changes:

• Add to the file the member function

TTextWindow::DoNumberDialog()

Here’s the source code:

long
TTextWindow::DoNumberDialog( void )
{
 BooleandialogDone = false;
 Str255 text;
 Handle itemHandle;
 short  itemHit, itemType;
 Rect   itemRect;
 long   returnValue;
 DialogPtrdialog;
 
 dialog = GetNewDialog( kGetNumberDialogResID, NULL, 
 (WindowPtr)-1L );
 
 if ( dialog == NULL )
 return kCancelButtonPressed;
 
 SetDialogDefaultItem( dialog, ok );
 SetDialogCancelItem( dialog, cancel );
 
 NumToString( (long)fCurrentFontSize, text );
 
 GetDialogItem( dialog, iNumberEditTextField, &itemType,
 &itemHandle, &itemRect );
 SetDialogItemText( itemHandle, text );
 
 SelectDialogItemText( dialog, iNumberEditTextField, 0, 32767 );
 
 ShowWindow( dialog );
 
 do
 {
 ModalDialog( NULL, &itemHit );
 
 if ( itemHit == ok )
 {
 GetDialogItem( dialog, iNumberEditTextField, &itemType,
 &itemHandle, &itemRect );
 GetDialogItemText( itemHandle, text );
 
 StringToNum( text, &returnValue );
 
 if ( (returnValue < kMinimumFontSize) ||
 (returnValue > kMaximumFontSize) )
 {
 SysBeep( 20 );
 SelectDialogItemText( dialog, iNumberEditTextField,
  0, 32767 );
 itemHit = iNumberEditTextField;
 }
 else
 dialogDone = true;
 }
 else if ( itemHit == cancel )
 {
 returnValue = kCancelButtonPressed;
 dialogDone = true;
 }
 } while ( ! dialogDone );
 
 DisposeDialog( dialog );
 
 return returnValue;
}

This function implements the dialog box whose resources you created earlier. The dialog allows the reader to enter a new font size for the frontmost text window. The user must enter a size between kMinimumFontSize and kMaximumFontSize, otherwise we’ll beep and highlight the editable text field.

We’ll start off by converting the current text size, stored in the data member fCurrentFontSize, into a Str255 and we’ll place the Str255 version of the number in the dialog’s editable text field. We’ll then highlight the text field so if the user wants to type a new value, they don’t have to select the text first to replace it.

Inside the dialog loop, if the user presses the OK button, we’ll convert the text field to a long, then check to be sure it’s in range. If so, we’ll set dialogDone to true so we drop out of the loop.

If they hit the Cancel button, we’ll set the return value to kCancelButtonPressed, which has been pre-defined as 0L, which we know won’t be a legal font size.

Finally, once we drop out of the loop, we’ll dispose of the dialog and return returnValue.

• Add to the file the member function

TTextWindow::PascalStringCat()

Here’s the source code:

void
TTextWindow::PascalStringCat( Str255 dest, Str255 source )
{
 unsigned char i, destStringLength, sourceStringLength;
 
 destStringLength = dest[0];
 sourceStringLength = source[0];
 
 if ( sourceStringLength <= 0 )
 return;
 
 for ( i=1; i<=sourceStringLength; i++ )
 dest[i+destStringLength] = source[i];
 
 dest[0] += sourceStringLength;
}

Much like the function strcat(), this function appends the source pascal string on to the end of thepascal string already in dest. The length byte from source is deleted and the length byte of dest is incremented to reflect its new length.

This function is called by SetOtherMenuItemString() which we’ll add next.

• Add to the file the member function

TTextWindow::SetOtherMenuItemString() .

Here’s the source code:

void
TTextWindow::SetOtherMenuItemString( void )
{
 Str255 menuItemStr = "\pOther (";
 Str255 numberStr;
 
 NumToString( (long)fCurrentFontSize, numberStr );
 
 PascalStringCat( menuItemStr, numberStr );
 PascalStringCat( menuItemStr, "\p)..." );
 
 gMenuBar->SetItemString( cFontSizeOther, menuItemStr );
}

This function changes the Other... item in the Size menu to reflect the current text size. For example, if the current size is 37, the menu item should read Other (37).... Note that the Size menu we’ve implemented here is not my invention. It comes from the pages of Inside Macintosh and represents the standard you should use in your own applications.

SetOtherMenuItemString() starts off by defining a Str255 named menuItemStr with the text “Other (”. We’ll then convert the current size to a pascal string and append it to menuItemStr. Finally, we’ll append the pascal string “)...” to menuItemStr then call the TMenuBar member function SetItemString() to change the item associated with the command cFontSizeOther to menuItemStr.

• Add a call of SetOtherMenuItemString() to the end of the member function AdjustSizeMenu(). AdjustSizeMenu() gets called when a mousedown occurs in the menu bar.

• Add to the file the member function

TTextWindow::DoMenuSelection()

Here’s the source code:

Boolean
TTextWindow::DoMenuSelection( short menu, short item )
{
 Str255 itemString;
 short  fontNumber;
 
 if ( menu == mTextFont )
 {
 GetMenuItemText( fgFontSubMenu, item, itemString );
 GetFNum( itemString, &fontNumber );
 SetPort( fWindow );
 TextFont( fontNumber );
 InvalRect( &fWindow->portRect );
 return true;
 }
 else
 return false;
}

DoMenuSelection() gets called before any of the command dispatching functions, giving you a chance to handle menus that contain unregistered items. Since there is no way to construct a CMNU resource for the Font menu (how would we assign command numbers when we don’t know how many or which fonts are installed?), we’ll handle selections from the Font menu in this function.

Before you read on, take a look at the function this function overrides, TWindow::DoMenuSelection(). Note that it simply returns false. If DoMenuSelection() returns false, Sprocket converts the menu selection to a command number and handles it that way. If DoMenuSelection() returns true, Sprocket assumes the menu selection has been processed and does nothing more.

DoMenuSelection() first checks to be sure the selection was from the Font menu. If so, it converts the selected item to a font number, then calls TextFont() to set the font for the associated fWindow, forces a redraw, and returns true.

Next, we’re going to make some changes to the function DoMenuCommand(), our menu command dispatcher that gets called if DoMenuSelection() returns false.

• Add a declaration of the variable newSize at the top of TTextWindow::DoMenuCommand():

 long newSize;

• Still in TTextWindow::DoMenuCommand(), edit the cFontSizeOther case in the switch statement to read:

 case cFontSizeOther:
 newSize = DoNumberDialog();
 if ( newSize != kCancelButtonPressed )
 SetNewTextSize( newSize );
 return true;
 break;

This case gets executed when Other (xx)... is selected from the Size menu. We’ll start by calling DoNumberDialog() to prompt the user for a new size. If a new size was entered, we’ll call SetNewTextSize() to set the new size.

Now we need to add the code that places the check mark next to the current font in the Font menu.

• Add a call of the function AdjustFontMenu() to the member function TTextWindow::AdjustMenusBeforeMenuSelection(). Here’s the new version of TTextWindow::AdjustMenusBeforeMenuSelection():

void
TTextWindow::AdjustMenusBeforeMenuSelection( void )
{
 AdjustFontMenu();
 AdjustSizeMenu();
 AdjustStyleMenu();
}

• Add the member function TTextWindow::AdjustFontMenu() to the file. Here’s the source code:

void
TTextWindow::AdjustFontMenu( void )
{
 short  fontNumber, numFonts, i;
 Str255 fontName, itemName;
 
 fontNumber = fWindow->txFont;
 GetFontName( fontNumber, fontName );
 
 numFonts = CountMItems( fgFontSubMenu );
 
 for ( i=1; i<=numFonts; i++ )
 {
 GetMenuItemText( fgFontSubMenu, i, itemName );
 
 if ( EqualString( itemName, fontName, true, true ) )
 CheckItem( fgFontSubMenu, i, true);
 else
 CheckItem( fgFontSubMenu, i, false);
 }
}

AdjustFontMenu() turns the current font family id into its name. Next, CountMItems() is called to return the number of items in the Font menu. The for loop steps through each of the items, checking to see if the item is equal to the font name. If so, the item gets a check mark. If not, the item does not get a check mark.

PictureWindow.h

Our final task is to add the code that implements the two items in the Picture menu, Centered and Upper Left. Centered causes the picture to be drawn centered in the picture window and Upper Left causes the picture to be pressed against the upper-left corner of the window.

• Close TextWindow.cp and open the file PictureWindow.h.

• Add the declaration of the data member fIsPictureCentered to the TPictureWindow class declaration:

 BooleanfIsPictureCentered;

• Add the declarations of the member functions DoMenuCommand() and AdjustMenusBeforeMenuSelection() to the TPictureWindow class declaration:

 virtual Boolean DoMenuCommand( unsigned long menuCommand );
 virtual void    AdjustMenusBeforeMenuSelection( void );

PictureWindow.cp

Close PictureWindow.h and open the file PictureWindow.cp.

• Add the code that initializes fIsPictureCentered to the TPictureWindow constructor:

 fIsPictureCentered = true;

• In the function TPictureWindow::Draw(), edit the last two lines of code so Draw() looks like this:

void
TPictureWindow::Draw(void)
{
 PicHandlepic;
 Rect   r;
 
 r = fWindow->portRect;
 EraseRect( &r );
 
 if ( fDraggedPicHandle == nil )
 pic = this->LoadDefaultPicture();
 else
 pic = fDraggedPicHandle;

 if ( fIsPictureCentered )
 {
 this->CenterPict( pic, &r );
 }
 else
 {
 r = (**pic).picFrame;
 OffsetRect( &r, - r.left, - r.top );
 }
 
 DrawPicture( pic, &r );
}

Basically, we’ve made Draw() depend on fIsPictureCentered.

• Add the member function TPictureWindow::DoMenuCommand() to the file. Here’s the source code:

Boolean
TPictureWindow::DoMenuCommand( unsigned long menuCommand )
{
 switch ( menuCommand )
 {
 case cCentered:
 fIsPictureCentered = true;
 SetPort( fWindow );
 InvalRect( &fWindow->portRect );
 return true;
 break;
 
 case cUpperLeft:
 fIsPictureCentered = false;
 SetPort( fWindow );
 InvalRect( &fWindow->portRect );
 return true;
 break;
 }

 return false;
}

This code gets called in response to a selection from the Picture menu.

• Add the member function TPictureWindow::AdjustMenusBeforeMenuSelection() to the file. Here’s the source code:

void
TPictureWindow::AdjustMenusBeforeMenuSelection( void )
{
 if ( fIsPictureCentered )
 {
 gMenuBar->EnableAndCheckCommand( cCentered, true, true );
 gMenuBar->EnableAndCheckCommand( cUpperLeft, true, false );
 }
 else
 {
 gMenuBar->EnableAndCheckCommand( cCentered, true, false );
 gMenuBar->EnableAndCheckCommand( cUpperLeft, true, true );
 }
}

Depending on the value of fIsPictureCentered, this code places a check mark next to the appropriate item in the Picture menu.

SprocketStarter.cp

Several months ago, when we first started this program, we added code to the function HandleMenuCommand() in SprocketStarter.cp that beeped when either of the Picture items was selected. Since we now handle these two cases inside the file PictureWindow.cp (as it should be done), we need to delete the two commands from the case statement in HandleMenuCommand() inside SprocketStarter.cp.

• Close the file PictureWindow.cp and open the file SprocketStarter.cp.

• Find the function HandleMenuCommand(). Inside the switch statement, delete the cases (all the way down to the break) for cCentered and cUpperLeft.

Running SprocketStarter

Well, that’s about it. Now’s the time to test your new creation. Select Run from the Project menu. When it runs, SprocketStarter will create a new text window, and the Text menu will appear at the end of the menu bar. Click on the Text menu, then make a selection from the Font submenu (Figure 2). The font of the frontmost text window should change and, the next time you select from the Font submenu, the check mark should appear next to this new font.

Figure 2. The Font submenu, showing the checkmark by Geneva
with Chicago selected.

Next, select Other (18)... from the Size submenu (we set up18 as the default font size). The font size dialog will appear, with the number 18 in the editable text field. Type 0 and click the OK button. You’ll hear a beep and the 0 will be highlighted. Type 37 and click the OK button (Figure 3). This time, your new font size is accepted and the text in the text window is redrawn showing the new font size.

Figure 3. The font size dialog that appears when you select Other (xx)...
from the Size submenu.

Once again, pull down the Text menu and bring down the Size submenu. Notice that the last item now reads Other (37)... and that the check mark has moved to this item (Figure 4).

Figure 4. The Size submenu with the Other (37)... item selected and checked.

Finally, create a picture window, then select one of the two items from the Picture menu that appears. With each selection, the picture in the picture window should be redrawn to match the selection and the check mark should appear next to the last selected item.

Figure 5. The Picture menu.

About the only thing left to do with this code is to make the sizes in the Size menu appear in outline font if they are available on the current machine or in regular font if that size is not available. Take some time to add this code. A good place to add the code is in the function TTextWindow::SetUpStaticMenu(), after you set up the static member fgSizeSubMenu. You’ll need to use the functions SetItemStyle() and RealFont() for each of the 6 sizes in the menu.

What’s Next

I hope the last few columns have given you an appreciation for the complexity and coolness of a framework. One thing I’ve grown to appreciate is the incredible amount of work it must have been for Dave Falkenburg to build Sprocket. Way to go, Dave!

I’m not sure what topic we’ll be tackling next month. I’ve been thinking about building a small PowerPlant project, just to explore the differences between Sprocket and PowerPlant. On the other hand, I’ve really got a hankering to build a tool palette using Sprocket. And there’s this cool OpenDoc part I’ve been fiddling with. Any preferences? Send some email my way...

See you next month!

 
AAPL
$111.78
Apple Inc.
-0.87
MSFT
$47.66
Microsoft Corpora
+0.14
GOOG
$516.35
Google Inc.
+5.25

MacTech Search:
Community Search:

Software Updates via MacUpdate

LibreOffice 4.3.5.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
CleanApp 5.0.0 Beta 5 - Application dein...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
Monolingual 1.6.2 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. It requires a 64-bit capable Intel-based Mac and at least... Read more
NetShade 6.1 - Browse privately using an...
NetShade is an Internet security tool that conceals your IP address on the web. NetShade routes your Web connection through either a public anonymous proxy server, or one of NetShade's own dedicated... Read more
calibre 2.13 - Complete e-library manage...
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 librarian... Read more
Mellel 3.3.7 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
ScreenFlow 5.0.1 - Create screen recordi...
Save 10% with the exclusive MacUpdate coupon code: AFMacUpdate10 Buy now! ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your... Read more
Simon 4.0 - Monitor changes and crashes...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
BBEdit 11.0.2 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
ExpanDrive 4.2.1 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more

Latest Forum Discussions

See All

Make your own Tribez Figures (and More)...
Make your own Tribez Figures (and More) with Toyze Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
So Many Holiday iOS Sales Oh My Goodness...
The holiday season is in full-swing, which means a whole lot of iOS apps and games are going on sale. A bunch already have, in fact. Naturally this means we’re putting together a hand-picked list of the best discounts and sales we can find in order... | Read more »
It’s Bird vs. Bird in the New PvP Mode f...
It’s Bird vs. Bird in the New PvP Mode for Angry Birds Epic Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Telltale Games and Mojang Announce Minec...
Telltale Games and Mojang Announce Minecraft: Story Mode – A Telltale Games Series Posted by Jessica Fisher on December 19th, 2014 [ permalink ] | Read more »
WarChest and Splash Damage Annouce Their...
WarChest and Splash Damage Annouce Their New Game: Tempo Posted by Jessica Fisher on December 19th, 2014 [ permalink ] WarChest Ltd and Splash Damage Ltd are teaming up again to work | Read more »
BulkyPix Celebrates its 6th Anniversary...
BulkyPix Celebrates its 6th Anniversary with a Bunch of Free Games Posted by Jessica Fisher on December 19th, 2014 [ permalink ] BulkyPix has | Read more »
Indulge in Japanese cuisine in Cooking F...
Indulge in Japanese cuisine in Cooking Fever’s new sushi-themed update Posted by Simon Reed on December 19th, 2014 [ permalink ] Lithuanian developer Nordcurrent has yet again updated its restaurant simulat | Read more »
Badland Daydream Level Pack Arrives to C...
Badland Daydream Level Pack Arrives to Celebrate 20 Million Downloads Posted by Ellis Spice on December 19th, 2014 [ permalink ] | Read more »
Far Cry 4, Assassin’s Creed Unity, Desti...
Far Cry 4, Assassin’s Creed Unity, Destiny, and Beyond – AppSpy Takes a Look at AAA Companion Apps Posted by Rob Rich on December 19th, 2014 [ permalink ] These day | Read more »
A Bunch of Halfbrick Games Are Going Fre...
A Bunch of Halfbrick Games Are Going Free for the Holidays Posted by Ellis Spice on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

The Apple Store offering free next-day shippi...
The Apple Store is now offering free next-day shipping on all in stock items if ordered before 12/23/14 at 10:00am PT. Local store pickup is also available within an hour of ordering for any in stock... Read more
It’s 1992 Again At Sony Pictures, Except For...
Techcrunch’s John Biggs interviewed a Sony Pictures Entertainment (SPE) employee, who quite understandably wished to remain anonymous, regarding post-hack conditions in SPE’s L.A office, explaining “... Read more
Holiday sales this weekend: MacBook Pros for...
 B&H Photo has new MacBook Pros on sale for up to $300 off MSRP as part of their Holiday pricing. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1699... Read more
Holiday sales this weekend: MacBook Airs for...
B&H Photo has 2014 MacBook Airs on sale for up to $120 off MSRP, for a limited time, for the Thanksgiving/Christmas Holiday shopping season. Shipping is free, and B&H charges NY sales tax... Read more
Holiday sales this weekend: iMacs for up to $...
B&H Photo has 21″ and 27″ iMacs on sale for up to $200 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 1.4GHz... Read more
Holiday sales this weekend: Mac minis availab...
B&H Photo has new 2014 Mac minis on sale for up to $80 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $459 $40 off MSRP - 2.6GHz Mac mini: $629 $70 off MSRP... Read more
Holiday sales this weekend: Mac Pros for up t...
B&H Photo has Mac Pros on sale for up to $500 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2599, $400 off MSRP - 3.5GHz 6-core Mac Pro: $3499, $... Read more
Save up to $400 on MacBooks with Apple Certif...
The Apple Store has Apple Certified Refurbished 2014 MacBook Pros and MacBook Airs available for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
Save up to $300 on Macs, $30 on iPads with Ap...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
iOS and Android OS Targeted by Man-in-the-Mid...
Cloud services security provider Akamai Technologies, Inc. has released, through the company’s Prolexic Security Engineering & Research Team (PLXsert), a new cybersecurity threat advisory. The... Read more

Jobs Board

*Apple* Store Leader Program (US) - Apple, I...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on experience, Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* 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.