TweetFollow Us on Twitter

Pie Menus
Volume Number:7
Issue Number:1
Column Tag:HyperChat

Related Info: Menu Manager Quickdraw

Pie Menus

By Boyd and Andrea Hays, Boulder, CO

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

[Boyd Hays is a Software Engineer for TRW. He is currently working on the “Data Integration Engine”, a tool for integrating heterogeneous data bases. Andrea Hays is a Research Assistant for the University of Colorado. She is developing “Metalog“, an “electronic notebook” for scientists.]

Introduction

Pie menus offer an interesting alternative to traditional linear menus when designing user interfaces. In linear menus, selectable items are placed in a vertical list; in pie menus, selectable items are placed in a circle around a midpoint. Each approach offers different advantages and disadvantages which we believed were worth exploring.

As a project in a user interface class, we implemented pie menus in Parcplace’s Smalltalk-80 on the Mac. Our goal was to study the relative benefits of this menuing technique in an actual environment that had a pervasive use of popup menus.

Though Smalltalk-80 was the target system, our familiarity with the HyperCard environment allowed us to quickly build an initial test via a HyperCard XCMD. What follows is a description of that implementation.

Description of Menu Types

We are all familiar with the use of linear menus. Their use is ubiquitous on the Mac; we have a entire menu bar full of them. To summarize, a linear menu consists of a list of selectable items (words or phrases) aligned vertically and framed within a rectangular space. Items in the menu are selected using some sort of pointing device, in the Mac’s case, a mouse. The user scans the menu by moving the mouse in a vertical motion through the menu. The current item of selection is highlighted in some way, often through the use of reverse video. A subsequent movement with the mouse (such as clicking or releasing a button) while an item is highlighted would “select” that item in the menu and cause the action indicated by that menu item to occur. The following figure shows the Finder’s “File” linear menu with “Close” selected.

Pie menus differ from linear menus in that the selectable items are placed in a circle around a midpoint. When the menu appears, the mouse pointer appears at this midpoint. To select an item, the user moves the mouse toward one of the items. As the mouse nears the item, a region associated with that item is highlighted to indicate its selectability. In our implementation the area of selection is in the shape of a pie wedge emanating from the midpoint, thus the term “pie menu”. A region in the center of the pie was left unfilled and does not participate in any of the wedge selections. This allows the user to de-select all items without having to move the cursor outside the pie.

Since we are interested in the perceived usability of pie menus in a real context, we have included a HyperCard stack that utilizes both linear and pie menus. This stack consists of a series of multiple-choice questions about presidents of the United States. Half of the cards in the stack utilize linear menus; the other half, pie menus. This test was administered to numerous individuals to obtain their preference between the two types of menuing systems. The following figure is an example from that stack with “Jackson” selected.

Why Pie Menus?

We became interested in pie menus after reading the article “An Empirical Comparison of Pie vs. Linear Menus”, by J. Callahan, D Hopkins, M. Weiser, and B. Shneiderman, in the ACM’s SIGCHI 1988 Conference Proceedings. For a more formal treatment of the topic, please refer to this paper. The general conclusion of this paper was that, overall, users could select items from pie menus faster than from linear menus. The major reason cited was that less hand movement was needed to select items from the pie menus. Other advantages to pie menus include: easy grouping of common selections; the ability to quickly locate semantically opposite selections by placing them at opposite positions on the menu; and the ability to “pre-mouse”, or select a desired item before the menu is even fully rendered on the display.

The pie menus always appear with the mouse in the center. This can be accomplished by positioning the menu so that it is centered about the current mouse location. An alternative is to “warp” or move the mouse until it is at the desired location. This is necessary when the natural location of the popup menu would result in it being partly off the screen. While this could certainly be construed to be in violation of Apples Human Interface Guidelines, we do in fact warp the mouse when necessary to keep the entire menu visible.

Pie Menu Usage

In many cases pie menus can be used interchangeably with linear menus. However, the differences between the two forms of popup menus bear noting.

Linear menus have proven useful for a number of reasons. A linear menu contains a great deal of information in a small physical space. The size of the menu need be only as large as the minimum space required to hold the text items. This has a distinct advantage when the context in which a menu appears, i.e. the background, must be preserved. By maintaining as much of the prior screen image as possible, the instantiation of a linear popup menu is made visually less obtrusive.

Linear menus correspond closely with the appearance of text in print. The mechanisms an individual has developed for reading are directly applicable to the scanning of a list of menu items. This appears to be a critical factor in how quickly one can assimilate a large amount of information from a menu.

Pie menus, due to their difference in form, exhibit a different set of advantages. They are capable of providing much semantic information in a spatial format. Menu choices that are semantically opposite e.g. cut and paste, can be placed on opposite sides of the menu.

Since pie menus are circular, they can be arranged to place the most frequently used items on the left or right side depending upon the “handedness” of the user. This facilitates mouse motion due to anatomical factors. It is an intrinsically stronger and more accurate physical movement to draw your hand toward your chest than it is to push it away.

Locating a menu item within a pie menu that has been frequently used is easier than it would be with a linear menu. It is difficult in a linear menu to “pre-mouse” to, for example, the seventh item. In a pie menu it is quite easy to pre-mouse to ten o’clock if the user anticipates the location of the desired item.

Pie menus do suffer from several major disadvantages. They take up much more screen real estate than linear menus, obstructing more of the background. They also become harder to read as the number of selectable items grows. In our experimentation we found an upper limit of twelve to fifteen items could be presented in a “reasonable” space. Many of the Smalltalk users in our experiment found that the ease of selection began to decrease as the menu became this dense.

The greatest difficulty with pie menus is that it is difficult to locate menu items when you have no pre-awareness of their location. Pie menus trade hand motion for eye motion, a exchange which evidently is not an even one. In our tests we found most users complained much more vehemently about trying to read a circular list of items from a pie menu than they did about having to move the mouse further with a linear menu.

Implementation Guidelines

Given the fact that pie menus appear to have applicability in certain domains, we offer the following implementation guidelines and suggestions:

• Make the selectable regions within the pie easily distinguishable.

• Provide the user visual information regarding the current selection in real-time. If the system does not handle highlighting in a “reasonable” time frame the menus will go unused.

