TweetFollow Us on Twitter

MACINTOSH C CARBON
MACINTOSH C CARBON: A Hobbyist's Guide To Programming the Macintosh in C
Version 1.0
© 2001 K. J. Bricknell
Go to Contents Go to Program Listing

CHAPTER 22

LISTS AND CUSTOM LIST DEFINITION FUNCTIONS

Introduction to Lists

If you need the user to be able to select a single item from a small group of items, you typically provide a pop-up menu. Pop-up menus, however, do not allow the user to select multiple items from a group of items, are not especially suitable for the presentation of large numbers of items, and cannot present items in columns as well as rows. Furthermore, the items in a pop-up menu remain displayed only as long as the user holds the mouse button down.

By using lists to present a group of items to the user, you can overcome these limitations. Although lists, like pop-up menus, are generally used to solicit the user's choices, they can also be used to simply present information. Perhaps the most familiar example of such a list is that at the bottom of the window opened when you choose About This Computer... from the Mac OS 8/9 Apple menu.

In essence, then, the List Manager allows you to create either one-column or multi-column scrollable lists which may be used to simply present items of information or, as is most often the case, to enable the user to select one or more of a group of items.

By default, the List Manager creates lists which contain only monostyled text. However, with a little additional effort, you can create lists which display items graphically (as does the list on the left side of the window opened when you choose Chooser from the Mac OS 8/9 Apple menu), or which display more than one type of information in each item (as does the list in the Mac OS 8/9 About This Computer... window).

List Manager Limitations

The List Manager is not suitable for displaying large amounts of data. The limiting size for list data is 32KB, and performance degrades well before that limit is reached.

Options For Creating and Managing Lists

You can use the List Manager function LNew to create a list, in which case you must provide all the functions necessary to add rows and data to the list, handle mouse and keyboard events, etc. Alternatively, you can use the list box control to simplify the matter of list creation and handling.

The first section of this chapter addresses the former method and the subject of lists in general. The second section addresses the list box control, and indicates those areas in the first section which are not relevant when you use list box controls.

Appearance and Features of Lists

Fig 1 shows a dialog with two typical single-column lists. The items in the list on the left are exclusively text items and the items in the list on the right are recorded pictures comprising a graphic and a title string. The list on the left supports the selection of multiple items.

To create a list with graphical elements, such as the list at the right at Fig 1, you must write a custom list definition function (see below), because the default list definition function only supports the display of text.

Cells, Cell Font, and Cell Highlighting

Cells

A list is a number of items displayed within a rectangle, each item being contained within an invisible rectangular cell. Cells may contain different types of data, but all cells within a particular list are of the same size.

Cell Font

By default, lists inherit the font of the graphics port associated with the window or dialog in which they reside. Ordinarily, your text-only lists should use the large or small system font.

In the case of list control boxes, the font may be set using SetControlFontStyle or a 'dftb' resource.

Regardless of the font your application uses, if a string is too long to fit in its cell using the current font, the List Manager uses condensed type in an attempt to make it fit (Mac OS 8/9 only). Failing that, the List Manager truncates the string and appends the ellipsis character.

Cell HighLighting

Your application may or may not allow the user to select one or more cells in a list. If your application allows users to select cells, then, when the user selects a cell, the List Manager automatically highlights that cell.

Scroll Bars

Lists may contain scroll bars, which allow you to include more items in a list than can be contained within the list's display rectangle. If a list includes a scroll bar but the number of cells is such that they are all visible, the List Manager disables the scroll bar.

Selection of Cells Using The Mouse

LClick

Your application must call LClick whenever a mouse-down occurs in an active list. LClick handles all user interaction within the list until the user releases the mouse button. This includes cell highlighting and, when the user drags the mouse outside the list's display rectangle, automatic list scrolling. LClick also examines the state of the Shift and Command keys, which are central to the process of multiple cell selection in lists.

Multiple Cell Selection Using the Default Cell-Selection Algorithm

The List Manager's cell-selection algorithm allows the user to select a contiguous range of cells, or even several discontiguous ranges of cells, by using the Shift and Command keys in conjunction with the mouse. The following describes the default cell-selection behaviour.

If the user presses both the Shift and Command keys when clicking a cell, the Shift key is ignored.

Cell Selection With the Shift Key

The user can extend a selection of just one cell to several contiguous cells by pressing the Shift key and clicking another item. By clicking and dragging with the Shift key down, the user can extend or shrink the range of selected cells. If the cursor is dragged outside the list's display rectangle, the list will scroll so as to enable the user to include cells which were not initially visible.

Cell Selection With the Command Key

A range of cells may be added or deleted from the current selection by pressing the Command key and then dragging the cursor over other cells. The List Manager checks the status of the first cell clicked in so as to determine whether to add or remove selections. All cells in the range over which the cursor passes will be deselected if that first cell is initially selected. On the other hand, all cells in the range over which the cursor passes will be selected if that first cell is initially not selected.

When a cell's selection status is changed by Command-dragging, that selection status remains the same for the duration of the drag, that is, it will not change if the user moves the cursor back over the cell. The effect of the Command key thus differs from that of the Shift key in this respect.

Shift-Clicking - Discontiguous Cells Selected

Discontiguity is lost if the user Shift-clicks a cell after having previously created discontiguous selections. The List Manager selects all cells in the range of the selected cell closest to the top of the list and the newly selected cell Ń unless the newly selected cell precedes the first selected cell, in which case the List Manager selects all cells in the range of the newly selected cell and the selected cell closest to the bottom of the list.

Customising the Cell-Selection Algorithm

