TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// LowEvents.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program:
//
// „  Contains a main event loop function, together with subsidiary functions which
//    perform nominal handling only of low-level and Operating System events.
//
// „  Opens a window in which the types of all received low-level and Operating System
//    events are displayed.
//
// „  Terminates when the user clicks the window's close box.
//
// Event handling is only nominal in this program because its main purpose is to
// demonstrate the basics of an application's main event loop.
//
// Programs in later chapters demonstrate the full gamut of individual event handling. 
//
// The program utilises the following resources:
//
// „  A 'WIND' resource (purgeable).
//
// „  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch, and
//    is32BitCompatible flags set.
//  
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

// ............................................................................. includes

#include <DiskInit.h>
#include <Sound.h>
#include <ToolUtils.h>

// .............................................................................. defines

#define rWindowResource  128

#define topLeft(r)    (((Point *) &(r))[0])
#define botRight(r)   (((Point *) &(r))[1])

// ..................................................................... global variables

Boolean    gDone;
Boolean    gInBackground;
RgnHandle  gCursorRegionHdl;

// .................................................................. function prototypes

void  main            (void);
void  doInitManagers  (void);
void  doNewWindow     (void);
void  eventLoop       (void);
void  doEvents        (EventRecord *);
void  doMouseDown     (EventRecord *);
void  doUpdate        (EventRecord *);
void  doDisk          (EventRecord *);
void  doOSEvent       (EventRecord *);
void  drawEventString (Str255);
void  doAdjustCursor  (WindowPtr);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× main