• Reduce the amount of screen real estate consumed by a popup menu to a minimum. This means that the system should collapse the bounding region of a pie menu to the smallest possible size to contain the menu items.

• Provide areas wherein no menu selection takes place, e.g., a central hole. This will give the user avenues of escape.

• Make the labels selectable as well as the pie regions.

• Have the pie menu appear centered about the mouse. In cases where this would cause part of the menu to be off the screen, reposition the menu and warp the mouse to the new location.

• Group menu items according to function when possible, much as separator lines are used in linear menus. Possible ways to do this would be to explode the pie into related sections, or to use lines to separate the regions.

• Try to keep the number of menu items small; pie menus are most effective with fewer items.

• Locate menu items that occur in multiple menus in the same location in each of the menus.

The Code

The pie wedges of this implementation are made up of QuickDraw regions. We wanted the implementation to be fast enough to keep-up with the movement of the cursor, so we decided to precompute the perimeters of the necessary circles. There are three circles used: an inner doughnut hole used to create a neutral spot for initial cursor positioning; a circle, termed inner in the source, that serves as the boundary of the circle of wedges; and the final outer circle upon which the labels are positioned.

A utility program, “plotcircle.c”, is responsible for constructing a header file, “circledata.h”, used by the routine that actually draws the pie menus. The method used to compute the circles is that of Bresenham’s Algorithm. It works by starting at the point (0,0). It then locates a point directly “above” the center of the circle at a point (0,-y) where y is equal to the circle’s radius. To find any point on the circle, one computes which of the three possible selections (horizontally and to the right, diagonally down and to the right, or vertically downward) minimizes the square of the distance between the trial pixel and the pixel that would lie on the “actual” circle’s arc. Through reflection, one only has to compute the first octant of the circle, as all other octants can be derived via a change in the sign of the x and/or y coordinates. Bresenham’s Circle Algorithm is detailed more fully in many readily available texts on computer graphics.

The source file “PieXCMD.c” is the HyperCard interface to the pie menu plotting function. It accepts the user’s argument list and formats the arguments into a suitable form. The format of the interface to the XCMD is:

/* 1 */

  put PiePopupMenu(“menu”,font#,size,pattern) into container

where menu is the list of menu items in Menu Manager format; font # is the number of the font to use, e.g., geneva; size is the point size of the font; and pattern is a boolean used to indicate whether or not patterns should be used in the display of the wedges.

The source file “PieUtils.c” contains the single function ConvertMenuList(). This is used to filter out the Menu Manager specific control characters from the menu list that is passed to the XCMD. This is useful in that one can freely interchange the PiePopupMenu XCMD with one of the many “normal” popup menu XCMDs that support the native Menu Manager options. (In fact, the accompanying stack interchanges calls to PiePopupMenu with LinearPopupMenu in order to demonstrate the different menuing techniques.)

The source file containing the routine that displays a pie menu is “PieMenu.c”. Its external entry point is DrawPieMenu(). It is implemented by taking the array of points that make up each of the circles and dividing them into equal chunks based on the number of elements to display. These chunks are then used to generate regions. Once the regions have been drawn on the display the routine awaits the mouse release, at which time the menu selection, if any, is returned to the caller.

[The files: HyperXcmd.h, StrToLong.c, EvalExpr.c, StrToNum.c, NumToStr.c, and XCMD-Utils.c are found and copyrighted in Gary Bond’s book, XCMD’s For Hypercard, published by MIS: Press.-ed]

Future Work

The implementation presented here suffers in a couple of areas. The circles used to generate the pie menus and the enclosing rectangle in which they are displayed are fixed in size. It would not be terribly difficult to modify these routines to use the trigonometric functions of the Mac’s Transcendental Functions Package to determine which wedge contained the cursor. Additionally, a needed improvement is to reduce the size of the enclosing window used to display the menu to the smallest size possible that would not clip the labels of the wedges. This would do much to enhance the utilization of the screen’s real estate. We chose not to implement these features due to both time constraints and our interest in minimizing run-time overhead. (Granted, when you’re in a spin-loop awaiting a mouse button release, all of the cycles of the machine are fundamentally consumed by your application anyway.)

During the course of our investigations we received numerous responses from individuals who took the pie menu test. One of the more interesting suggestions came from Daniels & Mara of “StackHeads”. They augmented our design to include hierarchical pie menus in the following manner:

Acknowledgements

Thanks to Clayton Lewis and the members of our user interface class at the University of Colorado who participated in our study. Additionally, we would like to thank all of the individuals who responded to our requests for feedback on the usage of pie menus, and, of course, to Callahan et. al. for providing our inspiration.

Listing:  plotcircle.c
/*
 * File:plotcircle.c
 * Purpose: Construct three collections of points
 *  representing concentric circles used
 *  in the plotting of pie popup menus.
 */
 
#include <stdio.h>

#define I_RADIUS 50/* Inner radius    */
#define O_RADIUS 70/* Outer radius    */
#define C_RADIUS 5 /* Center radius   */
#define CENTER_X 0 /* Bias all points */
#define CENTER_Y 0 /*  at origin 0,0. */
#define MAX_PTS  1000/* Max # points    */
#define X_COORD  1
#define Y_COORD  2
#define I_POINTS 1
#define O_POINTS 2
#define C_POINTS 3

/* This is the representation of a point. */
struct  xy_pt {
 int  x;
 int  y;
} pt[MAX_PTS];

/*
 * Array index into which to generate the next point. 
 */
int num_pts;

/* File into which the data is written. */
FILE  *fp;

/* Local Function prototypes. */
void  circle(int xc, int y_center, int radius);
void  sortpoints(void);
void  sort(struct xy_pt *, int, int);
intcompare(struct xy_pt, struct xy_pt, int);
void  swap(struct xy_pt *, struct xy_pt *);

int main()
{
 if ((fp=fopen(“circledata.h”,”w”)) != NULL) {
 /* Generate the set of interior points */
 circle(CENTER_X, CENTER_Y, I_RADIUS); 
 sortpoints();
 generate_quadrants();
 dumppoints(I_POINTS);

 /* Generate the set of outer points */
 circle(CENTER_X, CENTER_Y, O_RADIUS);
 sortpoints(); 
 generate_quadrants();  
 dumppoints(O_POINTS);  
 
 /* Generate the set of center points */
 circle(CENTER_X, CENTER_Y, C_RADIUS); 
 sortpoints(); 
 generate_quadrants();
 dumppoints(C_POINTS);

 fclose(fp);
 }
 else {
 fprintf(stderr,
 “Can’t open circledata.h\n”);
 exit(-1);
 }
 
 exit(0);
}

void circle(x_center, y_center, radius)
 int x_center, y_center ,radius;
{
 int  x,y,d;
 
 num_pts = 0;
 y = radius;
 d = 3 - 2 * radius;
 
 /* 
  * Use x,y to control the plotting of the
  * first octant.
  */
 for (x = 0; x < y;) {
 if (num_pts + 1 >= MAX_PTS)
 return;
 pt[num_pts].x   =  x + x_center;
 pt[num_pts++].y = -y + y_center;
 pt[num_pts].x   =  y + x_center;
 pt[num_pts++].y = -x + y_center;
 if (d < 0)
 d += 4 * x + 6;
 else {
 d += 4 * (x - y) + 10;
 --y;
 }
 ++x;
 }
 
 /*
  * If we happened to land on the last point,
  * add it to the list.
  */
 if (x == y) {
 pt[num_pts].x   =  x + x_center;
 pt[num_pts++].y = -y + y_center;
 }
 
 fprintf(fp,”\n”);
}

void sortpoints()
{
 sort(pt,num_pts,X_COORD);
 sort(pt,num_pts,Y_COORD);
}

int generate_quadrants()
{
 int i;
 
 /* 
  * Second quadrant same as first with
  * positive y coordinates. Note that the end
  * points are not duplicated.
  */
 for (i = num_pts - 1; i >= 0; --i) {
 pt[num_pts].x =  pt[i].x;
 pt[num_pts].y = -pt[i].y;
 ++num_pts;
 }
 
 /* 
  * 3rd & 4th quadrants same as 1st & 2nd with
  * negative x coordinates.
  */
 for (i = num_pts - 1; i >= 0; --i) {
 pt[num_pts].x = -pt[i].x;
 pt[num_pts].y = pt[i].y;
 ++num_pts;
 }
}

int dumppoints(type)
 int  type;
{
 int  i;
 int  cols = 0;
 
 /* Print out the title. */
 fprintf(fp, “/*\n”);
 fprintf(fp, “ * Points of a circle with “
 “origin at %d,%d “, CENTER_X, CENTER_Y);

 if (type == I_POINTS) {
 fprintf(fp, “with a radius of: %d\n”, I_RADIUS);
 fprintf(fp, “ */\n\n”);
 fprintf(fp, “#define\t\tCENTER_X\t\t\t%d\n”,  CENTER_X);
 fprintf(fp, “#define\t\tCENTER_Y\t\t\t%d\n\n”,  CENTER_Y);
 fprintf(fp,”#define\t\tI_RADIUS\t\t\t%d\n”,  I_RADIUS);
 
 fprintf(fp, “#define\t\tNUM_I_POINTS\t\t%d\n”, num_pts);
 }
 else if (type == O_POINTS) {
 fprintf(fp, “with a radius of: %d\n”, O_RADIUS);
 fprintf(fp, “ */\n”);
 fprintf(fp, “#define\t\tO_RADIUS\t\t\t%d\n”, O_RADIUS);
 fprintf(fp, “#define\t\tNUM_O_POINTS\t\t%d\n”, num_pts);
 } 
 else {
 fprintf(fp, “with a radius of: %d\n”, C_RADIUS);
 fprintf(fp, “ */\n”);
 fprintf(fp, “#define\t\tC_RADIUS\t\t\t%d\n”, C_RADIUS);
 fprintf(fp, “#define\t\tNUM_C_POINTS\t\t%d\n”, num_pts);
 }
 
 /*
  * Print the structure description we are
  * about to define.
  */
 if (type == I_POINTS) {
 fprintf(fp, “\n\n/* This represents a point.*/\n”);
 fprintf(fp,”struct\txy_pt {\n”);
 fprintf(fp,”\tint x;\n”);
 fprintf(fp,”\tint y;\n”);
 fprintf(fp,”};\n\n”);
 fprintf(fp,”struct xy_pt i_circle_points”);
 fprintf(fp,”[NUM_I_POINTS] = {\n\t”);
 }
 else if (type == O_POINTS) {
 fprintf(fp,”struct xy_pt o_circle_points”);
 fprintf(fp,”[NUM_O_POINTS] = {\n\t”);
 }
 else {
 fprintf(fp,”struct xy_pt c_circle_points”);
 fprintf(fp,”[NUM_C_POINTS] = {\n\t”);
 }
 
 for (i = 0; i < num_pts; i++) {
 fprintf(fp,”{%5d,%5d}”, pt[i].x, pt[i].y);
 if (i != (num_pts - 1))
 fprintf(fp, “,”);
 ++cols;
 if ( (cols % 5) == 0 ) {
 fprintf(fp,”\n”);
 cols = 0;
 }
 fprintf(fp, “\t”);
 }
 
 if (cols != 0)
 fprintf(fp,”\n”);
 
 fprintf(fp,”};\n”);
}

void sort(v, n, sort_field)
 struct xy_pt v[];
 int n;
 int sort_field;
{
 int gap, i, j;
 
 for (gap = n/2; gap > 0; gap /= 2)
   for (i = gap; i < n; i++)
 for (j = i-gap; j >= 0; j -= gap) {
   if (compare(v[j],v[j+gap],sort_field)<=0)
 break;
   swap(&v[j], &v[j+gap]);
 }
}

int compare(v1, v2, sort_field)
 struct xy_pt v1,v2;
 int  sort_field;
{
 int  temp1,temp2; 
 
 /*
  * When sorting Y coordinates, don’t swap
  * unless X’s are equal
  */
 if ((sort_field == Y_COORD) && (v1.x != v2.x)) 
 return (0);
 
 /* Extract the values to compare */
 if (sort_field == X_COORD) {
 temp1 = v1.x;
 temp2 = v2.x;
 }
 else {
 temp1 = v1.y;
 temp2 = v2.y;
 }
 
 /* Return the comparison of the two values */
 if (temp1 < temp2)
 return (-1);
 else if (temp1 > temp2)
 return (1);
 else
 return (0);
}

void swap(p1, p2)
 struct xy_pt *p1, *p2;
{
 struct xy_pt  temp;
 
 temp = *p1;
 *p1 = *p2;
 *p2 = temp;
}
Listing:  PieMenus.c

/*
 * File:PieMenus.c
 * Purpose: This file contains a driver “main”
 * function that is used to test the
 * pie popup menu drawing function.
 */

#define   NUM_MENU_ITEMS  7

char  *strcat();

int main()
{
 Point  windowLoc = {0,0};
 char buffer[400];
 char itembuf[40];
 int  num_items;
 int  i;
 
 /* Initialize the toolbox. */
 MaxApplZone();  
 InitGraf(&thePort);
 InitFonts();
 FlushEvents(everyEvent, 0);
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs(0L);
 InitCursor();

 /* Construct the test menu. */
 buffer[0] = ‘\0’;
 for (i = 0; i < NUM_MENU_ITEMS; i++) {
 sprintf(itembuf,”ITEM %d;”, i+1);
 strcat(buffer,itembuf);
 }
 num_items = ConvertMenuList(buffer);
 
 /* Display the menu. */
 DrawPieMenu(windowLoc,   /* */
 buffer,/* menu list */
 num_items, /* # menu items */
 (int) geneva, /* font to use */
 10,    /* size of font */
 TRUE,  /* use patterns */
 TRUE); /* allow warping */
}
Listing:  PiePopup.c

/*
 * File:PiePopup.c
 *
 * Purpose: Contains the function DrawPieMenu
 * that is callable from HyperCard via
 * an XCMD interface.
 */
 
#include “circledata.h” /* circle definitions */

/* character separating menu string items */
#define MENU_SEPARATOR    ‘;’
/* 1/2 the width of the popup menu */
#define ONE_HALF_POPUP_X  180
/* 1/2 the height of the popup menu */
#define ONE_HALF_POPUP_Y  80
/* minimum # of pixels to have around popup */
#define BORDER_SIZE4
/* Indicates no region is currently selected */
#define NO_REGION-1
/* # of pixels in Mac’s menu bar */
#define MENUBAR_HEIGHT    20

/*
 * Definitions used in warping the mouse.
 */
#define RawMouse (Point *) 0x82c
#define MTemp    (Point *) 0x828
#define CrsrNew  (char *)  0x8CE
#define CrsrCouple (char *)  0x8CF

/* Local Function Prototypes */
int DrawPieMenu(Point,char*,int,int,int,int, int);
Rect GetMenuRect(Point, int, WindowPtr *);

/* 
 * Number of different pie wedge patterns and how
 * many different ones to use when displaying
 * menus containing an even/odd number of menu
 * items.  The intent is to not have the first and
 * last pattern used be the same making the two
 * wedges look like one big wedge.
 */
#define NUM_PIE_PATTERNS  5
/* # of patterns to use with an odd # of items */
#define NUM_ODD_PATTERNS  5
/* # of patterns to use with an even # of items */
#define NUM_EVEN_PATTERNS 4

/* The actual patterns used for the pie wedges */
long  pattern[NUM_PIE_PATTERNS][2] =
{
 0x77DD77DD, 0x77DD77DD,  /* Dark Grey */
 0x88228822, 0x88228822,  /* Light Grey */
 0xAA55AA55, 0xAA55AA55,  /* Grey */
 0xCC33CC33, 0xCC33CC33,  /* Textured */
 0xAAAAAAAA, 0xAAAAAAAA,  /* Striped */
};

/*
 * We need to have our own black pattern because
 * the normal pen pattern of black is referenced
 * off A5 as part of the QuickDraw globals. Since
 * this is a code resource, it’s built referencing
 * globals off A4 and doesn’t have access to the
 * A5-based QuickDraw globals.
 */
long  black_pattern[2] = {0xFFFFFFFF,0xFFFFFFFF};

/* Maximum number of pie wedges allowed */
#define MAX_MENU_ITEMS  40

/*
 * Rectangles used to hold the coordinates of the
 * pie wedge label’s rectangles
 */
struct  {
 Rect r;
} label_rects[MAX_MENU_ITEMS];

/* 
 * Region table used to hold the region handles
 * for the pie wedges.
 */
RgnHandle pie_rgns[MAX_MENU_ITEMS];

/*
 * The region for the center “non-hot” region is
 * treated separately.
 */
RgnHandle center_rgn;

/*
 * Function:DrawPieMenu()
 * Outputs: Displays a pie menu for item selection
 * Returns:The # of the selected menu item or zero
 */
int DrawPieMenu(
 Point  windowLoc, /* popup location */
 char   *menuList, /* list of menu items */
 int  num_menu_items,/* # of menu itemsn */
 int  font, /* font to display */
 int  font_size, /* font point size */
 int  use_patterns,/* use/dont use ptrns */
 int  warp_mouse) /* do/dont allow warp */
{
 Rect   popupRect;  /* menu’s rectangle */
 WindowPtrorigWindow; /* original grafport */
 WindowPtrpieWindow;  /* ptr to pie popup */
 WindowRecord window_rec;/*pie popup window */
 int  num_patterns;/* # patterns to use */
 int  selection;   /* user’s selection */    

 /*
  * Before doing any work, ensure the mouse is
  * still depressed.
  */
 if (! StillDown())
 return (0);

 /* Nothing to do if the menu is empty */
 if (num_menu_items < 1)
 return (0);
 
 /* Save the grafport of the current window */
 GetPort(&origWindow);

 /* Compute the enclosing rectangle. */
 popupRect = GetMenuRect(windowLoc, warp_mouse, &origWindow);
 
 /* 
  * Open a dialog box window to place the pie
  * menu within, and make it current.
  */    
 pieWindow = NewWindow((Ptr) &window_rec, &popupRect,
   “\pPieMenu”, true, altDBoxProc,
   (WindowPtr) -1L, true, 0);
 SetPort(pieWindow);

 /*
  * Construct the array of regions that
  * represent pie wedges.
  */
 BuildPieRegions(&num_menu_items,
 use_patterns, &num_patterns);

 /* Display the menu on the screen. */
 DrawTheMenu(menuList, num_menu_items, &num_patterns,
 font, font_size, use_patterns);

 /* Obtain the user’s menu choice. */
 selection = GetMenuPick(use_patterns,
 num_menu_items, num_patterns);

 /* Dispose of the pie wedge regions */
 DisposePieRegions();

 /* Switch back to the original grafport */
 SetPort(origWindow);
 CloseWindow(pieWindow);

 return selection;
}

Rect GetMenuRect(
 Point  windowLoc, /* popup location */
 int  warp_mouse,/* do/dont warp */
 WindowPtr*origWindow)/* window coord */
{
 Point  mouseLoc;/* mouse location */
 Point  oldmouseLoc;/* save mouse loc */
 Rect   popupRect;
 short  scrn_left; /* left of screen  */
 short  scrn_right;/* right of screen */
 short  scrn_top;/* top of screen   */
 short  scrn_bottom; /* bot of screen   */

 /* Obtain the dimensions of the screen. */
 scrn_left = (*origWindow)->portBits.bounds.left;
 scrn_right = (*origWindow)->portBits.bounds.right;
 scrn_top =  (*origWindow)->portBits.bounds.top;
 scrn_bottom = (*origWindow)->portBits.bounds.bottom;
 
 /*
  * Obtain the current location of the mouse to
  * center the popup around.
  */
 GetMouse(&mouseLoc);
 oldmouseLoc = mouseLoc;
 mouseLoc.h += windowLoc.h;
 mouseLoc.v += windowLoc.v;

 /* 
  * Make sure the pie popup will fit on the
  * screen when centered about the cursor
  * location, adjust coordinates if necessary
  * to keep the entire popup menu visible.
  */
 if (mouseLoc.h <= 
 (scrn_left+ONE_HALF_POPUP_X+BORDER_SIZE))
   mouseLoc.h = scrn_left+ONE_HALF_POPUP_X+BORDER_SIZE;
 
 if ((mouseLoc.h+ONE_HALF_POPUP_X+BORDER_SIZE)
 >= scrn_right)
   mouseLoc.h = scrn_right-(ONE_HALF_POPUP_X+BORDER_SIZE);
 
 if (mouseLoc.v <= (scrn_top+ONE_HALF_POPUP_Y+ 
 MENUBAR_HEIGHT+BORDER_SIZE))
   mouseLoc.v = scrn_top + ONE_HALF_POPUP_Y +
    MENUBAR_HEIGHT + BORDER_SIZE;
 
 if ((mouseLoc.v+ONE_HALF_POPUP_Y+BORDER_SIZE)
  >= scrn_bottom )
   mouseLoc.v = scrn_bottom-(ONE_HALF_POPUP_Y+BORDER_SIZE);
 
 /* 
  * Reposition the mouse, if necessary and requested.
  */
 if (warp_mouse) {
 *RawMouse = *MTemp = mouseLoc;
 *CrsrNew = *CrsrCouple;
 }

 /* 
  * Create a rectangle for the popup using the
  * adjusted coordinates
  */
 SetRect(&popupRect, 
 mouseLoc.h - ONE_HALF_POPUP_X,
 mouseLoc.v - ONE_HALF_POPUP_Y,
 mouseLoc.h + ONE_HALF_POPUP_X,
 mouseLoc.v + ONE_HALF_POPUP_Y);
 
 return popupRect;

}

BuildPieRegions(
 int  *num_menu_items,
 int  use_patterns)
{
 int  i;
 
 /* 
  * The region points headers assume a 0,0
  * based coordinate system
  */
 SetOrigin(-(ONE_HALF_POPUP_X), -(ONE_HALF_POPUP_Y));

 /*
  * Create the center “non-hot” region in the
  * pie menu when using patterns.
  */
 if (use_patterns) {
 center_rgn = NewRgn();
 PenSize(1,1);
 PenPat((Pattern *) black_pattern);
 MoveTo(c_circle_points[0].x,
    c_circle_points[0].y);
 OpenRgn();
 for (i = 1; i < NUM_C_POINTS; i++) {
 LineTo(c_circle_points[i].x, c_circle_points[i].y);
 }
 CloseRgn(center_rgn);
 if (use_patterns)
 FrameRgn(center_rgn);
 }
 
 /*
  * Dissallow more menu items than we have
  * regions to display.
  */
 if (*num_menu_items > MAX_MENU_ITEMS)
 *num_menu_items = MAX_MENU_ITEMS;
 
 /*
  * Create as many regions as their are pie
  * slices to display.
  */
 for (i = 0; i < *num_menu_items; i++)
 pie_rgns[i] = NewRgn();
}

DrawTheMenu(
 char   *menuList,
 int  num_menu_items,
 int  *num_patterns,
 int  font, 
 int  font_size,
 int  use_patterns)
{
 int  line_start;/* start of wedge */
 int  line_end;  /* label end point */
 int  points_this_i_rgn; /* # inr rgn pts */
 int  points_this_o_rgn; /* # outr rgn pts*/
 int  i_pts;/* tot inr points */
 int  o_pts;/* tot outr pts */
 int  extra_i_pts; /* used to spread */
 int  extra_o_pts; /*  out extra pts */
 int  next_i_pt; /* next inr point */
 int  next_o_pt; /* next outr point */
 char *cp;/* temp pointer */
 int  i,j;/* temp indexes */
 Point  start_pt;/* label start pt */
 Point  end_pt;  /* label end pt */
 char label_buf[80]; /* temp label buf */
 short  label_width; /* label x pixels */    
 int  label_len; /* # chars in label*/

 /* 
  * Calculate the number of points to use in
  * defining a region.
  */
 i_pts    = NUM_I_POINTS / num_menu_items;
 extra_i_pts   = NUM_I_POINTS % num_menu_items;
 o_pts  = NUM_O_POINTS / num_menu_items;
 extra_o_pts   = NUM_O_POINTS % num_menu_items;

 /*
  * Regions have no width so we need to overlap
  * with next regions first point.
  */
 ++i_pts;
 ++o_pts;

 /*
  * Pick a number of patterns that won’t cause
  * 1st pattern to be equal last.
  */
 if (num_menu_items % 2)
 *num_patterns = NUM_ODD_PATTERNS;
 else
 *num_patterns = NUM_EVEN_PATTERNS;

 /*
  * Initialize next points and next menu
  * selection to use.
  */
 next_i_pt = 0;
 next_o_pt = 0;
 cp = menuList;
 
 /* Plot the regions. */
 for (i = 0; i < num_menu_items; i++) {
 points_this_i_rgn = i_pts;
 if (extra_i_pts) {
 ++points_this_i_rgn;
 --extra_i_pts;
 }
 
 points_this_o_rgn = o_pts;
 if (extra_o_pts) {
 ++points_this_o_rgn;
 --extra_o_pts;
 }
 
 /*
      * Draw a line from the center of this arc
  * to the outer circle.
  */
 line_start = (points_this_i_rgn / 2) + next_i_pt;
 line_end = (points_this_o_rgn / 2) + next_o_pt;
 PenSize(1,1);
 MoveTo(i_circle_points[line_start].x,
    i_circle_points[line_start].y);
 LineTo(o_circle_points[line_end].x,
    o_circle_points[line_end].y);
 
 /*
  * Put a horizontal tab 6 pixels long on
  * the radiating line.
  */
 if ( i < (num_menu_items / 2) )
 LineTo(o_circle_points[line_end].x + 6,
    o_circle_points[line_end].y);
 else
 LineTo(o_circle_points[line_end].x - 6,
    o_circle_points[line_end].y);
 next_o_pt += points_this_o_rgn;
 
 /* Set the typeface and style for text. */
 TextFont(font);
 TextFace(0);
 TextSize(font_size);
 
 /* Isolate next label and display it. */
 j = 0;

 while ((*cp != MENU_SEPARATOR) && (*cp != ‘\0’))
 label_buf[j++] = *cp++;

 label_buf[j] = ‘\0’;
 ++cp;  

 /*
  * Calculate the number of pixels contained
  * in this label.
  */
 label_len = strlen(label_buf);
 label_width = TextWidth(label_buf,0,label_len);
 
 /*
  * Calculate where the start point of the
  * label should be located.
  */
 if ( i < (num_menu_items / 2) ) {
 SetPt(&start_pt,
   o_circle_points[line_end].x + 10,
   o_circle_points[line_end].y + 4);
 }
 else {
 SetPt(&start_pt,
   o_circle_points[line_end].x - (8+label_width),
   o_circle_points[line_end].y + 4);
 }

 /*
  * The end point is to the left of the
  * start by the length of the label.
  */
 SetPt(&end_pt, start_pt.h + label_width, start_pt.v);
 
 MoveTo(start_pt.h,start_pt.v);  
 DrawText(label_buf,0,label_len);

 /*
  * Calculate the rectangle that encloses the label
  */    
 SetRect((Rect *) &label_rects[i], start_pt.h - 3, 
 start_pt.v - font_size, end_pt.h + 3, end_pt.v + 3);
 
 
 /*
  * Open the next region, assign points,
  * close it, fill it, remove center.
  */
 MoveTo(0,0);
 PenSize(1,1);
 OpenRgn();
 for (j = points_this_i_rgn;  j != 0;   j--, next_i_pt++)

 LineTo(i_circle_points[next_i_pt].x,
    i_circle_points[next_i_pt].y);
 
 LineTo(0,0);
 CloseRgn(pie_rgns[i]);
 if (use_patterns) {
 DiffRgn(pie_rgns[i], center_rgn,pie_rgns[i]);
 FillRgn(pie_rgns[i], (Pattern *) 
 pattern[i % (*num_patterns)]);
 }
 else
  FrameRgn(pie_rgns[i]);

 /*
  * The next region actually starts on our
  * ending point.
  */
 --next_i_pt;
 --next_o_pt;
 }
}

GetMenuPick(
 int  use_patterns,
 int  num_menu_items,
 int  num_patterns)
{
 Point  mouseLoc;/* current loc of mouse */
 int  old_rgn; /* previous rgn selection */
 int  in_a_region;/* TRUE iff crsr in pie */
 int  i;/* general purpose index */

 /* Indicate that we aren’t in any region */
 old_rgn = NO_REGION;

 /*
  * If we’re not using patterns put a 2 pixel
  * border around the wedges.
  */
 if (! use_patterns) {
 for (i = 0; i < num_menu_items; i++)
 InsetRgn(pie_rgns[i], 2, 2);
 }
 
 /*
  * Loop, tracking the mouse until the user
  * releases the button.
  */
 while (Button()) {
 SystemTask();
 in_a_region = FALSE;
 GetMouse(&mouseLoc);
 for (i = 0; i < num_menu_items; i++) {
   if (PtInRgn(mouseLoc, pie_rgns[i]) ) {
 in_a_region = TRUE;
 if (old_rgn == i) 
   break;
 else {
   in_a_region = TRUE;
   PaintRgn(pie_rgns[i]);
   InvertRect((Rect *) &label_rects[i]);
   if (old_rgn != NO_REGION) {
 if (use_patterns)
   FillRgn(pie_rgns[old_rgn], (Pattern *)
   pattern[old_rgn % num_patterns]);
 else
   InvertRgn(pie_rgns[old_rgn]);
     InvertRect((Rect *) &label_rects[old_rgn]);
   }
   old_rgn = i;
 }
   }
 }
 
 /*
  * Deselect the previously selected region
  * when we exit the pie regions
  */
 if ((in_a_region == FALSE) && 
     (old_rgn != NO_REGION) ) {
   if (use_patterns)
 FillRgn(pie_rgns[old_rgn],
   (Pattern *) pattern[old_rgn % num_patterns]);
   else
 InvertRgn(pie_rgns[old_rgn]);
   InvertRect((Rect *)  
 &label_rects[old_rgn]);
   old_rgn = NO_REGION;
 }
 }

 /* Return the index of the selected item */
 return ((old_rgn == NO_REGION) ? 0 : ++old_rgn);
}

DisposePieRegions(
 int    num_menu_items,
 int    use_patterns)
{
 int    i;
 
 
 /* Dispose of the pie regions */
 for (i = 0; i < num_menu_items; i++)
 DisposeRgn(pie_rgns[i]);
 
 /* Dispose of the center ‘non-hot’ region */
 if (use_patterns)
 DisposeRgn(center_rgn);
}
Listing:  PieUtils.c

/*
 * File:PieUtils.c
 * Purpose: This file contains utility functions
 * used by the Pie Popup Menu routines.
 */

/* The separating character between menu items. */
#define MENU_SEPARATOR  ‘;’

intConvertMenuList(char *menuList)
{
 char *cp1;
 char *cp2;
 int  num_items;
 int  in_item = FALSE;
 
 /* 
  * Scan the menu list eliminating any of
  * the native Mac Menu Manager’s special
  * characters.
  */
 for (cp1 = cp2 = menuList, 
  num_items = 0; *cp2 != ‘\0’; 
  cp2++) {
 switch (*cp2) {
 /*
  * For the following types of items
  * discard the argument character that follows.
  */
 case ‘^’:/* icon */
 case ‘!’:/* mark */
 case ‘<‘:/* special style */
 case ‘/’:/* keyboard equivalent */
 cp2++;
 /* Don’t go past the end */
 if (*cp2 == ‘\0’)
 cp2--;
 break;
 
 /* Ignore requests to dim the item. */
 case ‘(‘:
 break;
 
 /* For menu separators copy the item */
 case MENU_SEPARATOR:
 ++num_items;
 *cp1++ = *cp2;
 in_item = FALSE;
 break;
 
 /* Default is to just copy the char */
 default:
 *cp1++ = *cp2;
 in_item = TRUE;
 break;
 }
 }
 
 /* 
  * The last item isn’t required to have a
  * separator.
  */
 if (in_item)
 ++num_items;
 
 /*
  * We’ve built the modified menu list in 
  * place, so discard the rest of the list.
  */
 *cp1 = ‘\0’;
 
 return (num_items);
}
Listing:  PieXCMD.c

/*
 * File:PieXCMD.c
 * Purpose: XCMD interface for pie popup menus.
 */
#include<MacTypes.h>
#include<MenuMgr.h>
#include<QuickDraw.h>
#include<SetUpA4.h>
#include“HyperXCmd.h”

/* Local function prototypes. */
pascal  void main(XCmdBlockPtr);

/* External function prototypes.*/
intGetLocOfCardWindow(XCmdBlockPtr, Point *);
Handle  CopyStrToHand(char *);
long  HandleToNum(XCmdBlockPtr, Handle);
void  HandleToPstr(Str255, Handle);
char  *ToCstr(char *);
char  *ToPstr(char *);

#define ERRORFLAG(short)  -1
#define MENU_SEPARATOR    ‘;’
#define USAGE  “Usage: put\
 PiePopup(\”Menu;list\”,  \
      font#,fontsize,pat_flag)\
 into container”

pascal  void main(XCmdBlockPtr paramPtr)
{
 Str255 menuList;/* list of menu items */
 int  num_menu_items; /* # of menu items */
 char tempStr[32]; /* str scratch buffer */
 int  selection; /* menu item selected */
 Point  cardWindowLoc;/* popup positioner */
 int  font = geneva;/* selected font */
 int  font_size = 10;/* selected size */
 int  pattern_flag = TRUE;/*use patterns*/
 
 /* 
  * LightSpeed C allows global variables to be
  * referenced from A4.
  */
 RememberA0();
 SetUpA4();
 
 /* 
  * Make sure we were called with the proper
  * number of arguments.
  */
 if (paramPtr->paramCount > 4) {
 paramPtr->returnValue = (Handle) CopyStrToHand(USAGE);
 return;
 } 
 
 /* 
  * Bias the origin of the popup by 
  * HyperCard’s window location 
  */
 if ((GetLocOfCardWindow(
   paramPtr,&cardWindowLoc)) == ERRORFLAG) {
 return;
 }

 /* Buffer the menu list. */
 strcpy(menuList, *paramPtr->params[0]);
 
 /* 
  * Strip the features that are supported by
  * the Mac’s menu manager but we ignore.
  * These include: line separators, check
  * marks, icons, etc.
  */
 num_menu_items = ConvertMenuList(menuList);
 
 /* Obtain any optional parameters */
 if ((paramPtr->paramCount > 1) && 
 (paramPtr->params[1] != ‘\0’))
 font = (int) HandleToNum(
 paramPtr, paramPtr->params[1]);
 
 if ((paramPtr->paramCount > 2) && 
 (paramPtr->params[2] != ‘\0’))
 font_size = (int) HandleToNum(
 paramPtr, paramPtr->params[2]);
 
 if ((paramPtr->paramCount > 3) &&
 (paramPtr->params[3] != ‘\0’))
 pattern_flag = (int) HandleToNum(
 paramPtr, paramPtr->params[3]);

 /* Put up the pie popup menu */
 selection = DrawPieMenu(cardWindowLoc,
 menuList, num_menu_items,
 font, font_size, pattern_flag);
 
 /* Return the menu selection to HyperCard */
 NumToStr(paramPtr, (long) selection,
  (unsigned char *) tempStr);
 paramPtr->returnValue = (Handle)CopyStrToHand
 (ToCstr((char *) tempStr));
 
 /* Recover original A4 and return */
 RestoreA4();
 return;
}
 
AAPL
$97.23
Apple Inc.
-0.92
MSFT
$43.45
Microsoft Corpora
-0.13
GOOG
$583.21
Google Inc.
-4.21

MacTech Search:
Community Search:

Software Updates via MacUpdate

Knock 1.1.7 - Unlock your Mac by knockin...
Knock is a faster, safer way to sign in. You keep your iPhone with you all the time. Now you can use it as a password. You never have to open the app -- just knock on your phone twice, even when it's... Read more
Mellel 3.3.6 - 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
LibreOffice 4.3.0.4 - 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
Freeway Pro 7.0 - Drag-and-drop Web desi...
Freeway Pro lets you build websites with speed and precision... without writing a line of code! With it's user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of... Read more
Drive Genius 3.2.4 - Powerful system uti...
Drive Genius is an OS X utility designed to provide unsurpassed storage management. Featuring an easy-to-use interface, Drive Genius is packed with powerful tools such as a drive optimizer, a... Read more
Vitamin-R 2.15 - Personal productivity t...
Vitamin-R creates the optimal conditions for your brain to work at its best by structuring your work into short bursts of distraction-free, highly focused activity alternating with opportunities for... Read more
Toast Titanium 12.0 - The ultimate media...
Toast Titanium goes way beyond the very basic burning in the Mac OS and iLife software, and sets the standard for burning CDs, DVDs, and now Blu-ray discs on the Mac. Create superior sounding audio... Read more
OS X Yosemite Wallpaper 1.0 - Desktop im...
OS X Yosemite Wallpaper is the gorgeous new background image for Apple's upcoming OS X 10.10 Yosemite. This wallpaper is available for all screen resolutions with a source file that measures 5,418... Read more
Acorn 4.4 - Bitmap image editor. (Demo)
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
Bartender 1.2.20 - Organize your menu ba...
Bartender lets you organize your menu bar apps. Features: Lets you tidy your menu bar apps how you want. See your menu bar apps when you want. Hide the apps you need to run, but do not need to... Read more

Latest Forum Discussions

See All

Why Does It Spin? (Games)
Why Does It Spin? 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: THERE'S ONLY ONE RULE: DON'T TOUCH THE WALLS! Do you think you're able to follow this simple rule even if you would have to... | Read more »
Ice Wings Plus (Games)
Ice Wings Plus 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: THE GREAT ENDLESS RUNNER OF COMBAT JETS IS BACK !! With more than 680.000 downloads in the App Store, Ice Wings: Skies of Steel... | Read more »
Murl the Squirrel (Games)
Murl the Squirrel 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Meet Murl. He is teased by a group of flying squirrels because he can't fly. Determined to show them he's can fly, he meets... | Read more »
Celleste (Games)
Celleste 0.1 Device: iOS Universal Category: Games Price: $2.99, Version: 0.1 (iTunes) Description: Lots of cute action with amazing 3D graphics and a new type of gameplay! Take control over the forces of the universe to help a group... | Read more »
Super Heavy Sword (Games)
Super Heavy Sword 0.0.1 Device: iOS Universal Category: Games Price: $.99, Version: 0.0.1 (iTunes) Description: Get Ready to Get HEAVY! Monster Robot Studios presents SUPER Heavy Sword! The sequel to the smash hit HEAVY sword which... | Read more »
Angels In The Sky (Games)
Angels In The Sky 1.00 Device: iOS Universal Category: Games Price: $6.99, Version: 1.00 (iTunes) Description: - A.I.S will only run smoothly on iPhone 5s. It's NOT compatible with iPad, iPhone 5 or earlier devices.- In order to... | Read more »
80 Days (Games)
80 Days 1.0.2 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.2 (iTunes) Description: 1872, with a steampunk twist. Phileas Fogg has wagered he can circumnavigate the world in just eighty days. Choose your own route... | Read more »
Micromon (Games)
Micromon 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: 130+ Animated Monsters to Catch & Battle! No waiting, play at your own pace! Embark on an epic monster capture RPG like none... | Read more »
Empire Manager (Games)
Empire Manager 1.0 Device: iOS iPhone Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: Become ruler of an empire. Manage your economy, develop technology, hire an army and conquer the world in this addictive turn-... | Read more »
Empire Manager HD (Games)
Empire Manager HD 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Become ruler of an empire. Manage your economy, develop technology, hire an army and conquer the world in this addictive... | Read more »

Price Scanner via MacPrices.net

iPad Cannibalization Threat “Overblown”
Seeking Alpha’s Kevin Greenhalgh observes that while many commentators think Apple’s forthcoming 5.5-inch panel iPhone 6 will cannibalize iPad sales, in his estimation, these concerns are being... Read more
Primate Labs Releases July 2014 MacBook Pro P...
Primate Labs’ John Poole has posted Geekbench 3 results for most of the new MacBook Pro models that Apple released on Tuesday. Poole observes that overall performance improvements for the new MacBook... Read more
Apple Re-Releases Bugfixed MacBook Air EFI Fi...
Apple has posted a bugfixed version EFI Firmware Update 2.9 a for MacBook Air (Mid 2011) models. The update addresses an issue where systems may take longer to wake from sleep than expected, and... Read more
Save $50 on the 2.5GHz Mac mini, plus free sh...
B&H Photo has the 2.5GHz Mac mini on sale for $549.99 including free shipping. That’s $50 off MSRP, and B&H will also include a free copy of Parallels Desktop software. NY sales tax only. Read more
Save up to $140 on an iPad Air with Apple ref...
Apple is offering Certified Refurbished iPad Airs for up to $140 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free. Stock tends to come and go with some of these... Read more
$250 price drop on leftover 15-inch Retina Ma...
B&H Photo has dropped prices on 2013 15″ Retina MacBook Pros by $250 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.3GHz Retina MacBook Pro: $2249, $250 off... Read more
More iPad Upgrade Musings – The ‘Book Mystiqu...
Much discussed recently, what with Apple reporting iPad sales shrinkage over two consecutive quarters, is that it had apparently been widely assumed that tablet users would follow a two-year hardware... Read more
13-inch 2.5GHz MacBook Pro on sale for $999,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $999.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $100 off MSRP. Price is... Read more
Save up to $300 on an iMac with Apple refurbi...
The Apple Store has Apple Certified Refurbished iMacs available for up to $300 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. These are the best prices on... Read more
WaterField Unveils 15″ Outback Solo & 13″...
Hard on the heels of Apple’s refreshed MacBook Pro Retina laptops announcement, WaterField Designs has unveiled a 15-inch version of the waxed-canvas and leather Outback Solo and a 13-inch version of... Read more

Jobs Board

Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full Read more
Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full 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
Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.