As will be seen, the List Manager's cell-selection algorithm may easily be customised so as to modify its default behaviour. The most common modification is to defeat multiple cell selection, allowing the user to select only one cell.

Selection of Cells Using the Keyboard

Some users prefer to use the keyboard to select cells in lists. Your application should support the following methods of cell selection via the keyboard:

  • Cell Selection Using Arrow Keys. This method involves the use of the Arrow keys to move and extend cell selections.

  • Type Selection. This method involves the user simply typing the text associated with an item. It is thus only relevant to text-only lists (or lists whose items can be identified by text strings).

Cell Selection Using Arrow Keys

The List Manager does not provide any functions to support cell selection by Arrow key. Accordingly, your application must supply all of the necessary code. The following describes what that code should do.

Moving the Selection Using Arrow Keys

Shift and Command Keys Not Down

When the user presses an Arrow key, and is not at the same time pressing the Shift or Command key, the selection should be moved by one cell.

If the user presses the Up Arrow, for example, your application should respond by selecting the cell which is above the first selected cell and by deselecting all other selected cells. (Of course, if the first selected cell is the topmost cell in the list, your application should respond by simply deselecting all cells other than the first selected cell.) If necessary, your application should then scroll the list to ensure that the newly-selected cell is visible.

Command Key Down

When the user presses an Arrow key while at the same time pressing the Command key, your application should move the first selected cell or the last selected cell (depending on which arrow key is used) as far as it can move in the appropriate direction. For example, in a single-column list, pressing the Up Arrow key should select the first cell in the list, deselect all other cells, and scroll the list, if necessary, to ensure that the newly-selected cell is visible.

Extending the Selection Using Arrow Keys

When the user presses an Arrow key while the Shift key is down, the user is attempting to extend the selection. There are two alternative algorithms your application can use to respond to Shift-Arrow key combinations: the extend algorithm and the anchor algorithm. The easiest one to implement is the extend algorithm.

The Extend Algorithm

Using the extend algorithm, your application simply finds the first (or last) selected cell, and then selects another cell in the direction of the Arrow key. For example, if the user presses Shift-Down Arrow in a single-column list, the application should find the last selected cell and select the cell immediately below it, or, if the user presses Shift-Up Arrow, the application should find the first selected cell and select the cell above it. As always, the list should then be scrolled, if necessary, to make the newly-selected cell visible.

Type Selection

The List Manager does not provide any functions to support type selection, although the Text Utilities provide a data type and functions which support this method of keyboard selection. That said, your application must provide most of the code. The following describes what that code should do.

In a text-only list, when the user types the text of an item in a list, the list should scroll to that cell and select it.

However, rather than requiring the user to type the entire text of the item before searching for a match, your application should repeatedly search for a match as each character is entered. Accordingly, every time the user types a character, your application should add it to a string. If this string is currently two characters long, for example, your application should then walk the cells of the list, comparing these two characters with the first two characters of the text in each cell. If a match is found, that cell should be selected and the list scrolled, if necessary, to make the cell visible.

Your application should automatically reset the internal string to a null string when the user has not pressed a key for a given amount of time. To make your application consistent with other applications and the Finder, this time should be twice the number of ticks contained in the low memory global KeyThresh. (The value in KeyThresh is set by the user at the "Delay Until Repeat" section of the Keyboard control panel (Mac OS 8/9) and System Preferences/Keyboard (Mac OS X).)

Implementing Type Selection

To implement type selection, your application must store the characters the user has typed and the time when the user last typed a character. The Text Utilities TypeSelectRecord structure may be used for this purpose:

     struct TypeSelectRecord 
     {
       unsigned long tsrLastKeyTime;  // Time when the last character was typed
       ScriptCode    tsrScript;
       Str63         tsrKeyStrokes;   // Characters typed by the user
     };
     typedef struct TypeSelectRecord TypeSelectRecord;

The Text Utilities function TypeSelectNewKey adds a new character to the tsrKeyStrokes field.

A global variable of type SInt16 should be used to store the number of ticks after which type selection must be reset. The low memory accessor function LMGetKeyThresh may be used to obtain the value in the low memory global KeyThresh.

The Text Utilities function TypeSelectClear may be used to reset the tsrKeyStrokes and tsrLastKeyTime fields of the TypeSelectRecord structure to NULL and 0 respectively.

LSearch should be called to search the list's cells for a match with the specified characters, a universal procedure pointer to a comparison function being passed in the searchProc parameter.

Creating, Disposing Of, and Managing Lists

The List Structure

The list structure, which the List Manager uses to keep track of information about a list, is central to the creation and management of lists. Although the list structure is not opaque, you are encouraged to access the list structure indirectly using the provided accessor functions because this will give your application greater threading capability on Mac OS X.

Before describing the list structure and its associated accessor functions, however, it is necessary to describe another data type used exclusively by the List Manager, that is, the Cell data type.

The Cell Data Type

A cell in a list can be described by the Cell data type, which has the same structure as the Point data type:

     typedef Point Cell;

The Cell data type's fields, however, have a different meaning from those of the Point data type. In the Cell data type, the h field specifies the row number and the v field specifies the column number. The first cell in a list is defined as cell (0,0). Fig 2 shows a multi-column list in which each cell's text is set to the coordinates of the cell.

The ListRec Structure