void  main(void)
{
  doInitManagers();
  doNewWindow();
  eventLoop();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInitManagers

void  doInitManagers(void)
{
  MaxApplZone();
  MoreMasters();

  InitGraf(&qd.thePort);
  InitFonts();
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);

  InitCursor();
  FlushEvents(everyEvent,0);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doNewWindow

void  doNewWindow(void)
{
  WindowPtr   windowPtr;

  if(!(windowPtr = GetNewCWindow(rWindowResource,NULL,(WindowPtr) -1)))
  {
    SysBeep(10);
    ExitToShell();
  }

  SetPort(windowPtr);
  TextSize(10);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× eventLoop

void  eventLoop(void)
{
  EventRecord  eventStructure;
  Boolean      gotEvent;
  
  gDone = false;
  gCursorRegionHdl = NewRgn();
  doAdjustCursor(FrontWindow());
  
  while(!gDone)
  {
    gotEvent = WaitNextEvent(everyEvent,&eventStructure,180,gCursorRegionHdl);
    if(gotEvent)
      doEvents(&eventStructure);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEvent

void  doEvents(EventRecord *eventStrucPtr)
{
  switch(eventStrucPtr->what)
  {
    case mouseDown:
      drawEventString("\p   „ mouseDown");
      doMouseDown(eventStrucPtr);
      break;

    case mouseUp:
      drawEventString("\p   „ mouseUp");
      break;

    case keyDown:
      drawEventString("\p   „ keyDown");
      break;

    case autoKey:
      drawEventString("\p   „ autoKey");
      break;

    case updateEvt:
      drawEventString("\p   „ updateEvt");
      doUpdate(eventStrucPtr);
      break;

    case diskEvt:
      drawEventString("\p   „ diskEvt");
      doDisk(eventStrucPtr);
      break;

    case activateEvt:
      drawEventString("\p   „ activateEvt");
      break;

    case osEvt:
      drawEventString("\p   „ osEvt - ");
      doOSEvent(eventStrucPtr);
      break;
  }
}
  
// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  SInt16     partCode;
  WindowPtr  windowPtr;

  partCode = FindWindow(eventStrucPtr->where,&windowPtr);
  
  switch(partCode)
  {
    case inContent:
      if(windowPtr != FrontWindow())
        SelectWindow(windowPtr);
      break;

    case inDrag:
      DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
      doAdjustCursor(windowPtr);
      break;

    case inGoAway:
      if(TrackGoAway(windowPtr,eventStrucPtr->where))
        gDone = true;
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  BeginUpdate((WindowPtr)eventStrucPtr->message);
  EndUpdate((WindowPtr)eventStrucPtr->message);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDisk

void  doDisk(EventRecord *eventStrucPtr)
{
  Point  thePoint;
  OSErr  osErr;

  if(HiWord(eventStrucPtr->message) != noErr)
  {
    SetPt(&thePoint,120,120);
    osErr = DIBadMount(thePoint,eventStrucPtr->message);
  }
  else
  {
    // Attempt to mount was successful.  Record drive number for accessing the disk, etc.
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOSEvent

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      if((eventStrucPtr->message & resumeFlag) == 1)
      {        
        SetCursor(&qd.arrow);
        gInBackground = false;
        DrawString("\pResume event");
      }
      else
      {
        gInBackground = true;
        DrawString("\pSuspend event");
      }
      break;
        
    case mouseMovedMessage:
      doAdjustCursor(FrontWindow());
      DrawString("\pMouse-moved event");
      break;
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× drawEventString

void  drawEventString(Str255 eventString)
{
  RgnHandle  tempRegion;
  WindowPtr  windowPtr;
  
  windowPtr = FrontWindow();
  tempRegion = NewRgn();

  ScrollRect(&windowPtr->portRect,0,-15,tempRegion);
  DisposeRgn(tempRegion);
  
  MoveTo(8,291);
  DrawString(eventString);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAdjustCursor

void  doAdjustCursor(WindowPtr frontWindow)
{
  RgnHandle  myArrowRegion;
  RgnHandle  myIBeamRegion;
  Rect       cursorRect;
  Point      mousePt;

  myArrowRegion = NewRgn();
  myIBeamRegion = NewRgn();  
  SetRectRgn(myArrowRegion,-32768,-32768,32766,32766);
  
  cursorRect = frontWindow->portRect;
  LocalToGlobal(&topLeft(cursorRect));
  LocalToGlobal(&botRight(cursorRect));  

  RectRgn(myIBeamRegion,&cursorRect);
  DiffRgn(myArrowRegion,myIBeamRegion,myArrowRegion);
  
  GetMouse(&mousePt);
  LocalToGlobal(&mousePt);
  if(PtInRgn(mousePt,myIBeamRegion))
  {
    SetCursor(*(GetCursor(iBeamCursor)));
    CopyRgn(myIBeamRegion,gCursorRegionHdl);
  }
  else
  {
    SetCursor(&qd.arrow);
    CopyRgn(myArrowRegion,gCursorRegionHdl);
  }
  
  DisposeRgn(myArrowRegion);
  DisposeRgn(myIBeamRegion);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

Demonstration Program Comments

When the program is run, the user should move the mouse cursor inside and outside the
window, click the mouse inside and outside the window, drag the window, press and release
keyboard keys, and insert initialised and uninitialised disks, noting the types of events
generated by these actions as printed on the scrolling display inside the window.

The user should also note the basic window deactivation and activation which occurs when
the mouse is clicked outside, and then inside the window.

The program may be terminated by a click in the window's go-away box.

The general "flow" of the program is illustrated in the flow chart at Fig 4.

(LowEvents flowchart)

#define

rWindowResource establishes a constant for the ID of the 'WIND' resource.

The remaining two lines define two common macros.  The first converts the top and left
fields of a Rect to a Point.  The second converts the bottom and right field of a Rect to
a Point.

Global Variables

The global variable gDone controls the termination of the main event loop and thus of the
program.  gInBackground will be set to true when the application is about to move to the
background and to false when the application returns to the foreground. gCursorRegionHdl
will be assigned the handle to a region to be passed in the mouseRgn parameter of the
WaitNextEvent function.

main

The main function calls the application-defined functions for initialising the system
software managers and creating the window.  It then calls the function containing the
main event loop.

doInitManagers

doInitManagers is the standard system software managers initialisation function which
will be used in all demonstration programs.

Note that the call to FlushEvents at has now been added to this function.  FlushEvents
empties the Operating System event queue of any low-level events left unprocessed by
another application, for example, any mouse-down or keyboard events that the user may
have entered while this program was being launched.

doNewWindow

The function doNewWindow opens the window in which the types of low-level and Operating
System events will be printed as they occur.  The 'WIND' resource passed as the first
parameter specifies that the window has a go-away box and a title (drag) bar.  The
window's graphics port is set as the current port for drawing and the text size is set to
10 points.

eventLoop

eventLoop is the main event loop.

The global variable gDone is set to false before the event loop is entered.  This
variable will be set to true when the user clicks on the window's go-away box.  The event
loop (the while loop) terminates when gDone is set to true.

The calls to NewRgn and doAdjustCursor have to do with the generation of mouse-moved
events.  The NewRgn call allocates storage for a Region structure and initializes the
contents of the region to make it an empty region.  As will be seen, this first call to
doAdjustCursor defines two regions (one for the arrow cursor and one for the I-Beam
cursor) and copies the handle to one of them (depending on the current position of the
mouse cursor) to the global variable gCursorRegionHandle.

In the call to WaitNextEvent:

*   The event mask everyEvent ensures that all types of low-level and Operating System
    events will be returned to the application (except keyUp events, which are masked 
    out by the system event mask).

*   eventStructure is the EventRecord structure which, when WaitNextEvent returns, will
    contain information about the event.

*   180 represents the number of ticks for which the application agrees to relinquish 
    the processor if no events are pending for it.  180 ticks equates to about three
    seconds.

*   If the cursor is now not within the region passed in the cursorRegion parameter, 
    a mouse-moved event will be generated immediately.

WaitNextEvent returns true if an event was pending, otherwise it returns NULL.  If an
event was pending, the program branches to doEvent to determine the type of event and
handle the event according to its type.

doEvents

doEvents handles some events to finality and performs initial handling of others.

On return from WaitNextEvent, the what field of the event structure contains an unsigned
short integer which indicates the type of event received.  The doEvent function isolates
the type of event and switches according to that type.

In this demonstration, the action taken in every case is to print the type of event in
the window.  In addition, and in the case of mouse-down, update, disk and Operating
System events only, calls to individual event handling functions are made.

Note that, in the case of an Operating System event, doEvent will only print "osEvt - "
in the window.  At this stage, the program has not yet established whether the event is a
suspend, resume or mouse-moved event.

Note also that:

*   The inclusion of the key-up event handling would be pointless, since key-up events
    are masked out by the Operating System.

*   Only one activate event will ever be received when the program is run (that is, 
    when the window opens), the reasons being that only one window is ever open and the
    doesActivateOnFGSwitch flag in the 'SIZE' resource is set.  This latter means that
    activate events will not accompany suspend and resume events.

doMouseDown

The function doMouseDown handles mouse-down events to completion.

FindWindow is called to get a pointer to the window in which the event occurred and a
"part code" which indicates the part of that window in which the mouse-down occurred. 
The function then switches according to that part code.

The inContent case deals with a mouse-down in a window's content region.  FrontWindow
returns a pointer to the frontmost window.  If this is not the same as the pointer in the
event structure's message field, SelectWindow is called to generate activate events and
to perform basic window activation and deactivation.  (Actually, SelectWindow will never
be called in this demonstration because the program only opens one window, which is
always the front window.)

The inDrag case deals a mouse-down in the window's drag bar.  In this case, control is
handed over to DragWindow, which tracks the mouse and drags the window according to mouse
movement until the mouse button is released.  DragWindow requires a boundary rectangle
limiting the area in which the window can be dragged.  This is supplied in the third
argument which, in this case, is established by the bounds field of the QuickDraw global
variable screenBits.  screenBits.bounds contains a rectangle which encloses the main
screen.

The regions controlling the generation of mouse-moved events are defined in global
coordinates.  The region for the I-Beam cursor is based on the window's port rectangle. 
Accordingly, when the window is moved, the new location of the port rectangle, in global
coordinates, must be re-calculated so that the arrow cursor and I-Beam cursor regions may
be re-defined.  The call to doAdjustCursor re-defines these regions for the new window
location and copies the handle to one of them, depending on the current location of the
mouse cursor, to the global variable gCursorRegionHandle.  (Note that this call to
doAdjustCursor is also required, for the same reason, when a window is re-sized or
zoomed.)

The inGoAway case deals with the case of a mouse-down in the go-away box.  In this case,
control is handed over to TrackGoAway, which tracks the mouse while the button remains
down.  When the button is released, TrackGoAway returns true if the cursor is still
inside the go-away box, in which case the global variable gDone is set to true,
terminating the event loop and the program.

doUpdate

The function doUpdate handles update events to completion.

Although no window updating is performed by this program, it is nonetheless necessary to
call BeginUpdate because, amongst other things, BeginUpdate clears the update region,
thus preventing the generation of an unending stream of update events.  The call to
EndUpdate always concludes a call to BeginUpdate, undoing the results of the
visible/update region manipulations of the latter.

doDisk

doDisk further processes a disk event.  Many applications quite reasonably ignore
unexpected disk-inserted events; however, the function doDisk is included in this
demonstration to illustrate the basics of dealing with such occurrences.

In the case of a diskEvt event, the message field of the event structure contains the
drive number in bits 0-15 and the File Manager result code in bits 16-31.  At the first
line, the high word is tested.  If it indicates that the volume was not successfully
mounted, DIBadMount is called to inform the user via dialog box shown at Fig 5.

(Dialog box invoked by DIBadMount)

DIBadMount retains control until the disk is formatted (if the user clicks in the OK box)
or until the user clicks in the Cancel box.

The call to SetPt controls the positioning of the top left corner of the dialog box on
the screen.

doOSEvent

doOSEvent first determines whether the Operating System event passed to it is a
suspend/resume event or a mouse-moved event by examining bits 24-31 of the message field. 
It then switches according to that determination.

In the case of a suspend/resume event, a further examination of the message field
establishes whether the event was a suspend event or a resume event.  The global
variable gInBackground is set to true or false accordingly.    In the case of a
resume event, the call to SetCursor ensures that the cursor will be set to the arrow
cursor shape when the application comes to the foreground.

In the case of a mouse-moved event (which occurs when the mouse cursor has moved outside
the region whose handle is currently being passed in WaitNextEvent's mouseRgn parameter),
doAdjustCursor is called to change the region passed in the mouseRgn parameter according
to the current location of the mouse.

drawEventString

drawEventString is incidental to the demonstration.  It simply prints text in the window
indicating when the call to WaitNextEvent is made and when the various types of events
are received.  ScrollRect scrolls the contents of the current graphics port within the
rectangle specified in the first parameter.  The second parameter specifies the number of
pixels to be scrolled to the right and the third parameter specifies the number of pixels
to scroll vertically, in this case 15 up.

doAdjustCursor

doAdjustCursor's primary purpose in this particular demonstration is to force the
generation of mouse-moved events.  The fact that it also changes the cursor shape simply
reflects the fact that changing the cursor shape is usually the sole reason for
generating mouse-moved events in the first place.

Basically, the function establishes two regions (the calls to NewRgn), one describing the
content area of the window (in global coordinates) and the other everything outside that. 
The location of the cursor is then ascertained by the call to GetMouse.  If the cursor is
in the content area of the window (the I-Beam region), the cursor is set to the I-Beam
shape and the handle to the I-Beam region is copied to the global variable passed in the
mouseRgn parameter in the WaitNextEvent call in the eventLoop function  If the cursor is
in the other region (the arrow region), the cursor is set to the normal arrow shape and
the arrow region is copied to the global variable passed in the mouseRgn parameter.

GetCursor reads in the system 'CURS' resource specified by the constant iBeamCursor and
returns a handle to the 68-byte Cursor structure created by the call.  The parameter for
a SetCursor call is required to be the address of a Cursor structure.  Dereferencing the
handle once provides that address.

WaitNextEvent, of course, returns a mouse-moved event only when the cursor moves outside
the "current" region, the handle to which is passed in the mouseRgn parameter of the
WaitNextEvent call.  Only one mouse-moved event, rather than a stream of mouse-moved
events, will be generated when the cursor is moved outside the "current" region because: 

*   The mouse-moved event will cause doAdjustCursor to be called.

*   doAdjustCursor will thus reset the "current" region to the region in which the 
    cursor is now located.

The cursor and cursor adjustment aspects, as opposed to the region-swapping aspects, of
the doAdjustCursor function are incidental to the demonstration.  These aspects are
addressed in more detail at Chapter 13 - Offscreen Graphics Worlds, Pictures, Cursors,
and Icons.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasnā€™t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, donā€™t remind me I could currently buy him this month Iā€™m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly ā€œborrowedā€ many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »

Price Scanner via MacPrices.net

Limited-time sale: 13-inch M3 MacBook Airs fo...
Amazon has the base 13ā€³ M3 MacBook Air (8GB/256GB) in stock and on sale for a limited time for $989 shipped. Thatā€™s $110 off MSRP, and itā€™s the lowest price weā€™ve seen so far for an M3-powered... Read more
13-inch M2 MacBook Airs in stock today at App...
Apple has 13ā€³ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Appleā€™s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Appleā€™s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
Weā€™ve updated our iPhone Price Tracker with the latest carrier deals on Appleā€™s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobileā€™s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobileā€™s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13ā€³ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Appleā€™s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16ā€³ and 14ā€³ MacBook Pros along with 13ā€³ and 15ā€³ MacBook... Read more
Every model of Appleā€™s 13-inch M3 MacBook Air...
Best Buy has Apple 13ā€³ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13ā€³ M3 MacBook Airs among... Read more

Jobs Board

Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
ā€¦relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.