The list structure is defined by the ListRec data type:

     struct ListRec 
     {
       Rect              rView;         // List's display rectangle.
       GrafPtr           port;          // List's graphics port.
       Point             indent;        // Indent distance for drawing.
       Point             cellSize;      // Size in pixels of a cell.
       Rect              visible;       // Boundary of visible cells.
       ControlRef        vScroll;       // Vertical scroll bar.
       ControlRef        hScroll;       // Horizontal scroll bar.
       SInt8             selFlags;      // Selection flags.
       Boolean           lActive;       // true if list is active.
       SInt8             lReserved;     // (Reserved.)
       Sint8             listFlags;     // Automatic scrolling flags.
       long              clikTime;      // TickCount at time of last tick.
       Point             clikLoc;       // Position of last click.
       Point             mouseLoc;      // Current mouse location.
       ListClickLoopUPP  lClickLoop;    // Function called by LClick.
       Cell              lastClick;     // Last cell clicked.
       long              refCon;        // For application use.
       Handle            listDefProc;   // List definition function.
       Handle            userHandle;    // For application use.
       ListBounds        dataBounds;    // Boundary of cells allocated.
       DataHandle        cells;         // Cell data.
       short             maxIndex;      // (Used internally.)
       short             cellArray[1];  // Offsets to data.
     };

     typedef struct ListRec ListRec;
     typedef ListRec *ListPtr;
     typedef ListPtr *ListHandle;

Field Descriptions and Associated Accessor Functions

rView

The list's visible rectangle (local coordinates). This does not include the area occupied by the list's scroll bars (if any).

Accessor Functions: GetListViewBounds SetListViewBounds

port The graphics port of the window containing the list. The coordinates of the list's visible rectangle are local to this port.

Accessor Functions: GetListPort SetListPort

indent

The location, relative to the upper left corner of the cell, at which drawing should begin. For example, the default list definition function sets the vertical coordinate of this field to near the bottom of the cell so that characters drawn with QuickDraw's DrawText function are centred vertically in the cell.

Accessor Functions: GetListCellIndent SetListCellIndent

cellSize

The size (in pixels) of each cell in the list. For text-only lists, you usually let the List Manager automatically calculate the cell dimensions. In this case, the List Manager adds the ascent, descent and leading of the port's font to arrive at the height of a cell (which works out as 16 pixels for 12-point Charcoal on Mac OS 8/9, for example). You should make the height of your list equal to a multiple of cell height. For cell width, the List Manager divides the width of the list's display rectangle by the number of columns in the list.

Accessor Functions: GetListCellSize SetListCellSize

visible

Specifies those cells in a list that are visible within the rView rectangle. The left and top fields are set by the List Manager to the coordinates of the first visible cell. The right and bottom fields are set to one greater than the horizontal and vertical coordinates of the last visible cell. If, for example, the first three columns and six rows are visible (that is, the last visible cell has coordinates (2,5), the List Manager sets the visible field to (0,0,3,6).

The List Manager sets the right and bottom fields to one greater than the horizontal and vertical coordinates of the last visible cell so as to facilitate the use of QuickDraw's PtInRect function to determine whether a cell is currently visible. When PtInRect is used for this purpose, a Cell variable is passed as the first parameter and the visible field is passed as the second parameter. When PtInRect's parameters are expressed as cell coordinates, the cells "hang" down and to the right of the mathematical rectangle. Thus, in the above example, if the cell passed as the first parameter to PtInRect specifies row 6 or higher or column 3 or higher, PtInRect returns false.

The fact that the visible field is set in this way also means that the number of visible rows and columns may be determined by simply subtracting the value in the top field from the value in the bottom field (rows) and the value in the left field from the value in the right field (columns).

Accessor Functions: GetListVisibleCells SetListVisibleCells

vScroll

Handle to the vertical scroll bar (or NULL if there is no vertical scroll bar).

Accessor Functions: GetListVerticalScrollBar SetListVerticalScrollBar

hScroll

Handle to the horizontal scroll bar (or NULL if there is no horizontal scroll bar).

Accessor Functions: GetListHorizontalScrollBar SetListHorizontalScrollBar

selFlags

The algorithm the List Manager uses to select cells in response to a click in the list.

Accessor Functions: GetListSelectionFlags SetListSelectionFlags

lActive

true if a list is active or false if it is inactive. (Do not change this field directly. Use LActivate to activate or deactivate a list.)

Accessor Functions: GetListActive LActivate

listFlags

Flags indicating whether automatic vertical and horizontal scrolling is enabled. If automatic scrolling is enabled, the list scrolls when the user clicks a cell and then drags the cursor out of the rView rectangle. If the list has the associated scroll bar (horizontal or vertical), automatic scrolling is enabled by default. The following constants are used to specify whether horizontal and vertical autoscrolling should be enabled or disabled:

     lDoVAutoscroll = 2  Allows vertical scrolling.
     lDoHAutoscroll = 1  Allows horizontal scrolling.

Accessor Functions: GetListFlags SetListFlags

clikTime

Time when the user last clicked the mouse.

Accessor Functions: GetListClickTime SetListClickTime

clikLoc Local coordinates of the last mouse click. >

Accessor Function: GetListClickLocation

mouseLoc

Current location of the cursor in local coordinates.

Accessor Function: GetListMouseLocation

lClickLoop

Contains NULL if the default click loop function is to be used. However, your application cab assign a universal procedure pointer to a custom click-loop function to this field. Your application will not need a custom click-loop function unless it needs to perform some special processing while the user drags the cursor.

Accessor Functions: GetListClickLoop SetListClickLoop

lastClick

Cell coordinates of the last click. You can access the value in this field using LLastClick.

Accessor Function: SetListLastClick

refCon

For your application's use.

Accessor Functions: GetListRefCon SetListRefCon

listDefProc

Handle to the list definition function code.

Accessor Functions: GetListDefinition SetListDefinition

userHandle

For your application's use. Typically, applications use this field to store a handle to some additional storage associated with a list.

Accessor Functions: GetListUserHandle SetListUserHandle

dataBounds

The total cell dimensions of the list. It is similar to the visible field in that its right and bottom fields are each set to one greater than the horizontal and vertical coordinates of the last cell Ń except that, in this case, the "last cell" is the last cell in the list, not the last cell in the visible rectangle. For example, if a list contains 5 columns and 12 rows (that is, the last cell in the list has coordinates (4,10)), the dataBounds field is set to (0,0,5,12).

Accessor Function: GetListDataBounds

cells

Handle to a relocatable block where cell data is stored. Because of the way this field is defined, no list can contain more than 32,000 bytes of data.

Accessor Function: GetListDataHandle

cellArray

Offsets to data in the relocatable block specified by the cells field. Do not change the cells field, or access the information in the cellArray field, directly.

The fields of a list structure that you will be most concerned with are the rView, port, cellSize, visible, and dataBounds fields.

Creating Lists

Creating Lists Which Do Not Use a Custom List Definition Function

If you are creating a list that does not use a custom list definition function, you should use the function LNew to create the list:

     ListHandle  LNew(const Rect *rView,const ListBounds *dataBounds,Point cSize,
                 short theProc,WindowRef theWindow,Boolean drawIt,Boolean hasGrow,
                 Boolean scrollHoriz,Boolean scrollVert);

rView

Rectangle in which to display the list. This rectangle is in the local coordinates of the window passed in the theWindow parameter, and does not include the area occupied by the list's scroll bars.

dataBounds

Initial data bounds. For example, to create a list with 5 columns and 10 rows, set the left and top fields to (0,0) and the right and bottom fields to 5 and 10 respectively.

cSize

Size of each cell. If your application is using the default list definition function and passes (0,0) in this field, the size is calculated automatically.

theProc

Pass 0 in this parameter to cause the default list definition function to be used. (See also the Carbon Note below.)

theWindow

Reference to the window in which the list is to be installed.

drawIt

Specifies whether automatic drawing mode is to be initially enabled. When automatic redrawing is enabled (by setting this parameter to true), the list is automatically redrawn whenever it is changed.

This setting can be changed later using LSetDrawingMode. If your application chooses to disable automatic drawing mode (for example, for aesthetic reasons while adding rows and columns to a list) it should do so only for short periods of time.

hasGrow

Specifies whether space should be left for a size box/resize control.

scrollHoriz

Pass true if your list requires a horizontal scroll bar, otherwise pass false.

scrollVert

Pass true if your list requires a vertical scroll bar, otherwise pass false.

Creating Lists Which Use a Custom List Definition Function

If you are creating a list which uses a custom list definition function, you should use the function CreateCustomList to create the list:

     OSStatus  CreateCustomList(const Rect *rView,const ListBounds *  dataBounds,
               Point cellSize,const ListDefSpec *theSpec,
               WindowRef theWindow,Boolean drawIt,Boolean hasGrow,
               Boolean scrollHoriz,Boolean scrollVert,ListHandle *outList);

The main difference between LNew and CreateCustomList is that the former takes the resource ID of a list definition function whereas the latter takes a universal procedure pointer to a list definition function.

In the Classic API, custom list definition functions are compiled separately as 'CODE' resources and the resource ID is passed in the theProc parameter of LNew. In Carbon, custom definition functions (and, indeed, all other custom definition functions) cannot be stored in resources; accordingly, the function CreateCustomList was introduced with Carbon to accommodate that situation.

Drawing List Box Frames and Focus Rectangles

List Box Frame

The List Manager does not draw the list box frame around the list. Accordingly, this must be drawn by your application.

Focus Rectangle

In a window with multiple lists, you need to indicate to the user which list is the current list, that is, which list is the target of current mouse and keyboard activity. Accordingly, you should draw a focus rectangle around the current list. (See the list on the left at Fig 1). The focus rectangle should be removed when the window or dialog containing the lists is deactivated.

A single list in a window should also be outlined with a focus rectangle if keyboard input could have some other effect in the window not related to the list (for example, if the list is in a dialog containing both a list and an editable text item).

Disposing of a List

When you are finished with a list, you should dispose of it using LDispose, which disposes of the list structure as well as the data associated with the list. LDispose does not, however, dispose of any application-specific data you may have stored in a relocatable block specified by the userHandle field of the list structure. This should be separately disposed of before the call to LDispose.

Adding Rows and Columns to a List

When an application creates a list, it might choose to, for example, pre-allocate the columns it needs and then add rows to the list one by one. It might also create the list and add both rows and columns to it later.

Rows are inserted into a list using LAddRow and deleted using LDelRow. Columns are inserted in a list using LAddColumn and deleted using LDelColumn.

Disabling and Enabling the Automatic Drawing Mode

LSetDrawingMode should be used to turn off the automatic drawing mode before making changes to a list. After the changes have been made, LSetDrawingMode should be called again, this time to turn the automatic drawing mode back on.

InvalRect should be called after the second call to LSetDrawingMode to invalidate the rectangle containing the list and its scroll bars. LUpdate, which should be called when your application receives an update event, will then redraw the list.

Responding to Events in a List

Mouse-Down Events

As previously stated, when a mouse-down event occurs in a list, including in the associated scroll bar areas, your application must call LClick. If the click is outside the list's display rectangle or scroll bars, LClick returns immediately, otherwise it handles all user interaction until the user releases the mouse button. While the mouse button is down, the List Manager performs scrolling as necessary, selects or de-selects cells as appropriate, and adjusts the scroll bars.

Note that LClick returns true if the click was a double click. If the list is in a dialog, your application should respond to a double click in the same way that it would respond to a click on the default (OK) button.

In the case of multiple lists, if the mouse-down occurs inside a non-current list's display rectangle or scroll bar area, your application should call its function for changing the current list.

Key-Down Events

If a key-down event is received, and assuming that your application supports cell selection by Arrow key and/or type selection, your application should call its appropriate functions. In the case of multiple lists, your application should also respond to Tab key presses by changing the current list.

Update Events

If an update event (Classic event model) or kEventWindowDrawContent event type (Carbon event model) is received, your application must call LUpdate to redraw the list. The region specified in the first parameter to the LUpdate call is usually the window's visible region as retrieved by GetPortVisibleRegion.

Your application will also need to draw the list box frame in the correct state (window active or inactive) and, if a focus rectangle is required and the window is active, the focus rectangle.

Activate Events

If an activate event (Classic event model) or kEventWindowActivated or kEventWindowDeactivated event type is received (Carbon event model), your application must call LActivate to activate or deactivate the list as appropriate. Your application will also need to draw the list box frame in the correct state, and either draw or erase the keyboard focus rectangle, depending on whether the window is becoming active or inactive.

If your application supports type selection in a list, it will also need to reset certain type selection variables when the window containing that list is becoming active.

Getting and Setting List Selections

The List Manager provides functions for determining which cells are currently selected and for selecting and deselecting cells. LGetSelect is used to either determine whether a specified cell is selected or to keep advancing from a specified starting cell until the next selected cell is found. LSetSelect is used to select or deselect a specified cell.

LNextCell, which simply advances from one cell in a list to the next, is often used in functions associated with getting and setting list selections.

Scrolling a List

LAutoScroll may be used to scroll the first selected cell to the upper-left corner of the list's display rectangle.

LScroll allows your application to scroll the list by a specified number of rows and/or columns. Typically, you would use LScroll when you want your application to scroll a list just enough so that a certain cell (such as the cell the user has just selected using the an Arrow key or type selection) is visible.

Storing, Adding To, Getting, and Clearing Cell Data

Storing Data

Your application can store data in a cell using LSetCell. LSetCell's parameters include a pointer to the data, the length of the data, the location of the cell whose data you wish to set, and a handle to the list containing the cell. The data stored in a cell might be sourced from, for example, a string list resource.

Adding to Data

Your application can append data to a cell using LAddToCell.

Getting Cell Data

LGetCell may be used to copy the contents of a cell into a buffer. LGetCellDataLocation may be used to obtain the address and length of a cell's data. Unlike LGetCell, LGetCellDataLocation does not make a copy of the data, and should thus be used when you want to access, but not manipulate, the data.

Clearing Data

Your application can remove all data from a cell using LClrCell.

Searching a List

Your application can use LSearch to search through a list for a particular item. LSearch takes, as one parameter, a universal procedure pointer to a match function. If NULL is specified for this parameter, LSearch searches the list for the first cell whose data matches the specified data, calling the Text Utilities IdenticalString function to compare each cell's data with the specified data until IdenticalString returns 0, indicating that a match has been found.

Custom Match Functions

The default match function is useful for text-only lists. Your application can use a different match function to facilitate searches in other types of lists as long as that function is defined just like IdenticalString.

A common custom match function is one which supports type selection in lists, that is, one which works like the default match function but which allows the cell data to be longer than the data being searched for. For example, a search for the string "be" would match a cell containing the string "Beams".

Changing the Current List

As previously stated, when a window or dialog contains multiple lists, your application should allow the user to change the current list by clicking in one of the non-current lists or by pressing the Tab key or Shift-Tab. In a window with more than two lists, Tab key presses should make the next list in a pre-determined sequence the current list, and Shift-Tab should make the previous list in that sequence the current list. The pre-determined sequence is best implemented using a linked ring.

Linked Ring

To create a linked ring, you can use the refCon field of each list structure. Assign the handle to the second list to the refCon field of the first list, assign the handle to the third list to the refCon field of the second list, and so on, until, to close the circle, the handle to the first list is assigned to the refCon field of the last list. Then, in response to a Tab key press in the current list, your application can ascertain the next list in the ring by looking at the current list's refCon field.

Responding to Shift-Tab is a little more complex. The following example function shows how this can be done:

     ListHandle  gCurrentListHdl;

     void  doFindPreviousListInRing(void)
     {
       ListHandle  listHdl;

       listHdl = gCurrentListHdl;

       while((ListHandle) GetListRefCon(listHdl) != gCurrentList)
         listHdl = (ListHandle) GetListRefCon(listHdl);

       gCurrentListHdl = listHdl;
     }

Customising the Cell-Selection Algorithm

You can modify the algorithm the List Manager uses to select cells in response to mouse clicking and dragging by changing the value in the selFlags field of the list structure. (Recall that, by default, mouse clicks deselect all cells and select the current cell, Shift-click and Shift-drag extend the selection as a rectangular range, and Command-click and Command drag toggle selections according to the selection state of the initial cell.)

The bits in the selFlags field are represented by the following constants. Those constants, and the effect the values they represent have on the cell-selection algorithm, are as follows:

Constant

Value

Effect

lOnlyOne 128

Allow only one cell to be selected at any one time.

lExtendDrag 64

Allow the user to select a range of cells by clicking the first cell and dragging to the last cell without necessarily pressing the Shift or Command key. (Ordinarily, dragging in this manner results in only the last cell being selected.)

lNoDisjoint 32

Prevent discontiguous selections using the Command key, while still allowing the user to select a contiguous range of cells.

lNoExtend 16

Cause all previously selected cells to be deselected when the user Shift-clicks.

lNoRect 8

Disable the feature which allows the user to shrink a selection by Shift-clicking to select a range of cells and then dragging the cursor to a position within that range. (With this feature is disabled, all cells in the cursor's path during a Shift-drag become selected even if the user drags the cursor back over the cell.)

lUseSense 4

Allow the user to deselect a range of cells by Shift-dragging. (Ordinarily, Shift-dragging causes cells to become selected even if the first cell clicked is already selected.)

lNoNilHilite 2

Turn off the highlighting of cells which contain no data. (Note that this constant is somewhat different from the others in that it affects the display of a list, not the way that the List Manager selects items in response to a click.)

These constants are often used additively. For example, you could make the Shift key work just like the Command key using the following code:

     SetListSelectionFlags(listHdl,lNoRect + lNoExtend + lUseSense);

If your application customises the cell-selection algorithm in lists which allow multiple cell selection, it should make the non-standard behaviour clear to the user. Typically, this is done by displaying explanatory text above the list's display rectangle.

The List Box Control

The list box control reduces the programming effort involved in managing lists. Basically, this control frees your application from the requirement to provide its own functions for attending to mouse and keyboard interaction with the list (except for type selection). To create and manage lists using the list box control, you need to:

  • Provide a list box 'CNTL' resource and a list box description ('ldes') resource (see below).

  • Provide functions for storing data in the list's cells and, where required, for adding rows and/or columns.

  • Provide a function to support type selection, if required.

  • Modify the list's cell selection algorithm, if required.

  • Provide a function which searches for, and returns the data in, the selected cell or cells.

The handle to the control is assigned to the refCon field of the list structure. This allows a custom list definition function to determine whether the control should be drawn in the activated or deactivated state by looking at the contrlHilite field of the control structure.

List Box Variants, Values, Constants, and Resources

Variant and Control Definition ID

The list box CDEF resource ID is 22. The two available variants and their control definition IDs are as follows:

Variant

Var Code

Control Definition ID

List box.

0 352 kControlListBoxProc

Autosizing list box.

1 353 kControlListBoxAutoSizeProc

Control Values

Control Value

Content

Initial

Resource ID of the 'ldes' resource holding the list box information. Reset to 0 after creation. An initial value of 0 indicates not to read an 'ldes' resource. (See The List Box Description Resource, below.)

Minimum

Reserved. Set to 0.

Maximum

Reserved. Set to 0.

Control Data Tag Constants

Control Data Tag Constant

Meaning and Data Type Returned or Set

kControlListBoxListHandleTag

Gets a handle to a list box.

Data type returned or set: ListHandle

kControlListBoxDoubleClickTag

Checks to see whether the most recent click in a list box was a double click.

Data type returned or set: Boolean. If true, the last click was a double click. If false, not.

kControlListBoxLDEFTag

Sets the 'LDEF' resource to be used to draw a list box's contents. This is useful for creating a list box without an 'ldes' resource.

Data type returned or set: SInt16

kControlListBoxKeyFilterTag

Gets or sets a key filter function.

Data type returned or set: ControlKeyFilterUPP

kControlListBoxFontStyleTag

Gets or sets the font style.

Data type returned or set: ControlFontStyleRec

Control Part Codes

Constant

Value

Description

kControlListBoxPart 24

Event occurred in a list box.

kControlListBoxDoubleClickPart 25

Double-click occurred in a list box.

The List Box Description Resource

The list box description ('ldes') resource, which must have resource ID of greater than 127, is used to specify information for a list box. The information is used by The Control Manager to provide additional information to the relevant list box control. Fig 3 shows the structure of a compiled 'ldes' resource.

The following describes the main fields of a compiled 'ldes' resource:

Field

Description

NUMBER OF ROWS

The number of rows in the list box.

NUMBER OF COLUMNS

The number of columns in the list box.

CELL HEIGHT

The height of the list cells. Specify 0 to cause the height to be calculated automatically.

CELL WIDTH

The width of a the list cells. Specify 0 to cause the width to be calculated automatically.

HAS VERTICAL SCROLL BAR

true causes the list box to contain a vertical scroll bar.

HAS HORIZONTAL SCROLL BAR

true causes the list box to contain a horizontal scroll bar.

RESOURCE ID

The resource ID of the list definition function to use for the list. In Carbon, always set to 0.

HAS SIZE BOX

true causes the List Manager to leave room for, and draw, a size box.

Programmatic Creation

List box controls may be created programmatically using the function CreateListBoxControl:

     OSStatus  CreateListBoxControl(WindowRef window,const Rect *boundsRect,
               Boolean autoSize,SInt16 numRows,SInt16 numColumns,Boolean horizScroll,
               Boolean vertScroll,SInt16 cellHeight,SInt16 cellWidth,
               Boolean hasGrowSpace,const ListDefSpec *listDef,ControlRef *outControl);

Custom List Definition Functions

As previously stated, the default list definition function supports the display of unstyled text only. If your application needs to display items graphically, or display more than one type of information in each cell, you must create your own list definition function.

For example, the MAC OS 8/9 Finder's About This Computer... dialog contains a single-column list of applications currently in use. Each cell in the list contains an icon, the name of the application, the amount of memory in the application partition, and a graphical indication of how much of that memory has been used.

Your custom list definition function must be declared like this:

     void  myListDefinition(SInt16 lMessage,Boolean lSelect,Rect *lRect,Cell lCell,
                            SInt16 lDataOffset,SInt16 lDataLen,ListHandle lHandle);

Messages Sent by List Manager

In essence, the sole requirement of your list definition function is to respond appropriately to four types of messages sent to it by the List Manager, and which are received in the lMessage parameter. The following constants define the four message types:

Constant

Value

Meaning

lInitMsg 0

Do any special list initialisation.

lDrawMsg 1

Draw the cell.

lHiliteMsg 2

Invert the cell's highlight state.

lCloseMsg 3

Take any special disposal action.

The lSelect, lRect, lCell, lDataOffset, and lDataLen parameters, which contain information about the cell affected by the message, pass information to your custom list definition function only when the lDrawMsg or lHiliteMsg messages are received. The lSelect parameter indicates whether the cell should be highlighted. lRect and lCell provide the cell's rectangle and coordinates. lDataOffset and lDataLen parameters provide the offset and length of the cell's data referenced by the cells field of the list structure.

The Initialisation Message

The List Manager automatically allocates memory for a list and fills out the fields of a list structure before calling your list definition function with an lInitMsg message. Your application might respond to the initialisation message by changing, say, the cellSize and indent fields of the list structure. However, many list definition functions do not need to perform any action in response to the lInitMsg message.

The Draw Message

In response to the lDrawMsg message, your custom list definition function must examine the specified cell's data and draw the cell as appropriate, ensuring that the characteristics of the drawing environment are not altered.

The HighLighting Message

In response to the lHiliteMsg message, your custom list definition function should highlight the cell's rectangle. The following example shows how this might be done:

     void  doLDEFHighlight(Rect *cellRect)
     {
       UInt8  hiliteVal;

       hiliteVal = LMGetHiliteMode();
       BitClr(&hiliteVal,pHiliteBit);
       LMSetHiliteMode(hiliteVal);

       InvertRect(cellRect);
     }

Responding to the Close Message

The lCloseMsg is sent immediately before the List Manager disposes of the memory occupied by the list. Your custom list definition function needs to respond only if it needs to perform some special processing at that point, such as releasing any additional memory associated with the list.

Main List Manager Constants, Data Types, and Functions

Constants

Masks For listFlags Field of List Structure

lDoVAutoscroll           = 2  Allow vertical autoscrolling.
lDoHAutoscroll           = 1  Allow horizontal autoscrolling.

Masks For selFlags Field of List Structure

lOnlyOne                 = -128  Allow only one item to be selected at once.
lExtendDrag              = 64  Enable multiple item selection without Shift.
lNoDisjoint              = 32  Prevent discontiguous selections.
lNoExtend                = 16  Reset list before responding to Shift-click.
lNoRect                  = 8  Shift-drag selects items passed by cursor.
lUseSense                = 4  Allow use of Shift key to deselect items.
lNoNilHilite             = 2  Disable highlighting of empty cells.

Messages to List Definition Function

lInitMsg                 = 0  Do any special list initialisation.
lDrawMsg                 = 1  Draw the cell.
lHiliteMsg               = 2  Invert cell's highlight state.
lCloseMsg                = 3  Take any special disposal action.

Control Kind (Mac OS X Only)

kControlKindListBox = FOUR_CHAR_CODE('lbox')

Data Types

typedef Point   Cell;
typedef Rect    ListBounds;
typedef char    DataArray[32001];
typedef char    *DataPtr;
typedef DataPtr *DataHandle;

List Structure

struct ListRec 
{
  Rect             rView;          // List's display rectangle.
  GrafPtr          port;           // List's graphics port.
  Point            indent;         // Indent distance for drawing.
  Point            cellSize;       // Size in pixels of a cell.
  Rect             visible;        // Boundary of visible cells.
  ControlRef       vScroll;        // Vertical scroll bar.
  ControlRef       hScroll;        // Horizontal scroll bar.
  SInt8            selFlags;       // Selection flags.
  Boolean          lActive;        // true if list is active.
  SInt8            lReserved;      // (Reserved.)
  Sint8            listFlags;      // Automatic scrolling flags.
  long             clikTime;       // TickCount at time of last tick.
  Point            clikLoc;        // Position of last click.
  Point            mouseLoc;       // Current mouse location.
  ListClickLoopUPP lClickLoop;     // Function called by LClick.
  Cell             lastClick;      // Last cell clicked.
  long             refCon;         // For application use.
  Handle           listDefProc;    // List definition function.
  Handle           userHandle;     // For application use.
  ListBounds       dataBounds;     // Boundary of cells allocated.
  DataHandle       cells;          // Cell data.
  short            maxIndex;       // (Used internally.)
  short            cellArray[1];   // Offsets to data.
};
typedef struct ListRec ListRec;
typedef ListRec *ListPtr;
typedef ListPtr *ListHandle;

Functions

Creating and Disposing of Lists

ListHandle  LNew(const Rect *rView,const ListBounds *dataBounds,Point cSize,
            short theProc,  WindowRef theWindow,Boolean drawIt,Boolean hasGrow,
            Boolean scrollHoriz,Boolean scrollVert);
OSStatus    CreateCustomList(const Rect *rView,const ListBounds *dataBounds,
            Point   cellSize,const ListDefSpec *theSpec,WindowRef theWindow,Boolean drawIt,
            Boolean hasGrow,Boolean scrollHoriz,Boolean scrollVert,ListHandle *outList);
void        LDispose(ListHandle lHandle);

Creating List Box Controls

OSStatus   CreateListBoxControl(WindowRef window,const Rect *boundsRect,
           Boolean autoSize,SInt16 numRows,SInt16 numColumns,Boolean horizScroll,
           Boolean vertScroll,SInt16 cellHeight,SInt16 cellWidth,Boolean hasGrowSpace,
           const ListDefSpec *listDef,ControlRef *outControl);

Adding and Deleting Rows and Columns

short  LAddColumn(short count,short colNum,ListHandle lHandle);
short  LAddRow(short count,short rowNum,ListHandle lHandle);
void   LDelColumn(short count,short colNum,ListHandle lHandle);
void   LDelRow(short count,short rowNum,ListHandle lHandle);

Determining or Changing a Selection

Boolean  LGetSelect(Boolean next,Cell *theCell,ListHandle lHandle);
void     LSetSelect(Boolean setIt,Cell theCell,ListHandle lHandle);

Accessing and Manipulating Data Cells

void  LSetCell(const void *dataPtr,short dataLen,Cell theCell,ListHandle lHandle);
void  LAddToCell(const void *dataPtr,short dataLen,Cell theCell,
      ListHandle lHandle);
void  LClrCell(Cell theCell,ListHandle lHandle);
void  LGetCellDataLocation(short *offset,short *len,Cell theCell,
      ListHandle lHandle);
void  LGetCell(void *dataPtr,short *dataLen,Cell theCell,ListHandle lHandle);

Responding to Events

Boolean  LClick(Point pt,short modifiers,ListHandle lHandle);
void     LUpdate(RgnHandle theRgn,ListHandle lHandle);
void     LActivate(Boolean act,ListHandle lHandle);

Modifying a List's Appearance

void  LSetDrawingMode(Boolean drawIt,ListHandle lHandle);
void  LDraw(Cell theCell,ListHandle lHandle);
void  LAutoScroll(ListHandle lHandle);
void  LScroll(short dCols,short dRows,ListHandle lHandle);

Searching For a List Containing a Particular Item

Boolean  LSearch(const void *dataPtr,short dataLen,ListSearchUPP searchProc,
         Cell *theCell, ListHandle lHandle);

Changing the Size of Cells and Lists

void  LSize(short listWidth,short listHeight,ListHandle lHandle);
void  LCellSize(Point cSize,ListHandle lHandle);

Getting Information About Cells

Boolean  LNextCell(Boolean hNext,Boolean vNext,Cell *theCell,ListHandle lHandle);
void     LRect(Rect *cellRect,Cell theCell,ListHandle lHandle);
Cell     LLastClick(ListHandle lHandle);

List Structure Accessor Functions

Rect*             GetListViewBounds(ListRef list,Rect *view);
void              SetListViewBounds(ListRef list,const Rect *view);
CGrafPtr          GetListPort(ListRef list);
void              SetListPort(ListRef list,CGrafPtr port);
Point*            GetListCellIndent(ListRef list,Point *indent);
void              SetListCellIndent(ListRef list,Point *indent);
Point*            GetListCellSize(ListRef list,Point *size);
void              SetListCellSize(ListRef list,Point *size);
ListBounds*       GetListVisibleCells(ListRef list,ListBounds *visible);
void              SetListVisibleCells(ListRef list,ListBounds *visible);
ControlHandle     GetListVerticalScrollBar(ListRef list);
void              SetListVerticalScrollBar(ListRef list,ControlHandle vScroll);
ControlHandle     GetListHorizontalScrollBar(ListRef list);
void              SetListHorizontalScrollBar(ListRef list,ControlHandle hScroll);
OptionBits        GetListSelectionFlags(ListRef list);
void              SetListSelectionFlags(ListRef list,OptionBits selectionFlags);
Boolean           GetListActive(ListRef list);
OptionBits        GetListFlags(ListRef list);
void              SetListFlags(ListRef list,OptionBits listFlags);
SInt32            GetListClickTime(ListRef list);
void              SetListClickTime(ListRef list,SInt32 time);
Point*            GetListClickLocation(ListRef list,Point *click);
Point*            GetListMouseLocation(ListRef list,Point *mouse);
ListClickLoopUPP  GetListClickLoop(ListRef list);
void              SetListClickLoop(ListRef list,ListClickLoopUPP clickLoop);
void              SetListLastClick(ListRef list,Cell *lastClick);
SInt32            GetListRefCon(ListRef list);
void              SetListRefCon(ListRef list,SInt32 refCon);
Handle            GetListUserHandle(ListRef list);
void              SetListUserHandle(ListRef list,Handle userHandle);
Handle            GetListDefinition(ListRef list);
void              SetListDefinition(ListRef list,Handle listDefProc);
ListBounds*       GetListDataBounds(ListRef list,ListBounds *bounds);
DataHandle        GetListDataHandle(ListRef list);

Creating and Disposing of Creating and Disposing of Universal Procedure Pointers

ListSearchUPP     NewListSearchUPP(ListSearchProcPtr userRoutine);
ListClickLoopUPP  NewListClickLoopUPP(ListClickLoopProcPtruserRoutine);
ListDefUPP        NewListDefUPP(ListDefProcPtr userRoutine);
void              DisposeListSearchUPP(ListSearchUPP userUPP);
void              DisposeListClickLoopUPP(ListClickLoopUPP userUPP);
void              DisposeListDefUPP(ListDefUPP userUPP);

Application-Defined (Callback) Function

void             myListDefinition(short lMessage, Boolean lSelect, Rect *lRect, Cell lCell,
                 short lDataOffset, short lDataLen, ListHandle lHandle);

Relevant Text Utilities Data Type and Functions

Data Type

struct TypeSelectRecord 
{
  unsigned long tsrLastKeyTime;
  ScriptCode    tsrScript;
  Str63         tsrKeyStrokes;
};
typedef struct TypeSelectRecord TypeSelectRecord;

Functions

void      TypeSelectClear(TypeSelectRecord *tsr);
Boolean   TypeSelectNewKey  (const EventRecord *theEvent,TypeSelectRecord *tsr);

 

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

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
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... 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.